2001-10-03 17:10:38 +04:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2011-02-25 01:05:47 +03:00
|
|
|
// $Id$
|
2001-10-03 17:10:38 +04:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2009-12-04 22:50:29 +03:00
|
|
|
// Copyright (C) 2002-2009 The Bochs Project
|
2001-04-10 05:04:59 +04:00
|
|
|
//
|
|
|
|
// 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 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
|
2009-02-08 12:05:52 +03:00
|
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2009-02-08 00:05:31 +03:00
|
|
|
//
|
2008-02-16 01:05:43 +03:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2002-10-25 01:07:56 +04:00
|
|
|
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
2008-01-27 01:24:03 +03:00
|
|
|
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
2002-10-25 01:07:56 +04:00
|
|
|
// is used to know when we are exporting symbols and when we are importing.
|
|
|
|
#define BX_PLUGGABLE
|
|
|
|
|
2004-06-19 19:20:15 +04:00
|
|
|
#include "iodev.h"
|
2008-12-29 23:16:08 +03:00
|
|
|
#include "dma.h"
|
2002-10-25 01:07:56 +04:00
|
|
|
|
|
|
|
#define LOG_THIS theDmaDevice->
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
#define DMA_MODE_DEMAND 0
|
|
|
|
#define DMA_MODE_SINGLE 1
|
|
|
|
#define DMA_MODE_BLOCK 2
|
|
|
|
#define DMA_MODE_CASCADE 3
|
|
|
|
|
2002-10-25 01:07:56 +04:00
|
|
|
bx_dma_c *theDmaDevice = NULL;
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-06-14 20:44:33 +04:00
|
|
|
int libdma_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
|
2002-10-25 01:07:56 +04:00
|
|
|
{
|
2011-12-25 12:52:34 +04:00
|
|
|
if (type == PLUGTYPE_CORE) {
|
|
|
|
theDmaDevice = new bx_dma_c ();
|
|
|
|
bx_devices.pluginDmaDevice = theDmaDevice;
|
|
|
|
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theDmaDevice, BX_PLUGIN_DMA);
|
|
|
|
return 0; // Success
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
2002-10-25 01:07:56 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-05-27 19:54:49 +04:00
|
|
|
void libdma_LTX_plugin_fini(void)
|
2002-10-25 01:07:56 +04:00
|
|
|
{
|
2006-09-10 21:18:44 +04:00
|
|
|
delete theDmaDevice;
|
2002-10-25 01:07:56 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-06-14 20:44:33 +04:00
|
|
|
bx_dma_c::bx_dma_c()
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
2002-06-16 19:02:28 +04:00
|
|
|
put("DMA");
|
2012-02-11 21:11:58 +04:00
|
|
|
memset(&s, 0, sizeof(s));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
2006-06-14 20:44:33 +04:00
|
|
|
bx_dma_c::~bx_dma_c()
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
2012-08-19 12:16:20 +04:00
|
|
|
SIM->get_bochs_root()->remove("dma");
|
2006-09-10 21:18:44 +04:00
|
|
|
BX_DEBUG(("Exit"));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
unsigned bx_dma_c::registerDMA8Channel(unsigned channel,
|
2012-07-13 01:20:46 +04:00
|
|
|
Bit16u (* dmaRead)(Bit8u *data_byte, Bit16u maxlen),
|
|
|
|
Bit16u (* dmaWrite)(Bit8u *data_byte, Bit16u maxlen),
|
2006-03-06 22:23:13 +03:00
|
|
|
const char *name)
|
2002-06-16 19:02:28 +04:00
|
|
|
{
|
|
|
|
if (channel > 3) {
|
|
|
|
BX_PANIC(("registerDMA8Channel: invalid channel number(%u).", channel));
|
2006-03-06 22:23:13 +03:00
|
|
|
return 0; // Fail
|
|
|
|
}
|
2002-10-25 01:07:56 +04:00
|
|
|
if (BX_DMA_THIS s[0].chan[channel].used) {
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_PANIC(("registerDMA8Channel: channel(%u) already in use.", channel));
|
2006-03-06 22:23:13 +03:00
|
|
|
return 0; // Fail
|
|
|
|
}
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_INFO(("channel %u used by %s", channel, name));
|
2002-10-25 01:07:56 +04:00
|
|
|
BX_DMA_THIS h[channel].dmaRead8 = dmaRead;
|
|
|
|
BX_DMA_THIS h[channel].dmaWrite8 = dmaWrite;
|
|
|
|
BX_DMA_THIS s[0].chan[channel].used = 1;
|
2006-03-06 22:23:13 +03:00
|
|
|
return 1; // OK
|
2002-06-16 19:02:28 +04:00
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
unsigned bx_dma_c::registerDMA16Channel(unsigned channel,
|
2012-07-13 01:20:46 +04:00
|
|
|
Bit16u (* dmaRead)(Bit16u *data_word, Bit16u maxlen),
|
|
|
|
Bit16u (* dmaWrite)(Bit16u *data_word, Bit16u maxlen),
|
2006-03-06 22:23:13 +03:00
|
|
|
const char *name)
|
2002-06-16 19:02:28 +04:00
|
|
|
{
|
|
|
|
if ((channel < 4) || (channel > 7)) {
|
|
|
|
BX_PANIC(("registerDMA16Channel: invalid channel number(%u).", channel));
|
2006-03-06 22:23:13 +03:00
|
|
|
return 0; // Fail
|
|
|
|
}
|
2002-10-25 01:07:56 +04:00
|
|
|
if (BX_DMA_THIS s[1].chan[channel & 0x03].used) {
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_PANIC(("registerDMA16Channel: channel(%u) already in use.", channel));
|
2006-03-06 22:23:13 +03:00
|
|
|
return 0; // Fail
|
|
|
|
}
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_INFO(("channel %u used by %s", channel, name));
|
|
|
|
channel &= 0x03;
|
2002-10-25 01:07:56 +04:00
|
|
|
BX_DMA_THIS h[channel].dmaRead16 = dmaRead;
|
|
|
|
BX_DMA_THIS h[channel].dmaWrite16 = dmaWrite;
|
|
|
|
BX_DMA_THIS s[1].chan[channel].used = 1;
|
2006-03-06 22:23:13 +03:00
|
|
|
return 1; // OK
|
2002-06-16 19:02:28 +04:00
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
unsigned bx_dma_c::unregisterDMAChannel(unsigned channel)
|
2002-06-16 19:02:28 +04:00
|
|
|
{
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool ma_sl = (channel > 3);
|
2002-10-25 01:07:56 +04:00
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used = 0;
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_INFO(("channel %u no longer used", channel));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
unsigned bx_dma_c::get_TC(void)
|
2002-06-16 19:02:28 +04:00
|
|
|
{
|
|
|
|
return BX_DMA_THIS TC;
|
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::init(void)
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
2002-06-23 22:03:37 +04:00
|
|
|
unsigned c, i, j;
|
2011-02-25 01:05:47 +03:00
|
|
|
BX_DEBUG(("Init $Id$"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
/* 8237 DMA controller */
|
|
|
|
|
2002-06-23 22:03:37 +04:00
|
|
|
for (i=0; i < 2; i++) {
|
|
|
|
for (j=0; j < 4; j++) {
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS s[i].DRQ[j] = 0;
|
|
|
|
BX_DMA_THIS s[i].DACK[j] = 0;
|
|
|
|
}
|
2005-04-07 01:09:25 +04:00
|
|
|
}
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS HLDA = 0;
|
|
|
|
BX_DMA_THIS TC = 0;
|
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
// 0000..000F
|
|
|
|
for (i=0x0000; i<=0x000F; i++) {
|
2003-07-31 19:29:34 +04:00
|
|
|
DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
|
|
|
|
DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
|
2005-04-07 01:09:25 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2005-04-07 01:09:25 +04:00
|
|
|
// 00080..008F
|
|
|
|
for (i=0x0080; i<=0x008F; i++) {
|
2003-07-31 19:29:34 +04:00
|
|
|
DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
|
|
|
|
DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
|
2005-04-07 01:09:25 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// 000C0..00DE
|
2001-12-18 16:12:45 +03:00
|
|
|
for (i=0x00C0; i<=0x00DE; i+=2) {
|
2003-07-31 19:29:34 +04:00
|
|
|
DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
|
|
|
|
DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
|
2005-04-07 01:09:25 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
for (i=0; i<2; i++) {
|
|
|
|
for (c=0; c<4; c++) {
|
|
|
|
BX_DMA_THIS s[i].chan[c].mode.mode_type = 0; // demand mode
|
|
|
|
BX_DMA_THIS s[i].chan[c].mode.address_decrement = 0; // address increment
|
|
|
|
BX_DMA_THIS s[i].chan[c].mode.autoinit_enable = 0; // autoinit disable
|
|
|
|
BX_DMA_THIS s[i].chan[c].mode.transfer_type = 0; // verify
|
|
|
|
BX_DMA_THIS s[i].chan[c].base_address = 0;
|
|
|
|
BX_DMA_THIS s[i].chan[c].current_address = 0;
|
|
|
|
BX_DMA_THIS s[i].chan[c].base_count = 0;
|
|
|
|
BX_DMA_THIS s[i].chan[c].current_count = 0;
|
|
|
|
BX_DMA_THIS s[i].chan[c].page_reg = 0;
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS s[i].chan[c].used = 0;
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2005-04-07 01:09:25 +04:00
|
|
|
}
|
|
|
|
memset(&BX_DMA_THIS ext_page_reg[0], 0, 16);
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS s[1].chan[0].used = 1; // cascade channel in use
|
|
|
|
BX_INFO(("channel 4 used by cascade"));
|
2012-03-15 22:12:13 +04:00
|
|
|
#if BX_DEBUGGER
|
|
|
|
// register device for the 'info device' command (calls debug_dump())
|
|
|
|
bx_dbg_register_debug_info("dma", this);
|
|
|
|
#endif
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::reset(unsigned type)
|
2002-08-27 23:54:46 +04:00
|
|
|
{
|
2002-08-28 23:39:00 +04:00
|
|
|
reset_controller(0);
|
|
|
|
reset_controller(1);
|
2002-08-27 23:54:46 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::reset_controller(unsigned num)
|
2002-08-28 23:39:00 +04:00
|
|
|
{
|
|
|
|
BX_DMA_THIS s[num].mask[0] = 1;
|
|
|
|
BX_DMA_THIS s[num].mask[1] = 1;
|
|
|
|
BX_DMA_THIS s[num].mask[2] = 1;
|
|
|
|
BX_DMA_THIS s[num].mask[3] = 1;
|
2005-04-05 21:57:32 +04:00
|
|
|
BX_DMA_THIS s[num].ctrl_disabled = 0;
|
2002-08-28 23:39:00 +04:00
|
|
|
BX_DMA_THIS s[num].command_reg = 0;
|
|
|
|
BX_DMA_THIS s[num].status_reg = 0;
|
|
|
|
BX_DMA_THIS s[num].flip_flop = 0;
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-05-27 19:54:49 +04:00
|
|
|
void bx_dma_c::register_state(void)
|
|
|
|
{
|
|
|
|
unsigned i, c;
|
|
|
|
char name[6];
|
2012-02-12 22:43:20 +04:00
|
|
|
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "dma", "DMA State");
|
2006-05-27 19:54:49 +04:00
|
|
|
for (i=0; i<2; i++) {
|
|
|
|
sprintf(name, "%d", i);
|
2012-02-12 22:43:20 +04:00
|
|
|
bx_list_c *ctrl = new bx_list_c(list, name);
|
2006-05-28 22:43:01 +04:00
|
|
|
BXRS_PARAM_BOOL(ctrl, flip_flop, BX_DMA_THIS s[i].flip_flop);
|
|
|
|
BXRS_HEX_PARAM_FIELD(ctrl, status_reg, BX_DMA_THIS s[i].status_reg);
|
|
|
|
BXRS_HEX_PARAM_FIELD(ctrl, command_reg, BX_DMA_THIS s[i].command_reg);
|
|
|
|
BXRS_PARAM_BOOL(ctrl, ctrl_disabled, BX_DMA_THIS s[i].ctrl_disabled);
|
2006-05-27 19:54:49 +04:00
|
|
|
for (c=0; c<4; c++) {
|
|
|
|
sprintf(name, "%d", c);
|
2012-02-12 22:43:20 +04:00
|
|
|
bx_list_c *chan = new bx_list_c(ctrl, name);
|
2006-05-28 22:43:01 +04:00
|
|
|
BXRS_PARAM_BOOL(chan, DRQ, BX_DMA_THIS s[i].DRQ[c]);
|
|
|
|
BXRS_PARAM_BOOL(chan, DACK, BX_DMA_THIS s[i].DACK[c]);
|
|
|
|
BXRS_PARAM_BOOL(chan, mask, BX_DMA_THIS s[i].mask[c]);
|
|
|
|
BXRS_DEC_PARAM_FIELD(chan, mode_type, BX_DMA_THIS s[i].chan[c].mode.mode_type);
|
2012-03-20 22:29:49 +04:00
|
|
|
BXRS_PARAM_BOOL(chan, address_decrement, BX_DMA_THIS s[i].chan[c].mode.address_decrement);
|
|
|
|
BXRS_PARAM_BOOL(chan, autoinit_enable, BX_DMA_THIS s[i].chan[c].mode.autoinit_enable);
|
2006-05-28 22:43:01 +04:00
|
|
|
BXRS_DEC_PARAM_FIELD(chan, transfer_type, BX_DMA_THIS s[i].chan[c].mode.transfer_type);
|
|
|
|
BXRS_HEX_PARAM_FIELD(chan, base_address, BX_DMA_THIS s[i].chan[c].base_address);
|
|
|
|
BXRS_HEX_PARAM_FIELD(chan, current_address, BX_DMA_THIS s[i].chan[c].current_address);
|
|
|
|
BXRS_HEX_PARAM_FIELD(chan, base_count, BX_DMA_THIS s[i].chan[c].base_count);
|
|
|
|
BXRS_HEX_PARAM_FIELD(chan, current_count, BX_DMA_THIS s[i].chan[c].current_count);
|
|
|
|
BXRS_HEX_PARAM_FIELD(chan, page_reg, BX_DMA_THIS s[i].chan[c].page_reg);
|
2006-05-27 19:54:49 +04:00
|
|
|
}
|
|
|
|
}
|
2012-02-12 22:43:20 +04:00
|
|
|
bx_list_c *extpg = new bx_list_c(list, "ext_page");
|
2006-05-27 19:54:49 +04:00
|
|
|
for (i=0; i<16; i++) {
|
|
|
|
sprintf(name, "0x%02x", 0x80+i);
|
2006-06-14 20:44:33 +04:00
|
|
|
new bx_shadow_num_c(extpg, name, &BX_DMA_THIS ext_page_reg[i], BASE_HEX);
|
2006-05-27 19:54:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
// index to find channel from register number (only [0],[1],[2],[6] used)
|
|
|
|
Bit8u channelindex[7] = {2, 3, 1, 0, 0, 0, 0};
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
// static IO port read callback handler
|
|
|
|
// redirects to non-static class handler to avoid virtual functions
|
|
|
|
Bit32u bx_dma_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
|
|
|
#if !BX_USE_DMA_SMF
|
|
|
|
bx_dma_c *class_ptr = (bx_dma_c *) this_ptr;
|
2006-03-06 22:23:13 +03:00
|
|
|
return class_ptr->read(address, io_len);
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 8237 DMA controller */
|
2003-03-03 02:59:12 +03:00
|
|
|
Bit32u BX_CPP_AttrRegparmN(2)
|
2006-03-06 22:23:13 +03:00
|
|
|
bx_dma_c::read(Bit32u address, unsigned io_len)
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
|
|
|
#else
|
|
|
|
UNUSED(this_ptr);
|
|
|
|
#endif // !BX_USE_DMA_SMF
|
|
|
|
|
|
|
|
Bit8u retval;
|
|
|
|
Bit8u channel;
|
|
|
|
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("read addr=%04x", (unsigned) address));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
#if BX_DMA_FLOPPY_IO < 1
|
|
|
|
/* if we're not supporting DMA/floppy IO just return a bogus value */
|
|
|
|
return(0xff);
|
|
|
|
#endif
|
|
|
|
|
2004-08-11 15:48:55 +04:00
|
|
|
bx_bool ma_sl = (address >= 0xc0);
|
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
switch (address) {
|
|
|
|
case 0x00: /* DMA-1 current address, channel 0 */
|
|
|
|
case 0x02: /* DMA-1 current address, channel 1 */
|
|
|
|
case 0x04: /* DMA-1 current address, channel 2 */
|
|
|
|
case 0x06: /* DMA-1 current address, channel 3 */
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xc0: /* DMA-2 current address, channel 0 */
|
|
|
|
case 0xc4: /* DMA-2 current address, channel 1 */
|
|
|
|
case 0xc8: /* DMA-2 current address, channel 2 */
|
|
|
|
case 0xcc: /* DMA-2 current address, channel 3 */
|
|
|
|
channel = (address >> (1 + ma_sl)) & 0x03;
|
|
|
|
if (BX_DMA_THIS s[ma_sl].flip_flop==0) {
|
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
|
2005-04-05 21:57:32 +04:00
|
|
|
return (BX_DMA_THIS s[ma_sl].chan[channel].current_address & 0xff);
|
|
|
|
} else {
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
|
2005-04-05 21:57:32 +04:00
|
|
|
return (BX_DMA_THIS s[ma_sl].chan[channel].current_address >> 8);
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
case 0x01: /* DMA-1 current count, channel 0 */
|
|
|
|
case 0x03: /* DMA-1 current count, channel 1 */
|
|
|
|
case 0x05: /* DMA-1 current count, channel 2 */
|
|
|
|
case 0x07: /* DMA-1 current count, channel 3 */
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xc2: /* DMA-2 current count, channel 0 */
|
|
|
|
case 0xc6: /* DMA-2 current count, channel 1 */
|
|
|
|
case 0xca: /* DMA-2 current count, channel 2 */
|
|
|
|
case 0xce: /* DMA-2 current count, channel 3 */
|
|
|
|
channel = (address >> (1 + ma_sl)) & 0x03;
|
|
|
|
if (BX_DMA_THIS s[ma_sl].flip_flop==0) {
|
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
|
2005-04-05 21:57:32 +04:00
|
|
|
return (BX_DMA_THIS s[ma_sl].chan[channel].current_count & 0xff);
|
|
|
|
} else {
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
|
2005-04-05 21:57:32 +04:00
|
|
|
return (BX_DMA_THIS s[ma_sl].chan[channel].current_count >> 8);
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
case 0x08: // DMA-1 Status Register
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xd0: // DMA-2 Status Register
|
2001-04-10 05:04:59 +04:00
|
|
|
// bit 7: 1 = channel 3 request
|
|
|
|
// bit 6: 1 = channel 2 request
|
|
|
|
// bit 5: 1 = channel 1 request
|
|
|
|
// bit 4: 1 = channel 0 request
|
|
|
|
// bit 3: 1 = channel 3 has reached terminal count
|
|
|
|
// bit 2: 1 = channel 2 has reached terminal count
|
|
|
|
// bit 1: 1 = channel 1 has reached terminal count
|
|
|
|
// bit 0: 1 = channel 0 has reached terminal count
|
|
|
|
// reading this register clears lower 4 bits (hold flags)
|
2001-12-18 16:12:45 +03:00
|
|
|
retval = BX_DMA_THIS s[ma_sl].status_reg;
|
|
|
|
BX_DMA_THIS s[ma_sl].status_reg &= 0xf0;
|
2005-04-05 21:57:32 +04:00
|
|
|
return retval;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0d: // DMA-1: temporary register
|
|
|
|
case 0xda: // DMA-2: temporary register
|
2005-04-05 21:57:32 +04:00
|
|
|
// only used for memory-to-memory transfers
|
|
|
|
// write to 0x0d / 0xda clears temporary register
|
|
|
|
BX_ERROR(("DMA-%d: read of temporary register always returns 0", ma_sl+1));
|
|
|
|
return 0;
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
case 0x0081: // DMA-1 page register, channel 2
|
|
|
|
case 0x0082: // DMA-1 page register, channel 3
|
|
|
|
case 0x0083: // DMA-1 page register, channel 1
|
|
|
|
case 0x0087: // DMA-1 page register, channel 0
|
|
|
|
channel = channelindex[address - 0x81];
|
2005-04-05 21:57:32 +04:00
|
|
|
return BX_DMA_THIS s[0].chan[channel].page_reg;
|
2001-12-18 16:12:45 +03:00
|
|
|
|
|
|
|
case 0x0089: // DMA-2 page register, channel 2
|
|
|
|
case 0x008a: // DMA-2 page register, channel 3
|
|
|
|
case 0x008b: // DMA-2 page register, channel 1
|
|
|
|
case 0x008f: // DMA-2 page register, channel 0
|
|
|
|
channel = channelindex[address - 0x89];
|
2005-04-05 21:57:32 +04:00
|
|
|
return BX_DMA_THIS s[1].chan[channel].page_reg;
|
2001-12-18 16:12:45 +03:00
|
|
|
|
2005-04-07 01:09:25 +04:00
|
|
|
case 0x0080:
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0084:
|
|
|
|
case 0x0085:
|
|
|
|
case 0x0086:
|
|
|
|
case 0x0088:
|
|
|
|
case 0x008c:
|
|
|
|
case 0x008d:
|
|
|
|
case 0x008e:
|
2005-04-07 01:09:25 +04:00
|
|
|
BX_DEBUG(("read: extra page register 0x%04x (unused)", (unsigned) address));
|
|
|
|
return BX_DMA_THIS ext_page_reg[address & 0x0f];
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2004-08-11 15:48:55 +04:00
|
|
|
case 0x0f: // DMA-1: undocumented: read all mask bits
|
|
|
|
case 0xde: // DMA-2: undocumented: read all mask bits
|
|
|
|
retval = BX_DMA_THIS s[ma_sl].mask[0] |
|
|
|
|
(BX_DMA_THIS s[ma_sl].mask[1] << 1) |
|
|
|
|
(BX_DMA_THIS s[ma_sl].mask[2] << 2) |
|
|
|
|
(BX_DMA_THIS s[ma_sl].mask[3] << 3);
|
2005-04-05 21:57:32 +04:00
|
|
|
return (0xf0 | retval);
|
2004-08-11 15:48:55 +04:00
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
default:
|
2002-05-11 17:42:52 +04:00
|
|
|
BX_ERROR(("read: unsupported address=%04x", (unsigned) address));
|
2005-04-05 21:57:32 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
// static IO port write callback handler
|
|
|
|
// redirects to non-static class handler to avoid virtual functions
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
|
|
|
#if !BX_USE_DMA_SMF
|
|
|
|
bx_dma_c *class_ptr = (bx_dma_c *) this_ptr;
|
|
|
|
|
|
|
|
class_ptr->write(address, value, io_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 8237 DMA controller */
|
2003-03-03 02:59:12 +03:00
|
|
|
void BX_CPP_AttrRegparmN(3)
|
2001-04-10 05:04:59 +04:00
|
|
|
bx_dma_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
|
|
|
{
|
|
|
|
#else
|
|
|
|
UNUSED(this_ptr);
|
|
|
|
#endif // !BX_USE_DMA_SMF
|
|
|
|
Bit8u set_mask_bit;
|
|
|
|
Bit8u channel;
|
|
|
|
|
|
|
|
if (io_len > 1) {
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((io_len == 2) && (address == 0x0b)) {
|
2001-04-10 05:04:59 +04:00
|
|
|
#if BX_USE_DMA_SMF
|
|
|
|
BX_DMA_THIS write_handler(NULL, address, value & 0xff, 1);
|
|
|
|
BX_DMA_THIS write_handler(NULL, address+1, value >> 8, 1);
|
|
|
|
#else
|
|
|
|
BX_DMA_THIS write(address, value & 0xff, 1);
|
|
|
|
BX_DMA_THIS write(address+1, value >> 8, 1);
|
|
|
|
#endif
|
|
|
|
return;
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2001-06-13 20:53:58 +04:00
|
|
|
BX_ERROR(("io write to address %08x, len=%u",
|
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
|
|
|
(unsigned) address, (unsigned) io_len));
|
2002-06-16 19:02:28 +04:00
|
|
|
return;
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DEBUG(("write: address=%04x value=%02x",
|
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
|
|
|
(unsigned) address, (unsigned) value));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
#if BX_DMA_FLOPPY_IO < 1
|
|
|
|
/* if we're not supporting DMA/floppy IO just return */
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2004-08-11 15:48:55 +04:00
|
|
|
bx_bool ma_sl = (address >= 0xc0);
|
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
switch (address) {
|
|
|
|
case 0x00:
|
|
|
|
case 0x02:
|
|
|
|
case 0x04:
|
|
|
|
case 0x06:
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xc0:
|
|
|
|
case 0xc4:
|
|
|
|
case 0xc8:
|
|
|
|
case 0xcc:
|
|
|
|
channel = (address >> (1 + ma_sl)) & 0x03;
|
|
|
|
BX_DEBUG((" DMA-%d base and current address, channel %d", ma_sl+1, channel));
|
|
|
|
if (BX_DMA_THIS s[ma_sl].flip_flop==0) { /* 1st byte */
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].base_address = value;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_address = value;
|
2005-04-05 21:57:32 +04:00
|
|
|
} else { /* 2nd byte */
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].base_address |= (value << 8);
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_address |= (value << 8);
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_DEBUG((" base = %04x", BX_DMA_THIS s[ma_sl].chan[channel].base_address));
|
|
|
|
BX_DEBUG((" curr = %04x", BX_DMA_THIS s[ma_sl].chan[channel].current_address));
|
2005-04-05 21:57:32 +04:00
|
|
|
}
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x01:
|
|
|
|
case 0x03:
|
|
|
|
case 0x05:
|
|
|
|
case 0x07:
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xc2:
|
|
|
|
case 0xc6:
|
|
|
|
case 0xca:
|
|
|
|
case 0xce:
|
|
|
|
channel = (address >> (1 + ma_sl)) & 0x03;
|
|
|
|
BX_DEBUG((" DMA-%d base and current count, channel %d", ma_sl+1, channel));
|
|
|
|
if (BX_DMA_THIS s[ma_sl].flip_flop==0) { /* 1st byte */
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].base_count = value;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_count = value;
|
2005-04-05 21:57:32 +04:00
|
|
|
} else { /* 2nd byte */
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].base_count |= (value << 8);
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_count |= (value << 8);
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_DEBUG((" base = %04x", BX_DMA_THIS s[ma_sl].chan[channel].base_count));
|
|
|
|
BX_DEBUG((" curr = %04x", BX_DMA_THIS s[ma_sl].chan[channel].current_count));
|
2005-04-05 21:57:32 +04:00
|
|
|
}
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x08: /* DMA-1: command register */
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xd0: /* DMA-2: command register */
|
2005-04-05 21:57:32 +04:00
|
|
|
if ((value & 0xfb) != 0x00)
|
|
|
|
BX_ERROR(("write to command register: value 0x%02x not supported",
|
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
|
|
|
(unsigned) value));
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].command_reg = value;
|
2005-04-05 21:57:32 +04:00
|
|
|
BX_DMA_THIS s[ma_sl].ctrl_disabled = (value >> 2) & 0x01;
|
|
|
|
control_HRQ(ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x09: // DMA-1: request register
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xd2: // DMA-2: request register
|
2002-01-18 19:33:47 +03:00
|
|
|
channel = value & 0x03;
|
2005-04-05 21:57:32 +04:00
|
|
|
// note: write to 0x0d / 0xda clears this register
|
2001-04-10 05:04:59 +04:00
|
|
|
if (value & 0x04) {
|
|
|
|
// set request bit
|
2002-01-18 19:33:47 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4));
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_DEBUG(("DMA-%d: set request bit for channel %u", ma_sl+1, channel));
|
2005-04-05 21:57:32 +04:00
|
|
|
} else {
|
2001-04-10 05:04:59 +04:00
|
|
|
// clear request bit
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4));
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_DEBUG(("DMA-%d: cleared request bit for channel %u", ma_sl+1, channel));
|
2005-04-05 21:57:32 +04:00
|
|
|
}
|
2002-01-18 19:33:47 +03:00
|
|
|
control_HRQ(ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0a:
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0xd4:
|
2001-04-10 05:04:59 +04:00
|
|
|
set_mask_bit = value & 0x04;
|
|
|
|
channel = value & 0x03;
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].mask[channel] = (set_mask_bit > 0);
|
|
|
|
BX_DEBUG(("DMA-%d: set_mask_bit=%u, channel=%u, mask now=%02xh", ma_sl+1,
|
2012-03-20 22:29:49 +04:00
|
|
|
set_mask_bit, channel, BX_DMA_THIS s[ma_sl].mask[channel]));
|
2002-01-18 19:33:47 +03:00
|
|
|
control_HRQ(ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0b: /* DMA-1 mode register */
|
|
|
|
case 0xd6: /* DMA-2 mode register */
|
2001-04-10 05:04:59 +04:00
|
|
|
channel = value & 0x03;
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type = (value >> 6) & 0x03;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement = (value >> 5) & 0x01;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].mode.autoinit_enable = (value >> 4) & 0x01;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type = (value >> 2) & 0x03;
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_DEBUG(("DMA-%d: mode register[%u] = %02x", ma_sl+1, channel, (unsigned) value));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0c: /* DMA-1 clear byte flip/flop */
|
|
|
|
case 0xd8: /* DMA-2 clear byte flip/flop */
|
|
|
|
BX_DEBUG(("DMA-%d: clear flip/flop", ma_sl+1));
|
|
|
|
BX_DMA_THIS s[ma_sl].flip_flop = 0;
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2002-09-28 17:36:32 +04:00
|
|
|
case 0x0d: // DMA-1: master clear
|
|
|
|
case 0xda: // DMA-2: master clear
|
|
|
|
BX_DEBUG(("DMA-%d: master clear", ma_sl+1));
|
2001-12-18 16:12:45 +03:00
|
|
|
// writing any value to this port resets DMA controller 1 / 2
|
2001-04-10 05:04:59 +04:00
|
|
|
// same action as a hardware reset
|
|
|
|
// mask register is set (chan 0..3 disabled)
|
|
|
|
// command, status, request, temporary, and byte flip-flop are all cleared
|
2002-08-28 23:39:00 +04:00
|
|
|
reset_controller(ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0e: // DMA-1: clear mask register
|
|
|
|
case 0xdc: // DMA-2: clear mask register
|
|
|
|
BX_DEBUG(("DMA-%d: clear mask register", ma_sl+1));
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[0] = 0;
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[1] = 0;
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[2] = 0;
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[3] = 0;
|
2002-01-18 19:33:47 +03:00
|
|
|
control_HRQ(ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0f: // DMA-1: write all mask bits
|
|
|
|
case 0xde: // DMA-2: write all mask bits
|
|
|
|
BX_DEBUG(("DMA-%d: write all mask bits", ma_sl+1));
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[0] = value & 0x01; value >>= 1;
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[1] = value & 0x01; value >>= 1;
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[2] = value & 0x01; value >>= 1;
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[3] = value & 0x01;
|
2002-01-18 19:33:47 +03:00
|
|
|
control_HRQ(ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x81: /* DMA-1 page register, channel 2 */
|
|
|
|
case 0x82: /* DMA-1 page register, channel 3 */
|
|
|
|
case 0x83: /* DMA-1 page register, channel 1 */
|
|
|
|
case 0x87: /* DMA-1 page register, channel 0 */
|
2001-04-10 05:04:59 +04:00
|
|
|
/* address bits A16-A23 for DMA channel */
|
|
|
|
channel = channelindex[address - 0x81];
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[0].chan[channel].page_reg = value;
|
|
|
|
BX_DEBUG(("DMA-1: page register %d = %02x", channel, (unsigned) value));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x89: /* DMA-2 page register, channel 2 */
|
2002-06-16 19:02:28 +04:00
|
|
|
case 0x8a: /* DMA-2 page register, channel 3 */
|
|
|
|
case 0x8b: /* DMA-2 page register, channel 1 */
|
|
|
|
case 0x8f: /* DMA-2 page register, channel 0 */
|
2001-12-18 16:12:45 +03:00
|
|
|
/* address bits A16-A23 for DMA channel */
|
|
|
|
channel = channelindex[address - 0x89];
|
|
|
|
BX_DMA_THIS s[1].chan[channel].page_reg = value;
|
|
|
|
BX_DEBUG(("DMA-2: page register %d = %02x", channel + 4, (unsigned) value));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
2005-04-07 01:09:25 +04:00
|
|
|
case 0x0080:
|
2001-12-18 16:12:45 +03:00
|
|
|
case 0x0084:
|
|
|
|
case 0x0085:
|
|
|
|
case 0x0086:
|
|
|
|
case 0x0088:
|
2002-06-16 19:02:28 +04:00
|
|
|
case 0x008c:
|
|
|
|
case 0x008d:
|
|
|
|
case 0x008e:
|
2005-04-07 01:09:25 +04:00
|
|
|
BX_DEBUG(("write: extra page register 0x%04x (unused)", (unsigned) address));
|
|
|
|
BX_DMA_THIS ext_page_reg[address & 0x0f] = value;
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_ERROR(("write ignored: %04xh = %02xh",
|
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
|
|
|
(unsigned) address, (unsigned) value));
|
2005-04-05 21:57:32 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::set_DRQ(unsigned channel, bx_bool val)
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
|
|
|
Bit32u dma_base, dma_roof;
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool ma_sl;
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2002-06-16 19:02:28 +04:00
|
|
|
if (channel > 7) {
|
|
|
|
BX_PANIC(("set_DRQ() channel > 7"));
|
|
|
|
return;
|
|
|
|
}
|
2001-12-18 16:12:45 +03:00
|
|
|
ma_sl = (channel > 3);
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS s[ma_sl].DRQ[channel & 0x03] = val;
|
|
|
|
if (!BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used) {
|
|
|
|
BX_PANIC(("set_DRQ(): channel %d not connected to device", channel));
|
|
|
|
return;
|
|
|
|
}
|
2001-12-18 16:12:45 +03:00
|
|
|
channel &= 0x03;
|
2001-04-10 05:04:59 +04:00
|
|
|
if (!val) {
|
|
|
|
// clear bit in status reg
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4));
|
2002-01-05 13:28:49 +03:00
|
|
|
|
2002-01-18 19:33:47 +03:00
|
|
|
control_HRQ(ma_sl);
|
2002-01-05 13:28:49 +03:00
|
|
|
return;
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_SINGLE) &&
|
|
|
|
(BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_DEMAND) &&
|
|
|
|
(BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_CASCADE))
|
|
|
|
{
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_PANIC(("set_DRQ: mode_type(%02x) not handled",
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type));
|
2008-02-16 01:05:43 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2002-01-05 13:28:49 +03:00
|
|
|
dma_base = (BX_DMA_THIS s[ma_sl].chan[channel].page_reg << 16) |
|
|
|
|
(BX_DMA_THIS s[ma_sl].chan[channel].base_address << ma_sl);
|
2002-01-13 20:06:33 +03:00
|
|
|
if (BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement==0) {
|
|
|
|
dma_roof = dma_base + (BX_DMA_THIS s[ma_sl].chan[channel].base_count << ma_sl);
|
|
|
|
} else {
|
|
|
|
dma_roof = dma_base - (BX_DMA_THIS s[ma_sl].chan[channel].base_count << ma_sl);
|
|
|
|
}
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((dma_base & (0x7fff0000 << ma_sl)) != (dma_roof & (0x7fff0000 << ma_sl))) {
|
2012-03-20 22:29:49 +04:00
|
|
|
BX_INFO(("dma_base = 0x%08x", dma_base));
|
|
|
|
BX_INFO(("dma_base_count = 0x%08x", BX_DMA_THIS s[ma_sl].chan[channel].base_count));
|
|
|
|
BX_INFO(("dma_roof = 0x%08x", dma_roof));
|
2002-01-05 13:28:49 +03:00
|
|
|
BX_PANIC(("request outside %dk boundary", 64 << ma_sl));
|
2001-05-25 22:44:38 +04:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2002-01-18 19:33:47 +03:00
|
|
|
control_HRQ(ma_sl);
|
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::control_HRQ(bx_bool ma_sl)
|
2002-01-18 19:33:47 +03:00
|
|
|
{
|
|
|
|
unsigned channel;
|
|
|
|
|
2005-04-05 21:57:32 +04:00
|
|
|
// do nothing if controller is disabled
|
|
|
|
if (BX_DMA_THIS s[ma_sl].ctrl_disabled)
|
|
|
|
return;
|
|
|
|
|
2002-01-18 19:33:47 +03:00
|
|
|
// deassert HRQ if no DRQ is pending
|
|
|
|
if ((BX_DMA_THIS s[ma_sl].status_reg & 0xf0) == 0) {
|
|
|
|
if (ma_sl) {
|
|
|
|
bx_pc_system.set_HRQ(0);
|
|
|
|
} else {
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS set_DRQ(4, 0);
|
2002-01-18 19:33:47 +03:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// find highest priority channel
|
|
|
|
for (channel=0; channel<4; channel++) {
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((BX_DMA_THIS s[ma_sl].status_reg & (1 << (channel+4))) &&
|
|
|
|
(BX_DMA_THIS s[ma_sl].mask[channel]==0)) {
|
2002-01-18 19:33:47 +03:00
|
|
|
if (ma_sl) {
|
|
|
|
// assert Hold ReQuest line to CPU
|
|
|
|
bx_pc_system.set_HRQ(1);
|
|
|
|
} else {
|
|
|
|
// send DRQ to cascade channel of the master
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS set_DRQ(4, 1);
|
2002-01-18 19:33:47 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2002-01-02 13:00:54 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
2006-03-06 22:23:13 +03:00
|
|
|
void bx_dma_c::raise_HLDA(void)
|
2001-04-10 05:04:59 +04:00
|
|
|
{
|
|
|
|
unsigned channel;
|
2007-10-09 23:49:23 +04:00
|
|
|
bx_phy_address phy_addr;
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool ma_sl = 0;
|
2012-07-13 01:20:46 +04:00
|
|
|
Bit16u maxlen, len = 1;
|
2012-07-14 18:20:36 +04:00
|
|
|
Bit8u buffer[BX_DMA_BUFFER_SIZE];
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS HLDA = 1;
|
2001-04-10 05:04:59 +04:00
|
|
|
// find highest priority channel
|
|
|
|
for (channel=0; channel<4; channel++) {
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((BX_DMA_THIS s[1].status_reg & (1 << (channel+4))) &&
|
|
|
|
(BX_DMA_THIS s[1].mask[channel]==0)) {
|
2002-01-02 13:00:54 +03:00
|
|
|
ma_sl = 1;
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
}
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2002-01-02 13:00:54 +03:00
|
|
|
if (channel == 0) { // master cascade channel
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS s[1].DACK[0] = 1;
|
2001-12-18 16:12:45 +03:00
|
|
|
for (channel=0; channel<4; channel++) {
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((BX_DMA_THIS s[0].status_reg & (1 << (channel+4))) &&
|
|
|
|
(BX_DMA_THIS s[0].mask[channel]==0)) {
|
2002-01-02 13:00:54 +03:00
|
|
|
ma_sl = 0;
|
2001-12-18 16:12:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-12-18 16:12:45 +03:00
|
|
|
if (channel >= 4) {
|
2002-01-05 13:28:49 +03:00
|
|
|
// wait till they're unmasked
|
2001-04-10 05:04:59 +04:00
|
|
|
return;
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
phy_addr = (BX_DMA_THIS s[ma_sl].chan[channel].page_reg << 16) |
|
|
|
|
(BX_DMA_THIS s[ma_sl].chan[channel].current_address << ma_sl);
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2012-07-13 01:20:46 +04:00
|
|
|
if (!BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement) {
|
2012-07-14 18:20:36 +04:00
|
|
|
maxlen = (BX_DMA_THIS s[ma_sl].chan[channel].current_count + 1) << ma_sl;
|
|
|
|
BX_DMA_THIS TC = (maxlen <= BX_DMA_BUFFER_SIZE);
|
|
|
|
if (maxlen > BX_DMA_BUFFER_SIZE) {
|
|
|
|
maxlen = BX_DMA_BUFFER_SIZE;
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2012-07-13 01:20:46 +04:00
|
|
|
} else {
|
|
|
|
BX_DMA_THIS TC = (BX_DMA_THIS s[ma_sl].chan[channel].current_count == 0);
|
2012-07-14 18:20:36 +04:00
|
|
|
maxlen = 1 << ma_sl;
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 1) { // write
|
2012-07-14 18:20:36 +04:00
|
|
|
// DMA controlled xfer of bytes from I/O to Memory
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
if (!ma_sl) {
|
2002-06-16 19:02:28 +04:00
|
|
|
if (BX_DMA_THIS h[channel].dmaWrite8)
|
2012-07-13 01:20:46 +04:00
|
|
|
len = BX_DMA_THIS h[channel].dmaWrite8(buffer, maxlen);
|
2002-06-16 19:02:28 +04:00
|
|
|
else
|
|
|
|
BX_PANIC(("no dmaWrite handler for channel %u.", channel));
|
|
|
|
|
2012-07-13 01:20:46 +04:00
|
|
|
DEV_MEM_WRITE_PHYSICAL_DMA(phy_addr, len, buffer);
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2012-07-14 18:20:36 +04:00
|
|
|
BX_DBG_DMA_REPORT(phy_addr, len, BX_WRITE, buffer[0]); // FIXME
|
2012-07-13 01:20:46 +04:00
|
|
|
} else {
|
2002-06-16 19:02:28 +04:00
|
|
|
if (BX_DMA_THIS h[channel].dmaWrite16)
|
2012-07-14 18:20:36 +04:00
|
|
|
len = BX_DMA_THIS h[channel].dmaWrite16((Bit16u*)buffer, maxlen / 2);
|
2002-06-16 19:02:28 +04:00
|
|
|
else
|
|
|
|
BX_PANIC(("no dmaWrite handler for channel %u.", channel));
|
|
|
|
|
2012-07-14 18:20:36 +04:00
|
|
|
DEV_MEM_WRITE_PHYSICAL_DMA(phy_addr, len, buffer);
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2012-07-14 18:20:36 +04:00
|
|
|
BX_DBG_DMA_REPORT(phy_addr, len * 2, BX_WRITE, buffer[0] | (buffer[1] << 16)); // FIXME
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2012-07-13 01:20:46 +04:00
|
|
|
} else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 2) { // read
|
2012-07-14 18:20:36 +04:00
|
|
|
// DMA controlled xfer of bytes from Memory to I/O
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2001-12-18 16:12:45 +03:00
|
|
|
if (!ma_sl) {
|
2012-07-13 01:20:46 +04:00
|
|
|
DEV_MEM_READ_PHYSICAL_DMA(phy_addr, maxlen, buffer);
|
2002-06-16 19:02:28 +04:00
|
|
|
|
|
|
|
if (BX_DMA_THIS h[channel].dmaRead8)
|
2012-07-13 01:20:46 +04:00
|
|
|
len = BX_DMA_THIS h[channel].dmaRead8(buffer, maxlen);
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2012-07-14 18:20:36 +04:00
|
|
|
BX_DBG_DMA_REPORT(phy_addr, len, BX_READ, buffer[0]); // FIXME
|
2012-07-13 01:20:46 +04:00
|
|
|
} else {
|
2012-07-14 18:20:36 +04:00
|
|
|
DEV_MEM_READ_PHYSICAL_DMA(phy_addr, maxlen, buffer);
|
2002-06-16 19:02:28 +04:00
|
|
|
|
|
|
|
if (BX_DMA_THIS h[channel].dmaRead16)
|
2012-07-14 18:20:36 +04:00
|
|
|
len = BX_DMA_THIS h[channel].dmaRead16((Bit16u*)buffer, maxlen / 2);
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2012-07-14 18:20:36 +04:00
|
|
|
BX_DBG_DMA_REPORT(phy_addr, len * 2, BX_READ, buffer[0] | (buffer[1] << 16)); // FIXME
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2012-07-13 01:20:46 +04:00
|
|
|
} else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 0) {
|
2001-12-26 17:56:15 +03:00
|
|
|
// verify
|
2002-06-16 19:02:28 +04:00
|
|
|
|
2001-12-26 17:56:15 +03:00
|
|
|
if (!ma_sl) {
|
2002-06-16 19:02:28 +04:00
|
|
|
if (BX_DMA_THIS h[channel].dmaWrite8)
|
2012-07-13 01:20:46 +04:00
|
|
|
len = BX_DMA_THIS h[channel].dmaWrite8(buffer, 1);
|
2002-06-16 19:02:28 +04:00
|
|
|
else
|
|
|
|
BX_PANIC(("no dmaWrite handler for channel %u.", channel));
|
2012-07-13 01:20:46 +04:00
|
|
|
} else {
|
2002-06-16 19:02:28 +04:00
|
|
|
if (BX_DMA_THIS h[channel].dmaWrite16)
|
2012-07-14 18:20:36 +04:00
|
|
|
len = BX_DMA_THIS h[channel].dmaWrite16((Bit16u*)buffer, 1);
|
2002-06-16 19:02:28 +04:00
|
|
|
else
|
|
|
|
BX_PANIC(("no dmaWrite handler for channel %u.", channel));
|
2001-12-26 17:56:15 +03:00
|
|
|
}
|
2012-07-13 01:20:46 +04:00
|
|
|
} else {
|
2001-12-26 17:56:15 +03:00
|
|
|
BX_PANIC(("hlda: transfer_type 3 is undefined"));
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2012-07-13 01:20:46 +04:00
|
|
|
BX_DMA_THIS s[ma_sl].DACK[channel] = 1;
|
|
|
|
// check for expiration of count, so we can signal TC and DACK(n)
|
|
|
|
// at the same time.
|
|
|
|
if (!BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement)
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_address += len;
|
|
|
|
else
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_address--;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_count -= len;
|
|
|
|
if (BX_DMA_THIS s[ma_sl].chan[channel].current_count == 0xffff) {
|
|
|
|
// count expired, done with transfer
|
|
|
|
// assert TC, deassert HRQ & DACK(n) lines
|
|
|
|
BX_DMA_THIS s[ma_sl].status_reg |= (1 << channel); // hold TC in status reg
|
|
|
|
if (BX_DMA_THIS s[ma_sl].chan[channel].mode.autoinit_enable == 0) {
|
|
|
|
// set mask bit if not in autoinit mode
|
|
|
|
BX_DMA_THIS s[ma_sl].mask[channel] = 1;
|
|
|
|
} else {
|
|
|
|
// count expired, but in autoinit mode
|
|
|
|
// reload count and base address
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_address =
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].base_address;
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].current_count =
|
|
|
|
BX_DMA_THIS s[ma_sl].chan[channel].base_count;
|
|
|
|
}
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS TC = 0; // clear TC, adapter card already notified
|
|
|
|
BX_DMA_THIS HLDA = 0;
|
2001-04-10 05:04:59 +04:00
|
|
|
bx_pc_system.set_HRQ(0); // clear HRQ to CPU
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS s[ma_sl].DACK[channel] = 0; // clear DACK to adapter card
|
2002-01-02 13:00:54 +03:00
|
|
|
if (!ma_sl) {
|
2002-06-16 19:02:28 +04:00
|
|
|
BX_DMA_THIS set_DRQ(4, 0); // clear DRQ to cascade
|
|
|
|
BX_DMA_THIS s[1].DACK[0] = 0; // clear DACK to cascade
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2006-03-06 22:23:13 +03:00
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2012-03-15 22:12:13 +04:00
|
|
|
|
|
|
|
#if BX_DEBUGGER
|
2012-04-23 21:06:19 +04:00
|
|
|
void bx_dma_c::debug_dump(int argc, char **argv)
|
2012-03-15 22:12:13 +04:00
|
|
|
{
|
|
|
|
int ch, i, j;
|
|
|
|
|
|
|
|
dbg_printf("i8237A DMA controller\n\n");
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
if (BX_DMA_THIS s[i].chan[j].used) {
|
|
|
|
ch = i * 4 + j;
|
|
|
|
dbg_printf("DMA channel %d", ch);
|
|
|
|
if (ch == 4) {
|
|
|
|
dbg_printf(" (cascade)\n");
|
2012-03-20 22:29:49 +04:00
|
|
|
} else if (BX_DMA_THIS s[i].DRQ[j] && !BX_DMA_THIS s[i].mask[j]) {
|
2012-03-15 22:12:13 +04:00
|
|
|
dbg_printf(" (active)\n");
|
|
|
|
dbg_printf(" address: base=0x%04x, current=0x%04x\n", BX_DMA_THIS s[i].chan[j].base_address,
|
|
|
|
BX_DMA_THIS s[i].chan[j].current_address);
|
|
|
|
dbg_printf(" count: base=0x%04x, current=0x%04x\n", BX_DMA_THIS s[i].chan[j].base_count,
|
|
|
|
BX_DMA_THIS s[i].chan[j].current_count);
|
|
|
|
dbg_printf(" page: 0x%02x\n", BX_DMA_THIS s[i].chan[j].page_reg);
|
|
|
|
dbg_printf(" mask: %u\n", BX_DMA_THIS s[i].mask[j]);
|
|
|
|
dbg_printf(" flip_flop: %u\n", BX_DMA_THIS s[i].flip_flop);
|
|
|
|
dbg_printf(" status_reg: 0x%02x\n", BX_DMA_THIS s[i].status_reg);
|
|
|
|
dbg_printf(" mode_type: %u\n", BX_DMA_THIS s[i].chan[j].mode.mode_type);
|
|
|
|
dbg_printf(" address_decrement: %u\n", BX_DMA_THIS s[i].chan[j].mode.address_decrement);
|
|
|
|
dbg_printf(" autoinit_enable: %u\n", BX_DMA_THIS s[i].chan[j].mode.autoinit_enable);
|
|
|
|
dbg_printf(" transfer_type: %u\n", BX_DMA_THIS s[i].chan[j].mode.transfer_type);
|
|
|
|
} else {
|
2012-03-20 22:29:49 +04:00
|
|
|
dbg_printf(" (not active: DRQ=%u, mask=%u)\n", BX_DMA_THIS s[i].DRQ[j],
|
|
|
|
BX_DMA_THIS s[i].mask[j]);
|
2012-03-15 22:12:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-23 21:06:19 +04:00
|
|
|
if (argc > 0) {
|
|
|
|
dbg_printf("\nAdditional options not supported\n");
|
|
|
|
}
|
2012-03-15 22:12:13 +04:00
|
|
|
}
|
|
|
|
#endif
|