Program-controlled I/O (also called polling) is the simplest way to talk to an I/O device: the processor sits in a tight loop reading the device’s STATUS register, waiting for it to indicate “ready,” then reads or writes the DATA register. The whole interaction is driven by the processor’s running program.

The structure of a polling loop:

WAIT:
    LoadByte R4, DEVICE_STATUS    # read status
    And      R4, R4, #READY_BIT   # mask out the ready bit
    Branch_if_Zero R4, WAIT       # loop until ready
    LoadByte R5, DEVICE_DATA      # now read data

Read status, check the ready flag, branch back to start if not ready. Once the flag is set, do the transfer.

Worked example: read a line from the keyboard

    Move R2, #LOC                ; R2 points to memory buffer
    Move R3, #CR                  ; Load Carriage Return ASCII

READ:
    # Poll keyboard
    LoadByte R4, KBD_STATUS
    And      R4, R4, #2           ; Check Bit 1 (KIN)
    Branch_if_Zero R4, READ       ; loop if not ready
    LoadByte R5, KBD_DATA         ; Read Data (clears KIN)
    StoreByte R5, (R2)            ; Store in memory
    Add      R2, R2, #1           ; Increment pointer

ECHO:
    # Poll display
    LoadByte R4, DISP_STATUS
    And      R4, R4, #4           ; Check Bit 2 (DOUT)
    Branch_if_Zero R4, ECHO       ; loop if not ready
    StoreByte R5, DISP_DATA       ; Write to display (clears DOUT)
    
    # Check for Carriage Return to exit
    Branch_if_Equal R5, R3, EXIT
    Branch READ                   ; Get next character

EXIT:

The structure: poll keyboard → read character → poll display → echo character → check if it was Enter → repeat.

Pros

  • Simple. No interrupts, no special hardware, no asynchronous behavior to reason about.
  • Predictable timing. You know exactly when each byte gets read.
  • Easy to debug. Single thread of control.

Cons

  • Wastes CPU cycles. A keyboard might wait seconds between keypresses, but the processor spins flat-out checking the status. A 3 GHz CPU running a tight 4-instruction polling loop iterates about 750 million times per second — so per second of idle, that’s hundreds of millions of useless iterations.
  • Can’t do other work. While polling, the processor isn’t running anything else.
  • Latency depends on the polling rate. If the loop is slow (the processor is doing other things between polls), characters can be missed if the device buffer fills up.

The waste is what motivates interrupts — let the device alert the processor when it’s ready, and the processor can do useful work in the meantime.

When polling is still right

Polling isn’t always wrong. It’s the right choice when:

  • The device transfers data continuously and at a high rate (e.g., reading from a fast disk into a tight loop — interrupts would just add overhead).
  • The processor has nothing else to do.
  • The interrupt mechanism is unavailable or unreliable.
  • Latency must be minimal and deterministic (real-time systems sometimes prefer polling over interrupts for jitter reasons).

For most general-purpose computing, especially with slow human-input devices like keyboards and mice, interrupts win. But polling is the right starting point for understanding I/O — get the basic mechanism, then add asynchrony.