Lesson 47 of 48 advanced

Designing a Small Microprocessor-Based System

Putting All the Pieces Together

Open interactive version (quiz + challenge)

Real-world analogy

Designing a microprocessor system is like planning a small town. The CPU is the town hall (makes all decisions). RAM is the scratchpad office (temporary workspace). ROM is the library (permanent reference). The 8255 is the post office (handles mail in and out). The 8259 is the town dispatcher (routes emergency calls by priority). Address decoding is the street numbering system — without it, mail goes to the wrong address and the whole town falls apart. A good town plan (memory map) prevents chaos.

What is it?

This lesson integrates all previous concepts into a complete microprocessor-based system design. It covers the systematic process: defining requirements, selecting components (8086 + ROM + RAM + 8255 + 8254 + 8259 + 8251), designing the memory and I/O maps, implementing address decoding with 74LS138 decoders, wiring chip-select logic, writing initialization firmware, and validating the design. The result is a functional embedded system that can read input, process data, display output, handle interrupts, and communicate serially.

Real-world relevance

This is exactly how embedded systems engineers design products today — just with modern components. A car's engine control unit follows the same design process: select an MCU (now ARM), add sensors (via ADC/I/O), add actuators (via GPIO/PWM), design the memory map, write initialization firmware, implement ISRs for real-time events, and validate everything. The tools and chips have evolved, but the methodology is identical to what we practice here with the 8086.

Key points

Code example

; =============================================
; Complete 8086 System Design
; =============================================
;
; Components:
;   8086 CPU @ 5MHz
;   27256 ROM (32KB) at F8000-FFFFF
;   62256 RAM (32KB) at 00000-07FFF
;   8255 PPI at I/O 80-83h
;   8254 Timer at I/O 40-43h
;   8259 PIC at I/O 20-21h
;   8251 USART at I/O 60-61h
;
; === Address Decoding Equations ===
;
; CS_ROM = M/IO AND A19 AND A18 AND A17
;          AND A16 AND A15
; CS_RAM = M/IO AND NOT(A19) AND NOT(A18)
;          AND NOT(A17) AND NOT(A15)
;
; CS_8259 = NOT(M/IO) AND NOT(A7) AND
;           A6 AND NOT(A5)
; CS_8254 = NOT(M/IO) AND NOT(A7) AND
;           NOT(A6) AND A5   — wait, that's 20h
;
; Correction using 74LS138:
;   I/O decoder: C=A7, B=A6, A=A5
;   Y1 (001) → 20-3Fh → CS_8259
;   Y2 (010) → 40-5Fh → CS_8254
;   Y3 (011) → 60-7Fh → CS_8251
;   Y4 (100) → 80-9Fh → CS_8255
;
; === Initialization Sequence ===
;
  ORG 0FFF0h             ; ROM reset vector
  JMP FAR PTR BOOT       ; first instruction
;
  ORG 0F800h             ; Boot code in ROM
BOOT:
  CLI                    ; disable interrupts
  MOV AX, 0
  MOV DS, AX
  MOV ES, AX
  MOV AX, 07FFh
  MOV SS, AX
  MOV SP, 0FFEh
;
  ; --- Init 8259 PIC ---
  MOV AL, 13h
  OUT 20h, AL            ; ICW1
  MOV AL, 08h
  OUT 21h, AL            ; ICW2
  MOV AL, 01h
  OUT 21h, AL            ; ICW4
  MOV AL, 0FEh
  OUT 21h, AL            ; unmask IR0 (timer)
;
  ; --- Init 8254 Timer (1ms tick) ---
  MOV AL, 36h
  OUT 43h, AL            ; C0, mode 3
  MOV AL, 0A9h
  OUT 40h, AL            ; LSB of 1193
  MOV AL, 04h
  OUT 40h, AL            ; MSB of 1193
;
  ; --- Init 8255 (PA=out, PB=in) ---
  MOV AL, 82h
  OUT 83h, AL
;
  ; --- Init 8251 (9600, 8N1, 16x) ---
  MOV AL, 40h
  OUT 61h, AL            ; reset
  MOV AL, 4Eh
  OUT 61h, AL            ; mode word
  MOV AL, 37h
  OUT 61h, AL            ; command word
;
  ; --- Set IVT for timer (INT 08h) ---
  MOV WORD PTR DS:[0020h], OFFSET TIMER_ISR
  MOV WORD PTR DS:[0022h], CS
;
  STI                    ; enable interrupts
  JMP MAIN               ; start application

Line-by-line walkthrough

  1. 1. Title comment for complete system design
  2. 2. Separator line
  3. 3. Blank line
  4. 4. Component list with specifications
  5. 5. CPU: 8086 running at 5 MHz
  6. 6. ROM: 32KB mapped at top of memory for boot code
  7. 7. RAM: 32KB mapped at bottom for data, stack, and IVT
  8. 8. Peripheral chips with their I/O port assignments
  9. 9. Blank line
  10. 10. Address decoding equations section
  11. 11. Blank line
  12. 12. CS_ROM: active when M/IO=1 and all upper address bits = 1 (F8000-FFFFF)
  13. 13. CS_RAM: active when M/IO=1 and upper bits = 0 (00000-07FFF)
  14. 14. Blank line
  15. 15. I/O decoder equations using 74LS138
  16. 16. Comment on correction: using organized decoder outputs
  17. 17. Y1 output selects 8259 PIC at ports 20-3Fh
  18. 18. Y2 selects 8254 timer at ports 40-5Fh
  19. 19. Y3 selects 8251 USART at ports 60-7Fh
  20. 20. Y4 selects 8255 PPI at ports 80-9Fh
  21. 21. Blank line
  22. 22. Initialization sequence begins
  23. 23. Blank line
  24. 24. ORG directive places reset vector jump at ROM address FFF0h
  25. 25. JMP instruction — first thing executed after power-on reset
  26. 26. Blank line
  27. 27. ORG places boot code at beginning of ROM space
  28. 28. BOOT label — system initialization starts here
  29. 29. CLI disables interrupts to prevent crashes during setup
  30. 30. Clear AX for segment register initialization
  31. 31. Set DS and ES to segment 0 (data at physical 00000h)
  32. 32. Set ES
  33. 33. Set stack segment to 07FFh
  34. 34. Point stack to near top of RAM
  35. 35. Set SP — stack is ready
  36. 36. Blank line
  37. 37. Initialize 8259 PIC — sets interrupt handling policies
  38. 38. ICW1: edge triggered, single PIC, ICW4 needed
  39. 39. ICW2: IR0 maps to INT 08h vector
  40. 40. ICW4: 8086 mode for correct vector generation
  41. 41. OCW1: unmask only IR0 (timer) — all others masked
  42. 42. Blank line
  43. 43. Initialize 8254 timer for 1ms periodic interrupt
  44. 44. Control word: Counter 0, Mode 3 square wave, LSB+MSB
  45. 45. Load count value 1193 (for ~1 KHz with 1.193 MHz clock)
  46. 46. LSB of count
  47. 47. MSB of count — timer starts generating interrupts
  48. 48. Blank line
  49. 49. Initialize 8255: Port A output, Port B input
  50. 50. Control word 82h configures ports
  51. 51. Blank line
  52. 52. Initialize 8251 USART for 9600 baud 8N1
  53. 53. Reset command clears any previous state
  54. 54. Mode word: 8 data bits, no parity, 1 stop, 16x clock
  55. 55. Command word: enable transmitter, receiver, DTR, RTS
  56. 56. Blank line
  57. 57. Set up IVT entry for timer interrupt (INT 08h)
  58. 58. Store ISR offset at IVT address 0020h (vector 08h * 4)
  59. 59. Store ISR segment at IVT address 0022h
  60. 60. Blank line
  61. 61. STI enables interrupts — system is fully initialized
  62. 62. Jump to main application loop

Spot the bug

; System init — set up stack and start
;
  MOV AX, 0800h
  MOV SS, AX
  MOV SP, 0000h         ; stack pointer
;
  ; Init 8259
  MOV AL, 13h
  OUT 20h, AL           ; ICW1
  MOV AL, 20h
  OUT 21h, AL           ; ICW2: base vector 20h
  MOV AL, 01h
  OUT 21h, AL           ; ICW4
;
  STI
  JMP MAIN
Need a hint?
When SP=0000h and you PUSH, what happens? Also check if interrupts are enabled before the IVT is set up.
Show answer
Two bugs: (1) SP=0000h means the first PUSH will decrement SP to FFFEh and write to SS:FFFEh = 08000h+FFFEh = 17FFEh, which may be outside RAM (RAM ends at 07FFFh). SP should start at the TOP of the stack area, like FFFFh or FFFEh. (2) STI enables interrupts, but no ISR addresses have been written to the IVT. When the timer fires INT 20h, the CPU will jump to whatever garbage address is at IVT[20h], causing a crash. Fix: set up IVT entries BEFORE STI.

Explain like I'm 5

Imagine building a toy robot. The 8086 CPU is the robot's brain. RAM is a whiteboard where it writes notes. ROM is an instruction booklet it can never change. The 8255 is the robot's hands (touching buttons and moving things). The 8254 is a clock on the wall (tells the robot when to do things). The 8259 is a doorbell system (tells the brain when something needs attention). The address decoder is the name tag on each part so the brain knows which part it is talking to. You build the robot by connecting all these parts to the same set of wires (the bus) and giving each part its own address!

Fun fact

The original IBM PC motherboard had exactly this kind of design — an 8088 CPU connected to ROM, RAM, an 8255 (keyboard interface), an 8253 (timer), an 8259 (interrupts), and an 8250 (serial port, similar to 8251). The entire design team was just 12 engineers, and they built the whole system in about one year. Their design decisions — like the memory map, IRQ assignments, and I/O port addresses — became industry standards that persisted for over 30 years.

Hands-on challenge

Design a complete 8086-based temperature monitoring system. Requirements: (1) Read temperature sensor via 8255 Port B, (2) Display temperature on 4-digit multiplexed 7-segment via 8255 Port A, (3) Sound an alarm via 8254 speaker when temperature exceeds a threshold, (4) Log data via 8251 serial port at 9600 baud, (5) Use 8259 for timer-driven display refresh. Provide: complete memory map, I/O map, address decoding equations, initialization code, and main loop pseudocode.

More resources

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