/* * 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, * Miran Grca, * Fred N. van Kempen, * * Copyright 2008-2020 Sarah Walker. * Copyright 2016-2020 Miran Grca. * Copyright 2017-2020 Fred N. van Kempen. */ #include #include #include #include #include #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); }