Files
86Box/src/dma.c

1918 lines
51 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the Intel DMA controllers.
*
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca.
* Copyright 2017-2020 Fred N. van Kempen.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include "cpu.h"
#include "x86.h"
#include <86box/machine.h>
#include <86box/mca.h>
#include <86box/mem.h>
#include <86box/io.h>
#include <86box/pic.h>
#include <86box/dma.h>
#include <86box/plat_unused.h>
dma_t dma[8];
uint8_t dma_e;
uint8_t dma_m;
static uint8_t dmaregs[3][16];
static int dma_wp[2];
static uint8_t dma_stat;
static uint8_t dma_stat_rq;
static uint8_t dma_stat_rq_pc;
static uint8_t dma_stat_adv_pend;
static uint8_t dma_command[2];
static uint8_t dma_req_is_soft;
static uint8_t dma_advanced;
static uint8_t dma_at;
static uint8_t dma_buffer[65536];
static uint16_t dma_sg_base;
static uint16_t dma16_buffer[65536];
static uint32_t dma_mask;
static struct dma_ps2_t {
int xfr_command;
int xfr_channel;
int byte_ptr;
int is_ps2;
} dma_ps2;
#define DMA_PS2_IOA (1 << 0)
#define DMA_PS2_AUTOINIT (1 << 1)
#define DMA_PS2_XFER_MEM_TO_IO (1 << 2)
#define DMA_PS2_XFER_IO_TO_MEM (3 << 2)
#define DMA_PS2_XFER_MASK (3 << 2)
#define DMA_PS2_DEC2 (1 << 4)
#define DMA_PS2_SIZE16 (1 << 6)
#ifdef ENABLE_DMA_LOG
int dma_do_log = ENABLE_DMA_LOG;
static void
dma_log(const char *fmt, ...)
{
va_list ap;
if (dma_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define dma_log(fmt, ...)
#endif
static void dma_ps2_run(int channel);
int
dma_get_drq(int channel)
{
return !!(dma_stat_rq_pc & (1 << channel));
}
void
dma_set_drq(int channel, int set)
{
dma_stat_rq_pc &= ~(1 << channel);
if (set)
dma_stat_rq_pc |= (1 << channel);
}
static int
dma_transfer_size(dma_t *dev)
{
return dev->transfer_mode & 0xff;
}
static void
dma_sg_next_addr(dma_t *dev)
{
int ts = dma_transfer_size(dev);
dma_bm_read(dev->ptr_cur, (uint8_t *) &(dev->addr), 4, ts);
dma_bm_read(dev->ptr_cur + 4, (uint8_t *) &(dev->count), 4, ts);
dma_log("DMA S/G DWORDs: %08X %08X\n", dev->addr, dev->count);
dev->eot = dev->count >> 31;
dev->count &= 0xfffe;
dev->cb = (uint16_t) dev->count;
dev->cc = dev->count;
if (!dev->count)
dev->count = 65536;
if (ts == 2)
dev->addr &= 0xfffffffe;
dev->ab = dev->addr & dma_mask;
dev->ac = dev->addr & dma_mask;
dev->page = dev->page_l = (dev->ac >> 16) & 0xff;
dev->page_h = (dev->ac >> 24) & 0xff;
dev->ptr_cur += 8;
}
static void
dma_block_transfer(int channel)
{
int bit16 = (channel >= 4);
if (dma_advanced)
bit16 = !!(dma_transfer_size(&(dma[channel])) == 2);
dma_req_is_soft = 1;
for (uint16_t i = 0; i <= dma[channel].cb; i++) {
if ((dma[channel].mode & 0x8c) == 0x84) {
if (bit16)
dma_channel_write(channel, dma16_buffer[i]);
else
dma_channel_write(channel, dma_buffer[i]);
} else if ((dma[channel].mode & 0x8c) == 0x88) {
if (bit16)
dma16_buffer[i] = dma_channel_read(channel);
else
dma_buffer[i] = dma_channel_read(channel);
}
}
dma_req_is_soft = 0;
}
static void
dma_mem_to_mem_transfer(void)
{
int i;
if ((dma[0].mode & 0x0c) != 0x08)
fatal("DMA memory to memory transfer: channel 0 mode not read\n");
if ((dma[1].mode & 0x0c) != 0x04)
fatal("DMA memory to memory transfer: channel 1 mode not write\n");
dma_req_is_soft = 1;
for (i = 0; i <= dma[0].cb; i++)
dma_buffer[i] = dma_channel_read(0);
for (i = 0; i <= dma[1].cb; i++)
dma_channel_write(1, dma_buffer[i]);
dma_req_is_soft = 0;
}
static void
dma_sg_write(uint16_t port, uint8_t val, void *priv)
{
dma_t *dev = (dma_t *) priv;
dma_log("DMA S/G BYTE write: %04X %02X\n", port, val);
port &= 0xff;
if (port < 0x20)
port &= 0xf8;
else
port &= 0xe3;
switch (port) {
case 0x00:
dma_log("DMA S/G Cmd : val = %02X, old = %02X\n", val, dev->sg_command);
if ((val & 1) && !(dev->sg_command & 1)) { /*Start*/
#ifdef ENABLE_DMA_LOG
dma_log("DMA S/G start\n");
#endif
dev->ptr_cur = dev->ptr;
dma_sg_next_addr(dev);
dev->sg_status = (dev->sg_status & 0xf7) | 0x01;
}
if (!(val & 1) && (dev->sg_command & 1)) { /*Stop*/
#ifdef ENABLE_DMA_LOG
dma_log("DMA S/G stop\n");
#endif
dev->sg_status &= ~0x81;
}
dev->sg_command = val;
break;
case 0x20:
dev->ptr = (dev->ptr & 0xffffff00) | (val & 0xfc);
dev->ptr %= (mem_size * 1024);
dev->ptr0 = val;
break;
case 0x21:
dev->ptr = (dev->ptr & 0xffff00fc) | (val << 8);
dev->ptr %= (mem_size * 1024);
break;
case 0x22:
dev->ptr = (dev->ptr & 0xff00fffc) | (val << 16);
dev->ptr %= (mem_size * 1024);
break;
case 0x23:
dev->ptr = (dev->ptr & 0x00fffffc) | (val << 24);
dev->ptr %= (mem_size * 1024);
break;
default:
break;
}
}
static void
dma_sg_writew(uint16_t port, uint16_t val, void *priv)
{
dma_t *dev = (dma_t *) priv;
dma_log("DMA S/G WORD write: %04X %04X\n", port, val);
port &= 0xff;
if (port < 0x20)
port &= 0xf8;
else
port &= 0xe3;
switch (port) {
case 0x00:
dma_sg_write(port, val & 0xff, priv);
break;
case 0x20:
dev->ptr = (dev->ptr & 0xffff0000) | (val & 0xfffc);
dev->ptr %= (mem_size * 1024);
dev->ptr0 = val & 0xff;
break;
case 0x22:
dev->ptr = (dev->ptr & 0x0000fffc) | (val << 16);
dev->ptr %= (mem_size * 1024);
break;
default:
break;
}
}
static void
dma_sg_writel(uint16_t port, uint32_t val, void *priv)
{
dma_t *dev = (dma_t *) priv;
dma_log("DMA S/G DWORD write: %04X %08X\n", port, val);
port &= 0xff;
if (port < 0x20)
port &= 0xf8;
else
port &= 0xe3;
switch (port) {
case 0x00:
dma_sg_write(port, val & 0xff, priv);
break;
case 0x20:
dev->ptr = (val & 0xfffffffc);
dev->ptr %= (mem_size * 1024);
dev->ptr0 = val & 0xff;
break;
default:
break;
}
}
static uint8_t
dma_sg_read(uint16_t port, void *priv)
{
const dma_t *dev = (dma_t *) priv;
uint8_t ret = 0xff;
port &= 0xff;
if (port < 0x20)
port &= 0xf8;
else
port &= 0xe3;
switch (port) {
case 0x08:
ret = (dev->sg_status & 0x01);
if (dev->eot)
ret |= 0x80;
if ((dev->sg_command & 0xc0) == 0x40)
ret |= 0x20;
if (dev->ab != 0x00000000)
ret |= 0x08;
if (dev->ac != 0x00000000)
ret |= 0x04;
break;
case 0x20:
ret = dev->ptr0;
break;
case 0x21:
ret = dev->ptr >> 8;
break;
case 0x22:
ret = dev->ptr >> 16;
break;
case 0x23:
ret = dev->ptr >> 24;
break;
default:
break;
}
dma_log("DMA S/G BYTE read : %04X %02X\n", port, ret);
return ret;
}
static uint16_t
dma_sg_readw(uint16_t port, void *priv)
{
const dma_t *dev = (dma_t *) priv;
uint16_t ret = 0xffff;
port &= 0xff;
if (port < 0x20)
port &= 0xf8;
else
port &= 0xe3;
switch (port) {
case 0x08:
ret = (uint16_t) dma_sg_read(port, priv);
break;
case 0x20:
ret = dev->ptr0 | (dev->ptr & 0xff00);
break;
case 0x22:
ret = dev->ptr >> 16;
break;
default:
break;
}
dma_log("DMA S/G WORD read : %04X %04X\n", port, ret);
return ret;
}
static uint32_t
dma_sg_readl(uint16_t port, void *priv)
{
const dma_t *dev = (dma_t *) priv;
uint32_t ret = 0xffffffff;
port &= 0xff;
if (port < 0x20)
port &= 0xf8;
else
port &= 0xe3;
switch (port) {
case 0x08:
ret = (uint32_t) dma_sg_read(port, priv);
break;
case 0x20:
ret = dev->ptr0 | (dev->ptr & 0xffffff00);
break;
default:
break;
}
dma_log("DMA S/G DWORD read : %04X %08X\n", port, ret);
return ret;
}
static void
dma_ext_mode_write(uint16_t addr, uint8_t val, UNUSED(void *priv))
{
int channel = (val & 0x03);
if (addr == 0x4d6)
channel |= 4;
dma[channel].ext_mode = val & 0x7c;
switch ((val > 2) & 0x03) {
case 0x00:
dma[channel].transfer_mode = 0x0101;
break;
case 0x01:
dma[channel].transfer_mode = 0x0202;
break;
case 0x02: /* 0x02 is reserved. */
/* Logic says this should be an undocumented mode that counts by words,
but is 8-bit I/O, thus only transferring every second byte. */
dma[channel].transfer_mode = 0x0201;
break;
case 0x03:
dma[channel].transfer_mode = 0x0102;
break;
default:
break;
}
}
static uint8_t
dma_sg_int_status_read(UNUSED(uint16_t addr), UNUSED(void *priv))
{
uint8_t ret = 0x00;
for (uint8_t i = 0; i < 8; i++) {
if (i != 4)
ret = (!!(dma[i].sg_status & 8)) << i;
}
return ret;
}
static uint8_t
dma_read(uint16_t addr, UNUSED(void *priv))
{
int channel = (addr >> 1) & 3;
int count;
uint8_t ret = (dmaregs[0][addr & 0xf]);
switch (addr & 0xf) {
case 0:
case 2:
case 4:
case 6: /*Address registers*/
dma_wp[0] ^= 1;
if (dma_wp[0])
ret = (dma[channel].ac & 0xff);
else
ret = ((dma[channel].ac >> 8) & 0xff);
break;
case 1:
case 3:
case 5:
case 7: /*Count registers*/
dma_wp[0] ^= 1;
count = dma[channel].cc/* + 1*/;
if (dma_wp[0])
ret = count & 0xff;
else
ret = count >> 8;
break;
case 8: /*Status register*/
ret = dma_stat_rq_pc & 0xf;
ret <<= 4;
ret |= dma_stat & 0xf;
dma_stat &= ~0xf;
break;
case 0xd: /*Temporary register*/
ret = 0x00;
break;
default:
break;
}
dma_log("DMA: [R] %04X = %02X\n", addr, ret);
return ret;
}
static void
dma_write(uint16_t addr, uint8_t val, UNUSED(void *priv))
{
int channel = (addr >> 1) & 3;
dma_log("DMA: [W] %04X = %02X\n", addr, val);
dmaregs[0][addr & 0xf] = val;
switch (addr & 0xf) {
case 0:
case 2:
case 4:
case 6: /*Address registers*/
dma_wp[0] ^= 1;
if (dma_wp[0])
dma[channel].ab = (dma[channel].ab & 0xffffff00 & dma_mask) | val;
else
dma[channel].ab = (dma[channel].ab & 0xffff00ff & dma_mask) | (val << 8);
dma[channel].ac = dma[channel].ab;
return;
case 1:
case 3:
case 5:
case 7: /*Count registers*/
dma_wp[0] ^= 1;
if (dma_wp[0])
dma[channel].cb = (dma[channel].cb & 0xff00) | val;
else
dma[channel].cb = (dma[channel].cb & 0x00ff) | (val << 8);
dma[channel].cc = dma[channel].cb;
return;
case 8: /*Control register*/
dma_command[0] = val;
#ifdef ENABLE_DMA_LOG
if (val & 0x01)
dma_log("[%08X:%04X] Memory-to-memory enable\n", CS, cpu_state.pc);
#endif
return;
case 9: /*Request register */
channel = (val & 3);
if (val & 4) {
dma_stat_rq_pc |= (1 << channel);
if ((channel == 0) && (dma_command[0] & 0x01)) {
dma_log("Memory to memory transfer start\n");
dma_mem_to_mem_transfer();
} else
dma_block_transfer(channel);
} else
dma_stat_rq_pc &= ~(1 << channel);
break;
case 0xa: /*Mask*/
channel = (val & 3);
if (val & 4)
dma_m |= (1 << channel);
else
dma_m &= ~(1 << channel);
return;
case 0xb: /*Mode*/
channel = (val & 3);
dma[channel].mode = val;
if (dma_ps2.is_ps2) {
dma[channel].ps2_mode &= ~0x1c;
if (val & 0x20)
dma[channel].ps2_mode |= 0x10;
if ((val & 0xc) == 8)
dma[channel].ps2_mode |= 4;
else if ((val & 0xc) == 4)
dma[channel].ps2_mode |= 0xc;
}
return;
case 0xc: /*Clear FF*/
dma_wp[0] = 0;
return;
case 0xd: /*Master clear*/
dma_wp[0] = 0;
dma_m |= 0xf;
dma_stat_rq_pc &= ~0x0f;
return;
case 0xe: /*Clear mask*/
dma_m &= 0xf0;
return;
case 0xf: /*Mask write*/
dma_m = (dma_m & 0xf0) | (val & 0xf);
return;
default:
break;
}
}
static uint8_t
dma_ps2_read(uint16_t addr, UNUSED(void *priv))
{
const dma_t *dma_c = &dma[dma_ps2.xfr_channel];
uint8_t temp = 0xff;
switch (addr) {
case 0x1a:
switch (dma_ps2.xfr_command) {
case 2: /*Address*/
case 3:
switch (dma_ps2.byte_ptr) {
case 0:
temp = dma_c->ac & 0xff;
dma_ps2.byte_ptr = 1;
break;
case 1:
temp = (dma_c->ac >> 8) & 0xff;
dma_ps2.byte_ptr = 2;
break;
case 2:
temp = (dma_c->ac >> 16) & 0xff;
dma_ps2.byte_ptr = 0;
break;
default:
break;
}
break;
case 4: /*Count*/
case 5:
if (dma_ps2.byte_ptr)
temp = dma_c->cc >> 8;
else
temp = dma_c->cc & 0xff;
dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1;
break;
case 6: /*Read DMA status*/
if (dma_ps2.byte_ptr) {
temp = ((dma_stat_rq & 0xf0) >> 4) | (dma_stat & 0xf0);
dma_stat &= ~0xf0;
dma_stat_rq &= ~0xf0;
} else {
temp = (dma_stat_rq & 0xf) | ((dma_stat & 0xf) << 4);
dma_stat &= ~0xf;
dma_stat_rq &= ~0xf;
}
dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1;
break;
case 7: /*Mode*/
temp = dma_c->ps2_mode;
break;
case 8: /*Arbitration Level*/
temp = dma_c->arb_level;
break;
case 9: /*Set DMA mask*/
dma_m |= (1 << dma_ps2.xfr_channel);
break;
case 0xa: /*Reset DMA mask*/
dma_m &= ~(1 << dma_ps2.xfr_channel);
break;
case 0xb:
if (!(dma_m & (1 << dma_ps2.xfr_channel)))
dma_ps2_run(dma_ps2.xfr_channel);
break;
default:
fatal("Bad XFR Read command %i channel %i\n", dma_ps2.xfr_command, dma_ps2.xfr_channel);
}
break;
default:
break;
}
return temp;
}
static void
dma_ps2_write(uint16_t addr, uint8_t val, UNUSED(void *priv))
{
dma_t *dma_c = &dma[dma_ps2.xfr_channel];
uint8_t mode;
switch (addr) {
case 0x18:
dma_ps2.xfr_channel = val & 0x7;
dma_ps2.xfr_command = val >> 4;
dma_ps2.byte_ptr = 0;
switch (dma_ps2.xfr_command) {
case 9: /*Set DMA mask*/
dma_m |= (1 << dma_ps2.xfr_channel);
break;
case 0xa: /*Reset DMA mask*/
dma_m &= ~(1 << dma_ps2.xfr_channel);
break;
case 0xb:
if (!(dma_m & (1 << dma_ps2.xfr_channel)))
dma_ps2_run(dma_ps2.xfr_channel);
break;
default:
break;
}
break;
case 0x1a:
switch (dma_ps2.xfr_command) {
case 0: /*I/O address*/
if (dma_ps2.byte_ptr)
dma_c->io_addr = (dma_c->io_addr & 0x00ff) | (val << 8);
else
dma_c->io_addr = (dma_c->io_addr & 0xff00) | val;
dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1;
break;
case 2: /*Address*/
switch (dma_ps2.byte_ptr) {
case 0:
dma_c->ac = (dma_c->ac & 0xffff00) | val;
dma_ps2.byte_ptr = 1;
break;
case 1:
dma_c->ac = (dma_c->ac & 0xff00ff) | (val << 8);
dma_ps2.byte_ptr = 2;
break;
case 2:
dma_c->ac = (dma_c->ac & 0x00ffff) | (val << 16);
dma_ps2.byte_ptr = 0;
break;
default:
break;
}
dma_c->ab = dma_c->ac;
break;
case 4: /*Count*/
if (dma_ps2.byte_ptr)
dma_c->cc = (dma_c->cc & 0xff) | (val << 8);
else
dma_c->cc = (dma_c->cc & 0xff00) | val;
dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1;
dma_c->cb = dma_c->cc;
break;
case 7: /*Mode register*/
mode = 0;
if (val & DMA_PS2_DEC2)
mode |= 0x20;
if ((val & DMA_PS2_XFER_MASK) == DMA_PS2_XFER_MEM_TO_IO)
mode |= 8;
else if ((val & DMA_PS2_XFER_MASK) == DMA_PS2_XFER_IO_TO_MEM)
mode |= 4;
dma_c->mode = (dma_c->mode & ~0x2c) | mode;
if (val & DMA_PS2_AUTOINIT)
dma_c->mode |= 0x10;
dma_c->ps2_mode = val;
dma_c->size = val & DMA_PS2_SIZE16;
break;
case 8: /*Arbitration Level*/
dma_c->arb_level = val;
break;
case 9: /*Set DMA mask*/
dma_m |= (1 << dma_ps2.xfr_channel);
break;
case 0xa: /*Reset DMA mask*/
dma_m &= ~(1 << dma_ps2.xfr_channel);
break;
case 0xb:
if (!(dma_m & (1 << dma_ps2.xfr_channel)))
dma_ps2_run(dma_ps2.xfr_channel);
break;
default:
fatal("Bad XFR command %i channel %i val %02x\n", dma_ps2.xfr_command, dma_ps2.xfr_channel, val);
}
break;
default:
break;
}
}
static uint8_t
dma16_read(uint16_t addr, UNUSED(void *priv))
{
int channel = ((addr >> 2) & 3) + 4;
#ifdef ENABLE_DMA_LOG
uint16_t port = addr;
#endif
uint8_t ret;
int count;
addr >>= 1;
ret = dmaregs[1][addr & 0xf];
switch (addr & 0xf) {
case 0:
case 2:
case 4:
case 6: /*Address registers*/
dma_wp[1] ^= 1;
if (dma_ps2.is_ps2) {
if (dma_wp[1])
ret = (dma[channel].ac);
else
ret = ((dma[channel].ac >> 8) & 0xff);
} else if (dma_wp[1])
ret = ((dma[channel].ac >> 1) & 0xff);
else
ret = ((dma[channel].ac >> 9) & 0xff);
break;
case 1:
case 3:
case 5:
case 7: /*Count registers*/
dma_wp[1] ^= 1;
count = dma[channel].cc/* + 1*/;
if (dma_wp[1])
ret = count & 0xff;
else
ret = count >> 8;
break;
case 8: /*Status register*/
ret = (dma_stat_rq_pc & 0xf0);
ret |= dma_stat >> 4;
dma_stat &= ~0xf0;
break;
default:
break;
}
dma_log("dma16_read(%08X) = %02X\n", addr, ret);
return ret;
}
static void
dma16_write(uint16_t addr, uint8_t val, UNUSED(void *priv))
{
int channel = ((addr >> 2) & 3) + 4;
dma_log("dma16_write(%08X, %02X)\n", addr, val);
addr >>= 1;
dmaregs[1][addr & 0xf] = val;
switch (addr & 0xf) {
case 0:
case 2:
case 4:
case 6: /*Address registers*/
dma_wp[1] ^= 1;
if (dma_ps2.is_ps2) {
if (dma_wp[1])
dma[channel].ab = (dma[channel].ab & 0xffffff00 & dma_mask) | val;
else
dma[channel].ab = (dma[channel].ab & 0xffff00ff & dma_mask) | (val << 8);
} else {
if (dma_wp[1])
dma[channel].ab = (dma[channel].ab & 0xfffffe00 & dma_mask) | (val << 1);
else
dma[channel].ab = (dma[channel].ab & 0xfffe01ff & dma_mask) | (val << 9);
}
dma[channel].ac = dma[channel].ab;
return;
case 1:
case 3:
case 5:
case 7: /*Count registers*/
dma_wp[1] ^= 1;
if (dma_wp[1])
dma[channel].cb = (dma[channel].cb & 0xff00) | val;
else
dma[channel].cb = (dma[channel].cb & 0x00ff) | (val << 8);
dma[channel].cc = dma[channel].cb;
return;
case 8: /*Control register*/
return;
case 9: /*Request register */
channel = (val & 3) + 4;
if (val & 4) {
dma_stat_rq_pc |= (1 << channel);
dma_block_transfer(channel);
} else
dma_stat_rq_pc &= ~(1 << channel);
break;
case 0xa: /*Mask*/
channel = (val & 3);
if (val & 4)
dma_m |= (0x10 << channel);
else
dma_m &= ~(0x10 << channel);
return;
case 0xb: /*Mode*/
channel = (val & 3) + 4;
dma[channel].mode = val;
if (dma_ps2.is_ps2) {
dma[channel].ps2_mode &= ~0x1c;
if (val & 0x20)
dma[channel].ps2_mode |= 0x10;
if ((val & 0xc) == 8)
dma[channel].ps2_mode |= 4;
else if ((val & 0xc) == 4)
dma[channel].ps2_mode |= 0xc;
}
return;
case 0xc: /*Clear FF*/
dma_wp[1] = 0;
return;
case 0xd: /*Master clear*/
dma_wp[1] = 0;
dma_m |= 0xf0;
dma_stat_rq_pc &= ~0xf0;
return;
case 0xe: /*Clear mask*/
dma_m &= 0x0f;
return;
case 0xf: /*Mask write*/
dma_m = (dma_m & 0x0f) | ((val & 0xf) << 4);
return;
default:
break;
}
}
#define CHANNELS \
{ \
8, 2, 3, 1, 8, 8, 8, 0 \
}
static void
dma_page_write(uint16_t addr, uint8_t val, UNUSED(void *priv))
{
uint8_t convert[8] = CHANNELS;
dma_log("DMA: [W] %04X = %02X\n", addr, val);
#ifdef USE_DYNAREC
if ((addr == 0x84) && cpu_use_dynarec)
update_tsc();
#endif
addr &= 0x0f;
dmaregs[2][addr] = val;
if (addr >= 8)
addr = convert[addr & 0x07] | 4;
else
addr = convert[addr & 0x07];
if (addr < 8) {
dma[addr].page_l = val;
if (addr > 4) {
dma[addr].page = val & 0xfe;
dma[addr].ab = (dma[addr].ab & 0xff01ffff & dma_mask) | (dma[addr].page << 16);
dma[addr].ac = (dma[addr].ac & 0xff01ffff & dma_mask) | (dma[addr].page << 16);
} else {
dma[addr].page = dma_at ? val : val & 0xf;
dma[addr].ab = (dma[addr].ab & 0xff00ffff & dma_mask) | (dma[addr].page << 16);
dma[addr].ac = (dma[addr].ac & 0xff00ffff & dma_mask) | (dma[addr].page << 16);
}
}
}
static uint8_t
dma_page_read(uint16_t addr, UNUSED(void *priv))
{
uint8_t convert[8] = CHANNELS;
uint8_t ret = 0xff;
if (((addr & 0xfffc) == 0x80) && (CS == 0xf000) &&
((cpu_state.pc & 0xfffffff8) == 0x00007278) &&
!strcmp(machine_get_internal_name(), "megapc")) switch (addr) {
/* The Amstrad MegaPC Quadtel BIOS times a sequence of:
mov ax,di
div bx
And expects this value to be at least 0x06e0 for 20 MHz,
and at least 0x0898 for 25 MHz, everything below 0x06e0
is assumed to be 16 MHz. Given that for some reason, this
does not occur on 86Box, we have to work around it here,
we return 0x0580 for 16 MHz, because it logically follows
in the sequence (0x06e0 = 0x0898 * (20 / 25), and
0x0580 = 0x06e0 * (16 / 20)). */
case 0x0081:
if (cpu_busspeed >= 25000000)
ret = 0x98;
else if (cpu_busspeed >= 20000000)
ret = 0xe0;
else
ret = 0x80;
break;
case 0x0082:
if (cpu_busspeed >= 25000000)
ret = 0x08;
else if (cpu_busspeed >= 20000000)
ret = 0x06;
else
ret = 0x05;
break;
} else {
addr &= 0x0f;
ret = dmaregs[2][addr];
if (addr >= 8)
addr = convert[addr & 0x07] | 4;
else
addr = convert[addr & 0x07];
if (addr < 8)
ret = dma[addr].page_l;
}
dma_log("DMA: [R] %04X = %02X\n", addr, ret);
return ret;
}
static void
dma_high_page_write(uint16_t addr, uint8_t val, UNUSED(void *priv))
{
uint8_t convert[8] = CHANNELS;
addr &= 0x0f;
if (addr >= 8)
addr = convert[addr & 0x07] | 4;
else
addr = convert[addr & 0x07];
if (addr < 8) {
dma[addr].page_h = val;
dma[addr].ab = ((dma[addr].ab & 0xffffff) | (dma[addr].page << 24)) & dma_mask;
dma[addr].ac = ((dma[addr].ac & 0xffffff) | (dma[addr].page << 24)) & dma_mask;
}
}
static uint8_t
dma_high_page_read(uint16_t addr, UNUSED(void *priv))
{
uint8_t convert[8] = CHANNELS;
uint8_t ret = 0xff;
addr &= 0x0f;
if (addr >= 8)
addr = convert[addr & 0x07] | 4;
else
addr = convert[addr & 0x07];
if (addr < 8)
ret = dma[addr].page_h;
return ret;
}
void
dma_set_params(uint8_t advanced, uint32_t mask)
{
dma_advanced = advanced;
dma_mask = mask;
}
void
dma_set_mask(uint32_t mask)
{
dma_mask = mask;
for (uint8_t i = 0; i < 8; i++) {
dma[i].ab &= mask;
dma[i].ac &= mask;
}
}
void
dma_set_at(uint8_t at)
{
dma_at = at;
}
void
dma_reset(void)
{
int c;
dma_wp[0] = dma_wp[1] = 0;
dma_m = 0;
dma_e = 0xff;
for (c = 0; c < 16; c++)
dmaregs[0][c] = dmaregs[1][c] = 0;
for (c = 0; c < 8; c++) {
memset(&(dma[c]), 0x00, sizeof(dma_t));
dma[c].size = (c & 4) ? 1 : 0;
dma[c].transfer_mode = (c & 4) ? 0x0202 : 0x0101;
}
dma_stat = 0x00;
dma_stat_rq = 0x00;
dma_stat_rq_pc = 0x00;
dma_stat_adv_pend = 0x00;
dma_req_is_soft = 0;
dma_advanced = 0;
memset(dma_buffer, 0x00, sizeof(dma_buffer));
memset(dma16_buffer, 0x00, sizeof(dma16_buffer));
dma_remove_sg();
dma_sg_base = 0x0400;
dma_mask = 0x00ffffff;
dma_at = is286;
}
void
dma_remove_sg(void)
{
io_removehandler(dma_sg_base + 0x0a, 0x01,
dma_sg_int_status_read, NULL, NULL,
NULL, NULL, NULL,
NULL);
for (uint8_t i = 0; i < 8; i++) {
io_removehandler(dma_sg_base + 0x10 + i, 0x01,
dma_sg_read, dma_sg_readw, dma_sg_readl,
dma_sg_write, dma_sg_writew, dma_sg_writel,
&dma[i]);
io_removehandler(dma_sg_base + 0x18 + i, 0x01,
dma_sg_read, dma_sg_readw, dma_sg_readl,
dma_sg_write, dma_sg_writew, dma_sg_writel,
&dma[i]);
io_removehandler(dma_sg_base + 0x20 + i, 0x04,
dma_sg_read, dma_sg_readw, dma_sg_readl,
dma_sg_write, dma_sg_writew, dma_sg_writel,
&dma[i]);
}
}
void
dma_set_sg_base(uint8_t sg_base)
{
dma_sg_base = sg_base << 8;
io_sethandler(dma_sg_base + 0x0a, 0x01,
dma_sg_int_status_read, NULL, NULL,
NULL, NULL, NULL,
NULL);
for (uint8_t i = 0; i < 8; i++) {
io_sethandler(dma_sg_base + 0x10 + i, 0x01,
dma_sg_read, dma_sg_readw, dma_sg_readl,
dma_sg_write, dma_sg_writew, dma_sg_writel,
&dma[i]);
io_sethandler(dma_sg_base + 0x18 + i, 0x01,
dma_sg_read, dma_sg_readw, dma_sg_readl,
dma_sg_write, dma_sg_writew, dma_sg_writel,
&dma[i]);
io_sethandler(dma_sg_base + 0x20 + i, 0x04,
dma_sg_read, dma_sg_readw, dma_sg_readl,
dma_sg_write, dma_sg_writew, dma_sg_writel,
&dma[i]);
}
}
void
dma_ext_mode_init(void)
{
io_sethandler(0x040b, 0x01,
NULL, NULL, NULL, dma_ext_mode_write, NULL, NULL, NULL);
io_sethandler(0x04d6, 0x01,
NULL, NULL, NULL, dma_ext_mode_write, NULL, NULL, NULL);
}
void
dma_high_page_init(void)
{
io_sethandler(0x0480, 8,
dma_high_page_read, NULL, NULL, dma_high_page_write, NULL, NULL, NULL);
}
void
dma_init(void)
{
dma_reset();
io_sethandler(0x0000, 16,
dma_read, NULL, NULL, dma_write, NULL, NULL, NULL);
io_sethandler(0x0080, 8,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
dma_ps2.is_ps2 = 0;
}
void
dma16_init(void)
{
dma_reset();
io_sethandler(0x00C0, 32,
dma16_read, NULL, NULL, dma16_write, NULL, NULL, NULL);
io_sethandler(0x0088, 8,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
}
void
dma_alias_set(void)
{
io_sethandler(0x0090, 2,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_sethandler(0x0093, 13,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
}
void
dma_alias_set_piix(void)
{
io_sethandler(0x0090, 1,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_sethandler(0x0094, 3,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_sethandler(0x0098, 1,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_sethandler(0x009C, 3,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
}
void
dma_alias_remove(void)
{
io_removehandler(0x0090, 2,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_removehandler(0x0093, 13,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
}
void
dma_alias_remove_piix(void)
{
io_removehandler(0x0090, 1,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_removehandler(0x0094, 3,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_removehandler(0x0098, 1,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
io_removehandler(0x009C, 3,
dma_page_read, NULL, NULL, dma_page_write, NULL, NULL, NULL);
}
void
ps2_dma_init(void)
{
dma_reset();
io_sethandler(0x0018, 1,
dma_ps2_read, NULL, NULL, dma_ps2_write, NULL, NULL, NULL);
io_sethandler(0x001a, 1,
dma_ps2_read, NULL, NULL, dma_ps2_write, NULL, NULL, NULL);
dma_ps2.is_ps2 = 1;
}
extern void dma_bm_read(uint32_t PhysAddress, uint8_t *DataRead, uint32_t TotalSize, int TransferSize);
extern void dma_bm_write(uint32_t PhysAddress, const uint8_t *DataWrite, uint32_t TotalSize, int TransferSize);
static int
dma_sg(uint8_t *data, int transfer_length, int out, void *priv)
{
dma_t *dev = (dma_t *) priv;
#ifdef ENABLE_DMA_LOG
char *sop;
#endif
int force_end = 0;
int buffer_pos = 0;
#ifdef ENABLE_DMA_LOG
sop = out ? "Read" : "Writ";
#endif
if (!(dev->sg_status & 1))
return 2; /*S/G disabled*/
dma_log("DMA S/G %s: %i bytes\n", out ? "write" : "read", transfer_length);
while (1) {
if (dev->count <= transfer_length) {
dma_log("%sing %i bytes to %08X\n", sop, dev->count, dev->addr);
if (out)
dma_bm_read(dev->addr, (uint8_t *) (data + buffer_pos), dev->count, 4);
else
dma_bm_write(dev->addr, (uint8_t *) (data + buffer_pos), dev->count, 4);
transfer_length -= dev->count;
buffer_pos += dev->count;
} else {
dma_log("%sing %i bytes to %08X\n", sop, transfer_length, dev->addr);
if (out)
dma_bm_read(dev->addr, (uint8_t *) (data + buffer_pos), transfer_length, 4);
else
dma_bm_write(dev->addr, (uint8_t *) (data + buffer_pos), transfer_length, 4);
/* Increase addr and decrease count so that resumed transfers do not mess up. */
dev->addr += transfer_length;
dev->count -= transfer_length;
transfer_length = 0;
force_end = 1;
}
if (force_end) {
dma_log("Total transfer length smaller than sum of all blocks, partial block\n");
return 1; /* This block has exhausted the data to transfer and it was smaller than the count, break. */
} else {
if (!transfer_length && !dev->eot) {
dma_log("Total transfer length smaller than sum of all blocks, full block\n");
return 1; /* We have exhausted the data to transfer but there's more blocks left, break. */
} else if (transfer_length && dev->eot) {
dma_log("Total transfer length greater than sum of all blocks\n");
return 4; /* There is data left to transfer but we have reached EOT - return with error. */
} else if (dev->eot) {
dma_log("Regular EOT\n");
return 5; /* We have regularly reached EOT - clear status and break. */
} else {
/* We have more to transfer and there are blocks left, get next block. */
dma_sg_next_addr(dev);
}
}
}
}
uint8_t
_dma_read(uint32_t addr, dma_t *dma_c)
{
uint8_t temp = 0;
if (dma_advanced) {
if (dma_c->sg_status & 1)
dma_c->sg_status = (dma_c->sg_status & 0x0f) | (dma_sg(&temp, 1, 1, dma_c) << 4);
else
dma_bm_read(addr, &temp, 1, dma_transfer_size(dma_c));
} else
temp = mem_readb_phys(addr);
return temp;
}
static uint16_t
_dma_readw(uint32_t addr, dma_t *dma_c)
{
uint16_t temp = 0;
if (dma_advanced) {
if (dma_c->sg_status & 1)
dma_c->sg_status = (dma_c->sg_status & 0x0f) | (dma_sg((uint8_t *) &temp, 2, 1, dma_c) << 4);
else
dma_bm_read(addr, (uint8_t *) &temp, 2, dma_transfer_size(dma_c));
} else
temp = _dma_read(addr, dma_c) | (_dma_read(addr + 1, dma_c) << 8);
return temp;
}
static void
_dma_write(uint32_t addr, uint8_t val, dma_t *dma_c)
{
if (dma_advanced) {
if (dma_c->sg_status & 1)
dma_c->sg_status = (dma_c->sg_status & 0x0f) | (dma_sg(&val, 1, 0, dma_c) << 4);
else
dma_bm_write(addr, &val, 1, dma_transfer_size(dma_c));
} else {
mem_writeb_phys(addr, val);
if (dma_at)
mem_invalidate_range(addr, addr);
}
}
static void
_dma_writew(uint32_t addr, uint16_t val, dma_t *dma_c)
{
if (dma_advanced) {
if (dma_c->sg_status & 1)
dma_c->sg_status = (dma_c->sg_status & 0x0f) | (dma_sg((uint8_t *) &val, 2, 0, dma_c) << 4);
else
dma_bm_write(addr, (uint8_t *) &val, 2, dma_transfer_size(dma_c));
} else {
_dma_write(addr, val & 0xff, dma_c);
_dma_write(addr + 1, val >> 8, dma_c);
}
}
static void
dma_retreat(dma_t *dma_c)
{
int as = dma_c->transfer_mode >> 8;
if (dma->sg_status & 1) {
dma_c->ac = (dma_c->ac - as) & dma_mask;
dma_c->page = dma_c->page_l = (dma_c->ac >> 16) & 0xff;
dma_c->page_h = (dma_c->ac >> 24) & 0xff;
} else if (as == 2)
dma_c->ac = ((dma_c->ac & 0xfffe0000) & dma_mask) | ((dma_c->ac - as) & 0x1ffff);
else
dma_c->ac = ((dma_c->ac & 0xffff0000) & dma_mask) | ((dma_c->ac - as) & 0xffff);
}
void
dma_advance(dma_t *dma_c)
{
int as = dma_c->transfer_mode >> 8;
if (dma->sg_status & 1) {
dma_c->ac = (dma_c->ac + as) & dma_mask;
dma_c->page = dma_c->page_l = (dma_c->ac >> 16) & 0xff;
dma_c->page_h = (dma_c->ac >> 24) & 0xff;
} else if (as == 2)
dma_c->ac = ((dma_c->ac & 0xfffe0000) & dma_mask) | ((dma_c->ac + as) & 0x1ffff);
else
dma_c->ac = ((dma_c->ac & 0xffff0000) & dma_mask) | ((dma_c->ac + as) & 0xffff);
}
int
dma_channel_readable(int channel)
{
dma_t *dma_c = &dma[channel];
int ret = 1;
if (channel < 4) {
if (dma_command[0] & 0x04)
ret = 0;
} else {
if (dma_command[1] & 0x04)
ret = 0;
}
if (!(dma_e & (1 << channel)))
ret = 0;
if ((dma_m & (1 << channel)) && !dma_req_is_soft)
ret = 0;
if ((dma_c->mode & 0xC) != 8)
ret = 0;
return ret;
}
int
dma_channel_read_only(int channel)
{
dma_t *dma_c = &dma[channel];
uint16_t temp;
if (channel < 4) {
if (dma_command[0] & 0x04)
return (DMA_NODATA);
} else {
if (dma_command[1] & 0x04)
return (DMA_NODATA);
}
if (!(dma_e & (1 << channel)))
return (DMA_NODATA);
if ((dma_m & (1 << channel)) && !dma_req_is_soft)
return (DMA_NODATA);
if ((dma_c->mode & 0xC) != 8)
return (DMA_NODATA);
dma_channel_advance(channel);
if (!dma_at && !channel)
refreshread();
if (!dma_c->size) {
temp = _dma_read(dma_c->ac, dma_c);
if (dma_c->mode & 0x20) {
if (dma_ps2.is_ps2)
dma_c->ac--;
else if (dma_advanced)
dma_retreat(dma_c);
else
dma_c->ac = (dma_c->ac & 0xffff0000 & dma_mask) | ((dma_c->ac - 1) & 0xffff);
} else {
if (dma_ps2.is_ps2)
dma_c->ac++;
else if (dma_advanced)
dma_advance(dma_c);
else
dma_c->ac = (dma_c->ac & 0xffff0000 & dma_mask) | ((dma_c->ac + 1) & 0xffff);
}
} else {
temp = _dma_readw(dma_c->ac, dma_c);
if (dma_c->mode & 0x20) {
if (dma_ps2.is_ps2)
dma_c->ac -= 2;
else if (dma_advanced)
dma_retreat(dma_c);
else
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac - 2) & 0x1ffff);
} else {
if (dma_ps2.is_ps2)
dma_c->ac += 2;
else if (dma_advanced)
dma_advance(dma_c);
else
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac + 2) & 0x1ffff);
}
}
dma_stat_rq |= (1 << channel);
dma_stat_adv_pend |= (1 << channel);
return temp;
}
int
dma_channel_advance(int channel)
{
dma_t *dma_c = &dma[channel];
int tc = 0;
if (dma_stat_adv_pend & (1 << channel)) {
dma_c->cc--;
if (dma_c->cc < 0) {
if (dma_advanced && (dma_c->sg_status & 1) && !(dma_c->sg_status & 6))
dma_sg_next_addr(dma_c);
else {
tc = 1;
if (dma_c->mode & 0x10) { /*Auto-init*/
dma_c->cc = dma_c->cb;
dma_c->ac = dma_c->ab;
} else
dma_m |= (1 << channel);
dma_stat |= (1 << channel);
}
}
if (tc) {
if (dma_advanced && (dma_c->sg_status & 1) && ((dma_c->sg_command & 0xc0) == 0x40)) {
picint(1 << 13);
dma_c->sg_status |= 8;
}
}
dma_stat_adv_pend &= ~(1 << channel);
}
return tc;
}
int
dma_channel_read(int channel)
{
dma_t *dma_c = &dma[channel];
uint16_t temp;
int tc = 0;
if (channel < 4) {
if (dma_command[0] & 0x04)
return (DMA_NODATA);
} else {
if (dma_command[1] & 0x04)
return (DMA_NODATA);
}
if (!(dma_e & (1 << channel)))
return (DMA_NODATA);
if ((dma_m & (1 << channel)) && !dma_req_is_soft)
return (DMA_NODATA);
if ((dma_c->mode & 0xC) != 8)
return (DMA_NODATA);
if (dma_stat_adv_pend & (1 << channel))
dma_channel_advance(channel);
if (!dma_at && !channel)
refreshread();
if (!dma_c->size) {
temp = _dma_read(dma_c->ac, dma_c);
if (dma_c->mode & 0x20) {
if (dma_ps2.is_ps2)
dma_c->ac--;
else if (dma_advanced)
dma_retreat(dma_c);
else
dma_c->ac = (dma_c->ac & 0xffff0000 & dma_mask) | ((dma_c->ac - 1) & 0xffff);
} else {
if (dma_ps2.is_ps2)
dma_c->ac++;
else if (dma_advanced)
dma_advance(dma_c);
else
dma_c->ac = (dma_c->ac & 0xffff0000 & dma_mask) | ((dma_c->ac + 1) & 0xffff);
}
} else {
temp = _dma_readw(dma_c->ac, dma_c);
if (dma_c->mode & 0x20) {
if (dma_ps2.is_ps2)
dma_c->ac -= 2;
else if (dma_advanced)
dma_retreat(dma_c);
else
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac - 2) & 0x1ffff);
} else {
if (dma_ps2.is_ps2)
dma_c->ac += 2;
else if (dma_advanced)
dma_advance(dma_c);
else
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac + 2) & 0x1ffff);
}
}
dma_stat_rq |= (1 << channel);
dma_c->cc--;
if (dma_c->cc < 0) {
if (dma_advanced && (dma_c->sg_status & 1) && !(dma_c->sg_status & 6))
dma_sg_next_addr(dma_c);
else {
tc = 1;
if (dma_c->mode & 0x10) { /*Auto-init*/
dma_c->cc = dma_c->cb;
dma_c->ac = dma_c->ab;
} else
dma_m |= (1 << channel);
dma_stat |= (1 << channel);
}
}
if (tc) {
if (dma_advanced && (dma_c->sg_status & 1) && ((dma_c->sg_command & 0xc0) == 0x40)) {
picint(1 << 13);
dma_c->sg_status |= 8;
}
return (temp | DMA_OVER);
}
return temp;
}
int
dma_channel_write(int channel, uint16_t val)
{
dma_t *dma_c = &dma[channel];
if (channel < 4) {
if (dma_command[0] & 0x04)
return (DMA_NODATA);
} else {
if (dma_command[1] & 0x04)
return (DMA_NODATA);
}
if (!(dma_e & (1 << channel)))
return (DMA_NODATA);
if ((dma_m & (1 << channel)) && !dma_req_is_soft)
return (DMA_NODATA);
if ((dma_c->mode & 0xC) != 4)
return (DMA_NODATA);
if (!dma_c->size) {
_dma_write(dma_c->ac, val & 0xff, dma_c);
if (dma_c->mode & 0x20) {
if (dma_ps2.is_ps2)
dma_c->ac--;
else if (dma_advanced)
dma_retreat(dma_c);
else
dma_c->ac = (dma_c->ac & 0xffff0000 & dma_mask) | ((dma_c->ac - 1) & 0xffff);
} else {
if (dma_ps2.is_ps2)
dma_c->ac++;
else if (dma_advanced)
dma_advance(dma_c);
else
dma_c->ac = (dma_c->ac & 0xffff0000 & dma_mask) | ((dma_c->ac + 1) & 0xffff);
}
} else {
_dma_writew(dma_c->ac, val, dma_c);
if (dma_c->mode & 0x20) {
if (dma_ps2.is_ps2)
dma_c->ac -= 2;
else if (dma_advanced)
dma_retreat(dma_c);
else
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac - 2) & 0x1ffff);
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac - 2) & 0x1ffff);
} else {
if (dma_ps2.is_ps2)
dma_c->ac += 2;
else if (dma_advanced)
dma_advance(dma_c);
else
dma_c->ac = (dma_c->ac & 0xfffe0000 & dma_mask) | ((dma_c->ac + 2) & 0x1ffff);
}
}
dma_stat_rq |= (1 << channel);
dma_stat_adv_pend &= ~(1 << channel);
dma_c->cc--;
if (dma_c->cc < 0) {
if (dma_advanced && (dma_c->sg_status & 1) && !(dma_c->sg_status & 6))
dma_sg_next_addr(dma_c);
else {
if (dma_c->mode & 0x10) { /*Auto-init*/
dma_c->cc = dma_c->cb;
dma_c->ac = dma_c->ab;
} else
dma_m |= (1 << channel);
dma_stat |= (1 << channel);
}
}
if (dma_m & (1 << channel)) {
if (dma_advanced && (dma_c->sg_status & 1) && ((dma_c->sg_command & 0xc0) == 0x40)) {
picint(1 << 13);
dma_c->sg_status |= 8;
}
return DMA_OVER;
}
return 0;
}
static void
dma_ps2_run(int channel)
{
dma_t *dma_c = &dma[channel];
switch (dma_c->ps2_mode & DMA_PS2_XFER_MASK) {
case DMA_PS2_XFER_MEM_TO_IO:
do {
if (!dma_c->size) {
uint8_t temp = _dma_read(dma_c->ac, dma_c);
outb(dma_c->io_addr, temp);
if (dma_c->ps2_mode & DMA_PS2_DEC2)
dma_c->ac--;
else
dma_c->ac++;
} else {
uint16_t temp = _dma_readw(dma_c->ac, dma_c);
outw(dma_c->io_addr, temp);
if (dma_c->ps2_mode & DMA_PS2_DEC2)
dma_c->ac -= 2;
else
dma_c->ac += 2;
}
dma_stat_rq |= (1 << channel);
dma_c->cc--;
} while (dma_c->cc > 0);
dma_stat |= (1 << channel);
break;
case DMA_PS2_XFER_IO_TO_MEM:
do {
if (!dma_c->size) {
uint8_t temp = inb(dma_c->io_addr);
_dma_write(dma_c->ac, temp, dma_c);
if (dma_c->ps2_mode & DMA_PS2_DEC2)
dma_c->ac--;
else
dma_c->ac++;
} else {
uint16_t temp = inw(dma_c->io_addr);
_dma_writew(dma_c->ac, temp, dma_c);
if (dma_c->ps2_mode & DMA_PS2_DEC2)
dma_c->ac -= 2;
else
dma_c->ac += 2;
}
dma_stat_rq |= (1 << channel);
dma_c->cc--;
} while (dma_c->cc > 0);
ps2_cache_clean();
dma_stat |= (1 << channel);
break;
default: /*Memory verify*/
do {
if (!dma_c->size) {
if (dma_c->ps2_mode & DMA_PS2_DEC2)
dma_c->ac--;
else
dma_c->ac++;
} else {
if (dma_c->ps2_mode & DMA_PS2_DEC2)
dma_c->ac -= 2;
else
dma_c->ac += 2;
}
dma_stat_rq |= (1 << channel);
dma->cc--;
} while (dma->cc > 0);
dma_stat |= (1 << channel);
break;
}
}
int
dma_mode(int channel)
{
return (dma[channel].mode);
}
/* DMA Bus Master Page Read/Write */
void
dma_bm_read(uint32_t PhysAddress, uint8_t *DataRead, uint32_t TotalSize, int TransferSize)
{
uint32_t n;
uint32_t n2;
uint8_t bytes[4] = { 0, 0, 0, 0 };
n = TotalSize & ~(TransferSize - 1);
n2 = TotalSize - n;
/* Do the divisible block, if there is one. */
if (n) {
for (uint32_t i = 0; i < n; i += TransferSize)
mem_read_phys((void *) &(DataRead[i]), PhysAddress + i, TransferSize);
}
/* Do the non-divisible block, if there is one. */
if (n2) {
mem_read_phys((void *) bytes, PhysAddress + n, TransferSize);
memcpy((void *) &(DataRead[n]), bytes, n2);
}
}
void
dma_bm_write(uint32_t PhysAddress, const uint8_t *DataWrite, uint32_t TotalSize, int TransferSize)
{
uint32_t n;
uint32_t n2;
uint8_t bytes[4] = { 0, 0, 0, 0 };
n = TotalSize & ~(TransferSize - 1);
n2 = TotalSize - n;
/* Do the divisible block, if there is one. */
if (n) {
for (uint32_t i = 0; i < n; i += TransferSize)
mem_write_phys((void *) &(DataWrite[i]), PhysAddress + i, TransferSize);
}
/* Do the non-divisible block, if there is one. */
if (n2) {
mem_read_phys((void *) bytes, PhysAddress + n, TransferSize);
memcpy(bytes, (void *) &(DataWrite[n]), n2);
mem_write_phys((void *) bytes, PhysAddress + n, TransferSize);
}
if (dma_at)
mem_invalidate_range(PhysAddress, PhysAddress + TotalSize - 1);
}