diff options
Diffstat (limited to 'tools/ioemu/iodev/dma.cc')
-rw-r--r-- | tools/ioemu/iodev/dma.cc | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/dma.cc b/tools/ioemu/iodev/dma.cc new file mode 100644 index 0000000000..afe0238a92 --- /dev/null +++ b/tools/ioemu/iodev/dma.cc @@ -0,0 +1,825 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: dma.cc,v 1.30 2003/07/31 15:29:34 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.com/ +// +// 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 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + +// 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 "bochs.h" + +#define LOG_THIS theDmaDevice-> + +#define DMA_MODE_DEMAND 0 +#define DMA_MODE_SINGLE 1 +#define DMA_MODE_BLOCK 2 +#define DMA_MODE_CASCADE 3 + +bx_dma_c *theDmaDevice = NULL; + + int +libdma_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theDmaDevice = new bx_dma_c (); + bx_devices.pluginDmaDevice = theDmaDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theDmaDevice, BX_PLUGIN_DMA); + return(0); // Success +} + + void +libdma_LTX_plugin_fini(void) +{ +} + +bx_dma_c::bx_dma_c(void) +{ + put("DMA"); + settype(DMALOG); +} + +bx_dma_c::~bx_dma_c(void) +{ + BX_DEBUG(("Exit.")); +} + + unsigned +bx_dma_c::registerDMA8Channel( + unsigned channel, + void (* dmaRead)(Bit8u *data_byte), + void (* dmaWrite)(Bit8u *data_byte), + const char *name + ) +{ + if (channel > 3) { + BX_PANIC(("registerDMA8Channel: invalid channel number(%u).", channel)); + return 0; // Fail. + } + if (BX_DMA_THIS s[0].chan[channel].used) { + BX_PANIC(("registerDMA8Channel: channel(%u) already in use.", channel)); + return 0; // Fail. + } + BX_INFO(("channel %u used by %s", channel, name)); + BX_DMA_THIS h[channel].dmaRead8 = dmaRead; + BX_DMA_THIS h[channel].dmaWrite8 = dmaWrite; + BX_DMA_THIS s[0].chan[channel].used = 1; + return 1; // OK. +} + + unsigned +bx_dma_c::registerDMA16Channel( + unsigned channel, + void (* dmaRead)(Bit16u *data_word), + void (* dmaWrite)(Bit16u *data_word), + const char *name + ) +{ + if ((channel < 4) || (channel > 7)) { + BX_PANIC(("registerDMA16Channel: invalid channel number(%u).", channel)); + return 0; // Fail. + } + if (BX_DMA_THIS s[1].chan[channel & 0x03].used) { + BX_PANIC(("registerDMA16Channel: channel(%u) already in use.", channel)); + return 0; // Fail. + } + BX_INFO(("channel %u used by %s", channel, name)); + channel &= 0x03; + BX_DMA_THIS h[channel].dmaRead16 = dmaRead; + BX_DMA_THIS h[channel].dmaWrite16 = dmaWrite; + BX_DMA_THIS s[1].chan[channel].used = 1; + return 1; // OK. +} + + unsigned +bx_dma_c::unregisterDMAChannel(unsigned channel) +{ + bx_bool ma_sl = (channel > 3); + BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used = 0; + BX_INFO(("channel %u no longer used", channel)); + return 1; +} + + unsigned +bx_dma_c::get_TC(void) +{ + return BX_DMA_THIS TC; +} + + + void +bx_dma_c::init(void) +{ + unsigned c, i, j; + BX_DEBUG(("Init $Id: dma.cc,v 1.30 2003/07/31 15:29:34 vruppert Exp $")); + + /* 8237 DMA controller */ + + for (i=0; i < 2; i++) { + for (j=0; j < 4; j++) { + BX_DMA_THIS s[i].DRQ[j] = 0; + BX_DMA_THIS s[i].DACK[j] = 0; + } + } + BX_DMA_THIS HLDA = 0; + BX_DMA_THIS TC = 0; + + // 0000..000F + for (i=0x0000; i<=0x000F; i++) { + DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1); + DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3); + } + + // 00081..008F + for (i=0x0081; i<=0x008F; i++) { + DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1); + DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3); + } + + // 000C0..00DE + for (i=0x00C0; i<=0x00DE; i+=2) { + DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1); + DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3); + } + + + 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; + BX_DMA_THIS s[i].chan[c].used = 0; + } + } + BX_DMA_THIS s[1].chan[0].used = 1; // cascade channel in use + BX_INFO(("channel 4 used by cascade")); + bios_init(); +} + +/* Remove it when guest fw ready*/ + void +bx_dma_c::bios_init(void){ + BX_DMA_THIS s[1].mask[0] = 0; // unmask cascade channel + BX_DMA_THIS s[1].chan[0].mode.mode_type = 3; // cascade mode for channel 4 +} + + void +bx_dma_c::reset(unsigned type) +{ + reset_controller(0); + reset_controller(1); + bios_init(); +} + + void +bx_dma_c::reset_controller(unsigned num) +{ + 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; + BX_DMA_THIS s[num].command_reg = 0; + BX_DMA_THIS s[num].status_reg = 0; + BX_DMA_THIS s[num].request_reg = 0; + BX_DMA_THIS s[num].temporary_reg = 0; + BX_DMA_THIS s[num].flip_flop = 0; +} + + // index to find channel from register number (only [0],[1],[2],[6] used) + Bit8u channelindex[7] = {2, 3, 1, 0, 0, 0, 0}; + + + // 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) +{ +#if !BX_USE_DMA_SMF + bx_dma_c *class_ptr = (bx_dma_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + /* 8237 DMA controller */ + Bit32u BX_CPP_AttrRegparmN(2) +bx_dma_c::read( Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_DMA_SMF + + Bit8u retval; + Bit8u channel; + bx_bool ma_sl; + + BX_DEBUG(("read addr=%04x", (unsigned) address)); + +#if BX_DMA_FLOPPY_IO < 1 + /* if we're not supporting DMA/floppy IO just return a bogus value */ + return(0xff); +#endif + + 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 */ + 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 */ + ma_sl = (address >= 0xc0); + 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; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_address & 0xff); + } + else { + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_address >> 8); + } + + 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 */ + 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 */ + ma_sl = (address >= 0xc2); + 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; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_count & 0xff); + } + else { + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_count >> 8); + } + + case 0x08: // DMA-1 Status Register + case 0xd0: // DMA-2 Status Register + // 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) + ma_sl = (address == 0xd0); + retval = BX_DMA_THIS s[ma_sl].status_reg; + BX_DMA_THIS s[ma_sl].status_reg &= 0xf0; + return(retval); + break; + case 0x0d: // DMA-1: temporary register + case 0xda: // DMA-2: temporary register + ma_sl = (address == 0xda); + BX_ERROR(("DMA-%d: read of temporary register", ma_sl+1)); + // Note: write to 0x0D clears temporary register + return(0); + break; + + 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]; + return( BX_DMA_THIS s[0].chan[channel].page_reg ); + + 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]; + return( BX_DMA_THIS s[1].chan[channel].page_reg ); + + case 0x0084: + case 0x0085: + case 0x0086: + case 0x0088: + case 0x008c: + case 0x008d: + case 0x008e: + BX_DEBUG(("read: extra page register 0x%04x unsupported", (unsigned) address)); + return(0); + + default: + BX_ERROR(("read: unsupported address=%04x", (unsigned) address)); + return(0); + } +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_dma_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#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 */ + void BX_CPP_AttrRegparmN(3) +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; + bx_bool ma_sl; + + if (io_len > 1) { + if ( (io_len == 2) && (address == 0x0b) ) { +#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; + } + + BX_ERROR(("io write to address %08x, len=%u", + (unsigned) address, (unsigned) io_len)); + return; + } + + BX_DEBUG(("write: address=%04x value=%02x", + (unsigned) address, (unsigned) value)); + +#if BX_DMA_FLOPPY_IO < 1 + /* if we're not supporting DMA/floppy IO just return */ + return; +#endif + + switch (address) { + case 0x00: + case 0x02: + case 0x04: + case 0x06: + case 0xc0: + case 0xc4: + case 0xc8: + case 0xcc: + ma_sl = (address >= 0xc0); + 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; + } + else { /* 2nd byte */ + BX_DMA_THIS s[ma_sl].chan[channel].base_address |= (value << 8); + BX_DMA_THIS s[ma_sl].chan[channel].current_address |= (value << 8); + BX_DEBUG((" base = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_address)); + BX_DEBUG((" curr = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].current_address)); + } + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return; + break; + + case 0x01: + case 0x03: + case 0x05: + case 0x07: + case 0xc2: + case 0xc6: + case 0xca: + case 0xce: + ma_sl = (address >= 0xc2); + 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; + } + else { /* 2nd byte */ + BX_DMA_THIS s[ma_sl].chan[channel].base_count |= (value << 8); + BX_DMA_THIS s[ma_sl].chan[channel].current_count |= (value << 8); + BX_DEBUG((" base = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_count)); + BX_DEBUG((" curr = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].current_count)); + } + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return; + break; + + case 0x08: /* DMA-1: command register */ + case 0xd0: /* DMA-2: command register */ + ma_sl = (address == 0xd0); + if (value != 0x00) + BX_ERROR(("write to command register: value(%02xh) not 0x00", + (unsigned) value)); + BX_DMA_THIS s[ma_sl].command_reg = value; + return; + break; + + case 0x09: // DMA-1: request register + case 0xd2: // DMA-2: request register + ma_sl = (address == 0xd2); + channel = value & 0x03; + BX_ERROR(("DMA-%d: write to request register (%02x)", ma_sl+1, (unsigned) value)); + // note: write to 0x0d clears this register + if (value & 0x04) { + // set request bit + BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4)); + BX_DEBUG(("DMA-%d: set request bit for channel %u", ma_sl+1, (unsigned) channel)); + } + else { + // clear request bit + BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4)); + BX_DEBUG(("DMA-%d: cleared request bit for channel %u", ma_sl+1, (unsigned) channel)); + } + control_HRQ(ma_sl); + return; + break; + + case 0x0a: + case 0xd4: + ma_sl = (address == 0xd4); + set_mask_bit = value & 0x04; + channel = value & 0x03; + 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, + (unsigned) set_mask_bit, (unsigned) channel, (unsigned) BX_DMA_THIS s[ma_sl].mask[channel])); + control_HRQ(ma_sl); + return; + break; + + case 0x0b: /* DMA-1 mode register */ + case 0xd6: /* DMA-2 mode register */ + ma_sl = (address == 0xd6); + channel = value & 0x03; + 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; + BX_DEBUG(("DMA-%d: mode register[%u] = %02x", ma_sl+1, + (unsigned) channel, (unsigned) value)); + return; + break; + + case 0x0c: /* DMA-1 clear byte flip/flop */ + case 0xd8: /* DMA-2 clear byte flip/flop */ + ma_sl = (address == 0xd8); + BX_DEBUG(("DMA-%d: clear flip/flop", ma_sl+1)); + BX_DMA_THIS s[ma_sl].flip_flop = 0; + return; + break; + + case 0x0d: // DMA-1: master clear + case 0xda: // DMA-2: master clear + ma_sl = (address == 0xda); + BX_DEBUG(("DMA-%d: master clear", ma_sl+1)); + // writing any value to this port resets DMA controller 1 / 2 + // 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 + reset_controller(ma_sl); + return; + break; + + case 0x0e: // DMA-1: clear mask register + case 0xdc: // DMA-2: clear mask register + ma_sl = (address == 0xdc); + 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; + control_HRQ(ma_sl); + return; + break; + + case 0x0f: // DMA-1: write all mask bits + case 0xde: // DMA-2: write all mask bits + ma_sl = (address == 0xde); + 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; + control_HRQ(ma_sl); + return; + break; + + 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 */ + /* address bits A16-A23 for DMA channel */ + channel = channelindex[address - 0x81]; + BX_DMA_THIS s[0].chan[channel].page_reg = value; + BX_DEBUG(("DMA-1: page register %d = %02x", channel, (unsigned) value)); + return; + break; + + case 0x89: /* DMA-2 page register, channel 2 */ + case 0x8a: /* DMA-2 page register, channel 3 */ + case 0x8b: /* DMA-2 page register, channel 1 */ + case 0x8f: /* DMA-2 page register, channel 0 */ + /* 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)); + return; + break; + + case 0x0084: + case 0x0085: + case 0x0086: + case 0x0088: + case 0x008c: + case 0x008d: + case 0x008e: + BX_DEBUG(("write: extra page register 0x%04x unsupported", (unsigned) address)); + return; + break; + + default: + BX_ERROR(("write ignored: %04xh = %02xh", + (unsigned) address, (unsigned) value)); + } +} + + void +bx_dma_c::set_DRQ(unsigned channel, bx_bool val) +{ + Bit32u dma_base, dma_roof; + bx_bool ma_sl; + + if (channel > 7) { + BX_PANIC(("set_DRQ() channel > 7")); + return; + } + ma_sl = (channel > 3); + 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; + } + channel &= 0x03; + if (!val) { + //BX_DEBUG(("bx_dma_c::DRQ(): val == 0")); + // clear bit in status reg + BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4)); + + control_HRQ(ma_sl); + return; + } + +#if 0 + BX_INFO(("mask[%d]: %02x", channel, (unsigned) BX_DMA_THIS s[0].mask[channel])); + BX_INFO(("flip_flop: %u", (unsigned) BX_DMA_THIS s[0].flip_flop)); + BX_INFO(("status_reg: %02x", (unsigned) BX_DMA_THIS s[0].status_reg)); + BX_INFO(("mode_type: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.mode_type)); + BX_INFO(("address_decrement: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.address_decrement)); + BX_INFO(("autoinit_enable: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.autoinit_enable)); + BX_INFO(("transfer_type: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.transfer_type)); + BX_INFO(("base_address: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].base_address)); + BX_INFO(("current_address: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].current_address)); + BX_INFO(("base_count: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].base_count)); + BX_INFO(("current_count: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].current_count)); + BX_INFO(("page_reg: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].page_reg)); +#endif + + BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4)); + + 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) ) + BX_PANIC(("set_DRQ: mode_type(%02x) not handled", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type)); + + 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); + 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); + } + if ( (dma_base & (0x7fff0000 << ma_sl)) != (dma_roof & (0x7fff0000 << ma_sl)) ) { + BX_INFO(("dma_base = %08x", (unsigned) dma_base)); + BX_INFO(("dma_base_count = %08x", (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_count)); + BX_INFO(("dma_roof = %08x", (unsigned) dma_roof)); + BX_PANIC(("request outside %dk boundary", 64 << ma_sl)); + } + + control_HRQ(ma_sl); +} + + void +bx_dma_c::control_HRQ(bx_bool ma_sl) +{ + unsigned channel; + + // 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 { + BX_DMA_THIS set_DRQ(4, 0); + } + return; + } + // find highest priority channel + for (channel=0; channel<4; channel++) { + if ( (BX_DMA_THIS s[ma_sl].status_reg & (1 << (channel+4))) && + (BX_DMA_THIS s[ma_sl].mask[channel]==0) ) { + if (ma_sl) { + // assert Hold ReQuest line to CPU + bx_pc_system.set_HRQ(1); + } else { + // send DRQ to cascade channel of the master + BX_DMA_THIS set_DRQ(4, 1); + } + break; + } + } +} + + void +bx_dma_c::raise_HLDA(void) +{ + unsigned channel; + Bit32u phy_addr; + bx_bool count_expired = 0; + bx_bool ma_sl = 0; + + BX_DMA_THIS HLDA = 1; + // find highest priority channel + for (channel=0; channel<4; channel++) { + if ( (BX_DMA_THIS s[1].status_reg & (1 << (channel+4))) && + (BX_DMA_THIS s[1].mask[channel]==0) ) { + ma_sl = 1; + break; + } + } + if (channel == 0) { // master cascade channel + BX_DMA_THIS s[1].DACK[0] = 1; + for (channel=0; channel<4; channel++) { + if ( (BX_DMA_THIS s[0].status_reg & (1 << (channel+4))) && + (BX_DMA_THIS s[0].mask[channel]==0) ) { + ma_sl = 0; + break; + } + } + } + if (channel >= 4) { + // wait till they're unmasked + return; + } + + //BX_DEBUG(("hlda: OK in response to DRQ(%u)", (unsigned) channel)); + 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); + + 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==0) + BX_DMA_THIS s[ma_sl].chan[channel].current_address++; + else + BX_DMA_THIS s[ma_sl].chan[channel].current_address--; + BX_DMA_THIS s[ma_sl].chan[channel].current_count--; + 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 + BX_DMA_THIS TC = 1; + count_expired = 1; + 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; + } + } + + Bit8u data_byte; + Bit16u data_word; + + if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 1) { // write + // DMA controlled xfer of byte from I/O to Memory + + if (!ma_sl) { + if (BX_DMA_THIS h[channel].dmaWrite8) + BX_DMA_THIS h[channel].dmaWrite8(&data_byte); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + + BX_MEM_WRITE_PHYSICAL(phy_addr, 1, &data_byte); + + BX_DBG_DMA_REPORT(phy_addr, 1, BX_WRITE, data_byte); + } + else { + if (BX_DMA_THIS h[channel].dmaWrite16) + BX_DMA_THIS h[channel].dmaWrite16(&data_word); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + + BX_MEM_WRITE_PHYSICAL(phy_addr, 2, &data_word); + + BX_DBG_DMA_REPORT(phy_addr, 2, BX_WRITE, data_word); + } + } + else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 2) { // read + // DMA controlled xfer of byte from Memory to I/O + + if (!ma_sl) { + BX_MEM_READ_PHYSICAL(phy_addr, 1, &data_byte); + + if (BX_DMA_THIS h[channel].dmaRead8) + BX_DMA_THIS h[channel].dmaRead8(&data_byte); + + BX_DBG_DMA_REPORT(phy_addr, 1, BX_READ, data_byte); + } + else { + BX_MEM_READ_PHYSICAL(phy_addr, 2, &data_word); + + if (BX_DMA_THIS h[channel].dmaRead16) + BX_DMA_THIS h[channel].dmaRead16(&data_word); + + BX_DBG_DMA_REPORT(phy_addr, 2, BX_READ, data_word); + } + } + else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 0) { + // verify + + if (!ma_sl) { + if (BX_DMA_THIS h[channel].dmaWrite8) + BX_DMA_THIS h[channel].dmaWrite8(&data_byte); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + } + else { + if (BX_DMA_THIS h[channel].dmaWrite16) + BX_DMA_THIS h[channel].dmaWrite16(&data_word); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + } + } + else { + BX_PANIC(("hlda: transfer_type 3 is undefined")); + } + + if (count_expired) { + BX_DMA_THIS TC = 0; // clear TC, adapter card already notified + BX_DMA_THIS HLDA = 0; + bx_pc_system.set_HRQ(0); // clear HRQ to CPU + BX_DMA_THIS s[ma_sl].DACK[channel] = 0; // clear DACK to adapter card + if (!ma_sl) { + BX_DMA_THIS set_DRQ(4, 0); // clear DRQ to cascade + BX_DMA_THIS s[1].DACK[0] = 0; // clear DACK to cascade + } + } +} |