8086 Memory Management and I/O Programming

Explore advanced memory management techniques and I/O programming concepts for the 8086 microprocessor.

Memory Organization and Management

The 8086 microprocessor manages memory through a segmented architecture that allows efficient access to large memory spaces using 16-bit registers.

Loading diagram...

Memory Segmentation Details

Each segment can be up to 64KB in size, and segments can overlap or be completely separate depending on the application requirements.


; Segment register initialization example
.MODEL SMALL
.STACK 100H
.DATA
    data_array DW 1000 DUP(?)
    buffer DB 512 DUP(?)

.CODE
START:
    MOV AX, @DATA       ; Load data segment address
    MOV DS, AX          ; Initialize DS
    MOV ES, AX          ; Initialize ES (same as DS)
    
    ; Different segment for extra data
    MOV AX, 2000H       ; Different segment
    MOV ES, AX          ; ES points to different segment
    
    ; Now DS and ES point to different segments
    MOV [SI], AX        ; Store to DS:SI
    MOV ES:[DI], BX     ; Store to ES:DI

Memory Allocation Strategies

  • Static Allocation: Memory allocated at compile time
  • Dynamic Allocation: Memory allocated at runtime using DOS functions
  • Stack Allocation: Temporary storage using stack operations
  • Heap Management: Custom memory management routines

Advanced Memory Operations

Advanced memory management includes techniques for efficient data movement, memory copying, and large data structure handling.

Block Memory Operations


; Fast memory copy using string instructions
FAST_MEMCPY PROC
    ; Input: SI = source, DI = destination, CX = word count
    ; Copy CX words from DS:SI to ES:DI
    
    CLD                 ; Clear direction flag (forward)
    CMP CX, 8           ; Check if worth optimizing
    JL SIMPLE_COPY
    
    ; Optimize for word-aligned copies
    REP MOVSW           ; Copy words (faster than bytes)
    RET
    
SIMPLE_COPY:
    ; Simple byte copy for small amounts
    SHL CX, 1           ; Convert word count to byte count
    REP MOVSB
    RET
FAST_MEMCPY ENDP

; Memory fill operation
MEMORY_FILL PROC
    ; Input: DI = destination, AX = fill value, CX = count
    CLD
    REP STOSW           ; Fill memory with word value
    RET
MEMORY_FILL ENDP

; Memory compare operation
MEMORY_COMPARE PROC
    ; Input: SI = source1, DI = source2, CX = count
    ; Output: ZF = 1 if equal, ZF = 0 if different
    CLD
    REPE CMPSB          ; Compare bytes while equal
    RET
MEMORY_COMPARE ENDP

Segment Management


; Dynamic segment allocation
ALLOCATE_SEGMENT PROC
    ; Allocate a new 64KB segment
    ; Output: AX = segment address, CF = error flag
    
    MOV AH, 48H         ; DOS allocate memory function
    MOV BX, 1000H       ; Request 64KB (4096 paragraphs)
    INT 21H
    
    JC ALLOC_ERROR      ; Jump if error
    ; AX contains segment address
    CLC                 ; Clear carry flag (success)
    RET
    
ALLOC_ERROR:
    STC                 ; Set carry flag (error)
    RET
ALLOCATE_SEGMENT ENDP

; Free allocated segment
FREE_SEGMENT PROC
    ; Input: ES = segment to free
    MOV AH, 49H         ; DOS free memory function
    INT 21H
    RET
FREE_SEGMENT ENDP

Large Data Structure Management


; Handle data structures larger than 64KB
LARGE_ARRAY_ACCESS PROC
    ; Input: DX:AX = element index, BX = element size
    ; Output: ES:DI = address of element
    
    ; Calculate byte offset = index × element_size
    MUL BX              ; DX:AX = index × element_size
    
    ; Calculate segment offset
    MOV CX, DX          ; Save high word
    MOV DI, AX          ; Low word becomes offset
    
    ; Convert high word to segments (divide by 16)
    MOV AX, CX
    MOV CL, 4
    SHR AX, CL          ; Divide by 16
    
    ; Add to base segment
    ADD AX, [BASE_SEGMENT]
    MOV ES, AX
    
    RET
LARGE_ARRAY_ACCESS ENDP

BASE_SEGMENT DW ?

I/O Programming Fundamentals

The 8086 provides two types of I/O: Memory-mapped I/O and Port-mapped I/O. Understanding both approaches is essential for hardware interfacing.

Loading diagram...

Port-Mapped I/O


; Basic port I/O operations
; Reading from ports
IN AL, 60H          ; Read byte from port 60H (keyboard data)
IN AX, 40H          ; Read word from port 40H (timer)

; Using DX for port address (allows full 16-bit range)
MOV DX, 378H        ; Parallel port address
IN AL, DX           ; Read from parallel port
OUT DX, AL          ; Write to parallel port

; 16-bit port I/O
MOV DX, 1F0H        ; IDE controller data port
IN AX, DX           ; Read 16-bit data
OUT DX, AX          ; Write 16-bit data

; Port bit manipulation
MOV DX, 21H         ; Interrupt mask register
IN AL, DX           ; Read current mask
OR AL, 02H          ; Set bit 1 (disable IRQ1)
OUT DX, AL          ; Write back modified mask

Memory-Mapped I/O


; Video memory access (memory-mapped I/O)
.MODEL SMALL
.CODE
START:
    MOV AX, 0B800H      ; CGA/VGA text mode video segment
    MOV ES, AX          ; ES points to video memory
    
    ; Write character directly to video memory
    MOV DI, 0           ; Top-left corner (row 0, col 0)
    MOV AL, 'A'         ; Character to display
    MOV AH, 07H         ; Attribute (white on black)
    MOV ES:[DI], AX     ; Write to video memory
    
    ; Write to position (row 5, column 10)
    MOV AX, 5           ; Row
    MOV BX, 80          ; Columns per row
    MUL BX              ; AX = row × 80
    ADD AX, 10          ; Add column
    SHL AX, 1           ; Multiply by 2 (2 bytes per character)
    MOV DI, AX          ; DI = byte offset
    
    MOV AL, 'B'         ; Character
    MOV AH, 0FH         ; Bright white on black
    MOV ES:[DI], AX     ; Write to video memory

Hardware Register Programming


; Programmable Interval Timer (8253/8254) programming
INIT_TIMER PROC
    ; Initialize timer for square wave generation
    
    ; Program timer 0 for 1000 Hz output
    MOV AL, 36H         ; Timer 0, LSB/MSB, Mode 3 (square wave)
    OUT 43H, AL         ; Command register
    
    ; Calculate count value: 1193180 / 1000 = 1193
    MOV AX, 1193        ; Count value
    OUT 40H, AL         ; Send low byte
    MOV AL, AH
    OUT 40H, AL         ; Send high byte
    
    RET
INIT_TIMER ENDP

; Parallel port programming
PARALLEL_INIT PROC
    ; Initialize parallel port for output
    MOV DX, 37AH        ; Control port
    IN AL, DX           ; Read current settings
    AND AL, 0DFH        ; Clear bit 5 (enable output)
    OUT DX, AL          ; Write back
    RET
PARALLEL_INIT ENDP

PARALLEL_WRITE PROC
    ; Input: AL = data to write
    MOV DX, 378H        ; Data port
    OUT DX, AL          ; Write data
    
    ; Strobe signal
    MOV DX, 37AH        ; Control port
    IN AL, DX
    OR AL, 01H          ; Set strobe bit
    OUT DX, AL
    AND AL, 0FEH        ; Clear strobe bit
    OUT DX, AL
    RET
PARALLEL_WRITE ENDP

DMA Programming

Direct Memory Access (DMA) allows peripherals to transfer data directly to/from memory without CPU intervention, improving system performance.

Loading diagram...

DMA Controller Programming


; DMA channel setup for data transfer
SETUP_DMA PROC
    ; Setup DMA channel 1 for memory-to-memory transfer
    ; Input: SI = source, DI = destination, CX = count
    
    CLI                 ; Disable interrupts during setup
    
    ; Disable DMA channel 1
    MOV AL, 05H         ; Channel 1 mask bit
    OUT 0AH, AL         ; Single channel mask register
    
    ; Clear byte pointer flip-flop
    OUT 0CH, AL         ; Any value clears flip-flop
    
    ; Set transfer mode
    MOV AL, 45H         ; Channel 1, read transfer, single mode
    OUT 0BH, AL         ; Mode register
    
    ; Set source address (channel 1 address register)
    MOV AX, SI
    OUT 02H, AL         ; Low byte
    MOV AL, AH
    OUT 02H, AL         ; High byte
    
    ; Set page register for channel 1
    MOV AX, DS
    MOV CL, 4
    SHR AX, CL          ; Convert segment to page
    OUT 83H, AL         ; Page register
    
    ; Set transfer count
    DEC CX              ; DMA count is n-1
    MOV AL, CL
    OUT 03H, AL         ; Low byte
    MOV AL, CH
    OUT 03H, AL         ; High byte
    
    ; Enable DMA channel 1
    MOV AL, 01H         ; Channel 1 enable
    OUT 0AH, AL         ; Single channel mask register
    
    STI                 ; Re-enable interrupts
    RET
SETUP_DMA ENDP

DMA Transfer Monitoring


; Check DMA transfer status
CHECK_DMA_STATUS PROC
    ; Check if DMA transfer is complete
    ; Output: ZF = 1 if complete, ZF = 0 if in progress
    
    IN AL, 08H          ; DMA status register
    TEST AL, 02H        ; Test channel 1 terminal count
    RET
CHECK_DMA_STATUS ENDP

; Wait for DMA completion
WAIT_DMA_COMPLETE PROC
    PUSH AX
    
WAIT_LOOP:
    CALL CHECK_DMA_STATUS
    JZ WAIT_LOOP        ; Continue waiting if not complete
    
    POP AX
    RET
WAIT_DMA_COMPLETE ENDP

Interrupt-Driven I/O

Interrupt-driven I/O provides efficient handling of slow I/O devices without blocking the CPU.

Serial Port Programming


; 8250 UART programming for serial communication
INIT_SERIAL PROC
    ; Initialize COM1 (3F8H) for 9600 baud, 8N1
    
    ; Set DLAB (Divisor Latch Access Bit)
    MOV DX, 3FBH        ; Line Control Register
    IN AL, DX
    OR AL, 80H          ; Set DLAB
    OUT DX, AL
    
    ; Set baud rate to 9600 (divisor = 12)
    MOV DX, 3F8H        ; Divisor Latch Low
    MOV AL, 12          ; Low byte of divisor
    OUT DX, AL
    MOV DX, 3F9H        ; Divisor Latch High
    MOV AL, 0           ; High byte of divisor
    OUT DX, AL
    
    ; Configure line parameters (8N1)
    MOV DX, 3FBH        ; Line Control Register
    MOV AL, 03H         ; 8 bits, no parity, 1 stop bit
    OUT DX, AL          ; This also clears DLAB
    
    ; Enable interrupts
    MOV DX, 3F9H        ; Interrupt Enable Register
    MOV AL, 01H         ; Enable received data interrupt
    OUT DX, AL
    
    ; Set RTS and DTR
    MOV DX, 3FCH        ; Modem Control Register
    MOV AL, 03H         ; Set RTS and DTR
    OUT DX, AL
    
    RET
INIT_SERIAL ENDP

; Serial interrupt handler
SERIAL_ISR PROC
    PUSH AX
    PUSH DX
    PUSH DS
    
    MOV AX, @DATA
    MOV DS, AX
    
    ; Check interrupt type
    MOV DX, 3FAH        ; Interrupt Identification Register
    IN AL, DX
    
    TEST AL, 01H        ; Check if interrupt pending
    JNZ ISR_EXIT        ; No interrupt pending
    
    AND AL, 06H         ; Mask interrupt type bits
    CMP AL, 04H         ; Received data available?
    JE HANDLE_RECEIVE
    
    CMP AL, 02H         ; Transmitter holding register empty?
    JE HANDLE_TRANSMIT
    
    JMP ISR_EXIT

HANDLE_RECEIVE:
    MOV DX, 3F8H        ; Data register
    IN AL, DX           ; Read received character
    ; Store character in buffer
    MOV BX, OFFSET rx_buffer
    MOV SI, rx_head
    MOV [BX+SI], AL
    INC SI
    CMP SI, BUFFER_SIZE
    JL NO_WRAP
    MOV SI, 0
NO_WRAP:
    MOV rx_head, SI
    JMP ISR_EXIT

HANDLE_TRANSMIT:
    ; Handle transmit interrupt
    ; Send next character from transmit buffer
    
ISR_EXIT:
    ; Send EOI to interrupt controller
    MOV AL, 20H
    OUT 20H, AL         ; Send EOI to PIC
    
    POP DS
    POP DX
    POP AX
    IRET
SERIAL_ISR ENDP

BUFFER_SIZE EQU 256
rx_buffer DB BUFFER_SIZE DUP(?)
rx_head DW 0
rx_tail DW 0

Keyboard Controller Programming


; Low-level keyboard controller programming
KEYBOARD_INIT PROC
    ; Initialize 8042 keyboard controller
    
    ; Disable keyboard
    CALL WAIT_INPUT_EMPTY
    MOV AL, 0ADH        ; Disable keyboard command
    OUT 64H, AL
    
    ; Read configuration
    CALL WAIT_INPUT_EMPTY
    MOV AL, 20H         ; Read configuration command
    OUT 64H, AL
    CALL WAIT_OUTPUT_FULL
    IN AL, 60H          ; Read configuration byte
    
    ; Modify configuration
    OR AL, 01H          ; Enable keyboard interrupt
    AND AL, 0BFH        ; Clear keyboard disable bit
    MOV BL, AL          ; Save modified configuration
    
    ; Write configuration
    CALL WAIT_INPUT_EMPTY
    MOV AL, 60H         ; Write configuration command
    OUT 64H, AL
    CALL WAIT_INPUT_EMPTY
    MOV AL, BL          ; Modified configuration
    OUT 60H, AL
    
    ; Enable keyboard
    CALL WAIT_INPUT_EMPTY
    MOV AL, 0AEH        ; Enable keyboard command
    OUT 64H, AL
    
    RET
KEYBOARD_INIT ENDP

WAIT_INPUT_EMPTY PROC
    PUSH AX
WAIT_INPUT:
    IN AL, 64H          ; Read status register
    TEST AL, 02H        ; Check input buffer full bit
    JNZ WAIT_INPUT      ; Wait until empty
    POP AX
    RET
WAIT_INPUT_EMPTY ENDP

WAIT_OUTPUT_FULL PROC
    PUSH AX
WAIT_OUTPUT:
    IN AL, 64H          ; Read status register
    TEST AL, 01H        ; Check output buffer full bit
    JZ WAIT_OUTPUT      ; Wait until full
    POP AX
    RET
WAIT_OUTPUT_FULL ENDP

Performance Optimization

Optimizing memory access and I/O operations is crucial for high-performance 8086 applications.

Loading diagram...

Memory Access Optimization


; Optimized memory operations
FAST_MEMORY_CLEAR PROC
    ; Clear large memory block efficiently
    ; Input: ES:DI = destination, CX = word count
    
    MOV AX, 0           ; Fill value (zero)
    CLD                 ; Forward direction
    
    ; Use word operations for speed
    REP STOSW           ; Fill with words (faster than bytes)
    
    RET
FAST_MEMORY_CLEAR ENDP

; Cache-friendly array processing
PROCESS_ARRAY_OPTIMIZED PROC
    ; Process array in cache-friendly manner
    ; Input: SI = array address, CX = element count
    
    MOV BX, 0           ; Array index
    
PROCESS_LOOP:
    ; Load multiple elements to reduce memory access
    MOV AX, [SI+BX]     ; Element 0
    MOV DX, [SI+BX+2]   ; Element 1
    
    ; Process both elements
    ADD AX, 100         ; Process element 0
    ADD DX, 100         ; Process element 1
    
    ; Store back
    MOV [SI+BX], AX
    MOV [SI+BX+2], DX
    
    ADD BX, 4           ; Move to next pair
    SUB CX, 2           ; Processed 2 elements
    JA PROCESS_LOOP     ; Continue if more elements
    
    RET
PROCESS_ARRAY_OPTIMIZED ENDP

I/O Performance Optimization


; Buffered I/O for better performance
BUFFERED_READ PROC
    ; Buffered file reading
    ; Input: BX = file handle, CX = bytes to read
    ; Output: Buffer contains data, AX = bytes read
    
    CMP CX, BUFFER_SIZE
    JLE DIRECT_READ
    
    ; Large read - use multiple buffer loads
    MOV DI, 0           ; Total bytes read
    
BUFFER_LOOP:
    PUSH CX
    MOV CX, BUFFER_SIZE ; Read buffer size
    MOV AH, 3FH         ; DOS read function
    MOV DX, OFFSET read_buffer
    INT 21H
    
    ; Copy from buffer to destination
    MOV SI, OFFSET read_buffer
    MOV CX, AX          ; Bytes actually read
    REP MOVSB           ; Copy to user buffer
    
    ADD DI, AX          ; Add to total
    POP CX
    SUB CX, AX          ; Subtract from remaining
    JA BUFFER_LOOP      ; Continue if more to read
    
    MOV AX, DI          ; Return total bytes read
    RET
    
DIRECT_READ:
    ; Small read - direct operation
    MOV AH, 3FH
    MOV DX, OFFSET user_buffer
    INT 21H
    RET
BUFFERED_READ ENDP

BUFFER_SIZE EQU 4096
read_buffer DB BUFFER_SIZE DUP(?)
user_buffer DB 8192 DUP(?)

Numerical Problems and Examples

Problem 1: Memory Segmentation Calculation

Question: Calculate the physical addresses for the following segment:offset pairs: 1234:5678, ABCD:EF01, FFFF:0010


Solution:
Physical Address = (Segment × 16) + Offset

1. 1234:5678
   Physical Address = (1234H × 16) + 5678H
                    = 12340H + 5678H
                    = 179B8H

2. ABCD:EF01  
   Physical Address = (ABCDH × 16) + EF01H
                    = ABCD0H + EF01H
                    = 1ABD1H

3. FFFF:0010
   Physical Address = (FFFFH × 16) + 0010H
                    = FFFF0H + 0010H
                    = 100000H

Note: The last address (100000H) exceeds the 8086's 20-bit
address space (FFFFFH), demonstrating address wraparound.

Answer: 179B8H, 1ABD1H, 100000H (with wraparound)

Problem 2: DMA Transfer Time Calculation

Question: Calculate the time to transfer 64KB using DMA at 1 MB/s transfer rate.


Solution:
Given:
- Data size = 64KB = 65,536 bytes
- Transfer rate = 1 MB/s = 1,048,576 bytes/s

Transfer Time = Data Size / Transfer Rate
              = 65,536 bytes / 1,048,576 bytes/s
              = 0.0625 seconds
              = 62.5 milliseconds

Answer: 62.5 ms

Problem 3: Port Address Decoding

Question: Design an address decoder for I/O ports 300H-30FH. What are the address lines needed?


Solution:
Port range: 300H - 30FH (16 consecutive ports)

Binary analysis:
300H = 0011 0000 0000 0000B
30FH = 0011 0000 0000 1111B

Address lines needed:
- A15-A4: Must be 001100000000B (fixed)
- A3-A0: Variable bits for port selection (0000-1111)

Decoder logic:
Enable = A15' · A14' · A13 · A12 · A11' · A10' · A9' · A8' · A7' · A6' · A5' · A4'

Port selection: A3, A2, A1, A0

Answer: Need A15-A4 for base address decoding, A3-A0 for port selection

Problem 4: Buffer Size Optimization

Question: Calculate optimal buffer size for disk I/O if disk access time is 10ms and transfer rate is 500 KB/s.


Solution:
Given:
- Disk access time (seek + latency) = 10ms
- Transfer rate = 500 KB/s = 500,000 bytes/s

During 10ms access time, we could transfer:
Transfer capacity = 500,000 bytes/s × 0.01s = 5,000 bytes

Optimal buffer size should be at least 5KB to minimize
the impact of access time overhead.

However, practical considerations:
- Memory constraints in 8086 systems
- Typical buffer sizes: 512 bytes, 1KB, 2KB, 4KB

Recommendation: Use 4KB buffer as compromise between
performance and memory usage.

Answer: Optimal buffer size ≈ 4KB

Real-World Applications

Understanding practical applications helps in applying memory management and I/O concepts effectively.

Data Acquisition System


; Real-time data acquisition using interrupts and buffering
.MODEL SMALL
.DATA
    sample_buffer DW 1000 DUP(?)   ; Circular buffer
    buffer_head DW 0               ; Write pointer
    buffer_tail DW 0               ; Read pointer
    samples_lost DW 0              ; Overflow counter
    
.CODE
START:
    ; Initialize ADC and timer
    CALL INIT_ADC
    CALL INIT_TIMER
    
    ; Main processing loop
MAIN_LOOP:
    CALL PROCESS_SAMPLES
    CALL UPDATE_DISPLAY
    JMP MAIN_LOOP

; Timer interrupt for sampling
TIMER_ISR PROC
    PUSH AX
    PUSH BX
    PUSH DS
    
    MOV AX, @DATA
    MOV DS, AX
    
    ; Read ADC value
    CALL READ_ADC           ; Result in AX
    
    ; Store in circular buffer
    MOV BX, buffer_head
    MOV sample_buffer[BX], AX
    ADD BX, 2               ; Next word position
    CMP BX, 2000            ; End of buffer?
    JL NO_WRAP_HEAD
    MOV BX, 0               ; Wrap to beginning
    
NO_WRAP_HEAD:
    ; Check for buffer overflow
    CMP BX, buffer_tail
    JNE NO_OVERFLOW
    INC samples_lost        ; Count lost sample
    JMP ISR_EXIT
    
NO_OVERFLOW:
    MOV buffer_head, BX     ; Update head pointer
    
ISR_EXIT:
    POP DS
    POP BX
    POP AX
    IRET
TIMER_ISR ENDP

PROCESS_SAMPLES PROC
    ; Process available samples
    MOV BX, buffer_tail
    
PROCESS_LOOP:
    CMP BX, buffer_head     ; Any samples available?
    JE PROCESS_DONE
    
    MOV AX, sample_buffer[BX] ; Get sample
    CALL ANALYZE_SAMPLE     ; Process sample
    
    ADD BX, 2               ; Next sample
    CMP BX, 2000            ; Wrap check
    JL NO_WRAP_TAIL
    MOV BX, 0
    
NO_WRAP_TAIL:
    MOV buffer_tail, BX     ; Update tail
    JMP PROCESS_LOOP
    
PROCESS_DONE:
    RET
PROCESS_SAMPLES ENDP

Multi-Tasking System


; Simple cooperative multitasking system
TASK_CONTROL STRUC
    task_sp     DW ?        ; Stack pointer
    task_ss     DW ?        ; Stack segment
    task_state  DB ?        ; Task state
    task_priority DB ?      ; Priority level
TASK_CONTROL ENDS

.DATA
    current_task DW 0
    task_table TASK_CONTROL 4 DUP(<>)  ; 4 tasks maximum
    
.CODE
; Task scheduler (called by timer interrupt)
SCHEDULER PROC
    ; Save current task context
    MOV BX, current_task
    MOV AX, SIZE TASK_CONTROL
    MUL BX                  ; BX = task index × structure size
    MOV BX, AX
    
    MOV task_table[BX].task_sp, SP
    MOV task_table[BX].task_ss, SS
    
    ; Find next ready task
    MOV CX, 4               ; Number of tasks
    MOV DX, current_task
    
FIND_TASK:
    INC DX
    CMP DX, 4
    JL CHECK_TASK
    MOV DX, 0               ; Wrap to first task
    
CHECK_TASK:
    MOV BX, DX
    MOV AX, SIZE TASK_CONTROL
    MUL BX
    MOV BX, AX
    
    CMP task_table[BX].task_state, 1  ; Ready state?
    JE SWITCH_TASK
    
    LOOP FIND_TASK
    
    ; No ready task found, continue current
    RET
    
SWITCH_TASK:
    MOV current_task, DX
    
    ; Restore new task context
    MOV SS, task_table[BX].task_ss
    MOV SP, task_table[BX].task_sp
    
    RET
SCHEDULER ENDP

Summary and Best Practices

Effective memory management and I/O programming require understanding of hardware limitations and optimal use of available resources.

Loading diagram...

Key Guidelines

  • Memory Efficiency: Use appropriate data structures and avoid memory waste
  • I/O Performance: Use buffering and interrupt-driven techniques
  • Error Handling: Always check for and handle error conditions
  • Resource Management: Properly allocate and free system resources
  • Real-Time Constraints: Meet timing requirements for time-critical applications
  • Hardware Compatibility: Understand target hardware limitations

Suggetested Articles