// Binds a device to a DMA channel. // @param change DMA channel to use (0 ... 3) // @param dev device object to use for callbacks on this channel // @return the DmaChannel object public IDMAChannel BindChannel(int channelNumber, IDMADevice dev) { if (channelNumber == 0) { throw (new ArgumentException("Can not bind DMA channel 0")); } channels[channelNumber].Device = dev; channels[channelNumber].PendingRequest = false; channels[channelNumber].ExternalEop = false; return(channels[channelNumber]); }
protected void TryHandleRequest() { int i = 0; // Update request bits in status register int rbits = reqReg; for (i = 0; i <= 4 - 1; i++) { if (channels[i].PendingRequest) { rbits = rbits | (1 << i); } } statusReg = (int)((statusReg & 0xF) | (rbits << 4)); // Don't start a transfer during dead time after a previous transfer if (pendingTask) { return; } // Don't start a transfer if the controller is disabled if ((cmdReg & 0x4) != 0) { return; } // Select a channel with pending request rbits = rbits & (~MaskReg); rbits = rbits & (~1); // never select channel 0 if (rbits == 0) { return; } i = prioChannel; while (((rbits >> i) & 1) == 0) { i = (int)((i + 1) & 3); } // Just decided to start a transfer on channel i Channel chan = channels[i]; IDMADevice dev = chan.Device; int mode = chan.Mode; int page = chan.Page; // Update dynamic priority if ((cmdReg & 10) != 0) { prioChannel = (int)((i + 1) & 3); } // Block further transactions until this one completes pendingTask = true; long transferTime = 0; if ((mode & 0xC0) == 0xC0) { //log.warn("cascade mode not implemented (channel " + i + ")") Debugger.Break(); } else if ((mode & 0xC) == 0xC) { //log.warn("invalid mode on channel " + i) } else { // Prepare for transfer bool blockmode = (mode & 0xC0) == 0x80; bool singlemode = (mode & 0xC0) == 0x40; int curcount = chan.CurrentCount; int maxlen = curcount + 1; int curaddr = chan.CurrentAddress; int addrstep = (int)(((chan.Mode & 0x20) == 0) ? 1 : -1); chan.ExternalEop = false; // Don't combine too much single transfers in one atomic action if (singlemode && maxlen > 25) { maxlen = 25; } // Execute transfer switch (mode & 0xC) { case 0x0: // DMA verify curcount -= maxlen; curaddr = (int)((curaddr + maxlen * addrstep) & 0xFFFF); transferTime += (long)(3 * maxlen * Scheduler.BASECLOCK / cpu.Clock); break; case 0x4: // DMA write while ((maxlen > 0) && (!chan.ExternalEop) && (blockmode || chan.PendingRequest)) { if (dev != null) { cpu.Memory[(page << 16) | curaddr] = dev.DMAWrite(); } maxlen--; curcount--; curaddr = (int)((curaddr + addrstep) & 0xFFFF); transferTime += (long)(3 * Scheduler.BASECLOCK / cpu.Clock); } break; case 0x8: // DMA read while (maxlen > 0 && !chan.ExternalEop && (blockmode || chan.PendingRequest)) { if (dev != null) { dev.DMARead(cpu.Memory[(page << 16) | curaddr]); } maxlen--; curcount--; curaddr = (int)((curaddr + addrstep) & 0xFFFF); transferTime += (long)(3 * Scheduler.BASECLOCK / cpu.Clock); } break; } // Update registers bool termcount = curcount < 0; chan.CurrentCount = (int)(termcount ? 0xFFFF : curcount); chan.CurrentAddress = curaddr; // Handle terminal count or external EOP if (termcount || chan.ExternalEop) { if ((mode & 0x10) == 0) { // Set mask bit MaskReg = MaskReg | (1 << i); } else { // Auto-initialize chan.CurrentCount = chan.BaseCount; chan.CurrentAddress = chan.BaseAddress; } // Clear software request reqReg = reqReg & (~(1 << i)); // Set TC bit in status register statusReg = statusReg | (1 << i); } // Send EOP to device if (termcount && (!chan.ExternalEop) && dev != null) { dev.DMAEOP(); } } // Schedule a task to run when the simulated DMA transfer completes cpu.Sched.RunTaskAfter(task, transferTime); }