Lesson 31 of 48 intermediate

Arrays, Tables, and Block Operations

Organize data into arrays and tables, then process them efficiently with index registers

Open interactive version (quiz + challenge)

Real-world analogy

An array in assembly is like a row of mailboxes in an apartment building. Each mailbox (element) has an address (offset). The building's starting address is the array label, and SI or DI is like a mail carrier walking from box to box. A lookup table is like a speed-dial list — instead of computing a result, you just look it up by index. XLAT is the one-button speed-dial.

What is it?

Arrays in 8086 assembly are contiguous blocks of memory defined with DB or DW, accessed via index registers (SI, DI) and base+offset addressing. Lookup tables provide O(1) precomputed results using XLAT or indexed access. Block operations copy, search, or transform arrays element by element. These data structures and operations are the assembly-level foundation of every high-level array, list, and table.

Real-world relevance

Arrays and lookup tables are everywhere in embedded systems. A keyboard controller uses a lookup table to convert scan codes to ASCII characters. Display drivers store font bitmaps as 2D arrays. Sensor systems accumulate readings into arrays and compute averages. Sorting arrays of measurements enables finding medians for noise rejection.

Key points

Code example

; Program: Reverse an array and find its maximum element
.MODEL SMALL
.STACK 100h
.DATA
  arr     DB 34, 12, 78, 45, 23, 67, 89, 11, 56
  arr_len EQU 9
  rev_arr DB arr_len DUP(?)
  max_val DB ?
  msg1    DB 'Max value: $'
  msg2    DB 0Dh, 0Ah, 'Reversed: $'

.CODE
MAIN PROC
  MOV AX, @DATA
  MOV DS, AX

  ; --- Find maximum ---
  LEA SI, arr
  MOV AL, [SI]          ; max = first element
  MOV CX, arr_len - 1
  INC SI
find_max:
  CMP AL, [SI]
  JAE not_bigger
  MOV AL, [SI]          ; update max
not_bigger:
  INC SI
  LOOP find_max
  MOV [max_val], AL     ; store max (89)

  ; --- Reverse array ---
  LEA SI, arr           ; SI = start of source
  LEA DI, rev_arr
  ADD DI, arr_len - 1   ; DI = end of reversed

  MOV CX, arr_len
reverse_loop:
  MOV AL, [SI]
  MOV [DI], AL
  INC SI
  DEC DI
  LOOP reverse_loop

  ; --- Display max ---
  MOV AH, 09h
  LEA DX, msg1
  INT 21h
  MOV AL, [max_val]
  CALL print_num

  ; --- Display reversed array ---
  MOV AH, 09h
  LEA DX, msg2
  INT 21h
  LEA SI, rev_arr
  MOV CX, arr_len
show_loop:
  MOV AL, [SI]
  CALL print_num
  MOV DL, ' '
  MOV AH, 02h
  INT 21h           ; space separator
  INC SI
  LOOP show_loop

  MOV AH, 4Ch
  INT 21h
MAIN ENDP

; Print AL as decimal number (0-255)
print_num PROC NEAR
  XOR AH, AH
  MOV BL, 100
  DIV BL              ; AL = hundreds, AH = remainder
  CMP AL, 0
  JE skip_hundreds
  ADD AL, 30h
  MOV DL, AL
  PUSH AX
  MOV AH, 02h
  INT 21h
  POP AX
skip_hundreds:
  MOV AL, AH          ; remainder
  XOR AH, AH
  MOV BL, 10
  DIV BL              ; AL = tens, AH = ones
  CMP AL, 0
  JE skip_tens
  ADD AL, 30h
  MOV DL, AL
  PUSH AX
  MOV AH, 02h
  INT 21h
  POP AX
skip_tens:
  MOV AL, AH
  ADD AL, 30h
  MOV DL, AL
  MOV AH, 02h
  INT 21h
  RET
print_num ENDP
END MAIN

Line-by-line walkthrough

  1. 1. arr DB 34, 12, 78, ... — define 9 bytes in consecutive memory. The label 'arr' marks the starting offset
  2. 2. arr_len EQU 9 — constant for array length, used everywhere to avoid magic numbers
  3. 3. Find max loop: initialize AL with the first element, then compare with each remaining element using JAE to skip if current max is already >= element
  4. 4. Reverse loop: SI starts at the beginning of arr, DI starts at the end of rev_arr. Copy [SI] to [DI], then SI moves forward while DI moves backward
  5. 5. ADD DI, arr_len - 1 — positions DI at the last byte of rev_arr (offset 8 from start for a 9-element array)
  6. 6. print_num divides by 100 to get the hundreds digit, then by 10 to get the tens digit, and the ones are what remains
  7. 7. Each digit is converted to ASCII by adding 30h and printed with DOS function 02h
  8. 8. skip_hundreds / skip_tens — suppress leading zeros by checking if the digit is 0 before printing

Spot the bug

LEA SI, word_arr
MOV CX, 5
XOR AX, AX
add_loop:
  ADD AX, [SI]
  INC SI
  LOOP add_loop
Need a hint?
What is the size of each element in a word array, and how far does INC SI move?
Show answer
Bug: INC SI only moves 1 byte, but word_arr has 2-byte (word) elements. The second iteration reads the second byte of element 0 and the first byte of element 1 — a misaligned read. Fix: replace INC SI with ADD SI, 2 to advance by a full word.

Explain like I'm 5

Imagine you have a row of cubbyholes in your classroom, each labeled with a number. That is an array. To find something, you start at cubbyhole 0 and look at each one in order — that is traversal with SI. A lookup table is like a poster on the wall that says 'If it is Monday, wear blue. If Tuesday, wear red...' Instead of deciding, you just look it up. XLAT is pointing at the poster with your finger (index in AL) and reading the answer instantly.

Fun fact

The XLAT instruction was specifically designed for code page translations — converting characters from one encoding to another using a 256-byte lookup table. IBM PCs used it heavily to map between EBCDIC (used on mainframes) and ASCII (used on PCs) when transferring data over terminal connections.

Hands-on challenge

Write an 8086 program that: (1) defines a byte array of 10 student scores, (2) finds the minimum, maximum, and average, (3) counts how many scores are above the average, and (4) displays all four results. Use procedures for find_min, find_max, and compute_average.

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z