コード例 #1
0
 // 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]);
 }
コード例 #2
0
        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);
        }