13.2 DMA and the 8237 Controller
A programmed I/O loop moves every byte through a CPU register. That is appropriate for a short command, but a large stream makes the processor repeat the same read, write, and address-update work. Direct memory access (DMA) lets a hardware controller transfer data between an I/O device and memory. The CPU sets up the job, lends the system bus to the controller, and handles completion.
Transfer bus ownership without electrical contention
Only one bus master may drive the address and control lines at a time. An 8237-compatible controller requests ownership with HOLD. The CPU finishes its current bus cycle, stops driving the shared bus, and answers with hold acknowledge (HLDA). The DMA controller can then place an address on the bus and issue memory and I/O read/write strobes.
A device uses DMA request (DREQ) to ask its channel for a transfer. The controller answers the selected device with DMA acknowledge (DACK). A safe transfer follows this observable handshake:
1. A device asserts DREQ.
2. The controller asserts HOLD.
3. The CPU completes its current bus cycle, releases the bus, and asserts HLDA.
4. The controller asserts the matching DACK, drives the address, and performs transfer cycles.
5. At the end of its ownership interval, the controller releases HOLD; the CPU removes HLDA and resumes.
In single-transfer or cycle-stealing mode, the controller gives the bus back after each transfer and must arbitrate again for the next byte. CPU progress is interleaved with DMA. In block mode, the controller retains the bus for the programmed block, maximizing transfer throughput but delaying CPU bus access. Demand mode continues while DREQ remains asserted. These modes trade foreground responsiveness against transfer completion time.
Driving the bus before HLDA creates contention: both CPU and controller may drive incompatible values onto the same wires. That is not merely a scheduling error; it is an invalid electrical state. The ownership lab makes this incorrect state explicit.
Configure a channel, then verify every transferred address
The 8237 provides four DMA channels. Each channel has a current address register, a current word-count register, request and mask state, and mode settings. Despite its historical name, a standard 8237 transfer is one byte wide. Its word count means the number of transfer units minus one:
Thus, 16 bytes require a count value of 15. After every transfer, the address increments or decrements and the count decreases. When the count rolls from 0000H to FFFFH, the controller produces terminal count (TC) and ends the block. Programming the desired byte count directly causes an off-by-one transfer.
Direction is named from the controller's view of memory: an I/O-to-memory job writes memory, whereas memory-to-I/O reads memory. A channel mask prevents selection without destroying its configuration. Auto-initialization restores base address and base count after terminal count, useful for a repeated buffer but dangerous if software expected a one-shot job.
Classic PC-style systems combine the 8237's 16-bit address with an external page register to reach beyond 64 KiB. This exposes a boundary hazard: the controller increments its low 16-bit address but does not automatically carry into that external page register. A robust design chooses a buffer that does not cross the controller's address window and verifies the first address, final address, direction, exact transfer count, and terminal-count behavior.
Section 13.2 moved a block while preserving exclusive bus ownership and exact channel state. Section 13.3 applies the same setup–status–service discipline to a link that carries one bit at a time.