<!--
     The FreeBSD Documentation Project

     $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/dma/chapter.sgml,v 1.4 2002/02/14 13:20:33 keramida Exp $
-->

<chapter id="dma">
  <title>DMA</title>

  <sect1 id="dma-basics">
    <title>DMA: What it is and How it Works</title>
    
    <para><emphasis>Copyright &copy; 1995,1997 &a.uhclem;, All Rights
	Reserved.  10 December 1996.  Last Update 8 October
	1997.</emphasis></para>

    <para>Direct Memory Access (DMA) is a method of allowing data to be moved
      from one location to another in a computer without intervention from the
      central processor (CPU).</para>

    <para>The way that the DMA function is implemented varies between computer
      architectures, so this discussion will limit itself to the
      implementation and workings of the DMA subsystem on the IBM Personal
      Computer (PC), the IBM PC/AT and all of its successors and
      clones.</para>

    <para>The PC DMA subsystem is based on the Intel 8237 DMA controller. The
      8237 contains four DMA channels that can be programmed independently and
      any one of the channels may be active at any moment.  These channels are
      numbered 0, 1, 2 and 3.  Starting with the PC/AT, IBM added a second
      8237 chip, and numbered those channels 4, 5, 6 and 7.</para>

    <para>The original DMA controller (0, 1, 2 and 3) moves one byte in each
      transfer.  The second DMA controller (4, 5, 6, and 7) moves 16-bits from
      two adjacent memory locations in each transfer, with the first byte
      always coming from an even-numbered address.  The two controllers are
      identical components and the difference in transfer size is caused by
      the way the second controller is wired into the system.</para>

    <para>The 8237 has two electrical signals for each channel, named DRQ and
      -DACK.  There are additional signals with the names HRQ (Hold Request),
      HLDA (Hold Acknowledge), -EOP (End of Process), and the bus control
      signals -MEMR (Memory Read), -MEMW (Memory Write), -IOR (I/O Read), and
      -IOW (I/O Write).</para>

    <para>The 8237 DMA is known as a <quote>fly-by</quote> DMA controller.
      This means that the data being moved from one location to another does
      not pass through the DMA chip and is not stored in the DMA chip.
      Subsequently, the DMA can only transfer data between an I/O port and a
      memory address, but not between two I/O ports or two memory
      locations.</para>

    <note>
      <para>The 8237 does allow two channels to be connected together to allow
	memory-to-memory DMA operations in a non-<quote>fly-by</quote> mode,
	but nobody in the PC industry uses this scarce resource this way since
	it is faster to move data between memory locations using the
	CPU.</para>
    </note>

    <para>In the PC architecture, each DMA channel is normally activated only
      when the hardware that uses a given DMA channel requests a transfer by
      asserting the DRQ line for that channel.</para>

    <sect2>
      <title>A Sample DMA transfer</title>
      
      <para>Here is an example of the steps that occur to cause and perform a
	DMA transfer.  In this example, the floppy disk controller (FDC) has
	just read a byte from a diskette and wants the DMA to place it in
	memory at location 0x00123456.  The process begins by the FDC
	asserting the DRQ2 signal (the DRQ line for DMA channel 2) to alert
	the DMA controller.</para>
	  
      <para>The DMA controller will note that the DRQ2 signal is asserted. The
	DMA controller will then make sure that DMA channel 2 has been
	programmed and is unmasked (enabled).  The DMA controller also makes
	sure that none of the other DMA channels are active or want to be
	active and have a higher priority.  Once these checks are complete,
	the DMA asks the CPU to release the bus so that the DMA may use the
	bus.  The DMA requests the bus by asserting the HRQ signal which goes
	to the CPU.</para>
	  
      <para>The CPU detects the HRQ signal, and will complete executing the
	current instruction.  Once the processor has reached a state where it
	can release the bus, it will.  Now all of the signals normally
	generated by the CPU (-MEMR, -MEMW, -IOR, -IOW and a few others) are
	placed in a tri-stated condition (neither high or low) and then the
	CPU asserts the HLDA signal which tells the DMA controller that it is
	now in charge of the bus.</para>
	  
      <para>Depending on the processor, the CPU may be able to execute a few
	additional instructions now that it no longer has the bus, but the CPU
	will eventually have to wait when it reaches an instruction that must
	read something from memory that is not in the internal processor cache
	or pipeline.</para>
	  
      <para>Now that the DMA <quote>is in charge</quote>, the DMA activates its
	-MEMR, -MEMW, -IOR, -IOW output signals, and the address outputs from
	the DMA are set to 0x3456, which will be used to direct the byte that
	is about to transferred to a specific memory location.</para>
	  
      <para>The DMA will then let the device that requested the DMA transfer
	know that the transfer is commencing.  This is done by asserting the
	-DACK signal, or in the case of the floppy disk controller, -DACK2 is
	asserted.</para>
	  
      <para>The floppy disk controller is now responsible for placing the byte
	to be transferred on the bus Data lines.  Unless the floppy controller
	needs more time to get the data byte on the bus (and if the peripheral
	does need more time it alerts the DMA via the READY signal), the DMA
	will wait one DMA clock, and then de-assert the -MEMW and -IOR signals
	so that the memory will latch and store the byte that was on the bus,
	and the FDC will know that the byte has been transferred.</para>
	  
      <para>Since the DMA cycle only transfers a single byte at a time, the
	FDC now drops the DRQ2 signal, so the DMA knows that it is no longer
	needed.  The DMA will de-assert the -DACK2 signal, so that the FDC
	knows it must stop placing data on the bus.</para>
	  
      <para>The DMA will now check to see if any of the other DMA channels
	have any work to do.  If none of the channels have their DRQ lines
	asserted, the DMA controller has completed its work and will now
	tri-state the -MEMR, -MEMW, -IOR, -IOW and address signals.</para>
	  
      <para>Finally, the DMA will de-assert the HRQ signal.  The CPU sees
	this, and de-asserts the HOLDA signal.  Now the CPU activates its
	-MEMR, -MEMW, -IOR, -IOW and address lines, and it resumes executing
	instructions and accessing main memory and the peripherals.</para>
	  
      <para>For a typical floppy disk sector, the above process is repeated
	512 times, once for each byte.  Each time a byte is transferred, the
	address register in the DMA is incremented and the counter in the DMA
	that shows how many bytes are to be transferred is decremented.</para>
	  
      <para>When the counter reaches zero, the DMA asserts the EOP signal,
	which indicates that the counter has reached zero and no more data
	will be transferred until the DMA controller is reprogrammed by the
	CPU.  This event is also called the Terminal Count (TC). There is only
	one EOP signal, and since only one DMA channel can be active at any
	instant, the DMA channel that is currently active must be the DMA
	channel that just completed its task.</para>
	  
      <para>If a peripheral wants to generate an interrupt when the transfer
	of a buffer is complete, it can test for its -DACKn signal and the EOP
	signal both being asserted at the same time. When that happens, it
	means the DMA will not transfer any more information for that
	peripheral without intervention by the CPU. The peripheral can then
	assert one of the interrupt signals to get the processors' attention.
	In the PC architecture, the DMA chip itself is not capable of
	generating an interrupt.  The peripheral and its associated hardware
	is responsible for generating any interrupt that occurs.
	Subsequently, it is possible to have a peripheral that uses DMA but
	does not use interrupts.</para>
	  
      <para>It is important to understand that although the CPU always
	releases the bus to the DMA when the DMA makes the request, this
	action is invisible to both applications and the operating system,
	except for slight changes in the amount of time the processor takes to
	execute instructions when the DMA is active. Subsequently, the
	processor must poll the peripheral, poll the registers in the DMA
	chip, or receive an interrupt from the peripheral to know for certain
	when a DMA transfer has completed.</para>
    </sect2>
    
    <sect2>
      <title>DMA Page Registers and 16Meg address space limitations</title>
	  
      <para>You may have noticed earlier that instead of the DMA setting the
	address lines to 0x00123456 as we said earlier, the DMA only set
	0x3456.  The reason for this takes a bit of explaining.</para>
	  
      <para>When the original IBM PC was designed, IBM elected to use both DMA
	and interrupt controller chips that were designed for use with the
	8085, an 8-bit processor with an address space of 16 bits (64K).
	Since the IBM PC supported more than 64K of memory, something had to
	be done to allow the DMA to read or write memory locations above the
	64K mark.  What IBM did to solve this problem was to add an external
	data latch for each DMA channel that holds the upper bits of the
	address to be read to or written from. Whenever a DMA channel is
	active, the contents of that latch are written to the address bus and
	kept there until the DMA operation for the channel ends.  IBM called
	these latches <quote>Page Registers</quote>.</para>
	  
      <para>So for our example above, the DMA would put the 0x3456 part of the
	address on the bus, and the Page Register for DMA channel 2 would put
	0x0012xxxx on the bus.  Together, these two values form the complete
	address in memory that is to be accessed.</para>
	  
      <para>Because the Page Register latch is independent of the DMA chip,
	the area of memory to be read or written must not span a 64K physical
	boundary.  For example, if the DMA accesses memory location 0xffff,
	after that transfer the DMA will then increment the address register
	and the DMA will access the next byte at location 0x0000, not 0x10000.
	The results of letting this happen are probably not intended.</para>
	  
      <note>
	<para><quote>Physical</quote> 64K boundaries should not be confused
	  with 8086-mode 64K <quote>Segments</quote>, which are created by
	  mathematically adding a segment register with an offset register.
	  Page Registers have no address overlap and are mathematically OR-ed
	  together.</para>
      </note>
      
      <para>To further complicate matters, the external DMA address latches on
	the PC/AT hold only eight bits, so that gives us 8+16=24 bits, which
	means that the DMA can only point at memory locations between 0 and
	16Meg.  For newer computers that allow more than 16Meg of memory, the
	standard PC-compatible DMA cannot access memory locations above
	16Meg.</para>
	  
      <para>To get around this restriction, operating systems will reserve a
	RAM buffer in an area below 16Meg that also does not span a physical
	64K boundary.  Then the DMA will be programmed to transfer data from
	the peripheral and into that buffer.  Once the DMA has moved the data
	into this buffer, the operating system will then copy the data from
	the buffer to the address where the data is really supposed to be
	stored.</para>
	  
      <para>When writing data from an address above 16Meg to a DMA-based
	peripheral, the data must be first copied from where it resides into a
	buffer located below 16Meg, and then the DMA can copy the data from
	the buffer to the hardware.  In FreeBSD, these reserved buffers are
	called <quote>Bounce Buffers</quote>.  In the MS-DOS world, they are
	sometimes called <quote>Smart Buffers</quote>.</para>
	  
      <note>
	<para>A new implementation of the 8237, called the 82374, allows 16
	  bits of page register to be specified and enables access to the entire
	  32 bit address space, without the use of bounce buffers.</para>
      </note>
    </sect2>

    <sect2>
      <title>DMA Operational Modes and Settings</title>
      
      <para>The 8237 DMA can be operated in several modes.  The main ones
	are:</para>
	  
      <variablelist>
	<varlistentry>
	  <term>Single</term>

	  <listitem>
	    <para>A single byte (or word) is transferred.  The DMA must
	      release and re-acquire the bus for each additional byte. This is
	      commonly-used by devices that cannot transfer the entire block
	      of data immediately.  The peripheral will request the DMA each
	      time it is ready for another transfer.</para>
	    
	    <para>The standard PC-compatible floppy disk controller (NEC 765)
	      only has a one-byte buffer, so it uses this mode.</para>
	  </listitem>
	</varlistentry>

	<varlistentry>
	  <term>Block/Demand</term>
		
	  <listitem>
	    <para>Once the DMA acquires the system bus, an entire block of
	      data is transferred, up to a maximum of 64K.  If the peripheral
	      needs additional time, it can assert the READY signal to suspend
	      the transfer briefly.  READY should not be used excessively, and
	      for slow peripheral transfers, the Single Transfer Mode should
	      be used instead.</para>
		  
	    <para>The difference between Block and Demand is that once a Block
	      transfer is started, it runs until the transfer count reaches
	      zero.  DRQ only needs to be asserted until -DACK is asserted.
	      Demand Mode will transfer one more bytes until DRQ is
	      de-asserted, at which point the DMA suspends the transfer and
	      releases the bus back to the CPU.  When DRQ is asserted later,
	      the transfer resumes where it was suspended.</para>
		  
	    <para>Older hard disk controllers used Demand Mode until CPU
	      speeds increased to the point that it was more efficient to
	      transfer the data using the CPU, particularly if the memory
	      locations used in the transfer were above the 16Meg mark.</para>
	  </listitem>
	</varlistentry>

	<varlistentry>
	  <term>Cascade</term>
		
	  <listitem>
	    <para>This mechanism allows a DMA channel to request the bus, but
	      then the attached peripheral device is responsible for placing
	      the addressing information on the bus instead of the DMA.  This
	      is also used to implement a technique known as <quote>Bus
	      Mastering</quote>.</para>
		  
	    <para>When a DMA channel in Cascade Mode receives control of the
	      bus, the DMA does not place addresses and I/O control signals on
	      the bus like the DMA normally does when it is active.  Instead,
	      the DMA only asserts the -DACK signal for the active DMA
	      channel.</para>
		  
	    <para>At this point it is up to the peripheral connected to that
	      DMA channel to provide address and bus control signals.  The
	      peripheral has complete control over the system bus, and can do
	      reads and/or writes to any address below 16Meg.  When the
	      peripheral is finished with the bus, it de-asserts the DRQ line,
	      and the DMA controller can then return control to the CPU or to
	      some other DMA channel.</para>
		  
	    <para>Cascade Mode can be used to chain multiple DMA controllers
	      together, and this is exactly what DMA Channel 4 is used for in
	      the PC architecture.  When a peripheral requests the bus on DMA
	      channels 0, 1, 2 or 3, the slave DMA controller asserts HLDREQ,
	      but this wire is actually connected to DRQ4 on the primary DMA
	      controller instead of to the CPU.  The primary DMA controller,
	      thinking it has work to do on Channel 4, requests the bus from
	      the CPU using HLDREQ signal.  Once the CPU grants the bus to the
	      primary DMA controller, -DACK4 is asserted, and that wire is
	      actually connected to the HLDA signal on the slave DMA
	      controller.  The slave DMA controller then transfers data for
	      the DMA channel that requested it (0, 1, 2 or 3), or the slave
	      DMA may grant the bus to a peripheral that wants to perform its
	      own bus-mastering, such as a SCSI controller.</para>
		  
	    <para>Because of this wiring arrangement, only DMA channels 0, 1,
	      2, 3, 5, 6 and 7 are usable with peripherals on PC/AT
	      systems.</para>
	    
	    <note>
	      <para>DMA channel 0 was reserved for refresh operations in early
		IBM PC computers, but is generally available for use by
		peripherals in modern systems.</para>
	    </note>
	    
	    <para>When a peripheral is performing Bus Mastering, it is
	      important that the peripheral transmit data to or from memory
	      constantly while it holds the system bus.  If the peripheral
	      cannot do this, it must release the bus frequently so that the
	      system can perform refresh operations on main memory.</para>
		  
	    <para>The Dynamic RAM used in all PCs for main memory must be
	      accessed frequently to keep the bits stored in the components
	      <quote>charged</quote>.  Dynamic RAM essentially consists of
	      millions of capacitors with each one holding one bit of data.
	      These capacitors are charged with power to represent a
	      <literal>1</literal> or drained to represent a
	      <literal>0</literal>.  Because all capacitors leak, power must
	      be added at regular intervals to keep the <literal>1</literal>
	      values intact.  The RAM chips actually handle the task of
	      pumping power back into all of the appropriate locations in RAM,
	      but they must be told when to do it by the rest of the computer
	      so that the refresh activity will not interfere with the computer
	      wanting to access RAM normally.  If the computer is unable to
	      refresh memory, the contents of memory will become corrupted in
	      just a few milliseconds.</para>
		  
	    <para>Since memory read and write cycles <quote>count</quote> as
	      refresh cycles (a dynamic RAM refresh cycle is actually an
	      incomplete memory read cycle), as long as the peripheral
	      controller continues reading or writing data to sequential
	      memory locations, that action will refresh all of memory.</para>
		  
	    <para>Bus-mastering is found in some SCSI host interfaces and
	      other high-performance peripheral controllers.</para>
	  </listitem>
	</varlistentry>

	<varlistentry>
	  <term>Autoinitialize</term>
	  
	  <listitem>
	    <para>This mode causes the DMA to perform Byte, Block or Demand
	      transfers, but when the DMA transfer counter reaches zero, the
	      counter and address are set back to where they were when the DMA
	      channel was originally programmed.  This means that as long as
	      the peripheral requests transfers, they will be granted.  It is
	      up to the CPU to move new data into the fixed buffer ahead of
	      where the DMA is about to transfer it when doing output
	      operations, and to read new data out of the buffer behind where the
	      DMA is writing when doing input operations.</para>
		  
	    <para>This technique is frequently used on audio devices that have
	      small or no hardware <quote>sample</quote> buffers.  There is
	      additional CPU overhead to manage this <quote>circular</quote>
	      buffer, but in some cases this may be the only way to eliminate
	      the latency that occurs when the DMA counter reaches zero and
	      the DMA stops transfers until it is reprogrammed.</para>
	  </listitem>
	</varlistentry>
      </variablelist>
    </sect2>

    <sect2>
      <title>Programming the DMA</title>
      
      <para>The DMA channel that is to be programmed should always be
	<quote>masked</quote> before loading any settings.  This is because the
	hardware might unexpectedly assert the DRQ for that channel, and the
	DMA might respond, even though not all of the parameters have been
	loaded or updated.</para>
	  
      <para>Once masked, the host must specify the direction of the transfer
	(memory-to-I/O or I/O-to-memory), what mode of DMA operation is to be
	used for the transfer (Single, Block, Demand, Cascade, etc), and
	finally the address and length of the transfer are loaded.  The length
	that is loaded is one less than the amount you expect the DMA to
	transfer.  The LSB and MSB of the address and length are written to
	the same 8-bit I/O port, so another port must be written to first to
	guarantee that the DMA accepts the first byte as the LSB and the
	second byte as the MSB of the length and address.</para>
	  
      <para>Then, be sure to update the Page Register, which is external to
	the DMA and is accessed through a different set of I/O ports.</para>
	  
      <para>Once all the settings are ready, the DMA channel can be un-masked.
	That DMA channel is now considered to be <quote>armed</quote>, and will
	respond when the DRQ line for that channel is asserted.</para>
	  
      <para>Refer to a hardware data book for precise programming details for
	the 8237.  You will also need to refer to the I/O port map for the PC
	system, which describes where the DMA and Page Register ports are
	located.  A complete port map table is located below.</para>
    </sect2>

    <sect2>
      <title>DMA Port Map</title>
      
      <para>All systems based on the IBM-PC and PC/AT have the DMA hardware
	located at the same I/O ports.  The complete list is provided below.
	Ports assigned to DMA Controller #2 are undefined on non-AT
	designs.</para>
	  
      <sect3>
	<title>0x00&ndash;0x1f DMA Controller #1 (Channels 0, 1, 2 and
	  3)</title>
	    
	<para>DMA Address and Count Registers</para>

	<informaltable frame="none">
	  <tgroup cols="3">
	    <tbody>
	      <row>
		<entry>0x00</entry>
		<entry>write</entry>
		<entry>Channel 0 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0x00</entry>
		<entry>read</entry>
		<entry>Channel 0 current address</entry>
	      </row>
	      
	      <row>
		<entry>0x01</entry>
		<entry>write</entry>
		<entry>Channel 0 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0x01</entry>
		<entry>read</entry>
		<entry>Channel 0 remaining word count</entry>
	      </row>
	      
	      <row>
		<entry>0x02</entry>
		<entry>write</entry>
		<entry>Channel 1 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0x02</entry>
		<entry>read</entry>
		<entry>Channel 1 current address</entry>
	      </row>
	      
	      <row>
		<entry>0x03</entry>
		<entry>write</entry>
		<entry>Channel 1 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0x03</entry>
		<entry>read</entry>
		<entry>Channel 1 remaining word count</entry>
	      </row>
	      
	      <row>
		<entry>0x04</entry>
		<entry>write</entry>
		<entry>Channel 2 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0x04</entry>
		<entry>read</entry>
		<entry>Channel 2 current address</entry>
	      </row>
	      
	      <row>
		<entry>0x05</entry>
		<entry>write</entry>
		<entry>Channel 2 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0x05</entry>
		<entry>read</entry>
		<entry>Channel 2 remaining word count</entry>
	      </row>
	      
	      <row>
		<entry>0x06</entry>
		<entry>write</entry>
		<entry>Channel 3 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0x06</entry>
		<entry>read</entry>
		<entry>Channel 3 current address</entry>
	      </row>
	      
	      <row>
		<entry>0x07</entry>
		<entry>write</entry>
		<entry>Channel 3 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0x07</entry>
		<entry>read</entry>
		<entry>Channel 3 remaining word count</entry>
	      </row>		  
	    </tbody>
	  </tgroup>
	</informaltable>

	<para>DMA Command Registers</para>

	<informaltable frame="none">
	  <tgroup cols="3">
	    <tbody>
	      <row>
		<entry>0x08</entry>
		<entry>write</entry>
		<entry>Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x08</entry>
		<entry>read</entry>
		<entry>Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x09</entry>
		<entry>write</entry>
		<entry>Request Register</entry>
	      </row>
	      
	      <row>
		<entry>0x09</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0x0a</entry>
		<entry>write</entry>
		<entry>Single Mask Register Bit</entry>
	      </row>
	      
	      <row>
		<entry>0x0a</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0x0b</entry>
		<entry>write</entry>
		<entry>Mode Register</entry>
	      </row>
	      
	      <row>
		<entry>0x0b</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0x0c</entry>
		<entry>write</entry>
		<entry>Clear LSB/MSB Flip-Flop</entry>
	      </row>
	      
	      <row>
		<entry>0x0c</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0x0d</entry>
		<entry>write</entry>
		<entry>Master Clear/Reset</entry>
	      </row>
	      
	      <row>
		<entry>0x0d</entry>
		<entry>read</entry>
		<entry>Temporary Register (not available on newer
		  versions)</entry>
	      </row>
	      <row>
		<entry>0x0e</entry>
		<entry>write</entry>
		<entry>Clear Mask Register</entry>
	      </row>
	      
	      <row>
		<entry>0x0e</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0x0f</entry>
		<entry>write</entry>
		<entry>Write All Mask Register Bits</entry>
	      </row>
	      
	      <row>
		<entry>0x0f</entry>
		<entry>read</entry>
		<entry>Read All Mask Register Bits (only in Intel
		  82374)</entry>
	      </row>
	    </tbody>
	  </tgroup>
	</informaltable>
      </sect3>
      
      <sect3>
	<title>0xc0&ndash;0xdf DMA Controller #2 (Channels 4, 5, 6 and
	  7)</title>

	<para>DMA Address and Count Registers</para>

	<informaltable frame="none">
	  <tgroup cols="3">
	    <tbody>
	      <row>
		<entry>0xc0</entry>
		<entry>write</entry>
		<entry>Channel 4 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0xc0</entry>
		<entry>read</entry>
		<entry>Channel 4 current address</entry>
	      </row>
	      
	      <row>
		<entry>0xc2</entry>
		<entry>write</entry>
		<entry>Channel 4 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0xc2</entry>
		<entry>read</entry>
		<entry>Channel 4 remaining word count</entry>
	      </row>
	      
	      <row>
		<entry>0xc4</entry>
		<entry>write</entry>
		<entry>Channel 5 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0xc4</entry>
		<entry>read</entry>
		<entry>Channel 5 current address</entry>
	      </row>
	      
	      <row>
		<entry>0xc6</entry>
		<entry>write</entry>
		<entry>Channel 5 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0xc6</entry>
		<entry>read</entry>
		<entry>Channel 5 remaining word count</entry>
	      </row>
	      
	      <row>
		<entry>0xc8</entry>
		<entry>write</entry>
		<entry>Channel 6 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0xc8</entry>
		<entry>read</entry>
		<entry>Channel 6 current address</entry>
	      </row>
	      
	      <row>
		<entry>0xca</entry>
		<entry>write</entry>
		<entry>Channel 6 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0xca</entry>
		<entry>read</entry>
		<entry>Channel 6 remaining word count</entry>
	      </row>
	      
	      <row>
		<entry>0xcc</entry>
		<entry>write</entry>
		<entry>Channel 7 starting address</entry>
	      </row>
	      
	      <row>
		<entry>0xcc</entry>
		<entry>read</entry>
		<entry>Channel 7 current address</entry>
	      </row>
	      
	      <row>
		<entry>0xce</entry>
		<entry>write</entry>
		<entry>Channel 7 starting word count</entry>
	      </row>
	      
	      <row>
		<entry>0xce</entry>
		<entry>read</entry>
		<entry>Channel 7 remaining word count</entry>
	      </row>		  
	    </tbody>
	  </tgroup>
	</informaltable>

	<para>DMA Command Registers</para>

	<informaltable frame="none">
	  <tgroup cols="3">
	    <tbody>
	      <row>
		<entry>0xd0</entry>
		<entry>write</entry>
		<entry>Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0xd0</entry>
		<entry>read</entry>
		<entry>Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0xd2</entry>
		<entry>write</entry>
		<entry>Request Register</entry>
	      </row>
	      
	      <row>
		<entry>0xd2</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0xd4</entry>
		<entry>write</entry>
		<entry>Single Mask Register Bit</entry>
	      </row>
	      
	      <row>
		<entry>0xd4</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0xd6</entry>
		<entry>write</entry>
		<entry>Mode Register</entry>
	      </row>
	      
	      <row>
		<entry>0xd6</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0xd8</entry>
		<entry>write</entry>
		<entry>Clear LSB/MSB Flip-Flop</entry>
	      </row>
	      
	      <row>
		<entry>0xd8</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
    

	      
	      <row>
		<entry>0xda</entry>
		<entry>write</entry>
		<entry>Master Clear/Reset</entry>
	      </row>
	      
	      <row>
		<entry>0xda</entry>
		<entry>read</entry>
		<entry>Temporary Register (not present in Intel
		  82374)</entry>
	      </row>
	      
	      <row>
		<entry>0xdc</entry>
		<entry>write</entry>
		<entry>Clear Mask Register</entry>
	      </row>
	      
	      <row>
		<entry>0xdc</entry>
		<entry>read</entry>
		<entry>-</entry>
	      </row>
	      
	      <row>
		<entry>0xde</entry>
		<entry>write</entry>
		<entry>Write All Mask Register Bits</entry>
	      </row>
	      
	      <row>
		<entry>0xdf</entry>
		<entry>read</entry>
		<entry>Read All Mask Register Bits (only in Intel
		  82374)</entry>
	      </row>
	    </tbody>
	  </tgroup>
	</informaltable>
      </sect3>
      
      <sect3>
	<title>0x80&ndash;0x9f DMA Page Registers</title>

	<informaltable frame="none">
	  <tgroup cols="3">
	    <tbody>
	      <row>
		<entry>0x87</entry>
		<entry>r/w</entry>
		<entry>Channel 0 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x83</entry>
		<entry>r/w</entry>
		<entry>Channel 1 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x81</entry>
		<entry>r/w</entry>
		<entry>Channel 2 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x82</entry>
		<entry>r/w</entry>
		<entry>Channel 3 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x8b</entry>
		<entry>r/w</entry>
		<entry>Channel 5 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x89</entry>
		<entry>r/w</entry>
		<entry>Channel 6 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x8a</entry>
		<entry>r/w</entry>
		<entry>Channel 7 Low byte (23-16) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x8f</entry>
		<entry>r/w</entry>
		<entry>Low byte page Refresh</entry>
	      </row>
	    </tbody>
	  </tgroup>
	</informaltable>
      </sect3>
      
      <sect3>
	<title>0x400&ndash;0x4ff 82374 Enhanced DMA Registers</title>

	<para>The Intel 82374 EISA System Component (ESC) was introduced in
	  early 1996 and includes a DMA controller that provides a superset of
	  8237 functionality as well as other PC-compatible core peripheral
	  components in a single package.  This chip is targeted at both EISA
	  and PCI platforms, and provides modern DMA features like
	  scatter-gather, ring buffers as well as direct access by the system
	  DMA to all 32 bits of address space.</para>
	    
	<para>If these features are used, code should also be included to
	  provide similar functionality in the previous 16 years worth of
	  PC-compatible computers.  For compatibility reasons, some of the
	  82374 registers must be programmed <emphasis>after</emphasis>
	  programming the traditional 8237 registers for each  transfer.
	  Writing to a traditional 8237 register forces the contents of some
	  of the 82374 enhanced registers to zero to provide backward software
	  compatibility.</para>

	<informaltable frame="none">
	  <tgroup cols="3">
	    <tbody>
	      <row>
		<entry>0x401</entry>
		<entry>r/w</entry>
		<entry>Channel 0 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x403</entry>
		<entry>r/w</entry>
		<entry>Channel 1 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x405</entry>
		<entry>r/w</entry>
		<entry>Channel 2 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x407</entry>
		<entry>r/w</entry>
		<entry>Channel 3 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x4c6</entry>
		<entry>r/w</entry>
		<entry>Channel 5 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x4ca</entry>
		<entry>r/w</entry>
		<entry>Channel 6 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x4ce</entry>
		<entry>r/w</entry>
		<entry>Channel 7 High byte (bits 23-16) word count</entry>
	      </row>
	      
	      <row>
		<entry>0x487</entry>
		<entry>r/w</entry>
		<entry>Channel 0 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x483</entry>
		<entry>r/w</entry>
		<entry>Channel 1 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x481</entry>
		<entry>r/w</entry>
		<entry>Channel 2 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x482</entry>
		<entry>r/w</entry>
		<entry>Channel 3 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x48b</entry>
		<entry>r/w</entry>
		<entry>Channel 5 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x489</entry>
		<entry>r/w</entry>
		<entry>Channel 6 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x48a</entry>
		<entry>r/w</entry>
		<entry>Channel 6 High byte (bits 31-24) page Register</entry>
	      </row>
	      
	      <row>
		<entry>0x48f</entry>
		<entry>r/w</entry>
		<entry>High byte page Refresh</entry>
	      </row>
	      
	      <row>
		<entry>0x4e0</entry>
		<entry>r/w</entry>
		<entry>Channel 0 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e1</entry>
		<entry>r/w</entry>
		<entry>Channel 0 Stop Register (bits 15-8)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e2</entry>
		<entry>r/w</entry>
		<entry>Channel 0 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e4</entry>
		<entry>r/w</entry>
		<entry>Channel 1 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e5</entry>
		<entry>r/w</entry>
		<entry>Channel 1 Stop Register (bits 15-8)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e6</entry>
		<entry>r/w</entry>
		<entry>Channel 1 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e8</entry>
		<entry>r/w</entry>
		<entry>Channel 2 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4e9</entry>
		<entry>r/w</entry>
		<entry>Channel 2 Stop Register (bits 15-8)</entry>
	      </row>
	      
	      <row>
		<entry>0x4ea</entry>
		<entry>r/w</entry>
		<entry>Channel 2 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x4ec</entry>
		<entry>r/w</entry>
		<entry>Channel 3 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4ed</entry>
		<entry>r/w</entry>
		<entry>Channel 3 Stop Register (bits 15-8)</entry>
	      </row>
	      
	      <row>
		<entry>0x4ee</entry>
		<entry>r/w</entry>
		<entry>Channel 3 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x4f4</entry>
		<entry>r/w</entry>
		<entry>Channel 5 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4f5</entry>
		<entry>r/w</entry>
		<entry>Channel 5 Stop Register (bits 15-8)</entry>
	      </row>
	      
	      <row>
		<entry>0x4f6</entry>
		<entry>r/w</entry>
		<entry>Channel 5 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x4f8</entry>
		<entry>r/w</entry>
		<entry>Channel 6 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4f9</entry>
		<entry>r/w</entry>
		<entry>Channel 6 Stop Register (bits 15-8)</entry>
	      </row>

	      <row>
		<entry>0x4fa</entry>
		<entry>r/w</entry>
		<entry>Channel 6 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x4fc</entry>
		<entry>r/w</entry>
		<entry>Channel 7 Stop Register (bits 7-2)</entry>
	      </row>
	      
	      <row>
		<entry>0x4fd</entry>
		<entry>r/w</entry>
		<entry>Channel 7 Stop Register (bits 15-8)</entry>
	      </row>
	      
	      <row>
		<entry>0x4fe</entry>
		<entry>r/w</entry>
		<entry>Channel 7 Stop Register (bits 23-16)</entry>
	      </row>
	      
	      <row>
		<entry>0x40a</entry>
		<entry>write</entry>
		<entry>Channels 0-3 Chaining Mode Register</entry>
	      </row>
	      
	      <row>
		<entry>0x40a</entry>
		<entry>read</entry>
		<entry>Channel Interrupt Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x4d4</entry>
		<entry>write</entry>
		<entry>Channels 4-7 Chaining Mode Register</entry>
	      </row>
	      
	      <row>
		<entry>0x4d4</entry>
		<entry>read</entry>
		<entry>Chaining Mode Status</entry>
	      </row>
	      
	      <row>
		<entry>0x40c</entry>
		<entry>read</entry>
		<entry>Chain Buffer Expiration Control Register</entry>
	      </row>
	      
	      <row>
		<entry>0x410</entry>
		<entry>write</entry>
		<entry>Channel 0 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x411</entry>
		<entry>write</entry>
		<entry>Channel 1 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x412</entry>
		<entry>write</entry>
		<entry>Channel 2 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x413</entry>
		<entry>write</entry>
		<entry>Channel 3 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x415</entry>
		<entry>write</entry>
		<entry>Channel 5 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x416</entry>
		<entry>write</entry>
		<entry>Channel 6 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x417</entry>
		<entry>write</entry>
		<entry>Channel 7 Scatter-Gather Command Register</entry>
	      </row>
	      
	      <row>
		<entry>0x418</entry>
		<entry>read</entry>
		<entry>Channel 0 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x419</entry>
		<entry>read</entry>
		<entry>Channel 1 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x41a</entry>
		<entry>read</entry>
		<entry>Channel 2 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x41b</entry>
		<entry>read</entry>
		<entry>Channel 3 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x41d</entry>
		<entry>read</entry>
		<entry>Channel 5 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x41e</entry>
		<entry>read</entry>
		<entry>Channel 5 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x41f</entry>
		<entry>read</entry>
		<entry>Channel 7 Scatter-Gather Status Register</entry>
	      </row>
	      
	      <row>
		<entry>0x420-0x423</entry>
		<entry>r/w</entry>
		<entry>Channel 0 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	      
	      <row>
		<entry>0x424-0x427</entry>
		<entry>r/w</entry>
		<entry>Channel 1 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	      
	      <row>
		<entry>0x428-0x42b</entry>
		<entry>r/w</entry>
		<entry>Channel 2 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	      
	      <row>
		<entry>0x42c-0x42f</entry>
		<entry>r/w</entry>
		<entry>Channel 3 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	      
	      <row>
		<entry>0x434-0x437</entry>
		<entry>r/w</entry>
		<entry>Channel 5 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	      
	      <row>
		<entry>0x438-0x43b</entry>
		<entry>r/w</entry>
		<entry>Channel 6 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	      
	      <row>
		<entry>0x43c-0x43f</entry>
		<entry>r/w</entry>
		<entry>Channel 7 Scatter-Gather Descriptor Table Pointer
		  Register</entry>
	      </row>
	    </tbody>
	  </tgroup>
	</informaltable>
      </sect3>
    </sect2>
  </sect1>

</chapter>
