• Название:

    Barry B. Brey. The Intel Microprocessors Eighth Edition 2008

  • Размер: 10.42 Мб
  • Формат: PDF
  • или
  • Название: The Intel Microprocessors 8086/8088, 80186/80188, 80286, 80386, 80486, Pentium, Pentium Pro Processor, Pentium II, Pentium III, Pentium 4, and Core2 with 64-bit Extensions: Architecture, Programming, and Interfacing
  • Автор: Barry B. Brey

THE INTEL MICROPROCESSORS
8086/8088, 80186/80188, 80286, 80386,
80486, Pentium, Pentium Pro Processor,
Pentium II, Pentium III, Pentium 4, and Core2
with 64-Bit Extensions
Architecture, Programming, and Interfacing

Eighth Edition

BARRY B. BREY

Upper Saddle River, New Jersey
Columbus, Ohio

Library of Congress Cataloging in Publication Data
Brey, Barry B.
The Intel microprocessors 8086/8088, 80186/80188, 80286, 80386, 80486, Pentium, Pentium
Pro processor, Pentium II, Pentium III, Pentium 4, and Core2 with 64-bit extensions:
architecture, programming, and interfacing / Barry B. Brey—8th ed.
p. cm.
Includes index.
ISBN 0-13-502645-8
1. Intel 80xxx series microprocessors. 2. Pentium (Microprocessor) 3. Computer interfaces.
I. Title.
QA76.8.I292B75 2009
004.165—dc22
2008009338

Editor in Chief: Vernon Anthony
Acquisitions Editor: Wyatt Morris
Editorial Assistant: Christopher Reed
Production Coordination: GGS Book Services
Project Manager: Jessica Sykes
Operations Specialist: Laura Weaver
Design Coordinator: Mike Fruhbeis
Cover Designer: Ilze Lemesis
Cover image: iStockphoto
Director of Marketing: David Gesell
Marketing Manager: Jimmy Stephens
Marketing Assistant: Les Roberts

This book was set in Times by GGS Book Services. It was printed and bound by Hamilton
Printing. The cover was printed by Phoenix Color Corp.

Copyright © 2009, 2006, 2003, 2000, 1997, 1994, 1991, 1987 by Pearson Education,
Inc., Upper Saddle River, New Jersey 07458. Pearson Prentice Hall. All rights reserved.
Printed in the United States of America. This publication is protected by Copyright and permission should be obtained from the publisher prior to any prohibited reproduction, storage
in a retrieval system, or transmission in any form or by any means, electronic, mechanical,
photocopying, recording, or likewise. For information regarding permission(s), write to:
Rights and Permissions Department.

Pearson Prentice Hall™ is a trademark of Pearson Education, Inc.
Pearson® is a registered trademark of Pearson plc
Prentice Hall® is a registered trademark of Pearson Education, Inc.

Pearson Education Ltd., London
Pearson Education Singapore Pte. Ltd.
Pearson Education Canada, Inc.
Pearson Education—Japan

Pearson Education Australia Pty. Limited
Pearson Education North Asia Ltd., Hong Kong
Pearson Educación de Mexico, S.A. de C.V.
Pearson Education Malaysia Pte. Ltd.

10 9 8 7 6 5 4 3 2 1
ISBN–13: 978–0–13–502645–8
ISBN–10:
0–13–502645–8

This text is dedicated to my progenies, Brenda (the programmer) and Gary (the
veterinarian technician), and to my constant four-legged companions: Romy,
Sassy, Sir Elton, Eye Envy, and Baby Hooter.

iii

This page intentionally left blank

PREFACE

This practical reference text is written for students who require a thorough knowledge of programming and interfacing of the Intel family of microprocessors. Today, anyone functioning or
striving to function in a field of study that uses computers must understand assembly language
programming, a version of C language, and interfacing. Intel microprocessors have gained wide,
and at times exclusive, application in many areas of electronics, communications, and control
systems, particularly in desktop computer systems. A major addition to this eighth edition
explains how to interface C/C++ using Visual C++ Express, which is a free download from
Microsoft, with assembly language for both the older DOS and the Windows environments.
Many applications include Visual C++ as a basis for learning assembly language using the inline
assembler. Updated sections that detail new events in the fields of microprocessors and microprocessor interfacing have been added.

ORGANIZATION AND COVERAGE
To cultivate a comprehensive approach to learning, each chapter begins with a set of objectives
that briefly define its content. Chapters contain many programming applications and examples
that illustrate the main topics. Each chapter ends with a numerical summary, which doubles as a
study guide, and reviews the information just presented. Questions and problems are provided
for reinforcement and practice, including research paper suggestions.
This text contains many example programs using the Microsoft Macro Assembler program
and the inline assembler in the Visual C++ environment, which provide a learning opportunity to
program the Intel family of microprocessors. Operation of the programming environment
includes the linker, library, macros, DOS function, BIOS functions, and Visual C/C++ program
development. The inline assembler (C/C++) is illustrated for both the 16- and 32-bit programming environments of various versions of Visual C++. The text is written to use Visual C++
Express 2005 or 2008 as a development environment, but any version of Visual Studio can also
be used with almost no change.
This text also provides a thorough description of family members, memory systems, and
various I/O systems that include disk memory, ADC and DAC, 16550 UART, PIAs, timers, keyboard/display controllers, arithmetic coprocessors, and video display systems. Also discussed are

v

vi

PREFACE

the personal computer system buses (AGP, ISA, PCI, PCI Express, USB, serial ports, and parallel
port). Through these systems, a practical approach to microprocessor interfacing can be learned.

APPROACH
Because the Intel family of microprocessors is quite diverse, this text initially concentrates on
real mode programming, which is compatible with all versions of the Intel family of microprocessors. Instructions for each family member, which include the 80386, 80486, Pentium,
Pentium Pro, Pentium II, Pentium III, and Pentium 4 processors, are compared and contrasted
with those for the 8086/8088 microprocessors. This entire series of microprocessors is very similar, which allows more advanced versions and their instructions to be learned with the basic
8086/8088. Please note that the 8086/8088 are still used in embedded systems along with their
updated counterparts, the 80186/80188 and 80386EX embedded microprocessor.
This text also explains the programming and operation of the numeric coprocessor, MMX
extension, and the SIMD extension, which function in a system to provide access to floatingpoint calculations that are important in control systems, video graphics, and computer-aided
design (CAD) applications. The numeric coprocessor allows a program to access complex
arithmetic operations that are otherwise difficult to achieve with normal microprocessor programming. The MMX and SIMD instructions allow both integer and floating-point data to be
manipulated in parallel at very high speed.
This text also describes the pin-outs and function of the 8086–80486 and all versions of the
Pentium microprocessor. First, interfacing is explained using the 8086/8088 with some of the
more common peripheral components. After explaining the basics, a more advanced emphasis is
placed on the 80186/80188, 80386, 80486, and Pentium through Pentium 4 microprocessors.
Coverage of the 80286, because of its similarity to the 8086 and 80386, is minimized so the
80386, 80486, and Pentium versions can be covered in complete detail.
Through this approach, the operation of the microprocessor and programming with the
advanced family members, along with interfacing all family members, provides a working and
practical background of the Intel family of microprocessors. Upon completing a course using
this text, you will be able to:
1. Develop software to control an application interface microprocessor. Generally, the software
developed will also function on all versions of the microprocessor. This software also
includes DOS-based and Windows-based applications. The main emphasis is on developing
inline assembly and C++ mixed language programs in the Windows environment.
2. Program using MFC controls, handlers, and functions to use the keyboard, video display
system, and disk memory in assembly language and C++.
3. Develop software that uses macro sequences, procedures, conditional assembly, and flow
control assembler directives that are linked to a Visual C++ program.
4. Develop software for code conversions using lookup tables and algorithms.
5. Program the numeric coprocessor to solve complex equations.
6. Develop software for the MMX and SIMD extensions.
7. Explain the differences between the family members and highlight the features of each member.
8. Describe and use real and protected mode operation of the microprocessor.
9. Interface memory and I/O systems to the microprocessor.
10. Provide a detailed and comprehensive comparison of all family members and their software
and hardware interfaces.
11. Explain the function of the real-time operating system in an embedded application.
12. Explain the operation of disk and video systems.
13. Interface small systems to the ISA, PCI, serial ports, parallel port, and USB bus in a personal
computer system.

PREFACE

vii

CONTENT OVERVIEW
Chapter 1 introduces the Intel family of microprocessors with an emphasis on the microprocessorbased computer system: its history, operation, and the methods used to store data in a
microprocessor-based system. Number systems and conversions are also included. Chapter 2
explores the programming model of the microprocessor and system architecture. Both real and
protected mode operations are explained.
Once an understanding of the basic machine is grasped, Chapters 3 through 6 explain how
each instruction functions with the Intel family of microprocessors. As instructions are
explained, simple applications are presented to illustrate the operation of the instructions and
develop basic programming concepts.
Chapter 7 introduces the use of Visual C/C++ Express with the inline assembler and separate assembly language programming modules. It also explains how to configure Visual C++
Express for use with assembly language applications.
After the basis for programming is developed, Chapter 8 provides applications using the
Visual C++ Express with the inline assembler program. These applications include programming
using the keyboard and mouse through message handlers in the Windows environment. Disk
files are explained using the File class, as well as keyboard and video operations on a personal
computer system through Windows. This chapter provides the tools required to develop virtually
any program on a personal computer system through the Windows environment.
Chapter 9 introduces the 8086/8088 family as a basis for learning basic memory and I/O
interfacing, which follow in later chapters. This chapter shows the buffered system as well as the
system timing.
Chapter 10 explains memory interface using both integrated decoders and programmable
logic devices using VHDL. The 8-, 16-, 32-, and 64-bit memory systems are provided so the
8086–80486 and the Pentium through Pentium 4 microprocessors can be interfaced to memory.
Chapter 11 provides a detailed look at basic I/O interfacing, including PIAs, timers, the
16550 UART, and ADC/DAC. It also describes the interface of both DC and stepper motors.
Once these basic I/O components and their interface to the microprocessor are understood,
Chapters 12 and 13 provide detail on advanced I/O techniques that include interrupts and direct
memory access (DMA). Applications include a printer interface, real-time clock, disk memory,
and video systems.
Chapter 14 details the operation and programming for the 8087–Pentium 4 family of arithmetic coprocessors, as well as MMX and SIMD instructions. Today few applications function
efficiently without the power of the arithmetic coprocessor. Remember that all Intel microprocessors since the 80486 contain a coprocessor; since the Pentium, an MMX unit; and since
the Pentium II, an SIMD unit.
Chapter 15 shows how to interface small systems to the personal computer through the use
of the parallel port, serial ports, and the ISA, and PCI bus interfaces.
Chapters 16 and 17 cover the advanced 80186/80188–80486 microprocessors and explore
their differences with the 8086/8088, as well as their enhancements and features. Cache memory,
interleaved memory, and burst memory are described with the 80386 and 80486 microprocessors. Chapter 16 also covers real-time operating systems (RTOS), and Chapter 17 also describes
memory management and memory paging.
Chapter 18 details the Pentium and Pentium Pro microprocessors. These microprocessors
are based upon the original 8086/8088.
Chapter 19 introduces the Pentium II, Pentium III, Pentium 4, and Core2 microprocessors.
It covers some of the new features, package styles, and the instructions that are added to the original instruction set.
Appendices are included to enhance the text. Appendix A provides an abbreviated listing
of the DOS INT 21H function calls because the use of DOS has waned. It also details the use of

viii

PREFACE

the assembler program and the Windows Visual C++ interface. A complete listing of all
8086–Pentium 4 and Core2 instructions, including many example instructions and machine coding in hexadecimal as well as clock timing information, is found in Appendix B. Appendix C
provides a compact list of all the instructions that change the flag bits. Answers for the evennumbered questions and problems are provided in Appendix D.
To access supplementary materials online, instructors need to request an instructor access
code. Go to www.pearsonhighered.com/irc, where you can register for an instructor access
code. Within 48 hours after registering, you will receive a confirming e-mail, including an
instructor access code. Once you have received your code, go to the site and log on for full
instructions on downloading the materials you wish to use.

Acknowledgments
I greatly appreciate the feedback from the following reviewers:
James K. Archibald, Brigham Young University
William H. Murray III, Broome Community College.

STAY IN TOUCH
We can stay in touch through the Internet. My Internet site contains information about all of my
textbooks and many important links that are specific to the personal computer, microprocessors,
hardware, and software. Also available is a weekly lesson that details many of the aspects of the
personal computer. Of particular interest is the “Technical Section,” which presents many notes
on topics that are not covered in this text. Please feel free to contact me at bbrey@ee.net if you
need any type of assistance. I usually answer all of my e-mail within 24 hours.
My website is http://members.ee.net/brey

BRIEF CONTENTS

CHAPTER 1

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

CHAPTER 2

THE MICROPROCESSOR AND ITS ARCHITECTURE

51

CHAPTER 3

ADDRESSING MODES

77

CHAPTER 4

DATA MOVEMENT INSTRUCTIONS

111

CHAPTER 5

ARITHMETIC AND LOGIC INSTRUCTIONS

156

CHAPTER 6

PROGRAM CONTROL INSTRUCTIONS

192

CHAPTER 7

USING ASSEMBLY LANGUAGE WITH C/C++

223

CHAPTER 8

PROGRAMMING THE MICROPROCESSOR

250

CHAPTER 9

8086/8088 HARDWARE SPECIFICATIONS

302

CHAPTER 10

MEMORY INTERFACE

328

CHAPTER 11

BASIC I/O INTERFACE

377

CHAPTER 12

INTERRUPTS

451

CHAPTER 13

DIRECT MEMORY ACCESS AND DMA-CONTROLLED I/O

490

CHAPTER 14

THE ARITHMETIC COPROCESSOR, MMX, AND SIMD TECHNOLOGIES

531

CHAPTER 15

BUS INTERFACE

592

CHAPTER 16

THE 80185, 80188, AND 80286 MICROPROCESSORS

627

1

ix

x

BRIEF CONTENTS

CHAPTER 17

THE 80386 AND 80486 MICROPROCESSORS

677

CHAPTER 18

THE PENTIUM AND PENTIUM PRO MICROPROCESSORS

729

CHAPTER 19

THE PENTIUM II, PENTIUM III, PENTIUM 4, AND CORE2 MICROPROCESSORS

759

CONTENTS

CHAPTER 1

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

1

Introduction/Chapter Objectives 1
1–1 A Historical Background 2
The Mechanical Age 2; The Electrical Age 2; Programming Advancements 4;
The Microprocessor Age 5; The Modern Microprocessor 7
1–2 The Microprocessor-Based Personal Computer System 17
The Memory and I/O System 17; The Microprocessor 25
1–3 Number Systems 29
Digits 29; Positional Notation 30; Conversion to Decimal 31; Conversion from Decimal 32;
Binary-Coded Hexadecimal 33
1–4 Computer Data Formats 35
ASCII and Unicode Data 35; BCD (Binary-Coded Decimal) Data 37; Byte-Sized Data 38;
Word-Sized Data 40; Doubleword-Sized Data 41; Real Numbers 43
1–5 Summary 45
1–6 Questions and Problems 46

CHAPTER 2

THE MICROPROCESSOR AND ITS ARCHITECTURE

51

Introduction/Chapter Objectives 51
2–1 Internal Microprocessor Architecture 51
The Programming Model 52; Multipurpose Registers 54
2–2 Real Mode Memory Addressing 58
Segments and Offsets 58; Default Segment and Offset Registers 60;
Segment and Offset Addressing Scheme Allows Relocation 60
2–3 Introduction to Protected Mode Memory Addressing 63
Selectors and Descriptors 63; Program-Invisible Registers 67
2–4 Memory Paging 68
Paging Registers 69; The Page Directory and Page Table 70
2–5 Flat Mode Memory 72
2–6 Summary 73
2–7 Questions and Problems 74

CHAPTER 3

ADDRESSING MODES

77

Introduction/Chapter Objectives 77
3–1 Data-Addressing Modes 78
Register Addressing 81; Immediate Addressing 83; Direct Data Addressing 86;
Register Indirect Addressing 88; Base-Plus-Index Addressing 91;

xi

xii

CONTENTS

3–2

3–3
3–4
3–5

CHAPTER 4

Register Relative Addressing 93; Base Relative-Plus-Index Addressing 96;
Scaled-Index Addressing 98; RIP Relative Addressing 99; Data Structures 99
Program Memory-Addressing Modes 100
Direct Program Memory Addressing 100; Relative Program Memory Addressing 101;
Indirect Program Memory Addressing 101
Stack Memory-Addressing Modes 102
Summary 105
Questions and Problems 107

DATA MOVEMENT INSTRUCTIONS

111

Introduction/Chapter Objectives 111
4–1 MOV Revisited 112
Machine Language 112; The 64-Bit Mode for the Pentium 4 and Core2 120
4–2 PUSH/POP 122
PUSH 122; POP 124; Initializing the Stack 124
4–3 Load-Effective Address 127
LEA 127; LDS, LES, LFS, LGS, and LSS 128
4–4 String Data Transfers 130
The Direction Flag 130; DI and SI 130; LODS 130; STOS 131; MOVS 133;
INS 135; OUTS 136
4–5 Miscellaneous Data Transfer Instructions 137
XCHG 137; LANF and SAHF 137; XLAT 138; IN and OUT 138;
MOVSX and MOVZX 140; BSWAP 140; CMOV 141
4–6 Segment Override Prefix 142
4–7 Assembler Detail 142
Directives 143; Memory Organization 147; A Sample Program 150
4–8 Summary 151
4–9 Questions and Problems 154

CHAPTER 5

ARITHMETIC AND LOGIC INSTRUCTIONS

156

Introduction/Chapter Objectives 156
5–1 Addition, Subtraction, and Comparison 156
Addition 157; Subtraction 162; Comparison 165
5–2 Multiplication and Division 166
Multiplication 166; Division 169
5–3 BCD and ASCII Arithmetic 172
BCD Arithmetic 172; ASCII Arithmetic 173
5–4 Basic Logic Instructions 175
AND 175; OR 176; Test and Bit Test Instructions 180; NOT and NEG 181
5–5 Shift and Rotate 182
Shift 182; Rotate 184; Bit Scan Instructions 185
5–6 String Comparisons 186
SCAS 186; CMPS 187
5–7 Summary 187
5–8 Questions and Problems 189

CHAPTER 6

PROGRAM CONTROL INSTRUCTIONS
Introduction/Chapter Objectives 192
6–1 The Jump Group 192
Unconditional Jump (JMP) 193; Conditional Jumps and Conditional Sets 198; LOOP 201
6–2 Controlling the Flow of the Program 202
WHILE Loops 205; REPEAT-UNTIL Loops 206
6–3 Procedures 208
CALL 209; RET 211

192

CONTENTS

xiii

6–4 Introduction to Interrupts 213
Interrupt Vectors 213; Interrupt Instructions 214; Interrupt Control 215;
Interrupts in the Personal Computer 216; 64-Bit Mode Interrupts 216
6–5 Machine Control and Miscellaneous Instructions 217
Controlling the Carry Flag Bit 217; WAIT 217; HLT 217; NOP 217;
LOCK Prefix 218; ESC 218; BOUND 218; ENTER and LEAVE 218
6–6 Summary 219
6–7 Questions and Problems 221

CHAPTER 7

USING ASSEMBLY LANGUAGE WITH C/C++

223

Introduction/Chapter Objectives 223
7–1 Using Assembly Language with C++ for 16-Bit DOS Applications 224
Basic Rules and Simple Programs 224; What Cannot Be Used from MASM Inside
an _asm Block 226; Using Character Strings 226; Using Data Structures 227;
An Example of a Mixed-Language Program 229
7–2 Using Assembly Language with Visual C/C++ for 32-Bit Applications 231
An Example that Uses Console I/O to Access the Keyboard and Display 231;
Directly Addressing I/O Ports 233; Developing a Visual C++ Application for Windows 234
7–3 Mixed Assembly and C++ Objects 242
Linking Assembly Language with Visual C++ 242; Adding New Assembly Language
Instructions to C/C++ Programs 247
7–4 Summary 247
7–5 Questions and Problems 248

CHAPTER 8

PROGRAMMING THE MICROPROCESSOR

250

Introduction/Chapter Objectives 250
8–1 Modular Programming 251
The Assembler and Linker 251; PUBLIC and EXTRN 253; Libraries 254; Macros 257
8–2 Using the Keyboard and Video Display 259
Reading the Keyboard 259; Using the Video Display 265; Using a Timer in a Program 267;
The Mouse 269
8–3 Data Conversions 271
Converting from Binary to ASCII 272; Converting from ASCII to Binary 274;
Displaying and Reading Hexadecimal Data 274; Using Lookup Tables for Data
Conversions 276; An Example Program Using a Lookup Table 278
8–4 Disk Files 280
Disk Organization 280; File Names 281; Sequential Access Files 282;
Random Access Files 291
8–5 Example Programs 294
Time/Date Display Program 294; Numeric Sort Program 295; Data Encryption 297
8–6 Summary 299
8–7 Questions and Problems 300

CHAPTER 9

8086/8088 HARDWARE SPECIFICATIONS
Introduction/Chapter Objectives 302
9–1 Pin-Outs and the Pin Functions 302
The Pin-Out 303; Power Supply Requirements 303; DC Characteristics 303;
Pin Connections 304
9–2 Clock Generator (8284A) 307
The 8284A Clock Generator 307; Operation of the 8284A 309
9–3 Bus Buffering and Latching 310
Demultiplexing the Buses 310; The Buffered System 312
9–4 Bus Timing 315
Basic Bus Operation 315; Timing in General 315; Read Timing 316; Write Timing 319

302

xiv

CONTENTS

9–5 Ready and the Wait State 320
The READY Input 320; RDY and the 8284A 320
9–6 Minimum Mode versus Maximum Mode 323
Minimum Mode Operation 323; Maximum Mode Operation 323;
The 8288 Bus Controller 324; Pin Functions 325
9–7 Summary 325
9–8 Questions and Problems 326

CHAPTER 10

MEMORY INTERFACE

328

Introduction/Chapter Objectives 328
10–1 Memory Devices 328
Memory Pin Connections 329; ROM Memory 330; Static RAM (SRAM) Devices 332;
Dynamic RAM (DRAM) Memory 333
10–2 Address Decoding 340
Why Decode Memory? 340; Simple NAND Gate Decoder 341; The 3-to-8 Line Decoder
(74LS138) 342; The Dual 2-to-4 Line Decoder (74LS139) 344; PLD Programmable
Decoders 344
10–3 8088 and 80188 (8-Bit) Memory Interface 349
Basic 8088/80188 Memory Interface 349; Interfacing Flash Memory 351;
Error Correction 353
10–4 8086, 80186, 80286, and 80386SX (16-Bit) Memory Interface 356
16-Bit Bus Control 356
10–5 80386DX and 80486 (32-Bit) Memory Interface 363
Memory Banks 363; 32-Bit Memory Interface 364
10–6 Pentium through Core2 (64-Bit) Memory Interface 366
64-Bit Memory Interface 366
10–7 Dynamic RAM 370
DRAM Revisited 370; EDO Memory 371; SDRAM 371; DDR 373; DRAM Controllers 373
10–8 Summary 373
10–9 Questions and Problems 375

CHAPTER 11

BASIC I/O INTERFACE
Introduction/Chapter Objectives 377
11–1 Introduction to I/O Interface 377
The I/O Instructions 378; Isolated and Memory-Mapped I/O 379; Personal Computer I/O
Map 380; Basic Input and Output Interfaces 380; Handshaking 382; Notes about
Interfacing Circuitry 383
11–2 I/O Port Address Decoding 387
Decoding 8-Bit I/O Port Addresses 387; Decoding 16-Bit I/O Port Addresses 388;
8- and 16-Bit-Wide I/O Ports 389; 32-Bit-Wide I/O Ports 392
11–3 The Programmable Peripheral Interface 395
Basic Description of the 82C55 395; Programming the 82C55 397; Mode 0 Operation 398;
An LCD Display, Interfaced to the 82C55 403; Mode 1 Strobed Input 414; Signal
Definitions for Mode 1 Strobed Input 414; Mode 1 Strobed Output 416; Signal Definitions
for Mode 1 Strobed Output 416; Mode 2 Bidirectional Operation 418; Signal Definitions for
Bidirectional Mode 2 418; 82C55 Mode Summary 420; The Serial EEPROM Interface 421
11–4 8254 Programmable Interval Timer 423
8254 Functional Description 423; Pin Definitions 424; Programming the 8254 424;
DC Motor Speed and Direction Control 429
11–5 16550 Programmable Communications Interface 433
Asynchronous Serial Data 433; 16550 Functional Description 433; 16550 Pin Functions 434;
Programming the 16550 435
11–6 Analog-to-Digital (ADC) and Digital-to-Analog (DAC) Converters 440
The DAC0830 Digital-to-Analog Converter 440; The ADC080X Analog-to-Digital
Converter 442; Using the ADC0804 and the DAC0830 445

377

CONTENTS

xv

11–7 Summary 446
11–8 Questions and Problems 448

CHAPTER 12

INTERRUPTS

451

Introduction/Chapter Objectives 451
12–1 Basic Interrupt Processing 451
The Purpose of Interrupts 451; Interrupts 452; Interrupt Instructions: BOUND, INTO,
INT, INT 3, and IRET 455; The Operation of a Real Mode Interrupt 455; Operation of a
Protected Mode Interrupt 456; Interrupt Flag Bits 457; Storing an Interrupt Vector in the
Vector Table 458
12–2 Hardware Interrupts 459
INTR and INTA 461; The 82C55 Keyboard Interrupt 462
12–3 Expanding the Interrupt Structure 465
Using the 74ALS244 to Expand Interrupts 465; Daisy-Chained Interrupt 466
12–4 8259A Programmable Interrupt Controller 468
General Description of the 8259A 468; Connecting a Single 8259A 469; Cascading
Multiple 8259As 469; Programming the 8259A 469; 8259A Programming Example 475
12–5 Interrupt Examples 481
Real-Time Clock 482; Interrupt-Processed Keyboard 484
12–6 Summary 487
12–7 Questions and Problems 488

CHAPTER 13

DIRECT MEMORY ACCESS AND DMA-CONTROLLED I/O

490

Introduction/Chapter Objectives 490
13–1 Basic DMA Operation 490
Basic DMA Definitions 491
13–2 The 8237 DMA Controller 492
Pin Definitions 492; Internal Registers 494; Software Commands 497;
Programming the Address and Count Registers 498; The 8237 Connected
to the 80X86 Microprocessor 498; Memory-to-Memory Transfer with
the 8237 499; DMA-Processed Printer Interface 504
13–3 Shared-Bus Operation 506
Types of Buses Defined 507; The Bus Arbiter 509; Pin Definitions 509
13–4 Disk Memory Systems 513
Floppy Disk Memory 513; Pen Drives 517; Hard Disk Memory 518;
Optical Disk Memory 521
13–5 Video Displays 522
Video Signals 522; The TTL RGB Monitor 523; The Analog RGB Monitor 524
13–6 Summary 529
13–7 Questions and Problems 529

CHAPTER 14

THE ARITHMETIC COPROCESSOR, MMX, AND SIMD TECHNOLOGIES
Introduction/Chapter Objectives 531
14–1 Data Formats for the Arithmetic Coprocessor 532
Signed Integers 532; Binary-Coded Decimal (BCD) 533; Floating-Point 533
14–2 The 80X87 Architecture 536
Internal Structure of the 80X87 536
14–3 Instruction Set 541
Data Transfer Instructions 541; Arithmetic Instructions 543; Comparison Instructions 544;
Transcendental Operations 545; Constant Operations 546; Coprocessor Control
Instructions 546; Coprocessor Instructions 548
14–4 Programming with the Arithmetic Coprocessor 565
Calculating the Area of a Circle 565; Finding the Resonant Frequency 566; Finding the
Roots Using the Quadratic Equation 566; Using a Memory Array to Store Results 567;
Converting a Single-Precision Floating-Point Number to a String 568

531

xvi

CONTENTS

14–5 Introduction to MMX Technology 570
Data Types 570; Instruction Set 571
14–6 Introduction to SSE Technology 581
Floating-Point Data 582; The Instruction Set 583; The Control/Status Register 584;
Programming Examples 584; Optimization 587
14–7 Summary 587
14–8 Questions and Problems 589

CHAPTER 15

BUS INTERFACE

592

Introduction/Chapter Objectives 592
15–1 The ISA Bus 592
Evolution of the ISA Bus 593; The 8-Bit ISA Bus Output Interface 593; The 8-Bit ISA
Bus Input Interface 598; The 16-Bit ISA Bus 601
15–2 The Peripheral Component Interconnect (PCI) Bus 602
The PCI Bus Pin-Out 603; The PCI Address/Data Connections 603;
Configuration Space 605; BIOS for PCI 607; PCI Interface 610; PCI Express Bus 610
15–3 The Parallel Printer Interface (LPT) 612
Port Details 612; Using the Parallel Port Without ECP Support 614
15–4 The Serial COM Ports 614
Communication Control 615
15–5 The Universal Serial Bus (USB) 617
The Connector 617; USB Data 617; USB Commands 618; The USB Bus Node 620;
Software for the USBN9604/3 621
15–6 Accelerated Graphics Port (AGP) 623
15–7 Summary 624
15–8 Questions and Problems 625

CHAPTER 16

THE 80186, 80188, AND 80286 MICROPROCESSORS

627

Introduction/Chapter Objectives 627
16–1 80186/80188 Architecture 627
Versions of the 80186/80188 628; 80186 Basic Block Diagram 628; 80186/80188 Basic
Features 629; Pin-Out 631; DC Operating Characteristics 634; 80186/80188 Timing 634
16–2 Programming the 80186/80188 Enhancements 637
Peripheral Control Block 637; Interrupts in the 80186/80188 638; Interrupt Controller 638;
Timers 643; DMA Controller 649; Chip Selection Unit 651
16–3 80C188EB Example Interface 655
16–4 Real-Time Operating Systems (RTOS) 662
What Is a Real-Time Operating System (RTOS)? 662; An Example System 663;
A Threaded System 666
16–5 Introduction to the 80286 670
Hardware Features 670; Additional Instructions 672; The Virtual Memory Machine 674
16–6 Summary 674
16–7 Questions and Problems 675

CHAPTER 17

THE 80386 AND 80486 MICROPROCESSORS
Introduction/Chapter Objectives 677
17–1 Introduction to the 80386 Microprocessor 678
The Memory System 681; The Input/Output System 687; Memory and I/O Control
Signals 688; Timing 689; Wait States 691
17–2 Special 80386 Registers 692
Control Registers 692; Debug and Test Registers 693
17–3 80386 Memory Management 695
Descriptors and Selectors 695; Descriptor Tables 698; The Task State Segment (TSS) 700
17–4 Moving to Protected Mode 702

677

CONTENTS

xvii

17–5 Virtual 8086 Mode 712
17–6 The Memory Paging Mechanism 713
The Page Directory 714 The Page Table 715
17–7 Introduction to the 80486 Microprocessor 718
Pin-Out of the 80486DX and 80486SX Microprocessors 718; Pin Definitions 718;
Basic 80486 Architecture 722; 80486 Memory System 723
17–8 Summary 726
17–9 Questions and Problems 727

CHAPTER 18

THE PENTIUM AND PENTIUM PRO MICROPROCESSORS

729

Introduction/Chapter Objectives 729
18–1 Introduction to the Pentium Microprocessor 730
The Memory System 734; Input/Output System 735; System Timing 735;
Branch Prediction Logic 738; Cache Structure 738; Superscalar Architecture 738
18–2 Special Pentium Registers 738
Control Registers 738; EFLAG Register 739; Built-In Self-Test (BIST) 740
18–3 Pentium Memory Management 740
Paging Unit 740; Memory-Management Mode 740
18–4 New Pentium Instructions 742
18–5 Introduction to the Pentium Pro Microprocessor 747
Internal Structure of the Pentium Pro 748; Pin Connections 750; The Memory System 754;
Input/Output System 755; System Timing 755
18–6 Special Pentium Pro Features 756
Control Register 4 756
18–7 Summary 757
18–8 Questions and Problems 758

CHAPTER 19

THE PENTIUM II, PENTIUM III, PENTIUM 4, AND CORE2 MICROPROCESSORS

759

Introduction/Chapter Objectives 759
19–1 Introduction to the Pentium II Microprocessor 760
The Memory System 765; Input/Output System 767; System Timing 768
19–2 Pentium II Software Changes 768
CPUID Instruction 768; SYSENTER and SYSEXIT Instructions 769;
FXSAVE and FXRSTOR Instructions 770
19–3 The Pentium III 770
Chip Sets 770; Bus 771; Pin-Out 771
19–4 The Pentium 4 and Core2 771
Memory Interface 772; Register Set 773; Hyper-Threading Technology 775;
Multiple Core Technology 776; CPUID 776; Model-Specific Registers 779;
Performance-Monitoring Registers 780; 64-Bit Extension Technology 780
19–5 Summary 782
19–6 Questions and Problems 783

APPENDIX A: THE ASSEMBLER, VISUAL C++, AND DOS

785

The Assembler 785
Assembler Memory Models 786
Selected DOS Function Calls 787
Using Visual C++ 790
Create a Dialog Application 791

APPENDIX B: INSTRUCTION SET SUMMARY
Instruction Set Summary 798
SIMD Instruction Set Summary 881

794

xviii

CONTENTS

Data Movement Instructions 883
Arithmetic Instructions 885
Logic Instructions 891
Comparison Instructions 892
Data Conversion Instructions 894

APPENDIX C: FLAG-BIT CHANGES

895

APPENDIX D: ANSWERS TO SELECTED EVEN-NUMBERED QUESTIONS
AND PROBLEMS

897

INDEX

915

CHAPTER 1
Introduction to the Microprocessor
and Computer

INTRODUCTION
This chapter provides an overview of the Intel family of microprocessors. Included is a discussion of the history of computers and the function of the microprocessor in the microprocessorbased computer system. Also introduced are terms and jargon used in the computer field, so
that computerese is understood and applied when discussing microprocessors and computers.
The block diagram and a description of the function of each block detail the operation of
a computer system. Blocks, in the block diagram, show how the memory and input/output (I/O)
system of the personal computer interconnect. Detailed is the way data are stored in the memory so each data type can be used as software is developed. Numeric data are stored as integers,
floating-point, and binary-coded decimal (BCD); alphanumeric data are stored by using the
ASCII (American Standard Code for Information Interchange) code and the Unicode.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Converse by using appropriate computer terminology such as bit, byte, data, real memory
system, protected mode memory system, Windows, DOS, I/O, and so forth.
2. Briefly detail the history of the computer and list applications performed by computer
systems.
3. Provide an overview of the various 80X86 and Pentium family members.
4. Draw the block diagram of a computer system and explain the purpose of each block.
5. Describe the function of the microprocessor and detail its basic operation.
6. Define the contents of the memory system in the personal computer.
7. Convert between binary, decimal, and hexadecimal numbers.
8. Differentiate and represent numeric and alphabetic information as integers, floating-point,
BCD, and ASCII data.

1

2

CHAPTER 1

1–1

A HISTORICAL BACKGROUND
This first section outlines the historical events leading to the development of the microprocessor
and, specifically, the extremely powerful and current 80X86,1 Pentium, Pentium Pro, Pentium III,
Pentium 4,2 and Core2 microprocessors. Although a study of history is not essential to understand
the microprocessor, it furnishes interesting reading and provides a historical perspective of the
fast-paced evolution of the computer.

The Mechanical Age
The idea of a computing system is not new—it has been around long before modem electrical and
electronic devices were developed. The idea of calculating with a machine dates to 500 BC when
the Babylonians, the ancestors of the present-day Iraqis, invented the abacus, the first mechanical
calculator. The abacus, which uses strings of beads to perform calculations, was used by the
ancient Babylonian priests to keep track of their vast storehouses of grain. The abacus, which was
used extensively and is still in use today, was not improved until 1642, when mathematician
Blaise Pascal invented a calculator that was constructed of gears and wheels. Each gear contained
10 teeth that, when moved one complete revolution, advanced a second gear one place. This is the
same principle that is used in the automobile’s odometer mechanism and is the basis of all
mechanical calculators. Incidentally, the PASCAL programming language is named in honor of
Blaise Pascal for his pioneering work in mathematics and with the mechanical calculator.
The arrival of the first practical geared mechanical machines used to automatically compute information dates to the early 1800s. This is before humans invented the lightbulb or before
much was known about electricity. In this dawn of the computer age, humans dreamed of
mechanical machines that could compute numerical facts with a program—not merely calculating facts, as with a calculator.
In 1937 it was discovered through plans and journals that one early pioneer of mechanical computing machinery was Charles Babbage, aided by Augusta Ada Byron, the Countess of Lovelace.
Babbage was commissioned in 1823 by the Royal Astronomical Society of Great Britain to produce
a programmable calculating machine. This machine was to generate navigational tables for the Royal
Navy. He accepted the challenge and began to create what he called his Analytical Engine. This
engine was a steam-powered mechanical computer that stored a thousand 20-digit decimal numbers and a variable program that could modify the function of the machine to perform various calculating tasks. Input to his engine was through punched cards, much as computers in the 1950s and
1960s used punched cards. It is assumed that he obtained the idea of using punched cards from Joseph
Jacquard, a Frenchman who used punched cards as input to a weaving machine he invented in 1801,
which is today called Jacquard’s loom. Jacquard’s loom used punched cards to select intricate weaving patterns in the cloth that it produced. The punched cards programmed the loom.
After many years of work, Babbage’s dream began to fade when he realized that the
machinists of his day were unable to create the mechanical parts needed to complete his work.
The Analytical Engine required more than 50,000 machined parts, which could not be made with
enough precision to allow his engine to function reliably.

The Electrical Age
The 1800s saw the advent of the electric motor (conceived by Michael Faraday); with it came a
multitude of motor-driven adding machines, all based on the mechanical calculator developed by
Blaise Pascal. These electrically driven mechanical calculators were common pieces of office
180X86

is an accepted acronym for 8086, 8088, 80186, 80188, 80286, 80386, and 80486 microprocessors
and also include the Pentium series.
2Pentium, Pentium Pro, Pentium II, Pentium III, Pentium 4, and Core2 are registered trademarks of Intel Corporation.

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

3

equipment until well into the early 1970s, when the small handheld electronic calculator, first
introduced by Bomar Corporation and called the Bomar Brain, appeared. Monroe was also a
leading pioneer of electronic calculators, but its machines were desktop, four-function models
the size of cash registers.
In 1889, Herman Hollerith developed the punched card for storing data. Like Babbage, he
too apparently borrowed the idea of a punched card from Jacquard. He also developed a mechanical machine—driven by one of the new electric motors—that counted, sorted, and collated
information stored on punched cards. The idea of calculating by machinery intrigued the United
States government so much that Hollerith was commissioned to use his punched-card system to
store and tabulate information for the 1890 census.
In 1896, Hollerith formed a company called the Tabulating Machine Company, which
developed a line of machines that used punched cards for tabulation. After a number of mergers,
the Tabulating Machine Company was formed into the International Business Machines
Corporation, now referred to more commonly as IBM, Inc. The punched cards used in early
computer systems are often called Hollerith cards, in honor of Herman Hollerith. The 12-bit
code used on a punched card is called the Hollerith code.
Mechanical machines driven by electric motors continued to dominate the information
processing world until the construction of the first electronic calculating machine in 1941.
A German inventor named Konrad Zuse, who worked as an engineer for the Henschel Aircraft
Company in Berlin, invented the first modern electromechanical computer. His Z3 calculating
computer, as pictured in Figure 1–1, was probably invented for use in aircraft and missile design
during World War II for the German war effort. The Z3 was a relay logic machine that was
clocked at 5.33 Hz (far slower than the latest multiple GHz microprocessors). Had Zuse been
given adequate funding by the German government, he most likely would have developed a

FIGURE 1–1 The Z3 computer developed by Konrad Zuse uses a 5.33 hertz clocking frequency. (Photo courtesy
of Horst Zuse, the son of Konrad.)

4

CHAPTER 1

much more powerful computer system. Zuse is today finally receiving some belated honor for
his pioneering work in the area of digital electronics, which began in the 1930s, and for his Z3
computer system. In 1936 Zuse constructed a mechanical version of his system and later in 1939
Zuse constructed his first electromechanical computer system, called the Z2.
It has recently been discovered (through the declassification of British military documents)
that the first electronic computer was placed into operation in 1943 to break secret German military codes. This first electronic computing system, which used vacuum tubes, was invented by
Alan Turing. Turing called his machine Colossus, probably because of its size. A problem with
Colossus was that although its design allowed it to break secret German military codes generated
by the mechanical Enigma machine, it could not solve other problems. Colossus was not
programmable—it was a fixed-program computer system, which today is often called a specialpurpose computer.
The first general-purpose, programmable electronic computer system was developed in
1946 at the University of Pennsylvania. This first modem computer was called the ENIAC
(Electronic Numerical Integrator and Calculator). The ENIAC was a huge machine, containing over 17,000 vacuum tubes and over 500 miles of wires. This massive machine weighed
over 30 tons, yet performed only about 100,000 operations per second. The ENIAC thrust
the world into the age of electronic computers. The ENIAC was programmed by rewiring its
circuits—a process that took many workers several days to accomplish. The workers changed
the electrical connections on plug-boards that looked like early telephone switchboards.
Another problem with the ENIAC was the life of the vacuum tube components, which required
frequent maintenance.
Breakthroughs that followed were the development of the transistor on December 23, 1947
at Bell Labs by John Bardeen, William Shockley, and Walter Brattain. This was followed by the
1958 invention of the integrated circuit by Jack Kilby of Texas Instruments. The integrated
circuit led to the development of digital integrated circuits (RTL, or resistor-to-transistor logic)
in the 1960s and the first microprocessor at Intel Corporation in 1971. At that time, Intel engineers Federico Faggin, Ted Hoff, and Stan Mazor developed the 4004 microprocessor (U.S.
Patent 3,821,715)—the device that started the microprocessor revolution that continues today at
an ever-accelerating pace.

Programming Advancements
Now that programmable machines were developed, programs and programming languages
began to appear. As mentioned earlier, the first programmable electronic computer system was
programmed by rewiring its circuits. Because this proved too cumbersome for practical application, early in the evolution of computer systems, computer languages began to appear in order to
control the computer. The first such language, machine language, was constructed of ones and
zeros using binary codes that were stored in the computer memory system as groups of instructions called a program. This was more efficient than rewiring a machine to program it, but it was
still extremely time-consuming to develop a program because of the sheer number of program
codes that were required. Mathematician John von Neumann was the first modern person to
develop a system that accepted instructions and stored them in memory. Computers are often
called von Neumann machines in honor of John von Neumann. (Recall that Babbage also had
developed the concept long before von Neumann.)
Once computer systems such as the UNIVAC became available in the early 1950s,
assembly language was used to simplify the chore of entering binary code into a computer as
its instructions. The assembler allows the programmer to use mnemonic codes, such as ADD for
addition, in place of a binary number such as 0100 0111. Although assembly language was an
aid to programming, it wasn’t until 1957, when Grace Hopper developed the first high-level
programming language called FLOWMATIC, that computers became easier to program. In the

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

5

same year, IBM developed FORTRAN (FORmula TRANslator) for its computer systems. The
FORTRAN language allowed programmers to develop programs that used formulas to solve
mathematical problems. Note that FORTRAN is still used by some scientists for computer
programming. Another similar language, introduced about a year after FORTRAN, was ALGOL
(ALGOrithmic Language).
The first truly successful and widespread programming language for business applications
was COBOL (COmputer Business Oriented Language). Although COBOL usage has diminished considerably in recent years, it is still a player in some large business and government
systems. Another once-popular business language is RPG (Report Program Generator), which
allows programming by specifying the form of the input, output, and calculations.
Since these early days of programming, additional languages have appeared. Some of the
more common modern programming languages are BASIC, C#, C/C++, Java, PASCAL, and
ADA. The BASIC and PASCAL languages were both designed as teaching languages, but have
escaped the classroom. The BASIC language is used in many computer systems and may be one
of the most common programming languages today. The BASIC language is probably the easiest
of all to learn. Some estimates indicate that the BASIC language is used in the personal computer
for 80% of the programs written by users. In the past decade, a new version of BASIC, Visual
BASIC, has made programming in the Windows environment easier. The Visual BASIC language may eventually supplant C/C++ and PASCAL as a scientific language, but it is doubtful.
It is more apparent that the C# language is gaining headway and may actually replace C/C++ and
most other languages including Java and may eventually replace BASIC. This of course is conjecture and only the future will show which language eventually becomes dominant.
In the scientific community, primarily C/C++ and occasionally PASCAL and FORTRAN
appear as control programs. One recent survey of embedded system developers showed that C
was used by 60% and that 30% used assembly language. The remainder used BASIC and JAVA.
These languages, especially C/C++, allow the programmer almost complete control over the programming environment and computer system. In many cases, C/C++ is replacing some of the
low-level machine control software or drivers normally reserved for assembly language. Even so,
assembly language still plays an important role in programming. Many video games written for
the personal computer are written almost exclusively in assembly language. Assembly language
is also interspersed with C/C++ to perform machine control functions efficiently. Some of the
newer parallel instructions found on the newest Pentium and Core2 microprocessors are only
programmable in assembly language.
The ADA language is used heavily by the Department of Defense. The ADA language was
named in honor of Augusta Ada Byron, Countess of Lovelace. The Countess worked with
Charles Babbage in the early 1800s in the development of software for his Analytical Engine.

The Microprocessor Age
The world’s first microprocessor, the Intel 4004, was a 4-bit microprocessor–programmable controller on a chip. It addressed a mere 4096, 4-bit-wide memory locations. (A bit is a binary digit
with a value of one or zero. A 4-bit-wide memory location is often called a nibble.) The 4004
instruction set contained only 45 instructions. It was fabricated with the then-current state-ofthe-art P-channel MOSFET technology that only allowed it to execute instructions at the slow
rate of 50 KIPs (kilo-instructions per second). This was slow when compared to the 100,000
instructions executed per second by the 30-ton ENIAC computer in 1946. The main difference
was that the 4004 weighed much less than an ounce.
At first, applications abounded for this device. The 4-bit microprocessor debuted in early
video game systems and small microprocessor-based control systems. One such early video game,
a shuffleboard game, was produced by Bailey. The main problems with this early microprocessor
were its speed, word width, and memory size. The evolution of the 4-bit microprocessor ended

6

CHAPTER 1

TABLE 1–1 Early 8-bit
microprocessors.

Manufacturer
Fairchild
Intel
MOS Technology
Motorola
National Semiconductor
Rockwell International
Zilog

Part Number
F-8
8080
6502
MC6800
IMP-8
PPS-8
Z-8

when Intel released the 4040, an updated version of the earlier 4004. The 4040 operated at a
higher speed, although it lacked improvements in word width and memory size. Other companies,
particularly Texas Instruments (TMS-1000), also produced 4-bit microprocessors. The 4-bit
microprocessor still survives in low-end applications such as microwave ovens and small control
systems and is still available from some microprocessor manufacturers. Most calculators are still
based on 4-bit microprocessors that process 4-bit BCD (binary-coded decimal) codes.
Later in 1971, realizing that the microprocessor was a commercially viable product, Intel
Corporation released the 8008—an extended 8-bit version of the 4004 microprocessor. The
8008 addressed an expanded memory size (16K bytes) and contained additional instructions
(a total of 48) that provided an opportunity for its application in more advanced systems.
(A byte is generally an 8-bit-wide binary number and a K is 1024. Often, memory size is specified in K bytes.)
As engineers developed more demanding uses for the 8008 microprocessor, they discovered that its somewhat small memory size, slow speed, and instruction set limited its usefulness.
Intel recognized these limitations and introduced the 8080 microprocessor in 1973—the first of
the modem 8-bit microprocessors. About six months after Intel released the 8080 microprocessor, Motorola Corporation introduced its MC6800 microprocessor. The floodgates opened and
the 8080—and, to a lesser degree, the MC6800—ushered in the age of the microprocessor. Soon,
other companies began to introduce their own versions of the 8-bit microprocessor. Table 1–1 lists
several of these early microprocessors and their manufacturers. Of these early microprocessor
producers, only Intel and Motorola (IBM also produces Motorola-style microprocessors) continue
successfully to create newer and improved versions of the microprocessor. Motorola has sold its
microprocessor division, and that company is now called Freescale Semiconductors, Inc. Zilog
still manufactures microprocessors, but remains in the background, concentrating on microcontrollers and embedded controllers instead of general-purpose microprocessors. Rockwell has all
but abandoned microprocessor development in favor of modem circuitry. Motorola has declined
from having nearly 50% share of the microprocessor market to a much smaller share. Intel today
has nearly 100% of the desktop and notebook market.

What Was Special about the 8080? Not only could the 8080 address more memory and execute additional instructions, but it executed them 10 times faster than the 8008. An addition that
took 20 μs (50,000 instructions per second) on an 8008-based system required only 2.0 μs
(500,000 instructions per second) on an 8080-based system. Also, the 8080 was compatible with
TTL (transistor-transistor logic), whereas the 8008 was not directly compatible. This made interfacing much easier and less expensive. The 8080 also addressed four times more memory
(64K bytes) than the 8008 (l6K bytes). These improvements are responsible for ushering in the
era of the 8080 and the continuing saga of the microprocessor. Incidentally, the first personal
computer, the MITS Altair 8800, was released in 1974. (Note that the number 8800 was probably chosen to avoid copyright violations with Intel.) The BASIC language interpreter, written for
the Altair 8800 computer, was developed in 1975 by Bill Gates and Paul Allen, the founders of

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

7

Microsoft Corporation. The assembler program for the Altair 8800 was written by Digital
Research Corporation, which once produced DR-DOS for the personal computer.

The 8085 Microprocessor. In 1977, Intel Corporation introduced an updated version of the
8080—the 8085. The 8085 was to be the last 8-bit, general-purpose microprocessor developed
by Intel. Although only slightly more advanced than an 8080 microprocessor, the 8085 executed
software at an even higher speed. An addition that took 2.0 μs (500,000 instructions per second
on the 8080) required only 1.3 μs (769,230 instructions per second) on the 8085. The main
advantages of the 8085 were its internal clock generator, internal system controller, and higher
clock frequency. This higher level of component integration reduced the 8085’s cost and
increased its usefulness. Intel has managed to sell well over 100 million copies of the 8085
microprocessor, its most successful 8-bit, general-purpose microprocessor. Because the 8085 is
also manufactured (second-sourced) by many other companies, there are over 200 million
of these microprocessors in existence. Applications that contain the 8085 will likely continue to
be popular. Another company that sold 500 million 8-bit microprocessors is Zilog Corporation,
which produced the Z-80 microprocessor. The Z-80 is machine language–compatible with the
8085, which means that there are over 700 million microprocessors that execute 8085/Z-80
compatible code!

The Modern Microprocessor
In 1978, Intel released the 8086 microprocessor; a year or so later, it released the 8088. Both
devices are 16-bit microprocessors, which executed instructions in as little as 400 ns (2.5 MIPs,
or 2.5 millions of instructions per second). This represented a major improvement over the execution speed of the 8085. In addition, the 8086 and 8088 addressed 1M byte of memory, which
was 16 times more memory than the 8085. (A 1M-byte memory contains 1024K byte-sized
memory locations or 1,048,576 bytes.) This higher execution speed and larger memory size
allowed the 8086 and 8088 to replace smaller minicomputers in many applications. One other
feature found in the 8086/8088 was a small 4- or 6-byte instruction cache or queue that
prefetched a few instructions before they were executed. The queue sped the operation of many
sequences of instructions and proved to be the basis for the much larger instruction caches found
in modem microprocessors.
The increased memory size and additional instructions in the 8086 and 8088 have led to
many sophisticated applications for microprocessors. Improvements to the instruction set
included multiply and divide instructions, which were missing on earlier microprocessors.
In addition, the number of instructions increased from 45 on the 4004, to 246 on the 8085, to well
over 20,000 variations on the 8086 and 8088 microprocessors. Note that these microprocessors
are called CISC (complex instruction set computers) because of the number and complexity of
instructions. The additional instructions eased the task of developing efficient and sophisticated
applications, even though the number of instructions are at first overwhelming and timeconsuming to learn. The 16-bit microprocessor also provided more internal register storage
space than the 8-bit microprocessor. The additional registers allowed software to be written more
efficiently.
The 16-bit microprocessor evolved mainly because of the need for larger memory systems.
The popularity of the Intel family was ensured in 1981, when IBM Corporation decided to use
the 8088 microprocessor in its personal computer. Applications such as spreadsheets, word
processors, spelling checkers, and computer-based thesauruses were memory-intensive and
required more than the 64K bytes of memory found in 8-bit microprocessors to execute efficiently. The 16-bit 8086 and 8088 provided 1M byte of memory for these applications. Soon,
even the 1M-byte memory system proved limiting for large databases and other applications.
This led Intel to introduce the 80286 microprocessor, an updated 8086, in 1983.

8

CHAPTER 1

The 80286 Microprocessor. The 80286 microprocessor (also a 16-bit architecture microprocessor)
was almost identical to the 8086 and 8088, except it addressed a 16M-byte memory system instead
of a 1M-byte system. The instruction set of the 80286 was almost identical to the 8086 and 8088,
except for a few additional instructions that managed the extra 15M bytes of memory. The clock
speed of the 80286 was increased, so it executed some instructions in as little as 250 ns (4.0 MIPs)
with the original release 8.0 MHz version. Some changes also occurred to the internal execution of
the instructions, which led to an eightfold increase in speed for many instructions when compared to
8086/8088 instructions.
The 32-Bit Microprocessor. Applications began to demand faster microprocessor speeds, more
memory, and wider data paths. This led to the arrival of the 80386 in 1986 by Intel Corporation.
The 80386 represented a major overhaul of the 16-bit 8086–80286 architecture. The 80386 was
Intel’s first practical 32-bit microprocessor that contained a 32-bit data bus and a 32-bit memory
address. (Note that Intel produced an earlier, although unsuccessful, 32-bit microprocessor called
the iapx-432.) Through these 32-bit buses, the 80386 addressed up to 4G bytes of memory. (1G of
memory contains 1024M, or 1,073,741,824 locations.) A 4G-byte memory can store an astounding 1,000,000 typewritten, double-spaced pages of ASCII text data. The 80386 was available in a
few modified versions such as the 80386SX, which addressed 16M bytes of memory through a
16-bit data and 24-bit address bus, and the 80386SL/80386SLC, which addressed 32M bytes of
memory through a 16-bit data and 25-bit address bus. An 80386SLC version contained an internal
cache memory that allowed it to process data at even higher rates. In 1995, Intel released the
80386EX microprocessor. The 80386EX microprocessor is called an embedded PC because it
contains all the components of the AT class personal computer on a single integrated circuit. The
80386EX also contains 24 lines for input/output data, a 26-bit address bus, a 16-bit data bus, a
DRAM refresh controller, and programmable chip selection logic.
Applications that require higher microprocessor speeds and large memory systems include
software systems that use a GUI, or graphical user interface. Modem graphical displays often
contain 256,000 or more picture elements (pixels, or pels). The least sophisticated VGA
(variable graphics array) video display has a resolution of 640 pixels per scanning line with
480 scanning lines (this is the resolution used when the computer boots and display the boot
screen). To display one screen of information, each picture element must be changed, which
requires a high-speed microprocessor. Virtually all new software packages use this type of video
interface. These GUI-based packages require high microprocessor speeds and accelerated video
adapters for quick and efficient manipulation of video text and graphical data. The most striking
system, which requires high-speed computing for its graphical display interface, is Microsoft
Corporation’s Windows.3 We often call a GUI a WYSIWYG (what you see is what you get)
display.
The 32-bit microprocessor is needed because of the size of its data bus, which transfers
real (single-precision floating-point) numbers that require 32-bit-wide memory. In order to efficiently process 32-bit real numbers, the microprocessor must efficiently pass them between itself
and memory. If the numbers pass through an 8-bit data bus, it takes four read or write cycles;
when passed through a 32-bit data bus, however, only one read or write cycle is required. This
significantly increases the speed of any program that manipulates real numbers. Most high-level
languages, spreadsheets, and database management systems use real numbers for data storage.
Real numbers are also used in graphical design packages that use vectors to plot images on
the video screen. These include such CAD (computer-aided drafting/design) systems as
AUTOCAD, ORCAD, and so forth.

3Windows

is a registered trademark of Microsoft Corporation and is currently available as Windows 98, Windows 2000,
Windows ME, and Windows XP.

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

9

Besides providing higher clocking speeds, the 80386 included a memory management unit
that allowed memory resources to be allocated and managed by the operating system. Earlier
microprocessors left memory management completely to the software. The 80386 included hardware circuitry for memory management and memory assignment, which improved its efficiency
and reduced software overhead.
The instruction set of the 80386 microprocessor was upward-compatible with the earlier
8086, 8088, and 80286 microprocessors. Additional instructions referenced the 32-bit registers
and managed the memory system. Note that memory management instructions and techniques
used by the 80286 are also compatible with the 80386 microprocessor. These features allowed
older, 16-bit software to operate on the 80386 microprocessor.

The 80486 Microprocessor. In 1989, Intel released the 80486 microprocessor, which incorporated an 80386-like microprocessor, an 80387-like numeric coprocessor, and an 8K-byte cache
memory system into one integrated package. Although the 80486 microprocessor was not radically different from the 80386, it did include one substantial change. The internal structure of the
80486 was modified from the 80386 so that about half of its instructions executed in one clock
instead of two clocks. Because the 80486 was available in a 50 MHz version, about half of the
instructions executed in 25 ns (50 MIPs). The average speed improvement for a typical mix
of instructions was about 50% over the 80386 that operated at the same clock speed. Later
versions of the 80486 executed instructions at even higher speeds with a 66 MHz double-clocked
version (80486DX2). The double-clocked 66 MHz version executed instructions at the rate of
66 MHz, with memory transfers executing at the rate of 33 MHz. (This is why it was called a
double-clocked microprocessor.) A triple-clocked version from Intel, the 80486DX4, improved
the internal execution speed to 100 MHz with memory transfers at 33 MHz. Note that the
80486DX4 microprocessor executed instructions at about the same speed as the 60 MHz Pentium.
It also contained an expanded 16K-byte cache in place of the standard 8K-byte cache found on
earlier 80486 microprocessors. Advanced Micro Devices (AMD) has produced a triple-clocked
version that runs with a bus speed of 40 MHz and a clock speed of 120 MHz. The future promises
to bring microprocessors that internally execute instructions at rates of up to 10 GHz or higher.
Other versions of the 80486 were called OverDrive4 processors. The OverDrive processor
was actually a double-clocked version of the 80486DX that replaced an 80486SX or slowerspeed 80486DX. When the OverDrive processor was plugged into its socket, it disabled or
replaced the 80486SX or 80486DX, and functioned as a doubled-clocked version of the microprocessor. For example, if an 80486SX, operating at 25 MHz, was replaced with an OverDrive
microprocessor, it functioned as an 80486DX2 50 MHz microprocessor using a memory transfer
rate of 25 MHz.
Table 1–2 lists many microprocessors produced by Intel and Motorola with information
about their word and memory sizes. Other companies produce microprocessors, but none have
attained the success of Intel and, to a lesser degree, Motorola.
The Pentium Microprocessor. The Pentium, introduced in 1993, was similar to the 80386 and
80486 microprocessors. This microprocessor was originally labeled the P5 or 80586, but Intel
decided not to use a number because it appeared to be impossible to copyright a number. The two
introductory versions of the Pentium operated with a clocking frequency of 60 MHz and
66 MHz, and a speed of 110 MIPs, with a higher-frequency 100 MHz one and one-half clocked
version that operated at 150 MIPs. The double-clocked Pentium, operating at 120 MHz and
133 MHz, was also available, as were higher-speed versions. (The fastest version produced by
Intel is the 233 MHz Pentium, which is a three and one-half clocked version.) Another difference
was that the cache size was increased to 16K bytes from the 8K cache found in the basic version
4OverDrive

is a registered trademark of Intel Corporation.

10

CHAPTER 1

TABLE 1–2

Manufacturer
Intel

Many modern Intel and Motorola microprocessors.

Part Number
8048
8051
8085A
8086
8088
8096
80186
80188
80251
80286
80386EX
80386DX
80386SL
80386SLC
80386SX
80486DX/DX2
80486SX
80486DX4
Pentium
Pentium OverDrive
Pentium Pro

8
8
8
16
8
16
16
8
8
16
16
32
16
16
16
32
32
32
64
32
64

Pentium II

64

Pentium III

64

Pentium 4

64

Pentium4 D
(Dual Core)
Core2

64

Itanium (Dual Core)
Motorola

Data Bus Width

6800
6805
6809
68000
68008D
68008Q
68010
68020
68030
68040
68050
68060
PowerPC

64
128
8
8
8
16
8
8
16
32
32
32
32
64
64

Memory Size
2K internal
8K internal
64K
1M
1M
8K internal
1M
1M
16K internal
16M
64M
4G
32M
32M + 8K cache
16M
4G + 8K cache
4G + 8K cache
4G + 16 cache
4G + 16K cache
4G + 16K cache
64G + 16K L1 cache +
256K L2 cache
64G + 32K L1 cache +
256K L2 cache
64G + 32K L1 cache +
256K L2 cache
64G+32K L1 cache+
512K L2 cache (or larger)
(1T for 64-bit extensions)
1T + 32K L1 cache + 2 or
4 M L2 cache
1T + 32K L1 cache + a shared
2 or 4 M L2 cache
1T + 2.5 M L1 and L2 cache
+ 24 M L3 cache
64K
2K
64K
16M
4M
1M
16M
4G
4G + 256 cache
4G + 8K cache
Proposed, but never released
4G + 16K cache
4G + 32K cache

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

11

of the 80486. The Pentium contained an 8K-byte instruction cache and an 8K-byte data cache,
which allowed a program that transfers a large amount of memory data to still benefit from a
cache. The memory system contained up to 4G bytes, with the data bus width increased from the
32 bits found in the 80386 and 80486 to a full 64 bits. The data bus transfer speed was either
60 MHz or 66 MHz, depending on the version of the Pentium. (Recall that the bus speed of the
80486 was 33 MHz.) This wider data bus width accommodated double-precision floating-point
numbers used for modem high-speed, vector-generated graphical displays. These higher bus
speeds should allow virtual reality software and video to operate at more realistic rates on current
and future Pentium-based platforms. The widened data bus and higher execution speed of the
Pentium allow full-frame video displays to operate at scan rates of 30 Hz or higher—comparable
to commercial television. Recent versions of the Pentium also included additional instructions,
called multimedia extensions, or MMX instructions. Although Intel hoped that the MMX
instructions would be widely used, it appears that few software companies have used them. The
main reason is there is no high-level language support for these instructions.
Intel had also released the long-awaited Pentium OverDrive (P24T) for older 80486 systems
that operate at either 63 MHz or 83 MHz clock. The 63 MHz version upgrades older 80486DX2
50 MHz systems; the 83 MHz version upgrades the 80486DX2 66 MHz systems. The upgraded
83 MHz system performs at a rate somewhere between a 66 MHz Pentium and a 75 MHz
Pentium. If older VESA local bus video and disk-caching controllers seem too expensive to toss
out, the Pentium OverDrive represents an ideal upgrade path from the 80486 to the Pentium.
Probably the most ingenious feature of the Pentium is its dual integer processors. The
Pentium executes two instructions, which are not dependent on each other, simultaneously
because it contains two independent internal integer processors called superscaler technology.
This allows the Pentium to often execute two instructions per clocking period. Another feature
that enhances performance is a jump prediction technology that speeds the execution of program
loops. As with the 80486, the Pentium also employs an internal floating-point coprocessor to
handle floating-point data, albeit at a five times speed improvement. These features portend
continued success for the Intel family of microprocessors. Intel also may allow the Pentium to
replace some of the RISC (reduced instruction set computer) machines that currently execute
one instruction per clock. Note that some newer RISC processors execute more than one instruction per clock through the introduction of superscaler technology. Motorola, Apple, and IBM
produce the PowerPC, a RISC microprocessor that has two integer units and a floating-point
unit. The PowerPC certainly boosts the performance of the Apple Macintosh, but at present is
slow to efficiently emulate the Intel family of microprocessors. Tests indicate that the current
emulation software executes DOS and Windows applications at speeds slower than the 80486DX
25 MHz microprocessor. Because of this, the Intel family should survive for many years in personal computer systems. Note that there are currently 6 million Apple Macintosh5 systems and
well over 260 million personal computers based on Intel microprocessors. In 1998, reports
showed that 96% of all PCs were shipped with the Windows operating system.
Recently Apple computer replaced the PowerPC with the Intel Pentium in most of its computer systems. It appears that the PowerPC could not keep pace with the Pentium line from Intel.
In order to compare the speeds of various microprocessors, Intel devised the iCOMPrating index. This index is a composite of SPEC92, ZD Bench, and Power Meter. The iCOMP1
rating index is used to rate the speed of all Intel microprocessors through the Pentium.
Figure 1–2 shows relative speeds of the 80386DX 25 MHz version at the low end to the Pentium
233 MHz version at the high end of the spectrum.
Since the release of the Pentium Pro and Pentium II, Intel has switched to the iCOMP2-rating
index, which is scaled by a factor of 10 from the iCOMP1 index. A microprocessor with an index of
1000 using iCOMP1 is rated as 100 using iCOMP2. Another difference is the benchmarks used for
5Macintosh

is a registered trademark of Apple Computer Corporation.

12

CHAPTER 1
0

FIGURE 1–2 The Intel
iCOMP-rating index.
Pentium 200
Pentium 166
Pentium 133
Pentium 120
Pentium 100
Pentium 90
Pentium 75
Pentium 83*
Pentium 66
Pentium 60
Pentium 63*
486 DX4 100
486 DX4 75
486 DX2 66
486 DX 50
486 DX2 50
486 SX2 50
486 DX 33
486 SX2 40
486 SX 33
486 DX 25
486 SX 25
486 SX 20
386 DX 33
386 SX 33
386 DX 25
386 SX 25
386 SX 20
386 SX 16

100

200

400

600 800 1000 1200 1400 1600 1800
1810
1570
1110
1000
815
735
610
583
567
510
443

435
319
297
249
231
180
166
145
136
122
100
78
68
56
49
39
32
22
Note: *Pentium OverDrive, the first part of
the scale is not linear, and the 166 MHz
and 200 MHz are MMX technology.

the scores. Figure 1–3 shows the iCOMP2 index listing the Pentium III at speeds up to 1000 MHz.
Figure 1–4 shows SYSmark 2002 for the Pentium III and Pentium 4. Unfortunately Intel has not
released any benchmarks that compare versions of the microprocessor since the SYSmark 2002.
Newer benchmarks are available, but they do not compare one version with another.

Pentium Pro Processor. A recent entry from Intel is the Pentium Pro processor, formerly
named the P6 microprocessor. The Pentium Pro processor contains 21 million transistors, integer
units, as well as a floating-point unit to increase the performance of most software. The basic
clock frequency was 150 MHz and 166 MHz in the initial offering made available in late 1995.
In addition to the internal 16K level-one (L1) cache (8K for data and 8K for instructions) the
Pentium Pro processor also contains a 256K level-two (L2) cache. One other significant change
is that the Pentium Pro processor uses three execution engines, so it can execute up to three
instructions at a time, which can conflict and still execute in parallel. This represents a
change from the Pentium, which executes two instructions simultaneously as long as they do not
conflict. The Pentium Pro microprocessor has been optimized to efficiently execute 32-bit code;
for this reason, it was often bundled with Windows NT rather than with normal versions of
Windows 95. Intel launched the Pentium Pro processor for the server market. Still another
change is that the Pentium Pro can address either a 4G-byte memory system or a 64G-byte memory system. The Pentium Pro has a 36-bit address bus if configured for a 64G memory system.
Pentium II and Pentium Xeon Microprocessors. The Pentium II microprocessor (released in
1997) represents a new direction for Intel. Instead of being an integrated circuit as with prior versions of the microprocessor, Intel has placed the Pentium II on a small circuit board. The main
reason for the change is that the L2 cache found on the main circuit board of the Pentium was not

13

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

FIGURE 1–3 The Intel
iCOMP2-rating index.

Pentium III 1000 MHz

1277

Pentium III 933 MHz

1207

Pentium III 866 MHz

1125

Pentium III 800 MHz

1048

Pentium III 750 MHz

989

Pentium III 700 MHz

942

Pentium III 650 MHz

884

Pentium III 600 MHz

753

Pentium III 550 MHz

693

Pentium III 500 MHz

642

Pentium II 450 MHz

483

Pentium II 400 MHz

440

Pentium II 350 MHz
Pentium II 333 MHz

386
366

Pentium II 300 MHz

332

Pentium II 266 MHz

303

Pentium II 233 MHz

267

Pentium II* 266 MHz

213

Pentium 233 MHz

203

Note: *Pentium II Celeron, no cache.
iCOMP2 numbers are shown above. To
convert to iCOMP3, multiply by 2.568.

fast enough to function properly with the Pentium II. On the Pentium system, the L2 cache operates at the system bus speed of 60 MHz or 66 MHz. The L2 cache and microprocessor are on a
circuit board called the Pentium II module. This onboard L2 cache operates at a speed of
133 MHz and stores 512K bytes of information. The microprocessor on the Pentium II module is
actually Pentium Pro with MMX extensions.
In 1998, Intel changed the bus speed of the Pentium II. Because the 266 MHz through the
333 MHz Pentium II microprocessors used an external bus speed of 66 MHz, there was a bottleneck, so the newer Pentium II microprocessors use a 100 MHz bus speed. The Pentium II microprocessors rated at 350 MHz, 400 MHz, and 450 MHz all use this higher 100 MHz memory bus
speed. The higher speed memory bus requires the use of 8 ns SDRAM in place of the 10 ns
SDRAM found in use with the 66 MHz bus speed.

14

CHAPTER 1

FIGURE 1–4 Intel
microprocessor
performance using
SYSmark 2002.

Pentium 4 3.2
GHz
Pentium 4 2.8
GHz
Pentium 4 2.4
GHz
Pentium III
1000 MHz

0

200

400

In mid-1998 Intel announced a new version of the Pentium II called the Xeon,6 which was
specifically designed for high-end workstation and server applications. The main difference between
the Pentium II and the Pentium II Xeon is that the Xeon is available with a L1 cache size of 32K
bytes and a L2 cache size of either 512K, 1M, or 2M bytes. The Xeon functions with the 440GX
chip set. The Xeon is also designed to function with four Xeons in the same system, which is similar
to the Pentium Pro. This newer product represents a change in Intel’s strategy: Intel now produces a
professional version and a home/business version of the Pentium II microprocessor.

Pentium III Microprocessor. The Pentium III microprocessor uses a faster core than the
Pentium II, but it is still a P6 or Pentium Pro processor. It is also available in the slot 1 version
mounted on a plastic cartridge and a socket 370 version called a flip-chip, which looks like the
older Pentium package. Intel claims the flip-chip version costs less. Another difference is that the
Pentium III is available with clock frequencies of up to 1 GHz. The slot 1 version contains a
512K cache and the flip-chip version contains a 256K cache. The speeds are comparable because
the cache in the slot 1 version runs at one-half the clock speed, while the cache in the flip-chip
version runs at the clock speed. Both versions use a memory bus speed of 100 MHz, while the
Celeron7 uses memory bus clock speed of 66 MHz.
The speed of the front side bus, the connection from the microprocessor to the memory
controller, PCI controller, and AGP controller, is now either 100 MHz or 133 MHz. Although the
memory still runs at 100 MHz, this change has improved performance.
Pentium 4 and Core2 Microprocessors. The Pentium 4 microprocessor was first made
available in late 2000. The most recent version of the Pentium is called the Core2 by Intel. The
Pentium 4 and Core2, like the Pentium Pro through the Pentium III, use the Intel P6 architecture.
The main difference is that the Pentium 4 is available in speeds to 3.2 GHz and faster and the
chip sets that support the Pentium 4 use the RAMBUS or DDR memory technologies in place of
once standard SDRAM technology. The Core2 is available at speeds of up to 3 GHz. These
higher microprocessor speeds are made available by an improvement in the size of the internal
6Xeon

is a registered trademark of Intel Corporation.
is a trademark of Intel Corporation.

7Celeron

15

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

TABLE 1–3 Intel
microprocessor core (P)
versions.

Core (P) Version

Microprocessor

P1

8086 and 8088 (80186 and 80188)

P2

80286

P3

80386

P4

80486

P5

Pentium

P6

Pentium Pro, Pentium II, Pentium III,
Pentium 4, and Core2

P7

Itanium

integration, which at present is the 0.045 micron or 45 nm technology. It is also interesting to
note that Intel has changed the level 1 cache size from 32K to 8K bytes and most recently to 64K.
Research must have shown that this size is large enough for the initial release version of the
microprocessor, with future versions possibly containing a 64K L1 cache. The level 2 cache
remains at 256K bytes as in the Pentium coppermine version with the latest versions containing
a 512K cache. The Pentium 4 Extreme Edition contains a 2M L2 cache and the Pentium 4e contains a 1M level 2 cache, whereas the Core2 contains either a 2M or 4M L2 cache.
Another change likely to occur is a shift from aluminum to copper interconnections inside
the microprocessor. Because copper is a better conductor, it should allow increased clock frequencies for the microprocessor in the future. This is especially true now that a method for using
copper has surfaced at IBM Corporation. Another event to look for is a change in the speed of the
front side bus, which will likely increase beyond the current maximum 1033 MHz.
Table 1–3 shows the various Intel P numbers and the microprocessors that belong to each
class. The P versions show what internal core microprocessor is found in each of the Intel microprocessors. Notice that all of the microprocessors since the Pentium Pro use the same basic
microprocessor core.

Pentium 4 and Core2, 64-bit and Multiple Core Microprocessors. Recently Intel has included
new modifications to the Pentium 4 and Core2 that include a 64-bit core and multiple cores. The
64-bit modification allows the microprocessor to address more than 4G bytes of memory through
a wider 64-bit address. Currently, 40 address pins in these newer versions allow up to 1T (terabytes) of memory to be accessed. The 64-bit machine also allows 64-bit integer arithmetic, but
this is much less important than the ability to address more memory.
The biggest advancement in the technology is not the 64-bit operation, but the inclusion of
multiple cores. Each core executes a separate task in a program, which increases the speed of
execution if a program is written to take advantage of the multiple cores. Programs that do this
are called multithreaded applications. Currently, Intel manufactures dual and quad core versions, but in the future the number of cores will likely increase to eight or even sixteen. The problem faced by Intel is that the clock speed cannot be increased to a much higher rate, so multiple
cores are the current solution to providing faster microprocessors. Does this mean that higher
clock speeds are not possible? Only the future portends whether they are or are not.
Intel recently demonstrated a version of the Core2 that contains 80 cores that uses the 45 nm
fabrication technology. Intel expects to release an 80-core version some time in the next 5 years. The
fabrication technology will become slightly smaller with 35 nm and possibly 25 nm technology.
The Future of Microprocessors. No one can really make accurate predictions, but the success of
the Intel family should continue for quite a few years. What may occur is a change to RISC technology, but more likely are improvements to a new technology jointly by Intel and Hewlett-Packard

16

CHAPTER 1

called hyper-threading technology. Even this new technology embodies the CISC instruction set of
the 80X86 family of microprocessors, so that software for the system will survive. The basic
premise behind this technology is that many microprocessors communicate directly with each
other, allowing parallel processing without any change to the instruction set or program. Currently,
the superscaler technology uses many microprocessors, but they all share the same register set. This
new technology contains many microprocessors, each containing its own register set that is linked
with the other microprocessors’ registers. This technology offers true parallel processing without
writing any special program.
The hyper-threading technology should continue into the future, bringing even more parallel processors (at present two processors). There are suggestions that Intel may also incorporate
the chip set into the microprocessor package.
In 2002, Intel released a new microprocessor architecture that is 64 bits in width and has
a 128-bit data bus. This new architecture, named the Itanium,8 is a joint venture called
EPIC (Explicitly Parallel Instruction Computing) of Intel and Hewlett-Packard. The Itanium
architecture allows greater parallelism than traditional architectures, such as the Pentium III or
Pentium 4. These changes include 128 general-purpose integer registers, 128 floating-point
registers, 64 predicate registers, and many execution units to ensure enough hardware resources
for software. The Itanium is designed for the server market and may or may not trickle down to
the home/business market in the future.
Figure 1–5 is a conceptual view, comparing the 80486 through Pentium 4 microprocessors.
Each view shows the internal structure of these microprocessors: the CPU, coprocessor, and

FIGURE 1–5 Conceptual
views of the 80486,
Pentium Pro, Pentium II,
Pentium III, Pentium 4, and
Core2 microprocessors.

CPU

Coprocessor

CPU1 CPU2

Copro

8K
L1 Cache

16K L1 Cache

80486DX

Pentium

CPU1 CPU2 CPU3

Copro

16K L1 Cache

CPU1 CPU2 CPU3

Copro

32K L1 Cache

256K L2 Cache
Pentium Pro

512K L2 Cache
or
236K L2 Cache

Pentium II, Pentium III,
Pentium 4, or Core2 Module

8Itanium

is a trademark of Intel Corporation.

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

17

cache memory. This illustration shows the complexity and level of integration in each version of
the microprocessor.
Because clock frequencies seemed to have peaked and the surge to multiple cores
has begun, about the only major change to the Pentium will probably be a wider memory path
(128 bits). Another consideration is the memory speed. Today, dynamic RAMs are the mainstay,
but the speed of dynamic RAM memory has not changed for many years. A push to static RAM
memory will eventually appear and will increase the performance of the PC. The main problem
today with large static RAM is heat. Static RAM operates 50 times faster than dynamic RAM.
Imagine a computer that contains a memory composed of static RAM.
Another problem is the speed of the mass storage connected to a computer. The transfer
speed of hard disk drives has changed little in the past few years. A new technology is needed for
mass storage. Flash memory could be a solution, because its write speed is comparable to hard
disk memory. One change that would increase the speed of the computer system is the placement
of possibly 4G bytes of flash memory to store the operation system for common applications.
This would allow the operating system to load in a second or two instead of the many seconds
required to boot a modern computer system.

1–2

THE MICROPROCESSOR-BASED PERSONAL COMPUTER SYSTEM
Computer systems have undergone many changes recently. Machines that once filled large areas
have been reduced to small desktop computer systems because of the microprocessor. Although
these desktop computers are compact, they possess computing power that was only dreamed of a
few years ago. Million-dollar mainframe computer systems, developed in the early 1980s, are
not as powerful as the Pentium Core2-based computers of today. In fact, many smaller companies have replaced their mainframe computers with microprocessor-based systems. Companies
such as DEC (Digital Equipment Corporation, now owned by Hewlett-Packard Company) have
stopped producing mainframe computer systems in order to concentrate their resources on
microprocessor-based computer systems.
This section shows the structure of the microprocessor-based personal computer system.
This structure includes information about the memory and operating system used in many
microprocessor-based computer systems.
See Figure 1–6 for the block diagram of the personal computer. This diagram also applies to
any computer system, from the early mainframe computers to the latest microprocessor-based
systems. The block diagram is composed of three blocks that are interconnected by buses. (A bus
is the set of common connections that carry the same type of information. For example, the
address bus, which contains 20 or more connections, conveys the memory address to the memory.) These blocks and their function in a personal computer are outlined in this section of the text.

The Memory and I/O System
The memory structure of all Intel-based personal computers is similar. This includes the first personal computers based upon the 8088, introduced in 1981 by IBM, to the most powerful highspeed versions of today, based on the Pentium 4 or Core2. Figure 1–7 illustrates the memory map
of a personal computer system. This map applies to any IBM personal computer or to any of the
many IBM-compatible clones that are in existence.
The memory system is divided into three main parts: TPA (transient program area), system
area, and XMS (extended memory system). The type of microprocessor in your computer determines whether an extended memory system exists. If the computer is based upon a really old
8086 or 8088 (a PC or XT), the TPA and systems area exist, but there is no extended memory

18

CHAPTER 1
Buses

Memory system

Dynamic RAM (DRAM)
Static RAM (SRAM)
Cache
Read-only (ROM)
Flash memory
EEPROM
SDRAM
RAMBUS
DDR DRAM

FIGURE 1–6

Microprocessor

8086
8088
80186
80188
80286
80386
80486
Pentium
Pentium Pro
Pentium II
Pentium III
Pentium 4
Core2

I/O system

Printer
Serial communications
Floppy disk drive
Hard disk drive
Mouse
CD-ROM drive
Plotter
Keyboard
Monitor
Tape backup
Scanner
DVD

The block diagram of a microprocessor-based computer system.

area. The PC and XT computers contain 640K bytes of TPA and 384K bytes of system memory,
for a total memory size of 1M bytes. We often call the first 1M byte of memory the real or conventional memory system because each Intel microprocessor is designed to function in this area
by using its real mode of operation.
Computer systems based on the 80286 through the Core2 not only contain the TPA (640K
bytes) and system area (384K bytes), they also contain extended memory. These machines are
FIGURE 1–7 The memory
map of a personal computer.
Extended memory
15M bytes in the 80286 or 80386SX
31M bytes in the 80386SL/SLC
63M bytes in the 80386EX
4095M bytes in the 80386DX, 80486, and Pentium
64G bytes in the Pentium Pro, Pentium II, Pentium III,
Pentium 4, and Core2

System area
384K bytes
1M bytes of real (conventional) memory
TPA
640K bytes

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

19

often called AT class machines. The PS/l and PS/2, produced by IBM, are other versions of the
same basic memory design. Sometimes, these machines are also referred to as ISA (industry
standard architecture) or EISA (extended ISA) machines. The PS/2 is referred to as a microchannel architecture system, or ISA system, depending on the model number.
A change beginning with the introduction of the Pentium microprocessor and the ATX
class machine is the addition of a bus called the PCI (peripheral component interconnect) bus,
now being used in all Pentium through Core2 systems. Extended memory contains up to 15M
bytes in the 80286 and 80386SX-based computers, and up to 4095M bytes in the 80386DX,
80486, and Pentium microprocessors, in addition to the first 1M byte of real or conventional
memory. The Pentium Pro through Core2 computer systems have up to 1M less than 4G or 1 M
less than 64G of extended memory. Servers tend to use the larger 64G memory map, while
home/business computers use the 4G-byte memory map. The ISA machine contains an 8-bit
peripheral bus that is used to interface 8-bit devices to the computer in the 8086/8088-based
PC or XT computer system. The AT class machine, also called an ISA machine, uses a l6-bit
peripheral bus for interface and may contain the 80286 or above microprocessor. The EISA bus
is a 32-bit peripheral interface bus found in a few older 80386DX- and 80486-based systems.
Note that each of these buses is compatible with the earlier versions. That is, the 8-bit interface
card functions in the 8-bit ISA, l6-bit ISA, or 32-bit EISA bus system. Likewise, a l6-bit interface card functions in the l6-bit ISA or 32-bit EISA system.
Another bus type found in many 80486-based personal computers is called the VESA local
bus, or VL bus. The local bus interfaces disk and video to the microprocessor at the local bus
level, which allows 32-bit interfaces to function at the same clocking speed as the microprocessor. A recent modification to the VESA local bus supports the 64-bit data bus of the Pentium
microprocessor and competes directly with the PCI bus, although it has generated little, if any,
interest. The ISA and EISA standards function at only 8 MHz, which reduces the performance of
the disk and video interfaces using these standards. The PCI bus is either a 32- or 64-bit bus that
is specifically designed to function with the Pentium through Core2 microprocessors at a bus
speed of 33 MHz.
Three newer buses have appeared in ATX class systems. The first to appear was the USB
(universal serial bus). The universal serial bus is intended to connect peripheral devices such as
keyboards, a mouse, modems, and sound cards to the microprocessor through a serial data path
and a twisted pair of wires. The main idea is to reduce system cost by reducing the number of
wires. Another advantage is that the sound system can have a separate power supply from the
PC, which means much less noise. The data transfer rates through the USB are 10 Mbps at present for USB1; they increase to 480 Mbps in USB2.
The second newer bus is the AGP (advanced graphics port) for video cards. The
advanced graphics port transfers data between the video card and the microprocessor at higher
speeds (66 MHz, with a 64-bit data path, or 533M bytes per second) than were possible through
any other bus or connection. The latest AGP speed is 8X or 2G bytes per second. This video subsystem change has been made to accommodate the new DVD players for the PC.
The latest new buses to appear are the serial ATA interface (SATA) for hard disk drives and
the PCI Express bus for the video card. The SATA bus transfers data from the PC to the hard disk
drive at rates of 150M bytes per second or 300M bytes for SATA-2. The serial ATA standard will
eventually reach speeds of 450M bytes per second. Today PCI Express bus video cards operate at
16X speeds.

The TPA. The transient program area (TPA) holds the DOS (disk operating system)
operating system and other programs that control the computer system. The TPA is a DOS concept and not really applicable in Windows. The TPA also stores any currently active or inactive
DOS application programs. The length of the TPA is 640K bytes. As mentioned, this area
of memory holds the DOS operating system, which requires a portion of the TPA to function.

20

CHAPTER 1

FIGURE 1–8 The memory
map of the TPA in a personal
computer. (Note that this map
will vary between systems.)

9FFFF
9FFF0

MSDOS program

Free TPA

08E30
08490

COMMAND.COM
Device drivers
such as MOUSE.SYS

02530
MSDOS program
01160
00700
00500
00400

IO.SYS program
DOS communications area
BIOS communications area
Interrupt vectors

00000

In practice, the amount of memory remaining for application software is about 628K bytes if
MSDOS9 version 7.x is used as an operating system. Earlier versions of DOS required more of
the TPA area and often left only 530K bytes or less for application programs. Figure 1–8 shows
the organization of the TPA in a computer system running DOS.
The DOS memory map shows how the many areas of the TPA are used for system programs, data, and drivers. It also shows a large area of memory available for application programs. To the left of each area is a hexadecimal number that represents the memory addresses
that begin and end each data area. Hexadecimal memory addresses or memory locations are
used to number each byte of the memory system. (A hexadecimal number is a number represented in radix 16 or base 16, with each digit representing a value from 0 to 9 and A to F. We
often end a hexadecimal number with an H to indicate that it is a hexadecimal value. For example, 1234H is 1234 hexadecimal. We also represent hexadecimal data as 0xl234 for a 1234
hexadecimal.)

9MSDOS

is a trademark of Microsoft Corporation and version 7.x is supplied with Windows XP.

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

21

The Interrupt vectors access various features of the DOS, BIOS (basic I/O system), and
applications. The system BIOS is a collection of programs stored in either a read-only (ROM) or
flash memory that operates many of the I/O devices connected to your computer system. The
system BIOS and DOS communications areas contain transient data used by programs to access
I/O devices and the internal features of the computer system. These are stored in the TPA so they
can be changed as the DOS operates.
The IO.SYS is a program that loads into the TPA from the disk whenever an MSDOS system is started. The IO.SYS contains programs that allow DOS to use the keyboard, video display,
printer, and other I/O devices often found in the computer system. The IO.SYS program links
DOS to the programs stored on the system BIOS ROM.
The size of the driver area and number of drivers changes from one computer to another.
Drivers are programs that control installable I/O devices such as a mouse, disk cache, hand scanner, CD-ROM memory (Compact Disk Read-Only Memory), DVD (Digital Versatile Disk),
or installable devices, as well as programs. Installable drivers are programs that control or drive
devices or programs that are added to the computer system. DOS drivers are normally files that
have an extension of .SYS, such as MOUSE.SYS; in DOS version 3.2 and later, the files have an
extension of .EXE, such as EMM386.EXE. Note that even though these files are not used by
Windows, they are still used to execute DOS applications, even with Windows XP. Windows
uses a file called SYSTEM.INI to load drivers used by Windows. In newer versions of Windows
such as Windows XP, a registry is added to contain information about the system and the drivers
used by the system. You can view the registry with the REGEDIT program.
The COMMAND.COM program (command processor) controls the operation of the
computer from the keyboard when operated in the DOS mode. The COMMAND.COM program
processes the DOS commands as they are typed from the keyboard. For example, if DIR is typed,
the COMMAND.COM program displays a directory of the disk files in the current disk directory. If the COMMAND.COM program is erased, the computer cannot be used from the keyboard in DOS mode. Never erase COMMAND.COM, IO.SYS, or MSDOS.SYS to make room
for other software, or your computer will not function.

The System Area. The DOS system area, although smaller than the TPA, is just as important.
The system area contains programs on either a read-only memory (ROM) or flash memory, and
areas of read/write (RAM) memory for data storage. Figure 1–9 shows the system area of a
typical personal computer system. As with the map of the TPA, this map also includes the hexadecimal memory addresses of the various areas.
The first area of the system space contains video display RAM and video control programs
on ROM or flash memory. This area starts at location A0000H and extends to location C7FFFH.
The size and amount of memory used depends on the type of video display adapter attached to
the system. Display adapters generally have their video RAM located at A0000H–AFFFFH,
which stores graphical or bit-mapped data, and the memory at B0000H–BFFFFH stores text
data. The video BIOS, located on a ROM or flash memory, is at locations C0000H–C7FFFH and
contains programs that control the DOS video display.
The area at locations C8000H–DFFFFH is often open or free. This area is used for the
expanded memory system (EMS) in a PC or XT system, or for the upper memory system in an
AT system. Its use depends on the system and its configuration. The expanded memory system
allows a 64K-byte page frame of memory to be used by application programs. This 64K-byte
page frame (usually locations D0000H through DFFFFH) is used to expand the memory system
by switching in pages of memory from the EMS into this range of memory addresses.
Memory locations E0000H–EFFFFH contain the cassette BASIC language on ROM found in
early IBM personal computer systems. This area is often open or free in newer computer systems.
Finally, the system BIOS ROM is located in the top 64K bytes of the system area
(F0000H–FFFFFH). This ROM controls the operation of the basic I/O devices connected to the

22

CHAPTER 1

FIGURE 1–9 The system
area of a typical personal
computer.

FFFFF
BIOS system ROM
F0000
BASIC language ROM
(only on early PCs)
E0000

Free area

C8000

Hard disk controller ROM
LAN controller ROM
Video BIOS ROM

C0000

Video RAM
(text area)
B0000

Video RAM
(graphics area)
A0000

computer system. It does not control the operation of the video system, which has its own BIOS
ROM at location C0000H. The first part of the system BIOS (F0000H–F7FFFH) often contains
programs that set up the computer; the second part contains procedures that control the basic I/O
system.

Windows Systems. Modern computers use a different memory map with Windows than the
DOS memory maps of Figures 1–8 and 1–9. The Windows memory map appears in Figure 1–10
and has two main areas, a TPA and a system area. The difference between it and the DOS
memory map are the sizes and locations of these areas.
The Windows TPA is the first 2G bytes of the memory system from locations 00000000H
to 7FFFFFFFH. The Windows system area is the last 2G bytes of memory from locations
80000000H to FFFFFFFFH. It appears that the same idea used to construct the DOS memory
map was also used in a modern Windows-based system. The system area is where the system
BIOS is located and also the video memory. Also located in the system area is the actual
Windows program and drivers. Every program that is written for Windows can use up to 2G
bytes of memory located at linear addresses 00000000H through 7FFFFFFFH. This is even true
in a 64-bit system, which does allow access to more memory, but not as a direct part of Windows.
Information that is larger than 2G must be swapped into the Windows TPA area from another
area of memory. In future versions of Windows and the Pentium, this will most likely be
changed. The current version of Windows 64 (which is now a part of Windows Vista) supports
up to 8G bytes of Windows memory.

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

23

FIGURE 1–10 The memory
map used by Windows XP.

Does this mean that any program written for Windows will begin at physical address
00000000H? No, the memory system physical map is much different for the linear programming
model shown in Figure 1–10. Every process in a Windows Vista, Windows XP, or Windows 2000
system has its own set of page tables, which define where in the physical memory each 4K-byte
page of the process is located. This means that the process can be located anywhere in the memory, even in noncontiguous pages. Page tables and the paging structure of the microprocessor are
discussed later in this chapter and are beyond the scope of the text at this point. As far as an
application is concerned, you will always have 2G bytes of memory even if the computer has less
memory. The operating system (Windows) handles assigning physical memory to the application
and if not enough physical memory exists, it uses the hard disk drive for any that is not available.

I/O Space. The I/O (input/output) space in a computer system extends from I/O port 0000H to
port FFFFH. (An I/O port address is similar to a memory address, except that instead
of addressing memory, it addresses an I/O device.) The I/O devices allow the microprocessor to
communicate between itself and the outside world. The I/O space allows the computer to access
up to 64K different 8-bit I/O devices, 32K different 16-bit devices, or 16K different 32-bit
devices. The 64-bit extensions support the same I/O space and I/O sizes as the 32-bit version and
does not add 64-bit I/O devices to the system. A great number of these locations are available for
expansion in most computer systems. Figure 1–11 shows the I/O map found in many personal
computer systems. To view the map on your computer in Windows, go to the Control Panel,
Performance and Maintenance, System, Hardware tab, Device Manager, View tab, then select
resources by type and click on the plus next to Input/Output (I/O).

24

CHAPTER 1

FIGURE 1–11 Some
I/O locations in a typical
personal computer.

The I/O area contains two major sections. The area below I/O location 0400H is considered reserved for system devices; many are depicted in Figure 1–11. The remaining area is
available I/O space for expansion that extends from I/O port 0400H through FFFFH. Generally,
I/O addresses between 0000H and 00FFH address components on the main board of the computer, while addresses between 0100H and 03FFH address devices located on plug-in cards (or
on the main board). Note that the limitation of I/O addresses between 0000 and 03FFH comes
from the original PC standard, as specified by IBM. When using the ISA bus, you must only use
addresses between 0000H and 03FFH. The PCI bus uses I/O address between 0400H and
FFFFH.
Various I/O devices that control the operation of the system are usually not directly addressed.
Instead, the system BIOS ROM addresses these basic devices, which can vary slightly in location
and function from one computer to the next. Access to most I/O devices should always be made

25

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

through Windows, DOS, or BIOS function calls to maintain compatibility from one computer
system to another. The map shown in Figure 1–11 is provided as a guide to illustrate the I/O space in
the system.

The Microprocessor
At the heart of the microprocessor-based computer system is the microprocessor integrated
circuit. The microprocessor, sometimes referred to as the CPU (central processing unit), is the
controlling element in a computer system. The microprocessor controls memory and I/O through
a series of connections called buses. The buses select an I/O or memory device, transfer data
between an I/O device or memory and the microprocessor, and control the I/O and memory
system. Memory and I/O are controlled through instructions that are stored in the memory and
executed by the microprocessor.
The microprocessor performs three main tasks for the computer system: (1) data transfer
between itself and the memory or I/O systems, (2) simple arithmetic and logic operations, and
(3) program flow via simple decisions. Although these are simple tasks, it is through them that
the microprocessor performs virtually any series of operations or tasks.
The power of the microprocessor is in its capability to execute billions of millions of
instructions per second from a program or software (group of instructions) stored in the memory system. This stored program concept has made the microprocessor and computer system
very powerful devices. (Recall that Babbage also wanted to use the stored program concept in his
Analytical Engine.)
Table 1–4 shows the arithmetic and logic operations executed by the Intel family of microprocessors. These operations are very basic, but through them, very complex problems are solved.
Data are operated upon from the memory system or internal registers. Data widths are variable
and include a byte (8 bits), word (16 bits), and doubleword (32 bits). Note that only the 80386
through the Core2 directly manipulate 8-, 16-, and 32-bit numbers. The earlier 8086–80286
directly manipulated 8- and 16-bit numbers, but not 32-bit numbers. Beginning with the 80486,
the microprocessor contained a numeric coprocessor that allowed it to perform complex arithmetic using floating-point arithmetic. The numeric coprocessor, which is similar to a calculator
chip, was an additional component in the 8086- through the 80386-based personal computer. The
numeric coprocessor is also capable of performing integer operations on quadwords (64 bits).
The MMX and SIMD units inside the Pentium through Core2 function with integers and floatingpoint number in parallel. The SIMD unit requires numbers stored as octalwords (128 bits).
Another feature that makes the microprocessor powerful is its ability to make simple
decisions based upon numerical facts. For example, a microprocessor can decide if a number is
zero, if it is positive, and so forth. These simple decisions allow the microprocessor to modify the

TABLE 1–4 Simple
arithmetic and logic
operations.

Operation
Addition
Subtraction
Multiplication
Division
AND
OR
NOT
NEG
Shift
Rotate

Comment

Logical multiplication
Logic addition
Logical inversion
Arithmetic inversion

26

CHAPTER 1

TABLE 1–5 Decisions
found in the 8086 through
Core2 microprocessors.

Decision

Comment

Zero

Test a number for zero or not-zero

Sign

Test a number for positive or negative

Carry

Test for a carry after addition or a borrow after
subtraction

Parity

Test a number for an even or an odd number of
ones

Overflow

Test for an overflow that indicates an invalid result
after a signed addition or a signed subtraction

program flow, so that programs appear to think through these simple decisions. Table 1–5 lists
the decision-making capabilities of the Intel family of microprocessors.

Buses. A bus is a common group of wires that interconnect components in a computer system.
The buses that interconnect the sections of a computer system transfer address, data, and control
information between the microprocessor and its memory and I/O systems. In the microprocessorbased computer system, three buses exist for this transfer of information: address, data, and control. Figure 1–12 shows how these buses interconnect various system components such as the
microprocessor, read/write memory (RAM), read-only memory (ROM or flash), and a few I/O
devices.
The address bus requests a memory location from the memory or an I/O location from the
I/O devices. If I/O is addressed, the address bus contains a 16-bit I/O address from 0000H
through FFFFH. The 16-bit I/O address, or port number, selects one of 64K different I/O devices.
If memory is addressed, the address bus contains a memory address, which varies in width with
the different versions of the microprocessor. The 8086 and 8088 address 1M byte of memory,
using a 20-bit address that selects locations 00000H–FFFFFH. The 80286 and 80386SX address
16M bytes of memory using a 24-bit address that selects locations 000000H–FFFFFFH. The
80386SL, 80386SLC, and 80386EX address 32M bytes of memory, using 25-bit address that
selects locations 0000000H–1FFFFFFH. The 80386DX, 80486SX, and 80486DX address

Address bus

μp

Data bus

MWTC
MRDC
IOWC
IORC

Read-only
memory
ROM

Read/write
memory
RAM

Keyboard

Printer

FIGURE 1–12 The block diagram of a computer system showing the address, data, and
control bus structure.

27

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

TABLE 1–6

The Intel family of microprocessor bus and memory sizes.

Microprocessor
8086
8088
80186
80188
80286
80386SX
80386DX
80386EX
80486
Pentium
Pentium Pro–Core2
Pentium Pro–Core2
(if extended addressing is enabled)
Pentium 4 and Core2
with 64-bit extensions enabled
Itanium

Data Bus Width

Address Bus Width

Memory Size

16
8
16
8
16
16
32
16
32
64
64
64

20
20
20
20
24
24
32
26
32
32
32
36

1M
1M
1M
1M
16M
16M
4G
64M
4G
4G
4G
64G

64

40

1T

128

40

1T

4G bytes of memory, using a 32-bit address that selects locations 00000000H–FFFFFFFFH. The
Pentium also addresses 4G bytes of memory, but it uses a 64-bit data bus to access up to 8 bytes
of memory at a time. The Pentium Pro through Core2 microprocessors have a 64-bit data bus and
a 32-bit address bus that address 4G of memory from location 00000000H–FFFFFFFFH, or a
36-bit address bus that addresses 64G of memory at locations 000000000H–FFFFFFFFFH,
depending on their configuration. Refer to Table 1–6 for complete listing of bus and memory
sizes of the Intel family of microprocessors.
The 64-bit extensions to the Pentium family provide 40 address pins in its current version
that allow up to 1T byte of memory to be accessed through its 10 digit hexadecimal address.
Note that 240 is 1 terra. In future editions of the 64-bit microprocessors Intel plans to expand the
number of address bits to 52, and ultimately to 64 bits. A 52-bit address bus allows 4P (Peta)
bytes of memory to be accessed and a 64-bit address bus allows 16E (Exa) bytes of memory.
The data bus transfers information between the microprocessor and its memory and I/O
address space. Data transfers vary in size, from 8 bits wide to 64 bits wide in various members of
the Intel microprocessor family. For example, the 8088 has an 8-bit data bus that transfers 8 bits
of data at a time. The 8086, 80286, 80386SL, 80386SX, and 80386EX transfer 16 bits of data
through their data buses; the 80386DX, 80486SX, and 80486DX transfer 32 bits of data; and the
Pentium through Core2 microprocessors transfer 64 bits of data. The advantage of a wider data
bus is speed in applications that use wide data. For example, if a 32-bit number is stored in memory, it takes the 8088 microprocessor four transfer operations to complete because its data bus
is only 8 bits wide. The 80486 accomplishes the same task with one transfer because its data
bus is 32 bits wide. Figure 1–13 shows the memory widths and sizes of the 8086–80486 and
Pentium through Core2 microprocessors. Notice how the memory sizes and organizations differ
between various members of the Intel microprocessor family. In all family members, the memory is numbered by byte. Notice that the Pentium through Core2 microprocessors all contain a
64-bit-wide data bus.
The control bus contains lines that select the memory or I/O and cause them to perform a
read or write operation. In most computer systems, there are four control bus connections: MRDC
(memory read control), MWTC (memory write control), IORC (I/O read control), and IOWC
(I/O write control). Note that the overbar indicates that the control signal is active-low; that is,

28

CHAPTER 1

it is active when a logic zero appears on the control line. For example, if IOWC = 0, the
microprocessor is writing data from the data bus to an I/O device whose address appears on
the address bus. Note that these control signal names are slightly different in various versions of
the microprocessor.
The microprocessor reads the contents of a memory location by sending the memory an
address through the address bus. Next, it sends the memory read control signal (MRDC) to cause
the memory to read data. Finally, the data read from the memory are passed to the microprocessor through the data bus. Whenever a memory write, I/O write, or I/O read occurs, the same
sequence ensues, except that different control signals are issued and the data flow out of the
microprocessor through its data bus for a write operation.

Low bank
(Even bank)

High bank
(Odd bank)

FFFFF

FFFFFF

FFFFFE

FFFFE

FFFFFD

FFFFFC

FFFFD

FFFFFB

FFFFFA

8 bits

8 bits

8 bits

1M byte

8M bytes

8M bytes

00002

000005

000004

00001

000003

000002

00000

000001
D7–D0
8088 microprocessor

000000
D15–D8

D7–D0

8086 microprocessor (memory is only 1M bytes)
80286 microprocessor
80386SX microprocessor
80386SL microprocessor (memory is 32M bytes)
80386SLC microprocessor (memory is 32M bytes)

Bank 3

Bank 2

Bank 1

Bank 0

FFFFFFFF

FFFFFFFE

FFFFFFFD

FFFFFFFC

FFFFFFFB

FFFFFFFA

FFFFFFF9

FFFFFFF8

FFFFFFF7

FFFFFFF6

FFFFFFF5

FFFFFFF4

8 bits

8 bits

8 bits

8 bits

1G byte

1G byte

1G byte

1G byte

0000000B

0000000A

00000009

00000008

00000007

00000006

00000005

00000004

00000003

00000002

00000001

D31–D24

D23–D16

00000000
D15–D8

80386DX microprocessor
80486SX microprocessor
80486DX microprocessor

FIGURE 1–13

The physical memory systems of the 8086 through the Core2 microprocessors.

D7–D0

29

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER
Bank 7

Bank 6

FFFFFFFF

FFFFFFFE

Bank 5
FFFFFFFD

Bank 4
FFFFFFFC

FFFFFFF7

FFFFFFF6

FFFFFFF5

FFFFFFF4

FFFFFFEF

FFFFFFEE

FFFFFFED

FFFFFFEC

8 bits

8 bits

8 bits

8 bits

512M bytes

512M bytes

512M bytes

512M bytes

00000017

00000016

00000015

00000014

0000000F

0000000E

0000000D

0000000C

00000007

00000006

00000005

00000004

D63–D56

D55–D48

Bank 3

D47–D40

Bank 2

FFFFFFFB

FFFFFFFA

D39–D32

Bank 1
FFFFFFF9

Bank 0
FFFFFFF8

FFFFFFF3

FFFFFFF2

FFFFFFF1

FFFFFFF0

FFFFFFEB

FFFFFFEA

FFFFFFE9

FFFFFFE8

8 bits

8 bits

8 bits

8 bits

512M bytes

512M bytes

512M bytes

1G byte

00000013

00000012

00000011

00000010

0000000B

0000000A

00000009

00000008

00000003

00000002

00000001

D31–D24

D23–D16

00000000
D15–D8

D7–D0

Pentium–Core2 microprocessors

FIGURE 1–13

1–3

(continued)

NUMBER SYSTEMS
The use of the microprocessor requires a working knowledge of binary, decimal, and hexadecimal numbering systems. This section of the text provides a background for those who are unfamiliar with these numbering systems. Conversions between decimal and binary, decimal and
hexadecimal, and binary and hexadecimal are described.

Digits
Before numbers are converted from one number base to another, the digits of a number system
must be understood. Early in our education, we learned that a decimal (base 10) number is
constructed with 10 digits: 0 through 9. The first digit in any numbering system is always zero.
For example, a base 8 (octal) number contains 8 digits: 0 through 7; a base 2 (binary) number
contains 2 digits: 0 and 1. If the base of a number exceeds 10, the additional digits use the

30

CHAPTER 1

letters of the alphabet, beginning with an A. For example, a base 12 number contains 10 digits:
0 through 9, followed by A for 10 and B for 11. Note that a base 10 number does contain a
10 digit, just as a base 8 number does not contain an 8 digit. The most common numbering
systems used with computers are decimal, binary, and hexadecimal (base 16). (Many years ago
octal numbers were popular.) Each of these number systems are described and used in this
section the chapter.

Positional Notation
Once the digits of a number system are understood, larger numbers are constructed by using
positional notation. In grade school, we learned that the position to the left of the units position
is the tens position, the position to the left of the tens position is the hundreds position, and so
forth. (An example is the decimal number 132: This number has 1 hundred, 3 tens, and 2 units.)
What probably was not learned was the exponential value of each position: The units position
has a weight of 100, or 1; the tens position has weight of 101, or 10; and the hundreds position has
a weight of 102, or 100. The exponential powers of the positions are critical for understanding
numbers in other numbering systems. The position to the left of the radix (number base) point,
called a decimal point only in the decimal system, is always the units position in any number system. For example, the position to the left of the binary point is always 20, or 1; the position to the
left of the octal point is 80, or 1. In any case, any number raised to its zero power is always 1, or
the units position.
The position to the left of the units position is always the number base raised to the first
power; in a decimal system, this is 101, or 10. In a binary system, it is 21, or 2; and in an octal
system, it is 8l, or 8. Therefore, an 11 decimal has a different value from an 11 binary. The decimal number is composed of 1 ten plus 1 unit, and has a value of 11 units; while the binary
number 11 is composed of 1 two plus 1 unit, for a value of 3 decimal units. The 11 octal has a
value of 9 decimal units.
In the decimal system, positions to the right of the decimal point have negative powers.
The first digit to the right of the decimal point has a value of 10-1, or 0.1. In the binary system the
first digit to the right of the binary point has a value of 2-1, or 0.5. In general, the principles that
apply to decimal numbers also apply to numbers in any other number system.
Example 1–1 shows 110.101 in binary (often written as 110.1012). It also shows the
power and weight or value of each digit position. To convert a binary number to decimal, add
weights of each digit to form its decimal equivalent. The 110.1012 is equivalent to a 6.625 in
decimal (4 + 2 + 0.5 + 0.125). Notice that this is the sum of 22 (or 4) plus 21 (or 2), but 20
(or 1) is not added because there are no digits under this position. The fraction part is composed of 2-1 (.5) plus 2-3 (or .125), but there is no digit under the 2-2 (or .25) so .25 is not
added.
EXAMPLE 1–1
Power
Weight
Number
Numeric Value

22
4
1
4 +

21
2
1
2 +

20
1
0 .
0 +

2-1
.5
1
.5 +

2-2
.25
0
0 +

2-3
.125
1
.125

=

6.625

Suppose that the conversion technique is applied to a base 6 number, such as 25.26.
Example 1–2 shows this number placed under the powers and weights of each position. In the
example, there is a 2 under 61, which has a value of 1210 (2 * 6), and a 5 under 60, which has
a value of 5 (5 * 1). The whole number portion has a decimal value of 12 + 5, or 17. The number to the right of the hex point is a 2 under 6-1, which has a value of .333 (2 * .167). The
number 25.26, therefore, has a value of 17.333.

31

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

EXAMPLE 1–2
Power
Weight
Number
Numeric Value

61
6
2
12 +

60
1
5
5 +

6-1
.167
.2
.333 = 17.333

Conversion to Decimal
The prior examples have shown that to convert from any number base to decimal, determine the
weights or values of each position of the number, and then sum the weights to form the decimal
equivalent. Suppose that a 125.78 octal is converted to decimal. To accomplish this conversion,
first write down the weights of each position of the number. This appears in Example 1–3. The
value of 125.78 is 85.875 decimal, or 1 * 64 plus 2 * 8 plus 5 * 1 plus 7 * .125.

EXAMPLE 1–3
Power
Weight
Number
Numeric Value

82
64
1
64 +

81
8
2
16 +

80
1
5
5 +

8-1
.125
.7
.875 = 85.875

Notice that the weight of the position to the left of the units position is 8. This is 8 times 1.
Then notice that the weight of the next position is 64, or 8 times 8. If another position existed, it
would be 64 times 8, or 512. To find the weight of the next higher-order position, multiply the
weight of the current position by the number base (or 8, in this example). To calculate the
weights of position to the right of the radix point, divide by the number base. In the octal system,
the position immediately to the right of the octal point is 1/8, or .125. The next position is .125/8,
or .015625, which can also be written as 1/64. Also note that the number in Example 1–3 can also
be written as the decimal number 857/8.
Example 1–4 shows the binary number 11011.0111 written with the weights and powers of
each position. If these weights are summed, the value of the binary number converted to decimal
is 27.4375.

EXAMPLE 1–4
Power
Weight
Number
Numeric Value

24
16
1
16 +

23
8
1
8 +

22
4
0
0 +

21
2
1
2 +

20
1
1 .
1 +

2-1
.5
0
0 +

2-2
.25
1
.25 +

2-3
.125
1
.125 +

2-4
.0625
1
.0625 = 27.4375

It is interesting to note that 2-1 is also 1/2, 2-2 is 1/4, and so forth. It is also interesting to
note that 2-4 is 1/16, or .0625. The fractional part of this number is 7/16 or .4375 decimal. Notice
that 0111 is a 7 in binary code for the numerator and the rightmost one is in the 1/16 position for
the denominator. Other examples: The binary fraction of .101 is 5/8 and the binary fraction of
.001101 is 13/64.
Hexadecimal numbers are often used with computers. A 6A.CH (H for hexadecimal) is
illustrated with its weights in Example 1–5. The sum of its digits is 106.75, or 1063⁄4. The whole
number part is represented with 6 * 16 plus 10 1A2 * 1. The fraction part is 12 (C) as a numerator and 16 (16-1) as the denominator, or 12/16, which is reduced to 3/4.

32

CHAPTER 1

EXAMPLE 1–5
161
16
6
96 +

Power
Weight
Number
Number Value

160
1
A .
10 +

16-1
.0625
C
.75 = 106.75

Conversion from Decimal
Conversions from decimal to other number systems are more difficult to accomplish than conversion to decimal. To convert the whole number portion of a number to decimal, divide by 1
radix. To convert the fractional portion, multiply by the radix.

Whole Number Conversion from Decimal. To convert a decimal whole number to another
number system, divide by the radix and save the remainders as significant digits of the result. An
algorithm for this conversion as is follows:
1. Divide the decimal number by the radix (number base).
2. Save the remainder (first remainder is the least significant digit).
3. Repeat steps 1 and 2 until the quotient is zero.
For example, to convert a 10 decimal to binary, divide it by 2. The result is 5, with a remainder of 0. The first remainder is the units position of the result (in this example, a 0). Next divide
the 5 by 2. The result is 2, with a remainder of 1. The 1 is the value of the twos (21) position.
Continue the division until the quotient is a zero. Example 1–6 shows this conversion process. The
result is written as 10102 from the bottom to the top.
EXAMPLE 1–6
2) 10
2) 5
2) 2
2) 1
0

remainder
remainder
remainder
remainder

=
=
=
=

0
1
0
1

result = 1010

To convert a 10 decimal into base 8, divide by 8, as shown in Example 1–7. A 10 decimal
is a 12 octal.
EXAMPLE 1–7
8) 10
8) 1
0

remainder = 2
remainder = 1

result = 12

Conversion from decimal to hexadecimal is accomplished by dividing by 16. The remainders will range in value from 0 through 15. Any remainder of 10 through 15 is then converted to
the letters A through F for the hexadecimal number. Example 1–8 shows the decimal number 109
converted to a 6DH.
EXAMPLE 1–8
16) 109
16) 6
0

remainder = 13 (D)
remainder = 6

result = 6D

Converting from a Decimal Fraction. Conversion from a decimal fraction to another number
base is accomplished with multiplication by the radix. For example, to convert a decimal fraction
into binary, multiply by 2. After the multiplication, the whole number portion of the result is

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

33

saved as a significant digit of the result, and the fractional remainder is again multiplied by the
radix. When the fraction remainder is zero, multiplication ends. Note that some numbers are
never-ending (repetend). That is, a zero is never a remainder. An algorithm for conversion from
a decimal fraction is as follows:
1. Multiply the decimal fraction by the radix (number base).
2. Save the whole number portion of the result (even if zero) as a digit. Note that the first result
is written immediately to the right of the radix point.
3. Repeat steps 1 and 2, using the fractional part of step 2 until the fractional part of step 2 is zero.
Suppose that a .125 decimal is converted to binary. This is accomplished with multiplications by 2, as illustrated in Example 1–9. Notice that the multiplication continues until the
fractional remainder is zero. The whole number portions are written as the binary fraction
(0.001) in this example.
EXAMPLE 1–9
x

x

x

.125
2
0.25

digit is 0

.25
2
0.5

digit is 0

.5
2
1.0

digit is 1

result = 0.0012

This same technique is used to convert a decimal fraction into any number base. Example 1–10
shows the same decimal fraction of .125 from Example 1–9 converted to octal by multiplying by 8.
EXAMPLE 1–10
x

.125
8
1.0

digit is 1

result = 0.18

Conversion to a hexadecimal fraction appears in Example 1–11. Here, the decimal .046875
is converted to hexadecimal by multiplying by 16. Note that .046875 is 0.0CH.
EXAMPLE 1–11
x

.046875
16
0.75

.75
x
16
12.0

digit is 0

digit is 12(C)

result = 0.0C16

Binary-Coded Hexadecimal
Binary-coded hexadecimal (BCH) is used to represent hexadecimal data in binary code.
A binary-coded hexadecimal number is a hexadecimal number written so that each digit is represented by a 4-bit binary number. The values for the BCH digits appear in Table 1–7.
Hexadecimal numbers are represented in BCH code by converting each digit to BCH code
with a space between each coded digit. Example 1–12 shows a 2AC converted to BCH code.
Note that each BCH digit is separated by a space.

34

CHAPTER 1

TABLE 1–7 Binary-coded
hexadecimal (BCH) code.

Hexadecimal Digit

BCH Code

0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

EXAMPLE 1–12
2AC = 0010 1010 1100

The purpose of BCH code is to allow a binary version of a hexadecimal number to be
written in a form that can easily be converted between BCH and hexadecimal. Example 1–13
shows a BCH coded number converted back to hexadecimal code.
EXAMPLE 1–13
1000 0011 1101 . 1110 = 83D.E

Complements
At times, data are stored in complement form to represent negative numbers. There are two systems
that are used to represent negative data: radix and radix - 1 complements. The earliest system was
the radix -1 complement, in which each digit of the number is subtracted from the radix -1 to generate the radix -1 complement to represent a negative number.
Example 1–14 shows how the 8-bit binary number 01001100 is one’s (radix -1) complemented to represent it as a negative value. Notice that each digit of the number is subtracted
from one to generate the radix -1 (one’s) complement. In this example, the negative of
01001100 is 10110011. The same technique can be applied to any number system, as illustrated
in Example 1–15, in which the fifteen’s (radix -1) complement of a 5CD hexadecimal is computed by subtracting each digit from a fifteen.
EXAMPLE 1–14
1111 1111
- 0100 1100
1011 0011

EXAMPLE 1–15
-

15 15 15
5 C D
A 3 2

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

35

Today, the radix -1 complement is not used by itself; it is used as a step for finding the
radix complement. The radix complement is used to represent negative numbers in modem computer systems. (The radix -1 complement was used in the early days of computer technology.)
The main problem with the radix -1 complement is that a negative or a positive zero exists; in the
radix complement system, only a positive zero can exist.
To form the radix complement, first find the radix -1 complement, and then add a one to
the result. Example 1–16 shows how the number 0100 1000 is converted to a negative value by
two’s (radix) complementing it.
EXAMPLE 1–16
1111
- 0100
1011
+
1011

1111
1000
0111
1
1000

(one’s complement)
(two’s complement)

To prove that a 0100 1000 is the inverse (negative) of a 1011 1000, add the two together to
form an 8-digit result. The ninth digit is dropped and the result is zero because a 0100 1000 is a
positive 72, while a 1011 1000 is a negative 72. The same technique applies to any number system.
Example 1–17 shows how the inverse of a 345 hexadecimal is found by first fifteen’s complementing the number, and then by adding one to the result to form the sixteen’s complement. As before,
if the original 3-digit number 345 is added to the inverse of CBB, the result is a 3-digit 000.
As before, the fourth bit (carry) is dropped. This proves that 345 is the inverse of CBB. Additional
information about one’s and two’s complements is presented with signed numbers in the next
section of the text.
EXAMPLE 1–17
15 15 15
3 4 5
C B A
+
1
C B B

-

1–4

(fifteen’s complement)
(sixteen’s complement)

COMPUTER DATA FORMATS
Successful programming requires a precise understanding of data formats. In this section, many
common computer data formats are described as they are used with the Intel family of microprocessors. Commonly, data appear as ASCII, Unicode, BCD, signed and unsigned integers, and
floating-point numbers (real numbers). Other forms are available, but are not presented here
because they are not commonly found.

ASCII and Unicode Data
ASCII (American Standard Code for Information Interchange) data represent alphanumeric
characters in the memory of a computer system (see Table 1–8). The standard ASCII code is a 7-bit
code, with the eighth and most significant bit used to hold parity in some antiquated systems.
If ASCII data are used with a printer, the most significant bits are a 0 for alphanumeric printing and
1 for graphics printing. In the personal computer, an extended ASCII character set is selected by
placing a 1 in the leftmost bit. Table 1–9 shows the extended ASCII character set, using code
80H–FFH. The extended ASCII characters store some foreign letters and punctuation, Greek

36

CHAPTER 1

TABLE 1–8

ASCII code.

Second

First
0X
1X
2X
3X
4X
5X
6X
7X

X0

X1

X2

X3

X4

X5

X6

X7

X8

X9

XA

XB

XC

XD

XE

XF

NUL
DLE
SP
0
@
P

p

SOH
DC1
!
1
A
Q
a
q

STX
DC2

2
B
R
b
r

ETX
DC3
#
3
C
S
c
s

EOT
DC4
$
4
D
T
d
t

ENQ
NAK
%
5
E
U
e
u

ACK
SYN
&
6
F
V
f
v

BEL
ETB

7
G
W
g
w

BS
CAN
(
8
H
X
h
x

HT
EMS
)
9
I
Y
i
y

LF
SUB
*
:
J
Z
j
z

VT
ESC
+
;
K
[
k
{

FF
FS
,
<
L
\
l
|

CR
GS
=
M
]
m
}

SO
RS
.
>
N
^
n
~

SI
US
/
?
O
_
o
...
...
...

TABLE 1–9

Extended ASCII code, as printed by the IBM ProPrinter.

characters, mathematical characters, box-drawing characters, and other special characters. Note
that extended characters can vary from one printer to another. The list provided is designed to be
used with the IBM ProPrinter, which also matches the special character set found with most word
processors.
The ASCII control characters, also listed in Table 1–8, perform control functions in a computer system, including clear screen, backspace, line feed, and so on. To enter the control codes
through the computer keyboard, hold down the Control key while typing a letter. To obtain the
control code 01H, type a Control-A; a 02H is obtained by a Control-B, and so on. Note that the
control codes appear on the screen, from the DOS prompt, as ^A for Control-A, ^B for Control-B,
and so forth. Also note that the carriage return code (CR) is the Enter key on most modem keyboards. The purpose of CR is to return the cursor or print head to the left margin. Another code
that appears in many programs is the line feed code (LF), which moves the cursor down one line.
To use Table 1–8 or 1–9 for converting alphanumeric or control characters into ASCII
characters, first locate the alphanumeric code for conversion. Next, find the first digit of the
hexadecimal ASCII code. Then find the second digit. For example, the capital letter “A” is
ASCII code 41H, and the lowercase letter “a” is ASCII code 61H. Many Windows-based applications, since Windows 95, use the Unicode system to store alphanumeric data. This system

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

37

stores each character as 16-bit data. The codes 0000H–00FFH are the same as standard ASCII
code. The remaining codes, 0100H–FFFFH, are used to store all special characters from
many worldwide character sets. This allows software written for the Windows environment
to be used in many countries around the world. For complete information on Unicode, visit
http://www.unicode.org.
ASCII data are most often stored in memory by using a special directive to the assembler
program called define byte(s), or DB. (The assembler is a program that is used to program a
computer in its native binary machine language.) An alternative to DB is the word BYTE. The
DB and BYTE directives, and several examples of their usage with ASCII-coded character
strings, are listed in Example 1–18. Notice how each character string is surrounded by apostrophes (’)—never use the quote (”). Also notice that the assembler lists the ASCII-coded value for
each character to the left of the character string. To the far left is the hexadecimal memory location where the character string is first stored in the memory system. For example, the character
string WHAT is stored beginning at memory address 001D, and the first letter is stored as
57 (W), followed by 68 (H), and so forth. Example 1–19 shows the same three strings defined as
String^ character strings for use with Visual C++ Express 2005 and 2008. Note that Visual C++
uses quotes to surround strings. If an earlier version of C++ is used, then the string is defined
with a CString for Microsoft Visual C++ instead of a String^. The ^ symbol indicates that String
is a member of the garbage collection heap for managing the storage. A garbage collector cleans
off the memory system (frees unused memory) when the object falls from visibility or scope in a
C++ program and it also prevents memory leaks.
EXAMPLE 1–18
0000

OOOD

001D

42
20
72
57
20
69
3F
57
69
20
74

61
42
65
68
63
74

72
2E
79
65
61
20

72 79
20 42

NAMES

DB

‘Barry B. Brey’

20 63
6E 20
62 65

MESS

DB

‘Where can it be?’

69 20 74 20
73 20 6F 6E
66 69 72 73
2E

WHAT

DB

‘What is on first.’

EXAMPLE 1–19
String^ NAMES = “Barry B. Brey”

// C++ Express version

String^ MESS = “Where can it be?”
String^ WHAT = “What is on first.”

BCD (Binary-Coded Decimal) Data
Binary-coded decimal (BCD) information is stored in either packed or unpacked forms. Packed
BCD data are stored as two digits per byte and unpacked BCD data are stored as one digit per
byte. The range of a BCD digit extends from 00002 to 10012, or 0–9 decimal. Unpacked BCD
data are returned from a keypad or keyboard. Packed BCD data are used for some of the instructions included for BCD addition and subtraction in the instruction set of the microprocessor.
Table 1–10 shows some decimal numbers converted to both the packed and unpacked BCD
forms. Applications that require BCD data are point-of-sales terminals and almost any device
that performs a minimal amount of simple arithmetic. If a system requires complex arithmetic,
BCD data are seldom used because there is no simple and efficient method of performing
complex BCD arithmetic.

38

CHAPTER 1

TABLE 1–10

Packed and unpacked BCD data.

Decimal
12
623
910

Packed
0001 0010
0000 0110
0000 1001

0010 0011
0001 0000

Unpacked
0000 0001
0000 0110
0000 1001

0000 0010
0000 0010
0000 0001

0000 0011
0000 0000

Example 1–20 shows how to use the assembler to define both packed and unpacked
BCD data. Example 1–21 shows how to do this using Visual C++ and char or bytes. In all
cases, the convention of storing the least-significant data first is followed. This means that to
store 83 into memory, the 3 is stored first, and then followed by the 8. Also note that with
packed BCD data, the letter H (hexadecimal) follows the number to ensure that the assembler stores the BCD value rather than a decimal value for packed BCD data. Notice how the
numbers are stored in memory as unpacked, one digit per byte; or packed, as two digits
per byte.
EXAMPLE 1–20

0000 03 04 05
0003 07 08

0005 37 34
0007 03 45

;Unpacked BCD data (least-significant data first)
;
NUMB1 DB
3,4,5
;defines number 543
NUMB2 DB
7,8
;defines number 87
;
;Packed BCD data (least-significant data first)
;
NUMB3 DB
37H,34H
;defines number 3437
NUMB4 DB
3,45H
;defines number 4503

EXAMPLE 1–21
//Unpacked BCD data (least-significant data first)
//
char Numb1 = 3,4,5;
;defines number 543
char Numb2 = 7,8
;defines number 87
//
//Packed BCD data (least-significant data first)
//
char Numb3 = 0x37,0x34
;defines number 3437
char Numb4 = 3,0x45
;defines number 4503

Byte-Sized Data
Byte-sized data are stored as unsigned and signed integers. Figure 1–14 illustrates both the
unsigned and signed forms of the byte-sized integer. The difference in these forms is the weight
of the leftmost bit position. Its value is 128 for the unsigned integer and minus 128 for the
signed integer. In the signed integer format, the leftmost bit represents the sign bit of the number, as well as a weight of minus 128. For example, 80H represents a value of 128 as
an unsigned number; as a signed number, it represents a value of minus 128. Unsigned integers
range in value from 00H to FFH (0–255). Signed integers range in value from -128 to
0 to + 127.
Although negative signed numbers are represented in this way, they are stored in the two’s
complement form. The method of evaluating a signed number by using the weights of each bit
position is much easier than the act of two’s complementing a number to find its value. This is
especially true in the world of calculators designed for programmers.

39

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER
128

64

32

16

8

4

2

1

Binary weights

4

2

1

Binary weights

Unsigned byte

-128

64

32

16

8

Signed byte

FIGURE 1–14

The unsigned and signed bytes illustrating the weights of each binary-bit position.

Whenever a number is two’s complemented, its sign changes from negative to positive or
positive to negative. For example, the number 00001000 is a +8. Its negative value (-8) is found
by two’s complementing the +8. To form a two’s complement, first one’s complement the
number. To one’s complement a number, invert each bit of a number from zero to one or from
one to zero. Once the one’s complement is formed, the two’s complement is found by adding a
one to the one’s complement. Example 1–22 shows how numbers are two’s complemented using
this technique.
EXAMPLE 1–22
+ 8 = 00001000
11110111
+
1
- 8 = 11111000

(one’s complement)
(two’s complement)

Another, and probably simpler, technique for two’s complementing a number starts with
the rightmost digit. Start by writing down the number from right to left. Write the number
exactly as it appears until the first one. Write down the first one, and then invert all bits to its left.
Example 1–23 shows this technique with the same number as in Example 1–22.
EXAMPLE 1–23
+8 = 00001000
1000 (write number to first 1)
1111
(invert the remaining bits)
-8 = 11111000

To store 8-bit data in memory using the assembler program, use the DB directive as in
prior examples or char as in Visual C++ examples. Example 1–24 lists many forms of 8-bit numbers stored in memory using the assembler program. Notice in the example that a hexadecimal
number is defined with the letter H following the number, and that a decimal number is written
as is, without anything special. Example 1–25 shows the same byte data defined for use with a
Visual C++ program. In C/C++ the hexadecimal value is preceded by a 0x to indicate a hexadecimal value.

40

CHAPTER 1

EXAMPLE 1–24

0000
0001
0002

FE
87
47

0003
0004
0005
0006

9C
64
FF
38

;Unsigned byte-sized data
;
DATA1 DB
254
;define 254 decimal
DATA2 DB
87H
;define 87 hexadecimal
DATA3 DB
71
;define 71 decimal
;
;Signed byte-sized data
;
DATA4 DB
-100 ;define -100 decimal
DARA5 DB
+100 ;define +100 decimal
DATA6 DB
-1
;define -1 decimal
DATA7 DB
56
;define 56 decimal

EXAMPLE 1–25
//Unsigned byte-sized data
//
unsigned char Data1 = 254;
unsigned char Data2 = 0x87;
unsigned char Data3 = 71
//
//Signed byte-sized data
//
char Data4 = -100;
char Data5 = +100;
char Data6 = -1;
char Data7 = 56;

//define 254 decimal
//define 87 hexadecimal
//define 71 decimal

//define
//define
//define
//define

-100 decimal
+100 decimal
-1 decimal
56 decimal

Word-Sized Data
A word (16-bits) is formed with two bytes of data. The least significant byte is always stored in the
lowest-numbered memory location, and the most significant byte is stored in the highest. This
method of storing a number is called the little endian format. An alternate method, not used with
the Intel family of microprocessors, is called the big endian format. In the big endian format,
numbers are stored with the lowest location containing the most significant data. The big endian
format is used with the Motorola family of microprocessors. Figure 1–15 (a) shows the weights of
each bit position in a word of data, and Figure 1–15 (b) shows how the number 1234H appears
when stored in the memory locations 3000H and 3001H. The only difference between a signed
and an unsigned word in the leftmost bit is position. In the unsigned form, the leftmost bit is
unsigned and has a weight of 32,768; in the signed form, its weight is -32,768. As with bytesized signed data, the signed word is in two’s complement form when representing a negative
number. Also, notice that the low-order byte is stored in the lowest-numbered memory location
(3000H) and the high-order byte is stored in the highest-numbered location (3001H).
Example 1–26 shows several signed and unsigned word-sized data stored in memory using
the assembler program. Example 1–27 shows how to store the same numbers in a Visual C++
EXAMPLE 1–26

0000
0002
0004

09F0
87AC
02C6

0006
0008
000A

CBA8
00C6
FFFF

;Unsigned word-sized data
;
DATA1 DW
2544
;define
DATA2 DW
87ACH
;define
DATA3 DW
710
;define
;
;Signed word-sized data
;
DATA4 DW
-13400
;define
DATA5 DW
+198
;define
DATA6 DW
-1
;define

2544 decimal
87AC hexadecimal
710 decimal

-13400 decimal
+198 decimal
-1 decimal

41
1

2

4

8

16

32

64

128

256

512

1024

2048

4096

8192

16,384

32,768

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

Binary weights

(a) Unsigned word

3003H
3002H
3001H

12H

High-order byte

3000H

34H

Low-order byte

2FFFH

(b) The contents of memory location 3000H and 3001H are the word 1234H.

FIGURE 1–15

The storage format for a 16-bit word in (a) a register and (b) two bytes of memory.
EXAMPLE 1–27
//Unsigned word-sized data
//
unsigned short Data1 = 2544;
unsigned short Data2 = 0x87AC
unsigned short Data3 = 710;
//
//Signed word-sized data
//
short Data4 = -13400;
short Data5 = +198;
short Data6 = -1;

//define 2544 decimal
//define 87AC hexadecimal
//define 710 decimal

//define -13400 decimal
//define +198 decimal
//define -1 decimal

program (assuming version 5.0 or newer), which uses the short directive to store a 16-bit integer.
Notice that the define word(s) directive, or DW, causes the assembler to store words in the
memory instead of bytes, as in prior examples. The WORD directive can also be used to define a
word. Notice that the word data is displayed by the assembler in the same form as entered. For
example, a l000H is displayed by the assembler as a 1000. This is for our convenience because
the number is actually stored in the memory as 00 l0 in two consecutive memory bytes.

Doubleword-Sized Data
Doubleword-sized data requires four bytes of memory because it is a 32-bit number. Doubleword
data appear as a product after a multiplication and also as a dividend before a division. In the 80386
through the Core2, memory and registers are also 32 bits in width. Figure 1–16 shows the form
used to store doublewords in the memory and the binary weights of each bit position.
When a doubleword is stored in memory, its least significant byte is stored in the lowest
numbered memory location, and its most significant byte is stored in the highest-numbered

42

1

2

4

8

16

32

64

128

256

512

1024

2048

4096

8192

16,384

32,768

65,536

131,072

262,144

524,288

1,048,576

2,097,152

4,194,304

8,388,608

16,777,216

33,554,432

67,108,864

134,217,728

268,435,456

536,870,912

1,073,741,824

2,147,438,648

CHAPTER 1

Binary weights

(a) Unsigned doubleword

00103H
00102H

12H

00101H

34H
56H

00100H

78H

High-order byte

Low-order byte

000FFH

(b) The contents of memory location 00100H–00103H are the doubleword 12345678H.

FIGURE 1–16

The storage format for a 32-bit word in (a) a register and (b) 4 bytes of memory.

memory location using the little endian format. Recall that this is also true for word-sized data.
For example, 12345678H that is stored in memory locations 00100H–00103H is stored with the
78H in memory location 00100H, the 56H in location 00101H, the 34H in location 00102H, and
the 12H in location 00103H.
To define doubleword-sized data, use the assembler directive define doubleword(s), or
DD. (You can also use the DWORD directive in place of DD.) Example 1–28 shows both signed
and unsigned numbers stored in memory using the DD directive. Example 1–29 shows how to
define the same doublewords in Visual C++ using the int directive.
EXAMPLE 1–28

0000
0004
0008

0003E1C0
87AC1234
00000046

000C FFEB8058
0010 000000C6
0014 FFFFFFFF

;Unsigned doubleword-sized data
;
DATA1 DD
254400
;define
DATA2 DD
87AC1234H
;define
DATA3 DD
70
;define
;
;Signed doubleword-sized data
;
DATA4 DD
-1343400
;define
DATA5 DD
+198
;define
DATA6 DD
-1
;define

254400 decimal
87AC1234 hexadecimal
70 decimal

-1343400 decimal
+198 decimal
-1 decimal

EXAMPLE 1–29
//Unsigned doubleword-sized data
//
unsigned int Data1 = 254400;
unsigned int Data2 = 0x87AC1234;
unsigned int Data3 = 70;
//
//Signed doubleword-sized data
//
int Data4 = -1343400;
int Data5 = +198;
int Data6 = -1;

//define 254400 decimal
//define 87AC1234 hexadecimal
//define 70 decimal

//define -1342400 decimal
//define +198 decimal
//define -1 decimal

43

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

Integers may also be stored in memory that is of any width. The forms listed here are
standard forms, but that doesn’t mean that a 256-byte wide integer can’t be stored in the memory.
The microprocessor is flexible enough to allow any size of data in assembly language. When nonstandard-width numbers are stored in memory, the DB directive is normally used to store them. For
example, the 24-bit number 123456H is stored using a DB 56H, 34H, 12H directive. Note that this
conforms to the little endian format. This could also be done in Visual C++ using the char directive.

Real Numbers
Because many high-level languages use the Intel family of microprocessors, real numbers are
often encountered. A real number, or a floating-point number, as it is often called, contains two
parts: a mantissa, significand, or fraction; and an exponent. Figure 1–17 depicts both the 4- and
8-byte forms of real numbers as they are stored in any Intel system. Note that the 4-byte number
is called single-precision and the 8-byte form is called double-precision. The form presented
here is the same form specified by the IEEE10 standard, IEEE-754, version 10.0. The standard
has been adopted as the standard form of real numbers with virtually all programming languages
and many applications packages. The standard also applies the data manipulated by the numeric
coprocessor in the personal computer. Figure 1–17 (a) shows the single-precision form that
contains a sign-bit, an 8-bit exponent, and a 24-bit fraction (mantissa). Note that because applications often require double-precision floating-point numbers [see Figure 1–17 (b)], the
Pentium–Core2 with their 64-bit data bus perform memory transfers at twice the speed of the
80386/80486 microprocessors.
Simple arithmetic indicates that it should take 33 bits to store all three pieces of data. Not
true—the 24-bit mantissa contains an implied (hidden) one-bit that allows the mantissa to represent 24 bits while being stored in only 23 bits. The hidden bit is the first bit of the normalized real
number. When normalizing a number, it is adjusted so that its value is at least 1, but less than 2.
For example, if 12 is converted to binary (11002), it is normalized and the result is 1.1 * 23. The
whole number 1 is not stored in the 23-bit mantissa portion of the number; the 1 is the hidden
one-bit. Table 1–11 shows the single-precision form of this number and others.
The exponent is stored as a biased exponent. With the single-precision form of the real
number, the bias is 127 (7FH) and with the double-precision form, it is 1023 (3FFH). The bias

31

30

S

23 22

0

Exponent

Significand
(a)

63

62

S

52 51
Exponent

0
Significand

(b)

FIGURE 1–17 The floating-point numbers in (a) single-precision using a bias of 7FH and
(b) double-precision using a bias of 3FFH.

10IEEE

is the Institute of Electrical and Electronic Engineers.

44

CHAPTER 1

TABLE 1–11

Decimal
+12
–12
+100
–1.75
+0.25
+0.0

Single-precision real numbers.

Binary

Normalized

1100
1100
1100100
1.11
0.01
0

1.1 * 23
1.1 * 23
1.1001 * 26
1.11 * 20
1.0 * 2-2
0

Sign Biased Exponent
0
1
0
1
0
0

10000010
10000010
10000101
01111111
01111101
00000000

Mantissa
10000000 00000000 00000000
10000000 00000000 00000000
10010000 00000000 00000000
11000000 00000000 00000000
00000000 00000000 00000000
00000000 00000000 00000000

and exponent are added before being stored in the exponent portion of the floating-point number.
In the previous example, there is an exponent of 23, represented as a biased exponent of 127 + 3
or 130 (82H) in the single-precision form, or as 1026 (402H) in the double-precision form.
There are two exceptions to the rules for floating-point numbers. The number 0.0 is stored
as all zeros. The number infinity is stored as all ones in the exponent and all zeros in the mantissa. The sign-bit indicates either a positive or a negative infinity.
As with other data types, the assembler can be used to define real numbers in both singleand double-precision forms. Because single-precision numbers are 32-bit numbers, use the DD
directive or use the define quadword(s), or DQ, directive to define 64-bit double-precision real
numbers. Optional directives for real numbers are REAL4, REAL8, and REAL10 for defining
single-, double-, and extended precision real numbers. Example 1–30 shows numbers defined in
real number format for the assembler. If using the inline assembler in Visual C++ singleprecision numbers are defined as float and double-precision numbers are defined as double as
shown in Example 1–31. There is no way to define the extended-precision floating-point number
for use in Visual C++.
EXAMPLE 1–30

0000
0004
0008

3F9DF3B6
C1BB3333
43D20000

000C
0014

405ED9999999999A
C1BB333333333333

001C

4005F6CCCCCCCCCCCCCD

EXAMPLE 1–31
//Single-precision real numbers
//
float Numb1 = 1.234;
float Numb2 = -23.4;
float Numb3 = 4.3e2;
//
//Double-precision real numbers
//
double Numb4 = 123.4;
double Numb5 = -23.4;

;single-precision real numbers
;
NUMB1
DD
1.234
;define
NUMB2
DD
-23.4
;define
NUMB3
REAL4 4.2E2
;define
;
;double-precision real numbers
;
NUMB4
DQ
123.4
;define
NUMB5
REAL8 -23.4
;define
;
;Extended-precision real numbers
;
NUMB6
REAL10 123.4
;define

1.234
-23.4
420

123.4
-23.4

123.4

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

1–5

45

SUMMARY
1. The mechanical computer age began with the advent of the abacus in 500 B.C. This first
mechanical calculator remained unchanged until 1642, when Blaise Pascal improved it. An
early mechanical computer system was the Analytical Engine developed by Charles
Babbage in 1823. Unfortunately, this machine never functioned because of the inability to
create the necessary machine parts.
2. The first electronic calculating machine was developed during World War II by Konrad
Zuse, an early pioneer of digital electronics. His computer, the Z3, was used in aircraft and
missile design for the German war effort.
3. The first electronic computer, which used vacuum tubes, was placed into operation in 1943
to break secret German military codes. This first electronic computer system, the Colossus,
was invented by Alan Turing. Its only problem was that the program was fixed and could not
be changed.
4. The first general-purpose, programmable electronic computer system was developed in
1946 at the University of Pennsylvania. This first modern computer was called the ENIAC
(Electronics Numerical Integrator and Calculator).
5. The first high-level programming language, called FLOWMATIC, was developed for the
UNIVAC I computer by Grace Hopper in the early 1950s. This led to FORTRAN and other
early programming languages such as COBOL.
6. The world’s first microprocessor, the Intel 4004, was a 4-bit microprocessor—a programmable controller on a chip—that was meager by today’s standards. It addressed a mere 4096
4-bit memory locations. Its instruction set contained only 45 different instructions.
7. Microprocessors that are common today include the 8086/8088, which were the first 16-bit
microprocessors. Following these early 16-bit machines were the 80286, 80386, 80486,
Pentium, Pentium Pro, Pentium II, Pentium III, Pentium 4, and Core2 processors. The architecture has changed from 16 bits to 32 bits and, with the Itanium, to 64 bits. With each newer
version, improvements followed that increased the processor’s speed and performance. From
all indications, this process of speed and performance improvement will continue, although
the performance increases may not always come from an increased clock frequency.
8. The DOS-based personal computers contain memory systems that include three main areas:
TPA (transient program area), system area, and extended memory. The TPA hold: application programs, the operating system, and drivers. The system area contains memory used for
video display cards, disk drives, and the BIOS ROM. The extended memory area is only
available to the 80286 through the Core2 microprocessor in an AT-style or ATX-style personal computer system. The Windows-based personal computers contain memory systems
that include two main areas: TPA and systems area.
9. The 8086/8088 address 1M byte of memory from locations 00000H–FFFFFH. The 80286 and
80386SX address 16M bytes of memory from locations 000000H–FFFFFFH. The 80386SL
addresses 32M bytes of memory from locations 0000000H–1FFFFFFH. The 80386DX
through the Core2 address 4G bytes of memory from locations 00000000H–FFFFFFFFH.
In addition, the Pentium Pro through the Core2 can operate with a 36-bit address and access
up to 64G bytes of memory from locations 000000000H–FFFFFFFFFH. A Pentium 4 or
Core2 operating with 64-bit extensions addresses memory from locations 0000000000H–
FFFFFFFFFFH for 1T byte of memory.
10. All versions of the 8086 through the Core2 microprocessors address 64K bytes of I/O address
space. These I/O ports are numbered from 0000H to FFFFH with I/O ports 0000H–03FFH
reserved for use by the personal computer system. The PCI bus allows ports 0400H–FFFFH.
11. The operating system in early personal computers was either MSDOS (Microsoft disk operating system) or PCDOS (personal computer disk operating system from IBM). The operating

46

CHAPTER 1

12.

13.

14.

15.

16.

17.

18.

19.

20.
21.

1–6

system performs the task of operating or controlling the computer system, along with its I/O
devices. Modern computers use Microsoft Windows in place of DOS as an operating system.
The microprocessor is the controlling element in a computer system. The microprocessor
performs data transfers, does simple arithmetic and logic operations, and makes simple decisions. The microprocessor executes programs stored in the memory system to perform complex operations in short periods of time.
All computer systems contain three buses to control memory and I/O. The address bus is used to
request a memory location or I/O device. The data bus transfers data between the microprocessor and its memory and I/O spaces. The control bus controls the memory and I/O, and requests
reading or writing of data. Control is accomplished with IORC (I/O read control), IOWC (I/O
write control), MRDC (memory read control), and MWTC (memory write control).
Numbers are converted from any number base to decimal by noting the weights of each
position. The weight of the position to the left of the radix point is always the units position
in any number system. The position to the left of the units position is always the radix times
one. Succeeding positions are determined by multiplying by the radix. The weight of the
position to the right of the radix point is always determined by dividing by the radix.
Conversion from a whole decimal number to any other base is accomplished by dividing by
the radix. Conversion from a fractional decimal number is accomplished by multiplying by
the radix.
Hexadecimal data are represented in hexadecimal form or in a code called binary-coded
hexadecimal (BCH). A binary-coded hexadecimal number is one that is written with a 4-bit
binary number that represents each hexadecimal digit.
The ASCII code is used to store alphabetic or numeric data. The ASCII code is a 7-bit code;
it can have an eighth bit that is used to extend the character set from 128 codes to 256 codes.
The carriage return (Enter) code returns the print head or cursor to the left margin. The line
feed code moves the cursor or print head down one line. Most modern applications use
Unicode, which contains ASCII at codes 0000H–00FFH.
Binary-coded decimal (BCD) data are sometimes used in a computer system to store decimal data. These data are stored either in packed (two digits per byte) or unpacked (one digit
per byte) form.
Binary data are stored as a byte (8 bits), word (16 bits), or doubleword (32 bits) in a computer system. These data may be unsigned or signed. Signed negative data are always stored
in the two’s complement form. Data that are wider than 8 bits are always stored using the
little endian format. In 32-bit Visual C++ these data are represented with char (8 bits), short
(16 bits) and int (32 bits).
Floating-point data are used in computer systems to store whole, mixed, and fractional numbers. A floating-point number is composed of a sign, a mantissa, and an exponent.
The assembler directives DB or BYTE define bytes, DW or WORD define words, DD or
DWORD define doublewords, and DQ or QWORD define quadwords.

QUESTIONS AND PROBLEMS
1.
2.
3.
4.
5.
6.
7.

Who developed the Analytical Engine?
The 1890 census used a new device called a punched card. Who developed the punched card?
Who was the founder of IBM Corporation?
Who developed the first electronic calculator?
The first electronic computer system was developed for what purpose?
The first general-purpose, programmable computer was called the ____________.
The world’s first microprocessor was developed in 1971 by ____________.

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.

47

Who was the Countess of Lovelace?
Who developed the first high-level programming language called FLOWMATIC?
What is a von Neumann machine?
Which 8-bit microprocessor ushered in the age of the microprocessor?
The 8085 microprocessor, introduced in 1977, has sold ____________ copies.
Which Intel microprocessor was the first to address 1M bytes of memory?
The 80286 addresses ____________ bytes of memory.
How much memory is available to the 80486 microprocessor?
When did Intel introduce the Pentium microprocessor?
When did Intel introduce the Pentium Pro processor?
When did Intel introduce the Pentium 4 microprocessor?
Which Intel microprocessor addresses 1T of memory?
What is the acronym MIPs?
What is the acronym CISC?
A binary bit stores a(n) ____________or a(n) ____________.
A computer K (pronounced kay) is equal to ____________ bytes.
A computer M (pronounced meg) is equal to ____________ K bytes.
A computer G (pronounced gig) is equal to ____________ M bytes.
A computer P (pronounced peta) is equal to ____________ T bytes.
How many typewritten pages of information are stored in a 4G-byte memory?
The first 1M byte of memory in a DOS-based computer system contains a(n) ____________
and a(n) ____________area.
How large is the Windows application programming area?
How much memory is found in the DOS transient program area?
How much memory is found in the Windows systems area?
The 8086 microprocessor addresses ____________ bytes of memory.
The Core2 microprocessor addresses ____________ bytes of memory.
Which microprocessors address 4G bytes of memory?
Memory above the first 1M byte is called ____________ memory.
What is the system BIOS?
What is DOS?
What is the difference between an XT and an AT computer system?
What is the VESA local bus?
The ISA bus holds ____________-bit interface cards.
What is the USB?
What is the AGP?
What is the XMS?
What is the SATA interface and where is it used in a system?
A driver is stored in the ____________ area.
The personal computer system addresses ____________ bytes of I/O space.
What is the purpose of the BIOS?
Draw the block diagram of a computer system.
What is the purpose of the microprocessor in a microprocessor-based computer?
List the three buses found in all computer systems.
Which bus transfers the memory address to the I/O device or to the memory?
Which control signal causes the memory to perform a read operation?
What is the purpose of the IORC signal?
If the MRDC signal is a logic 0, which operation is performed by the microprocessor?
Define the purpose of the following assembler directives:
(a) DB
(b) DQ

48

CHAPTER 1

56.

57.

58.

59.

60.

61.

62.

63.

64.

(c) DW
(d) DD
Define the purpose of the following 32-bit Visual C++ directives:
(a) char
(b) short
(c) int
(d) float
(e) double
Convert the following binary numbers into decimal:
(a) 1101.01
(b) 111001.0011
(c) 101011.0101
(d) 111.0001
Convert the following octal numbers into decimal:
(a) 234.5
(b) 12.3
(c) 7767.07
(d) 123.45
(e) 72.72
Convert the following hexadecimal numbers into decimal:
(a) A3.3
(b) 129.C
(c) AC.DC
(d) FAB.3
(e) BB8.0D
Convert the following decimal integers into binary, octal, and hexadecimal:
(a) 23
(b) 107
(c) 1238
(d) 92
(e) 173
Convert the following decimal numbers into binary, octal, and hexadecimal:
(a) 0.625
(b) .00390625
(c) .62890625
(d) 0.75
(e) .9375
Convert the following hexadecimal numbers into binary-coded hexadecimal code (BCH):
(a) 23
(b) AD4
(c) 34.AD
(d) BD32
(e) 234.3
Convert the following binary-coded hexadecimal numbers into hexadecimal:
(a) 1100 0010
(b) 0001 0000 1111 1101
(c) 1011 1100
(d) 0001 0000
(e) 1000 1011 1010
Convert the following binary numbers to the one’s complement form:
(a) 1000 1000
(b) 0101 1010

INTRODUCTION TO THE MICROPROCESSOR AND COMPUTER

65.

66.
67.

68.
69.
70.
71.

72.

73.
74.
75.

76.
77.
78.

79.

80.

49

(c) 0111 0111
(d) 1000 0000
Convert the following binary numbers to the two’s complement form:
(a) 1000 0001
(b) 1010 1100
(c) 1010 1111
(d) 1000 0000
Define byte, word, and doubleword.
Convert the following words into ASCII-coded character strings:
(a) FROG
(b) Arc
(c) Water
(d) Well
What is the ASCII code for the Enter key and what is its purpose?
What is the Unicode?
Use an assembler directive to store the ASCII-character string ‘What time is it?’ in the memory.
Convert the following decimal numbers into 8-bit signed binary numbers:
(a) +32
(b) -12
(c) +100
(d) -92
Convert the following decimal numbers into signed binary words:
(a) +1000
(b) -120
(c) +800
(d) -3212
Use an assembler directive to store byte-sized -34 into the memory.
Create a byte-sized variable called Fred1 and store a -34 in it in Visual C++.
Show how the following 16-bit hexadecimal numbers are stored in the memory system (use
the standard Intel little endian format):
(a) 1234H
(b) A122H
(c) B100H
What is the difference between the big endian and little endian formats for storing numbers
that are larger than 8 bits in width?
Use an assembler directive to store a 123A hexadecimal into memory.
Convert the following decimal numbers into both packed and unpacked BCD forms:
(a) 102
(b) 44
(c) 301
(d) 1000
Convert the following binary numbers into signed decimal numbers:
(a) 10000000
(b) 00110011
(c) 10010010
(d) 10001001
Convert the following BCD numbers (assume that these are packed numbers) to decimal
numbers:
(a) 10001001
(b) 00001001
(c) 00110010
(d) 00000001

50

CHAPTER 1

81. Convert the following decimal numbers into single-precision floating-point numbers:
(a) +1.5
(b) –10.625
(c) +100.25
(d) –1200
82. Convert the following single-precision floating-point numbers into decimal numbers:
(a) 0 10000000 11000000000000000000000
(b) 1 01111111 00000000000000000000000
(c) 0 10000010 10010000000000000000000
83. Use the Internet to write a short report about any one of the following computer pioneers:
(a) Charles Babbage
(b) Konrad Zuse
(c) Joseph Jacquard
(d) Herman Hollerith
84. Use the Internet to write a short report about any one of the following computer languages:
(a) COBOL
(b) ALGOL
(c) FORTRAN
(d) PASCAL
85. Use the Internet to write a short report detailing the features of the Itanium 2 microprocessor.
86. Use the Internet to detail the Intel 45 nm (nanometer) fabrication technology.

CHAPTER 2
The Microprocessor and Its Architecture

INTRODUCTION
This chapter presents the microprocessor as a programmable device by first looking at its
internal programming model and then how its memory space is addressed. The architecture of
the family of Intel microprocessors is presented simultaneously, as are the ways that the family
members address the memory system.
The addressing modes for this powerful family of microprocessors are described for the real,
protected, and flat modes of operation. Real mode memory (DOS memory) exists at locations
00000H–FFFFFH, the first 1M byte of the memory system, and is present on all versions of the
microprocessor. Protected mode memory (Windows memory) exists at any location in the entire
protected memory system, but is available only to the 80286–Core2, not to the earlier 8086 or 8088
microprocessors. Protected mode memory for the 80286 contains 16M bytes; for the 80386–
Pentium, 4G bytes; and for the Pentium Pro through the Core2, either 4G or 64G bytes. With the
64-bit extensions enabled, the Pentium 4 and Core2 address 1T byte of memory in a flat memory
model. Windows Vista or Windows 64 is needed to operate the Pentium 4 or Core2 in 64-bit mode
using the flat mode memory to access the entire 1T byte of memory.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Describe the function and purpose of each program-visible register in the 8086–Core2
microprocessors, including the 64-bit extensions.
2. Detail the flag register and the purpose of each flag bit.
3. Describe how memory is accessed using real mode memory-addressing techniques.
4. Describe how memory is accessed using protected mode memory-addressing techniques.
5. Describe how memory is accessed using the 64-bit flat memory model.
6. Describe the program-invisible registers found within the 80286 through Core2 microprocessors.
7. Detail the operation of the memory-paging mechanism.

2–1

INTERNAL MICROPROCESSOR ARCHITECTURE
Before a program is written or any instruction investigated, the internal configuration of the microprocessor must be known. This section of the chapter details the program-visible internal architecture of the 8086–Core2 microprocessors. Also detailed are the function and purpose of each of these
51

52

CHAPTER 2

internal registers. Note that in a multiple core microprocessor each core contains the same programming model. The only difference is that each core runs a separate task or thread simultaneously.

The Programming Model
The programming model of the 8086 through the Core2 is considered to be program visible
because its registers are used during application programming and are specified by the instructions.
Other registers, detailed later in this chapter, are considered to be program invisible because they
are not addressable directly during applications programming, but may be used indirectly during
system programming. Only the 80286 and above contain the program-invisible registers used to
control and operate the protected memory system and other features of the microprocessor.
Figure 2–1 illustrates the programming model of the 8086 through the Core2 microprocessor including the 64-bit extensions. The earlier 8086, 8088, and 80286 contain 16-bit internal
FIGURE 2–1 The programming
model of the 8086 through the
Core2 microprocessor including
the 64-bit extensions.

64-bit Names

32-bit Names

16-bit Names

RAX

EAX

AX

RBX

EBX

BX

RCX

ECX

CX

RDX

EDX

DX

RBP

EBP

BP

RSI

ESI

SI

RDI

EDI

DI

RSP

ESP

SP

8-bit Names

AH

AL

BH

BL

CH

CL

DH

DL

64 bits
32 bits
16 bits
R8
R9
R10
R11
R12
R13
R14
R15

RFLAGS

RIP

EFLAGS

EIP

FLAGS

IP

CS
DS
ES
SS
FS
GS

53

THE MICROPROCESSOR AND ITS ARCHITECTURE

architectures, a subset of the registers shown in Figure 2–1. The 80386 through the Core2
microprocessors contain full 32-bit internal architectures. The architectures of the earlier 8086
through the 80286 are fully upward-compatible to the 80386 through the Core2. The shaded areas
in this illustration represent registers that are found in early versions of the 8086, 8088, or 80286
microprocessors and are provided on the 80386–Core2 microprocessors for compatibility to the
early versions
The programming model contains 8-, 16-, and 32-bit registers. The Pentium 4 and Core2
also contain 64-bit registers when operated in the 64-bit mode as illustrated in the programming
model. The 8-bit registers are AH, AL, BH, BL, CH, CL, DH, and DL and are referred to when
an instruction is formed using these two-letter designations. For example, an ADD AL,AH
instruction adds the 8-bit contents of AH to AL. (Only AL changes due to this instruction.) The
16-bit registers are AX, BX, CX, DX, SP, BP, DI, SI, IP, FLAGS, CS, DS, ES, SS, FS, and GS.
Note that the first 4 16 registers contain a pair of 8-bit registers. An example is AX, which contains AH and AL. The 16-bit registers are referenced with the two-letter designations such as
AX. For example, an ADD DX, CX instruction adds the 16-bit contents of CX to DX. (Only DX
changes due to this instruction.) The extended 32-bit registers are EAX, EBX, ECX, EDX, ESP,
EBP, EDI, ESI, EIP, and EFLAGS. These 32-bit extended registers, and 16-bit registers FS and
GS, are available only in the 80386 and above. The 16-bit registers are referenced by the designations FS or GS for the two new 16-bit registers, and by a three-letter designation for the 32-bit
registers. For example, an ADD ECX, EBX instruction adds the 32-bit contents of EBX to ECX.
(Only ECX changes due to this instruction.)
Some registers are general-purpose or multipurpose registers, while some have special
purposes. The multipurpose registers include EAX, EBX, ECX, EDX, EBP, EDI, and ESI. These
registers hold various data sizes (bytes, words, or doublewords) and are used for almost any purpose, as dictated by a program.
The 64-bit registers are designated as RAX, RBX, and so forth. In addition to the renaming of the registers for 64-bit widths, there are also additional 64-bit registers that are called
R8 through R15. The 64-bit extensions have multiplied the available register space by more
than 8 times in the Pentium 4 and the Core2 when compared to the original microprocessor
architecture as indicated in the shaded area in Figure 2–1. An example 64-bit instruction is
ADD RCX, RBX, instruction, which adds the 64-bit contents of RBX to RCX. (Only RCX
changes due to this instruction.) One difference exists: these additional 64-bit registers (R8
through R15) are addressed as a byte, word, doubleword, or quadword, but only the rightmost
8 bits is a byte. R8 through R15 have no provision for directly addressing bits 8 through 15 as
a byte. In the 64-bit mode, a legacy high byte register (AH, BH, CH, or DH) cannot be
addressed in the same instruction with an R8 through R15 byte. Because legacy software does
not access R8 through R15, this causes no problems with existing 32-bit programs, which
function without modification.
Table 2–1 shows the overrides used to access portions of a 64-bit register. To access the
low-order byte of the R8 register, use R8B (where B is the low-order byte). Likewise, to access
the low-order word of a numbered register, such as R10, use R10W in the instruction. The letter
D is used to access a doubleword. An example instruction that copies the low-order doubleword
from R8 to R11 is MOV R11D, R8D. There is no special letter for the entire 64-bit register.
TABLE 2–1 Flat
mode 64-bit access to
numbered registers.

Register Size
8 bits
16 bits
32 bits
64 bits

Override

Bits Accessed

B
W
D


7–0
15–0
31–0
63–0

Example
MOV R9B, R10B
MOV R10W, AX
MOV R14D, R15D
MOV R13, R12

54

CHAPTER 2

Multipurpose Registers
RAX
(accumulator)

RAX is referenced as a 64-bit register (RAX), a 32-bit register
(EAX), a 16-bit register (AX), or as either of two 8-bit registers (AH
and AL). Note that if an 8- or 16-bit register is addressed, only that
portion of the 32-bit register changes without affecting the remaining
bits. The accumulator is used for instructions such as multiplication,
division, and some of the adjustment instructions. For these
instructions, the accumulator has a special purpose, but is generally
considered to be a multipurpose register. In the 80386 and above, the
EAX register may also hold the offset address of a location in the
memory system. In the 64-bit Pentium 4 and Core2, RAX holds a 64bit offset address, which allows 1T (terra) byte of memory to be
accessed through a 40-bit address bus. In the future, Intel plans to
expand the address bus to 52 bits to address 4P (peta) bytes of memory.

RBX
(base index)

RBX is addressable as RBX, EBX, BX, BH, or BL. The BX register
sometimes holds the offset address of a location in the memory
system in all versions of the microprocessor. In the 80386 and
above, EBX also can address memory data. In the 64-bit Pentium 4
and Core2, RBX can also address memory data.

RCX
(count)

RCX, which is addressable as RCX, ECX, CX, CH, or CL, is a
general-purpose register that also holds the count for various
instructions. In the 80386 and above, the ECX register also can hold
the offset address of memory data. In the 64-bit Pentium 4, RCX can
also address memory data. Instructions that use a count are the
repeated string instructions (REP/REPE/REPNE); and shift, rotate,
and LOOP/LOOPD instructions. The shift and rotate instructions use
CL as the count, the repeated string instructions use CX, and the
LOOP/LOOPD instructions use either CX or ECX. If operated in the
64-bit mode, LOOP uses the 64-bit RCX register for the loop counter.

RDX
(data)

RDX, which is addressable as RDX, EDX, DX, DH, or DL, is a
general-purpose register that holds a part of the result from a
multiplication or part of the dividend before a division. In the 80386
and above, this register can also address memory data.

RBP
(base pointer)

RBP, which is addressable as RBP, EBP, or BP, points to a memory
location in all versions of the microprocessor for memory data transfers.

RDI
(destination index)

RDI, which is addressable as RDI, EDI, or DI, often addresses
string destination data for the string instructions.

RSI
(source index)

RSI is used as RSI, ESI, or SI. The source index register often
addresses source string data for the string instructions. Like RDI,
RSI also functions as a general-purpose register. As a 16-bit
register, it is addressed as SI; as a 32-bit register, it is addressed as
ESI; and as a 64-bit register, it is addressed as RSI.

R8 through R15

These registers are only found in the Pentium 4 and Core2 if 64-bit
extensions are enabled. As mentioned, data in these registers are
addressed as 64-, 32-, 16-, or 8-bit sizes and are of general purpose.
Most applications will not use these registers until 64-bit processors
are common. Please note that the 8-bit portion is the rightmost 8-bit
only; bits 8 to 15 are not directly addressable as a byte.

55

THE MICROPROCESSOR AND ITS ARCHITECTURE

FIGURE 2–2 The EFLAG
and FLAG register counts for
the entire 8086 and Pentium
microprocessor family.

31

21 20 19 18 17 16

14 13 12 11 10

9

8

7

6

4

2

0

ID VIP VIF AC VM RF

NT IOP IOP O

I

T

S

Z

A

P

C

1

0

D

8086/8088/80186/80188
80286
80386/8986DX
80486SX
Pentium/Pentium 4

Special-Purpose Registers. The special-purpose registers include RIP, RSP, and RFLAGS;
and the segment registers include CS, DS, ES, SS, FS, and GS.
RIP
RIP addresses the next instruction in a section of memory defined as
(instruction pointer)
a code segment. This register is IP (16 bits) when the microprocessor
operates in the real mode and EIP (32 bits) when the 80386 and
above operate in the protected mode. Note that the 8086, 8088, and
80286 do not contain an EIP register and only the 80286 and above
operate in the protected mode. The instruction pointer, which points
to the next instruction in a program, is used by the microprocessor to
find the next sequential instruction in a program located within the
code segment. The instruction pointer can be modified with a jump
or a call instruction. In the 64-bit mode, RIP contains a 40-bit
address at present to address a 1T flat address space.
RSP
RSP addresses an area of memory called the stack. The stack memory
(stack pointer)
stores data through this pointer and is explained later in the text with
the instructions that address stack data. This register is referred to as
SP if used as a 16-bit register and ESP if referred to as a 32-bit register.
RFLAGS
RFLAGS indicate the condition of the microprocessor and control
its operation. Figure 2–2 shows the flag registers of all versions of the
microprocessor. (Note the flags are upward-compatible from the
8086/8088 through the Core2 microprocessors.) The 8086–80286
contain a FLAG register (16 bits) and the 80386 and above contain an
EFLAG register (32-bit extended flag register). The 64-bit RFLAGS
contain the EFLAG register, which is unchanged in the 64-bit version.
The rightmost five flag bits and the overflow flag change after many arithmetic and logic
instructions execute. The flags never change for any data transfer or program control operation.
Some of the flags are also used to control features found in the microprocessor. Following is a list of
each flag bit, with a brief description of their function. As instructions are introduced in subsequent
chapters, additional detail on the flag bits is provided. The rightmost five flags and the overflow flag
are changed by most arithmetic and logic operations, although data transfers do not affect them.
C (carry)

P (parity)

Carry holds the carry after addition or the borrow after subtraction. The
carry flag also indicates error conditions, as dictated by some programs
and procedures. This is especially true of the DOS function calls.
Parity is a logic 0 for odd parity and a logic 1 for even parity. Parity is
the count of ones in a number expressed as even or odd. For example,
if a number contains three binary one bits, it has odd parity. If a
number contains no one bits, it has even parity. The parity flag finds
little application in modern programming and was implemented in
early Intel microprocessors for checking data in data communications
environments. Today parity checking is often accomplished by the
data communications equipment instead of the microprocessor.

56

CHAPTER 2

A (auxiliary carry)

The auxiliary carry holds the carry (half-carry) after addition or the
borrow after subtraction between bit positions 3 and 4 of the result.
This highly specialized flag bit is tested by the DAA and DAS
instructions to adjust the value of AL after a BCD addition or
subtraction. Otherwise, the A flag bit is not used by the
microprocessor or any other instructions.

Z (zero)

The zero flag shows that the result of an arithmetic or logic operation is
zero. If Z = 1, the result is zero; if Z = 0, the result is not zero. This
may be confusing, but that is how Intel decided to name this flag.

S (sign)

The sign flag holds the arithmetic sign of the result after an arithmetic
or logic instruction executes. If S = 1, the sign bit (leftmost bit of a
number) is set or negative; if S = 0, the sign bit is cleared or positive.

T (trap)

The trap flag enables trapping through an on-chip debugging
feature. (A program is debugged to find an error or bug.) If the T
flag is enabled (1), the microprocessor interrupts the flow of the
program on conditions as indicated by the debug registers and
control registers. If the T flag is a logic 0, the trapping (debugging)
feature is disabled. The Visual C++ debugging tool uses the trap
feature and debug registers to debug faulty software.

I (interrupt)

The interrupt flag controls the operation of the INTR (interrupt
request) input pin. If I = 1, the INTR pin is enabled; if I = 0, the
INTR pin is disabled. The state of the I flag bit is controlled by the
STI (set I flag) and CLI (clear I flag) instructions.

D (direction)

The direction flag selects either the increment or decrement mode
for the DI and/or SI registers during string instructions. If D = 1,
the registers are automatically decremented; if D = 0, the registers
are automatically incremented. The D flag is set with the STD (set
direction) and cleared with the CLD (clear direction) instructions.

O (overflow)

Overflows occur when signed numbers are added or subtracted. An
overflow indicates that the result has exceeded the capacity of the
machine. For example, if 7FH ( +127) is added—using an 8-bit
addition—to 01H ( +1), the result is 80H (–128). This result represents
an overflow condition indicated by the overflow flag for signed
addition. For unsigned operations, the overflow flag is ignored.

IOPL
(I/O privilege level)

IOPL is used in protected mode operation to select the privilege
level for I/O devices. If the current privilege level is higher or more
trusted than the IOPL, I/O executes without hindrance. If the IOPL
is lower than the current privilege level, an interrupt occurs, causing
execution to suspend. Note that an IOPL of 00 is the highest or most
trusted and an IOPL of 11 is the lowest or least trusted.

NT (nested task)

The nested task flag indicates that the current task is nested within
another task in protected mode operation. This flag is set when the
task is nested by software.

RF (resume)

The resume flag is used with debugging to control the resumption of
execution after the next instruction.

VM (virtual mode)

The VM flag bit selects virtual mode operation in a protected mode
system. A virtual mode system allows multiple DOS memory
partitions that are 1M byte in length to coexist in the memory

THE MICROPROCESSOR AND ITS ARCHITECTURE

AC
(alignment check)

VIF
(virtual interrupt)
VIP (virtual
interrupt pending)

ID (identification)

57

system. Essentially, this allows the system program to execute
multiple DOS programs. VM is used to simulate DOS in the
modern Windows environment.
The alignment check flag bit activates if a word or doubleword is
addressed on a non-word or non-doubleword boundary. Only the
80486SX microprocessor contains the alignment check bit that is
primarily used by its companion numeric coprocessor, the
80487SX, for synchronization.
The VIF is a copy of the interrupt flag bit available to the Pentium–
Pentium 4 microprocessors.
VIP provides information about a virtual mode interrupt for the
Pentium–Pentium 4 microprocessors. This is used in multitasking
environments to provide the operating system with virtual interrupt
flags and interrupt pending information.
The ID flag indicates that the Pentium–Pentium 4 microprocessors
support the CPUID instruction. The CPUID instruction provides the
system with information about the Pentium microprocessor, such as
its version number and manufacturer.

Segment Registers. Additional registers, called segment registers, generate memory
addresses when combined with other registers in the microprocessor. There are either
four or six segment registers in various versions of the microprocessor. A segment register functions differently in the real mode when compared to the protected mode operation of the microprocessor. Details on their function in real and protected mode are provided later in this chapter.
In the 64-bit flat model, segment registers have little use in a program except for the code segment register. Following is a list of each segment register, along with its function in the system:
CS (code)

DS (data)

ES (extra)
SS (stack)

The code segment is a section of memory that holds the code
(programs and procedures) used by the microprocessor. The code
segment register defines the starting address of the section of memory
holding code. In real mode operation, it defines the start of a 64Kbyte section of memory; in protected mode, it selects a descriptor that
describes the starting address and length of a section of memory
holding code. The code segment is limited to 64K bytes in the
8088–80286, and 4G bytes in the 80386 and above when these
microprocessors operate in the protected mode. In the 64-bit mode,
the code segment register is still used in the flat model, but its use
differs from other programming modes as explained in Section 2-5.
The data segment is a section of memory that contains most data used
by a program. Data are accessed in the data segment by an offset
address or the contents of other registers that hold the offset address.
As with the code segment and other segments, the length is limited to
64K bytes in the 8086–80286, and 4G bytes in the 80386 and above.
The extra segment is an additional data segment that is used by
some of the string instructions to hold destination data.
The stack segment defines the area of memory used for the stack.
The stack entry point is determined by the stack segment and stack
pointer registers. The BP register also addresses data within the
stack segment.

58

CHAPTER 2

FS and GS

2–2

The FS and GS segments are supplemental segment registers available
in the 80386–Core2 microprocessors to allow two additional memory
segments for access by programs. Windows uses these segments for
internal operations, but no definition of their usage is available.

REAL MODE MEMORY ADDRESSING
The 80286 and above operate in either the real or protected mode. Only the 8086 and 8088 operate
exclusively in the real mode. In the 64-bit operation mode of the Pentium 4 and Core2, there is no
real mode operation. This section of the text details the operation of the microprocessor in the real
mode. Real mode operation allows the microprocessor to address only the first 1M byte of memory
space—even if it is the Pentium 4 or Core2 microprocessor. Note that the first 1M byte of memory is
called the real memory, conventional memory, or DOS memory system. The DOS operating system requires that the microprocessor operates in the real mode. Windows does not use the real mode.
Real mode operation allows application software written for the 8086/8088, which only contains 1M
byte of memory, to function in the 80286 and above without changing the software. The upward
compatibility of software is partially responsible for the continuing success of the Intel family of
microprocessors. In all cases, each of these microprocessors begins operation in the real mode by
default whenever power is applied or the microprocessor is reset. Note that if the Pentium 4 or Core2
operate in the 64-bit mode, it cannot execute real mode applications; hence, DOS applications will
not execute in the 64-bit mode unless a program that emulates DOS is written for the 64-bit mode.

Segments and Offsets
A combination of a segment address and an offset address accesses a memory location in the
real mode. All real mode memory addresses must consist of a segment address plus an offset
address. The segment address, located within one of the segment registers, defines the beginning address of any 64K-byte memory segment. The offset address selects any location within
the 64K byte memory segment. Segments in the real mode always have a length of 64K bytes.
Figure 2–3 shows how the segment plus offset addressing scheme selects a memory location.
This illustration shows a memory segment that begins at location 10000H and ends at location
IFFFFH—64K bytes in length. It also shows how an offset address, sometimes called a
displacement, of F000H selects location 1F000H in the memory system. Note that the offset
or displacement is the distance above the start of the segment, as shown in Figure 2–3.
The segment register in Figure 2–3 contains 1000H, yet it addresses a starting segment at
location 10000H. In the real mode, each segment register is internally appended with a 0H on its
rightmost end. This forms a 20-bit memory address, allowing it to access the start of a segment.
The microprocessor must generate a 20-bit memory address to access a location within the first
1M of memory. For example, when a segment register contains 1200H, it addresses a 64K-byte
memory segment beginning at location 12000H. Likewise, if a segment register contains 1201H,
it addresses a memory segment beginning at location 12010H. Because of the internally
appended 0H, real mode segments can begin only at a l6-byte boundary in the memory system.
This l6-byte boundary is often called a paragraph.
Because a real mode segment of memory is 64K in length, once the beginning address is
known, the ending address is found by adding FFFFH. For example, if a segment register contains 3000H, the first address of the segment is 30000H, and the last address is 30000H + FFFFH
or 3FFFFH. Table 2–2 shows several examples of segment register contents and the starting and
ending addresses of the memory segments selected by each segment address.
The offset address, which is a part of the address, is added to the start of the segment to
address a memory location within the memory segment. For example, if the segment address is

59

THE MICROPROCESSOR AND ITS ARCHITECTURE

FIGURE 2–3 The real
mode memory-addressing
scheme, using a segment
address plus an offset.

Real mode memory
FFFFF

1FFFF
Offset = F000

1F000
64K-byte
segment
10000

Segment register
1 0 0 0

00000

1000H and the offset address is 2000H, the microprocessor addresses memory location 12000H.
The offset address is always added to the starting address of the segment to locate the data. The
segment and offset address is sometimes written as 1000:2000 for a segment address of 1000H
with an offset of 2000H.
In the 80286 (with special external circuitry) and the 80386 through the Pentium 4, an extra
64K minus 16 bytes of memory is addressable when the segment address is FFFFH and the
HIMEM.SYS driver for DOS is installed in the system. This area of memory (0FFFF0H–
10FFEFH) is referred to as high memory. When an address is generated using a segment address
of FFFFH, the A20 address pin is enabled (if supported in older systems) when an offset is added.
For example, if the segment address is FFFFH and the offset address is 4000H, the machine
addresses memory location FFFF0H + 4000H or 103FF0H. Notice that the A20 address line is
the one in address 103FF0H. If A20 is not supported, the address is generated as 03FF0H because
A20 remains a logic zero.
Some addressing modes combine more than one register and an offset value to form an
offset address. When this occurs, the sum of these values may exceed FFFFH. For example, the
address accessed in a segment whose segment address is 4000H and whose offset address is
specified as the sum of F000H plus 3000H will access memory location 42000H instead of location 52000H. When the F000H and 3000H are added, they form a l6-bit (modulo 16) sum of
2000H used as the offset address; not 12000H, the true sum. Note that the carry of 1
(F000H + 3000H = 12000H) is dropped for this addition to form the offset address of 2000H.
The address is generated as 4000:2000 or 42000H.
TABLE 2–2 Example
of real mode segment
addresses.

Segment Register

Starting Address

Ending Address

2000H

20000H

2FFFFH

2001H

20010H

3000FH

2100H

21000H

30FFFH

AB00H

AB000H

BAFFFH

1234H

12340H

2233FH

60

CHAPTER 2

Default Segment and Offset Registers
The microprocessor has a set of rules that apply to segments whenever memory is addressed.
These rules, which apply in the real and protected mode, define the segment register and offset register combination. For example, the code segment register is always used with the
instruction pointer to address the next instruction in a program. This combination is CS:IP or
CS:EIP, depending upon the microprocessor’s mode of operation. The code segment register defines the start of the code segment and the instruction pointer locates the next instruction
within the code segment. This combination (CS:IP or CS:EIP) locates the next instruction executed by the microprocessor. For example, if CS = 1400H and IP>EIP = 1200H, the microprocessor fetches its next instruction from memory location 14000H + 1200H or 15200H.
Another of the default combinations is the stack. Stack data are referenced through the
stack segment at the memory location addressed by either the stack pointer (SP/ESP) or the
pointer (BP/EBP). These combinations are referred to as SS:SP (SS:ESP), or SS:BP (SS:EBP).
For example, if SS = 2000H and BP = 3000H, the microprocessor addresses memory location
23000H for the stack segment memory location. Note that in real mode, only the rightmost 16 bits
of the extended register address a location within the memory segment. In the 80386–Pentium 4,
never place a number larger than FFFFH into an offset register if the microprocessor is operated
in the real mode. This causes the system to halt and indicate an addressing error.
Other defaults are shown in Table 2–3 for addressing memory using any Intel microprocessor with 16-bit registers. Table 2–4 shows the defaults assumed in the 80386 and above
using 32-bit registers. Note that the 80386 and above have a far greater selection of segment/
offset address combinations than do the 8086 through the 80286 microprocessors.
The 8086–80286 microprocessors allow four memory segments and the 80386–Core2
microprocessors allow six memory segments. Figure 2–4 shows a system that contains four memory segments. Note that a memory segment can touch or even overlap if 64K bytes of memory are
not required for a segment. Think of segments as windows that can be moved over any area of
memory to access data or code. Also note that a program can have more than four or six segments,
but only access four or six segments at a time.
Suppose that an application program requires 1000H bytes of memory for its code, 190H
bytes of memory for its data, and 200H bytes of memory for its stack. This application does not
require an extra segment. When this program is placed in the memory system by DOS, it is loaded in
the TPA at the first available area of memory above the drivers and other TPA program. This area is
indicated by a free-pointer that is maintained by DOS. Program loading is handled automatically by
the program loader located within DOS. Figure 2–5 shows how an application is stored in the
memory system. The segments show an overlap because the amount of data in them does not require
64K bytes of memory. The side view of the segments clearly shows the overlap. It also shows how
segments can be moved over any area of memory by changing the segment starting address.
Fortunately, the DOS program loader calculates and assigns segment starting addresses.

Segment and Offset Addressing Scheme Allows Relocation
The segment and offset addressing scheme seems unduly complicated. It is complicated, but it also
affords an advantage to the system. This complicated scheme of segment plus offset addressing
TABLE 2–3 Default
16-bit segment and
offset combinations.

Segment

Offset

Special Purpose

CS

IP

Instruction address

SS

SP or BP

Stack address

DS

BX, DI, SI, an 8- or 16-bit number

Data address

ES

DI for string instructions

String destination address

61

THE MICROPROCESSOR AND ITS ARCHITECTURE

TABLE 2–4 Default
32-bit segment and
offset combinations.

Segment

Offset

Special Purpose

CS

EIP

Instruction address

SS

ESP or EBP

Stack address

DS

EAX, EBX, ECX, EDX, ESI, EDI,
an 8- or 32-bit number

Data address

ES

EDI for string instructions

String destination address

FS

No default

General address

GS

No default

General address

allows DOS programs to be relocated in the memory system. It also allows programs written to function in the real mode to operate in a protected mode system. A relocatable
program is one that can be placed into any area of memory and executed without change.
Relocatable data are data that can be placed in any area of memory and used without any change to
FIGURE 2–4 A memory
system showing the placement of four memory
segments.

Memory
FFFFF

59000
58FFF
Extra
49000
48FFF
44000
43FFF

4 9 0 0

ES

3 4 0 0

SS

2 0 0 0

CS

1 0 0 0

DS

Stack
34000
33FFF
30000
2FFFF
Code
20000
1FFFF
Data
10000
0FFFF

00000

62

CHAPTER 2

FIGURE 2–5 An application
program containing a code,
data, and stack segment
loaded into a DOS system
memory.

Imaginary side
view detailing
segment overlap

Memory
FFFFF

S
t
a
c
k D
a
t
a C
o
d
e

0A480
0A47F
Stack
0A280
0A27F

0 A 2 8

SS

0 A 0 F

DS

0 9 0 F

CS

Data
0A0F0
0A0EF
Code
090F0
0908F

DOS and drivers

00000

the program. The segment and offset addressing scheme allows both programs and data to be relocated without changing a thing in a program or data. This is ideal for use in a general-purpose computer system in which not all machines contain the same memory areas. The personal computer
memory structure is different from machine to machine, requiring relocatable software and data.
Because memory is addressed within a segment by an offset address, the memory segment can be moved to any place in the memory system without changing any of the offset
addresses. This is accomplished by moving the entire program, as a block, to a new area and
then changing only the contents of the segment registers. If an instruction is 4 bytes above the
start of the segment, its offset address is 4. If the entire program is moved to a new area of memory, this offset address of 4 still points to 4 bytes above the start of the segment. Only the contents of the segment register must be changed to address the program in the new area of memory. Without this feature, a program would have to be extensively rewritten or altered before it
is moved. This would require additional time or many versions of a program for the many
different configurations of computer systems. This concept also applies to programs written to

THE MICROPROCESSOR AND ITS ARCHITECTURE

63

execute in the protected mode for Windows. In the Windows environment all programs are written assuming that the first 2G of memory are available for code and data. When the program is
loaded, it is placed in the actual memory, which may be anywhere and a portion may be located
on the disk in the form of a swap file.

2–3

INTRODUCTION TO PROTECTED MODE MEMORY ADDRESSING
Protected mode memory addressing (80286 and above) allows access to data and programs located
above the first 1M byte of memory, as well as within the first 1M byte of memory. Protected mode
is where Windows operates. Addressing this extended section of the memory system requires a
change to the segment plus an offset addressing scheme used with real mode memory addressing.
When data and programs are addressed in extended memory, the offset address is still used to
access information located within the memory segment. One difference is that the segment address,
as discussed with real mode memory addressing, is no longer present in the protected mode. In
place of the segment address, the segment register contains a selector that selects a descriptor from
a descriptor table. The descriptor describes the memory segment’s location, length, and access
rights. Because the segment register and offset address still access memory, protected mode
instructions are identical to real mode instructions. In fact, most programs written to function in the
real mode will function without change in the protected mode. The difference between modes is in
the way that the segment register is interpreted by the microprocessor to access the memory segment. Another difference, in the 80386 and above, is that the offset address can be a 32-bit number
instead of a 16-bit number in the protected mode. A 32-bit offset address allows the microprocessor to access data within a segment that can be up to 4G bytes in length. Programs that are written
for the 32-bit protected mode execute in the 64-bit mode of the Pentium 4.

Selectors and Descriptors
The selector, located in the segment register, selects one of 8192 descriptors from one of two
tables of descriptors. The descriptor describes the location, length, and access rights of the segment of memory. Indirectly, the segment register still selects a memory segment, but not directly
as in the real mode. For example, in the real mode, if CS = 0008H, the code segment begins at
location 00080H. In the protected mode, this segment number can address any memory location
in the entire system for the code segment, as explained shortly.
There are two descriptor tables used with the segment registers: one contains global descriptors and the other contains local descriptors. The global descriptors contain segment definitions
that apply to all programs, whereas the local descriptors are usually unique to an application. You
might call a global descriptor a system descriptor and call a local descriptor an application
descriptor. Each descriptor table contains 8192 descriptors, so a total of 16,384 total descriptors
are available to an application at any time. Because the descriptor describes a memory segment,
this allows up to 16,384 memory segments to be described for each application. Since a memory
segment can be up to 4G bytes in length, this means that an application could have access to
4G * 16,384 bytes of memory or 64T bytes.
Figure 2–6 shows the format of a descriptor for the 80286 through the Core2. Note that
each descriptor is 8 bytes in length, so the global and local descriptor tables are each a maximum
of 64K bytes in length. Descriptors for the 80286 and the 80386–Core2 differ slightly, but the
80286 descriptor is upward-compatible.
The base address portion of the descriptor indicates the starting location of the memory
segment. For the 80286 microprocessor, the base address is a 24-bit address, so segments begin
at any location in its 16M bytes of memory. Note that the paragraph boundary limitation is

64

CHAPTER 2
80286

31

0

Access Rights

0000 0000 0000 0000

4

Base
B23

B16

0

Limit

Base
B15

B0

L15

L0

Offset

80386–P4

31

Base
B31

B24

A
G D 0
V

Limit
L19

0

Access Rights

L16

Base

4

Base
B23

B16

0

Limit

B15

B0

L15

L0

Offset

64–bit P4

31

0000 0000

G D L

A
V

0000 0000 0000 0000

0000

0

Access Rights

0000 0000

0000 0000 0000 0000

4

0
Offset

FIGURE 2–6

The 80286 through Core2 64-bit descriptors.

removed in these microprocessors when operated in the protected mode so segments may begin
at any address. The 80386 and above use a 32-bit base address that allows segments to begin at
any location in its 4G bytes of memory. Notice how the 80286 descriptor’s base address is
upward-compatible to the 80386 through the Pentium 4 descriptor because its most-significant
16 bits are 0000H. Refer to Chapters 18 and 19 for additional detail on the 64G memory space
provided by the Pentium Pro through the Core2.
The segment limit contains the last offset address found in a segment. For example, if a segment
begins at memory location F00000H and ends at location F000FFH, the base address is F00000H and
the limit is FFH. For the 80286 microprocessor, the base address is F00000H and the limit is 00FFH.
For the 80386 and above, the base address is 00F00000H and the limit is 000FFH. Notice that the
80286 has a 16-bit limit and the 80386 through the Pentium 4 have a 20-bit limit. An 80286 can
access memory segments that are between 1 and 64K bytes in length. The 80386 and above access
memory segments that are between 1 and 1M byte, or 4K and 4G bytes in length.
There is another feature found in the 80386 through the Pentium 4 descriptor that is not
found in the 80286 descriptor: the G bit, or granularity bit. If G = 0, the limit specifies a segment limit of 00000H to FFFFFH. If G = 1, the value of the limit is multiplied by 4K bytes
(appended with FFFH). The limit is then 00000FFFFH to FFFFFFFFH, if G = 1. This allows a
segment length of 4K to 4G bytes in steps of 4K bytes. The reason that the segment length is 64K
bytes in the 80286 is that the offset address is always 16 bits because of its 16-bit internal architecture. The 80386 and above use a 32-bit architecture that allows an offset address, in the protected mode operation, of the 32 bits. This 32-bit offset address allows segment lengths of 4G
bytes and the 16-bit offset address allows segment lengths of 64K bytes. Operating systems operate in a 16- or 32-bit environment. For example, DOS uses a 16-bit environment, while most
Windows applications use a 32-bit environment called WIN32.
In the 64-bit descriptor, the L bit (probably means large, but Intel calls it the 64-bit)
selects 64-bit addresses in a Pentium 4 or Core2 with 64-bit extensions when L = 1 and 32-bit

THE MICROPROCESSOR AND ITS ARCHITECTURE

65

compatibility mode when L = 0. In 64-bit protected operation, the code segment register is still
used to select a section of code from the memory. Notice that the 64-bit descriptor has no limit
or base address. It only contains an access rights byte and the control bits. In the 64-bit mode,
there is no segment or limit in the descriptor and the base address of the segment, although not
placed in the descriptor, is 00 0000 0000H. This means that all code segments start at address
zero for 64-bit operation. There are no limit checks for a 64-bit code segment.
Example 2-1 shows the segment start and end if the base address is 10000000H, the limit
is 001FFH, and the G bit = 0.
EXAMPLE 2–1
Base = Start = 10000000H
G = 0
End = Base + Limit = 10000000H + 001FFH = 100001FFH

Example 2-2 uses the same data as Example 2-1, except that the G bit = 1. Notice that the
limit is appended with FFFH to determine the ending segment address.
EXAMPLE 2–2
Base = Start = 10000000H
G = 1
End = Base + Limit = 10000000H + 001FFFFFH = 101FFFFFH

The AV bit, in the 80386 and above descriptor, is used by some operating systems to indicate
that the segment is available (AV = 1) or not available (AV = 0). The D bit indicates how the
80386 through the Core2 instructions access register and memory data in the protected or real mode.
If D = 0, the instructions are 16-bit instructions, compatible with the 8086–80286 microprocessors.
This means that the instructions use 16-bit offset addresses and 16-bit register by default. This mode
is often called the 16-bit instruction mode or DOS mode. If D = 1, the instructions are 32-bit
instructions. By default, the 32-bit instruction mode assumes that all offset addresses and all registers
are 32 bits. Note that the default for register size and offset address is overridden in both the 16- and
32-bit instruction modes. Both the MSDOS and PCDOS operating systems require that the instructions are always used in the 16-bit instruction mode. Windows 3.1, and any application that was written for it, also requires that the 16-bit instruction mode is selected. Note that the instruction mode is
accessible only in a protected mode system such as Windows Vista. More detail on these modes and
their application to the instruction set appears in Chapters 3 and 4.
The access rights byte (see Figure 2–7) controls access to the protected mode segment.
This byte describes how the segment functions in the system. The access rights byte allows
complete control over the segment. If the segment is a data segment, the direction of growth is
specified. If the segment grows beyond its limit, the microprocessor’s operating system program
is interrupted, indicating a general protection fault. You can even specify whether a data segment
can be written or is write-protected. The code segment is also controlled in a similar fashion and
can have reading inhibited to protect software. Again, note that in 64-bit mode there is only a
code segment and no other segment descriptor types. A 64-bit flat model program contains its
data and stacks in the code segment.
Descriptors are chosen from the descriptor table by the segment register. Figure 2–8 shows
how the segment register functions in the protected mode system. The segment register contains
a 13-bit selector field, a table selector bit, and a requested privilege level field. The 13-bit
selector chooses one of the 8192 descriptors from the descriptor table. The TI bit selects either
the global descriptor table (TI = 0) or the local descriptor table (TI = 1). The requested privilege level (RPL) requests the access privilege level of a memory segment. The highest privilege
level is 00 and the lowest is 11. If the requested privilege level matches or is higher in priority
than the privilege level set by the access rights byte, access is granted. For example, if the

66

CHAPTER 2
7

6

5

P

DPL

4

3

S

E

2

1

0

ED R/W A
/C
A = 0 Segment not accessed
A = 1 Segment has been accessed
E = 0 Descriptor describes a data segment
ED = 0 Segment expands upward (data segment)
ED = 1 Segment expands downward (stack segment)
W = 0 Data may not be written
W = 1 Data may be written
E = 1 Descriptor describes code segment
C=0
C=1
R=0
R=1

Ignore descriptor privilege level
Abide by privilege level
Code segment may not be read
Code segment may be read

S = 0 System descriptor
S = 1 Code or data segment descriptor
DLP = Sets the descriptor privilege level
P = 0 Descriptor is undefined
P = 1 Segment contains a valid base and limit
Note: Some of the letters used to describe the bits in the access rights bytes vary in Intel documentation.

FIGURE 2–7

The access rights byte for the 80286 through Core2 descriptor.

requested privilege level is 10 and the access rights byte sets the segment privilege level at 11,
access is granted because 10 is higher in priority than privilege level 11. Privilege levels are used
in multiuser environments. Windows uses privilege level 00 (ring 0) for the kernel and driver
programs and level 11 (ring 3) for applications. Windows does not use levels 01 or 10. If privilege levels are violated, the system normally indicates an application or privilege level violation.
Figure 2–9 shows how the segment register, containing a selector, chooses a descriptor from the
global descriptor table. The entry in the global descriptor table selects a segment in the memory system. In this illustration, DS contains 0008H, which accesses the descriptor number 1 from the global
descriptor table using a requested privilege level of 00. Descriptor number 1 contains a descriptor that
defines the base address as 00100000H with a segment limit of 000FFH. This means that a value of
0008H loaded into DS causes the microprocessor to use memory locations 00100000H–001000FFH
for the data segment with this example descriptor table. Note that descriptor zero is called the null
descriptor, must contain all zeros, and may not be used for accessing memory.
3 2

15
Selector

1 0

TI RPL

RPL = Requested privilege level where
00 is the highest and 11 is the lowest
TI = 0 Global descriptor table
TI = 1 Local descriptor table
Selects one descriptor from 8192 descriptors
in either the global or the local descriptor table

FIGURE 2–8 The contents of a segment register during protected mode operation of the
80286 through Core2 microprocessors.

67

THE MICROPROCESSOR AND ITS ARCHITECTURE

Memory system
FFFFFF
Global descriptor table

100100
1000FF

Descriptor 1

DS
0 0 0 8

0
0
9
1
0
0
0
F

0
0
2
0
0
0
0
F

Data segment

100000
0FFFFF

000000

FIGURE 2–9 Using the DS register to select a description from the global descriptor table. In this
example, the DS register accesses memory locations 00100000H–001000FFH as a data segment.

Program-Invisible Registers
The global and local descriptor tables are found in the memory system. In order to access and
specify the address of these tables, the 80286–Core2 contain program-invisible registers. The
program-invisible registers are not directly addressed by software so they are given this name
(although some of these registers are accessed by the system software). Figure 2–10 illustrates
the program-invisible registers as they appear in the 80286 through the Core2. These registers
control the microprocessor when operated in protected mode.
Each of the segment registers contains a program-invisible portion used in the protected
mode. The program-invisible portion of these registers is often called cache memory because
cache is any memory that stores information. This cache is not to be confused with the level 1
or level 2 caches found with the microprocessor. The program-invisible portion of the segment
register is loaded with the base address, limit, and access rights each time the number segment
register is changed. When a new segment number is placed in a segment register, the microprocessor accesses a descriptor table and loads the descriptor into the program-invisible portion
of the segment register. It is held there and used to access the memory segment until the segment number is again changed. This allows the microprocessor to repeatedly access a memory
segment without referring to the descriptor table (hence the term cache).
The GDTR (global descriptor table register) and IDTR (interrupt descriptor table register) contain the base address of the descriptor table and its limit. The limit of each descriptor

68

CHAPTER 2

Segment registers
CS

Descriptor cache
Base address

Limit

Access

Base address

Limit

Access

DS
ES
SS
FS
GS

TR
LDTR

Descriptor table addresses
GDTR

Base address

Limit

Program invisible

IDTR

Notes:
1. The 80286 does not contain FS and GS nor the program-invisible portions of these registers.
2. The 80286 contains a base address that is 24-bits and a limit that is 16-bits.
3. The 80386/80486/Pentium/Pentium Pro contain a base address that is 32-bits and a limit that is 20-bits.
4. The access rights are 8-bits in the 80286 and 12-bits in the 80386/80486/Pentium–Core2.

FIGURE 2–10

The program-invisible register within the 80286–Core2 microprocessors.

table is 16 bits because the maximum table length is 64K bytes. When the protected mode operation is desired, the address of the global descriptor table and its limit are loaded into the GDTR.
Before using the protected mode, the interrupt descriptor table and the IDTR must also be
initialized. More detail is provided on protected mode operation later in the text. At this point,
programming and additional description of these registers are impossible.
The location of the local descriptor table is selected from the global descriptor table. One of the
global descriptors is set up to address the local descriptor table. To access the local descriptor table,
the LDTR (local descriptor table register) is loaded with a selector, just as a segment register is
loaded with a selector. This selector accesses the global descriptor table and loads the address, limit,
and access rights of the local descriptor table into the cache portion of the LDTR.
The TR (task register) holds a selector, which accesses a descriptor that defines a task. A task
is most often a procedure or application program. The descriptor for the procedure or application
program is stored in the global descriptor table, so access can be controlled through the privilege
levels. The task register allows a context or task switch in about 17 μs. Task switching allows the
microprocessor to switch between tasks in a fairly short amount of time. The task switch allows
multitasking systems to switch from one task to another in a simple and orderly fashion.

2–4

MEMORY PAGING
The memory paging mechanism located within the 80386 and above allows any physical memory
location to be assigned to any linear address. The linear address is defined as the address generated by a program. The physical address is the actual memory location accessed by a program.
With the memory paging unit, the linear address is invisibly translated to any physical address,
which allows an application written to function at a specific address to be relocated through the
paging mechanism. It also allows memory to be placed into areas where no memory exists. An
example is the upper memory blocks provided by EMM386.EXE in a DOS system.

69

THE MICROPROCESSOR AND ITS ARCHITECTURE

The EMM386.EXE program reassigns extended memory, in 4K blocks, to the system
memory between the video BIOS and the system BIOS ROMS for upper memory blocks.
Without the paging mechanism, the use of this area of memory is impossible.
In Windows, each application is allowed a 2G linear address space from location
00000000H–7FFFFFFFH even though there may not be enough memory or memory available at
these addresses. Through paging to the hard disk drive and paging to the memory through the
memory paging unit, any Windows application can be executed.

Paging Registers

0

12
11

31

The paging unit is controlled by the contents of the microprocessor’s control registers. See Figure
2–11 for the contents of control registers CR0 through CR4. Note that these registers are available to
the 80386 through the Core2 microprocessors. Beginning with the Pentium, an additional control
register labeled CR4 controls extensions to the basic architecture provided in the Pentium or newer
microprocessor. One of these features is a 2M- or a 4M-byte page that is enabled by controlling CR4.
The registers important to the paging unit are CR0 and CR3. The leftmost bit (PG) position
of CR0 selects paging when placed at a logic 1 level. If the PG bit is cleared (0), the linear
address generated by the program becomes the physical address used to access memory. If the
PG bit is set (1), the linear address is converted to a physical address through the paging mechanism. The paging mechanism functions in both the real and protected modes.
CR3 contains the page directory base or root address, and the PCD and PWT bits. The PCD
and PWT bits control the operation of the PCD and PWT pins on the microprocessor. If PCD is set
(1), the PCD pin becomes a logic one during bus cycles that are not paged. This allows the external hardware to control the level 2 cache memory. (Note that the level 2 cache memory is an internal [on modern versions of the Pentium] high-speed memory that functions as a buffer between
the microprocessor and the main DRAM memory system.) The PWT bit also appears on the PWT
pin during bus cycles that are not paged to control the write-through cache in the system. The page
directory base address locates the directory for the page translation unit. Note that this address
locates the page directory at any 4K boundary in the memory system because it is appended internally with 000H. The page directory contains 1024 directory entries of 4 bytes each. Each page
directory entry addresses a page table that contains 1024 entries.

M
C
E
Page directory base address

FIGURE 2–11

P P
CW
DT

CR4 Pentium, Pentium Pro,
Pentium II, Pentium III,
Pentium 4 and Core2.
CR3

Page fault linear address

CR2

Reserved

CR1

W
P

N E T E M P
E T S M P E

16

A
M
18

P CN
G DW

P D T P V
S E S V M
D I E
E

The control register structure of the microprocessor.

CR0

70

Directory

11

12

22
21

31

CHAPTER 2

Page table

0
Offset

12

31

(a)

D A P P UWP
CW
D T

Address

(b)

FIGURE 2–12

6 5 4 3 2 1 0

Present
Writable
User defined
Write-through
Cache disable
Accessed
Dirty (0 in page directory)

The format for the linear address (a) and a page directory or page table entry (b).

The linear address, as it is generated by the software, is broken into three sections that are
used to access the page directory entry, page table entry, and memory page offset address.
Figure 2–12 shows the linear address and its makeup for paging. Notice how the leftmost 10 bits
address an entry in the page directory. For linear address 00000000H–003FFFFFH, the first page
directory is accessed. Each page directory entry represents or repages a 4M section of the memory
system. The contents of the page directory select a page table that is indexed by the next 10 bits of
the linear address (bit positions 12–21). This means that address 00000000H–00000FFFH selects
page directory entry of 0 and page table entry of 0. Notice this is a 4K-byte address range. The offset part of the linear address (bit positions 0–11) next selects a byte in the 4K-byte memory page.
In Figure 2–12, if the page table entry 0 contains address 00100000H, then the physical address is
00100000H-00100FFFH for linear address 00000000H–00000FFFH. This means that when the
program accesses a location between 00000000H and 00000FFFH, the microprocessor physically
addresses location 00100000H–00100FFFH.
Because the act of repaging a 4K-byte section of memory requires access to the page directory and a page table, which are both located in memory, Intel has incorporated a special type of
cache called the TLB (translation look-aside buffer). In the 80486 microprocessor, the cache
holds the 32 most recent page translation addresses. This means that the last 32 page table translations are stored in the TLB, so if the same area of memory is accessed, the address is already
present in the TLB, and access to the page directory and page tables is not required. This speeds
program execution. If a translation is not in the TLB, the page directory and page table must be
accessed, which requires additional execution time. The Pentium–Pentium 4 microprocessors
contain separate TLBs for each of their instruction and data caches.

The Page Directory and Page Table
Figure 2–13 shows the page directory, a few page tables, and some memory pages. There is only
one page directory in the system. The page directory contains 1024 doubleword addresses that
locate up to 1024 page tables. The page directory and each page table are 4K bytes in length. If

Page tabIes

Page

diгectoгy

CR3

FIGURE 2-13

FIGURE 2-14 The page
directory, page tabIe О, and
two тетогу pages. Note
how the address of page
ОООС8000-000С9000 has
Ьееп moved to
00110000-00110FFF

The paging mechanism in the 80386 through

Page tabIe

Соге2

microprocessors.

О

0003F003

00003FFC
00003FF8

0003ЕО03

00003FF4

0ОО3ООО3

00110FFF

ОООЗСООЗ

00110FFE~

00003FFO

00003328

---

00112003

00003324

00111003

00003320

00110003

::::=::::::
00003008

00002003

00003004

00001003

00003000

00000003

-

-

~

00110002
00110001

~

§

00110000
Page

OOOOOFFF
OOOOOFFE

~

t=j
.......

0000200С

-

00000002

00002008

00000001

00002004
00002000

000С8

00003003
Page

diгectory

00000000
Page

ОООООН

71

72

CHAPTER 2

the entire 4G byte of memory is paged, the system must allocate 4K bytes of memory for the
page directory, and 4K times 1024 or 4M bytes for the 1024 page tables. This represents a considerable investment in memory resources.
The DOS system and EMM386.EXE use page tables to redefine the area of memory
between locations C8000H–EFFFFH as upper memory blocks. This is done by repaging
extended memory to backfill this part of the conventional memory system to allow DOS access
to additional memory. Suppose that the EMM386.EXE program allows access to 16M bytes of
extended and conventional memory through paging and locations C8000H–EFFFFH must be
repaged to locations 110000–138000H, with all other areas of memory paged to their normal
locations. Such a scheme is depicted in Figure 2–14.
Here, the page directory contains four entries. Recall that each entry in the page directory
corresponds to 4M bytes of physical memory. The system also contains four page tables with
1024 entries each. Recall that each entry in the page table repages 4K bytes of physical memory.
This scheme requires a total of 16K of memory for the four page tables and 16 bytes of memory
for the page directory.
As with DOS, the Windows program also repages the memory system. At present, Windows
version 3.11 supports paging for only 16M bytes of memory because of the amount of memory
required to store the page tables. Newer versions of Windows repage the entire memory system.
On the Pentium–Core2 microprocessors, pages can be 4K, 2M, or 4M bytes in length. In the 2M
and 4M variations, there is only a page directory and a memory page, but no page table.

2–5

FLAT MODE MEMORY
The memory system in a Pentium-based computer (Pentium 4 or Core2) that uses the 64-bit extensions uses a flat mode memory system. A flat mode memory system is one in which there is no segmentation. The address of the first byte in the memory is at 00 0000 0000H and the last location is
at FF FFFF FFFFH (address is 40-bits). The flat model does not use a segment register to address a
location in the memory. The CS segment register is used to select a descriptor from the descriptor
table that defines the access rights of only a code segment. The segment register still selects the
privilege level of the software. The flat model does not select the memory address of a segment
using the base and limit in the descriptor (see Figure 2–6). In 64-bit mode the actual address is not
modified by the descriptor as in 32-bit protected mode. The offset address is the actual physical
address in 64-bit mode. Refer to Figure 2–15 for the flat mode memory model.
This form of addressing is much easier to understand, but offers little protection to the system, through the hardware, as did the protected mode system discussed in Section 2.3. The real
mode system is not available if the processor operates in the 64-bit mode. Protection and paging
are allowed in the 64-bit mode. The CS register is still used in the protected mode operation in
the 64-bit mode.
In the 64-bit mode if set to IA32 compatibility (when the L bit -0 is in the descriptor), an
address is 64-bits, but since only 40 bits of the address are brought out to the address pins, any
address above 40 bits is truncated. Instructions that use a displacement address can only use a 32bit displacement, which allows a range of ;2G from the current instruction. This addressing
mode is called RIP relative addressing, and is explained in Chapter 3. The move immediate
instruction allows a full 64-bit address and access to any flat mode memory location. Other
instructions do not allow access to a location above 4G because the offset address is still 32-bits.
If the Pentium is operated in the full 64-bit mode (where the L = 1 in the descriptor), the
address may be 64-bits or 32-bits. This is shown in examples in the next chapter with addressing
modes and in more detail in Chapter 4. Most programs today are operated in the IA32 compatible mode so current versions of Windows software operates properly, but this will change in a

73

THE MICROPROCESSOR AND ITS ARCHITECTURE
FFFFFFFFFF

FIGURE 2–15 The 64-bit
flat mode memory model.

Linear Address
00000F0000

00000F0000

0000000000

few years as memory becomes larger and most people have 64-bit computers. This is another
example of how the industry makes the software obsolete as the hardware changes.

2–6

SUMMARY
1. The programming model of the 8086 through 80286 contains 8- and 16-bit registers. The
programming model of the 80386 and above contains 8-, 16-, and 32-bit extended registers
as well as two additional 16-bit segment registers: FS and GS.
2. The 8-bit registers are AH, AL, BH, BL, CH, CL, DH, and DL. The 16-bit registers are AX,
BX, CX, DX, SP, BP, DI, and SI. The segment registers are CS, DS, ES, SS, FS, and GS. The
32-bit extended registers are EAX, EBX, ECX, EDX, ESP, EBP, EDI, and ESI. The 64-bit
registers in a Pentium 4 with 64-bit extensions are RAX, RBX, RCX, RDX, RSP, RBP, RDI,
RSI, and R8 through R15. In addition, the microprocessor contains an instruction pointer
(IP/EIP/RIP) and flag register (FLAGS, EFLAGS, or RFLAGS).
3. All real mode memory addresses are a combination of a segment address plus an offset
address. The starting location of a segment is defined by the 16-bit number in the segment
register that is appended with a hexadecimal zero at its rightmost end. The offset address is
a 16-bit number added to the 20-bit segment address to form the real mode memory address.
4. All instructions (code) are accessed by the combination of CS (segment address) plus IP or
EIP (offset address).
5. Data are normally referenced through a combination of the DS (data segment) and either an
offset address or the contents of a register that contains the offset address. The 8086–Core2
use BX, DI, and SI as default offset registers for data if 16-bit registers are selected. The
80386 and above can use the 32-bit registers EAX, EBX, ECX, EDX, EDI, and ESI as
default offset registers for data.

74

CHAPTER 2

6. Protected mode operation allows memory above the first 1M byte to be accessed by the
80286 through the Core2 microprocessors. This extended memory system (XMS) is
accessed via a segment address plus an offset address, just as in the real mode. The difference is that the segment address is not held in the segment register. In the protected mode,
the segment starting address is stored in a descriptor that is selected by the segment register.
7. A protected mode descriptor contains a base address, limit, and access rights byte. The base
address locates the starting address of the memory segment; the limit defines the last location of
the segment. The access rights byte defines how the memory segment is accessed via a program.
The 80286 microprocessor allows a memory segment to start at any of its 16M bytes of memory
using a 24-bit base address. The 80386 and above allow a memory segment to begin at any of its
4G bytes of memory using a 32-bit base address. The limit is a 16-bit number in the 80286 and a
20-bit number in the 80386 and above. This allows an 80286 memory segment limit of 64K
bytes, and an 80386 and above memory segment limit of either 1M bytes (G = 0) or 4G bytes
(G = 1). The L bit selects 64-bit address operation in the code descriptor.
8. The segment register contains three fields of information in the protected mode. The leftmost 13 bits of the segment register address one of 8192 descriptors from a descriptor table.
The TI bit accesses either the global descriptor table (TI = 0) or the local descriptor table
(TI = 1). The rightmost 2 bits of the segment register select the requested priority level for
the memory segment access.
9. The program-invisible registers are used by the 80286 and above to access the descriptor
tables. Each segment register contains a cache portion that is used in protected mode to hold
the base address, limit, and access rights acquired from a descriptor. The cache allows the
microprocessor to access the memory segment without again referring to the descriptor table
until the segment register’s contents are changed.
10. A memory page is 4K bytes in length. The linear address, as generated by a program, can be
mapped to any physical address through the paging mechanism found within the 80386
through the Pentium 4 microprocessor.
11. Memory paging is accomplished through control registers CR0 and CR3. The PG bit of CR0
enables paging, and the contents of CR3 addresses the page directory. The page directory
contains up to 1024 page table addresses that are used to access paging tables. The page
table contains 1024 entries that locate the physical address of a 4K-byte memory page.
12. The TLB (translation look-aside buffer) caches the 32 most recent page table translations.
This precludes page table translation if the translation resides in the TLB, speeding the execution of the software.
13. The flat mode memory contains 1T byte of memory using a 40-bit address. In the future, Intel
plans to increase the address width to 52 bits to access 4P bytes of memory. The flat mode is
only available in the Pentium 4 and Core2 that have their 64-bit extensions enabled.

2–7

QUESTIONS AND PROBLEMS
1.
2.
3.
4.
5.
6.
7.
8.
9.

What are program-visible registers?
The 80286 addresses registers that are 8 and _________ bits wide.
The extended registers are addressable by which microprocessors?
The extended BX register is addressed as _________.
Which register holds a count for some instructions?
What is the purpose of the IP/EIP register?
The carry flag bit is not modified by which arithmetic operations?
Will an overflow occur if a signed FFH is added to a signed 01H?
A number that contains 3 one bits is said to have _________ parity.

THE MICROPROCESSOR AND ITS ARCHITECTURE

10.
11.
12.
13.

14.

15.
16.
17.
18.
19.
20.

21.

22.
23.
24.
25.
26.
27.
28.

75

Which flag bit controls the INTR pin on the microprocessor?
Which microprocessors contain an FS segment register?
What is the purpose of a segment register in the real mode operation of the microprocessor?
In the real mode, show the starting and ending addresses of each segment located by the following segment register values:
(a) 1000H
(b) 1234H
(c) 2300H
(d) E000H
(e) AB00H
Find the memory address of the next instruction executed by the microprocessor, when operated in the real mode, for the following CS:IP combinations:
(a) CS = 1000H and IP = 2000H
(b) CS = 2000H and IP = 1000H
(c) CS = 2300H and IP = 1A00H
(d) CS = 1A00H and IP = B000H
(e) CS = 3456H and IP = ABCDH
Real mode memory addresses allow access to memory below which memory address?
Which register or registers are used as an offset address for the string instruction destination
in the microprocessor?
Which 32-bit register or registers are used to hold an offset address for data segment data in
the Pentium 4 microprocessor?
The stack memory is addressed by a combination of the _________ segment plus
_________ offset.
If the base pointer (BP) addresses memory, the _________ segment contains the data.
Determine the memory location addressed by the following real mode 80286 register
combinations:
(a) DS = 1000H and DI = 2000H
(b) DS = 2000H and SI = 1002H
(c) SS = 2300H and BP = 3200H
(d) DS = A000H and BX = 1000H
(e) SS = 2900H and SP = 3A00H
Determine the memory location addressed by the following real mode Core2 register
combinations:
(a) DS = 2000H and EAX = 00003000H
(b) DS = 1A00H and ECX = 00002000H
(c) DS = C000H and ESI = 0000A000H
(d) SS = 8000H and ESP = 00009000H
(e) DS = 1239H and EDX = 0000A900H
Protected mode memory addressing allows access to which area of the memory in the 80286
microprocessor?
Protected mode memory addressing allows access to which area of the memory in the
Pentium 4 microprocessor?
What is the purpose of the segment register in protected mode memory addressing?
How many descriptors are accessible in the global descriptor table in the protected mode?
For an 80286 descriptor that contains a base address of A00000H and a limit of 1000H, what
starting and ending locations are addressed by this descriptor?
For a Core2 descriptor that contains a base address of 01000000H, a limit of 0FFFFH, and
G = 0, what starting and ending locations are addressed by this descriptor?
For a Core2 descriptor that contains a base address of 00280000H, a limit of 00010H, and
G = 1, what starting and ending locations are addressed by this descriptor?

76

CHAPTER 2

29. If the DS register contains 0020H in a protected mode system, which global descriptor table
entry is accessed?
30. If DS = 0103H in a protected mode system, the requested privilege level is _________.
31. If DS = 0105H in a protected mode system, which entry, table, and requested privilege
level are selected?
32. What is the maximum length of the global descriptor table in the Pentium 4 microprocessor?
33. Code a descriptor that describes a memory segment that begins at location 210000H and
ends at location 21001FH. This memory segment is a code segment that can be read. The
descriptor is for an 80286 microprocessor.
34. Code a descriptor that describes a memory segment that begins at location 03000000H and
ends at location 05FFFFFFH. This memory segment is a data segment that grows upward in
the memory system and can be written. The descriptor is for a Pentium 4 microprocessor.
35. Which register locates the global descriptor table?
36. How is the local descriptor table addressed in the memory system?
37. Describe what happens when a new number is loaded into a segment register when the
microprocessor is operated in the protected mode.
38. What are the program-invisible registers?
39. What is the purpose of the GDTR?
40. How many bytes are found in a memory page?
41. What register is used to enable the paging mechanism in the 80386, 80486, Pentium,
Pentium Pro, Pentium 4, and Core2 microprocessors?
42. How many 32-bit addresses are stored in the page directory?
43. Each entry in the page directory translates how much linear memory into physical memory?
44. If the microprocessor sends linear address 00200000H to the paging mechanism, which paging directory entry is accessed, and which page table entry is accessed?
45. What value is placed in the page table to redirect linear address 20000000H to physical
address 30000000H?
46. What is the purpose of the TLB located within the Pentium class microprocessor?
47. Using the Internet, write a short report that details the TLB. Hint: You might want to go to
the Intel Web site and search for information.
48. Locate articles about paging on the Internet and write a report detailing how paging is used
in a variety of systems.
49. What is the flat mode memory system?
50. A flat mode memory system in the current version of the 64-bit Pentium 4 and Core2 allow
these microprocessors to access _________ bytes of memory.

CHAPTER 3
Addressing Modes

INTRODUCTION
Efficient software development for the microprocessor requires a complete familiarity with
the addressing modes employed by each instruction. In this chapter, the MOV (move data)
instruction is used to describe the data-addressing modes. The MOV instruction transfers bytes
or words of data between two registers or between registers and memory in the 8086 through
the 80286. Bytes, words, or doublewords are transferred in the 80386 and above by a MOV.
In describing the program memory-addressing modes, the CALL and JUMP instructions
show how to modify the flow of the program.
The data-addressing modes include register, immediate, direct, register indirect, baseplus index, register-relative, and base relative-plus-index in the 8086 through the 80286 microprocessor. The 80386 and above also include a scaled-index mode of addressing memory data.
The program memory-addressing modes include program relative, direct, and indirect. This
chapter explains the operation of the stack memory so that the PUSH and POP instructions and
other stack operations will be understood.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1.
2.
3.
4.
5.
6.
7.
8.

Explain the operation of each data-addressing mode.
Use the data-addressing modes to form assembly language statements.
Explain the operation of each program memory-addressing mode.
Use the program memory-addressing modes to form assembly and machine language
statements.
Select the appropriate addressing mode to accomplish a given task.
Detail the difference between addressing memory data using real mode and protected mode
operation.
Describe the sequence of events that place data onto the stack or remove data from the
stack.
Explain how a data structure is placed in memory and used with software.

77

78

CHAPTER 3

3–1

DATA-ADDRESSING MODES
Because the MOV instruction is a very common and flexible instruction, it provides a basis for
the explanation of the data-addressing modes. Figure 3–1 illustrates the MOV instruction and
defines the direction of data flow. The source is to the right and the destination is to the left, next
to the opcode MOV. (An opcode, or operation code, tells the microprocessor which operation to
perform.) This direction of flow, which is applied to all instructions, is awkward at first. We naturally assume that things move from left to right, whereas here they move from right to left.
Notice that a comma always separates the destination from the source in an instruction. Also,
note that memory-to-memory transfers are not allowed by any instruction except for the MOVS
instruction.
In Figure 3–1, the MOV AX, BX instruction transfers the word contents of the source register (BX) into the destination register (AX). The source never changes, but the destination
always changes.1 It is crucial to remember that a MOV instruction always copies the source data
into the destination. The MOV never actually picks up the data and moves it. Also, note the flag
register remains unaffected by most data transfer instructions. The source and destination are
often called operands.
Figure 3–2 shows all possible variations of the data-addressing modes using the MOV
instruction. This illustration helps to show how each data-addressing mode is formulated with
the MOV instruction and also serves as a reference on data-addressing modes. Note that these are
the same data-addressing modes found with all versions of the Intel microprocessor, except for
the scaled-index-addressing mode, which is found only in the 80386 through the Core2. The RIP
relative addressing mode is not illustrated and is only available on the Pentium 4 and the Core2
when operated in the 64-bit mode. The data-addressing modes are as follows:
Register
addressing

Immediate
addressing

Register addressing transfers a copy of a byte or word from the source
register or contents of a memory location to the destination register or
memory location. (Example: The MOV CX, DX instruction copies the
word-sized contents of register DX into register CX.) In the 80386 and
above, a doubleword can be transferred from the source register or
memory location to the destination register or memory location.
(Example: The MOV ECX, EDX instruction copies the doublewordsized contents of register EDX into register ECX.) In the Pentium 4
operated in the 64-bit mode, any 64-bit register is also allowed. An
example is the MOV RDX, RCX instruction that transfers a copy of the
quadword contents of register RCX into register RDX.
Immediate addressing transfers the source, an immediate byte, word,
doubleword, or quadword of data, into the destination register or
memory location. (Example: The MOV AL, 22H instruction copies a
byte-sized 22H into register AL.) In the 80386 and above, a
doubleword of immediate data can be transferred into a register or

FIGURE 3–1 The MOV
instruction showing the source,
destination, and direction of
data flow.

MOV AX,BX

Destination
1The

Source

exceptions are the CMP and TEST instructions, which never change the destination. These instructions are
described in later chapters.

Address Generation

Destination

Type

Instruction

Source

Register

MOV AX,BX

Register
BX

Register
AX

Immediate

MOV CH,3AH

Data
3AH

Register
CH

Direct

MOV [1234H],AX

Register
AX

Register indirect

MOV [BX],CL

Register
CL

Base-plus-index

MOV [BX+SI],BP

Register
SP

Register relative

MOV CL,[BX+4]

Memory
address
10304H

DS × 10H + BX + 4
10000H + 0300H + 4

Register
CL

Base relative-plus-index

MOV ARRAY[BX+SI],DX

Register
DX

DS × 10H + ARRAY + BX + SI
10000H + 1000H + 0300H + 0200H

Memory
address
11500H

Scaled index

MOV [EBX+2 × ESI],AX

Register
AX

DS × 10H + EBX + 2 × ESI
10000H + 00000300H + 00000400H

Memory
address
10700H

DS × 10H + DISP
10000H + 1234H

DS × 10H + BX
10000H + 0300H

DS × 10H + BX + SI
10000H + 0300H + 0200H

Notes: EBX = 00000300H, ESI = 00000200H, ARRAY = 1000H, and DS = 1000H

FIGURE 3–2

8086–Core2 data-addressing modes.

Memory
address
11234H

Memory
address
10300H

Memory
address
10500H

79

80

CHAPTER 3

Direct
addressing

Register indirect
addressing

Base-plus-index
addressing

Register relative
addressing

memory location. (Example: The MOV EBX, 12345678H instruction
copies a doubleword-sized l2345678H into the 32-bit-wide EBX
register.) In 64-bit operation of the Pentium 4 or Core2, only a MOV
immediate instruction allows access to any location in the memory
using a 64-bit linear address.
Direct addressing moves a byte or word between a memory location
and a register. The instruction set does not support a memory-tomemory transfer, except with the MOVS instruction. (Example: The
MOV CX, LIST instruction copies the word-sized contents of
memory location LIST into register CX.) In the 80386 and above, a
doubleword-sized memory location can also be addressed. (Example:
The MOV ESI, LIST instruction copies a 32-bit number, stored in
four consecutive bytes of memory, from location LIST into register
ESI.) The direct memory instructions in the 64-bit mode use a full
64-bit linear address.
Register indirect addressing transfers a byte or word between a
register and a memory location addressed by an index or base register.
The index and base registers are BP, BX, DI, and S1. (Example: The
MOV AX, [BX] instruction copies the word-sized data from the data
segment offset address indexed by BX into register AX.) In the 80386
and above, a byte, word, or doubleword is transferred between a
register and a memory location addressed by any register: EAX, EBX,
ECX, EDX, EBP, EDI, or ESI. (Example: The MOV AL, [ECX]
instruction loads AL from the data segment offset address selected by
the contents of ECX.) In 64-bit mode, the indirect address remains 32
bits in size, which means this form of addressing at present only allows
access to 4G bytes of address space if the program operates in the 32bit compatible mode. In the full 64-bit mode, any address is accessed
using either a 64-bit address or the address contained in a register.
Base-plus-index addressing transfers a byte or word between a
register and the memory location addressed by a base register (BP or
BX) plus an index register (DI or SI). (Example: The MOV [BX+DI ],
CL instruction copies the byte-sized contents of register CL into the
data segment memory location addressed by BX plus DI.) In the
80386 and above, any two registers (EAX, EBX, ECX, EDX, EBP,
EDI, or ESI) may be combined to generate the memory address.
(Example: The MOV [EAX+EBX], CL instruction copies the bytesized contents of register CL into the data segment memory location
addressed by EAX plus EBX.)
Register relative addressing moves a byte or word between a register
and the memory location addressed by an index or base register plus a
displacement. (Example: MOV AX,[BX+4] or MOV AX,ARRAY[BX].
The first instruction loads AX from the data segment address formed by
BX plus 4. The second instruction loads AX from the data segment
memory location in ARRAY plus the contents of BX.) The 80386 and
above use any 32-bit register except ESP to address memory. (Example:
MOV AX,[ECX+4 ] or MOV AX,ARRAY[EBX]. The first instruction
loads AX from the data segment address formed by ECX plus 4. The
second instruction loads AX from the data segment memory location
ARRAY plus the contents of EBX.)

81

ADDRESSING MODES

Base relative-plusindex addressing

Scaled-index
addressing

RIP relative
addressing

Base relative-plus-index addressing transfers a byte or word between a
register and the memory location addressed by a base and an index
register plus a displacement. (Example: MOV AX, ARRAY[BX+DI]
or MOV AX, [BX+DI+4 ]. These instructions load AX from a data
segment memory location. The first instruction uses an address formed
by adding ARRAY, BX, and DI and the second by adding BX, DI, and
4.) In the 80386 and above, MOV EAX, ARRAY[EBX+ECX] loads
EAX from the data segment memory location accessed by the sum of
ARRAY, EBX, and ECX.
Scaled-index addressing is available only in the 80386 through the
Pentium 4 microprocessor. The second register of a pair of registers is
modified by the scale factor of 2×, 4×, or 8× to generate the operand
memory address. (Example: A MOV EDX, [EAX+4*EBX ] instruction
loads EDX from the data segment memory location addressed by EAX
plus four times EBX.) Scaling allows access to word (2× ), doubleword
(4× ), or quadword (8× ) memory array data. Note that a scaling factor
of 1* also exists, but it is normally implied and does not appear
explicitly in the instruction. The MOV AL, [EBX+ECX ] is an example
in which the scaling factor is a one. Alternately, the instruction can be
rewritten as MOV AL, [EBX+1*ECX ]. Another example is a MOV
AL, [2*EBX] instruction, which uses only one scaled register to
address memory.
This addressing mode is only available to the 64-bit extensions on the
Pentium 4 or Core2. This mode allows access to any location in the
memory system by adding a 32-bit displacement to the 64-bit contents
of the 64-bit instruction pointer. For example, if RIP = 1000000000H
and a 32-bit displacement is 300H, the location accessed is 1000000300H.
The displacement is signed so data located within ;2G from the
instruction is accessible by this addressing mode.

Register Addressing
Register addressing is the most common form of data addressing and, once the register names are
learned, is the easiest to apply. The microprocessor contains the following 8-bit register names
used with register addressing: AH, AL, BH, BL, CH, CL, DH, and DL. Also present are the following 16-bit register names: AX, BX, CX, DX, SP, BP, SI, and DI. In the 80386 and above, the
extended 32-bit register names are: EAX, EBX, ECX, EDX, ESP, EBP, EDI, and ESI. In the 64bit mode of the Pentium 4, the register names are: RAX, RBX, RCX, RDX, RSP, RBP, RDI, RSI,
and R8 through R15. With register addressing, some MOV instructions and the PUSH and POP
instructions also use the 16-bit segment register names (CS, ES, DS, SS, FS, and GS). It is
important for instructions to use registers that are the same size. Never mix an 8-bit register with
a 16-bit register, an 8-bit register with a 32-bit register, or a l6-bit register with a 32-bit register
because this is not allowed by the microprocessor and results in an error when assembled.
Likewise never mix 64-bit registers with any other size register. This is even true when a MOV
AX, AL (MOV EAX, AL) instruction may seem to make sense. Of course, the MOV AX, AL or
MOV EAX, AL instructions are not allowed because the registers are of different sizes. Note that
a few instructions, such as SHL DX, CL, are exceptions to this rule, as indicated in later chapters.
It is also important to note that none of the MOV instructions affect the flag bits. The flag bits are
normally modified by arithmetic or logic instructions.
Table 3–1 shows many variations of register move instructions. It is impossible to show all
combinations because there are too many. For example, just the 8-bit subset of the MOV instruction

82

CHAPTER 3

TABLE 3–1 Examples
of register-addressed
instructions.

Assembly Language

Size

Operation

MOV AL,BL

8 bits

Copies BL into AL

MOV CH,CL

8 bits

Copies CL into CH

MOV R8B,CL

8 bits

Copies CL to the byte portion of R8 (64-bit mode)

MOV R8B,CH

8 bits

Not allowed

MOV AX,CX

16 bits

Copies CX into AX

MOV SP,BP

16 bits

Copies BP into SP

MOV DS,AX

16 bits

Copies AX into DS

MOV BP,R10W

16 bits

Copies R10 into BP (64-bit mode)

MOV SI,DI

16 bits

Copies DI into SI

MOV BX,ES

16 bits

Copies ES into BX

MOV ECX,EBX

32 bits

Copies EBX into ECX

MOV ESP,EDX

32 bits

Copies EDX into ESP

MOV EDX,R9D

32 bits

Copies R9 into EDX (64-bit mode)

MOV RAX,RDX

64 bits

Copies RDX into RAX

MOV DS,CX

16 bits

Copies CX into DS

MOV ES,DS



Not allowed (segment-to-segment)

MOV BL,DX



Not allowed (mixed sizes)

MOV CS,AX



Not allowed (the code segment register may not
be the destination register)

has 64 different variations. A segment-to-segment register MOV instruction is about the only
type of register MOV instruction not allowed. Note that the code segment register is not normally
changed by a MOV instruction because the address of the next instruction is found by both
IP/EIP and CS. If only CS were changed, the address of the next instruction would be unpredictable. Therefore, changing the CS register with a MOV instruction is not allowed.
Figure 3–3 shows the operation of the MOV BX, CX instruction. Note that the source register’s contents do not change, but the destination register’s contents do change. This instruction
moves (copies) a l234H from register CX into register BX. This erases the old contents
(76AFH) of register BX, but the contents of CX remain unchanged. The contents of the destination register or destination memory location change for all instructions except the CMP and
TEST instructions. Note that the MOV BX, CX instruction does not affect the leftmost 16 bits
of register EBX.

FIGURE 3–3 The effect
of executing the MOV BX,
CX instruction at the point
just before the BX register
changes. Note that only the
rightmost 16 bits of register
EBX change.

Register array
EAX

EBX

2 2 3 4

7

6

A F

ECX

1 1 A C

1

2

3 4

1

2 3 4

83

ADDRESSING MODES

Example 3–1 shows a sequence of assembled instructions that copy various data between
8-, 16-, and 32-bit registers. As mentioned, the act of moving data from one register to another
changes only the destination register, never the source. The last instruction in this example
(MOV CS,AX) assembles without error, but causes problems if executed. If only the contents of
CS change without changing IP, the next step in the program is unknown and therefore causes the
program to go awry.
EXAMPLE 3–1
0000
0002
0004
0006
0009
000C
000F
0012
0014
0016

8B C3
8A CE
8A CD
66|8B
66|8B
66|8B
66|8B
8C C8
8E D8
8E C8

C3
D8
C8
D0

MOV
MOV
MOV
MOV
MOV
MOV
MOV
MOV
MOV
MOV

AX,BX
CL,DH
CL,CH
EAX,EBX
EBX,EAX
ECX,EAX
EDX,EAX
AX,CS
DS,AX
CS,AX

;copy
;copy
;copy
;copy
;copy
;copy
;copy
;copy

contents of BX into AX
contents of DH into CL
contents of CH into CL
contents of EBX into EAX
contents of EAX into EBX
contents of EAX into ECX
contents of EAX into EDX
CS into DS (two steps)

;copy AX into CS (causes problems)

Immediate Addressing
Another data-addressing mode is immediate addressing. The term immediate implies that the
data immediately follow the hexadecimal opcode in the memory. Also note that immediate data
are constant data, whereas the data transferred from a register or memory location are variable
data. Immediate addressing operates upon a byte or word of data. In the 80386 through the
Core2 microprocessors, immediate addressing also operates on doubleword data. The MOV
immediate instruction transfers a copy of the immediate data into a register or a memory location. Figure 3–4 shows the operation of a MOV EAX,13456H instruction. This instruction
copies the 13456H from the instruction, located in the memory immediately following the
hexadecimal opcode, into register EAX. As with the MOV instruction illustrated in Figure 3–3,
the source data overwrites the destination data.
In symbolic assembly language, the symbol # precedes immediate data in some assemblers.
The MOV AX,#3456H instruction is an example. Most assemblers do not use the # symbol, but
represent immediate data as in the MOV AX,3456H instruction. In this text, the # symbol is not
used for immediate data. The most common assemblers—Intel ASM, Microsoft MASM,2 and
Borland TASM3—do not use the # symbol for immediate data, but an older assembler used with
some Hewlett-Packard logic development system does, as may others.
As mentioned, the MOV immediate instruction under 64-bit operation can include a 64-bit
immediate number. An instruction such as MOV RAX,123456780A311200H is allowed in the
64-bit mode.
The symbolic assembler portrays immediate data in many ways. The letter H appends
hexadecimal data. If hexadecimal data begin with a letter, the assembler requires that the data
FIGURE 3–4 The operation
of the MOV EAX,3456H
instruction. This instruction
copies the immediate data
(13456H) into EAX.

Register array
EAX

6

2

9 1

EBX

2MASM
3TASM

3 3 3 3

Program

(MACRO assembler) is a trademark of Microsoft Corporation.
(Turbo assembler) is a trademark of Borland Corporation.

MOV EAX,13456H
13456H

84

CHAPTER 3

TABLE 3–2

Examples of immediate addressing using the MOV instruction.

Assembly Language

Size

Operation

MOV BL,44

8 bits

Copies 44 decimal (2CH) into BL

MOV AX,44H

16 bits

Copies 0044H into AX

MOV SI,0

16 bits

Copies 0000H into SI

MOV CH,100

8 bits

Copies 100 decimal (64H) into CH

MOV AL,’A’

8 bits

Copies ASCII A into AL

MOV AH,1

8 bits

Not allowed in 64-bit mode, but allowed in 32or 16-bit modes

MOV AX,’AB’

16 bits

Copies ASCII BA* into AX

MOV CL,11001110B

8 bits

Copies 11001110 binary into CL

MOV EBX,12340000H

32 bits

Copies 12340000H into EBX

MOV ESI,12

32 bits

Copies 12 decimal into ESI

MOV EAX,100B

32 bits

Copies 100 binary into EAX

MOV RCX,100H

64 bits

Copies 100H into RCX

*Note: This is not an error. The ASCII characters are stored as BA, so exercise care when using word-sized
pairs of ASCII characters.

start with a 0. For example, to represent a hexadecimal F2, 0F2H is used in assembly language.
In some assemblers (though not in MASM, TASM, or this text), hexadecimal data are represented with an ’h, as in MOV AX,#’h1234. Decimal data are represented as is and require no
special codes or adjustments. (An example is the 100 decimal in the MOV AL,100 instruction.)
An ASCII-coded character or characters may be depicted in the immediate form if the ASCII
data are enclosed in apostrophes. (An example is the MOV BH, ‘A’ instruction, which moves an
ASCII-coded letter A [41H] into register BH.) Be careful to use the apostrophe (‘) for ASCII
data and not the single quotation mark (‘). Binary data are represented if the binary number is
followed by the letter B, or, in some assemblers, the letter Y. Table 3–2 shows many different
variations of MOV instructions that apply immediate data.
Example 3–2 shows various immediate instructions in a short assembly language program
that places 0000H into the 16-bit registers AX, BX, and CX. This is followed by instructions that
use register addressing to copy the contents of AX into registers SI, DI, and BP. This is a complete program that uses programming models for assembly and execution with MASM. The
.MODEL TINY statement directs the assembler to assemble the program into a single code segment. The .CODE statement or directive indicates the start of the code segment; the .STARTUP
statement indicates the starting instruction in the program; and the .EXIT statement causes the
program to exit to DOS. The END statement indicates the end of the program file. This program
is assembled with MASM and executed with CodeView4 (CV) to view its execution. Note
that the most recent version of TASM will also accept MASM code without any changes. To
store the program into the system use the DOS EDIT program, Windows NotePad,5 or
Programmer’s WorkBench6 (PWB). Note that a TINY program always assembles as a command
(.COM) program.

4CodeView
5NotePad

is a registered trademark of Microsoft Corporation.
is a registered trademark of Microsoft Corporation.

6Programmer’s

WorkBench is a registered trademark of Microsoft Corporation.

85

ADDRESSING MODES

EXAMPLE 3–2
0000

.MODEL TINY
.CODE
.STARTUP

;choose single segment model
;start of code segment
;start of program

0100 B8 0000
0103 BB 0000
0106 B9 0000

MOV AX,0
MOV BX,0
MOV CX,0

;place 0000H into AX
;place 0000H into BX
;place 0000H into CX

0109 8B F0
010B 8B F8
010D 8B E8

MOV SI,AX
MOV DI,AX
MOV BP,AX

;copy AX into SI
;copy AX into DI
;copy AX into BP

.EXIT
END

;exit to DOS
;end of program

Each statement in an assembly language program consists of four parts or fields, as illustrated in Example 3–3. The leftmost field is called the label and it is used to store a symbolic
name for the memory location that it represents. All labels must begin with a letter or one of the
following special characters: @, $, -, or ? A label may be of any length from 1 to 35 characters.
The label appears in a program to identify the name of a memory location for storing data and for
other purposes that are explained as they appear. The next field to the right is called the opcode
field; it is designed to hold the instruction, or opcode. The MOV part of the move data instruction
is an example of an opcode. To the right of the opcode field is the operand field, which contains
information used by the opcode. For example, the MOV AL,BL instruction has the opcode MOV
and operands AL and BL. Note that some instructions contain between zero and three operands.
The final field, the comment field, contains a comment about an instruction or a group of instructions. A comment always begins with a semicolon (;).
EXAMPLE 3–3
Label

Opcode

Operand

Comment

DATA1
DATA2

DB
DW

23H
1000H

;define DATA1 as a byte of 23H
;define DATA2 as a word of 1000H

START:

MOV
MOV
MOV

AL,BL
BH,AL
CX,200

;copy BL into AL
;copy AL into BH
;copy 200 into CX

When the program is assembled and the list (.LST) file is viewed, it appears as the program
listed in Example 3–2. The hexadecimal number at the far left is the offset address of the instruction
or data. This number is generated by the assembler. The number or numbers to the right of the offset address are the machine-coded instructions or data that are also generated by the assembler. For
example, if the instruction MOV AX,0 appears in a file and it is assembled, it appears in offset
memory location 0100 in Example 3–2. Its hexadecimal machine language form is B8 0000. The
B8 is the opcode in machine language and the 0000 is the 16-bit-wide data with a value of zero.
When the program was written, only the MOV AX,0 was typed into the editor; the assembler generated the machine code and addresses, and stored the program in a file with the extension .LST.
Note that all programs shown in this text are in the form generated by the assembler.
EXAMPLE 3–4
int MyFunction(int temp)
{
_asm
{
mov eax,temp
add eax,20h
mov temp,eax
}
return temp;
// return a 32-bit integer
}

86

CHAPTER 3

Programs are also written using the inline assembler in some Visual C++ programs.
Example 3–4 shows a function in a Visual C++ program that includes some code written with the
inline assembler. This function adds 20H to the number returned by the function. Notice that the
assembly code accesses C++ variable temp and all of the assembly code is placed in an _asm code
block. Many examples in this text are written using the inline assembler within a C++ program.

Direct Data Addressing
Most instructions can use the direct data-addressing mode. In fact, direct data addressing is applied
to many instructions in a typical program. There are two basic forms of direct data addressing:
(1) direct addressing, which applies to a MOV between a memory location and AL, AX, or EAX,
and (2) displacement addressing, which applies to almost any instruction in the instruction set.
In either case, the address is formed by adding the displacement to the default data segment
address or an alternate segment address. In 64-bit operation, the direct-addressing instructions
are also used with a 64-bit linear address, which allows access to any memory location.

Direct Addressing. Direct addressing with a MOV instruction transfers data between a memory location, located within the data segment, and the AL (8-bit), AX (l6-bit), or EAX (32-bit)
register. A MOV instruction using this type of addressing is usually a 3-byte long instruction. (In
the 80386 and above, a register size prefix may appear before the instruction, causing it to exceed
3 bytes in length.)
The MOV AL,DATA instruction, as represented by most assemblers, loads AL from the
data segment memory location DATA (1234H). Memory location DATA is a symbolic memory
location, while the 1234H is the actual hexadecimal location. With many assemblers, this instruction is represented as a MOV AL,[1234H]7 instruction. The [1234H] is an absolute memory location that is not allowed by all assembler programs. Note that this may need to be formed as MOV
AL, DS:[1234H] with some assemblers, to show that the address is in the data segment. Figure 3–5
shows how this instruction transfers a copy of the byte-sized contents of memory location 11234H
into AL. The effective address is formed by adding 1234H (the offset address) and 10000H (the
data segment address of 1000H times 10H) in a system operating in the real mode.
Table 3–3 lists the direct-addressed instructions. These instructions often appear in programs,
so Intel decided to make them special 3-byte-long instructions to reduce the length of programs. All
other instructions that move data from a memory location to a register, called displacementaddressed instructions, require 4 or more bytes of memory for storage in a program.
Displacement Addressing. Displacement addressing is almost identical to direct addressing,
except that the instruction is 4 bytes wide instead of 3. In the 80386 through the Pentium 4,
Memory

11235H
EAX

AH

AL

8AH

8AH

8 A

EBX

11233H

ECX

FIGURE 3–5
7This

11234H

11232H

The operation of the MOV AL,[1234H] instruction when DS = 1000H.

form may be typed into a MASM program, but it most often appears when the debugging tool is executed.

87

ADDRESSING MODES

TABLE 3–3 Direct
addressed instructions
using EAX, AX, and AL
and RAX in 64-bit mode.

Assembly Language

Size

Operation

MOV AL,NUMBER

8 bits

Copies the byte contents of data segment
memory location NUMBER into AL

MOV AX,COW

16 bits

Copies the word contents of data segment
memory location COW into AX

MOV EAX,WATER*

32 bits

Copies the doubleword contents of data
segment location WATER into EAX

MOV NEWS,AL

8 bits

Copies AL into byte memory location NEWS

MOV THERE,AX

16 bits

Copies AX into word memory location THERE

MOV HOME,EAX*

32 bits

Copies EAX into doubleword memory location
HOME

MOV ES:[2000H],AL

8 bits

Copies AL into extra segment memory at
offset address 2000H

MOV AL,MOUSE

8 bits

Copies the contents of location MOUSE into AL;
in 64-bit mode MOUSE can be any address

MOV RAX,WHISKEY

64 bits

Copies 8 bytes from memory location WHISKEY
into RAX

*Note: The 80386–Pentium 4 at times use more than 3 bytes of memory for 32-bit instructions.

this instruction can be up to 7 bytes wide if both a 32-bit register and a 32-bit displacement are
specified. This type of direct data addressing is much more flexible because most instructions
use it.
If the operation of the MOV CL,DS:[1234H] instruction is compared to that of the MOV
AL,DS:[1234H] instruction of Figure 3–5, we see that both basically perform the same operation
except for the destination register (CL versus AL). Another difference only becomes apparent
upon examining the assembled versions of these two instructions. The MOV AL,DS:[1234H]
instruction is 3 bytes long and the MOV CL,DS:[1234H] instruction is 4 bytes long, as illustrated
in Example 3–5. This example shows how the assembler converts these two instructions into
hexadecimal machine language. You must include the segment register DS: in this example,
before the [offset] part of the instruction. You may use any segment register, but in most cases,
data are stored in the data segment, so this example uses DS:[1234H].
EXAMPLE 3–5
0000 A0 1234 R
0003 BA 0E 1234 R

MOV AL,DS:[1234H]
MOV CL,DS:[1234H]

Table 3–4 lists some MOV instructions using the displacement form of direct addressing.
Not all variations are listed because there are many MOV instructions of this type. The segment
registers can be stored or loaded from memory.
Example 3–6 shows a short program using models that address information in the data segment.
Note that the data segment begins with a .DATA statement to inform the assembler where the data
segment begins. The model size is adjusted from TINY, as shown in Example 3–3, to SMALL so that
a data segment can be included. The SMALL model allows one data segment and one code segment.
The SMALL model is often used whenever memory data are required for a program. A SMALL
model program assembles as an execute (.EXE) program file. Notice how this example allocates
memory locations in the data segment by using the DB and DW directives. Here the .STARTUP statement not only indicates the start of the code, but it also loads the data segment register with the

88

CHAPTER 3

TABLE 3–4

Examples of direct data addressing using a displacement.

Assembly Language

Size

Operation

MOV CH,DOG

8 bits

Copies the byte contents of data segment memory
location DOG into CH

MOV CH,DS:[1000H]*

8 bits

Copies the byte contents of data segment memory offset
address 1000H into CH

MOV ES,DATA6

16 bits

Copies the word contents of data segment memory
location DATA6 into ES

MOV DATA7,BP

16 bits

Copies BP into data segment memory location DATA7

MOV NUMBER,SP

16 bits

Copies SP into data segment memory location NUMBER

MOV DATA1,EAX

32 bits

Copies EAX into data segment memory location DATA1

MOV EDI,SUM1

32 bits

Copies the doubleword contents of data segment memory
location SUM1 into EDI

*This form of addressing is seldom used with most assemblers because an actual numeric offset address is
rarely accessed.

segment address of the data segment. If this program is assembled and executed with CodeView, the
instructions can be viewed as they execute and change registers and memory locations.
EXAMPLE 3–6
0000
0000
0001
0002
0004

10
00
0000
AAAA

0000

0017
001A
001E
0021

A00000 R
8A 26 0001 R
A3 0002 R
8B 1E 0004 R

DATA1
DATA2
DATA3
DATA4

.MODEL SMALL
.DATA

;choose small model
;start data segment

DB
DB
DW
DW

;place
;place
;place
;place

10H
0
0
0AAAAH

10H into DATA1
00H into DATA2
0000H into DATA3
AAAAH into DATA4

.CODE
.STARTUP

;start code segment
;start program

MOV
MOV
MOV
MOV

;copy
;copy
;copy
;copy

AL,DATA1
AH,DATA2
DATA3,AX
BX,DATA4

.EXIT;
END;

DATA1 into AL
DATA2 into AH
AX into DATA3
DATA4 into BX

exit to DOS
end program listing

Register Indirect Addressing
Register indirect addressing allows data to be addressed at any memory location through an offset
address held in any of the following registers: BP, BX, DI, and SI. For example, if register BX contains 1000H and the MOV AX,[BX] instruction executes, the word contents of data segment offset
address 1000H are copied into register AX. If the microprocessor is operated in the real mode and
DS = 0100H, this instruction addresses a word stored at memory bytes 2000H and 2001H, and
transfers it into register AX (see Figure 3–6). Note that the contents of 2000H are moved into AL
and the contents of 2001H are moved into AH. The [ ] symbols denote indirect addressing in
assembly language. In addition to using the BP, BX, DI, and SI registers to indirectly address memory, the 80386 and above allow register indirect addressing with any extended register except ESP.
Some typical instructions using indirect addressing appear in Table 3–5. If a Pentium 4 or Core2 is
available that operates in the 64-bit mode, any 64-bit register is used to hold a 64-bit linear address.
In the 64-bit mode, the segment registers serve no purpose in addressing a location in the flat model.

89

ADDRESSING MODES

00002002
EAX

AH
3 4

AL
1 2

EBX

1 0

0 0

3412
1000

+

2000

3 4

00002001

1 2

00002000

ECX

00001002
00001001

CS
0 1 0 0

DS

*1000
00001000

*After DS is appended with a 0.

FIGURE 3–6 The operation of the MOV AX,[BX] instruction when BX = 1000H and DS = 0100H.
Note that this instruction is shown after the contents of memory are transferred to AX.

The data segment is used by default with register indirect addressing or any other addressing mode that uses BX, DI, or SI to address memory. If the BP register addresses memory, the
stack segment is used by default. These settings are considered the default for these four index and
base registers. For the 80386 and above, EBP addresses memory in the stack segment by default;
EAX, EBX, ECX, EDX, EDI, and ESI address memory in the data segment by fault. When using a
32-bit register to address memory in the real mode, the contents of the 32-bit register must never
TABLE 3–5

Examples of register indirect addressing.

Assembly Language

Size

Operation

MOV CX,[BX]

16 bits

Copies the word contents of the data segment memory
location addressed by BX into CX

MOV [BP],DL*

8 bits

Copies DL into the stack segment memory location
addressed by BP

MOV [DI],BH

8 bits

Copies BH into the data segment memory location
addressed by DI

MOV [DI],[BX]



MOV AL,[EDX]

8 bits

Copies the byte contents of the data segment memory
location addressed by EDX into AL

MOV ECX,[EBX]

32 bits

Copies the doubleword contents of the data segment
memory location addressed by EBX into ECX

MOV RAX,[RDX]

64 bits

Copies the quadword contents of the memory location
address by the linear address located in RDX into RAX
(64-bit mode)

Memory-to-memory transfers are not allowed except with
string instructions

*Note: Data addressed by BP or EBP are by default in the stack segment, while other indirect addressed
instructions use the data segment by default.

90

CHAPTER 3

exceed 0000FFFFH. In the protected mode, any value can be used in a 32-bit register that is used
to indirectly address memory, as long as it does not access a location outside of the segment, as
dictated by the access rights byte. An example 80386–Pentium 4 instruction is MOV
EAX,[EBX]. This instruction loads EAX with the doubleword-sized number stored at the data
segment offset address indexed by EBX. In the 64-bit mode, the segment registers are not used in
the address calculation because the register contains the actual linear memory address.
In some cases, indirect addressing requires specifying the size of the data. The size is specified by the special assembler directive BYTE PTR, WORD PTR, DWORD PTR, or QWORD
PTR. These directives indicate the size of the memory data addressed by the memory pointer
(PTR). For example, the MOV AL,[DI] instruction is clearly a byte-sized move instruction, but
the MOV [DI],10H instruction is ambiguous. Does the MOV [DI],10H instruction address a
byte-, word-, doubleword-, or quadword-sized memory location? The assembler can’t determine
the size of the 10H. The instruction MOV BYTE PTR [DI],10H clearly designates the location
addressed by DI as a byte-sized memory location. Likewise, the MOV DWORD PTR [DI],10H
clearly identifies the memory location as doubleword-sized. The BYTE PTR, WORD PTR,
DWORD PTR, and QWORD PTR directives are used only with instructions that address a memory location through a pointer or index register with immediate data, and for a few other instructions that are described in subsequent chapters. Another directive that is occasionally used is the
QWORD PTR, where a QWORD is a quadword (64-bits mode). If programs are using the SIMD
instructions, the OWORD PTR, an octal word, is also used to represent a 128-bit-wide number.
Indirect addressing often allows a program to refer to tabular data located in the memory
system. For example, suppose that you must create a table of information that contains 50 samples taken from memory location 0000:046C. Location 0000:046C contains a counter in DOS
that is maintained by the personal computer’s real-time clock. Figure 3–7 shows the table and the
BX register used to sequentially address each location in the table. To accomplish this task, load
the starting location of the table into the BX register with a MOV immediate instruction. After
initializing the starting address of the table, use register indirect addressing to store the 50 samples sequentially.
The sequence shown in Example 3–7 loads register BX with the starting address of the table
and it initializes the count, located in register CX, to 50. The OFFSET directive tells the assembler
to load BX with the offset address of memory location TABLE, not the contents of TABLE. For
example, the MOV BX,DATAS instruction copies the contents of memory location DATAS into BX,
while the MOV BX,OFFSET DATAS instruction copies the offset address DATAS into BX. When
the OFFSET directive is used with the MOV instruction, the assembler calculates the offset address
and then uses a MOV immediate instruction to load the address in the specified 16-bit register.
FIGURE 3–7 An array
(TABLE) containing 50 bytes
that are indirectly addressed
through register BX.

Memory

Table + 49

Table + 2
Table + 1
EBX

0 0 0 0

T A B L E

Table

91

ADDRESSING MODES

EXAMPLE 3–7
.MODEL SMALL
.DATA

0000
0000 0032 [
0000
]
0000
0017
001A
001C
001F
0022
0022
0026
0028
0029
002A

B8
8E
B8
B9

0000
C0
0000 R
0032

26:A1 046C
89 07
43
43
E2 F6

DATAS

DW

;select small model
;start data segment

50 DUP(?) ;setup array of 50 words

.CODE
.STARTUP
MOV AX,0
MOV ES,AX
MOV BX,OFFSET DATAS
MOV CX,50
AGAIN:
MOV AX,ES:[046CH]
MOV [BX],AX
INC BX
INC BX
LOOP AGAIN
.EXIT
END

;start code segment
;start program
;address segment 0000 with ES
;address DATAS array with BX
;load counter with 50
;get clock value
;save clock value in DATAS
;increment BX to next element
;repeat 50 times
;exit to DOS
;end program listing

Once the counter and pointer are initialized, a repeat-until CX = 0 loop executes. Here data
are read from extra segment memory location 46CH with the MOV AX,ES:[046CH] instruction
and stored in memory that is indirectly addressed by the offset address located in register BX.
Next, BX is incremented (1 is added to BX) twice to address the next word in the table. Finally,
the LOOP instruction repeats the LOOP 50 times. The LOOP instruction decrements (subtracts 1
from) the counter (CX); if CX is not zero, LOOP causes a jump to memory location AGAIN. If
CX becomes zero, no jump occurs and this sequence of instructions ends. This example copies the
most recent 50 values from the clock into the memory array DATAS. This program will often
show the same data in each location because the contents of the clock are changed only 18.2 times
per second. To view the program and its execution, use the CodeView program. To use CodeView,
type CV XXXX.EXE, where XXXX.EXE is the name of the program that is being debugged. You
can also access it as DEBUG from the Programmer’s WorkBench program under the RUN menu.
Note that CodeView functions only with .EXE or .COM files. Some useful CodeView switches
are /50 for a 50-line display and /S for use of high-resolution video displays in an application. To
debug the file TEST.COM with 50 lines, type CV /50 /S TEST.COM at the DOS prompt.

Base-Plus-Index Addressing
Base-plus-index addressing is similar to indirect addressing because it indirectly addresses memory data. In the 8086 through the 80286, this type of addressing uses one base register (BP or
BX) and one index register (DI or SI) to indirectly address memory. The base register often holds
the beginning location of a memory array, whereas the index register holds the relative position
of an element in the array. Remember that whenever BP addresses memory data, both the stack
segment register and BP generate the effective address.
In the 80386 and above, this type of addressing allows the combination of any two 32-bit
extended registers except ESP. For example, the MOV DL,[EAX+EBX ] instruction is an example using EAX (as the base) plus EBX (as the index). If the EBP register is used, the data are
located in the stack segment instead of in the data segment.

Locating Data with Base-Plus-Index Addressing. Figure 3–8 shows how data are addressed by
the MOV DX,[BX+DI ] instruction when the microprocessor operates in the real mode. In this
example, BX = 1000H, DI = 0010H, and DS = 0100H, which translate into memory address
02010H. This instruction transfers a copy of the word from location 02010H into the DX register.

92

CHAPTER 3
Memory
02015H
EAX

02014H

EBX

1 0

0 0

02013H
02012H

ECX
EDX

A B

0 3

AB03

A B

02011H

0 3

02010H
0200FH

ESP
EBP
1000H
ESI
2010H

0010H
EDI

+

0 0 1 0

1010H

+
1000H

DS × 10H

FIGURE 3–8 An example showing how the base-plus-index addressing mode functions for the MOV DX,[BX +DI]
instruction. Notice that memory address 02010H is accessed because DS = 0100H, BX = 100H, and DI = 0010H.

Table 3–6 lists some instructions used for base-plus-index addressing. Note that the Intel assembler requires that this addressing mode appear as [BX][DI] instead of [BX+DI ]. The MOV
DX,[BX+DI ] instruction is MOV DX,[BX][DI] for a program written for the Intel ASM assembler. This text uses the first form in all example programs, but the second form can be used in
many assemblers, including MASM from Microsoft. Instructions like MOV DI,[BX+DI ] will
assemble, but will not execute correctly.

Locating Array Data Using Base-Plus-Index Addressing. A major use of the base-plus-index
addressing mode is to address elements in a memory array. Suppose that the elements in an array
TABLE 3–6

Examples of base-plus-index addressing.

Assembly Language

Size

Operation

MOV CX,[BX+DI]

16 bits

Copies the word contents of the data segment memory
location addressed by BX plus DI into CX

MOV CH,[BP+SI]

8 bits

Copies the byte contents of the stack segment memory
location addressed by BP plus SI into CH

MOV [BX+SI],SP

16 bits

Copies SP into the data segment memory location
addressed by BX plus SI

MOV [BP+DI],AH

8 bits

Copies AH into the stack segment memory location
addressed by BP plus DI

MOV CL,[EDX+EDI]

8 bits

Copies the byte contents of the data segment memory
location addressed by EDX plus EDI into CL

MOV [EAX+EBX],ECX

32 bits

Copies ECX into the data segment memory location
addressed by EAX plus EBX

MOV [RSI+RBX],RAX

64 bit

Copies RAX into the linear memory location addressed
by RSI plus RBX (64-bit mode)

93

ADDRESSING MODES
Memory

ARRAY + 5
DI

ARRAY + 4
ARRAY + 3

Element

ARRAY + 2

BX

ARRAY + 1
ARRAY

ARRAY

FIGURE 3–9 An example of the base-plus-index addressing mode. Here an element (DI) of an
ARRAY (BX) is addressed.

located in the data segment at memory location ARRAY must be accessed. To accomplish this,
load the BX register (base) with the beginning address of the array and the DI register (index)
with the element number to be accessed. Figure 3–9 shows the use of BX and DI to access an element in an array of data.
A short program, listed in Example 3–8, moves array element 10H into array element 20H.
Notice that the array element number, loaded into the DI register, addresses the array element. Also
notice how the contents of the ARRAY have been initialized so that element 10H contains 29H.
EXAMPLE 3–8
.MODEL SMALL
.DATA
ARRAY DB 16 DUP(?)

0000
0000 0010 [

;select small model
;start data segment
;setup array of 16 bytes

00
]
0010 29
0011 001E [

DB 29H
DB 20 dup(?)

;element 10H

.CODE
.STARTUP

;start code segment

MOV
MOV
MOV
MOV
MOV

;address ARRAY
;address element 10H
;get element 10H
;address element 20H
;save in element 20H

00
]
0000

0017
001A
001D
001F
0022

B8
BF
8A
BF
88

0000 R
0010
01
0020
01

.EXIT
END

BX,OFFSET ARRAY
DI,10H
AL,[BX+DI]
DI,20H
[BX+DI],AL

;exit to DOS
;end program

Register Relative Addressing
Register relative addressing is similar to base-plus-index addressing and displacement
addressing. In register relative addressing, the data in a segment of memory are addressed by

94

CHAPTER 3

FIGURE 3–10 The operation
of the MOV AX, [BX+1000H]
instructon, when BX = 0100H
and DS = 0200H.

Memory

Register array
EAX

2222

A0

76

EBX

0000

01

00

A076

A 0

03101H

7 6

03100H

0100H
1000H

+

1100H
DS × 10H

+

2000H

3100H

adding the displacement to the contents of a base or an index register (BP, BX, DI, or SI).
Figure 3–10 shows the operation of the MOV AX,[BX+1000H] instruction. In this example,
BX = 0100H and DS = 0200H, so the address generated is the sum of DS * 0H, BX, and the
displacement of 1000H, which addresses location 03100H. Remember that BX, DI, or SI
addresses the data segment and BP addresses the stack segment. In the 80386 and above, the
displacement can be a 32-bit number and the register can be any 32-bit register except the ESP
register. Remember that the size of a real mode segment is 64K bytes long. Table 3–7 lists a
few instructions that use register relative addressing
The displacement is a number added to the register within the [ ], as in the MOV
AL,[DI+2] instruction, or it can be a displacement is subtracted from the register, as in MOV
AL,[SI–l]. A displacement also can be an offset address appended to the front of the [ ], as in
MOV AL,DATA[DI]. Both forms of displacements also can appear simultaneously, as in the
MOV AL,DATA[DI+3 ] instruction. Both forms of the displacement add to the base or base plus
index register within the [ ] symbols. In the 8086–80286 microprocessors, the value of the displacement is limited to a 16-bit signed number with a value ranging between +32,767 (7FFFH)
TABLE 3–7

Examples of register relative addressing.

Assembly Language

Size

Operation

MOV AX,[DI+100H]

16 bits

Copies the word contents of the data segment memory location
addressed by DI plus 100H into AX

MOV ARRAY[SI],BL

8 bits

Copies BL into the data segment memory location addressed by
ARRAY plus SI

MOV LIST[SI+2],CL

8 bits

Copies CL into the data segment memory location addressed by the
sum of LIST, SI, and 2

MOV DI,SET_IT[BX]

16 bits

Copies the word contents of the data segment memory location
addressed by SET_IT plus BX into DI

MOV DI,[EAX+10H]

16 bits

Copies the word contents of the data segment location addressed by
EAX plus 10H into DI

MOV ARRAY[EBX],EAX

32 bits

Copies EAX into the data segment memory location addressed by
ARRAY plus EBX

MOV ARRAY[RBX],AL

8 bits

Copies AL into the memory location ARRAY plus RBX (64-bit mode)

MOV ARRAY[RCX],EAX

32 bits

Copies EAX into memory location ARRAY plus RCX (64-bit mode)

95

ADDRESSING MODES
Memory

ARRAY + 6
ARRAY + 5
ARRAY + 4
ARRAY + 3

Element

ARRAY + 2

Displacement

ARRAY + 1
ARRAY

ARRAY

FIGURE 3–11 Register relative addressing used to address an element of ARRAY. The displacement addresses the start of ARRAY, and DI accesses an element.

and –32,768 (8000H); in the 80386 and above, a 32-bit displacement is allowed with a value
ranging between + 2,147,483,647 (7FFFFFFFH) and -2,147,483,648 (80000000H).

Addressing Array Data with Register Relative. It is possible to address array data with register
relative addressing, such as one does with base-plus-index addressing. In Figure 3–11, register
relative addressing is illustrated with the same example as for base-plus-index addressing.
This shows how the displacement ARRAY adds to index register DI to generate a reference to an
array element.
Example 3–9 shows how this new addressing mode can transfer the contents of array element 10H into array element 20H. Notice the similarity between this example and Example 3–8.
The main difference is that, in Example 3–9, register BX is not used to address memory ARRAY;
instead, ARRAY is used as a displacement to accomplish the same task.
EXAMPLE 3–9
0000
0000 0010 [

ARRAY

.MODEL SMALL
.DATA
DB 16 dup(?)

;select small model
;start data segment
;setup ARRAY

DB
DB

;element 10H

00
]
0010 29
0011 001E [

29
30 dup(?)

00
]
0000
0017
001A
001E
0021

BF
8A
BF
88

0010
85 0000 R
0020
85 0000 R

.CODE
.STARTUP
MOV DI,10H
MOV AL,ARRAY[DI]
MOV DI,20H
MOV ARRAY[DI],AL
.EXIT
END

;start code segment
;start program
;address element 10H
;get ARRAY element 10H
;address element 20H
;save it in element 20H
;exit to DOS
;end of program

96

CHAPTER 3
Memory
Register array
EAX

A 3

1 6

EBX

0 0

2 0

A316

A 3

10131H

1 6

10130H

ECX
EDX
0020H

ESP
EBP
ESI

0030H 0130H
0 0 1 0

+
0010H

+

+
10000H

10130H

0100H DS × 10H

FIGURE 3–12 An example of base relative-plus-index addressing using a MOV AX,[BX+SI+100H]
instruction. Note: DS = 1000H.

Base Relative-Plus-Index Addressing
The base relative-plus-index addressing mode is similar to base-plus-index addressing, but it
adds a displacement, besides using a base register and an index register, to form the memory
address. This type of addressing mode often addresses a two-dimensional array of memory data.

Addressing Data with Base Relative-Plus-Index. Base relative-plus-index addressing is the
least-used addressing mode. Figure 3–12 shows how data are referenced if the instruction executed by the microprocessor is MOV AX,[BX+SI+100H ]. The displacement of 100H adds to
BX and SI to form the offset address within the data segment. Registers BX = 0020H,
SI = 0100H, and DS = 1000H, so the effective address for this instruction is 10130H—the sum
of these registers plus a displacement of 100H. This addressing mode is too complex for frequent
use in programming. Some typical instructions using base relative-plus-index addressing appear
in Table 3–8. Note that with the 80386 and above, the effective address is generated by the sum
of two 32-bit registers plus a 32-bit displacement.

TABLE 3–8

Example base relative-plus-index instructions.

Assembly Language

Size

Operation

MOV DH,[BX+DI+20H]

8 bits

Copies the byte contents of the data segment memory location
addressed by the sum of BX, DI and 20H into DH

MOV AX,FILE[BX+DI]

16 bits

Copies the word contents of the data segment memory location
addressed by the sum of FILE, BX and DI into AX

MOV LIST[BP+DI],CL

8 bits

Copies CL into the stack segment memory location addressed
by the sum of LIST, BP, and DI

MOV LIST[BP+SI+4],DH

8 bits

Copies DH into the stack segment memory location addressed
by the sum of LIST, BP, SI, and 4

MOV EAX,FILE[EBX+ECX+2]

32 bits

Copies the doubleword contents of the memory location
addressed by the sum of FILE, EBX, ECX, and 2 into EAX

97

ADDRESSING MODES

FIGURE 3–13 Base relativeplus-index addressing used to
access a FILE that contains
multiple records (REC).

Memory

EDI

Element
REC C

REC B

EBX

REC C
REC A

Displacement
FILE

Addressing Arrays with Base Relative-Plus-Index. Suppose that a file of many records exists in
memory and each record contains many elements. The displacement addresses the file, the base
register addresses a record, and the index register addresses an element of a record. Figure 3–13
illustrates this very complex form of addressing.
Example 3–10 provides a program that copies element 0 of record A into element 2
of record C by using the base relative-plus-index mode of addressing. This example FILE
contains four records and each record contains 10 elements. Notice how the THIS BYTE
statement is used to define the label FILE and RECA as the same memory location.
EXAMPLE 3–10
0000
0000 = 0000
0000 000A [

FILE
RECA

.MODEL SMALL
.DATA
EQU THIS BYTE
DB 10 dup(?)

;select small model
;start data segment
;assign FILE to this byte
;10 bytes for record A

RECB

DB

10 dup(?)

;10 bytes for record B

RECC

DB

10 dup(?)

;10 bytes for record C

RECD

DB

10 dup(?)

;10 bytes for record D

00
]
000A 000A [
00
]
0014 000A [
00
]
001E 000A [
00
]
0000
0017
001A
001D
0021
0024
0027

BB
BF
8A
BB
BF
88

0000 R
0000
81 0000 R
0014 R
0002
81 0000 R

.CODE
.STARTUP
MOV BX,OFFSET RECA
MOV DI,0
MOV AL,FILE[BX+DI]
MOV BX,OFFSET RECC
MOV DI,2
MOV FILE[BX+DI],AL
.exit
end

;start code segment
;start program
;address record A
;address element 0
;get data
;address record C
;address element 2
;save data
;exit to DOS
;end of program

98
TABLE 3–9

CHAPTER 3

Examples of scaled-index addressing.

Assembly Language

Size

Operation

MOV EAX,[EBX+4*ECX]

32 bits

Copies the doubleword contents of the data segment memory
location addressed by the sum of 4 times ECX plus EBX into EAX

MOV [EAX+2*EDI+100H],CX

16 bits

Copies CX into the data segment memory location addressed by the
sum of EAX, 100H, and 2 times EDI

MOV AL,[EBP+2*EDI+2]

8 bits

Copies the byte contents of the stack segment memory location
addressed by the sum of EBP, 2, and 2 times EDI into AL

MOV EAX,ARRAY[4*ECX]

32 bits

Copies the doubleword contents of the data segment memory
location addressed by the sum of ARRAY and 4 times ECX into EAX

Scaled-Index Addressing
Scaled-index addressing is the last type of data-addressing mode discussed. This data-addressing
mode is unique to the 80386 through the Core2 microprocessors. Scaled-index addressing uses
two 32-bit registers (a base register and an index register) to access the memory. The second
register (index) is multiplied by a scaling factor. The scaling factor can be 1×, 2×, 4×, or 8× .
A scaling factor of 1 × is implied and need not be included in the assembly language instruction
(MOV AL,[EBX+ECX]). A scaling factor of 2× is used to address word-sized memory arrays,
a scaling factor of 4× is used with doubleword-sized memory arrays, and a scaling factor of
8× is used with quadword-sized memory arrays.
An example instruction is MOV AX,[EDI+2*ECX]. This instruction uses a scaling factor
of 2× , which multiplies the contents of ECX by 2 before adding it to the EDI register to form the
memory address. If ECX contains a 00000000H, word-sized memory element 0 is addressed; if
ECX contains a 00000001H, word-sized memory element 1 is accessed, and so forth. This scales
the index (ECX) by a factor of 2 for a word-sized memory array. Refer to Table 3–9 for some
examples of scaled-index addressing. As you can imagine, there are an extremely large number
of the scaled-index addressed register combinations. Scaling is also applied to instructions that
use a single indirect register to access memory. The MOV EAX,[ 4*EDI] is a scaled-index
instruction that uses one register to indirectly address memory. In the 64-bit mode, an instruction
such as MOV RAX,[8*RDI] might appear in a program.
Example 3–11 shows a sequence of instructions that uses scaled-index addressing to access
a word-sized array of data called LIST. Note that the offset address of LIST is loaded into register
EBX with the MOV EBX,OFFSET LIST instruction. Once EBX addresses array LIST, the elements (located in ECX) of 2, 4, and 7 of this word-wide array are added, using a scaling factor of
2 to access the elements. This program stores the 2 at element 2 into elements 4 and 7. Also notice
the .386 directive to select the 80386 microprocessor. This directive must follow the .MODEL
statement for the assembler to process 80386 instructions for DOS. If the 80486 is in use, the .486
directive appears after the .MODEL statement; if the Pentium is in use, then use .586; and if the
Pentium Pro, Pentium II, Pentium III, Pentium 4, or Core2 is in use, then use the .686 directive. If
the microprocessor selection directive appears before the .MODEL statement, the microprocessor
executes instructions in the 32-bit protected mode, which must execute in Windows.
EXAMPLE 3–11

0000
0000 0000 0001 0002 LIST
0003 0004

.MODEL SMALL
.386
.DATA
DW 0,1,2,3,4

;select small model
;select 80386 microprocessor
;start data segment
;define array LIST

99

ADDRESSING MODES
000A 0005 0006 0007
0008 0009
0000
0010 66|BB 00000000 R
0016 66|B9 00000002
001C 67&8B 04 4B
0020 66|B9 00000004
0026 67&89 04 4B
002A 66|B9 00000007
0030 67&89 04 4B

DW

5,6,7,8,9

.CODE
MOV EBX,OFFSET LIST
MOV ECX,2
MOV AX,EBX+2*ECX]
MOV ECX,4
MOV [EBX+2*ECX],AX
MOV ECX,7
MOV [EBX+2*ECX],AX
.exit
end

;start code segment
;address array LIST
;address element 2
;get element 2
;address element 4
;store in element 4
;address element 7
;store in element 7
;exit to DOS

RIP Relative Addressing
This form of addressing uses the 64-bit instruction pointer register in the 64-bit mode to address
a linear location in the flat memory model. The inline assembler program available to Visual
C++ does not contain any way of using this addressing mode or any other 64-bit addressing
mode. The Microsoft Visual C++ does not at present support developing 64-bit assembly code.
The instruction pointer is normally addressed using a * as in *+34 , which is 34 bytes ahead in a
program. When Microsoft finally places an inline assembler into Visual C++ for the 64-bit
mode, this most likely will be the way that RIP relative addressing will appear.
One source is Intel, which does produce a compiler with an inline assembler for 64-bit
code (http://www.intel.com/cd/software/products/asmo-na/eng/compilers/cwin/279582.htm).

Data Structures
A data structure is used to specify how information is stored in a memory array and can be quite
useful with applications that use arrays. It is best to think of a data structure as a template for
data. The start of a structure is identified with the STRUC assembly language directive and the
end with the ENDS statement. A typical data structure is defined and used three times in
Example 3–12. Notice that the name of the structure appears with the STRUC and with ENDS
statement. The example shows the data structure as it was typed without the assembled version.
EXAMPLE 3–12
;define the INFO data structure
;
INFO
STRUC
NAMES
STREET
CITY
STATE
ZIP

DB
DB
DB
DB
DB

32
32
16
2
5

dup(?)
dup(?)
dup(?)
dup(?)
dup(?)

;reserve
;reserve
;reserve
;reserve
;reserve

32 bytes for a name
32 bytes for the street address
16 bytes for the city
2 bytes for the state
5 bytes for the zipcode

INFO

ENDS

NAME1
NAME2
NAME3

INFO <'Bob Smith', '123 Main Street', 'Wanda', 'OH', '44444'>
INFO <'Steve Doe', '222 Moose Lane', 'Miller', 'PA', '18100'>
INFO <'Jim Dover', '303 Main Street', 'Orender', 'CA', '90000'>

The data structure in Example 3–12 defines five fields of information. The first is 32 bytes
long and holds a name; the second is 32 bytes long and holds a street address; the third is 16 bytes
long for the city; the fourth is 2 bytes long for the state; the fifth is 5 bytes long for the ZIP code.
Once the structure is defined (INFO), it can be filled, as illustrated, with names and addresses.
Three example uses for INFO are illustrated. Note that literals are surrounded with apostrophes
and the entire field is surrounded with < > symbols when the data structure is used to define data.

100

CHAPTER 3

When data are addressed in a structure, use the structure name and the field name to select
a field from the structure. For example, to address the STREET in NAME2, use the operand
NAME2.STREET, where the name of the structure is first followed by a period and then by the
name of the field. Likewise, use NAME3.CITY to refer to the city in structure NAME3.
A short sequence of instructions appears in Example 3-13 that clears the name field in
structure NAME1, the address field in structure NAME2, and the ZIP code field in structure
NAME3. The function and operation of the instructions in this program are defined in later chapters in the text. You may wish to refer to this example once you learn these instructions.
EXAMPLE 3–13

3–2

0000
0003
0005
0008

B9 0020
B0 00
BE 0000 R
F3/AA

000A
000D
000F
0012

B9 0020
B0 00
BE 0077 R
F3/AA

0014
0017
0019
001C

B9 0005
B0 00
BE 0100 R
F3/AA

;clear NAMES in array NAME1
;
MOV CX,32
MOV AL,0
MOV DI,OFFSET NAME1.NAMES
REP STOSB
;
;clear STREET in array NAME2
;
MOV CX,32
MOV AL,0
MOV DI,OFFSET NAME2.STREET
REP STOSB
;
;clear ZIP in NAME3
;
MOV CX,5
MOV AL,0
MOV DI,OFFSET NAME3.ZIP
REP STOSB

PROGRAM MEMORY-ADDRESSING MODES
Program memory-addressing modes, used with the JMP (jump) and CALL instructions, consist
of three distinct forms: direct, relative, and indirect. This section introduces these three addressing forms, using the JMP instruction to illustrate their operation.

Direct Program Memory Addressing
Direct program memory addressing is what many early microprocessors used for all jumps and
calls. Direct program memory addressing is also used in high-level languages, such as the
BASIC language GOTO and GOSUB instructions. The microprocessor uses this form of
addressing, but not as often as relative and indirect program memory addressing are used.
The instructions for direct program memory addressing store the address with the opcode.
For example, if a program jumps to memory location 10000H for the next instruction, the address
(10000H) is stored following the opcode in the memory. Figure 3–14 shows the direct intersegment
JMP instruction and the 4 bytes required to store the address 10000H. This JMP instruction loads
CS with 1000H and IP with 0000H to jump to memory location 10000H for the next instruction.
(An intersegment jump is a jump to any memory location within the entire memory system.) The
direct jump is often called a far jump because it can jump to any memory location for the next
FIGURE 3–14 The 5-byte
machine language version of
a JMP [10000H] instruction.

Opcode

Offset (low)

Offset (high)

E A

0 0

0 0

Segment (low) Segment (high)
0 0

1 0

101

ADDRESSING MODES

FIGURE 3–15 A JMP [2]
instruction. This instruction
skips over the 2 bytes of
memory that follow the JMP
instruction.

10000
10001
10002
10003
10004

EB
02



JMP [2]

instruction. In the real mode, a far jump accesses any location within the first 1M byte of memory
by changing both CS and IP. In protected mode operation, the far jump accesses a new code segment descriptor from the descriptor table, allowing it to jump to any memory location in the entire
4G-byte address range in the 80386 through Core2 microprocessors.
In the 64-bit mode for the Pentium 4 and Core2, a jump or a call can be to any memory
location in the system. The CS segment is still used, but not for the address of the jump or the
call. The CS register contains a pointer to a descriptor that describes the access rights and privilege level of the code segment, but not the address of the jump or call.
The only other instruction that uses direct program addressing is the intersegment or far
CALL instruction. Usually, the name of a memory address, called a label, refers to the location
that is called or jumped to instead of the actual numeric address. When using a label with the
CALL or JMP instruction, most assemblers select the best form of program addressing.

Relative Program Memory Addressing
Relative program memory addressing is not available in all early microprocessors, but it is available to this family of microprocessors. The term relative means “relative to the instruction
pointer (IP).” For example, if a JMP instruction skips the next 2 bytes of memory, the address in
relation to the instruction pointer is a 2 that adds to the instruction pointer. This develops the
address of the next program instruction. An example of the relative JMP instruction is shown in
Figure 3–15. Notice that the JMP instruction is a 1-byte instruction, with a 1-byte or a 2-byte displacement that adds to the instruction pointer. A 1-byte displacement is used in short jumps, and
a 2-byte displacement is used with near jumps and calls. Both types are considered to be
intrasegment jumps. (An intrasegment jump is a jump anywhere within the current code segment.) In the 80386 and above, the displacement can also be a 32-bit value, allowing them to use
relative addressing to any location within their 4G-byte code segments.
Relative JMP and CALL instructions contain either an 8-bit or a 16-bit signed displacement
that allows a forward memory reference or a reverse memory reference. (The 80386 and above can
have an 8-bit or 32-bit displacement.) All assemblers automatically calculate the distance for the displacement and select the proper 1-, 2- or 4-byte form. If the distance is too far for a 2-byte displacement in an 8086 through an 80286 microprocessor, some assemblers use the direct jump. An 8-bit
displacement (short) has a jump range of between +127 and -128 bytes from the next instruction;
a 16-bit displacement (near) has a range of ;32K bytes. In the 80386 and above, a 32-bit displacement allows a range of ;2G bytes. The 32-bit displacement can only be used in the protected mode.

Indirect Program Memory Addressing
The microprocessor allows several forms of program indirect memory addressing for the JMP
and CALL instructions. Table 3–10 lists some acceptable program indirect jump instructions,
which can use any 16-bit register (AX, BX, CX, DX, SP, BP, DI, or SI); any relative register
([BP], [BX], [DI], or [SI]); and any relative register with a displacement. In the 80386 and above,
an extended register can also be used to hold the address or indirect address of a relative JMP or
CALL. For example, the JMP EAX jumps to the location address by register EAX.
If a 16-bit register holds the address of a JMP instruction, the jump is near. For example, if
the BX register contains 1000H and a JMP BX instruction executes, the microprocessor jumps to
offset address 1000H in the current code segment.

102
TABLE 3–10

CHAPTER 3

Examples of indirect program memory addressing.

Assembly Language

Operation

JMP AX

Jumps to the current code segment location addressed by the contents of AX

JMP CX

Jumps to the current code segment location addressed by the contents of CX

JMP NEAR PTR[BX]

Jumps to the current code segment location addressed by the contents of the data
segment location addressed by BX

JMP NEAR PTR[DI+2]

Jumps to the current code segment location addressed by the contents of the data
segment memory location addressed by DI plus 2

JMP TABLE[BX]

Jumps to the current code segment location addressed by the contents of the data
segment memory location address by TABLE plus BX

JMP ECX

Jumps to the current code segment location addressed by the contents of ECX

JMP RDI

Jumps to the linear address contained in the RDI register (64-bit mode)
FIGURE 3–16 A jump table
that stores addresses of various
programs. The exact address
chosen from the TABLE is
determined by an index stored
with the jump instruction.

TABLE DW
DW
DW
DW

LOC0
LOC1
LOC2
LOC3

If a relative register holds the address, the jump is also considered to be an indirect jump. For
example, JMP [BX] refers to the memory location within the data segment at the offset address contained in BX. At this offset address is a l6-bit number that is used as the offset address in the intrasegment jump. This type of jump is sometimes called an indirect-indirect or double-indirect jump.
Figure 3–16 shows a jump table that is stored, beginning at memory location TABLE. This
jump table is referenced by the short program of Example 3–14. In this example, the BX register
is loaded with a 4 so, when it combines in the JMP TABLE[BX] instruction with TABLE, the
effective address is the contents of the second entry in the 16-bit-wide jump table.
EXAMPLE 3–14

0000 BB 0004
0003 FF A7 23A1 R

3–3

;Using indirect addressing for a jump
;
MOV BX,4
;address LOC2
JMP TABLE[BX]
;jump to LOC2

STACK MEMORY-ADDRESSING MODES
The stack plays an important role in all microprocessors. It holds data temporarily and stores the
return addresses used by procedures. The stack memory is an LIFO (last-in, first-out) memory,
which describes the way that data are stored and removed from the stack. Data are placed onto
the stack with a PUSH instruction and removed with a POP instruction. The CALL instruction
also uses the stack to hold the return address for procedures and a RET (return) instruction to
remove the return address from the stack.
The stack memory is maintained by two registers: the stack pointer (SP or ESP) and the stack
segment register (SS). Whenever a word of data is pushed onto the stack [see Figure 3–17(a)],
the high-order 8 bits are placed in the location addressed by SP – 1. The low-order 8 bits are placed
in the location addressed by SP – 2. The SP is then decremented by 2 so that the next word of data

103

ADDRESSING MODES
Memory
Register array
EAX
EBX

1 2

3 4

1 2 3 4

1 2
3 4

ECX
EDX

+

ESP

SS × 10H
(a)

Register array

Memory

EAX
EBX
ECX

1 2

3 4

1 2 3 4

3 4

EDX

ESP

1 2

+
SS × 10H
(b)

FIGURE 3–17 The PUSH and POP instructions: (a) PUSH BX places the contents of BX onto
the stack; (b) POP CX removes data from the stack and places them into CX. Both instructions
are shown after execution.

is stored in the next available stack memory location. The SP/ESP register always points to an area
of memory located within the stack segment. The SP/ESP register adds to SS * 10H to form the
stack memory address in the real mode. In protected mode operation, the SS register holds a selector that accesses a descriptor for the base address of the stack segment.
Whenever data are popped from the stack [see Figure 3–17(b)], the low-order 8 bits are
removed from the location addressed by SP. The high-order 8 bits are removed from the location
addressed by SP + 1. The SP register is then incremented by 2. Table 3–11 lists some of the
PUSH and POP instructions available to the microprocessor. Note that PUSH and POP store or
retrieve words of data—never bytes—in the 8086 through the 80286 microprocessors. The 80386
and above allow words or doublewords to be transferred to and from the stack. Data may be
pushed onto the stack from any 16-bit register or segment register; in the 80386 and above, from
any 32-bit extended register. Data may be popped off the stack into any register or any segment
register except CS. The reason that data may not be popped from the stack into CS is that this only

104

CHAPTER 3

TABLE 3–11

Example PUSH and POP instructions.

Assembly Language

Operation

POPF

Removes a word from the stack and places it into the flag register

POPFD

Removes a doubleword from the stack and places it into the
EFLAG register

PUSHF

Copies the flag register to the stack

PUSHFD

Copies the EFLAG register to the stack

PUSH AX

Copies the AX register to the stack

POP BX

Removes a word from the stack and places it into the BX register

PUSH DS

Copies the DS register to the stack

PUSH 1234H

Copies a word-sized 1234H to the stack

POP CS

This instruction is illegal

PUSH WORD PTR[BX]

Copies the word contents of the data segment memory location
addressed by BX onto the stack

PUSHA

Copies AX, CX, DX, BX, SP, BP, DI, and SI to the stack

POPA

Removes the word contents for the following registers from the
stack: SI, DI, BP, SP, BX, DX, CX, and AX

PUSHAD

Copies EAX, ECX, EDX, EBX, ESP, EBP, EDI, and ESI to the stack

POPAD

Removes the doubleword contents for the following registers from
the stack: ESI, EDI, EBP, ESP, EBX, EDX, ECX, and EAX

POP EAX

Removes a doubleword from the stack and places it into the EAX
register

POP RAX

Removes a quadword from the stack and places it into the RAC
register (64-bit mode)

PUSH EDI

Copies EDI to the stack

PUSH RSI

Copies RSI into the stack (64-bit mode)

PUSH QWORD PTR[RDX]

Copies the quadword contents of the memory location addressed
by RDX onto the stack

changes part of the address of the next instruction. In the Pentium 4 or Core2 operated in 64-bit
mode, the 64-bit registers can be pushed or popped from the stack, but they are 8 bytes in length.
The PUSHA and POPA instructions either push or pop all of the registers, except segment
registers, onto the stack. These instructions are not available on the early 8086/8088 processors.
The push immediate instruction is also new to the 80286 through the Core2 microprocessors. Note
the examples in Table 3–11, which show the order of the registers transferred by the PUSHA and
POPA instructions. The 80386 and above also allow extended registers to be pushed or popped. The
64-bit mode for the Pentium 4 and Core2 does not contain a PUSHA or POPA instruction.
Example 3–15 lists a short program that pushes the contents of AX, BX, and CX onto the stack.
The first POP retrieves the value that was pushed onto the stack from CX and places it into AX. The
second POP places the original value of BX into CX. The last POP places the value of AX into BX.
EXAMPLE 3–15
0000
0100 B8 1000
0103 BB 2000

.MODEL TINY
.CODE
.STARTUP
MOV
AX,1000H
MOV
BX,2000H

;select tiny model
;start code segment
;start program
;load test data

105

ADDRESSING MODES

3–4

0106 B9 3000

MOV

CX,3000H

0109 50
010A 53
010B 51

PUSH
PUSH
PUSH

AX
BX
CX

010C 58
010D 59
010E 5B

POP
AX
POP
CX
POP
BX
.exit
end

;1000H to stack
;2000H to stack
;3000H to stack
;3000H to AX
;2000H to CBX
;1000H to BX
;exit to DOS
;end program

SUMMARY
1. The data-addressing modes include register, immediate, direct, register indirect, basep1us-index, register relative, and base relative-plus-index addressing. The 80386 through
the Pentium 4 microprocessors have an additional addressing mode called scaled-index
addressing.
2. The program memory-addressing modes include direct, relative, and indirect addressing.
3. Table 3–12 lists all real mode data-addressing modes available to the 8086 through the
80286 microprocessors. Note that the 80386 and above use these modes, plus the many
defined through this chapter. In the protected mode, the function of the segment register is to
address a descriptor that contains the base address of the memory segment.
4. The 80386 through Core2 microprocessors have additional addressing modes that allow the
extended registers EAX, EBX, ECX, EDX, EBP, EDI, and ESI to address memory.
Although these addressing modes are too numerous to list in tabular form, in general, any of
these registers function in the same way as those listed in Table 3–12. For example, the
MOV AL,TABLE[EBX+2*ECX+10H ] is a valid addressing mode for the 80386–Core2
microprocessors.
5. The 64-bit mode for the Pentium 4 and Core2 microprocessors use the same addressing
modes as the Pentium 4 or Core2 in 32-bit mode, except the registers contain a linear
address and they are 64 bits in width. An additional addressing mode called RIP relative
exists for the 64-bit mode that addresses data relative to the address in the instruction
pointer.
6. The MOV instruction copies the contents of the source operand into the destination operand.
The source never changes for any instruction.
7. Register addressing specifies any 8-bit register (AH, AL, BH, BL, CH, CL, DH, or DL) or
any 16-bit register (AX, BX, CX, DX, SP, BP, SI, or DI). The segment registers (CS, DS, ES,
or SS) are also addressable for moving data between a segment register and a 16-bit register/memory location or for PUSH and POP. In the 80386 through the Core2 microprocessors, the extended registers also are used for register addressing; they consist of EAX, EBX,
ECX, EDX, ESP, EBP, EDI, and ESI. Also available to the 80386 and above are the FS and
GS segment registers. In the 64-bit mode, the registers are RAX, RBX, RCX, RDX, RSP,
RBP, RDI, RSI, and R8 through R15.
8. The MOV immediate instruction transfers the byte or word that immediately follows the
opcode into a register or a memory location. Immediate addressing manipulates constant
data in a program. In the 80386 and above, doubleword immediate data may also be loaded
into a 32-bit register or memory location.
9. The .MODEL statement is used with assembly language to identify the start of a file and the
type of memory model used with the file. If the size is TINY, the program exists in one segment, the code segment, and is assembled as a command (.COM) program. If the SMALL

106

CHAPTER 3

TABLE 3–12 Example
real mode data-addressing
modes.

Assembly Language
MOV AL,BL
MOV AX,BX
MOV EAX,ECX
MOV DS,DX
MOV AL,LIST
MOV CH,DATA1
MOV ES,DATA2
MOV AL,12
MOV AL,[BP]
MOV AL,[BX]
MOV AL,[DI]
MOV AL,[SI]
MOV AL,[BP+2]
MOV AL,[BX–4]
MOV AL,[ DI+1000H]
MOV AL,[ SI+300H]
MOV AL,LIST[BP]
MOV AL,LIST[BX]
MOV AL,LIST[DI]
MOV AL,LIST[SI]
MOV AL,LIST[BP+2]
MOV AL,LIST[BX–6]
MOV AL,LIST[DI+100H]
MOV AL,LIST[SI+200H]
MOV AL,[ BP+DI]
MOV AL,[BP+SI]
MOV AL,[BX+DI]
MOV AL,[BX+SI]
MOV AL,[BP+DI+8]
MOV AL,[BP+SI–8]
MOV AL,[BX+DI+10H]
MOV AL,[BX+SI–10H]
MOV AL,LIST[BP+DI]
MOV AL,LIST[BP+SI]
MOV AL,LIST[BX+DI]
MOV AL,LIST[BX+SI]
MOV AL,LIST[BP+DI+2]
MOV AL,LIST[BP+SI–7]
MOV AL,LIST[BX+DI+3]
MOV AL,LIST[BX+SI–2]

Address Generation
8-bit register addressing
16-bit register addressing
32-bit register addressing
Segment register addressing
(DS x 10H) + LIST
(DS x 10H) + DATA1
(DS x 10H) + DATA2
Immediate data of 12
(SS x 10H) + BP
(DS x 10H) + BX
(DS x 10H) + DI
(DS x 10H) + SI
(SS x 10H) + BP + 2
(DS x 10H) + BX – 4
(DS x 10H) + DI + 1000H
(DS x 10H) + SI + 300H
(SS x 10H) + LIST + BP
(DS x 10H) + LIST + BX
(DS x 10H) + LIST + DI
(DS x 10H) + LIST + SI
(SS x 10H) + LIST + BP + 2
(DS x 10H) + LIST + BX – 6
(DS x 10H) + LIST + DI + 100H
(DS x 10H) + LIST + SI + 200H
(SS x 10H) + BP + DI
(SS x 10H) + BP + SI
(DS x 10H) + BX + DI
(DS x 10H) + BX + SI
(SS x 10H) + BP + DI + 8
(SS x 10H) + BP + SI – 8
(DS x 10H) + BX + DI + 10H
(DS x 10H) + BX + SI – 10H
(SS x 10H) + LIST + BP + DI
(SS x 10H) + LIST + BP + SI
(DS x 10H) + LIST + BX + DI
(DS x 10H) + LIST + BX + SI
(SS x 10H) + LIST + BP + DI + 2
(SS x 10H) + LIST + BP + SI – 7
(DS x 10H) + LIST + BX + DI + 3
(DS x 10H) + LIST + BX + SI – 2

model is used, the program uses a code and data segment and assembles as an execute
(.EXE) program. Other model sizes and their attributes are listed in Appendix A.
10. Direct addressing occurs in two forms in the microprocessor: (1) direct addressing and (2)
displacement addressing. Both forms of addressing are identical except that direct addressing is used to transfer data between EAX, AX, or AL and memory; displacement addressing
is used with any register-memory transfer. Direct addressing requires 3 bytes of memory,
whereas displacement addressing requires 4 bytes. Note that some of these instructions in
the 80386 and above may require additional bytes in the form of prefixes for register and
operand sizes.

ADDRESSING MODES

107

11. Register indirect addressing allows data to be addressed at the memory location pointed to
by either a base (BP and BX) or index register (DI and SI). In the 80386 and above,
extended registers EAX, EBX, ECX, EDX, EBP, EDI, and ESI are used to address memory
data.
12. Base-plus-index addressing often addresses data in an array. The memory address for this
mode is formed by adding a base register, index register, and the contents of a segment register times 10H. In the 80386 and above, the base and index registers may be any 32-bit register except EIP and ESP.
13. Register relative addressing uses a base or index register, plus a displacement to access
memory data.
14. Base relative-plus-index addressing is useful for addressing a two-dimensional memory
array. The address is formed by adding a base register, an index register, displacement, and
the contents of a segment register times 10H.
15. Scaled-index addressing is unique to the 80386 through the Core2. The second of two registers (index) is scaled by a factor of 2*, 4*, or 8* to access words, doublewords, or quadwords in memory arrays. The MOV AX,[EBX+2*ECX ] and the MOV [4*ECX],EDX are
examples of scaled-index instructions.
16. Data structures are templates for storing arrays of data and are addressed by array name and
field. For example, array NUMBER and field TEN of array NUMBER is addressed as
NUMBER.TEN.
17. Direct program memory addressing is allowed with the JMP and CALL instructions to any
location in the memory system. With this addressing mode, the offset address and segment
address are stored with the instruction.
18. Relative program addressing allows a JMP or CALL instruction to branch forward or
backward in the current code segment by ;32K bytes. In the 80386 and above, the 32-bit
displacement allows a branch to any location in the current code segment by using a displacement value of ;2G bytes. The 32-bit displacement can be used only in protected
mode.
19. Indirect program addressing allows the JMP or CALL instructions to address another portion of the program or subroutine indirectly through a register or memory location.
20. The PUSH and POP instructions transfer a word between the stack and a register or memory location. A PUSH immediate instruction is available to place immediate data on the
stack. The PUSHA and POPA instructions transfer AX, CX, DX, BX, BP, SP, SI, and DI
between the stack and these registers. In the 80386 and above, the extended register and
extended flags can also be transferred between registers and the stack. A PUSHFD stores
the EFLAGS, whereas a PUSHF stores the FLAGS. POPA and PUSHA are not available
in the 64-bit mode.

3–5

QUESTIONS AND PROBLEMS
1. What do the following MOV instructions accomplish?
(a) MOV AX,BX
(b) MOV BX,AX
(c) MOV BL,CH
(d) MOV ESP,EBP
(e) MOV RAX,RCX
2. List the 8-bit registers that are used for register addressing.

108

CHAPTER 3

3. List the 16-bit registers that are used for register addressing.
4. List the 32-bit registers that are used for register addressing in the 80386 through the Core2
microprocessors.
5. List the 64-bit registers available to the 64-bit mode of the Pentium 4 and Core2.
6. List the 16-bit segment registers used with register addressing by MOV, PUSH, and POP.
7. What is wrong with the MOV BL,CX instruction?
8. What is wrong with the MOV DS,SS instruction?
9. Select an instruction for each of the following tasks:
(a) copy EBX into EDX
(b) copy BL into CL
(c) copy SI into BX
(d) copy DS into AX
(e) copy AL into AH
(f) copy R8 into R10
10. Select an instruction for each of the following tasks:
(a) move 12H into AL
(b) move 123AH into AX
(c) move 0CDH into CL
(d) move 1000H into RAX
(e) move 1200A2H into EBX
11. What special symbol is sometimes used to denote immediate data?
12. What is the purpose of the .MODEL TINY statement?
13. What assembly language directive indicates the start of the CODE segment?
14. What is a label?
15. The MOV instruction is placed in what field of a statement?
16. A label may begin with what characters?
17. What is the purpose of the .EXIT directive?
18. Does the .MODEL TINY statement cause a program to assemble as an execute (.EXE)
program?
19. What tasks does the .STARTUP directive accomplish in the small memory model?
20. What is a displacement? How does it determine the memory address in a MOV
DS:[2000H],AL instruction?
21. What do the symbols [ ] indicate?
22. Suppose that DS = 0200H, BX = 0300H, and DI = 400H. Determine the memory address
accessed by each of the following instructions, assuming real mode operation:
(a) MOV AL,[1234H]
(b) MOV EAX,[BX]
(c) MOV [DI],AL
23. What is wrong with a MOV [BX],[DI] instruction?
24. Choose an instruction that requires BYTE PTR.
25. Choose an instruction that requires WORD PTR.
26. Choose an instruction that requires DWORD PTR.
27. Select an instruction that requires QWORD PTR.
28. Explain the difference between the MOV BX,DATA instruction and the MOV BX,OFFSET
DATA instruction.
29. Suppose that DS = 1000H, SS = 2000H, BP = 1000H, and DI = 0100H. Determine the
memory address accessed by each of the following instructions, assuming real mode
operation:
(a) MOV AL,[BP+DI]

ADDRESSING MODES

30.
31.

32.

33.

34.
35.

36.
37.
38.
39.
40.
41.
42.
43.
44.

45.

46.
47.
48.

109

(b) MOV CX,[DI]
(c) MOV EDX,[BP]
What, if anything, is wrong with a MOV AL,[BX][SI] instruction?
Suppose that DS = 1200H, BX = 0100H, and SI = 0250H. Determine the address accessed
by each of the following instructions, assuming real mode operation:
(a) MOV [100H],DL
(b) MOV [SI+100H],EAX
(c) MOV DL,[BX+100H]
Suppose that DS = 1100H, BX = 0200H, LIST = 0250H, and SI = 0500H. Determine
the address accessed by each of the following instructions, assuming real mode operation:
(a) MOV LIST[SI],EDX
(b) MOV CL,LIST[BX+SI]
(c) MOV CH,[BX+SI]
Suppose that DS = 1300H, SS = 1400H, BP = 1500H, and SI = 0100H. Determine the
address accessed by each of the following instructions, assuming real mode operation:
(a) MOV EAX,[BP+200H]
(b) MOV AL,[BP+SI-200H]
(c) MOV AL,[SI-0100H]
Which base register addresses data in the stack segment?
Suppose that EAX = 00001000H, EBX = 00002000H, and DS = 0010H. Determine the
addresses accessed by the following instructions, assuming real mode operation:
(a) MOV ECX,[EAX+EBX]
(b) MOV [EAX+2*EBX],CL
(c) MOV DH,[EBX+4*EAX+1000H]
Develop a data structure that has five fields of one word each named Fl, F2, F3, F4, and F5
with a structure name of FIELDS.
Show how field F3 of the data structure constructed in question 36 is addressed in a program.
What are the three program memory-addressing modes?
How many bytes of memory store a far direct jump instruction? What is stored in each of
the bytes?
What is the difference between an intersegment and intrasegment jump?
If a near jump uses a signed 16-bit displacement, how can it jump to any memory location
within the current code segment?
The 80386 and above use a ____________-bit displacement to jump to any location within
the 4G-byte code segment.
What is a far jump?
If a JMP instruction is stored at memory location 100H within the current code segment, it
cannot be a ____________ jump if it is jumping to memory location 200H within the current
code segment.
Show which JMP instruction assembles (short, near, or far) if the JMP THERE instruction is
stored at memory address 10000H and the address of THERE is:
(a) l0020H
(b) 11000H
(c) 0FFFEH
(d) 30000H
Form a JMP instruction that jumps to the address pointed to by the BX register.
Select a JMP instruction that jumps to the location stored in memory at the location TABLE.
Assume that it is a near JMP.
How many bytes are stored on the stack by a PUSH AX?

110

CHAPTER 3

49.
50.
51.
52.
53.

Explain how the PUSH [DI] instruction functions.
What registers are placed on the stack by the PUSHA instruction? In what order?
What does the PUSHAD instruction accomplish?
Which instruction places the EFLAGS on the stack in the Pentium 4 microprocessor?
Is a PUSHA available in the 64-bit mode of the Pentium 4 or the Core2?

CHAPTER 4
Data Movement Instructions

INTRODUCTION
This chapter concentrates on the data movement instructions. The data movement instructions
include MOV, MOVSX, MOVZX, PUSH, POP, BSWAP, XCHG, XLAT, IN, OUT, LEA, LDS,
LES, LFS, LGS, LSS, LAHF, SAHF, and the string instructions MOVS, LODS, STOS, INS,
and OUTS. The latest data transfer instruction implemented on the Pentium Pro and above is
the CMOV (conditional move) instruction. The data movement instructions are presented first
because they are more commonly used in programs and are easy to understand.
The microprocessor requires an assembler program, which generates machine language,
because machine language instructions are too complex to efficiently generate by hand. This
chapter describes the assembly language syntax and some of its directives. [This text assumes
that the user is developing software on an IBM personal computer or clone. It is recommended
that the Microsoft MACRO assembler (MASM) be used as the development tool, but the Intel
Assembler (ASM), Borland Turbo assembler (TASM), or similar software function equally as
well. The most recent version of TASM completely emulates the MASM program. This text
presents information that functions with the Microsoft MASM assembler, but most programs
assemble without modification with other assemblers. Appendix A explains the Microsoft
assembler and provides detail on the linker program.] As a more modern alternative, the Visual
C⫹⫹ Express compiler and its inline assembler program may also be used as a development
system. Both are explained in detail in the text.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Explain the operation of each data movement instruction with applicable addressing modes.
2. Explain the purposes of the assembly language pseudo-operations and key words such as
ALIGN, ASSUME, DB, DD, DW, END, ENDS, ENDP, EQU, .MODEL, OFFSET, ORG,
PROC, PTR, SEGMENT, USEI6, USE32, and USES.
3. Select the appropriate assembly language instruction to accomplish a specific data movement task.
4. Determine the symbolic opcode, source, destination, and addressing mode for a hexadecimal machine language instruction.
5. Use the assembler to set up a data segment, stack segment, and code segment.
111

112

CHAPTER 4

6. Show how to set up a procedure using PROC and ENDP.
7. Explain the difference between memory models and full-segment definitions for the
MASM assembler.
8. Use the Visual C⫹⫹ online assembler to perform data movement tasks.

4–1

MOV REVISITED
The MOV instruction, introduced in Chapter 3, explains the diversity of 8086–Core2 addressing
modes. In this chapter, the MOV instruction introduces the machine language instructions available
with various addressing modes and instructions. Machine code is introduced because it may occasionally be necessary to interpret machine language programs generated by an assembler or inline
assembler of Visual C⫹⫹ . Interpretation of the machine’s native language (machine language)
allows debugging or modification at the machine language level. Occasionally, machine language
patches are made by using the DEBUG program available with DOS and also in Visual C⫹⫹ for
Windows, which requires some knowledge of machine language. Conversion between machine and
assembly language instructions is illustrated in Appendix B.

Machine Language
Machine language is the native binary code that the microprocessor understands and uses as its
instructions to control its operation. Machine language instructions for the 8086 through the
Core2 vary in length from 1 to as many as 13 bytes. Although machine language appears
complex, there is order to this microprocessor’s machine language. There are well over 100,000
variations of machine language instructions, meaning that there is no complete list of these variations. Because of this, some binary bits in a machine language instruction are given, and the
remaining bits are determined for each variation of the instruction.
Instructions for the 8086 through the 80286 are 16-bit mode instructions that take the form
found in Figure 4–1(a). The 16-bit mode instructions are compatible with the 80386 and above if
they are programmed to operate in the 16-bit instruction mode, but they may be prefixed, as
shown in Figure 4–1(b). The 80386 and above assume that all instructions are 16-bit mode
instructions when the machine is operated in the real mode (DOS). In the protected mode
(Windows), the upper byte of the descriptor contains the D-bit that selects either the 16- or 32-bit
instruction mode. At present, only Windows 95 through Windows XP and Linux operate in the
32-bit instruction mode. The 32-bit mode instructions are in the form shown in Figure 4–1(b).

16-bit instruction mode
Opcode
1–2 bytes

Displacement
0–1 bytes

MOD-REG-R/M
0–1 bytes

Immediate
0–2 bytes

(a)

32-bit instruction mode (80386 through Pentium 4 only)
Address size
0–1 bytes

Register size
0–1 bytes

Opcode
1–2 bytes

MOD-REG-R/M
0–1 bytes

Scaled-index
0–1 bytes

Displacement
0–4 bytes

Immediate
0–4 bytes

(b)

FIGURE 4–1

The formats of the 8086–Core2 instructions. (a) The 16-bit form and (b) the 32-bit form.

113

DATA MOVEMENT INSTRUCTIONS

FIGURE 4–2 Byte 1 of
many machine language
instructions, showing the
position of the D- and W-bits.

D

W

Opcode

These instructions occur in the 16-bit instruction mode by the use of prefixes, which are
explained later in this chapter.
The first 2 bytes of the 32-bit instruction mode format are called override prefixes because
they are not always present. The first modifies the size of the operand address used by the instruction and the second modifies the register size. If the 80386 through the Pentium 4 operate as 16-bit
instruction mode machines (real or protected mode) and a 32-bit register is used, the register-size
prefix (66H) is appended to the front of the instruction. If operated in the 32-bit instruction mode
(protected mode only) and a 32-bit register is used, the register-size prefix is absent. If a 16-bit
register appears in an instruction in the 32-bit instruction mode, the register-size 16-bit instruction
mode, the register-size prefix is present to select a l6-bit register. The address size-prefix (67H) is
used in a similar fashion, as explained later in this chapter. The prefixes toggle the size of the register and operand address from l6-bit to 32-bit or from 32-bit to l6-bit for the prefixed instruction.
Note that the l6-bit instruction mode uses 8- and l6-bit registers and addressing modes, while the
32-bit instruction mode uses 8- and 32-bit registers and addressing modes by default. The prefixes
override these defaults so that a 32-bit register can be used in the l6-bit mode or a l6-bit register
can be used in the 32-bit mode. The mode of operation (16 or 32 bits) should be selected to function with the current application. If 8- and 32-bit data pervade the application, the 32-bit mode
should be selected; likewise, if 8- and l6-bit data pervade, the l6-bit mode should be selected.
Normally, mode selection is a function of the operating system. (Remember that DOS can operate
only in the l6-bit mode, where Windows can operate in both modes.)

The Opcode. The opcode selects the operation (addition, subtraction, move, and so on) that is
performed by the microprocessor. The opcode is either 1 or 2 bytes long for most machine language instructions. Figure 4–2 illustrates the general form of the first opcode byte of many, but
not all, machine language instructions. Here, the first 6 bits of the first byte are the binary
opcode. The remaining 2 bits indicate the direction (D)—not to be confused with the instruction
mode bit (16/32) or direction flag bit (used with string instructions)—of the data flow, and indicate whether the data are a byte or a word (W). In the 80386 and above, words and doublewords
are both specified when W⫽1. The instruction mode and register-size prefix (66H) determine
whether W represents a word or a doubleword.
If the direction bit 1D2⫽1, data flow to the register REG field from the R/M field located
in the second byte of an instruction. If the D-bit⫽0 in the opcode, data flow to the R/M field
from the REG field. If the W-bit ⫽1, the data size is a word or doubleword; if the W-bit ⫽0, the
data size is always a byte. The W-bit appears in most instructions, while the D-bit appears mainly
with the MOV and some other instructions. Refer to Figure 4–3 for the binary bit pattern of the
second opcode byte (reg-mod-r/m) of many instructions. Figure 4–3 shows the location of the
MOD (mode), REG (register), and R/M (register/memory) fields.
FIGURE 4–3 Byte 2 of
many machine language
instructions, showing the
position of the MOD, REG,
and R/M fields.

MOD

REG

R/M

114

CHAPTER 4

TABLE 4–1 MOD field for
the 16-bit instruction mode.

MOD

Function

00
01
10
11

No displacement
8-bit sign-extended displacement
16-bit signed displacement
R/M is a register

MOD Field. The MOD field specifies the addressing mode (MOD) for the selected instruction.
The MOD field selects the type of addressing and whether a displacement is present with the
selected type. Table 4–1 lists the operand forms available to the MOD field for l6-bit instruction
mode, unless the operand address-size override prefix (67H) appears. If the MOD field contains
an 11, it selects the register-addressing mode. Register addressing uses the R/M field to specify a
register instead of a memory location. If the MOD field contains a 00, 01, or 10, the R/M field
selects one of the data memory-addressing modes. When MOD selects a data memory addressing mode, it indicates that the addressing mode contains no displacement (00), an 8-bit signextended displacement (01), or a l6-bit displacement (10). The MOV AL,[DI] instruction is an
example that contains no displacement, a MOV AL,[DI⫹2] instruction uses an 8-bit displacement ( ⫹2), and a MOV AL,[DI⫹1000H] instruction uses a 16-bit displacement ( ⫹1000H).
All 8-bit displacements are sign-extended into 16-bit displacements when the microprocessor executes the instruction. If the 8-bit displacement is 00H–7FH (positive), it is signextended to 0000H–007FH before adding to the offset address. If the 8-bit displacement is
80H–FFH (negative), it is sign-extended to FF80H–FFFFH. To sign-extend a number, its sign-bit
is copied to the next higher-order byte, which generates either a 00H or an FFH in the next
higher-order byte. Some assembler programs do not use the 8-bit displacements and in place
default to all 16-bit displacements.
In the 80386 through the Core2 microprocessors, the MOD field may be the same as
shown in Table 4–1 for 16-bit instruction mode; if the instruction mode is 32 bits, the MOD field
is as it appears in Table 4–2. The MOD field is interpreted as selected by the address-size override prefix or the operating mode of the microprocessor. This change in the interpretation of the
MOD field and instruction supports many of the numerous additional addressing modes allowed
in the 80386 through the Core2. The main difference is that when the MOD field is a 10, this
causes the 16-bit displacement to become a 32-bit displacement, to allow any protected mode
memory location (4G bytes) to be accessed. The 80386 and above only allow an 8- or 32-bit displacement when operated in the 32-bit instruction mode, unless the address-size override prefix
appears. Note that if an 8-bit displacement is selected, it is sign-extended into a 32-bit displacement by the microprocessor.
Register Assignments. Table 4–3 lists the register assignments for the REG field and the R/M
field (MOD⫽11). This table contains three lists of register assignments: one is used when the
W bit⫽0 (bytes), and the other two are used when the W bit⫽1 (words or doublewords). Note
that doubleword registers are only available to the 80386 through the Core2.

TABLE 4–2 MOD field for
the 32-bit instruction mode
(80386–Core2 only).

MOD
00
01
10
11

Function
No displacement
8-bit sign-extended displacement
32-bit signed displacement
R/M is a register

115

DATA MOVEMENT INSTRUCTIONS

TABLE 4–3 REG and
R/M (when) MOD = 11
assignments.

Code

W = 0 (Byte)

W = 1 (Word)

W = 1 (Doubleword)

AL
CL
DL
BL
AH
CH
DH
BH

AX
CX
DX
BX
SP
BP
SI
DI

EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI

000
001
010
011
100
101
110
111

Suppose that a 2-byte instruction, 8BECH, appears in a machine language program.
Because neither a 67H (operand address-size override prefix) nor a 66H (register-size override
prefix) appears as the first byte, the first byte is the opcode. If the microprocessor is operated in the
16-bit instruction mode, this instruction is converted to binary and placed in the instruction format
of bytes 1 and 2, as illustrated in Figure 4–4. The opcode is 100010. If you refer to Appendix B,
which lists the machine language instructions, you will find that this is the opcode for a MOV
instruction. Notice that both the D and W bits are a logic 1, which means that a word moves into
the destination register specified in the REG field. The REG field contains a 101, indicating register BP, so the MOV instruction moves data into register BP. Because the MOD field contains a 11,
the R/M field also indicates a register. Here, R>M⫽100 (SP); therefore, this instruction moves
data from SP into BP and is written in symbolic form as a MOV BP,SP instruction.
Suppose that a 668BE8H instruction appears in an 80386 or above, operated in the 16-bit
instruction mode. The first byte (66H) is the register-size override prefix that selects 32-bit
register operands for the 16-bit instruction mode. The remainder of the instruction indicates that
the opcode is a MOV with a source operand of EAX and a destination operand of EBP. This
instruction is a MOV EBP,EAX. The same instruction becomes a MOV BP,AX instruction in the
80386 and above if it is operated in the 32-bit instruction mode, because the register-size override
prefix selects a 16-bit register. Luckily, the assembler program keeps track of the register- and
address-size prefixes and the mode of operation. Recall that if the .386 switch is placed before
the .MODEL statement, the 32-bit mode is selected; if it is placed after the .MODEL statement,
the 16-bit mode is selected. All programs written using the inline assembler in Visual C⫹⫹ are
always in the 32-bit mode.

R/M Memory Addressing. If the MOD field contains a 00, 01, or 10, the R/M field takes on a
new meaning. Table 4–4 lists the memory-addressing modes for the R/M field when MOD is a
00, 01, or 10 for the 16-bit instruction mode.
Opcode
1

0

0

0

1

0

D

W

1

1

MOD
1

1

R/M

REG
1

0

1

1

0

0

Opcode = MOV
D = Transfer to register (REG)
W = Word
MOD = R/M is a register
REG = BP
R/M = SP

FIGURE 4–4 The 8BEC instruction placed into bytes 1 and 2 formats from Figures 4–2 and
4–3. This instruction is a MOV BP,SP.

116

CHAPTER 4

TABLE 4–4 16-bit R/M
memory-addressing modes.

R/M Code
000
001
010
011
100
101
110
111

Addressing Mode
DS:3BX +SI4
DS:3BX +DI4
SS:3BP +SI4
SS:3BP +DI4
DS:[SI]
DS:[DI]
SS:[BP]*
DS:[BX]

*Note: See text section, Special Addressing
Mode.

All of the 16-bit addressing modes presented in Chapter 3 appear in Table 4–4. The displacement, discussed in Chapter 3, is defined by the MOD field. If MOD⫽00 and R>M ⫽101,
the addressing mode is [DI]. If MOD⫽01 or 10, the addressing mode is [DI⫹33H ], or LIST
[DI⫹22H] for the 16-bit instruction mode. This example uses LIST, 33H, and 22H as arbitrary
values for the displacement.
Figure 4–5 illustrates the machine language version of the 16-bit instruction MOV
DL,[DI] or instruction (8AI5H). This instruction is 2 bytes long and has an opcode 100010,
D⫽1 (to REG from R/M), W⫽0 (byte), MOD⫽00 (no displacement), REG⫽010 (DL), and
R>M⫽101 ([DI]). If the instruction changes to MOV DL,3DI⫹14, the MOD field changes to 01
for an 8-bit displacement, but the first 2 bytes of the instruction otherwise remain the same. The
instruction now becomes 8A5501H instead of 8A15H. Notice that the 8-bit displacement
appends to the first 2 bytes of the instruction to form a 3-byte instruction instead of 2 bytes. If the
instruction is again changed to a MOV DL,3DI⫹1000H], the machine language form becomes
8A750010H. Here, the 16-bit displacement of 1000H (coded as 0010H) appends the opcode.

Special Addressing Mode. There is a special addressing mode that does not appear in Tables
4–2, 4–3, or 4–4. It occurs whenever memory data are referenced by only the displacement mode
of addressing for 16-bit instructions. Examples are the MOV [1000H],DL and MOV NUMB,DL
instructions. The first instruction moves the contents of register DL into data segment memory
location 1000H. The second instruction moves register DL into symbolic data segment memory
location NUMB.
Whenever an instruction has only a displacement, the MOD field is always a 00 and the
R/M field is always 110. As shown in the tables, the instruction contains no displacement and uses
addressing mode [BP]. You cannot actually use addressing mode [BP] without a displacement in
machine language. The assembler takes care of this by using an 8-bit displacement (MOD⫽01)

Opcode
1

0

0

0

1

0

D

W

1

0

MOD
0

0

R/M

REG
0

1

0

1

Opcode = MOV
D = Transfer to register (REG)
W = Byte
MOD = No displacement
REG = DL
R/M = DS:[DI]

FIGURE 4–5

A MOV DL,[DI] instruction converted to its machine language form.

0

1

117

DATA MOVEMENT INSTRUCTIONS
Opcode
1

0

0

0

0

0

1

D

W

0

0

MOD
0

0

R/M

REG
0

1

0

1

Byte 1

Byte 2

Displacement—low

Displacement—high

0

0

0

0

0

0

0

0

0

0

1

Byte 3

0

0

1

0

0

0

Byte 4

Opcode = MOV
D = Transfer from register (REG)
W = Byte
MOD = because R/M is [BP] (special addressing)
REG = DL
R/M = DS:[BP]
Displacement = 1000H

FIGURE 4–6

The MOV [1000H],DI instruction uses the special addressing mode.

of 00H whenever the [BP] addressing mode appears in an instruction. This means that the [BP]
addressing mode assembles as a [BP⫹0], even though a [BP] is used in the instruction. The same
special addressing mode is also available for the 32-bit mode.
Figure 4–6 shows the binary bit pattern required to encode the MOV [1000H],DL instruction in machine language. If the individual translating this symbolic instruction into machine
language does not know about the special addressing mode, the instruction would incorrectly
translate to a MOV [BP],DL instruction. Figure 4–7 shows the actual form of the MOV [BP],DL
instruction. Notice that this is a 3-byte instruction with a displacement of 00H.

Opcode
1

0

0

0

1

0

D

W

0

0

MOD
0

1

R/M

REG
0

1

0

Byte 2

Byte 1

8-bit displacement
0

0

0

0

0

0

0

0

Byte 3

Opcode = MOV
D = Transfer from register (REG)
W = Byte
MOD = because R/M is [BP] (special addressing)
REG = DL
R/M = DS:[BP]
Displacement = 00H

FIGURE 4–7

The MOV [BP],DL instruction converted to binary machine language.

1

1

0

118

CHAPTER 4

TABLE 4–5 32-bit addressing modes selected by R/M.

R/M Code

Function

000
001
010
011
100
101
110
111

DS:[EAX]
DS:[ECX]
DS:[EDX]
DS:[EBX]
Uses scaled-index byte
SS:[EBP]*
DS:[ESI]
DS:[EDI]

*Note: See text section, Special Addressing Mode.

32-Bit Addressing Modes. The 32-bit addressing modes found in the 80386 and above are
obtained by either running these machines in the 32-bit instruction mode or in the 16-bit instruction mode by using the address-size prefix 67H. Table 4–5 shows the coding for R/M used to
specify the 32-bit addressing modes. Notice that when R>M⫽100, an additional byte called a
scaled-index byte appears in the instruction. The scaled-index byte indicates the additional forms
of scaled-index addressing that do not appear in Table 4–5. The scaled-index byte is mainly used
when two registers are added to specify the memory address in an instruction. Because the
scaled-index byte is added to the instruction, there are 7 bits in the opcode and 8 bits in the
scaled-index byte to define. This means that a scaled-index instruction has 215 (32K) possible
combinations. There are over 32,000 different variations of the MOV instruction alone in the
80386 through the Core2 microprocessors.
Figure 4–8 shows the format of the scaled-index byte as selected by a value of 100 in the
R/M field of an instruction when the 80386 and above use a 32-bit address. The leftmost 2 bits
select a scaling factor (multiplier) of 1×, 2×, 4×, or 8× . Note that a scaling factor of 1× is
implicit if none is used in an instruction that contains two 32-bit indirect address registers. The
index and base fields both contain register numbers, as indicated in Table 4–3 for 32-bit registers.
The instruction MOV EAX,3EBX+4*ECX4 is encoded as 67668B048BH. Notice that
both the address size (67H) and register size (66H) override prefixes appear in the instruction.
This coding (67668B048BH) is used when the 80386 and above microprocessors are operated in
the 16-bit instruction mode for this instruction. If the microprocessor operates in the 32-bit
instruction mode, both prefixes disappear and the instruction becomes an 8B048BH instruction.
The use of the prefixes depends on the mode of operation of the microprocessor. Scaled-index
addressing can also use a single register multiplied by a scaling factor. An example is the MOV
AL,[2*ECX] instruction. The contents of the data segment location addressed by two times ECX
are copied into AL.
An Immediate Instruction. Suppose that the MOV WORD PTR [BX+1000H ],1234H instruction is chosen as an example of a 16-bit instruction using immediate addressing. This instruction
moves a 1234H into the word-sized memory location addressed by the sum of 1000H, BX, and
FIGURE 4–8
index byte.

The scaled-

s

s

ss
00 =
01 =
10 =
11 =

×1
×2
×4
×8

Index

Base

119

DATA MOVEMENT INSTRUCTIONS
Opcode
1

0

0

1

0

0

0

0

W
0

1

1

1

R/M

MOD
1

0

0

0

0

1

1

0

0

0

0

1

0

1

Byte 1

Byte 2

Displacement—low

Displacement—high

0

1

0

0

0

0

0

0

0

0

1

0

Byte 3

Byte 4

Data—low

Data—high

1

0

1

0

0

Byte 5

0

0

0

1

0

Byte 6

Opcode = MOV (immediate)
W = Word
MOD = 16-bit displacement
REG = 000 (not used in immediate addressing)
R/M = DS:[BX]
Displacement = 1000H
Data = 1234H

FIGURE 4–9 A MOV WORD PTR [BX⫹ 1000H], 1234H instruction converted to binary
machine language.

DS × 10H. This 6-byte instruction uses 2 bytes for the opcode, W, MOD, and R/M fields. Two
of the 6 bytes are the data of 1234H; 2 of the 6 bytes are the displacement of 1000H. Figure 4–9
shows the binary bit pattern for each byte of this instruction.
This instruction, in symbolic form, includes WORD PTR. The WORD PTR directive indicates to the assembler that the instruction uses a word-sized memory pointer. If the instruction
moves a byte of immediate data, BYTE PTR replaces WORD PTR in the instruction. Likewise,
if the instruction uses a doubleword of immediate data, the DWORD PTR directive replaces
BYTE PTR. Most instructions that refer to memory through a pointer do not need the BYTE
PTR, WORD PTR, or DWORD PTR directives. These directives are necessary only when it is
not clear whether the operation is a byte, word, or doubleword. The MOV [BX],AL instruction is
clearly a byte move; the MOV [BX],9 instruction is not exact, and could therefore be a byte-,
word-, or doubleword-sized move. Here, the instruction must be coded as MOV BYTE PTR
[BX],9, MOV WORD PTR [BX],9, or MOV DWORD PTR [BX],9. If not, the assembler flags it
as an error because it cannot determine the intent of the instruction.

Segment MOV Instructions. If the contents of a segment register are moved by the MOV,
PUSH, or POP instructions, a special set of register bits (REG field) selects the segment register
(see Table 4–6).
Figure 4–10 shows a MOV BX,CS instruction converted to binary. The opcode for this
type of MOV instruction is different for the prior MOV instructions. Segment registers can be
moved between any 16-bit register or 16-bit memory location. For example, the MOV [DI],DS
instruction stores the contents of DS into the memory location addressed by DI in the data

120

CHAPTER 4

TABLE 4–6 Segment register selection.

Code

Segment Register

000
001
010
011
100
101

ES
CS*
SS
DS
FS
GS

*Note: MOV CS,R/M and POP CS are not
allowed.

MOD

Opcode
1

0

0

0

1

1

0

0

1

1

R/M

REG
0

0

1

0

1

1

Opcode = MOV
MOD = R/M is a register
REG = CS
R/M = BX

FIGURE 4–10

A MOV BX,CS instruction converted to binary machine language.

segment. An immediate segment register MOV is not available in the instruction set. To load a
segment register with immediate data, first load another register with the data and then move it to
a segment register.
Although this discussion has not been a complete coverage of machine language coding, it
provides enough information for machine language programming. Remember that a program
written in symbolic assembly language (assembly language) is rarely assembled by hand into
binary machine language. An assembler program converts symbolic assembly language into
machine language. With the microprocessor and its over 100,000 instruction variations, let us
hope that an assembler is available for the conversion, because the process is very time-consuming,
although not impossible.

The 64-Bit Mode for the Pentium 4 and Core2
None of the information presented thus far addresses the issue of 64-bit operation of the Pentium
4 or Core2. In the 64-bit mode, an additional prefix called REX (register extension) is added.
The REX prefix, which is encoded as a 40H–4FH, follows other prefixes and is placed immediately before the opcode to modify it for 64-bit operation. The purpose of the REX prefix is to
modify the reg and r/m fields in the second byte of the instruction. REX is needed to be able to
address registers R8 through R15. Figure 4–11 illustrates the structure of REX and also its application to the second byte of the opcode.
The register and memory address assignments for the rrrr and mmmm fields are shown in
Table 4–7 for 64-bit operations. The reg field can only contain register assignments as in other
modes of operation and the r/m field contains either a register or memory assignment.
Figure 4–12 shows the scaled-index byte with the REX prefix for more complex addressing modes and also for using a scaling factor in the 64-bit mode of operation. As with 32-bit
instructions, the modes allowed by the scaled-index byte are fairly all conclusive allowing pairs
of registers to address memory and also an index factor of 2×, 4×, or 8× . An example is the
instruction MOV RAXW,3RDX+RCX-124 , which requires the scaled-index byte with an
index of 1, which is understood but never entered into the instruction.

121

DATA MOVEMENT INSTRUCTIONS

FIGURE 4–11 The application
of REX without scaled index.

Second byte of opcode
REX prefix
0100

Opcode

MOD

REG

W R 0 M

R/M

R R R M M M

RR R R

MM M M
W ⫽ 1 (64 bits)
W ⫽ 0 (CS descriptor)

TABLE 4–7 The 64-bit register and memory designators
for rrrr and mmmm.

Code

Register

Memory

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

RAX
RCX
RDX
RBX
RSP
RBP
RSI
RDI
R8
R9
R10
R11
R12
R13
R14
R15

[RAX]
[RCX]
[RDX]
[RBX]
See note
[RBP]
[RSI]
[RDI]
[R8]
[R9]
[R10]
[R11]
[R12]
[R13]
[R14]
[R15]

Note: This addressing mode specifies the inclusion
of the scaled-index byte.
REX prefix

Opcode

Second byte of opcode
MOD

0

1

0

REG

R/M
1

0 W R X B

0

Scaled index
Scale

Index

0

R R R R

BB B B

X
W ⫽ 1 (64 bits)
W ⫽ 0 (CS descriptor)

FIGURE 4–12

The scaled-index byte and REX prefix for 64-bit operations.

Base

X X X

122

CHAPTER 4

4–2

PUSH/POP
The PUSH and POP instructions are important instructions that store and retrieve data from
the LIFO (last-in, first-out) stack memory. The microprocessor has six forms of the PUSH
and POP instructions: register, memory, immediate, segment register, flags, and all registers.
The PUSH and POP immediate and the PUSHA and POPA (all registers) forms are not available in the earlier 8086/8088 microprocessors, but are available to the 80286 through the
Core2.
Register addressing allows the contents of any 16-bit register to be transferred to or
from the stack. In the 80386 and above, the 32-bit extended registers and flags (EFLAGS) can
also be pushed or popped from the stack. Memory-addressing PUSH and POP instructions
store the contents of a 16-bit memory location (or 32 bits in the 80386 and above) on the
stack or stack data into a memory location. Immediate addressing allows immediate data to
be pushed onto the stack, but not popped off the stack. Segment register addressing allows the
contents of any segment register to be pushed onto the stack or removed from the stack (ES
may be pushed, but data from the stack may never be popped into ES). The flags may be
pushed or popped from that stack, and the contents of all the registers may be pushed or
popped.

PUSH
The 8086–80286 PUSH instruction always transfers 2 bytes of data to the stack; the 80386
and above transfer 2 or 4 bytes, depending on the register or size of the memory location.
The source of the data may be any internal 16- or 32-bit register, immediate data, any segment register, or any 2 bytes of memory data. There is also a PUSHA instruction that copies
the contents of the internal register set, except the segment registers, to the stack. The
PUSHA (push all) instruction copies the registers to the stack in the following order: AX,
CX, DX, BX, SP, BP, SI, and DI. The value for SP that is pushed onto the stack is whatever
it was before the PUSHA instruction executed. The PUSHF (push flags) instruction copies
the contents of the flag register to the stack. The PUSHAD and POPAD instructions push
and pop the contents of the 32-bit register set found in the 80386 through the Pentium 4. The
PUSHA and POPA instructions do not function in the 64-bit mode of operation for the
Pentium 4.
Whenever data are pushed onto the stack, the first (most-significant) data byte moves to
the stack segment memory location addressed by SP - 1. The second (least-significant) data
byte moves into the stack segment memory location addressed by SP - 2. After the data
are stored by a PUSH, the contents of the SP register decrement by 2. The same is true for a
doubleword push, except that 4 bytes are moved to the stack memory (most-significant byte
first), after which the stack pointer decrements by 4. Figure 4–13 shows the operation of the
PUSH AX instruction. This instruction copies the contents of AX onto the stack where address
SS:3SP - 14⫽ AH, SS:3SP - 24 ⫽ AL , and afterwards SP ⫽ SP - 2 . In 64-bit mode,
8 bytes of the stack are used to store the number pushed onto the stack.
The PUSHA instruction pushes all the internal 16-bit registers onto the stack, as illustrated
in Figure 4–14. This instruction requires 16 bytes of stack memory space to store all eight 16-bit
registers. After all registers are pushed, the contents of the SP register are decremented by 16.
The PUSHA instruction is very useful when the entire register set (microprocessor environment)
of the 80286 and above must be saved during a task. The PUSHAD instruction places the 32-bit
register set on the stack in the 80386 through the Core2. PUSHAD requires 32 bytes of stack
storage space.

123

DATA MOVEMENT INSTRUCTIONS
Stack segment
12FFF

03800
EAX

6 A B 3

6 A B 3

6 A

037FF

B 3

037FE

0 7 F E

ESP

CS

03000
07FE

DS
SS

0 3 0 0

+
3000

37FE

FIGURE 4–13 The effect of the PUSH AX instruction on ESP and stack memory locations
37FFH and 37FEH. This instruction is shown at the point after execution.

The PUSH immediate data instruction has two different opcodes, but in both cases, a 16bit immediate number moves onto the stack; if PUSHD is used, a 32-bit immediate datum is
pushed. If the values of the immediate data are 00H–FFH, the opcode is a 6AH; if the data are
0100H–FFFFH, the opcode is 68H. The PUSH 8 instruction, which pushes 0008H onto the

FIGURE 4–14 The operation of the PUSHA instruction,
showing the location and
order of stack data.

16-bits
AX
CX
DX
BX
SP
BP
SI
SP after PUSHA

DI

124
TABLE 4–8
instruction.

CHAPTER 4

The PUSH

Symbolic
PUSH reg16
PUSH reg32
PUSH mem16
PUSH mem32
PUSH mem64
PUSH seg
PUSH imm8
PUSH imm16
PUSHD imm32
PUSHA
PUSHAD
PUSHF
PUSHFD

Example
PUSH BX
PUSH EDX
PUSH WORD PTR[BX]
PUSH DWORD PTR[EBX]
PUSH QWORD PTR[RBX]
PUSH DS
PUSH ‘R’
PUSH 1000H
PUSHD 20
PUSHA
PUSHAD
PUSHF
PUSHFD

Note
16-bit register
32-bit register
16-bit pointer
32-bit pointer
64-bit pointer (64-bit mode)
Segment register
8-bit immediate
16-bit immediate
32-bit immediate
Save all 16-bit registers
Save all 32-bit registers
Save flags
Save EFLAGS

stack, assembles as 6A08H. The PUSH 1000H instruction assembles as 680010H. Another
example of PUSH immediate is the PUSH ‘A’ instruction, which pushes a 0041H onto the stack.
Here, the 41H is the ASCII code for the letter A.
Table 4–8 lists the forms of the PUSH instruction that include PUSHA and PUSHF. Notice
how the instruction set is used to specify different data sizes with the assembler.

POP
The POP instruction performs the inverse operation of a PUSH instruction. The POP instruction
removes data from the stack and places it into the target 16-bit register, segment register, or a 16bit memory location. In the 80386 and above, a POP can also remove 32-bit data from the stack
and use a 32-bit address. The POP instruction is not available as an immediate POP. The POPF
(pop flags) instruction removes a 16-bit number from the stack and places it into the flag register; the POPFD removes a 32-bit number from the stack and places it into the extended flag register. The POPA (pop all) instruction removes 16 bytes of data from the stack and places them
into the following registers, in the order shown: DI, SI, BP, SP, BX, DX, CX, and AX. This is the
reverse order from the way they were placed on the stack by the PUSHA instruction, causing the
same data to return to the same registers. In the 80386 and above, a POPAD instruction reloads
the 32-bit registers from the stack.
Suppose that a POP BX instruction executes. The first byte of data removed from the stack
(the memory location addressed by SP in the stack segment) moves into register BL. The second
byte is removed from stack segment memory location SP + 1 and is placed into register BH.
After both bytes are removed from the stack, the SP register is incremented by 2. Figure 4–15
shows how the POP BX instruction removes data from the stack and places them into register BX.
The opcodes used for the POP instruction and all of its variations appear in Table 4–9.
Note that a POP CS instruction is not a valid instruction in the instruction set. If a POP CS
instruction executes, only a portion of the address (CS) of the next instruction changes. This
makes the POP CS instruction unpredictable and therefore not allowed.

Initializing the Stack
When the stack area is initialized, load both the stack segment (SS) register and the stack pointer
(SP) register. It is normal to designate an area of memory as the stack segment by loading SS
with the bottom location of the stack segment.

125

DATA MOVEMENT INSTRUCTIONS

Stack segment
0FFFF

01008

EAX
EBX

392F

ESP

1008

392F

3 9

01007

2 F

01006

CS
1008

DS
SS

0000

00000

+

00000

1008

FIGURE 4–15 The POP BX instruction, showing how data are removed from the stack. This
instruction is shown after execution.
TABLE 4–9 The POP
instructions.

Symbolic
POP reg16
POP reg32
POP mem16
POP mem32
POP mem64
POP seg
POPA
POPAD
POPF
POPFD

Example
POP CX
POP EBP
POP WORD PTR[BX+1]
POP DATA3
POP FROG
POP FS
POPA
POPAD
POPF
POPFD

Note
16-bit register
32-bit register
16-bit pointer
32-bit memory address
64-bit memory address (64-bit mode)
Segment register
Pops all 16-bit registers
Pops all 32-bit registers
Pops flags
Pops EFLAGS

For example, if the stack segment is to reside in memory locations 10000H–1FFFFH, load SS
with a 1000H. (Recall that the rightmost end of the stack segment register is appended with a 0H for
real mode addressing.) To start the stack at the top of this 64K-byte stack segment, the stack pointer
(SP) is loaded with a 0000H. Likewise, to address the top of the stack at location 10FFFH, use a
value of 1000H in SP. Figure 4–16 shows how this value causes data to be pushed onto the top of the
stack segment with a PUSH CX instruction. Remember that all segments are cyclic in nature—that
is, the top location of a segment is contiguous with the bottom location of the segment.
In assembly language, a stack segment is set up as illustrated in Example 4–1. The first
statement identifies the start of the stack segment and the last statement identifies the end of the
stack segment. The assembler and linker programs place the correct stack segment address in SS
and the length of the segment (top of the stack) into SP. There is no need to load these registers
in your program unless you wish to change the initial values for some reason.

126

CHAPTER 4

EAX
Stack segment

EBX

1FFFF

ECX

A037

ESP

0000

A037

1FFFE

CS
0000

DS
SS

1000

+

10000
10000
10000

FIGURE 4–16 The PUSH CX instruction, showing the cyclical nature of the stack segment. This
instruction is shown just before execution, to illustrate that the stack bottom is contiguous to the top.

EXAMPLE 4–1
0000
0000 0100[
????
]
0200

STACK_SEG

SEGMENT STACK
DW
100H DUP(?)

STACK_SEG

ENDS

An alternative method for defining the stack segment is used with one of the memory models for the MASM assembler only (refer to Appendix A). Other assemblers do not use models; if
they do, the models are not exactly the same as with MASM. Here, the .STACK statement, followed by the number of bytes allocated to the stack, defines the stack area (see Example 4–2).
The function is identical to Example 4–1. The .STACK statement also initializes both SS and SP.
Note that this text uses memory models that are designed for the Microsoft Macro Assembler
program MASM.
EXAMPLE 4–2
.MODEL SMALL
.STACK 200H ;set stack size

If the stack is not specified by using either method, a warning will appear when the program is linked. The warning may be ignored if the stack size is 128 bytes or fewer. The system
automatically assigns (through DOS) at least 128 bytes of memory to the stack. This memory
section is located in the program segment prefix (PSP), which is appended to the beginning of
each program file. If you use more memory for the stack, you will erase information in the PSP
that is critical to the operation of your program and the computer. This error often causes the
computer program to crash. If the TINY memory model is used, the stack is automatically
located at the very end of the segment, which allows for a larger stack area.

127

DATA MOVEMENT INSTRUCTIONS

4–3

LOAD-EFFECTIVE ADDRESS
There are several load-effective address instructions in the microprocessor instruction set. The LEA
instruction loads any 16-bit register with the offset address, as determined by the addressing mode
selected for the instruction. The LDS and LES variations load any 16-bit register with the offset
address retrieved from a memory location, and then load either DS or ES with a segment address
retrieved from memory. In the 80386 and above, LFS, LGS, and LSS are added to the instruction set,
and a 32-bit register can be selected to receive a 32-bit offset from memory. In the 64-bit mode for
the Pentium 4, the LDS and LES instructions are invalid and not used because the segments have no
function in the flat memory model. Table 4–10 lists the load-effective address instructions.

LEA
The LEA instruction loads a 16- or 32-bit register with the offset address of the data specified by
the operand. As the first example in Table 4–9 shows, the operand address NUMB is loaded into
register AX, not the contents of address NUMB.
By comparing LEA with MOV, we observe that LEA BX,[DI] loads the offset address
specified by [DI] (contents of DI) into the BX register; MOV BX,[DI] loads the data stored at the
memory location addressed by [DI] into register BX.
Earlier in the text, several examples were presented by using the OFFSET directive. The
OFFSET directive performs the same function as an LEA instruction if the operand is a displacement. For example, the MOV BX,OFFSET LIST performs the same function as LEA
BX,LIST. Both instructions load the offset address of memory location LIST into the BX register. See Example 4–3 for a short program that loads SI with the address of DATA1 and DI with
the address of DATA2. It then exchanges the contents of these memory locations. Note that the
LEA and MOV with OFFSET instructions are both the same length (3 bytes).
EXAMPLE 4–3
0000
0000 2000
0002 3000
0000
0017
001A
001D
001F
0021
0023

TABLE 4–10

BE
BF
8B
8B
89
89

DATA1
DATA2

0000 R
0002 R
1C
0D
0C
1D

.MODEL SMALL
.DATA
DW
2000H
DW
3000H
.CODE
.STARTUP
LEA SI,DATA1
MOV DI,OFFSET DATA2
MOV BX,[SI]
MOV CX,[DI]
MOV [SI],CX
MOV [DI],BX
.EXIT
END

;select small model
;start data segment
;define DATA1
;define DATA2
;start code segment
;start program
;address DATA1 with SI
;address DATA2 with DI
;exchange DAT1 with DATA2

Load-effective address instructions.

Assembly Language
LEA AX,NUMB
LEA EAX,NUMB
LDS DI,LIST
LDS EDI,LIST1
LES BX,CAT
LFS DI,DATA1
LGS SI,DATA5
LSS SP,MEM

Operation
Loads AX with the offset address of NUMB
Loads EAX with the offset address of NUMB
Loads DS and DI with the 32-bit contents of data segment memory location LIST
Loads the DS and EDI with the 48-bit contents of data segment memory location LIST1
Loads ES and BX with the 32-bit contents of data segment memory location CAT
Loads FS and DI with the 32-bit contents of data segment memory location DATA1
Loads GS and SI with the 32-bit contents of data segment memory location DATA5
Loads SS and SP with the 32-bit contents of data segment memory location MEM

128

CHAPTER 4

But why is the LEA instruction available if the OFFSET directive accomplishes the same
task? First, OFFSET only functions with simple operands such as LIST. It may not be used for an
operand such as [DI], LIST [SI], and so on. The OFFSET directive is more efficient than the
LEA instruction for simple operands. It takes the microprocessor longer to execute the LEA
BX,LIST instruction than the MOV BX,OFFSET LIST. The 80486 microprocessor, for example,
requires two clocks to execute the LEA BX,LIST instruction and only one clock to execute MOV
BX,OFFSET LIST. The reason that the MOV BX,OFFSET LIST instruction executes faster is
because the assembler calculates the offset address of LIST, whereas the microprocessor calculates the address for the LEA instruction. The MOV BX,OFFSET LIST instruction is actually
assembled as a move immediate instruction and is more efficient.
Suppose that the microprocessor executes an LEA BX,[DI] instruction and DI contains a
1000H. Because DI contains the offset address, the microprocessor transfers a copy of DI into
BX. A MOV BX,DI instruction performs this task in less time and is often preferred to the LEA
BX,[DI] instruction.
Another example is LEA SI,[BX + DI]. This instruction adds BX to DI and stores the sum
in the SI register. The sum generated by this instruction is a modulo-64K sum. (A modulo-64K
sum drops any carry out of the 16-bit result.) If BX = 1000H and DI = 2000H , the offset
address moved into SI is 3000H. If BX = 1000H and DI = FF00H , the offset address is 0F00H
instead of 10F00H. Notice that the second result is a modulo-64K sum of 0F00H.

LDS, LES, LFS, LGS, and LSS
The LDS, LES, LFS, LGS, and LSS instructions load any 16-bit or 32-bit register with an offset
address, and the DS, ES, FS, GS, or SS segment register with a segment address. These instructions use any of the memory-addressing modes to access a 32-bit or 48-bit section of memory
that contains both the segment and offset address. The 32-bit section of memory contains a 16bit offset and segment address, while the 48-bit section contains a 32-bit offset and a segment
address. These instructions may not use the register addressing mode 1MOD = 112 . Note that
the LFS, LGS, and LSS instructions are only available on 80386 and above, as are the 32-bit
registers.
Figure 4–17 illustrates an example LDS BX,[DI] instruction. This instruction transfers the
32-bit number, addressed by DI in the data segment, into the BX and DS registers. The LDS,
LES, LFS, LGS, and LSS instructions obtain a new far address from memory. The offset address
appears first, followed by the segment address. This format is used for storing all 32-bit memory
addresses.
A far address can be stored in memory by the assembler. For example, the ADDR DD FAR
PTR FROG instruction stores the offset and segment address (far address) of FROG in 32 bits of
memory at location ADDR. The DD directive tells the assembler to store a doubleword (32-bit
number) in memory address ADDR.
In the 80386 and above, an LDS EBX,[DI] instruction loads EBX from the 4-byte section
of memory addressed by DI in the data segment. Following this 4-byte offset is a word that is
loaded to the DS register. Notice that instead of addressing a 32-bit section of memory, the 80386
and above address a 48-bit section of the memory whenever a 32-bit offset address is loaded to a
32-bit register. The first 4 bytes contain the offset value loaded to the 32-bit register and the last
2 bytes contain the segment address.
The most useful of the load instructions is the LSS instruction. Example 4–4 shows a short
program that creates a new stack area after saving the address of the old stack area. After executing some dummy instructions, the old stack area is reactivated by loading both SS and SP with
the LSS instruction. Note that the CLI (disable interrupt) and STI (enable interrupt) instructions must be included to disable interrupts. (This topic is discussed near the end of this chapter.)
Because the LSS instruction functions in the 80386 or above, the .386 statement appears after

129

DATA MOVEMENT INSTRUCTIONS
Data segment
1FFFF
EAX
EBX

6 F 2 A

ESP
EBP
ESI

3 0

11003

0 0

11002

1 2

11001

7 A

11000

1 0 0 0

EDI

1000
CS
DS

+

1 0 0 0
10000

10000
11000

FIGURE 4–17 The LDS BX,[DI] instruction loads register BX from addresses 11000H and
11001H and register DS from locations 11002H and 11003H. This instruction is shown at the
point just before DS changes to 3000H and BX changes to 127AH.

the .MODEL statement to select the 80386 microprocessor. Notice how the WORD PTR directive is used to override the doubleword (DD) definition for the old stack memory location. If an
80386 or newer microprocessor is in use, it is suggested that the .386 switch be used to develop
software for the 80386 microprocessor. This is true even if the microprocessor is a Pentium,
Pentium Pro, Pentium II, Pentium III, Pentium 4, or Core2. The reason is that the 80486–Core2
microprocessors add only a few additional instructions to the 80386 instruction set, which are
seldom used in software development. If the need arises to use any of the CMPXCHG, CMPXCHG8 (new to the Pentium), XADD or BSWAP instructions, select either the .486 switch for the
80486 microprocessor of the .586 switch for the Pentium. You can even specify the Pentium II
–Core2 using the .686 switch.
EXAMPLE 4–4

0000
0000 00000000
0004 1000 [
????

SADDR
SAREA

.MODEL SMALL
.386
.DATA
DD
?
DW
1000H DUP(?)

;select small model
;select 80386
;start data segment
;old stack address
;new stack area

]
2004 = 2004
0000
0010
0011
0013
0016
0018

FA
8B
A3
8C
A3

C4
0000 R
D0
0002 R

STOP

EQU THIS WORD
;define top of new stack
.CODE
;start code segment
.STARTUP
;start program
CLI
;disable interrupts
MOV AX,SP
;save old SP
MOV WORD PTR SADDR,AX
MOV AX,SS
;save old SS
MOV WORD PTR SADDR+2,AX

130

CHAPTER 4
001B
001D
001F
0022
0024

8C
8E
B8
8B
FB

D8
D0
2004 R
E0

0025 8B C0
0027 8B C0
0029 9F B2 26 0000 R

4–4

MOV
MOV
MOV
MOV
STI

AX,DS
SS,AX
AX,OFFSET STOP
SP,AX

MOV AX,AX
MOV AX,AX
LSS SP,SADDR
.EXIT
END

;load new SS
;load new SP
;enable interrupts
;do some dummy instructions
;get old stack
;exit to DOS
;end program listing

STRING DATA TRANSFERS
There are five string data transfer instructions: LODS, STOS, MOVS, INS, and OUTS. Each
string instruction allows data transfers that are either a single byte, word, or doubleword (or if
repeated, a block of bytes, words, or doublewords). Before the string instructions are presented,
the operation of the D flag-bit (direction), DI, and SI must be understood as they apply to the
string instructions. In the 64-bit mode of the Pentium 4 and Core2, quadwords are also used with
the string instructions such as LODSQ.

The Direction Flag
The direction flag (D, located in the flag register) selects the auto-increment 1D ⫽ 02 or
the auto-decrement 1D ⫽ 12 operation for the DI and SI registers during string operations.
The direction flag is used only with the string instructions. The CLD instruction clears the D
flag 1D ⫽ 02 and the STD instruction sets it 1D ⫽ 12. Therefore, the CLD instruction selects
the auto-increment mode 1D ⫽ 02 and STD selects the auto-decrement mode 1D ⫽ 12.
Whenever a string instruction transfers a byte, the contents of DI and/or SI are incremented
or decremented by 1. If a word is transferred, the contents of DI and/or SI are incremented or
decremented by 2. Doubleword transfers cause DI and/or SI to increment or decrement by 4. Only
the actual registers used by the string instruction are incremented or decremented. For example,
the STOSB instruction uses the DI register to address a memory location. When STOSB executes,
only the DI register is incremented or decremented without affecting SI. The same is true of the
LODSB instruction, which uses the SI register to address memory data. A LODSB instruction
will only increment or decrement SI without affecting DI.

DI and SI
During the execution of a string instruction, memory accesses occur through either or both of the DI
and SI registers. The DI offset address accesses data in the extra segment for all string instructions
that use it. The SI offset address accesses data, by default, in the data segment. The segment assignment of SI may be changed with a segment override prefix, as described later in this chapter. The DI
segment assignment is always in the extra segment when a string instruction executes. This assignment cannot be changed. The reason that one pointer addresses data in the extra segment and the
other in the data segment is so that the MOVS instruction can move 64K bytes of data from one segment of memory to another.
When operating in the 32-bit mode in the 80386 microprocessor or above, the EDI and ESI
registers are used in place of DI and SI. This allows string using any memory location in the
entire 4G-byte protected mode address space of the microprocessor.

LODS
The LODS instruction loads AL, AX, or EAX with data stored at the data segment offset address
indexed by the SI register. (Note that only the 80386 and above use EAX.) After loading AL with

131

DATA MOVEMENT INSTRUCTIONS

TABLE 4–11 Forms of the
LODS instruction.

Assembly Language
LODSB
LODSW
LODSD
LODSQ
LODS LIST
LODS DATA1
LODS FROG

Operation
AL = DS:3SI4; SI = SI ; 1
AX = DS:3SI4; SI = SI ; 2
EAX = DS:3SI4; SI = SI ; 4
RAX = 3RSI4; RSI = RSI ; 8 164-bit mode2
AL = DS:3SI4; SI = SI ; 1 1if LIST is a byte2
AX = DS:3SI4; SI = SI ; 2 1if DATA1 is a word2
EAX = DS:3SI4; SI = SI ; 4 1if FROG is a doubleword2

Note: The segment register can be overridden with a segment override prefix as in LODS
ES:DATA4.

a byte, AX with a word, or EAX with a doubleword, the contents of SI increment, if D = 0 or
decrement, if D = 1 . A 1 is added to or subtracted from SI for a byte-sized LODS, a 2 is added or
subtracted for a word-sized LODS, and a 4 is added or subtracted for a doubleword-sized LODS.
Table 4–11 lists the permissible forms of the LODS instruction. The LODSB (loads a byte)
instruction causes a byte to be loaded into AL, the LODSW (loads a word) instruction causes
a word to be loaded into AX, and the LODSD (loads a doubleword) instruction causes a doubleword to be loaded into EAX. Although rare, as an alternative to LODSB, LODSW, LODSD,
and LODSQ, the LODS instruction may be followed by a byte-, word- or doubleword-sized
operand to select a byte, word, or doubleword transfer. Operands are often defined as bytes
with DB, as words with DW, and as doublewords with DD. The DB pseudo-operation defines
byte(s), the DW pseudo-operation defines word(s), and the DD pseudo-operations define
doubleword(s).
Figure 4–18 shows the effect of executing the LODSW instruction if the D flag ⫽ 0,
SI ⫽ 1000H, and DS ⫽ 1000H. Here, a 16-bit number stored at memory locations 11000H
and 1l001H moves into AX. Because D ⫽ 0 and this is a word transfer, the contents of SI increment by 2 after AX loads with memory data.

STOS
The STOS instruction stores AL, AX, or EAX at the extra segment memory location addressed
by the DI register. (Note that only the 80386–Core2 use EAX and doublewords.) Table 4–12 lists
all forms of the STOS instruction. As with LODS, a STOS instruction may be appended with a
B, W, or D for byte, word, or doubleword transfers. The STOSB (stores a byte) instruction
stores the byte in AL at the extra segment memory location addressed by DI. The STOSW
(stores a word) instruction stores AX in the extra segment memory location addressed by DI. A
doubleword is stored in the extra segment location addressed by DI with the STOSD (stores a
doubleword) instruction. After the byte (AL), word (AX), or doubleword (EAX) is stored, the
contents of DI increment or decrement.

STOS with a REP. The repeat prefix (REP) is added to any string data transfer instruction,
except the LODS instruction. It doesn’t make any sense to perform a repeated LODS operation.
The REP prefix causes CX to decrement by 1 each time the string instruction executes. After CX
decrements, the string instruction repeats. If CX reaches a value of 0, the instruction terminates
and the program continues with the next sequential instruction. Thus, if CX is loaded with 100
and a REP STOSB instruction executes, the microprocessor automatically repeats the STOSB
instruction 100 times. Because the DI register is automatically incremented or decremented after
each datum is stored, this instruction stores the contents of AL in a block of memory instead of a
single byte of memory. In the Pentium 4 operated in 64-bit mode, the RCX register is used with
the REP prefix.

132

CHAPTER 4
Data segment
1FFFF

A 0 3 2

EAX

A 0 3 2

A 0

11001

3 2

11000

ESP
EBP
ESI

1 0 0 0

EDI

10000

1000

CS
DS

10000
1 0 0 0

+

11000

FIGURE 4–18 The operation of the LODSW instruction if DS = 1000H, D = 0, 11000H = 32,
and 11001H = A0. This instruction is shown after AX is loaded from memory, but before SI
increments by 2.

Suppose that the STOSW instruction is used to clear an area of memory called Buffer
using a count called Count and the program is to function call ClearBuffer in the C+ + environment using the inline assembler. (See Example 4–5.) Note that both the Count and Buffer
address are transferred to the function. The REP STOSW instruction clears the memory
buffer called Buffer. Notice that Buffer is a pointer to the actual buffer that is cleared by this
function.

TABLE 4–12 Forms of the
STOS instruction.

Assembly Language
STOSB
STOSW
STOSD
STOSQ
STOS LIST
STOS DATA3
STOS DATA4

Operation
ES:3DI4
ES:3DI4
ES:3DI4
3RDI4 =
ES:3DI4
ES:3DI4
ES:3DI4

= AL; DI = DI ; 1
= AX; DI = DI ; 2
= EAX; DI = DI ; 4
RAX; RDI = RDI ; 8 164-bit mode2
= AL; DI = DI ; 1 1if LIST is a byte2
= AX; DI = DI ; 2 1if DATA3 is a word2
= EAX; DI = DI ; 4 1if DATA4 is a doubleword2

133

DATA MOVEMENT INSTRUCTIONS

EXAMPLE 4–5
void ClearBuffer (int count,
{
_asm{
push edi
push es
push ds
mov ax,0
mov ecx, count
mov edi, buffer
pop es
rep stosw
pop es
pop edi
}
}

short* buffer)

;save registers

;load ES with DS
;clear Buffer
;restore registers

The operands in a program can be modified by using arithmetic or logic operators such as
multiplication (*). Other operators appear in Table 4–13.

MOVS
One of the more useful string data transfer instructions is MOVS, because it transfers data from
one memory location to another. This is the only memory-to-memory transfer allowed in the
8086–Pentium 4 microprocessors. The MOVS instruction transfers a byte, word, or doubleword
from the data segment location addressed by SI to the extra segment location addressed by SI. As
with the other string instructions, the pointers then are incremented or decremented, as dictated
by the direction flag. Table 4–14 lists all the permissible forms of the MOVS instruction. Note
that only the source operand (SI), located in the data segment, may be overridden so that another
segment may be used. The destination operand (DI) must always be located in the extra segment.
It is often necessary to transfer the contents of one area of memory to another. Suppose that
we have two blocks of doubleword memory, blockA and blockB, and we need to copy blockA
into blockB. This can be accomplished using the MOVSD instruction as illustrated in Example
4–6, which is a C+ + language function written using the inline assembler. The function receives
three pieces of information from the caller: blockSize and the addresses of blockA and blockB.
Note that all data are in the data segment in a Visual C+ + program so we need to copy DS into
ES, which is done using a PUSH DS followed by a POP ES. We also need to save all registers
that we changed except for EAX, EBX, ECX, and EDX.
Example 4–7 shows the same function written in C+ + exclusively, so the two methods
can be compared and contrasted. Example 4–8 shows the assembly language version of

TABLE 4–13

Operator
+
*
/
MOD
AND
OR
NOT

Common operand modifiers.

Example
MOV AL,6 + 3
MOV AL,6 -3
MOV AL,4*3
MOV AX,12>5
MOV AX,12 MOD 7
MOV AX,12 AND 4
MOV EAX,12 OR 1
MOV AL,NOT 1

Comment
Copies 9 into AL
Copies 3 into AL
Copies 12 into AL
Copies 2 into AX (remainder is lost)
Copies 5 into AX (quotient is lost)
Copies 4 into AX 11100 AND 0100 = 01002
Copies 13 into EAX 11100 OR 0001 = 11012
Copies 254 into AL 1NOT 0000 0001 = 1111 1110 or 2542

134

CHAPTER 4

TABLE 4–14

Forms of the MOVS instruction.

Assembly Language

Operation

MOVSB

ES:[DI] = DS:[SI]; DI = DI ± 1; SI = SI ± 1 (byte transferred)

MOVSW

ES:[DI] = DS:[SI]; DI = DI ± 2; SI = SI ± 2 (word transferred)

MOVSD

ES:[DI] = DS:[SI]; DI = DI ± 4; SI = SI ± 4 (doubleword transferred)

MOVSQ

[RDI] = [RSI]; RDI = RDI ± 8; RSI = RSI ± 8 (64-bit mode)

MOVS BYTE1, BYTE2

ES:[DI] = DS:[SI]; DI = DI ± 1; SI = SI ± 1 (byte transferred if
BYTE1 and BYTE2 are bytes)

MOVS WORD1,WORD2

ES:[DI] = DS:[SI]; DI = DI ± 2; SI = SI ± 2 (word transferred if
WORD1 and WORD2 are words)

MOVS TED,FRED

ES:[DI] = DS:[SI]; DI = DI ± 4; SI = SI ± 4 (doubleword transferred
if TED and FRED are doublewords)

Example 4–7 for comparison to Example 4–6. Notice how much shorter the assembly language
version is compared to the C+ + version generated in Example 4–8. Admittedly the C+ + version is a little easier to type, but if execution speed is important, Example 4–6 will run much
faster than Example 4–7.

EXAMPLE 4–6
//Function that copies blockA into blockB using the inline assembler
//
void TransferBlocks (int blockSize, int* blockA, int* blockB)
{
_asm{
push es
;save registers
push edi
push esi
push ds
;copy DS into ES
pop es
mov esi, blockA
;address blockA
mov edi, blockB
;address blockB
mov ecx, blockSize
;load count
rep movsd
;move data
pop esi
pop edi
pop es
;restore registers
}
}

EXAMPLE 4–7
//C++ version of Example 4–6
//
void TransferBlocks (int blockSize, int* blockA, int* blockB)
{
for (int a = 0; a < blockSize; a++)
{
blockA = blockB++;
blockA++;
}
}

DATA MOVEMENT INSTRUCTIONS

135

EXAMPLE 4–8
void TransferBlocks(int blockSize, int* blockA, int* blockB)
{
004136A0 push
ebp
004136A1 mov
ebp,esp
004136A3 sub
esp,0D8h
004136A9 push
ebx
004136AA push
esi
004136AB push
edi
004136AC push
ecx
004136AD lea
edi,[ebp-0D8h]
004136B3 mov
ecx,36h
004136B8 mov
eax,0CCCCCCCCh
004136BD rep stos
dword ptr [edi]
004136BF pop
ecx
004136C0 mov
dword ptr [ebp-8],ecx
for( int a = 0; a < blockSize; a++ )
004136C3 mov
dword ptr [a],0
004136CA jmp
TransferBlocks+35h (4136D5h)
004136CC mov
eax,dword ptr [a]
004136CF add
eax, 1
004136D2 mov
dword ptr [a],eax
004136D5 mov
eax,dword ptr [a]
004136D8 cmp
eax,dword ptr [blockSize]
004136DB jge
TransferBlocks+57h (4136F7h)
{
blockA = blockB++;
004136DD mov
eax,dword ptr [blockB]
004136E0 mov
dword ptr [blockA],eax
004136E3 mov
ecx,dword ptr [blockB]
004136E6 add
ecx,4
004136E9 mov
dword ptr [blockB],ecx
blockA++;
004136EC mov
eax,dword ptr [blockA]
004136EF add
eax,4
004136F2 mov
dword ptr [blockA],eax
}
004136F5 jmp
TransferBlocks+2Ch (4136CCh)
}
004136F7 pop
edi
004136F8 pop
esi
004136F9 pop
ebx
004136FA mov
esp,ebp
004136FC pop
ebp
004136FD ret
0Ch

INS
The INS (input string) instruction (not available on the 8086/8088 microprocessors) transfers a
byte, word, or doubleword of data from an I/O device into the extra segment memory location
addressed by the DI register. The I/O address is contained in the DX register. This instruction is
useful for inputting a block of data from an external I/O device directly into the memory. One
application transfers data from a disk drive to memory. Disk drives are often considered and
interfaced as I/O devices in a computer system.
As with the prior string instructions, there are three basic forms of the INS. The INSB
instruction inputs data from an 8-bit I/O device and stores it in the byte-sized memory location
indexed by SI. The INSW instruction inputs 16-bit I/O data and stores it in a word-sized memory location. The INSD instruction inputs a doubleword. These instructions can be repeated
using the REP prefix, which allows an entire block of input data to be stored in the memory
from an I/O device. Table 4–15 lists the various forms of the INS instruction. Note that in the
64-bit mode there is no 64-bit input, but the memory address is 64 bits and located in RDI for
the INS instructions.

136

CHAPTER 4

TABLE 4–15 Forms of the
INS instruction.

Assembly Language
INSB
INSW
INSD
INS LIST
INS DATA4
INS DATA5

Operation
ES:[DI] = [DX]; DI = DI ± 1 (byte transferred)
ES:[DI] = [DX]; DI = DI ± 2 (word transferred)
ES:[DI] = [DX]; DI = DI ± 4 (doubleword transferred)
ES:[DI] = [DX]; DI = DI ± 1 (if LIST is a byte)
ES:[DI] = [DX]; DI = DI ± 2 (if DATA4 is a word)
ES:[DI] = [DX]; DI = DI ± 4 (if DATA5 is a doubleword)

Note: [DX] indicates that DX is the I/O device address. These instructions are not available
on the 8086 and 8088 microprocessors.

Example 4–9 shows a sequence of instructions that inputs 50 bytes of data from an I/O
device whose address is 03ACH and stores the data in extra segment memory array LISTS. This
software assumes that data are available from the I/O device at all times. Otherwise, the software
must check to see if the I/O device is ready to transfer data precluding the use of a REP prefix.
EXAMPLE 4–9

0000
0003
0006
0007
000A

BF 0000 R
BA 03AC
FC
B9 0032
F3/6C

;Using the REP INSB to input
;
MOV DI,OFFSET LISTS
MOV DX,3ACH
CLD
MOV CX,50
REP INSB

data to a memory array
;address array
;address I/O
;auto-increment
;load counter
;input data

OUTS
The OUTS (output string) instruction (not available on the 8086/8088 microprocessors) transfers
a byte, word, or doubleword of data from the data segment memory location address by SI to an
I/O device. The I/O device is addressed by the DX register as it is with the INS instruction. Table
4–16 shows the variations available for the OUTS instruction. In the 64-bit mode for the Pentium
4 and Core2, there is no 64-bit output, but the address in RSI is 64 bits wide.
Example 4–10 shows a short sequence of instructions that transfer data from a data segment memory array (ARRAY) to an I/O device at I/O address 3ACH. This software assumes that
the I/O device is always ready for data.
EXAMPLE 4–10

0000
0003
0006
0007
000A

TABLE 4–16 Forms of the
OUTS instruction.

BE 0064 R
BA 03AC
FC
B9 0064
F3/6E

;Using the REP OUTSB to output data from a memory array
;
MOV SI,OFFSET ARRAY
;address array
MOV DX,3ACH
;address I/O
CLD
;auto-increment
MOV CX,100
;load counter
REP OUTSB
;output data

Assembly Language
OUTSB
OUTSW
OUTSD
OUTS DATA7
OUTS DATA8
OUTS DATA9

Operation
[DX] = DS:[SI]; SI = SI ± 1 (byte transferred)
[DX] = DS:[SI]; SI = SI ± 2 (word transferred)
[DX] = DS:[SI]; SI = SI ± 4 (doubleword transferred)
[DX] = DS:[SI]; SI = SI ± 1 (if DATA7 is a byte)
[DX] = DS:[SI]; SI = SI ± 2 (if DATA8 is a word)
[DX] = DS:[SI]; SI = SI ± 4 (if DATA9 is a doubleword)

Note: [DX] indicates that DX is the I/O device address. These instructions are not available
on the 8086 and 8088 microprocessors.

137

DATA MOVEMENT INSTRUCTIONS

4–5

MISCELLANEOUS DATA TRANSFER INSTRUCTIONS
Don’t be fooled by the term miscellaneous; these instructions are used in programs. The data
transfer instructions detailed in this section are XCHG, LAHF, SAHF, XLAT, IN, OUT, BSWAP,
MOVSX, MOVZX, and CMOV. Because the miscellaneous instructions are not used as often as
a MOV instruction, they have been grouped together and presented in this section.

XCHG
The XCHG (exchange) instruction exchanges the contents of a register with the contents of
any other register or memory location. The XCHG instruction cannot exchange segment registers or memory-to-memory data. Exchanges are byte-, word-, or doubleword-sized (80386 and
above), and they use any addressing mode discussed in Chapter 3, except immediate addressing. Table 4–17 shows some examples of the XCHG instruction. In the 64-bit mode, data sizes
may also be 64 bits for the exchange instruction.
The XCHG instruction, using the 16-bit AX register with another 16-bit register, is the
most efficient exchange. This instruction occupies 1 byte of memory. Other XCHG instructions
require 2 or more bytes of memory, depending on the addressing mode selected.
When using a memory-addressing mode and the assembler, it doesn’t matter which
operand addresses memory. The XCHG AL,[DI] instruction is identical to the XCHG [DI],AL
instruction, as far as the assembler is concerned.
If the 80386 through the Core2 microprocessor is available, the XCHG instruction can
exchange doubleword data. For example, the XCHG EAX,EBX instruction exchanges the contents of the EAX register with the EBX register.

LAHF and SAHF
The LAHF and SAHF instructions are seldom used because they were designed as bridge
instructions. These instructions allowed 8085 (an early 8-bit microprocessor) software to be
translated into 8086 software by a translation program. Because any software that required translation was completed many years ago, these instructions have little application today. The LAHF
instruction transfers the rightmost 8 bits of the flag register into the AH register. The SAHF
instruction transfers the AH register into the rightmost 8 bits of the flag register.
At times, the SAHF instruction may find some application with the numeric coprocessor.
The numeric coprocessor contains a status register that is copied into the AX register with the
FSTSW AX instruction. The SAHF instruction is then used to copy from AH into the flag register. The flags are then tested for some of the conditions of the numeric coprocessor. This is
detailed in Chapter 14, which explains the operation and programming of the numeric coprocessor. Because LAHF and LAFH are legacy instructions, they do not function in the 64-bit mode
and are invalid instructions.

TABLE 4–17

Forms of the XCHG instruction.

Assembly Language
XCHG AL,CL
XCHG CX,BP
XCHG EDX,ESI
XCHG AL,DATA2
XCHG RBX,RCX

Operation
Exchanges the contents of AL with CL
Exchanges the contents of CX with BP
Exchanges the contents of EDX with ESI
Exchanges the contents of AL with data segment memory location DATA2
Exchange the contents of RBX with RCX (64-bit mode)

138

CHAPTER 4

0 5

EAX
EBX

1 0

0 0

05
6 D

+

1000
1005

CS
DS

11006
11005
11004
11003
11002
11001
11000

+

1 0 0 0
10000

11005

FIGURE 4–19
into AL.

10000

The operation of the XLAT instruction at the point just before 6DH is loaded

XLAT
The XLAT (translate) instruction converts the contents of the AL register into a number stored in
a memory table. This instruction performs the direct table lookup technique often used to convert
one code to another. An XLAT instruction first adds the contents of AL to BX to form a memory
address within the data segment. It then copies the contents of this address into AL. This is the
only instruction that adds an 8-bit number to a l6-bit number.
Suppose that a 7-segment LED display lookup table is stored in memory at address
TABLE. The XLAT instruction then uses the lookup table to translate the BCD number in AL to
a 7-segment code in AL. Example 4–11 provides a sequence of instructions that converts from a
BCD code to a 7-segment code. Figure 4–19 shows the operation of this example program if
TABLE = 1000H, DS = 1000H , and the initial value of AL = 05H 15 BCD2 . After the translation, AL = 6DH .
EXAMPLE 4–11

0017 B0 05
0019 BB 1000 R
001C D7

TABLE

DB
DB
DB

3FH, 06H, 5BH, 4FH
66H, 6DH, 7DH, 27H
7FH, 6FH

LOOK:

MOV AL,5
MOV BX,OFFSET TABLE
XLAT

;lookup table

;load AL with 5 (a test number)
;address lookup table
;convert

IN and OUT
Table 4–18 lists the forms of the IN and OUT instructions, which perform I/O operations. Notice
that the contents of AL, AX, or EAX are transferred only between the I/O device and the microprocessor. An IN instruction transfers data from an external I/O device into AL, AX, or EAX; an
OUT transfers data from AL, AX, or EAX to an external I/O device. (Note that only the 80386
and above contain EAX.)
Two forms of I/O device (port) addressing exist for IN and OUT: fixed port and variable
port. Fixed-port addressing allows data transfer between AL, AX, or EAX using an 8-bit I/O port

139

DATA MOVEMENT INSTRUCTIONS

TABLE 4–18
instructions.

In and OUT

Assembly Language
IN AL,p8
IN AX,p8
IN EAX,p8
IN AL,DX
IN AX,DX
IN EAX,DX
OUT p8,AL
OUT p8,AX
OUT p8,EAX
OUT DX,AL
OUT DX,AX
OUT DX,EAX

Operation
8 bits are input to AL from I/O port p8
16 bits are input to AX from I/O port p8
32 bits are input to EAX from I/O port p8
8 bits are input to AL from I/O port DX
16 bits are input to AX from I/O port DX
32 bits are input to EAX from I/O port DX
8 bits are output to I/O port p8 from AL
16 bits are output to I/O port p8 from AX
32 bits are output to I/O port p8 from EAX
8 bits are output to I/O port DX from AL
16 bits are output to I/O port DX from AX
32 bits are output to I/O port DX from EAX

Note: p8 = an 8-bit I/O port number (0000H to 00FFH) and DX = the 16-bit I/O
port number (0000H to FFFFH) held in register DX.

address. It is called fixed-port addressing because the port number follows the instruction’s
opcode, just as it did with immediate addressing. Often, instructions are stored in ROM. A fixedport instruction stored in ROM has its port number permanently fixed because of the nature of
read-only memory. A fixed-port address stored in RAM can be modified, but such a modification
does not conform to good programming practices.
The port address appears on the address bus during an I/O operation. For the 8-bit
fixed-port I/O instructions, the 8-bit port address is zero-extended into a 16-bit address. For
example, if the IN AL,6AH instruction executes, data from I/O address 6AH are input to AL.
The address appears as a 16-bit 006AH on pins A0–A15 of the address bus. Address bus
bits A16–A19 (8086/8088), A16–A23 (80286/80386SX), A16–A24 (80386SL/80386SLC/
80386EX), or A16–A31 (80386–Core2) are undefined for an IN or OUT instruction. Note
that Intel reserves the last 16 I/O ports (FFF0H–FFFFH) for use with some of its peripheral
components.
Variable-port addressing allows data transfers between AL, AX, or EAX and a 16-bit port
address. It is called variable-port addressing because the I/O port number is stored in register DX,
which can be changed (varied) during the execution of a program. The 16-bit I/O port address
appears on the address bus pin connections A0–A15. The IBM PC uses a 16-bit port address to
access its I/O space. The ISA bus I/O space for a PC is located at I/O port 0000H–03FFH. Note
that PCI bus cards may use I/O addresses above 03FFH.
Figure 4–20 illustrates the execution of the OUT 19H,AX instruction, which transfers
the contents of AX to I/O port 19H. Notice that the I/O port number appears as a 0019H on
the 16-bit address bus and that the data from AX appears on the data bus of the microprocessor. The system control signal IOWC (I/O write control) is a logic zero to enable the I/O
device.
A short program that clicks the speaker in the personal computer appears in Example
4–12. The speaker (in DOS only) is controlled by accessing I/O port 61H. If the rightmost
2 bits of this port are set (11) and then cleared (00), a click is heard on the speaker. Note that
this program uses a logical OR instruction to set these 2 bits and a logical AND instruction to
clear them. These logic operation instructions are described in Chapter 5. The MOV
CX,8000H instruction, followed by the LOOP L1 instruction, is used as a time delay. If the
count is increased, the click will become longer; if shortened, the click will become shorter.
To obtain a series of clicks that can be heard, the program must be modified to repeat many
times.

140

CHAPTER 4
Microprocessor-based system

(Port data)
Contents of register AX

Data bus (D0–D15)

(Port address)
0019H

(Port control)

FIGURE 4–20
instruction.

Address bus (A0–A15)

IOWC

The signals found in the microprocessor-based system for an OUT 19H,AX

EXAMPLE 4–12
0000
0100
0102
0104
0106
0109
0109
010B
010D
010F

E4
0C
E6
B9

61
03
61
8000

E2
E4
24
E6

FE
61
FC
61

.MODEL TINY
.CODE
.STARTUP
IN AL,61H
OR AL,3
OUT 61H,AL
MOV CX,8000H
L1:
LOOP L1
IN AL,61H
AND AL,0FCH
OUT 61H,AL
.EXIT
END

;select tiny model
;start code segment
;start program
;read I/O port 61H
;set rightmost two bits
;speaker on
;load delay count
;time delay
;speaker off

MOVSX and MOVZX
The MOVSX (move and sign-extend) and MOVZX (move and zero-extend) instructions are
found in the 80386–Pentium 4 instruction sets. These instructions move data, and at the same time
either sign- or zero-extend it. Table 4–19 illustrates these instructions with several examples of each.
When a number is zero-extended, the most significant part fills with zeros. For example, if
an 8-bit 34H is zero-extended into a 16-bit number, it becomes 0034H. Zero-extension is often
used to convert unsigned 8- or 16-bit numbers into unsigned 16- or 32-bit numbers by using the
MOVZX instruction.
A number is sign-extended when its sign-bit is copied into the most significant part. For
example, if an 8-bit 84H is sign-extended into a 16-bit number, it becomes FF84H. The sign-bit
of an 84H is a 1, which is copied into the most significant part of the sign-extended result. Signextension is most often used to convert 8- or 16-bit signed numbers into 16- or 32-bit signed
numbers by using the MOVSX instruction.

BSWAP
The BSWAP (byte swap) instruction is available only in the 80486–Pentium 4 microprocessors. This instruction takes the contents of any 32-bit register and swaps the first byte with
the fourth, and the second with the third. For example, the BSWAP EAX instruction with

141

DATA MOVEMENT INSTRUCTIONS

TABLE 4–19 The MOVSX
and MOVZX instructions.

Assembly Language

Operation

MOVSX CX,BL

Sign-extends BL into CX

MOVSX ECX,AX

Sign-extends AX into ECX

MOVSX BX,DATA1

Sign-extends the byte at DATA1 into BX

MOVSX EAX,[EDI]

Sign-extends the word at the data segment memory
location addressed by EDI into EAX

MOVSX RAX,[RDI]

Sign-extends the doubleword at address RDI into
RAX (64-bit mode)

MOVZX DX,AL

Zero-extends AL into DX

MOVZX EBP,DI

Zero-extends DI into EBP

MOVZX DX,DATA2

Zero-extends the byte at DATA2 into DX

MOVZX EAX,DATA3

Zero-extends the word at DATA3 into EAX

MOVZX RBX,ECX

Zero-extends ECX into RBX

EAX = 00112233H swaps bytes in EAX, resulting in EAX = 33221100H . Notice that the
order of all 4 bytes is reversed by this instruction. This instruction is used to convert data
between the big and little endian forms. In 64-bit operation for the Pentium 4, all 8 bytes in
the selected operand are swapped.

CMOV
The CMOV (conditional move) class of instruction is new to the Pentium Pro–Core2 instruction
sets. There are many variations of the CMOV instruction. Table 4–20 lists these variations

TABLE 4–20

The conditional move instructions.

Assembly Language
CMOVB
CMOVAE
CMOVBE
CMOVA
CMOVE or CMOVZ
CMOVNE or CMOVNZ
CMOVL
CMOVLE
CMOVG
CMOVGE
CMOVS
CMOVNS
CMOVC
CMOVNC
CMOVO
CMOVNO
CMOVP or CMOVPE
CMOVNP or CMOVPO

Flag(s) Tested

Operation

C=1
C=0
Z = 1 or C = 1
Z = 0 and C = 0
Z=1
Z=0
S!=O
Z = 1 or S ! = O
Z = 0 and S = O
S=O
S=1
S=0
C=1
C=0
O=1
O=0
P=1
P=0

Move if below
Move if above or equal
Move if below or equal
Move of above
Move if equal or move if zero
Move if not equal or move if not zero
Move if less than
Move if less than or equal
Move if greater than
Move if greater than or equal
Move if sign (negative)
Move if no sign (positive)
Move if carry
Move if no carry
Move if overflow
Move if no overflow
Move if parity or move if parity even
Move if no parity or move if parity odd

142

CHAPTER 4

TABLE 4–21 Instructions
that include segments
override prefixes.

Assembly Language

Segment Accessed

Default Segment

MOV AX,DS:[BP]
MOV AX,ES:[BP]
MOV AX,SS:[DI]
MOV AX,CS:LIST
MOV ES:[SI],AX
LODS ES:DATA1
MOV EAX,FS:DATA2
MOV GS:[ECX],BL

Data
Extra
Stack
Code
Extra
Extra
FS
GS

Stack
Stack
Data
Data
Data
Data
Data
Data

of CMOV. These instructions move the data only if the condition is true. For example, the
CMOVZ instruction moves data only if the result from some prior instruction was a zero. The
destination is limited to only a 16- or 32-bit register, but the source can be a 16- or 32-bit register or memory location.
Because this is a new instruction, you cannot use it with the assembler unless the .686
switch is added to the program.

4–6

SEGMENT OVERRIDE PREFIX
The segment override prefix, which may be added to almost any instruction in any memoryaddressing mode, allows the programmer to deviate from the default segment. The segment override prefix is an additional byte that appends the front of an instruction to select an alternate
segment register. About the only instructions that cannot be prefixed are the jump and call instructions that must use the code segment register for address generation. The segment override is also
used to select the FS and GS segments in the 80386 through the Core2 microprocessors.
For example, the MOV AX,[DI] instruction accesses data within the data segment by
default. If required by a program, this can be changed by prefixing the instruction. Suppose that
the data are in the extra segment instead of in the data segment. This instruction addresses the
extra segment if changed to MOV AX,ES:[DI].
Table 4–21 shows some altered instructions that address different memory segments that
are different from normal. Each time an instruction is prefixed with a segment override prefix,
the instruction becomes 1 byte longer. Although this is not a serious change to the length of the
instruction, it does add to the instruction’s execution time. It is usually customary to limit the use
of the segment override prefix and remain in the default segments so that shorter and more efficient software is written.

4–7

ASSEMBLER DETAIL
The assembler (MASM)1 for the microprocessor can be used in two ways: (1) with models that
are unique to a particular assembler, and (2) with full-segment definitions that allow complete
control over the assembly process and are universal to all assemblers. This section of the text pre1The

assembler used throughout this text is the Microsoft MACRO assembler called MASM, version 6.1X.

143

DATA MOVEMENT INSTRUCTIONS

sents both methods and explains how to organize a program’s memory space by using the assembler. It also explains the purpose and use of some of the more important directives used with this
assembler. Appendix A provides additional detail about the assembler.
In most cases, the inline assembler found in Visual C+ + is used for developing assembly
code for use in a C+ + program, but there are occasions that require separate assembly modules
writing using the assembler. This section of the text contrasts, where possible, the inline assembler and the assembler.

Directives
Before the format of an assembly language program is discussed, some details about the directives (pseudo-operations) that control the assembly process must be learned. Some common
assembly language directives appear in Table 4–22. Directives indicate how an operand or section of a program is to be processed by the assembler. Some directives generate and store information in the memory; others do not. The DB (define byte) directive stores bytes of data in the
memory, whereas the BYTE PTR directive never stores data. The BYTE PTR directive indicates
the size of the data referenced by a pointer or index register. Note that none of the directives
function in the inline assembler program that is a part of Visual C+ + . If you are using the inline
assembler exclusively, you can skip this part of the text. Be aware that complex sections of
assembly code are still written using MASM.
Note that by default the assembler accepts only 8086/8088 instructions, unless a program
is preceded by the .686 or .686P directive or one of the other microprocessor selection switches.
The .686 directive tells the assembler to use the Pentium Pro instruction set in the real mode, and
the .686P directive tells the assembler to use the Pentium Pro protected mode instruction set.
Most modern software is written assuming that the microprocessor is a Pentium Pro or newer, so
the .686 switch is often used. Windows 95 was the first major operating system to use a 32-bit
architecture that conforms to the 80386. Windows XP requires a Pentium class machine (.586
switch) using at least a 233MHz microprocessor.

Storing Data in a Memory Segment. The DB (define byte), DW (define word), and DD
(define doubleword) directives, first presented in Chapter 1, are most often used with MASM to
define and store memory data. If a numeric coprocessor executes software in the system, the DQ
(define quadword) and DT (define ten bytes) directives are also common. These directives
label a memory location with a symbolic name and indicate its size.
Example 4–13 shows a memory segment that contains various forms of data definition
directives. It also shows the full-segment definition with the first SEGMENT statement to indicate
the start of the segment and its symbolic name. Alternately, as in past examples in this and prior
chapters, the SMALL model can be used with the .DATA statement. The last statement in this
example contains the ENDS directive, which indicates the end of the segment. The name of the
segment (LIST_SEG) can be anything that the programmer desires to call it. This allows a program to contain as many segments as required.
EXAMPLE 4–13
;Using the DB, DW, and DD directives
;
LIST_SEG
SEGMENT

0000
0000
0003
0004
0005
0006
000A

01 02 03
45
41
F0
000C 000D
0200

DATA1 DB
DB
DB
DB
DATA2 DW
DW

1,2,3
45H
'A'
11110000B
12,13
LIST1

;define bytes
;hexadecimal
;ASCII
;binary
;define words
;symbolic

144

CHAPTER 4

TABLE 4–22

Directive
.286
.286P
.386
.386P
.486
.486P
.586
.586P
.686
.686P
.287
.387
.CODE
.DATA
.EXIT
.MODEL
.STACK
.STARTUP
ALIGN n
ASSUME
BYTE
DB
DD
DQ
DT
DUP
DW
DWORD
END
ENDM
ENDP
ENDS
EQU
FAR
MACRO
NEAR
OFFSET
ORG
OWORD
PROC
PTR
QWORD
SEGMENT
STACK
STRUC
USES
USE16
USE32
WORD

Common MASM directives.

Function
Selects the 80286 instruction set
Selects the 80286 protected mode instruction set
Selects the 80386 instruction set
Selects the 80386 protected mode instruction set
Selects the 80486 instruction set
Selects the 80498 protected mode instruction set
Selects the Pentium instruction set
Selects the Pentium protected mode instruction set
Selects the Pentium Pro–Core2 instruction set
Selects the Pentium Pro–Core2 protected mode instruction set
Selects the 80287 math coprocessor
Selects the 80387 math coprocessor
Indicates the start of the code segment (models only)
Indicates the start of the data segment (models only)
Exits to DOS (models only)
Selects the programming model
Selects the start of the stack segment (models only)
Indicates the starting instruction in a program (models only)
Align to boundary n (n = 2 for words, n = 4 for doublewords)
Informs the assembler to name each segment (full segments only)
Indicates byte-sized as in BYTE PTR
Defines byte(s) (8 bits)
Defines doubleword(s) (32 bits)
Defines quadwords(s) (64 bits)
Defines ten byte(s) (80 bits)
Generates duplicates
Define word(s) (16 bits)
Indicates doubleword-sized, as in DWORD PTR
Ends a program file
Ends a MACRO sequence
Ends a procedure
Ends a segment or data structure
Equates data or a label to a label
Defines a far pointer, as in FAR PTR
Designates the start of a MACRO sequence
Defines a near pointer, as in NEAR PTR
Specifies an offset address
Sets the origin within a segment
Indicates octalwords, as in OWORD PTR
Starts a procedure
Designates a pointer
Indicates quadwords, as in QWORD PTR
Starts a segment for full segments
Starts a stack segment for full segments
Defines the start of a data structure
Automatically pushes and pops registers
Uses 16-bit instruction mode
Uses 32-bit instruction mode
Indicates word-sized, as in WORD PTR

145

DATA MOVEMENT INSTRUCTIONS
000C
000E
0012
0016
001A
001B

0025
0026

0226

027E

2345
00000300
4007DF3B
544269E1
00
000A[
??
]
00
0100[
0000
]
0016[
????????
]
0064[
06
]

02E2

DW
DATA3 DD
DD
DD
LISTA DB
LISTB DB

2345H
300H
2.123
3.34E+12
?
10 DUP(?)

ALIGN 2

;hexadecimal
;define doubleword
;real
;real
;reserve 1 byte
;reserve 10 bytes

;set word boundary

LISTC DW
LISTD DD

100H DUP(0)
22 DUP(?)

;reserve 100H words
;reserve 22 doublewords

SIXES DB

100 DUP(6)

;reserve 100 bytes

LIST_SEG

ENDS

Example 4–13 shows various forms of data storage for bytes at DATA1. More than 1 byte
can be defined on a line in binary, hexadecimal, decimal, or ASCII code. The DATA2 label
shows how to store various forms of word data. Doublewords are stored at DATA3; they include
floating-point, single-precision real numbers.
Memory is reserved for use in the future by using a question mark (?) as an operand for a
DB, DW, or DD directive. When a ? is used in place of a numeric or ASCII value, the assembler
sets aside a location and does not initialize it to any specific value. (Actually, the assembler usually stores a zero into locations specified with a?.) The DUP (duplicate) directive creates an
array, as shown in several ways in Example 4–12. A 10 DUP (?) reserves 10 locations of memory, but stores no specific value in any of the 10 locations. If a number appears within the ( ) part
of the DUP statement, the assembler initializes the reserved section of memory with the data
indicated. For example, the LIST2 DB 10 DUP (2) instruction reserves 10 bytes of memory for
array LIST2 and initializes each location with a 02H.
The ALIGN directive, used in this example, makes sure that the memory arrays are stored
on word boundaries. An ALIGN 2 places data on word boundaries and an ALIGN 4 places them
on doubleword boundaries. In the Pentium–Pentium 4, quadword data for double-precision
floating-point numbers should use ALIGN 8. It is important that word-sized data are placed at
word boundaries and doubleword-sized data are placed at doubleword boundaries. If not,
the microprocessor spends additional time accessing these data types. A word stored at an oddnumbered memory location takes twice as long to access as a word stored at an even-numbered
memory location. Note that the ALIGN directive cannot be used with memory models because
the size of the model determines the data alignment. If all doubleword data are defined first, followed by word-sized and then byte-sized data, the ALIGN statement is not necessary to align
data correctly.

ASSUME, EQU, and ORG. The equate directive (EQU) equates a numeric, ASCII, or label to
another label. Equates make a program clearer and simplify debugging. Example 4–14 shows
several equate statements and a few instructions that show how they function in a program.
EXAMPLE 4–14

= 000A
= 0009
0000 B0 0A
0002 04 09

;Using equate directive
;
TEN
EQU 10
NINE
EQU 9
MOV AL,TEN
ADD AL,NINE

146

CHAPTER 4

The THIS directive always appears as THIS BYTE, THIS WORD, THIS DWORD, or
THIS QWORD. In certain cases, data must be referred to as both a byte and a word. The assembler can only assign either a byte, word, or doubleword address to a label. To assign a byte label
to a word, use the software listed in Example 4–15.
EXAMPLE 4–15

0000
0300

;Using the THIS and ORG directives
;
DATA_SEG
SEGMENT
ORG

300H

= 0300
0300
0302

DATA1 EQU
DATA2 DW
DATA_SEG

THIS BYTE
?
ENDS

0000
0000 8A 1E 0300 R
0004 A1 0300 R
0007 8A 3E 0301 R

CODE_SEG
SEGMENT 'CODE'
ASSUME CS:CODE_SEG, DS:DATA_SEG
MOV BL,DATA1
MOV AX,DATA2
MOV BH,DATA1+1

000B

CODE_SEG

ENDS

This example also illustrates how the ORG (origin) statement changes the starting offset address of the data in the data segment to location 300H. At times, the origin of data or the
code must be assigned to an absolute offset address with the ORG statement. The ASSUME
statement tells the assembler what names have been chosen for the code, data, extra, and
stack segments. Without the ASSUME statement, the assembler assumes nothing and automatically uses a segment override prefix on all instructions that address memory data. The
ASSUME statement is only used with full-segment definitions, as described later in this section of the text.

PROC and ENDP. The PROC and ENDP directives indicate the start and end of a procedure
(subroutine). These directives force structure because the procedure is clearly defined. Note that
if structure is to be violated for whatever reason, use the CALLF, CALLN, RETF, and RETN
instructions. Both the PROC and ENDP directives require a label to indicate the name of the procedure. The PROC directive, which indicates the start of a procedure, must also be followed with
a NEAR or FAR. A NEAR procedure is one that resides in the same code segment as the
program. A FAR procedure may reside at any location in the memory system. Often the call
NEAR procedure is considered to be local, and the call FAR procedure is considered to be
global. The term global denotes a procedure that can be used by any program; local defines a
procedure that is only used by the current program. Any labels that are defined within the procedure block are also defined as either local (NEAR) or global (FAR).
Example 4–16 shows a procedure that adds BX, CX, and DX and stores the sum in register AX. Although this procedure is short and may not be particularly useful, it does illustrate how
to use the PROC and ENDP directives to delineate the procedure. Note that information about
the operation of the procedure should appear as a grouping of comments that show the registers
changed by the procedure and the result of the procedure.
EXAMPLE 4–16

0000

;A procedure that adds BX, CX, and DX with the
;sum stored in AX
;
ADDEM PROC FAR
;start of procedure

147

DATA MOVEMENT INSTRUCTIONS
0000
0002
0004
0006

03 D9
03 DA
8B C3
CB

ADD
ADD
MOV
RET

0007

BX,CX
BX,DX
AX,BX

ADDEM ENDP

;end of procedure

If version 6.x of the Microsoft MASM assembler program is available, the PROC directive
specifies and automatically saves any registers used within the procedure. The USES statement
indicates which registers are used by the procedure, so that the assembler can automatically save
them before your procedure begins and restore them before the procedure ends with the RET
instruction. For example, the ADDS PROC USES AX BX CX statement automatically pushes
AX, BX, and CX on the stack before the procedure begins and pops them from the stack before
the RET instruction executes at the end of the procedure. Example 4–17 illustrates a procedure
written using MASM version 6.x that shows the USES statement. Note that the registers in the
list are not separated by commas, but by spaces, and the PUSH and POP instructions are displayed in the procedure listing because it was assembled with the .LIST ALL directive. The
instructions prefaced with an asterisk (*) are inserted by the assembler and were not typed in the
source file. The USES statement appears elsewhere in this text, so if MASM version 5.10 is in
use, the code will need to be modified.
EXAMPLE 4–17

0000

ADDS

0000
0001
0002
0003
0005
0007
0009

53
51
52
03
03
03
8B

000B
000C
000D
000E

5A
59
5B
C3

000F

*
*
*

;A procedure that includes the USES directive to
;save BX, CX, and DX on the stack and restore them
;before the return instruction.
PROC
NEAR
USES BX CX DX
push
push
push
ADD
ADD
ADD
MOV
RET
pop
pop
pop
ret

D8
CB
D1
C2
*
*
*
*
ADDS

bx
cx
dx
BX,AX
CX,BX
DX,CX
AX,DX
dx
cx
bx
0000h

ENDP

Memory Organization
The assembler uses two basic formats for developing software: One method uses models and the
other uses full-segment definitions. Memory models, as presented in this section and briefly in
earlier chapters, are unique to the MASM assembler program. The TASM assembler also uses
memory models, but they differ somewhat from the MASM models. The full-segment definitions are common to most assemblers, including the Intel assembler, and are often used for software development. The models are easier to use for simple tasks. The full-segment definitions
offer better control over the assembly language task and are recommended for complex programs. The model was used in early chapters because it is easier to understand for the beginning
programmer. Models are also used with assembly language procedures that are used by highlevel languages such as C>C+ + . Although this text fully develops and uses the memory model
definitions for its programming examples, realize that full-segment definitions offer some advantages over memory models, as discussed later in this section.

148

CHAPTER 4

Models. There are many models available to the MASM assembler, ranging from tiny to huge.
Appendix A contains a table that lists all the models available for use with the assembler. To designate a model, use the .MODEL statement followed by the size of the memory system. The
TINY model requires that all software and data fit into one 64K-byte memory segment; it is useful for many small programs. The SMALL model requires that only one data segment be used
with one code segment for a total of 128K bytes of memory. Other models are available, up to the
HUGE model.
Example 4–18 illustrates how the .MODEL statement defines the parameters of a short
program that copies the contents of a 100-byte block of memory (LISTA) into a second 100byte block of memory (LISTB). It also shows how to define the stack, data, and code segments. The .EXIT 0 directive returns to DOS with an error code of 0 (no error). If no parameter is added to .EXIT, it still returns to DOS, but the error code is not defined. Also note
that special directives such as @DATA (see Appendix A) are used to identify various segments. If the .STARTUP directive is used (MASM version 6.x), the MOV AX,@DATA followed by MOV DS,AX statements can be eliminated. The .STARTUP directive also eliminates the need to store the starting address next to the END label. Models are important with
both Microsoft Visual C+ + and Borland C+ + development systems if assembly language is
included with C+ + programs. Both development systems use inline assembly programming
for adding assembly language instructions and require an understanding of programming
models.
EXAMPLE 4–18
.MODEL SMALL
.STACK 100H
.DATA
0000 0064[
??
]
0064 0064[
??
]

LISTA

DB

100 DUP(?)

LISTB

DB

100 DUP(?)

.CODE
0000
0003
0005
0007
0008
000B
000E
0011
0013

B9 —— ?
8E C0
8E D8
FC
BE 0000 R
BF 0064 R
B9 0064
F3/A4

HERE:

MOV
MOV
MOV
CLD
MOV
MOV
MOV
REP

;select small model
;define stack
;start data segment

;start code segment
AX,@DATA
ES,AX
DS,AX

;load ES and DS

;move data
SI,OFFSET LISTA
DI,OFFSET LISTB
CX,100
MOVSB

.EXIT 0
END HERE

;exit to DOS

Full-Segment Definitions. Example 4–19 illustrates the same program using full segment definitions. Full-segment definitions are also used with the Borland and Microsoft C>C+ + environments
for procedures developed in assembly language. The program in Example 4–19 appears longer than
the one pictured in Example 4–18, but it is more structured than the model method of setting up a
program. The first segment defined is the STACK_SEG, which is clearly delineated with the SEGMENT and ENDS directives. Within these directives, a DW 100 DUP (?) sets aside 100H words for
the stack segment. Because the word STACK appears next to SEGMENT, the assembler and linker
automatically load both the stack segment register (SS) and stack pointer (SP).

149

DATA MOVEMENT INSTRUCTIONS

EXAMPLE 4–19
0000
0000 0064[
????
]
0200

STACK_SEG
DW

SEGMENT
100H DUP(?)

STACK_SEG

ENDS

0000
0000 0064[
??
]
0064 0064[
??
]
00CB

DATA_SEG
LISTA DB

SEGMENT
100 DUP(?)

LISTB

100 DUP(?)

DATA_SEG

ENDS

0000

CODE_SEG
ASSUME
ASSUME
MAIN
PROC
MOV
MOV
MOV
CLD
MOV
MOV
MOV
REP
MOV
INT
MAIN
ENDP
CODE_SEG
END

SEGMENT
'CODE'
CS:CODE_SEG, DS:DATA_SEG
SS:STACK_SEG
FAR
AX,DATA_SEG
;load DS and ES
ES,AX
DS,AX
;save data
SI,OFFSET LISTA
DI,OFFSET LISTB
CX,100
MOVSB
AH,4CH
;exit to DOS
21H

0000
0000
0003
0005
0007
0008
000B
000E
0011
0013
0015
0017
0017

B8 —— R
8E C0
8E D8
FC
BE 0000 R
BF 0064 R
B9 0064
F3/A4
B4 4C
CD 21

DB

'STACK'

'DATA'

ENDS
MAIN

Next, the data are defined in the DATA_SEG. Here, two arrays of data appear as LISTA and
LISTB. Each array contains 100 bytes of space for the program. The names of the segments in this
program can be changed to any name. Always include the group name ‘DATA’, so that the
Microsoft program CodeView can be effectively used to symbolically debug this software.
CodeView is a part of the MASM package used to debug software. To access CodeView, type CV,
followed by the file name at the DOS command line; if operating from Programmer’s
WorkBench, select Debug under the Run menu. If the group name is not placed in a program,
CodeView can still be used to debug a program, but the program will not be debugged in symbolic
form. Other group names such as ‘STACK’, ‘CODE’, and so forth are listed in Appendix A.
You must at least place the word ‘CODE’ next to the code segment SEGMENT statement if you
want to view the program symbolically in CodeView.
The CODE_SEG is organized as a far procedure because most software is procedureoriented. Before the program begins, the code segment contains the ASSUME statement. The
ASSUME statement tells the assembler and linker that the name used for the code segment
(CS) is CODE_SEG; it also tells the assembler and linker that the data segment is
DATA_SEG and the stack segment is STACK_SEG. Notice that the group name ‘CODE’ is
used for the code segment for use by CodeView. Other group names appear in Appendix A
with the models.
After the program loads both the extra segment register and data segment register with the
location of the data segment, it transfers 100 bytes from LISTA to LISTB. Following this is a
sequence of two instructions that return control back to DOS (the disk operating system). Note
that the program loader does not automatically initialize DS and ES. These registers must be
loaded with the desired segment addresses in the program.

150

CHAPTER 4

The last statement in the program is END MAIN. The END statement indicates the end of
the program and the location of the first instruction executed. Here, we want the machine to execute the main procedure so the MAIN label follows the END directive.
In the 80386 and above, an additional directive is found attached to the code segment. The
USE16 or USE32 directive tells the assembler to use either the 16- or 32-bit instruction modes for
the microprocessor. Software developed for the DOS environment must use the USE16 directive
for the 80386 through the Core2 program to function correctly because MASM assumes that all
segments are 32 bits and all instruction modes are 32 bits by default.

A Sample Program
Example 4–20 provides a sample program, using full-segment definitions, that reads a character
from the keyboard and displays it on the CRT screen. Although this program is trivial, it illustrates a complete workable program that functions on any personal computer using DOS, from
the earliest 8088-based system to the latest Core2-based system. This program also illustrates the
use of a few DOS function calls. (Appendix A lists the DOS function calls with their parameters.) The BIOS function calls allow the use of the keyboard, printer, disk drives, and everything
else that is available in your computer system.
This example program uses only a code segment because there is no data. A stack segment
should appear, but it has been left out because DOS automatically allocates a l28-byte stack for
all programs. The only time that the stack is used in this example is for the INT 21H instructions
that call a procedure in DOS. Note that when this program is linked, the linker signals that no
stack segment is present. This warning may be ignored in this example because the stack is fewer
than 128 bytes.
Notice that the entire program is placed into a far procedure called MAIN. It is good programming practice to write all software in procedural form, which allows the program to be used
as a procedure at some future time if necessary. It is also fairly important to document register
use and any parameters required for the program in the program header, which is a section of
comments that appear at the start of the program.
The program uses DOS functions 06H and 4CH. The function number is placed in AH
before the INT 21H instruction executes. The 06H function reads the keyboard if DL = 0FFH ,
or displays the ASCII contents of DL if it is not 0FFH. Upon close examination, the first section
of the program moves 06H into AH and 0FFH into DL, so that a key is read from the keyboard.
The INT 21H tests the keyboard; if no key is typed, it returns equal. The JE instruction tests the
equal condition and jumps to MAIN if no key is typed.
When a key is typed, the program continues to the next step, which compares the contents
of AL with an @ symbol. Upon return from the INT 21H, the ASCII character of the typed key
is found in AL. In this program, if an @ symbol is typed, the program ends. If the @ symbol is
not typed, the program continues by displaying the character typed on the keyboard with the next
INT 21H instruction.
The second INT 21H instruction moves the ASCII character into DL so it can be displayed
on the CRT screen. After displaying the character, a JMP executes. This causes the program to
continue at MAIN, where it repeats reading a key.
If the @ symbol is typed, the program continues at MAIN1, where it executes the DOS
function code number 4CH. This causes the program to return to the DOS prompt so that the
computer can be used for other tasks.
More information about the assembler and its application appears in Appendix A and in
the next several chapters. Appendix A provides a complete overview of the assembler, linker, and
DOS functions. It also provides a list of the BIOS (basic I/O system) functions. The information
provided in the following chapters clarifies how to use the assembler for certain tasks at different
levels of the text.

151

DATA MOVEMENT INSTRUCTIONS

EXAMPLE 4–20
;An example DOS full-segment program that reads a key and
;displays it. Note that an @ key ends the program.
;
CODE_SEG
SEGMENT 'CODE'
ASSUME CS:CODE_SEG

0000

0000
0000
0002
0004
0006
0008
000A
000C
000E
0010
0012
0014
0014
0016

MAIN
B4
B2
CD
74
3C
74
B4
8A
CD
EB

06
FF
21
F8
40
08
06
D0
21
EC

PROC

FAR

MOV
MOV
INT
JE
CMP
JE
MOV
MOV
INT
JMP

AH,06H
DL,0FFH
21H
MAIN
AL,'@'
MAIN1
AH,06H
DL,AL
21H
MAIN

;read a key

MOV
INT

AH,4CH
21H

;exit to DOS

;if no key typed
;if an @ key
;display key (echo)

;repeat

MAIN1:
B4 4C
CD 21

0018
0018

MAIN

ENDP
END MAIN

EXAMPLE 4–21
;An example DOS model program that reads a key and displays
;it. Note that an @ key ends the program.
;
.MODEL TINY
.CODE
.STARTUP

0000

0100
0100
0102
0104
0106
0108
010A
010C
010E
0110
0112
0114

MAIN:
B4
B2
CD
74
3C
74
B4
8A
CD
EB

06
FF
21
F8
40
08
06
D0
21
EC

MOV
MOV
INT
JE
CMP
JE
MOV
MOV
INT
JMP

AH,6
DL,0FFH
21H
MAIN
AL, '@'
MAIN1
AH,06H
DL,AL
21H
MAIN

;read a key

;if no key typed
;if an @ key
;display key (echo)

;repeat

MAIN1:
.EXIT
END

;exit to DOS

Example 4–21 shows the program listed in Example 4–20, except models are used instead
of full-segment descriptions. Please compare the two programs to determine the differences.
Notice how much shorter and cleaner looking the models can make a program.

4–8

SUMMARY
1. Data movement instructions transfer data between registers, a register and memory, a register and the stack, memory and the stack, the accumulator and I/O, and the flags and the
stack. Memory-to-memory transfers are allowed only with the MOVS instruction.

152

CHAPTER 4

2. Data movement instructions include MOV, PUSH, POP, XCHG, XLAT, IN, OUT, LEA,
LOS, LES, LSS, LGS, LFS, LAHF, SAHF, and the following string instructions: LODS,
STOS, MOVS, INS, and OUTS.
3. The first byte of an instruction contains the opcode. The opcode specifies the operation performed by the microprocessor. The opcode may be preceded by one or more override prefixes in some forms of instructions.
4. The D-bit, located in many instructions, selects the direction of data flow. If D = 0, the data
flow from the REG field to the R/M field of the instruction. If D = 1, the data flow from the
R/M field to the REG field.
5. The W-bit, found in most instructions, selects the size of the data transfer. If W = 0, the data
are byte-sized; if W = 1, the data are word-sized. In the 80386 and above, W = 1 specifies
either a word or doubleword register.
6. MOD selects the addressing mode of operation for a machine language instruction’s R/M
field. If MOD = 00, there is no displacement; if MOD = 01, an 8-bit sign-extended displacement appears; if MOD = 10, a 16-bit displacement occurs; and if MOD = 11, a register is used instead of a memory location. In the 80386 and above, the MOD bits also specify
a 32-bit displacement.
7. A 3-bit binary register code specifies the REG and R/M fields when the MOD = 11. The 8bit registers are AH, AL, BH, BL, CH, CL, DH, and DL. The l6-bit registers are AX, BX,
CX, DX, SP, BP, DI, and SI. The 32-bit registers are EAX, EBX, ECX, EDX, ESP, EBP,
EDI, and ESI. To access the 64-bit registers, a new prefix is added called the REX prefix that
contains a fourth bit for accessing registers R8 through R15.
8. When the R/M field depicts a memory mode, a 3-bit code selects one of the following
modes: 3BX+DI4, 3BX+SI4, 3BP+DI4, 3BP+SI4, [BX], [BP], [DI], or [SI] for 16-bit
instructions. In the 80386 and above, the R/M field specifies EAX, EBX, ECX, EDX, EBP,
EDI, and ESI or one of the scaled-index modes of addressing memory data. If the scaledindex mode is selected 1R>M = 1002, an additional byte (scaled-index byte) is added to the
instruction to specify the base register, index register, and the scaling factor.
9. By default, all memory-addressing modes address data in the data segment unless BP or
EBP addresses memory. The BP or EBP register addresses data in the stack segment.
10. The segment registers are addressed only by the MOV, PUSH, or POP instructions. The
instruction may transfer a segment register to a 16-bit register, or vice versa. MOV CS,reg or
POP CS instructions are not allowed because they change only a portion of the address. The
80386 through the Pentium 4 include two additional segment registers, FS and GS.
11. Data are transferred between a register or a memory location and the stack by the PUSH and
POP instructions. Variations of these instructions allow immediate data to be pushed onto
the stack, the flags to be transferred between the stack, and all 16-bit registers can be transferred between the stack and the registers. When data are transferred to the stack, 2 bytes
(8086–80286) always move. The most significant byte is placed at the location addressed by
SP - 1, and the least significant byte is placed at the location addressed by SP - 2. After
placing the data on the stack, SP is decremented by 2. In the 80386–Core2, 4 bytes of data
from a memory location or register may also be transferred to the stack.
12. Opcodes that transfer data between the stack and the flags are PUSHF and POPF. Opcodes
that transfer all the 16-bit registers between the stack and the registers are PUSHA and
POPA. In the 80386 and above, PUSHFD and POPFD transfer the contents of the EFLAGS
between the microprocessor and the stack, and PUSHAD and POPAD transfer all the 32-bit
registers. The PUSHA and POPA instructions are invalid in the 64-bit mode.
13. LEA, LDS, and LES instructions load a register or registers with an effective address. The
LEA instruction loads any 16-bit register with an effective address; LDS and LES load any 16bit register and either DS or ES with the effective address. In the 80386 and above, additional
instructions include LFS, LGS, and LSS, which load a 16-bit register and FS, GS, or SS.

DATA MOVEMENT INSTRUCTIONS

153

14. String data transfer instructions use either or both DI and SI to address memory. The DI offset address is located in the extra segment, and the SI offset address is located in the data
segment. If the 80386–Core2 is operated in protected mode, ESI and EDI are used with the
string instructions.
15. The direction flag (D) chooses the auto-increment or auto-decrement mode of operation for
DI and SI for string instructions. To clear D to 0, use the CLD instruction to select the autoincrement mode; to set D to 1, use the STD instruction to select the auto-decrement mode.
Either or both DI and SI increment/decrement by 1 for a byte operation, by 2 for a word
operation, and by 4 for a doubleword operation.
16. LODS loads AL, AX, or EAX with data from the memory location addressed by SI; STOS stores
AL, AX, or EAX in the memory location addressed by DI; and MOVS transfers a byte, a word,
or a doubleword from the memory location addressed by SI into the location addressed by DI.
17. INS inputs data from an I/O device addressed by DX and stores it in the memory location
addressed by DI. The OUTS instruction outputs the contents of the memory location
addressed by SI and sends it to the I/O device addressed by DX.
18. The REP prefix may be attached to any string instruction to repeat it. The REP prefix repeats
the string instruction the number of times found in register CX.
19. Arithmetic and logic operators can be used in assembly language. An example is MOV
AX,34*3, which loads AX with 102.
20. Translate (XLAT) converts the data in AL into a number stored at the memory location
addressed by BX plus AL.
21. IN and OUT transfer data between AL, AX, or EAX and an external I/O device. The address
of the I/O device is either stored with the instruction (fixed-port addressing) or in register
DX (variable-port addressing).
22. The Pentium Pro–Core2 contain a new instruction called CMOV, or conditional move. This
instruction only performs the move if the condition is true.
23. The segment override prefix selects a different segment register for a memory location than
the default segment. For example, the MOV AX,[BX] instruction uses the data segment, but
the MOV AX,ES:[BX] instruction uses the extra segment because of the ES: override prefix.
Using the segment override prefix is the only way to address the FS and GS segments in the
80386 through the Pentium 4.
24. The MOVZX (move and zero-extend) and MOVSX (move and sign-extend) instructions,
found in the 80386 and above, increase the size of a byte to a word or a word to a doubleword. The zero-extend version increases the size of the number by inserting leading zeros.
The sign-extend version increases the size of the number by copying the sign-bit into the
more significant bits of the number.
25. Assembler directives DB (define byte), DW (define word), DD (define doubleword), and
DUP (duplicate) store data in the memory system.
26. The EQU (equate) directive allows data or labels to be equated to labels.
27. The SEGMENT directive identifies the start of a memory segment and ENDS identifies the
end of a segment when full-segment definitions are in use.
28. The ASSUME directive tells the assembler what segment names you have assigned to CS,
DS, ES, and SS when full-segment definitions are in effect. In the 80386 and above,
ASSUME also indicates the segment name for FS and GS.
29. The PROC and ENDP directives indicate the start and end of a procedure. The USES directive (MASM version 6.x) automatically saves and restores any number of registers on the
stack if they appear with the PROC directive.
30. The assembler assumes that software is being developed for the 8086/8088 microprocessor
unless the .286, .386, .486, .586, or .686 directive is used to select one of these other microprocessors. This directive follows the .MODEL statement to use the 16-bit instruction mode
and precedes it for the 32-bit instruction mode.

154

CHAPTER 4

31. Memory models can be used to shorten the program slightly, but they can cause problems
for larger programs. Also be aware that memory models are not compatible with all assembler programs.

4–9

QUESTIONS AND PROBLEMS
1. The first byte of an instruction is the ____________, unless it contains one of the override
prefixes.
2. Describe the purpose of the D- and W-bits found in some machine language instructions.
3. In a machine language instruction, what information is specified by the MOD field?
4. If the register field (REG) of an instruction contains 010 and W = 0, what register is
selected, assuming that the instruction is a 16-bit mode instruction?
5. How are the 32-bit registers selected for the Pentium 4 microprocessor?
6. What memory-addressing mode is specified by R>M = 001 with MOD = 00 for a 16-bit
instruction?
7. Identify the default segment registers assigned to the following:
(a) SP
(b) EBX
(c) DI
(d) EBP
(e) SI
8. Convert an 8B07H from machine language to assembly language.
9. Convert an 8B9E004CH from machine language to assembly language.
10. If a MOV SI,[BX+2] instruction appears in a program, what is its machine language
equivalent?
11. If a MOV ESI,[EAX] instruction appears in a program for the Core2 microprocessor operating in the 16-bit instruction mode, what is its machine language equivalent?
12. What is the purpose of REX?
13. What is wrong with a MOV CS,AX instruction?
14. Form a short sequence of instructions that load the data segment register with a 1000H.
15. The PUSH and POP instructions always transfer a(n) ____________ -bit number between
the stack and a register or memory location in the 80386–Core2 microprocessors when operated in the 32-bit mode.
16. Create an instruction that places RAX onto the stack in the 64-bit mode for the Pentium 4.
17. What segment register may not be popped from the stack?
18. Which registers move onto the stack with the PUSHA instruction?
19. Which registers move onto the stack for a PUSHAD instruction?
20. Describe the operation of each of the following instructions:
(a) PUSH AX
(b) POP ESI
(c) PUSH [BX]
(d) PUSHFD
(e) POP DS
(f) PUSHD 4
21. Explain what happens when the PUSH BX instruction executes. Make sure to show where
BH and BL are stored. (Assume that SP = 0100H and SS = 0200H.)
22. Repeat question 21 for the PUSH EAX instruction.
23. The 16-bit POP instruction (except for POPA) increments SP by ____________.
24. What values appear in SP and SS if the stack is addressed at memory location 02200H?

DATA MOVEMENT INSTRUCTIONS

155

25. Compare the operation of a MOV DI,NUMB instruction with an LEA DI,NUMB instruction.
26. What is the difference between an LEA SI,NUMB instruction and a MOV SI,OFFSET
NUMB instruction?
27. Which is more efficient, a MOV with an OFFSET or an LEA instruction?
28. Describe how the LDS BX,NUMB instruction operates.
29. What is the difference between the LDS and LSS instructions?
30. Develop a sequence of instructions that moves the contents of data segment memory locations NUMB and NUMB+1 into BX, DX, and SI.
31. What is the purpose of the direction flag?
32. Which instructions set and clear the direction flag?
33. Which string instruction(s) use both DI and SI to address memory data?
34. Explain the operation of the LODSB instruction.
35. Explain the operation of the LODSQ instruction for the 64-bit mode of the Pentium 4 or Core2.
36. Explain the operation of the OUTSB instruction.
37. Explain the operation of the STOSW instruction.
38. Develop a sequence of instructions that copy 12 bytes of data from an area of memory
addressed by SOURCE into an area of memory addressed by DEST.
39. What does the REP prefix accomplish and what type of instruction is it used with?
40. Select an assembly language instruction that exchanges the contents of the EBX register
with the ESI register.
41. Where is the I/O address (port number) stored for an INSB instruction?
42. Would the LAHF and SAHF instructions normally appear in software?
43. Write a short program that uses the XLAT instruction to convert the BCD numbers 0–9 into
ASCII-coded numbers 30H–39H. Store the ASCII-coded data in a TABLE located within
the data segment.
44. Explain how the XLAT instruction transforms the contents of the AL register.
45. Explain what the IN AL,12H instruction accomplishes.
46. Explain how the OUT DX,AX instruction operates.
47. What is a segment override prefix?
48. Select an instruction that moves a byte of data from the memory location addressed by the
BX register in the extra segment into the AH register.
49. Develop a sequence of instructions that exchanges the contents of AX with BX, ECX with
EDX, and SI with DI.
50. What is an assembly language directive?
51. What is accomplished by the CMOVNE CX,DX instruction in the Pentium 4 microprocessor?
52. Describe the purpose of the following assembly language directives: DB, DW, and DD.
53. Select an assembly language directive that reserves 30 bytes of memory for array LIST1.
54. Describe the purpose of the EQU directive.
55. What is the purpose of the .686 directive?
56. What is the purpose of the .MODEL directive?
57. If the start of a segment is identified with .DATA, what type of memory organization is in effect?
58. If the SEGMENT directive identifies the start of a segment, what type of memory organization is in effect?
59. What does the INT 21H accomplish if AH contains a 4CH?
60. What directives indicate the start and end of a procedure?
61. Explain the purpose of the USES statement as it applies to a procedure with version 6.x of MASM.
62. Develop a near procedure that stores AL in four consecutive memory locations within the
data segment, as addressed by the DI register.
63. How is the Pentium 4 microprocessor instructed to use the 16-bit instruction mode?
64. Develop a far procedure that copies contents of the word-sized memory location CS:DATA4
into AX, BX, CX, DX, and SI.

CHAPTER 5
Arithmetic and Logic Instructions

INTRODUCTION
In this chapter, we examine the arithmetic and logic instructions. The arithmetic instructions
include addition, subtraction, multiplication, division, comparison, negation, increment, and
decrement. The logic instructions include AND, OR, Exclusive-OR, NOT, shifts, rotates, and
the logical compare (TEST). This chapter also presents the 80386 through the Core2 instructions XADD, SHRD, SHLD, bit tests, and bit scans. The chapter concludes with a discussion
of string comparison instructions, which are used for scanning tabular data and for comparing
sections of memory data. Both comparison tasks are performed efficiently with the string scan
(SCAS) and string compare (CMPS) instructions.
If you are familiar with an 8-bit microprocessor, you will recognize that the 8086 through
the Core2 instruction set is superior to most 8-bit microprocessors because most of the instructions have two operands instead of one. Even if this is your first microprocessor, you will
quickly learn that this microprocessor possesses a powerful and easy-to-use set of arithmetic
and logic instructions.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1.
2.
3.
4.

Use arithmetic and logic instructions to accomplish simple binary, BCD, and ASCII arithmetic.
Use AND, OR, and Exclusive-OR to accomplish binary bit manipulation.
Use the shift and rotate instructions.
Explain the operation of the 80386 through the Core2 exchange and add, compare and
exchange, double-precision shift, bit test, and bit scan instructions.
5. Check the contents of a table for a match with the string instructions.

5-1

ADDITION, SUBTRACTION, AND COMPARISON
The bulk of the arithmetic instructions found in any microprocessor include addition, subtraction, and comparison. In this section, addition, subtraction, and comparison instructions are
illustrated. Also shown are their uses in manipulating register and memory data.

156

157

ARITHMETIC AND LOGIC INSTRUCTIONS

Addition
Addition (ADD) appears in many forms in the microprocessor. This section details the use of
the ADD instruction for 8-, 16-, and 32-bit binary addition. A second form of addition, called
add-with-carry, is introduced with the ADC instruction. Finally, the increment instruction
(INC) is presented. Increment is a special type of addition that adds 1 to a number. In Section 5–3,
other forms of addition are examined, such as BCD and ASCII. Also described is the XADD
instruction, found in the 80486 through the Pentium 4.
Table 5–1 illustrates the addressing modes available to the ADD instruction. (These
addressing modes include almost all those mentioned in Chapter 3.) However, because there are
more than 32,000 variations of the ADD instruction in the instruction set, it is impossible to list
them all in this table. The only types of addition not allowed are memory-to-memory and segment register. The segment registers can only be moved, pushed, or popped. Note that, as with all
other instructions, the 32-bit registers are available only with the 80386 through the Core2. In the
64-bit mode of the Pentium 4 and Core2, the 64-bit registers are also used for addition.

TABLE 5–1

Example addition instructions.

Assembly Language

Operation

ADD AL,BL

AL = AL + BL

ADD CX,DI

CX = CX + DI

ADD EBP,EAX

EBP = EBP + EAX

ADD CL,44H

CL = CL + 44H

ADD BX,245FH

BX = BX + 245FH

ADD EDX,12345H

EDX = EDX + 12345H

ADD [BX],AL

AL adds to the byte contents of the data segment memory location
addressed by BX with the sum stored in the same memory location

ADD CL,[BP]

The byte contents of the stack segment memory location addressed
by BP add to CL with the sum stored in CL

ADD AL,[EBX]

The byte contents of the data segment memory location addressed by
EBX add to AL with the sum stored in AL

ADD BX,[SI+2]

The word contents of the data segment memory location addressed by
SI + 2 add to BX with the sum stored in BX

ADD CL,TEMP

The byte contents of data segment memory location TEMP add to CL
with the sum stored in CL

ADD BX,TEMP[DI]

The word contents of the data segment memory location addressed by
TEMP + DI add to BX with the sum stored in BX

ADD [BX+D],DL

DL adds to the byte contents of the data segment memory location
addressed by BX + DI with the sum stored in the same memory location

ADD BYTE PTR [DI],3

A 3 adds to the byte contents of the data segment memory location
addressed by DI with the sum stored in the same location

ADD BX,[EAX+2*ECX]

The word contents of the data segment memory location addressed by
EAX plus 2 times ECX add to BX with the sum stored in BX

ADD RAX,RBX

RBX adds to RAX with the sum stored in RAX (64-bit mode)

ADD EDX,[RAX+RCX]

The doubleword in EDX is added to the doubleword addressed by the
sum of RAX and RCX and the sum is stored in EDX (64-bit mode)

158

CHAPTER 5

Register Addition. Example 5–1 shows a simple sequence of instructions that uses register
addition to add the contents of several registers. In this example, the contents of AX, BX, CX,
and DX are added to form a 16-bit result stored in the AX register.
EXAMPLE 5–1
0000 03 C3
0002 03 C1
0004 03 C2

ADD AX,BX
ADD AX,CX
ADD AX,DX

Whenever arithmetic and logic instructions execute, the contents of the flag register change.
Note that the contents of the interrupt, trap, and other flags do not change due to arithmetic and
logic instructions. Only the flags located in the rightmost 8 bits of the flag register and the overflow
flag change. These rightmost flags denote the result of the arithmetic or a logic operation. Any
ADD instruction modifies the contents of the sign, zero, carry, auxiliary carry, parity, and overflow
flags. The flag bits never change for most of the data transfer instructions presented in Chapter 4.

Immediate Addition. Immediate addition is employed whenever constant or known data are
added. An 8-bit immediate addition appears in Example 5-2. In this example, DL is first loaded
with 12H by using an immediate move instruction. Next, 33H is added to the 12H in DL by an
immediate addition instruction. After the addition, the sum (45H) moves into register DL and the
flags change, as follows:
Z
C
A
S
P
O

=
=
=
=
=
=

0 1result not zero2
0 1no carry2
0 1no half-carry2
0 1result positive2
0 1odd parity2
0 1no overflow2

EXAMPLE 5–2
0000 B2 12
0002 80 C2 33

MOV DL,12H
ADD DL,33H

Memory-to-Register Addition. Suppose that an application requires memory data to be added
to the AL register. Example 5–3 shows an example that adds two consecutive bytes of data, stored
at the data segment offset locations NUMB and NUMB+1, to the AL register.
EXAMPLE 5–3
0000
0003
0005
0007

BF
B0
02
02

0000 R
00
05
45 01

MOV
MOV
ADD
ADD

DI,OFFSET NUMB
AL,0
AL,[DI]
AL,[DI+1]

;address NUMB
;clear sum
;add NUMB
;add NUMB+1

The first instruction loads the destination index register (DI) with offset address NUMB. The
DI register, used in this example, addresses data in the data segment beginning at memory location
NUMB. After clearing the sum to zero, the ADD AL,[DI] instruction adds the contents of memory
location NUMB to AL. Finally, the ADD AL,[ DI+I] instruction adds the contents of memory
location NUMB plus 1 byte to the AL register. After both ADD instructions execute, the result
appears in the AL register as the sum of the contents of NUMB plus the contents of NUMB+1.

Array Addition. Memory arrays are sequential lists of data. Suppose that an array of data
(ARRAY) contains 10 bytes, numbered from element 0 through element 9. Example 5–4 shows
how to add the contents of array elements 3, 5, and 7 together.

159

ARITHMETIC AND LOGIC INSTRUCTIONS

This example first clears AL to 0, so it can be used to accumulate the sum. Next, register SI
is loaded with a 3 to initially address array element 3. The ADD AL,ARRAY[SI] instruction
adds the contents of array element 3 to the sum in AL. The instructions that follow add array elements 5 and 7 to the sum in AL, using a 3 in SI plus a displacement of 2 to address element 5, and
a displacement of 4 to address element 7.
EXAMPLE 5–4
0000
0002
0005
0009
000D

B0
BE
02
02
02

00
0003
84 0000 R
84 0002 R
84 0004 R

MOV
MOV
ADD
ADD
ADD

AL,0
SI,3
AL,ARRAY[SI]
AL,ARRAY[SI+2]
AL,ARRAY[SI+4]

;clear sum
;address element 3
;add element 3
;add element 5
;add element 7

Suppose that an array of data contains 16-bit numbers used to form a 16-bit sum in register AX. Example 5–5 shows a sequence of instructions written for the 80386 and above, showing
the scaled-index form of addressing to add elements 3, 5, and 7 of an area of memory called
ARRAY. In this example, EBX is loaded with the address ARRAY, and ECX holds the array element number. Note how the scaling factor is used to multiply the contents of the ECX register by
2 to address words of data. (Recall that words are 2 bytes long.)
EXAMPLE 5–5
0000
0006
000C
0010
0016
001A
0020

66|BB
66|B9
67&8B
66|B9
67&03
66|B0
67&03

00000000 R
00000003
04 4B
00000005
04 4B
00000007
04 4B

MOV
MOV
MOV
MOV
ADD
MOV
ADD

EBX,OFFSET ARRAY
ECX,3
AX,[EBX+2*ECX]
ECX,5
AX,[EBX+2*ECX]
ECX,7
AX,[EBX+2*ECX]

;address ARRAY
;address element 3
;get element 3
;address element 5
;add element 5
;address element 7
;add element 7

Increment Addition. Increment addition (INC) adds 1 to a register or a memory location. The
INC instruction adds 1 to any register or memory location, except a segment register. Table 5–2
illustrates some of the possible forms of the increment instructions available to the 8086–Core2
processors. As with other instructions presented thus far, it is impossible to show all variations of
the INC instruction because of the large number available.
With indirect memory increments, the size of the data must be described by using the
BYTE PTR, WORD PTR, DWORD PTR, or QWORD PTR directives. The reason is that the
TABLE 5–2

Example increment instructions.

Assembly Language

Operation

INC BL

BL = BL + 1

INC SP

SP = SP + 1

INC EAX

EAX = EAX + 1

INC BYTE PTR[BX]

Adds 1 to the byte contents of the data segment memory location
addressed by BX

INC WORD PTR[SI]

Adds 1 to the word contents of the data segment memory location
addressed by SI

INC DWORD PTR[ECX]

Adds 1 to the doubleword contents of the data segment memory
location addressed by ECX

INC DATA1

Adds 1 to the contents of data segment memory location DATA1

INC RCX

Adds 1 to RCX (64-bit mode)

160

CHAPTER 5

assembler program cannot determine if, for example, the INC [DI] instruction is a byte-, word-,
or doubleword-sized increment. The INC BYTE PTR [DI] instruction clearly indicates bytesized memory data; the INC WORD PTR [DI] instruction unquestionably indicates a word-sized
memory data; and the INC DWORD PTR [DI] instruction indicates doubleword-sized data. In
64-bit mode operation of the Pentium 4 and Core2, the INC QWORD PTR [RSI] instruction
indicates quadword-sized data.
Example 5–6 shows how to modify Example 5–3 to use the increment instruction for
addressing NUMB and NUMB+1. Here, an INC DI instruction changes the contents of register
DI from offset address NUMB to offset address NUMB+1. Both program sequences shown in
Examples 5–3 and 5–6 add the contents of NUMB and NUMB+1. The difference between them
is the way that the address is formed through the contents of the DI register using the increment
instruction.
EXAMPLE 5–6
0000
0003
0005
0007
0008

BF
B0
02
47
02

0000 R
00
05
05

MOV
MOV
ADD
INC
ADD

DI,OFFSET NUMB
AL,0
AL,[DI]
DI
AL,[DI]

;address NUMB
;clear sum
;add NUMB
;increment DI
;add NUMB+1

Increment instructions affect the flag bits, as do most other arithmetic and logic operations.
The difference is that increment instructions do not affect the carry flag bit. Carry doesn’t change
because we often use increments in programs that depend upon the contents of the carry flag.
Note that increment is used to point to the next memory element in a byte-sized array of data
only. If word-sized data are addressed, it is better to use an ADD DI,2 instruction to modify the
DI pointer in place of two INC DI instructions. For doubleword arrays, use the ADD DI,4
instruction to modify the DI pointer. In some cases, the carry flag must be preserved, which may
mean that two or four INC instructions might appear in a program to modify a pointer.

Addition-with-Carry. An addition-with-carry instruction (ADC) adds the bit in the carry flag (C)
to the operand data. This instruction mainly appears in software that adds numbers that are wider
than 16 bits in the 8086–80286 or wider than 32 bits in the 80386–Core2.
Table 5–3 lists several add-with-carry instructions, with comments that explain their
operation. Like the ADD instruction, ADC affects the flags after the addition.

TABLE 5–3

Example add-with-carry instructions.

Assembly Language

Operation

ADC AL,AH

AL = AL + AH + carry

ADC CX,BX

CX = CX + BX + carry

ADC EBX,EDX

EBX = EBX + EDX + carry

ADC RBX,0

RBX = RBX + 0 + carry (64-bit mode)

ADC DH,[BX]

The byte contents of the data segment memory location addressed
by BX add to DH with the sum stored in DH

ADC BX,[BP+2]

The word contents of the stack segment memory location addressed
by BP plus 2 add to BX with the sum stored in BX

ADC ECX,[EBX]

The doubleword contents of the data segment memory location
addressed by EBX add to ECX with the sum stored in ECX

161

ARITHMETIC AND LOGIC INSTRUCTIONS

FIGURE 5–1 Additionwith-carry showing how the
carry flag (C) links the two
16-bit additions into one
32-bit addition.

CF

+

(ADC)

(ADD)

BX

AX

DX

CX

BX

AX

Suppose that a program is written for the 8086–80286 to add the 32-bit number in BX and
AX to the 32-bit number in DX and CX. Figure 5–1 illustrates this addition so that the placement
and function of the carry flag can be understood. This addition cannot be easily performed without adding the carry flag bit because the 8086–80286 only adds 8- or 16-bit numbers. Example
5–7 shows how the contents of registers AX and CX add to form the least significant 16 bits of
the sum. This addition may or may not generate a carry. A carry appears in the carry flag if the
sum is greater than FFFFH. Because it is impossible to predict a carry, the most significant
16 bits of this addition are added with the carry flag using the ADC instruction. The ADC
instruction adds the 1 or the 0 in the carry flag to the most significant 16 bits of the result. This
program adds BX–AX to DX–CX, with the sum appearing in BX–AX.
EXAMPLE 5–7
0000 03 C1
0002 13 DA

ADD AX,CX
ADC BX,DX

Suppose the same software is rewritten for the 80386 through the Core2, but modified to
add two 64-bit numbers in the 32-bit mode. The changes required for this operation are the use
of the extended registers to hold the data and modifications of the instructions for the 80386 and
above. These changes are shown in Example 5–8, which adds two 64-bit numbers. In the 64-bit
mode of the Pentium 4 and Core2, this addition is handled with a single ADD instruction if the
location of the operands is changed to RAX and RBX as in the instruction ADD RAX,RBX,
which adds RBX to RAX.
EXAMPLE 5–8
0000 66|03 C1
0003 66|13 DA

ADD EAX,ECX
ADC EBX,EDX

Exchange and Add for the 80486–Core2 Processors. A new type of addition called exchange
and add (XADD) appears in the 80486 instruction set and continues through the Core2. The
XADD instruction adds the source to the destination and stores the sum in the destination, as with
any addition. The difference is that after the addition takes place, the original value of the destination is copied into the source operand. This is one of the few instructions that change the source.
For example, if BL = 12H and DL = 02H, and the XADD BL,DL instruction executes,
the BL register contains the sum of 14H and DL becomes 12H. The sum of 14H is generated and
the original destination of 12H replaces the source. This instruction functions with any register
size and any memory operand, just as with the ADD instruction.

162

CHAPTER 5

Subtraction
Many forms of subtraction (SUB) appear in the instruction set. These forms use any addressing
mode with 8-, 16-, or 32-bit data. A special form of subtraction (decrement, or DEC) subtracts 1
from any register or memory location. Section 5–3 shows how BCD and ASCII data subtract. As
with addition, numbers that are wider than 16 bits or 32 bits must occasionally be subtracted. The
subtract-with-borrow instruction (SBB) performs this type of subtraction. In the 80486
through the Core2 processors, the instruction set also includes a compare and exchange instruction. In the 64-bit mode for the Pentium 4 and Core2, a 64-bit subtraction is also available.
Table 5–4 lists some of the many addressing modes allowed with the subtract instruction
(SUB). There are well over 1000 possible subtraction instructions, far too many to list here.
About the only types of subtraction not allowed are memory-to-memory and segment register
subtractions. Like other arithmetic instructions, the subtract instruction affects the flag bits.

Register Subtraction. Example 5–9 shows a sequence of instructions that perform register subtraction. This example subtracts the 16-bit contents of registers CX and DX from the contents of
register BX. After each subtraction, the microprocessor modifies the contents of the flag register.
The flags change for most arithmetic and logic operations.
EXAMPLE 5–9
0000 2B D9
0002 2B DA

SUB BX,CX
SUB BX,DX

Immediate Subtraction. As with addition, the microprocessor also allows immediate operands
for the subtraction of constant data. Example 5–10 presents a short sequence of instructions
that subtract 44H from 22H. Here, we first load the 22H into CH using an immediate move
TABLE 5–4

Example subtraction instructions.

Assembly Language

Operation

SUB CL,BL

CL = CL – BL

SUB AX,SP

AX = AX – SP

SUB ECX,EBP

ECX = ECX – EBP

SUB RDX,R8

RDX = RDX – R8 (64-bit mode)

SUB DH,6FH

DH = DH – 6FH

SUB AX,0CCCCH

AX = AX – 0CCCCH

SUB ESI,2000300H

ESI = ESI – 2000300H

SUB [DI],CH

Subtracts CH from the byte contents of the data segment memory
addressed by DI and stores the difference in the same memory location

SUB CH,[BP]

Subtracts the byte contents of the stack segment memory location
addressed by BP from CH and stores the difference in CH

SUB AH,TEMP

Subtracts the byte contents of memory location TEMP from AH and
stores the difference in AH

SUB DI,TEMP[ESI]

Subtracts the word contents of the data segment memory location
addressed by TEMP plus ESI from DI and stores the difference in DI

SUB ECX,DATA1

Subtracts the doubleword contents of memory location DATA1 from
ECX and stores the difference in ECX

SUB RCX,16

RCX = RCX – 18 (64-bit mode)

163

ARITHMETIC AND LOGIC INSTRUCTIONS

instruction. Next, the SUB instruction, using immediate data 44H, subtracts 44H from the 22H.
After the subtraction, the difference (0DEH) moves into the CH register. The flags change as follows for this subtraction:
Z
C
A
S
P
O

=
=
=
=
=
=

0 1result not zero2
1 1borrow2
1 1half-borrow2
1 1result negative2
1 1even parity2
0 1no overflow2

EXAMPLE 5–10
0000 B5 22
0002 80 ED 44

MOV CH,22H
SUB CH,44H

Both carry flags (C and A) hold borrows after a subtraction instead of carries, as after an
addition. Notice in this example that there is no overflow. This example subtracted 44H ( +68)
from 22H ( +34 ), resulting in a 0DEH ( –34). Because the correct 8-bit signed result is – 34, there
is no overflow in this example. An 8-bit overflow occurs only if the signed result is greater than
+127 or less than –128 .

Decrement Subtraction. Decrement subtraction (DEC) subtracts 1 from a register or the contents of a memory location. Table 5–5 lists some decrement instructions that illustrate register
and memory decrements.
The decrement indirect memory data instructions require BYTE PTR, WORD PTR,
DWORD PTR, or QWORD PTR because the assembler cannot distinguish a byte from a word or
doubleword when an index register addresses memory. For example, DEC [SI] is vague because
the assembler cannot determine whether the location addressed by SI is a byte, word, or doubleword. Using DEC BYTE PTR[SI], DEC WORD PTR[DI], or DEC DWORD PTR[SI] reveals

TABLE 5–5

Example decrement instructions.

Assembly Language

Operation

DEC BH

BH = BH – 1

DEC CX

CX = CX – 1

DEC EDX

EDX = EDX – 1

DEC R14

R14 = R14 – 1 (64-bit mode)

DEC BYTE PTR[DI]

Subtracts 1 from the byte contents of the data segment memory
location addressed by DI

DEC WORD PTR[BP]

Subtracts 1 from the word contents of the stack segment memory location addressed by BP

DEC DWORD PTR[EBX]

Subtracts 1 from the doubleword contents of the data segment
memory location addressed by EBX

DEC QWORD PTR[RSI]

Subtracts 1 from the quadword contents of the memory location
addressed by RSI (64-bit mode)

DEC NUMB

Subtracts 1 from the contents of data segment memory
location NUMB

164

CHAPTER 5

TABLE 5–6

Example subtraction-with-borrow instructions.

Assembly Language

Operation

SBB AH,AL

AH = AH – AL – carry

SBB AX,BX

AX = AX – BX – carry

SBB EAX,ECX

EAX = EAX – ECX – carry

SBB CL,2

CL = CL – 2 – carry

SBB RBP,8

RBP = RBP– 2 – carry (64-bit mode)

SBB BYTE PTR[DI],3

Both 3 and carry subtract from the data segment memory location
addressed by DI

SBB [DI],AL

Both AL and carry subtract from the data segment memory location addressed by DI

SBB DI,[BP+2]

Both carry and the word contents of the stack segment memory
location addressed by BP plus 2 subtract from DI

SBB AL,[EBX+ECX]

Both carry and the byte contents of the data segment memory
location addressed by EBX plus ECX subtract from AL

the size of the data to the assembler. In the 64-bit mode, a DEC QWORD PTR[RSI] decrement
the 64-bit number stored at the address pointed to by the RSI register.

Subtraction-with-Borrow. A subtraction-with-borrow (SBB) instruction functions as a regular
subtraction, except that the carry flag (C), which holds the borrow, also subtracts from the difference. The most common use for this instruction is for subtractions that are wider than 16 bits in
the 8086–80286 microprocessors or wider than 32 bits in the 80386–Core2. Wide subtractions
require that borrows propagate through the subtraction, just as wide additions propagate the carry.
Table 5–6 lists several SBB instructions with comments that define their operations. Like the
SUB instruction, SBB affects the flags. Notice that the immediate subtract from memory instruction in this table requires a BYTE PTR, WORD PTR, DWORD PTR, or QWORD PTR directive.
When the 32-bit number held in BX and AX is subtracted from the 32-bit number held in
SI and DI, the carry flag propagates the borrow between the two 16-bit subtractions. The carry
flag holds the borrow for subtraction. Figure 5–2 shows how the borrow propagates through the
carry flag (C) for this task. Example 5–11 shows how this subtraction is performed by a program.
With wide subtraction, the least significant 16- or 32-bit data are subtracted with the SUB

FIGURE 5–2 Subtractionwith-borrow showing how the
carry flag (C) propagates the
borrow.

CF



(SBB)

(SUB)

BX

AX

SI

DI

BX

AX

165

ARITHMETIC AND LOGIC INSTRUCTIONS

instruction. All subsequent and more significant data are subtracted by using the SBB instruction. The example uses the SUB instruction to subtract DI from AX, then uses SBB to subtractwith-borrow SI from BX.
EXAMPLE 5–11
0000 2B C7
0002 1B DE

SUB AX,DI
SBB BX,SI

Comparison
The comparison instruction (CMP) is a subtraction that changes only the flag bits; the destination operand never changes. A comparison is useful for checking the entire contents of a register
or a memory location against another value. A CMP is normally followed by a conditional jump
instruction, which tests the condition of the flag bits.
Table 5–7 lists a variety of comparison instructions that use the same addressing modes as
the addition and subtraction instructions already presented. Similarly, the only disallowed forms
of compare are memory-to-memory and segment register compares.
Example 5–12 shows a comparison followed by a conditional jump instruction. In this
example, the contents of AL are compared with 10H. Conditional jump instructions that often
follow the comparison are JA (jump above) or JB (jump below). If the JA follows the comparison, the jump occurs if the value in AL is above 10H. If the JB follows the comparison, the jump
occurs if the value in AL is below 10H. In this example, the JAE instruction follows the comparison. This instruction causes the program to continue at memory location SUBER if the value in
AL is 10H or above. There is also a JBE (jump below or equal) instruction that could follow the
comparison to jump if the outcome is below or equal to 10H. Later chapters provide additional
detail on the comparison and conditional jump instructions.

TABLE 5–7

Example comparison instructions.

Assembly Language

Operation

CMP CL,BL

CL – BL

CMP AX,SP

AX – SP

CMP EBP,ESI

EBP – ESI

CMP RDI,RSI

RDI – RSI (64-bit mode)

CMP AX,2000H

AX – 2000H

CMP R10W,12H

R10 (word portion) – 12H (64-bit mode)

CMP [DI],CH

CH subtracts from the byte contents of the data segment memory
location addressed by DI

CMP CL,[BP]

The byte contents of the stack segment memory location addressed
by BP subtracts from CL

CMP AH,TEMP

The byte contents of data segment memory location TEMP subtracts
from AH

CMP DI,TEMP[BX]

The word contents of the data segment memory location addressed
by TEMP plus BX subtracts from DI

CMP AL,[EDI+ESI]

The byte contents of the data segment memory location addressed
by EDI plus ESI subtracts from AL

166

CHAPTER 5

EXAMPLE 5–12
0000 3C 10
0002 73 1C

CMP AL,10H
JAE SUBER

;compare AL against 10H
;if AL is 10H or above

Compare and Exchange (80486–Core2 Processors Only). The compare and exchange instruction (CMPXCHG), found only in the 80486 through the Core2 instruction sets, compares the
destination operand with the accumulator. If they are equal, the source operand is copied into the
destination; if they are not equal, the destination operand is copied into the accumulator. This
instruction functions with 8-, 16-, or 32-bit data.
The CMPXCHG CX,DX instruction is an example of the compare and exchange instruction. This instruction first compares the contents of CX with AX. If CX equals AX, DX is copied
into AX; if CX is not equal to AX, CX is copied into AX. This instruction also compares AL
with 8-bit data and EAX with 32-bit data if the operands are either 8- or 32-bit.
In the Pentium–Core2 processors, a CMPXCHG8B instruction is available that compares
two quadwords. This is the only new data manipulation instruction provided in the Pentium–Core2
when they are compared with prior versions of the microprocessor. The compare-and-exchange8-bytes instruction compares the 64-bit value located in EDX:EAX with a 64-bit number located
in memory. An example is CMPXCHG8B TEMP. If TEMP equals EDX:EAX, TEMP is
replaced with the value found in ECX:EBX; if TEMP does not equal EDX:EAX, the number
found in TEMP is loaded into EDX:EAX. The Z (zero) flag bit indicates that the values are equal
after the comparison.
This instruction has a bug that will cause the operating system to crash. More information
about this flaw can be obtained at www.intel.com. There is also a CMPXCHG16B instruction
available to the Pentium 4 when operated in 64-bit mode.

5-2

MULTIPLICATION AND DIVISION
Only modern microprocessors contain multiplication and division instructions. Earlier 8-bit
microprocessors could not multiply or divide without the use of a program that multiplied or
divided by using a series of shifts and additions or subtractions. Because microprocessor manufacturers were aware of this inadequacy, they incorporated multiplication and division instructions
into the instruction sets of the newer microprocessors. The Pentium–Core2 processors contain
special circuitry that performs a multiplication in as little as one clocking period, whereas it took
over 40 clocking periods to perform the same multiplication in earlier Intel microprocessors.

Multiplication
Multiplication is performed on bytes, words, or doublewords, and can be signed integer (IMUL)
or unsigned integer (MUL). Note that only the 80386 through the Core2 processors multiply
32-bit doublewords. The product after a multiplication is always a double-width product. If two
8-bit numbers are multiplied, they generate a 16-bit product; if two 16-bit numbers are multiplied, they generate a 32-bit product; and if two 32-bit numbers are multiplied, a 64-bit product
is generated. In the 64-bit mode of the Pentium 4, two 64-bit numbers are multiplied to generate
a 128-bit product.
Some flag bits (overflow and carry) change when the multiply instruction executes and
produce predictable outcomes. The other flags also change, but their results are unpredictable
and therefore are unused. In an 8-bit multiplication, if the most significant 8 bits of the result are
zero, both C and O flag bits equal zero. These flag bits show that the result is 8 bits wide
(C = 0 ) or 16 bits wide (C = 1 ). In a 16-bit multiplication, if the most significant 16-bits part of

167

ARITHMETIC AND LOGIC INSTRUCTIONS

TABLE 5–8

Example 8-bit multiplication instructions.

Assembly Language

Operation

MUL CL

AL is multiplied by CL; the unsigned product is in AX

IMUL DH

AL is multiplied by DH; the signed product is in AX

IMUL BYTE PTR[BX]

AL is multiplied by the byte contents of the data segment memory
location addressed by BX; the signed product is in AX

MUL TEMP

AL is multiplied by the byte contents of data segment memory
location TEMP; the unsigned product is in AX

the product is 0, both C and O clear to zero. In a 32-bit multiplication, both C and O indicate that
the most significant 32 bits of the product are zero.

8-Bit Multiplication. With 8-bit multiplication, the multiplicand is always in the AL register,
whether signed or unsigned. The multiplier can be any 8-bit register or any memory location.
Immediate multiplication is not allowed unless the special signed immediate multiplication
instruction, discussed later in this section, appears in a program. The multiplication instruction
contains one operand because it always multiplies the operand times the contents of register AL.
An example is the MUL BL instruction, which multiplies the unsigned contents of AL by the
unsigned contents of BL. After the multiplication, the unsigned product is placed in AX—a
double-width product. Table 5–8 illustrates some 8-bit multiplication instructions.
Suppose that BL and CL each contain two 8-bit unsigned numbers, and these numbers
must be multiplied to form a 16-bit product stored in DX. This procedure cannot be accomplished by a single instruction because we can only multiply a number times the AL register for
an 8-bit multiplication. Example 5–13 shows a short program that generates DX = BL * CL .
This example loads register BL and CL with example data 5 and 10. The product, a 50, moves
into DX from AX after the multiplication by using the MOV DX,AX instruction.
EXAMPLE 5–13
0000
0002
0004
0006
0008

B3
B1
8A
F6
8B

05
0A
C1
E3
D0

MOV
MOV
MOV
MUL
MOV

BL,5
CL,10
AL,CL
BL
DX,AX

;load data
;position data
;multiply
;position product

For signed multiplication, the product is in binary form, if positive, and in two’s complement form, if negative. These are the same forms used to store all positive and negative signed
numbers used by the microprocessor. If the program of Example 5–13 multiplies two signed
numbers, only the MUL instruction is changed to IMUL.

16-Bit Multiplication. Word multiplication is very similar to byte multiplication. The difference
is that AX contains the multiplicand instead of AL, and the 32-bit product appears in DX–AX
instead of AX. The DX register always contains the most significant 16 bits of the product, and
AX contains the least significant 16 bits. As with 8-bit multiplication, the choice of the multiplier
is up to the programmer. Table 5–9 shows several different 16-bit multiplication instructions.
A Special Immediate 16-Bit Multiplication. The 8086/8088 microprocessors could not perform immediate multiplication; the 80186 through the Core2 processors can do so by using a
special version of the multiply instruction. Immediate multiplication must be signed multiplication, and the instruction format is different because it contains three operands. The first
operand is the 16-bit destination register; the second operand is a register or memory location

168

CHAPTER 5

TABLE 5–9

Example 16-bit multiplication instructions.

Assembly Language

Operation

MUL CX

AX is multiplied by CX; the unsigned product is in DX–AX

IMUL DI

AX is multiplied by DI; the signed product is in DX–AX

MUL WORD PTR[SI]

AX is multiplied by the word contents of the data segment memory
location addressed by SI; the unsigned product is in DX–AX

that contains the 16-bit multiplicand; and the third operand is either 8-bit or 16-bit immediate
data used as the multiplier.
The IMUL CX,DX,12H instruction multiplies 12H times DX and leaves a 16-bit signed
product in CX. If the immediate data are 8 bits, they sign-extend into a 16-bit number before the
multiplication occurs. Another example is IMUL BX,NUMBER,1000H, which multiplies NUMBER times 1000H and leaves the product in BX. Both the destination and multiplicand must be
16-bit numbers. Although this is immediate multiplication, the restrictions placed upon it limit its
utility, especially the fact that it is a signed multiplication and the product is 16 bits wide.

32-Bit Multiplication. In the 80386 and above, 32-bit multiplication is allowed because these
microprocessors contain 32-bit registers. As with 8- and 16-bit multiplication, 32-bit multiplication can be signed or unsigned by using the IMUL and MUL instructions. With 32-bit multiplication, the contents of EAX are multiplied by the operand specified with the instruction. The
product (64 bits wide) is found in EDX–EAX, where EAX contains the least significant 32 bits
of the product. Table 5–10 lists some of the 32-bit multiplication instructions found in the 80386
and above instruction set.
64-Bit Multiplication. The result of a 64-bit multiplication in the Pentium 4 appears in the
RDX:RAX register pair as a 128-bit product. Although multiplication of this size is relatively
rare, the Pentium 4 and Core2 can perform it on both signed and unsigned numbers. Table 5–11
shows a few examples of this high precision multiplication.
TABLE 5–10

Example 32-bit multiplication instructions.

Assembly Language

Operation

MUL ECX

EAX is multiplied by ECX; the unsigned product is in EDX–EAX

IMUL EDI

EAX is multiplied by EDI; the signed product is in EDX–EAX

MUL DWORD PTR[ESI]

EAX is multiplied by the doubleword contents of the data segment
memory location address by ESI; the unsigned product is in
EDX–EAX

TABLE 5–11

Example 64-bit multiplication instructions.

Assembly Language

Operation

MUL RCX

RAX is multiplied by RCX; the unsigned product is in RDX–RAX

IMUL RDI

RAX is multiplied by RDI; the signed product is in RDX–RAX

MUL QWORD PTR[RSI]

RAX is multiplied by the quadword contents of the memory
location address by RSI; the unsigned product is in RDX–RAX

169

ARITHMETIC AND LOGIC INSTRUCTIONS

Division
As with multiplication, division occurs on 8- or 16-bit numbers in the 8086–80286 microprocessors, and on 32-bit numbers in the 80386 and above microprocessor. These numbers are signed
(IDIV) or unsigned (DIV) integers. The dividend is always a double-width dividend that is
divided by the operand. This means that an 8-bit division divides a 16-bit number by an 8-bit
number; a 16-bit division divides a 32-bit number by a 16-bit number; and a 32-bit division
divides a 64-bit number by a 32-bit number. There is no immediate division instruction available
to any microprocessor. In the 64-bit mode of the Pentium 4 and Core2, a 64-bit division divides
a 128-bit number by a 64-bit number.
None of the flag bits change predictably for a division. A division can result in two different types of errors; one is an attempt to divide by zero and the other is a divide overflow. A divide
overflow occurs when a small number divides into a large number. For example, suppose that
AX = 3000 and that it is divided by 2. Because the quotient for an 8-bit division appears in AL,
the result of 1500 causes a divide overflow because the 1500 does not fit into AL. In either case,
the microprocessor generates an interrupt if a divide error occurs. In most systems, a divide error
interrupt displays an error message on the video screen. The divide error interrupt and all other
interrupts for the microprocessor are explained in Chapter 6.

8-Bit Division. An 8-bit division uses the AX register to store the dividend that is divided by
the contents of any 8-bit register or memory location. The quotient moves into AL after the division with AH containing a whole number remainder. For a signed division, the quotient is positive or negative; the remainder always assumes the sign of the dividend and is always an integer.
For example, if AX = 0010H 1+162 and BL = 0FDH1–32 and the IDIV BL instruction executes, AX = 01FBH . This represents a quotient of –5 1AL2 with a remainder of 1 (AH). If, on
the other hand, a –16 is divided by +3 , the result will be a quotient of –5 1AL2 with a remainder of –1 1AH2 . Table 5–12 lists some of the 8-bit division instructions.
With 8-bit division, the numbers are usually 8 bits wide. This means that one of them,
the dividend, must be converted to a 16-bit wide number in AX. This is accomplished differently for signed and unsigned numbers. For the unsigned number, the most significant 8 bits
must be cleared to zero (zero-extended). The MOVZX instruction described in Chapter 4 can
be used to zero-extend a number in the 80386 through the Core2 processors. For signed numbers, the least significant 8 bits are sign-extended into the most significant 8 bits. In the microprocessor, a special instruction sign-extends AL into AH, or converts an 8-bit signed number
in AL into a 16-bit signed number in AX. The CBW (convert byte to word) instruction performs this conversion. In the 80386 through the Core2, a MOVSX instruction (see Chapter 4)
sign-extends a number.

TABLE 5–12

Example 8-bit division instructions.

Assembly Language

Operation

DIV CL

AX is divided by CL; the unsigned quotient is in AL and the
unsigned remainder is in AH

IDIV BL

AX is divided by BL; the signed quotient is in AL and the signed
remainder is in AH

DIV BYTE PTR[BP]

AX is divided by the byte contents of the stack segment memory
location addressed by BP; the unsigned quotient is in AL and the
unsigned remainder is in AH

170

CHAPTER 5

EXAMPLE 5–14
0000
0003
0005
0009
000C

A0
B4
F6
A2
88

0000 R
00
36 0002 R
0003 R
26 0004 R

MOV
MOV
DIV
MOV
MOV

AL,NUMB
AH,0
NUMB1
ANSQ,AL
ANSR,AH

;get NUMB
;zero-extend
;divide by NUMB1
;save quotient
;save remainder

Example 5–14 illustrates a short program that divides the unsigned byte contents of memory location NUMB by the unsigned contents of memory location NUMB1. Here, the quotient is
stored in location ANSQ and the remainder is stored in location ANSR. Notice how the contents
of location NUMB are retrieved from memory and then zero-extended to form a 16-bit unsigned
number for the dividend.

16-Bit Division. Sixteen-bit division is similar to 8-bit division, except that instead of dividing
into AX, the 16-bit number is divided into DX–AX, a 32-bit dividend. The quotient appears in
AX and the remainder appears in DX after a 16-bit division. Table 5–13 lists some of the 16-bit
division instructions.
As with 8-bit division, numbers must often be converted to the proper form for the dividend.
If a 16-bit unsigned number is placed in AX, DX must be cleared to zero. In the 80386 and above,
the number is zero-extended by using the MOVZX instruction. If AX is a 16-bit signed number, the
CWD (convert word to doubleword) instruction sign-extends it into a signed 32-bit number. If the
80386 and above is available, the MOVSX instruction can also be used to sign-extend a number.
EXAMPLE 5–15
0000
0003
0006
0007

B8 FF9C
B9 0009
99
F7 F9

MOV AX,–100
MOV CX,9
CWD
IDIV CX

;load a –100
;load +9
;sign-extend

Example 5–15 shows the division of two 16-bit signed numbers. Here, – 100 in AX is
divided by + 9 in CX. The CWD instruction converts the –100 in AX to –100 in DX–AX before
the division. After the division, the results appear in DX–AX as a quotient of – 11 in AX and a
remainder of –1 in DX.

32-Bit Division. The 80386 through the Pentium 4 processors perform 32-bit division on
signed or unsigned numbers. The 64-bit contents of EDX–EAX are divided by the operand specified by the instruction, leaving a 32-bit quotient in EAX and a 32-bit remainder in EDX. Other
than the size of the registers, this instruction functions in the same manner as the 8- and 16-bit
divisions. Table 5–14 shows some 32-bit division instructions. The CDQ (convert doubleword
to quadword) instruction is used before a signed division to convert the 32-bit contents of EAX
into a 64-bit signed number in EDX–EAX.
TABLE 5–13

Example 16-bit division instructions.

Assembly Language

Operation

DIV CX

DX–AX is divided by CX; the unsigned quotient is AX and the
unsigned remainder is in DX

IDIV SI

DX–AX is divided by SI; the signed quotient is in AX and the
signed remainder is in DX

DIV NUMB

DX–AX is divided by the word contents of data segment memory
NUMB; the unsigned quotient is in AX and the unsigned
remainder is in DX

171

ARITHMETIC AND LOGIC INSTRUCTIONS

TABLE 5–14

Example 32-bit division instructions.

Assembly Language

Operation

DIV ECX

EDX–EAX is divided by ECX; the unsigned quotient is in EAX and
the unsigned remainder is in EDX

IDIV DATA4

EDX–EAX is divided by the doubleword contents in data segment
memory location DATA4; the signed quotient is in EAX and the
signed remainder is in EDX

DIV DWORD PTR[EDI]

EDX–EAX is divided by the doubleword contents of the data
segment memory location addressed by EDI; the unsigned
quotient is in EAX and the unsigned remainder is in EDX

The Remainder. What is done with the remainder after a division? There are a few possible
choices. The remainder could be used to round the quotient or just dropped to truncate the quotient. If the division is unsigned, rounding requires that the remainder be compared with half the
divisor to decide whether to round up the quotient. The remainder could also be converted to a
fractional remainder.
EXAMPLE 5–16
0000
0002
0004
0006
0008
000A

F6
02
3A
72
FE

F3
E4
E3
02
C0

DIV
ADD
CMP
JB
INC

BL
AH,AH
AH,BL
NEXT
AL

;divide
;double remainder
;test for rounding
;if OK
;round

NEXT:

Example 5–16 shows a sequence of instructions that divide AX by BL and round the unsigned
result. This program doubles the remainder before comparing it with BL to decide whether to round
the quotient. Here, an INC instruction rounds the contents of AL after the comparison.
Suppose that a fractional remainder is required instead of an integer remainder. A fractional
remainder is obtained by saving the quotient. Next, the AL register is cleared to zero. The number
remaining in AX is now divided by the original operand to generate a fractional remainder.
EXAMPLE 5–17
0000
0003
0005
0007
000A
000C
000E

B8
B3
F6
A2
B0
F6
A2

000D
02
F3
0003 R
00
F3
0004 R

MOV
MOV
DIV
MOV
MOV
DIV
MOV

AX,13
BL,2
BL
ANSQ,AL
AL,0
BL
ANSR,AL

;load 13
;load 2
;13/2
;save quotient
;clear AL
;generate remainder
;save remainder

Example 5–17 shows how 13 is divided by 2. The 8-bit quotient is saved in memory location ANSQ, and then AL is cleared. Next, the contents of AX are again divided by 2 to generate
a fractional remainder. After the division, the AL register equals 80H. This is 100000002. If the
binary point (radix) is placed before the leftmost bit of AL, the fractional remainder in AL is
0.100000002 or 0.5 decimal. The remainder is saved in memory location ANSR in this example.

64-Bit Division. The Pentium 4 processor operated in 64-bit mode performs 64-bit division on
signed or unsigned numbers. The 64-bit division uses the RDX:RAX register pair to hold the
dividend and the quotient is found in RAX and the remainder is in RDX after the division. Table
5–15 illustrates a few 64-bit division instructions.

172

CHAPTER 5

TABLE 5–15

Example 64-bit division instructions.

Assembly Language

5-3

Operation

DIV RCX

RDX–RAX is divided by RCX; the unsigned quotient is in RAX and
the unsigned remainder is in RDX

IDIV DATA4

RDX–RAX is divided by the quadword contents in memory
location DATA4; the signed quotient is in RAX and the signed
remainder is in RDX

DIV QWORD PTR[RDI]

RDX–RAX is divided by the quadword contents of the memory
location addressed by RDI; the unsigned quotient is in RAX and
the unsigned remainder is in RDX

BCD AND ASCII ARITHMETIC
The microprocessor allows arithmetic manipulation of both BCD (binary-coded decimal) and
ASCII (American Standard Code for Information Interchange) data. This is accomplished
by instructions that adjust the numbers for BCD and ASCII arithmetic.
The BCD operations occur in systems such as point-of-sales terminals (e.g., cash registers)
and others that seldom require complex arithmetic. The ASCII operations are performed on
ASCII data used by many programs. In many cases, BCD or ASCII arithmetic is rarely used
today, but some of the operations can be used for other purposes.
None of the instructions detailed in this section of the chapter function in the 64-bit mode
of the Pentium 4 or Core2. In the future it appears that the BCD and ASCII instruction will
become obsolete.

BCD Arithmetic
Two arithmetic techniques operate with BCD data: addition and subtraction. The instruction set
provides two instructions that correct the result of a BCD addition and a BCD subtraction. The
DAA (decimal adjust after addition) instruction follows BCD addition, and the DAS (decimal
adjust after subtraction) follows BCD subtraction. Both instructions correct the result of the
addition or subtraction so that it is a BCD number.
For BCD data, the numbers always appear in the packed BCD form and are stored as two
BCD digits per byte. The adjustment instructions function only with the AL register after BCD
addition and subtraction.

DAA Instruction. The DAA instruction follows the ADD or ADC instruction to adjust the result
into a BCD result. Suppose that DX and BX each contain 4-digit packed BCD numbers.
Example 5–18 provides a short sample program that adds the BCD numbers in DX and BX, and
stores the result in CX.
EXAMPLE 5–18
0000
0003
0006
0008
000A
000B
000D
000F
0011
0012

BA
BB
8A
02
27
8A
9A
12
27
8A

1234
3099
C3
C2
C8
C7
C6
E8

MOV
MOV
MOV
ADD
DAA
MOV
MOV
ADC
DAA
MOV

DX,1234H
BX,3099H
AL,BL
AL,DL

;load 1234 BCD
;load 3099 BCD
;sum BL and DL

CL,AL
AL,BH
AL,DH

;answer to CL
;sum BH, DH and carry

CH,AL

;answer to CH

173

ARITHMETIC AND LOGIC INSTRUCTIONS

Because the DAA instruction functions only with the AL register, this addition must
occur 8 bits at a time. After adding the BL and DL registers, the result is adjusted with a DAA
instruction before being stored in CL. Next, add BH and DH registers with carry; the result is
then adjusted with DAA before being stored in CH. In this example, a 1234 is added to 3099 to
generate a sum of 4333, which moves into CX after the addition. Note that 1234 BCD is the
same as 1234H.

DAS Instruction. The DAS instruction functions as does the DAA instruction, except that it follows a subtraction instead of an addition. Example 5-19 is the same as Example 5–18, except that
it subtracts instead of adds DX and BX. The main difference in these programs is that the DAA
instructions change to DAS, and the ADD and ADC instructions change to SUB and SBB
instructions.
EXAMPLE 5–19
0000
0003
0006
0008
000A
000B
000D
000F
0011
0012

BA
BB
8A
2A
2F
8A
9A
1A
2F
8A

1234
3099
C3
C2
C8
C7
C6
E8

MOV
MOV
MOV
SUB
DAS
MOV
MOV
SBB
DAS
MOV

DX,1234H
BX,3099H
AL,BL
AL,DL

;load 1234 BCD
;load 3099 BCD
;subtract DL from BL

CL,AL
AL,BH
AL,DH

;answer to CL
;subtract DH

CH,AL

;answer to CH

ASCII Arithmetic
The ASCII arithmetic instructions function with ASCII-coded numbers. These numbers range in
value from 30H to 39H for the numbers 0–9. There are four instructions used with ASCII arithmetic operations: AAA (ASCII adjust after addition), AAD (ASCII adjust before division),
AAM (ASCII adjust after multiplication), and AAS (ASCII adjust after subtraction). These
instructions use register AX as the source and as the destination.

AAA Instruction. The addition of two one-digit ASCII-coded numbers will not result in any
useful data. For example, if 31H and 39H are added, the result is 6AH. This ASCII addition
(1 + 9 ) should produce a two-digit ASCII result equivalent to a 10 decimal, which is a 31H and
a 30H in ASCII code. If the AAA instruction is executed after this addition, the AX register will
contain a 0100H. Although this is not ASCII code, it can be converted to ASCII code by adding
3030H to AX which generates 3130H. The AAA instruction clears AH if the result is less than 10,
and adds 1 to AH if the result is greater than 10.
EXAMPLE 5–20
0000
0003
0005
0006

B8 0031
04 39
37
05 3030

MOV
ADD
AAA
ADD

AX,31H
AL,39H
AX,3030H

;load ASCII 1
;add ASCII 9
;adjust sum
;answer to ASCII

Example 5–20 shows the way ASCII addition functions in the microprocessor. Please note
that AH is cleared to zero before the addition by using the MOV AX,31H instruction. The
operand of 0031H places 00H in AH and 31H into AL.

AAD Instruction. Unlike all other adjustment instructions, the AAD instruction appears before
a division. The AAD instruction requires that the AX register contain a two-digit unpacked BCD
number (not ASCII) before executing. After adjusting the AX register with AAD, it is divided by
an unpacked BCD number to generate a single-digit result in AL with any remainder in AH.

174

CHAPTER 5

Example 5–21 illustrates how 72 in unpacked BCD is divided by 9 to produce a quotient
of 8. The 0702H loaded into the AX register is adjusted by the AAD instruction to 0048H.
Notice that this converts a two-digit unpacked BCD number into a binary number so it can be
divided with the binary division instruction (DIV). The AAD instruction converts the unpacked
BCD numbers between 00 and 99 into binary.
EXAMPLE 5–21
0000
0002
0005
0007

B3
B8
D5
F6

09
0702
0A
F3

MOV
MOV
AAD
DIV

BL,9
AX,702H
BL

;load divisor
;load dividend
;adjust
;divide

AAM Instruction. The AAM instruction follows the multiplication instruction after multiplying
two one-digit unpacked BCD numbers. Example 5–22 shows a short program that multiplies 5
times 5. The result after the multiplication is 0019H in the AX register. After adjusting the result
with the AAM instruction, AX contains 0205H. This is an unpacked BCD result of 25. If 3030H
is added to 0205H, it has an ASCII result of 3235H.
EXAMPLE 5–22
0000
0002
0004
0006

B0
B1
F6
DA

05
03
E1
0A

MOV
MOV
MUL
AAM

AL,5
CL,3
CL

;load multiplicand
;load multiplier
;adjust

The AAM instruction accomplishes this conversion by dividing AX by 10. The remainder
is found in AL, and the quotient is in AH. Note that the second byte of the instruction contains
0AH. If the 0AH is changed to another value, AAM divides by the new value. For example, if the
second byte is changed to 0BH, the AAM instruction divides by 11. This is accomplished with
DB 0D4H, 0BH in place of AAM, which forces the AMM instruction to multiply by 11.
One side benefit of the AAM instruction is that AAM converts from binary to unpacked
BCD. If a binary number between 0000H and 0063H appears in the AX register, the AAM
instruction converts it to BCD. For example, if AX contains a 0060H before AAM, it will contain 0906H after AAM executes. This is the unpacked BCD equivalent of 96 decimal. If 3030H
is added to 0906H, the result changes to ASCII code.
Example 5–23 shows how the l6-bit binary content of AX is converted to a four-digit
ASCII character string by using division and the AAM instruction. Note that this works for numbers between 0 and 9999. First DX is cleared and then DX–AX is divided by 100. For example,
if AX = 24510 , AX = 2 and DX = 45 after the division. These separate halves are converted to
BCD using AAM, and then 3030H is added to convert to ASCII code.
EXAMPLE 5–23
0000
0002
0005
0007
0009
000C
000D
000F

33
B9
F7
D4
05
92
D4
05

D2
0064
F1
0A
3030
0A
3030

XOR DX,DX
MOV CX,100
DIV CX
AAM
ADD AX,3030H
XCHG AX,DX
AAM
ADD AX,3030H

;clear DX
;divide DX-AX by 100
;convert to BCD
;convert to ASCII
;repeat for remainder

Example 5–24 uses the DOS 21H function AH = 02H to display a sample number in decimal on the video display using the AAM instruction. Notice how AAM is used to convert AL
into BCD. Next, ADD AX,3030H converts the BCD code in AX into ASCII for display with
DOS INT 21H. Once the data are converted to ASCII code, they are displayed by loading DL

ARITHMETIC AND LOGIC INSTRUCTIONS

175

with the most significant digit from AH. Next, the least significant digit is displayed from AL.
Note that the DOS INT 2lH function calls change AL.
EXAMPLE 5–24

0000
0100
0102
0104
0106
0109
010B
010D
010E
0110
0111
0113

B0
B4
D4
05
8A
B4
50
CD
58
8A
CD

48
00
0A
3030
D4
02
21
D0
21

;A program that displays the number in AL, loaded
;with the first instruction (48H).
;
.MODEL TINY
;select tiny model
.CODE
;start code segment
.STARTUP
;start program
MOV AL,48H
;load test data
MOV AH,0
;clear AH
AAM
;convert to BCD
ADD AX,3030H ;convert to ASCII
MOV DL,AH
;display most-significant digit
MOV AH,2
PUSH AX
INT 21H
POP AX
MOV DL,AL
;display least-significant digit
INT 21H
.EXIT
;exit to DOS
END

AAS Instruction. Like other ASCII adjust instructions, AAS adjusts the AX register after an
ASCII subtraction. For example, suppose that 35H subtracts from 39H. The result will be 04H,
which requires no correction. Here, AAS will modify neither AH nor AL. On the other hand, if
38H is subtracted from 37H, then AL will equal 09H and the number in AH will decrement by 1.
This decrement allows multiple-digit ASCII numbers to be subtracted from each other.

5-4

BASIC LOGIC INSTRUCTIONS
The basic logic instructions include AND, OR, Exclusive-OR, and NOT. Another logic instruction is TEST, which is explained in this section of the text because the operation of the TEST
instruction is a special form of the AND instruction. Also explained is the NEG instruction,
which is similar to the NOT instruction.
Logic operations provide binary bit control in low-level software. The logic instructions
allow bits to be set, cleared, or complemented. Low-level software appears in machine language
or assembly language form and often controls the I/O devices in a system. All logic instructions
affect the flag bits. Logic operations always clear the carry and overflow flags, while the other
flags change to reflect the condition of the result.
When binary data are manipulated in a register or a memory location, the rightmost bit
position is always numbered bit 0. Bit position numbers increase from bit 0 toward the left, to bit
7 for a byte, and to bit 15 for a word. A doubleword (32 bits) uses bit position 31 as its leftmost
bit and a quadword (64-bits) uses bit position 63 as it leftmost bit.

AND
The AND operation performs logical multiplication, as illustrated by the truth table in Figure
5–3. Here, two bits, A and B, are ANDed to produce the result X. As indicated by the truth table,
X is a logic 1 only when both A and B are logic 1s. For all other input combinations of A and B,
X is a logic 0. It is important to remember that 0 AND anything is always 0, and 1 AND 1 is
always 1.

176

CHAPTER 5

FIGURE 5–3 (a) The truth
table for the AND operation
and (b) the logic symbol of an
AND gate.

A

B

T

0

0

0

0

1

0

1

0

0

1

1

1

A

T

B

(b)

(a)

The AND instruction can replace discrete AND gates if the speed required is not too great,
although this is normally reserved for embedded control applications. (Note that Intel has
released the 80386EX embedded controller, which embodies the basic structure of the personal
computer system.) With the 8086 microprocessor, the AND instruction often executes in about a
microsecond. With newer versions, the execution speed is greatly increased. Take the 3.0 GHz
Pentium with its clock time of 1/3 ns that executes up to three instruction per clock (1/9 ns per
AND operation). If the circuit that the AND instruction replaces operates at a much slower speed
than the microprocessor, the AND instruction is a logical replacement. This replacement can
save a considerable amount of money. A single AND gate integrated circuit (74HCT08) costs
approximately 40¢, while it costs less than 1/100¢ to store the AND instruction in read-only
memory. Note that a logic circuit replacement such as this only appears in control systems based
on microprocessors and does not generally find application in the personal computer.
The AND operation clears bits of a binary number. The task of clearing a bit in a binary
number is called masking. Figure 5–4 illustrates the process of masking. Notice that the leftmost 4 bits clear to 0 because 0 AND anything is 0. The bit positions that AND with 1s do not
change. This occurs because if a 1 ANDs with a 1, a 1 results; if a 1 ANDs with a 0, a 0
results.
The AND instruction uses any addressing mode except memory-to-memory and segment
register addressing. Table 5–16 lists some AND instructions and comments about their operations.
An ASCII-coded number can be converted to BCD by using the AND instruction to mask
off the leftmost four binary bit positions. This converts the ASCII 30H to 39H to 0–9. Example
5–25 shows a short program that converts the ASCII contents of BX into BCD. The AND
instruction in this example converts two digits from ASCII to BCD simultaneously.
EXAMPLE 5–25
0000 BB 3135
0003 81 E3 0F0F

MOV BX,3135H
AND BX,0F0FH

;load ASCII
;mask BX

OR
The OR operation performs logical addition and is often called the Inclusive-OR function. The
OR function generates a logic 1 output if any inputs are 1. A 0 appears at the output only when
all inputs are 0. The truth table for the OR function appears in Figure 5–5. Here, the inputs A and

FIGURE 5–4 The operation
of the AND function showing
how bits of a number are
cleared to zero.

xxxx xxxx
• 0000 1111
0000 xxxx

Unknown number
Mask
Result

177

ARITHMETIC AND LOGIC INSTRUCTIONS

TABLE 5–16

Example AND instructions.

Assembly Language

Operation

AND AL,BL

AL = AL and BL

AND CX,DX

CX = CX and DX

AND ECX,EDI

ECX = ECX and EDI

AND RDX,RBP

RDX = RDX and RBP 164-bit mode2

AND CL,33H

CL = CL and 33H

AND DI,4FFFH

DI = DI and 4FFFH

AND ESI,34H

ESI = ESI and 34H

AND RAX,1

RAX = RAX and 1 164-bit mode2
The word contents of the data segment memory location addressed
by DI are ANDed with AX

AND AX,[DI]
AND ARRAY[SI],AL

The byte contents of the data segment memory location addressed
by ARRAY plus SI are ANDed with AL

AND [EAX],CL

CL is ANDed with the byte contents of the data segment memory
location addressed by ECX

FIGURE 5–5 (a) The truth
table for the OR operation and
(b) the logic symbol of an OR
gate.

A

B

T

0

0

0

0

1

1

1

0

1

1

1

1

A
T

B

(b)

(a)

B OR together to produce the X output. It is important to remember that 1 ORed with anything
yields a 1.
In embedded controller applications, the OR instruction can also replace discrete OR
gates. This results in considerable savings because a quad, two-input OR gate (74HCT32) costs
about 40¢, while the OR instruction costs less than 1/100¢ to store in a read-only memory.
Figure 5–6 shows how the OR gate sets (1) any bit of a binary number. Here, an unknown
number (XXXX XXXX) ORs with a 0000 1111 to produce a result of XXXX 1111. The rightmost 4 bits set, while the leftmost 4 bits remain unchanged. The OR operation sets any bit; the
AND operation clears any bit.
The OR instruction uses any of the addressing modes allowed to any other instruction
except segment register addressing. Table 5–17 illustrates several example OR instructions with
comments about their operation.
FIGURE 5–6 The operation
of the OR function showing
how bits of a number are set
to one.

xxxx xxxx
+ 0000 1111
xxxx 1111

Unknown number
Mask
Result

178

CHAPTER 5

TABLE 5–17

Example OR instructions.

Assembly Language

Operation

OR AH,BL

AL = AL or BL

OR SI,DX

SI = SI or DX

OR EAX,EBX

EAX = EAX or EBX

OR R9,R10

R9 = R9 or R10 164-bit mode2

OR DH,0A3H

DH = DH or 0A3H

OR SP,990DH

SP = SP or 990DH

OR EBP,10

EBP = EBP or 10

OR RBP,1000H

RBP = RBP or 1000H 164-bit mode2
DX is ORed with the word contents of data segment memory
location addressed by BX

OR DX,[BX]

The byte contents of the data segment memory location
addressed by DI plus 2 are ORed with AL

OR DATES[DI + 2],AL

Suppose that two BCD numbers are multiplied and adjusted with the AAM instruction.
The result appears in AX as a two-digit unpacked BCD number. Example 5–26 illustrates this
multiplication and shows how to change the result into a two-digit ASCII-coded number using
the OR instruction. Here, OR AX,3030H converts the 0305H found in AX to 3335H. The OR
operation can be replaced with an ADD AX,3030H to obtain the same results.
EXAMPLE 5–26
0000
0002
0004
0006
0008

B0
B3
F6
D4
0D

05
07
E3
0A
3030

MOV
MOV
MUL
AAM
OR

AL,5
BL,7
BL

;load data

;adjust
;convert to ASCII

AX,3030H

Exclusive-OR
The Exclusive-OR instruction (XOR) differs from Inclusive-OR (OR). The difference is that a
1,1 condition of the OR function produces a 1; the 1,1 condition of the Exclusive-OR operation
produces a 0. The Exclusive-OR operation excludes this condition; the Inclusive-OR includes it.
Figure 5–7 shows the truth table of the Exclusive-OR function. (Compare this with
Figure 5–5 to appreciate the difference between these two OR functions.) If the inputs of the

FIGURE 5–7 (a) The truth
table for the Exclusive-OR
operation and (b) the logic
symbol of an Exclusive-OR
gate.

A

B

T

0

0

0

0

1

1

1

0

1

1

1

0

(a)

A
T

B

(b)

179

ARITHMETIC AND LOGIC INSTRUCTIONS

TABLE 5–18

Example Exclusive-OR instructions.

Assembly Language

Operation

XOR CH,DL

CH = CH xor DL

XOR SI,BX

SI = SI xor BX

XOR EBX,EDI

EBX = EBX xor EDI

XOR RAX,RBX

RAX = RAX xor RBX 164-bit mode2

XOR AH,0EEH

AH = AH xor 0EEH

XOR DI,00DDH

DI = DI xor 00DDH

XOR ESI,100

ESI = ESI xor 100

XOR R12,20

R12 = R12 xor 20 164-bit mode2
DX is Exclusive-ORed with the word contents of the data segment
memory location addressed by SI

XOR DX,[SI]
XOR DEAL[BP+2],AH

AH is Exclusive-ORed with the byte contents of the stack segment
memory location addressed by BP plus 2

Exclusive-OR function are both 0 or both 1, the output is 0. If the inputs are different, the output is 1. Because of this, the Exclusive-OR is sometimes called a comparator.
The XOR instruction uses any addressing mode except segment register addressing. Table
5–18 lists several Exclusive-OR instructions and their operations.
As with the AND and OR functions, Exclusive-OR can replace discrete logic circuitry in
embedded applications. The 74HCT86 quad, two-input Exclusive-OR gate is replaced by one
XOR instruction. The 74HCT86 costs about 40¢, whereas the instruction costs less than 1/100¢
to store in the memory. Replacing just one 74HCT86 saves a considerable amount of money,
especially if many systems are built.
The Exclusive-OR instruction is useful if some bits of a register or memory location must
be inverted. This instruction allows part of a number to be inverted or complemented. Figure 5–8
shows how just part of an unknown quantity can be inverted by XOR. Notice that when a 1
Exclusive-ORs with X, the result is X. If a 0 Exclusive-ORs with X, the result is X.
Suppose that the leftmost 10 bits of the BX register must be inverted without changing the
rightmost 6 bits. The XOR BX,0FFC0H instruction accomplishes this task. The AND instruction
clears (0) bits, the OR instruction sets (1) bits, and now the Exclusive-OR instruction inverts bits.
These three instructions allow a program to gain complete control over any bit stored in any register or memory location. This is ideal for control system applications in which equipment must
be turned on (1), turned off (0), and toggled from on to off or off to on.
A common use for the Exclusive-OR instruction is to clear a register to zero. For example,
the XOR CH,CH instruction clears register CH to 00H and requires 2 bytes of memory to store
the instruction. Likewise, the MOV CH, 00H instruction also clears CH to 00H, but requires
3 bytes of memory. Because of this saving, the XOR instruction is often used to clear a register
in place of a move immediate.
Example 5–27 shows a short sequence of instructions that clears bits 0 and 1 of CX, sets
bits 9 and 10 of CX, and inverts bit 12 of CX. The OR instruction is used to set bits, the AND
instruction is used to clear bits, and the XOR instruction inverts bits.
FIGURE 5–8 The operation
of the Exclusive-OR function
showing how bits of a number
are inverted.

xxxx xxxx

Unknown number

+ 0 0 0 0 1 1 1 1 Mask
xxxx xxxx

Result

180

CHAPTER 5

EXAMPLE 5–27
0000 81 C9 0600
0004 83 E1 FC
0007 81 F1 1000

OR
AND
XOR

CX,0600H
CX,0FFFCH
CX,1000H

;set bits 9 and 10
;clear bits 0 and 1
;invert bit 12

Test and Bit Test Instructions
The TEST instruction performs the AND operation. The difference is that the AND instruction
changes the destination operand, whereas the TEST instruction does not. A TEST only affects
the condition of the flag register, which indicates the result of the test. The TEST instruction uses
the same addressing modes as the AND instruction. Table 5–19 lists some TEST instructions and
their operations.
The TEST instruction functions in the same manner as a CMP instruction. The difference
is that the TEST instruction normally tests a single bit (or occasionally multiple bits), whereas
the CMP instruction tests the entire byte, word, or doubleword. The zero flag (Z) is a logic 1
(indicating a zero result) if the bit under test is a zero, and Z = 0 (indicating a nonzero result) if
the bit under test is not zero.
Usually the TEST instruction is followed by either the JZ (jump if zero) or JNZ (jump if
not zero) instruction. The destination operand is normally tested against immediate data. The
value of immediate data is 1 to test the rightmost bit position, 2 to test the next bit, 4 for the next,
and so on.
Example 5–28 lists a short program that tests the rightmost and leftmost bit positions of the
AL register. Here, 1 selects the rightmost bit and 128 selects the leftmost bit. (Note: A 128 is an
80H.) The JNZ instruction follows each test to jump to different memory locations, depending
on the outcome of the tests. The JNZ instruction jumps to the operand address (RIGHT or LEFT
in the example) if the bit under test is not zero.
EXAMPLE 5–28
0000
0002
0004
0006

A8
75
A8
75

01
1C
80
38

TEST
JNZ
TEST
JNZ

AL,1
RIGHT
AL,128
LEFT

;test right bit
;if set
;test left bit
;if set

The 80386 through the Pentium 4 processors contain additional test instructions that test
single bit positions. Table 5–20 lists the four different bit test instructions available to these
microprocessors.
All four forms of the bit test instruction test the bit position in the destination operand
selected by the source operand. For example, the BT AX,4 instruction tests bit position 4 in AX.
The result of the test is located in the carry flag bit. If bit position 4 is a 1, carry is set; if bit position 4 is a 0, carry is cleared.
TABLE 5–19 Example
TEST instructions.

Assembly Language

Operation

TEST DL,DH

DL is ANDed with DH

TEST CX,BX

CX is ANDed with BX

TEST EDX,ECX

EDX is ANDed with ECX

TEST RDX,R15

RDX is ANDed with R15 (64-bit mode)

TEST AH,4

AH is ANDed with 4

TEST EAX,256

EAX is ANDed with 256

181

ARITHMETIC AND LOGIC INSTRUCTIONS

TABLE 5–20

Bit test instructions.

Assembly Language

Operation

BT

Tests a bit in the destination operand specified by the source
operand

BTC

Tests and complements a bit in the destination operand specified
by the source operand

BTR

Tests and resets a bit in the destination operand specified by the
source operand

BTS

Tests and sets a bit in the destination operand specified by the
source operand

The remaining 3-bit test instructions also place the bit under test into the carry flag and
change the bit under test afterward. The BTC AX,4 instruction complements bit position 4 after
testing it, the BTR AX,4 instruction clears it (0) after the test, and the BTS AX,4 instruction sets
it (1) after the test.
Example 5–29 repeats the sequence of instructions listed in Example 5–27. Here, the BTR
instruction clears bits in CX, BTS sets bits in CX, and BTC inverts bits in CX.
EXAMPLE 5–29
0000
0004
0008
000C
0010

0F
0F
0F
0F
0F

BA
BA
BA
BA
BA

E9
E9
F1
F1
F9

09
0A
00
01
0C

BTS
BTS
BTR
BTR
BTC

CX,9
CX,10
CX,0
CX,1
CX,12

;set bit 9
;set bit 10
;clear bit 0
;clear bit 1
;complement bit 12

NOT and NEG
Logical inversion, or the one’s complement (NOT), and arithmetic sign inversion, or the two’s
complement (NEG), are the last two logic functions presented (except for shift and rotate in the
next section of the text). These are two of a few instructions that contain only one operand.
Table 5–21 lists some variations of the NOT and NEG instructions. As with most other instructions, NOT and NEG can use any addressing mode except segment register addressing.
TABLE 5–21

Example NOT and NEG instructions.

Assembly Language

Operation

NOT CH

CH is one’s complemented

NEG CH

CH is two’s complemented

NEG AX

AX is two’s complemented

NOT EBX

EBX is one’s complemented

NEG ECX

ECX is two’s complemented

NOT RAX

RAX is one’s complemented (64-bit mode)

NOT TEMP

The contents of data segment memory location TEMP is one’s
complemented

NOT BYTE PTR[BX]

The byte contents of the data segment memory location
addressed by BX are one’s complemented

182

CHAPTER 5

The NOT instruction inverts all bits of a byte, word, or doubleword. The NEG instruction
two’s complements a number, which means that the arithmetic sign of a signed number changes
from positive to negative or from negative to positive. The NOT function is considered logical,
and the NEG function is considered an arithmetic operation.

5-5

SHIFT AND ROTATE
Shift and rotate instructions manipulate binary numbers at the binary bit level, as did the AND,
OR, Exclusive-OR, and NOT instructions. Shifts and rotates find their most common applications in low-level software used to control I/O devices. The microprocessor contains a complete
complement of shift and rotate instructions that are used to shift or rotate any memory data or
register.

Shift
Shift instructions position or move numbers to the left or right within a register or memory location. They also perform simple arithmetic such as multiplication by powers of 2+n (left shift) and
division by powers of 2–n (right shift). The microprocessor’s instruction set contains four different shift instructions: Two are logical shifts and two are arithmetic shifts. All four shift operations appear in Figure 5–9.
Notice in Figure 5–9 that there are two right shifts and two left shifts. The logical shifts
move a 0 into the rightmost bit position for a logical left shift and a 0 into the leftmost bit position for a logical right shift. There are also two arithmetic shifts. The arithmetic shift left and logical left shift are identical. The arithmetic right shift and logical right shift are different because
the arithmetic right shift copies the sign-bit through the number, whereas the logical right shift
copies a 0 through the number.

FIGURE 5–9 The shift
instructions showing the
operation and direction
of the shift.

Target register or memory
C
0

SHL

C
0

SAL

C
SHR

0

C
SAR

Sign
bit

183

ARITHMETIC AND LOGIC INSTRUCTIONS

TABLE 5–22

Example shift instructions.

Assembly Language

Operation

SHL AX,1

AX is logically shifted left 1 place

SHR BX,12

BX is logically shifted right 12 places

SHR ECX,10

ECX is logically shifted right 10 places

SHL RAX,50

RAX is logically shifted left 50 places (64-bit mode)

SAL DATA1,CL

The contents of data segment memory location DATA1 are
arithmetically shifted left the number of spaces specified by CL

SHR RAX,CL

RAX is logically shifted right the number of spaces specified by CL
(64-bit mode)

SAR SI,2

SI is arithmetically shifted right 2 places

SAR EDX,14

EDX is arithmetically shifted right 14 places

Logical shift operations function with unsigned numbers, and arithmetic shifts function
with signed numbers. Logical shifts multiply or divide unsigned data, and arithmetic shifts multiply or divide signed data. A shift left always multiplies by 2 for each bit position shifted, and a
shift right always divides by 2 for each bit position shifted. Shifting a number two places, to the
left or right, multiplies or divides by 4.
Table 5–22 illustrates some addressing modes allowed for the various shift instructions. There are two different forms of shifts that allow any register (except the segment register) or memory location to be shifted. One mode uses an immediate shift count, and the
other uses register CL to hold the shift count. Note that CL must hold the shift count. When
CL is the shift count, it does not change when the shift instruction executes. Note that the
shift count is a modulo-32 count, which means that a shift count of 33 will shift the data one
place (33>32 = remainder of 1 ). The same applies to a 64-bit number, but the shift count is
modulo-64.
Example 5–30 shows how to shift the DX register left 14 places in two different ways. The
first method uses an immediate shift count of 14. The second method loads 14 into CL and then
uses CL as the shift count. Both instructions shift the contents of the DX register logically to the
left 14 binary bit positions or places.
EXAMPLE 5–30
0000 C1 E2 0E

SHL DX,14
or

0003 B1 0E
0005 D3 E2

MOV CL,14
SHL DX,CL

Suppose that the contents of AX must be multiplied by 10, as shown in Example 5–31.
This can be done in two ways: by the MUL instruction or by shifts and additions. A number is
doubled when it shifts left one place. When a number is doubled, and then added to the number
times 8, the result is 10 times the number. The number 10 decimal is 1010 in binary. A logic 1
appears in both the 2’s and 8’s positions. If 2 times the number is added to 8 times the number,
the result is 10 times the number. Using this technique, a program can be written to multiply by
any constant. This technique often executes faster than the multiply instruction found in earlier
versions of the Intel microprocessor.

184

CHAPTER 5

EXAMPLE 5–31

0000
0002
0004
0007

D1
8B
C1
03

E0
D8
E0 02
C3

0009
000B
000D
0010

D1
8B
C1
03

E0
D8
E0 03
C3

0012 8B D8
0014 C1 E0 02
0017 03 C3

;Multiply AX by 10 (1010)
;
SHL AX,1
MOV BX,AX
SHL AX,2
ADD AX,BX
;
;Multiply AX by 18 (10010)
;
SHL AX,1
MOV BX,AX
SHL AX,3
ADD AX,BX
;
;Multiply AX by 5 (101)
;
MOV BX,AX
SHL AX,2
ADD AX,BX

;AX times 2
;AX times 8
;AX times 10

;AX times 2
;AX times 16
;AX times 18

;AX times 4
;AX times 5

Double-Precision Shifts (80386–Core2 Only). The 80386 and above contain two double precision shifts: SHLD (shift left) and SHRD (shift right). Each instruction contains three operands,
instead of the two found with the other shift instructions. Both instructions function with two
16-or 32-bit registers, or with one 16- or 32-bit memory location and a register.
The SHRD AX,BX,12 instruction is an example of the double-precision shift right instruction. This instruction logically shifts AX right by 12 bit positions. The rightmost 12 bits of BX
shift into the leftmost 12 bits of AX. The contents of BX remain unchanged by this instruction.
The shift count can be an immediate count, as in this example, or it can be found in register CL,
as with other shift instructions.
The SHLD EBX,ECX,16 instruction shifts EBX left. The leftmost 16 bits of ECX fill the
rightmost 16 bits of EBX after the shift. As before, the contents of ECX, the second operand,
remain unchanged. This instruction, as well as SHRD, affects the flag bits.

Rotate
Rotate instructions position binary data by rotating the information in a register or memory location, either from one end to another or through the carry flag. They are often used to shift or position numbers that are wider than 16 bits in the 8086–80286 microprocessors or wider than 32 bits
in the 80386 through the Core2. The four available rotate instructions appear in Figure 5–10.
Numbers rotate through a register or memory location, through the C flag (carry), or through
a register or memory location only. With either type of rotate instruction, the programmer can select
either a left or a right rotate. Addressing modes used with rotate are the same as those used with
shifts. A rotate count can be immediate or located in register CL. Table 5–23 lists some of the possible rotate instructions. If CL is used for a rotate count, it does not change. As with shifts, the count
in CL is a modulo-32 count for a 32-bit operation and modulo-64 for a 64-bit operation.
Rotate instructions are often used to shift wide numbers to the left or right. The program
listed in Example 5–32 shifts the 48-bit number in registers DX, BX, and AX left one binary
place. Notice that the least significant 16 bits (AX) shift left first. This moves the leftmost bit of
AX into the carry flag bit. Next, the rotate BX instruction rotates carry into BX, and its leftmost
bit moves into carry. The last instruction rotates carry into DX, and the shift is complete.
EXAMPLE 5–32
0000 D1 E0
0002 D1 D3
0004 D1 D2

SHL AX,1
RCL BX,1
RCL DX,1

185

ARITHMETIC AND LOGIC INSTRUCTIONS

FIGURE 5–10 The rotate
instructions showing the
direction and operation of
each rotate.

Target register or memory
C
RCL

C
ROL

C
RCR

C
ROR

TABLE 5–23

Example rotate instructions.

Assembly Language

Operation

ROL SI,14

SI rotates left 14 places

RCL BL,6

BL rotates left through carry 6 places

ROL ECX,18

ECX rotates left 18 places

ROL RDX,40

RDX rotates left 40 places

RCR AH,CL

AH rotates right through carry the number of places specified by CL

ROR WORD PTR[BP],2

The word contents of the stack segment memory location
addressed by BP rotate right 2 places

Bit Scan Instructions
Although the bit scan instructions don’t shift or rotate numbers, they do scan through a number
searching for a 1-bit. Because this is accomplished within the microprocessor by shifting the
number, bit scan instructions are included in this section of the text.
The bit scan instructions BSF (bit scan forward) and BSR (bit scan reverse) are available
only in the 80386–Pentium 4 processors. Both forms scan through the source number, searching
for the first 1-bit. The BSF instruction scans the number from the leftmost bit toward the right,
and BSR scans the number from the rightmost bit toward the left. If a 1-bit is encountered, the
zero flag is set and the bit position number of the 1-bit is placed into the destination operand. If
no 1-bit is encountered (i.e., the number contains all zeros), the zero flag is cleared. Thus, the
result is not-zero if no 1-bit is encountered.
For example, if EAX = 60000000H and the BSF EBX,EAX instruction executes, the
number is scanned from the leftmost bit toward the right. The first 1-bit encountered is at bit
position 30, which is placed into EBX and the zero flag bit is set. If the same value for EAX is
used for the BSR instruction, the EBX register is loaded with 29 and the zero flag bit is set.

186

CHAPTER 5

5-6

STRING COMPARISONS
As illustrated in Chapter 4, the string instructions are very powerful because they allow the programmer to manipulate large blocks of data with relative ease. Block data manipulation occurs
with the string instructions MOVS, LODS, STOS, INS, and OUTS. In this section, additional
string instructions that allow a section of memory to be tested against a constant or against
another section of memory are discussed. To accomplish these tasks, use the SCAS (string scan)
or CMPS (string compare) instructions.

SCAS
The SCAS (string scan instruction) compares the AL register with a byte block of memory, the
AX register with a word block of memory, or the EAX register (80386-Core2) with a doubleword block of memory. The SCAS instruction subtracts memory from AL, AX, or EAX without
affecting either the register or the memory location. The opcode used for byte comparison is
SCASB, the opcode used for the word comparison is SCASW, and the opcode used for a doubleword comparison is SCASD. In all cases, the contents of the extra segment memory location
addressed by DI is compared with AL, AX, or EAX. Recall that this default segment (ES) cannot be changed with a segment override prefix.
Like the other string instructions, SCAS instructions use the direction flag (D) to select
either auto-increment or auto-decrement operation for DI. They also repeat if prefixed by a conditional repeat prefix.
Suppose that a section of memory is 100 bytes long and begins at location BLOCK. This
section of memory must be tested to see whether any location contains 00H. The program in
Example 5–33 shows how to search this part of memory for 00H using the SCASB instruction.
In this example, the SCASB instruction has an REPNE (repeat while not equal) prefix. The
REPNE prefix causes the SCASB instruction to repeat until either the CX register reaches 0, or
until an equal condition exists as the outcome of the SCASB instruction’s comparison. Another
conditional repeat prefix is REPE (repeat while equal). With either repeat prefix, the contents of
CX decrements without affecting the flag bits. The SCASB instruction and the comparison it
makes change the flags.
EXAMPLE 5–33
0000
0003
0004
0007
0009

BF 0011 R
FC
B9 0064
32 C0
F2/AE

MOV
CLD
MOV
XOR
REPNE

DI,OFFSET BLOCK
CX,100
AL,AL
SCASB

;address data
;auto-increment
;load counter
;clear AL

Suppose that you must develop a program that skips ASCII-coded spaces in a memory
array. (This task appears in the procedure listed in Example 5–34.) This procedure assumes that
the DI register already addresses the ASCII-coded character string and that the length of the
string is 256 bytes or fewer. Because this program is to skip spaces (20H), the REPE prefix is
used with a SCASB instruction. The SCASB instruction repeats the comparison, searching for a
20H, as long as an equal condition exists.
EXAMPLE 5–34
0000
0001
0004
0006

FC
B9 0100
B0 20
F3/AE

CLD
MOV CX,256
MOV AL,20H
REPE SCASB

;auto-increment
;load counter
;get space

187

ARITHMETIC AND LOGIC INSTRUCTIONS

CMPS
The CMPS (compare strings instruction) always compares two sections of memory data as bytes
(CMPSB), words (CMPSW), or doublewords (CMPSD). Note that only the 80386 through
Core2 can use doublewords. In the Pentium 4 or Core2 operated in 64-bit mode, a CMPSQ
instruction uses quadwords. The contents of the data segment memory location addressed by SI
are compared with the contents of the extra segment memory location addressed by DI. The
CMPS instruction increments or decrements both SI and DI. The CMPS instruction is normally
used with either the REPE or REPNE prefix. Alternates to these prefixes are REPZ (repeat while
zero) and REPNZ (repeat while not zero), but usually the REPE or REPNE prefixes are used in
programming.
Example 5–35 illustrates a short procedure that compares two sections of memory searching for a match. The CMPSB instruction is prefixed with REPE. This causes the search to
continue as long as an equal condition exists. When the CX register becomes 0 or an unequal
condition exists, the CMPSB instruction stops execution. After the CMPSB instruction ends, the
CX register is 0 or the flags indicate an equal condition when the two strings match. If CX is not
0 or the flags indicate a not-equal condition, the strings do not match.
EXAMPLE 5–35
0000
0003
0006
0007
000A

5–7

BE 0075 R
BF 007F R
FC
B9 000A
F3/A6

MOV
MOV
CLD
MOV
REPE

SI,OFFSET LINE
DI,OFFSET TABLE
CX,10
CMPSB

;address LINE
;address TABLE
;auto-increment
;load counter
;search

SUMMARY
1. Addition (ADD) can be 8, 16, 32, or 64 bits. The ADD instruction allows any addressing mode except segment register addressing. Most flags (C, A, S, Z, P, and O) change
when the ADD instruction executes. A different type of addition, add-with-carry
(ADC), adds two operands and the contents of the carry flag (C). The 80486 through the
Core2 processors have an additional instruction (XADD) that combines an addition with
an exchange.
2. The increment instruction (INC) adds 1 to the byte, word, or doubleword contents of a register or memory location. The INC instruction affects the same flag bits as ADD except the
carry flag. The BYTE PTR, WORD PTR, DWORD PTR, or QWORD PTR directives
appear with the INC instruction when the contents of a memory location are addressed by a
pointer.
3. Subtraction (SUB) is a byte, word, doubleword, or quadword and is performed on a register
or a memory location. The only form of addressing not allowed by the SUB instruction is
segment register addressing. The subtract instruction affects the same flags as ADD and subtracts carry if the SBB form is used.
4. The decrement (DEC) instruction subtracts 1 from the contents of a register or a memory
location. The only addressing modes not allowed with DEC are immediate or segment register addressing. The DEC instruction does not affect the carry flag and is often used with
BYTE PTR, WORD PTR, DWORD PTR, or QWORD PTR.
5. The comparison (CMP) instruction is a special form of subtraction that does not store the
difference; instead, the flags change to reflect the difference. Comparison is used to compare an entire byte or word located in any register (except segment) or memory location.

188

CHAPTER 5

6.

7.

8.

9.

10.

11.
12.

13.

14.
15.

16.

An additional comparison instruction (CMPXCHG), which is a combination of comparison and exchange instructions, is found in the 80486–Core2 processors. In the
Pentium–Core2 processors, the CMPXCHG8B instruction compares and exchanges
quadword data. In the 64-bit Pentium 4 and Core2, a COMPXCHG16B instruction is
available.
Multiplication is byte, word, or doubleword, and it can be signed (IMUL) or unsigned
(MUL). The 8-bit multiplication always multiplies register AL by an operand with the product found in AX. The 16-bit multiplication always multiplies register AX by an operand
with the product found in DX–AX. The 32-bit multiply always multiplies register EAX by
an operand with the product found in EDX–EAX. A special IMUL immediate instruction
exists on the 80186–Core2 processors that contains three operands. For example, the IMUL
BX,CX,3 instruction multiplies CX by 3 and leaves the product in BX. In the Pentium 4 and
Core2 with 64-bit mode enabled, multiplication is 64 bits.
Division is byte, word, or doubleword, and it can be signed (IDIV) or unsigned (DIV). For
an 8-bit division, the AX register divides by the operand, after which the quotient appears
in AL and the remainder appears in AH. In the 16-bit division, the DX–AX register
divides by the operand, after which the AX register contains the quotient and DX contains
the remainder. In the 32-bit division, the EDX–EAX register is divided by the operand,
after which the EAX register contains the quotient and the EDX register contains the
remainder. Note that the remainder after a signed division always assumes the sign of the
dividend.
BCD data add or subtract in packed form by adjusting the result of the addition with DAA or
the subtraction with DAS. ASCII data are added, subtracted, multiplied, or divided when the
operations are adjusted with AAA, AAS, AAM, and AAD. These instructions do not function in the 64-bit mode.
The AAM instruction has an interesting added feature that allows it to convert a binary number into unpacked BCD. This instruction converts a binary number between 00H–63H into
unpacked BCD in AX. The AAM instruction divides AX by 10, and leaves the remainder in
AL and quotient in AH. These instructions do not function in the 64-bit mode.
The AND, OR, and Exclusive-OR instructions perform logic functions on a byte, word, or
doubleword stored in a register or memory location. All flags change with these instructions,
with carry (C) and overflow (O) cleared.
The TEST instruction performs the AND operation, but the logical product is lost. This
instruction changes the flag bits to indicate the outcome of the test.
The NOT and NEG instructions perform logical inversion and arithmetic inversion. The
NOT instruction one’s complements an operand, and the NEG instruction two’s complements an operand.
There are eight different shift and rotate instructions. Each of these instructions shifts or
rotates a byte, word, or doubleword register or memory data. These instructions have two
operands: The first is the location of the data shifted or rotated, and the second is an immediate shift or rotate count or CL. If the second operand is CL, the CL register holds the shift
or rotate count. In the 80386 through the Core2 processors, two additional double-precision
shifts (SHRD and SHLD) exist.
The scan string (SCAS) instruction compares AL, AX, or EAX with the contents of the extra
segment memory location addressed by DI.
The string compare (CMPS) instruction compares the byte, word, or doubleword contents of
two sections of memory. One section is addressed by DI in the extra segment, and the other
is addressed by SI in the data segment.
The SCAS and CMPS instructions repeat with the REPE or REPNE prefixes. The REPE
prefix repeats the string instruction while an equal condition exists, and the REPNE repeats
the string instruction while a not-equal condition exists.

ARITHMETIC AND LOGIC INSTRUCTIONS

5–8

189

QUESTIONS AND PROBLEMS
1. Select an ADD instruction that will:
(a) add BX to AX
(b) add 12H to AL
(c) add EDI and EBP
(d) add 22H to CX
(e) add the data addressed by SI to AL
(f) add CX to the data stored at memory location FROG
(g) add 234H to RCX
2. What is wrong with the ADD RCX,AX instruction?
3. Is it possible to add CX to DS with the ADD instruction?
4. If AX = 1001H and DX = 20FFH , list the sum and the contents of each flag register bit
(C, A, S, Z, and O) after the ADD AX,DX instruction executes.
5. Develop a short sequence of instructions that adds AL, BL, CL, DL, and AH. Save the sum
in the DH register.
6. Develop a short sequence of instructions that adds AX, BX, CX, DX, and SP. Save the sum
in the DI register.
7. Develop a short sequence of instructions that adds ECX, EDX, and ESI. Save the sum in the
EDI register.
8. Develop a short sequence of instructions that adds RCX, RDX, and RSI. Save the sum in the
R12 register.
9. Select an instruction that adds BX to DX, and also adds the contents of the carry flag (C) to
the result.
10. Choose an instruction that adds 1 to the contents of the SP register.
11. What is wrong with the INC [BX] instruction?
12. Select a SUB instruction that will:
(a) subtract BX from CX
(b) subtract 0EEH from DH
(c) subtract DI from SI
(d) subtract 3322H from EBP
(e) subtract the data address by SI from CH
(f) subtract the data stored 10 words after the location addressed by SI from DX
(g) subtract AL from memory location FROG
(h) subtract R9 from R10
13. If DL = 0F3H and BH = 72H , list the difference after BH is subtracted from DL and show
the contents of the flag register bits.
14. Write a short sequence of instructions that subtracts the numbers in DI, SI, and BP from the
AX register. Store the difference in register BX.
15. Choose an instruction that subtracts 1 from register EBX.
16. Explain what the SBB [DI–4],DX instruction accomplishes.
17. Explain the difference between the SUB and CMP instruction.
18. When two 8-bit numbers are multiplied, where is the product found?
19. When two 16-bit numbers are multiplied, what two registers hold the product? Show the registers that contain the most and least significant portions of the product.
20. When two numbers multiply, what happens to the O and C flag bits?
21. Where is the product stored for the MUL EDI instruction?
22. Write a sequence of instructions that cube the 8-bit number found in DL. Load DL with a
5 initially, and make sure that your result is a l6-bit number.

190

CHAPTER 5

23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

33.
34.
35.
36.

37.

38.
39.

40.
41.

42.
43.

What is the difference between the IMUL and MUL instructions?
Describe the operation of the IMUL BX,DX,100H instruction.
When 8-bit numbers are divided, in which register is the dividend found?
When l6-bit numbers are divided, in which register is the quotient found?
When 64-bit numbers are divided, in which register is the quotient found?
What errors are detected during a division?
Explain the difference between the IDIV and DIV instructions.
Where is the remainder found after an 8-bit division?
Where is the quotient found after a 64-bit division?
Write a short sequence of instructions that divides the number in BL by the number in CL
and then multiplies the result by 2. The final answer must be a 16-bit number stored in the
DX register.
Which instructions are used with BCD arithmetic operations?
Explain how the AAM instruction converts from binary to BCD.
Which instructions are used with ASCII arithmetic operations?
Develop a sequence of instructions that converts the unsigned number in AX (values of
0–65535) into a 5-digit BCD number stored in memory, beginning at the location addressed
by the BX register in the data segment. Note that the most significant character is stored first
and no attempt is made to blank leading zeros.
Develop a sequence of instructions that adds the 8-digit BCD number in AX and BX to the
8-digit BCD number in CX and DX. (AX and CX are the most significant registers. The
result must be found in CX and DX after the addition.)
Does the AAM instruction function in the 64-bit mode?
Select an AND instruction that will:
(a) AND BX with DX and save the result in BX
(b) AND 0EAH with DH
(c) AND DI with BP and save the result in DI
(d) AND 1122H with EAX
(e) AND the data addressed by BP with CX and save the result in memory
(f) AND the data stored in four words before the location addressed by SI with DX and save
the result in DX
(g) AND AL with memory location WHAT and save the result at location WHAT
Develop a short sequence of instructions that clears (0) the three leftmost bits of DH without
changing the remainder of DH and stores the result in BH.
Select an OR instruction that will:
(a) OR BL with AH and save the result in AH
(b) OR 88H with ECX
(c) OR DX with SI and save the result in SI
(d) OR 1122H with BP
(e) OR the data addressed by RBX with RCX and save the result in memory
(f ) OR the data stored 40 bytes after the location addressed by BP with AL and save the
result in AL
(g) OR AH with memory location WHEN and save the result in WHEN
Develop a short sequence of instructions that sets (1) the rightmost 5 bits of DI without
changing the remaining bits of DI. Save the results in SI.
Select the XOR instruction that will:
(a) XOR BH with AH and save the result in AH
(b) XOR 99H with CL
(c) XOR DX with DI and save the result in DX
(d) XOR lA23H with RSP

ARITHMETIC AND LOGIC INSTRUCTIONS

44.
45.
46.
47.
48.

49.
50.
51.
52.
53.
54.
55.
56.

191

(e) XOR the data addressed by EBX with DX and save the result in memory
(f) XOR the data stored 30 words after the location addressed by BP with DI and save the
result in DI
(g) XOR DI with memory location WELL and save the result in DI
Develop a sequence of instructions that sets (1) the rightmost 4 bits of AX; clears (0) the
leftmost 3 bits of AX; and inverts bits 7, 8, and 9 of AX.
Describe the difference between the AND and TEST instructions.
Select an instruction that tests bit position 2 of register CH.
What is the difference between the NOT and the NEG instruction?
Select the correct instruction to perform each of the following tasks:
(a) shift DI right three places, with zeros moved into the leftmost bit
(b) move all bits in AL left one place, making sure that a 0 moves into the rightmost bit
position
(c) rotate all the bits of AL left three places
(d) rotate carry right one place through EDX
(e) move the DH register right one place, making sure that the sign of the result is the same
as the sign of the original number
What does the SCASB instruction accomplish?
For string instructions, DI always addresses data in the ____________ segment.
What is the purpose of the D flag bit?
Explain what the REPE prefix does when coupled with the SCASB instruction.
What condition or conditions will terminate the repeated string instruction REPNE SCASB?
Describe what the CMPSB instruction accomplishes.
Develop a sequence of instructions that scans through a 300H-byte section of memory called
LIST, located in the data segment, searching for a 66H.
What happens if AH = 02H and DL = 43H when the INT 21H instruction is executed?

CHAPTER 6
Program Control Instructions

INTRODUCTION
The program control instructions direct the flow of a program and allow the flow to change. A
change in flow often occurs after a decision made with the CMP or TEST instruction is followed by a conditional jump instruction. This chapter explains the program control instructions,
including the jumps, calls, returns, interrupts, and machine control instructions.
This chapter also presents the relational assembly language statements (.IF, .ELSE,
.ELSEIF, .ENDIF, .WHILE, .ENDW, .REPEAT, and .UNTIL) that are available in version 6.xx
and above of MASM or TASM, with version 5.xx set for MASM compatibility. These relational assembly language commands allow the programmer to develop control flow portions of
the program with C/C++ language efficiency.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Use both conditional and unconditional jump instructions to control the flow of a program.
2. Use the relational assembly language statements .IF, .REPEAT, .WHILE, and so forth in
programs.
3. Use the call and return instructions to include procedures in the program structure.
4. Explain the operation of the interrupts and interrupt control instructions.
5. Use machine control instructions to modify the flag bits.
6. Use ENTER and LEAVE to enter and leave programming structures.

6–1

THE JUMP GROUP
The main program control instruction, jump (JMP), allows the programmer to skip sections of a
program and branch to any part of the memory for the next instruction. A conditional jump
instruction allows the programmer to make decisions based upon numerical tests. The results of
numerical tests are held in the flag bits, which are then tested by conditional jump instructions.
Another instruction similar to the conditional jump, the conditional set, is explained with the
conditional jump instructions in this section.

192

193

PROGRAM CONTROL INSTRUCTIONS

In this section of the text, all jump instructions are illustrated with their uses in sample programs. Also revisited are the LOOP and conditional LOOP instructions, first presented in
Chapter 3, because they are also forms of the jump instruction.

Unconditional Jump (JMP)
Three types of unconditional jump instructions (see Figure 6–1) are available to the microprocessor: short jump, near jump, and far jump. The short jump is a 2-byte instruction that
allows jumps or branches to memory locations within +127 and –128 bytes from the address following the jump. The 3-byte near jump allows a branch or jump within ±32K bytes (or anywhere in the current code segment) from the instruction in the current code segment. Remember
that segments are cyclic in nature, which means that one location above offset address FFFFH is
offset address 0000H. For this reason, if you jump 2 bytes ahead in memory and the instruction
pointer addresses offset address FFFFH, the flow continues at offset address 0001H. Thus, a displacement of ±32K bytes allows a jump to any location within the current code segment. Finally,
the 5-byte far jump allows a jump to any memory location within the real memory system. The
short and near jumps are often called intrasegment jumps, and the far jumps are often called
intersegment jumps.
In the 80386 through the Core2 processors, the near jump is within ±2G if the machine is
operated in the protected mode, with a code segment that is 4G bytes long. If operated in the real
mode, the near jump is within ±32K bytes. In the protected mode, the 80386 and above use a 32bit displacement that is not shown in Figure 6–1. If the Pentium 4 is operated in the 64-bit mode,
a jump can be to any address in its 1T memory space.

Short Jump. Short jumps are called relative jumps because they can be moved, along with
their related software, to any location in the current code segment without a change. This is
because the jump address is not stored with the opcode. Instead of a jump address, a distance, or
displacement, follows the opcode. The short jump displacement is a distance represented by a
1-byte signed number whose value ranges between +127 and -128. The short jump instruction
appears in Figure 6–2. When the microprocessor executes a short jump, the displacement is signextended and added to the instruction pointer (IP/EIP) to generate the jump address within the
current code segment. The short jump instruction branches to this new address for the next
instruction in the program.
Example 6–1 shows how short jump instructions pass control from one part of the program
to another. It also illustrates the use of a label (a symbolic name for a memory address) with the
jump instruction. Notice how one jump (JMP SHORT NEXT) uses the SHORT directive to force
FIGURE 6–1 The three
main forms of the JMP
instruction. Note that Disp is
either an 8- or 16-bit signed
displacement or distance.

Opcode
(a)

E B

Disp

Short

Opcode
(b)

E 9

Disp
Low

Disp
High

IP
Low

IP
High

Near

Opcode
(c)

E A

CS
Low

CS
High

Far

194

CHAPTER 6

FIGURE 6–2 A short jump
to four memory locations
beyond the address of the
next instruction.

Memory

1000A
10009
10008
10007
10006

(Jump to here)

10005
10004
10003
10002
10001

04

10000

JMP

CS = 1000H
IP = 0002H
New IP = IP + 4
New IP = 0006H

a short jump, while the other does not. Most assembler programs choose the best form of the
jump instruction so the second jump instruction (JMP START) also assembles as a short jump. If
the address of the next instruction (0009H) is added to the sign-extended displacement (0017H)
of the first jump, the address of NEXT is at location 0017H + 0009H or 0020H.
EXAMPLE 6–1
0000
0002
0005
0007

33
B8
03
EB

DB
0001
C3
17

XOR
START: MOV
ADD
JMP

BX,BX
AX,1
AX,BX
SHORT NEXT



0020 8B D8
0022 EB DE

NEXT:

MOV
JMP

BX,AX
START

Whenever a jump instruction references an address, a label normally identifies the address.
The JMP NEXT instruction is an example; it jumps to label NEXT for the next instruction. It is
very rare to use an actual hexadecimal address with any jump instruction, but the assembler supports addressing in relation to the instruction pointer by using the $+a displacement. For example, the JMP $+2 instruction jumps over the next two memory locations (bytes) following the
JMP instruction. The label NEXT must be followed by a colon (NEXT:) to allow an instruction
to reference it for a jump. If a colon does not follow a label, you cannot jump to it. Note that the
only time a colon is used after a label is when the label is used with a jump or call instruction.
This is also true in Visual C++.

Near Jump. The near jump is similar to the short jump, except that the distance is farther. A
near jump passes control to an instruction in the current code segment located within ±32K bytes
from the near jump instruction. The distance is ±2G in the 80386 and above when operated in
protected mode. The near jump is a 3-byte instruction that contains an opcode followed by a
signed 16-bit displacement. In the 80386 through the Pentium 4 processors, the displacement is
32 bits and the near jump is 5 bytes long. The signed displacement adds to the instruction pointer
(IP) to generate the jump address. Because the signed displacement is in the range of ±32K, a

195

PROGRAM CONTROL INSTRUCTIONS

FIGURE 6–3 A near jump
that adds the displacement
(0002H) to the contents of IP.

Memory

1000A
10009
10008
10007
10006
10005

(Jump to here)

10004

CS = 1000H
IP = 0002H
New IP = 0005H

10003
10002

00

10001

02

10000

JMP

Near jump

near jump can jump to any memory location within the current real mode code segment. The protected mode code segment in the 80386 and above can be 4G bytes long, so the 32-bit displacement allows a near jump to any location within ±2G bytes. Figure 6–3 illustrates the operation of
the real mode near jump instruction.
The near jump is also relocatable (as was the short jump) because it is also a relative jump.
If the code segment moves to a new location in the memory, the distance between the jump
instruction and the operand address remains the same. This allows a code segment to be relocated by simply moving it. This feature, along with the relocatable data segments, makes the
Intel family of microprocessors ideal for use in a general-purpose computer system. Software
can be written and loaded anywhere in the memory and function without modification because of
the relative jumps and relocatable data segments.
Example 6–2 shows the same basic program that appeared in Example 6–1, except that the
jump distance is greater. The first jump (JMP NEXT) passes control to the instruction at offset
memory location 0200H within the code segment. Notice that the instruction assembles as E9
0200 R. The letter R denotes a relocatable jump address of 0200H. The relocatable address of
0200H is for the assembler program’s internal use only. The actual machine language instruction
assembles as E9 F6 01, which does not appear in the assembler listing. The actual displacement
is 01F6H for this jump instruction. The assembler lists the jump address as 0200 R, so the
address is easier to interpret as software is developed. If the linked execution file (.EXE) or command file (.COM) is displayed in hexadecimal code, the jump instruction appears as E9 F6 01.
EXAMPLE 6–2
0000
0002
0005
0007

33DB
B8 0001
03 C3
E9 0200 R

XOR
START: MOV
ADD
JMP

BX,BX
AX,1
AX,BX
NEXT



0200 8B D8
0202 E9 0002 R

NEXT:

MOV
JMP

BX,AX
START

Far Jump. A far jump instruction (see Figure 6–4) obtains a new segment and offset address to
accomplish the jump. Bytes 2 and 3 of this 5-byte instruction contain the new offset address; bytes

196

CHAPTER 6

FIGURE 6–4 A far jump
instruction replaces the contents of both CS and IP with 4
bytes following the opcode.

Memory

A3129
A3128
A3127

(Jump to here)

A3126

10004

A3

10003

00

10002

01

10001

27

10000

JMP

Far jump

4 and 5 contain the new segment address. If the microprocessor (80286 through the Core2) is
operated in the protected mode, the segment address accesses a descriptor that contains the base
address of the far jump segment. The offset address, which is either 16 or 32 bits, contains the offset address within the new code segment.
Example 6–3 lists a short program that uses a far jump instruction. The far jump instruction sometimes appears with the FAR PTR directive, as illustrated. Another way to obtain a far
jump is to define a label as a far label. A label is far only if it is external to the current code
segment or procedure. The JMP UP instruction in the example references a far label. The label
UP is defined as a far label by the EXTRN UP:FAR directive. External labels appear in programs that contain more than one program file. Another way of defining a label as global is to
use a double colon (LABEL::) following the label in place of the single colon. This is required
inside procedure blocks that are defined as near if the label is accessed from outside the procedure block.
When the program files are joined, the linker inserts the address for the UP label into the
JMP UP instruction. It also inserts the segment address in the JMP START instruction. The segment address in JMP FAR PTR START is listed as - - - - R for relocatable; the segment address
in JMP UP is listed as - - - - E for external. In both cases, the - - - - is filled in by the linker when
it links or joins the program files.
EXAMPLE 6–3
EXTRN
0000 33 DB
0002 B8 0001
0005 E9 0200 R

UP:FAR

XOR BX,BX
START: ADD AX,1
JMP NEXT
;

0200 8B D8
NEXT:
0202 EA 0002 —— R

MOV BX,AX
JMP FAR PTR START

0207 EA 0000 —— R

JMP UP

Jumps with Register Operands. The jump instruction can also use a 16- or 32-bit register as
an operand. This automatically sets up the instruction as an indirect jump. The address of the
jump is in the register specified by the jump instruction. Unlike the displacement associated

PROGRAM CONTROL INSTRUCTIONS

197

with the near jump, the contents of the register are transferred directly into the instruction
pointer. An indirect jump does not add to the instruction pointer, as with short and near jumps.
The JMP AX instruction, for example, copies the contents of the AX register into the IP when
the jump occurs. This allows a jump to any location within the current code segment. In the
80386 and above, a JMP EAX instruction also jumps to any location within the current code
segment; the difference is that in protected mode the code segment can be 4G bytes long, so a
32-bit offset address is needed.
Example 6–4 shows how the JMP AX instruction accesses a jump table in the code segment. This DOS program reads a key from the keyboard and then modifies the ASCII code to
00H in AL for a ‘1’, 01H for a ‘2’, and 02H for a ‘3’. If a ‘1’, ‘2’, or ‘3’ is typed, AH is
cleared to 00H. Because the jump table contains 16-bit offset addresses, the contents of AX
are doubled to 0, 2, or 4, so a 16-bit entry in the table can be accessed. Next, the offset
address of the start of the jump table is loaded to SI, and AX is added to form the reference to
the jump address. The MOV AX,[SI] instruction then fetches an address from the jump table,
so the JMP AX instruction jumps to the addresses (ONE, TWO, or THREE) stored in the
jump table.
EXAMPLE 6–4

0000
0000 0030 R
0002 0034 R
0004 0038 R
0000
0017
0019
001B
001D
001F
0021
0023
0025
0027
002A
002C
002E
0030
0032
0034
0036
0038
003A
003C

B4
CD
2C
72
32
77
B4
03
BE
03
8B
FF
B2
EB
B2
EB
B2
B4
CD

01
21
31
F9
02
F4
00
C0
0000 R
F0
04
E0
31
06
32
02
33
02
21

;Instructions that read 1, 2, or 3 from the keyboard.
;The number is displayed as 1, 2, or 3 using a jump table
;
.MODEL SMALL
;select SMALL model
.DATA
;start data segment
TABLE: DW ONE
;jump table
DW TWO
DW THREE
.CODE
;start code segment
.STARTUP
;start program
TOP:
MOV AH,1
;read key into AL
INT 21H
SUB AL,31
;convert to BCD
JB TOP
;if key < 1
CMP AL,2
JA TOP
;if key > 3
MOV AH,0
;double key code
ADD AX,AX
MOV SI,OFFSET TABLE
;address TABLE
ADD SI,AX
;form lookup address
MOV AX,[SI]
;get ONE, TWO or THREE
JMP AX
;jump to ONE, TWO or THREE
ONE:
MOV DL,’1’
;get ASCII 1
JMP BOT
TWO:
MOV DL,’2’
;get ASCII 2
JMP BOT
THREE: MOV DL,’3’
;get ASCII 3
BOT:
MOV AH,2
;display number
INT 21H
.EXIT
END

Indirect Jumps Using an Index. The jump instruction may also use the [ ] form of addressing
to directly access the jump table. The jump table can contain offset addresses for near indirect
jumps, or segment and offset addresses for far indirect jumps. (This type of jump is also known
as a double-indirect jump if the register jump is called an indirect jump.) The assembler assumes
that the jump is near unless the FAR PTR directive indicates a far jump instruction. Here
Example 6–5 repeats Example 6–4 by using the JMP TABLE [SI] instead of JMP AX. This
reduces the length of the program.

198

CHAPTER 6

EXAMPLE 6–5
;Instructions that read 1, 2, or 3 from the keyboard.
;The number is displayed as 1, 2, or 3 using a jump table
;
.MODEL SMALL
;select SMALL model
0000
.DATA
;start data segment
0000 002D R
TABLE: DW
ONE
;jump table
0002 0031 R
DW
TWO
0004 0035 R
DW
THREE
0000
.CODE
;start code segment
.STARTUP
;start program
0017 B4 01
TOP:
MOV
AH,1
;read key into AL
0019 CD 21
INT
21H
001B 2C 31
SUB
AL,31
;convert to BCD
001D 72 F9
JB
TOP
;if key < 1
001F 32 02
CMP
AL,2
0021 77 F4
JA
TOP
;if key > 3
0023 B4 00
MOV
AH,0
;double key code
0025 03 C0
ADD
AX,AX
0027 B5 F0
MOV
SI,AX
;form lookup address
0029 FF A4 0000 R
JMP
TABLE[SI] ;jump to ONE, TWO or THREE
002D B2 31
ONE:
MOV
DL,’1’
;get ASCII 1
002F EB 06
JMP
BOT
0031 B2 32
TWO:
MOV
DL,’2’
;get ASCII 2
0033 EB 02
JMP
BOT
0035 B2 33
THREE: MOV
DL,’3’
;get ASCII 3
0037 B4 02
BOT:
MOV
AH,2
;display number
0039 CD 21
INT
21H
.EXIT
END

The mechanism used to access the jump table is identical with a normal memory reference.
The JMP TABLE [SI] instruction points to a jump address stored at the code segment offset location addressed by SI. It jumps to the address stored in the memory at this location. Both the
register and indirect indexed jump instructions usually address a 16-bit offset. This means that
both types of jumps are near jumps. If a JMP FAR PTR [SI] or JMP TABLE [SI], with TABLE
data defined with the DD directive appears in a program, the microprocessor assumes that the
jump table contains doubleword, 32-bit addresses (IP and CS).

Conditional Jumps and Conditional Sets
Conditional jump instructions are always short jumps in the 8086 through the 80286 microprocessors. This limits the range of the jump to within +127 bytes and -128 bytes from the location following the conditional jump. In the 80386 and above, conditional jumps are either short
or near jumps (±32K). In the 64-bit mode of the Pentium 4, the near jump distance is ±2G for the
conditional jumps. This allows these microprocessors to use a conditional jump to any location
within the current code segment. Table 6–1 lists all the conditional jump instructions with their
test conditions. Note that the Microsoft MASM version 6.x assembler automatically adjusts conditional jumps if the distance is too great.
The conditional jump instructions test the following flag bits: sign (S), zero (Z), carry (C),
parity (P), and overflow (0). If the condition under test is true, a branch to the label associated
with the jump instruction occurs. If the condition is false, the next sequential step in the program
executes. For example, a JC will jump if the carry bit is set.
The operation of most conditional jump instructions is straightforward because they often
test just one flag bit, although some test more than one. Relative magnitude comparisons require
more complicated conditional jump instructions that test more than one flag bit.

199

PROGRAM CONTROL INSTRUCTIONS

TABLE 6–1

Conditional jump instructions.

Assembly Language

Tested Condition

Operation

JA

Z = 0 and C = 0

Jump if above

JAE

C=0

Jump if above or equal

JB

C=1

Jump if below

JBE

Z = 1 or C = 1

Jump if below or equal

JC

C=1

Jump if carry

JE or JZ

Z=1

Jump if equal or jump if zero

JG

Z = 0 and S = 0

Jump if greater than

JGE

S=0

Jump if greater than or equal

JL

S != O

Jump if less than

JLE

Z = 1 or S != O

Jump if less than or equal

JNC

C=0

Jump if no carry

JNE or JNZ

Z=0

Jump if not equal or jump if not zero

JNO

O=0

Jump if no overflow

JNS

S=0

Jump if no sign (positive)

JNP or JPO

P=0

Jump if no parity or jump if parity odd

JO

O=1

Jump if overflow

JP or JPE

P=1

Jump if parity or jump if parity even

JS

S=1

Jump if sign (negative)

JCXZ

CX = 0

Jump if CX is zero

JECXZ

ECX = 0

Jump if ECX equals zero

JRCXZ

RCX = 0

Jump if RCX equals zero (64-bit mode)

Because both signed and unsigned numbers are used in programming, and because
the order of these numbers is different, there are two sets of conditional jump instructions
for magnitude comparisons. Figure 6–5 shows the order of both signed and unsigned 8-bit
FIGURE 6–5 Signed and
unsigned numbers follow
different orders.

255
254

Unsigned numbers
FFH
FEH

132
131
130
129
128

84H
83H
82H
81H
80H

+2
+1
+0
−1
−2

02H
01H
00H
FFH
FEH

4
3
2
1
0

04H
03H
02H
01H
00H

–124
–125
–126
−127
−128

84H
83H
82H
81H
80H

+127
+126

Signed numbers
7FH
7EH

200

CHAPTER 6

numbers. The 16- and 32-bit numbers follow the same order as the 8-bit numbers, except that
they are larger. Notice that an FFH (255) is above the 00H in the set of unsigned numbers, but
an FFH (-1) is less than 00H for signed numbers. Therefore, an unsigned FFH is above 00H,
but a signed FFH is less than 00H.
When signed numbers are compared, use the JG, JL, JGE, JLE, JE, and JNE instructions.
The terms greater than and less than refer to signed numbers. When unsigned numbers are compared, use the JA, JB, JAB, JBE, JE, and JNE instructions. The terms above and below refer to
unsigned numbers.
The remaining conditional jumps test individual flag bits, such as overflow and parity.
Notice that JE has an alternative opcode JZ. All instructions have alternates, but many aren’t
used in programming because they don’t usually fit the condition under test. (The alternates
appear in Appendix B with the instruction set listing.) For example, the JA instruction (jump if
above) has the alternative JNBE (jump if not below or equal). A JA functions exactly as a JNBE,
but a JNBE is awkward in many cases when compared to a JA.
The conditional jump instructions all test flag bits except for JCXZ (jump if CX = 0)
and JECXZ (jump if ECX = 0). Instead of testing flag bits, JCXZ directly tests the contents
of the CX register without affecting the flag bits, and JECXZ tests the contents of the ECX
register. For the JCXZ instruction, if CX = 0, a jump occurs, and if CX != 0, no jump occurs.
Likewise for the JECXZ instruction, if ECX = 0, a jump occurs; if ECX != 0, no jump occurs.
In the Pentium 4 or Core2 operated in the 64-bit mode, the JRCXZ instruction jumps is
RCX = 0.
A program that uses JCXZ appears in Example 6–6. Here, the SCASB instruction searches
a table for 0AH. Following the search, a JCXZ instruction tests CX to see if the count has
reached zero. If the count is zero, the 0AH is not found in the table. The carry flag is used in this
example to pass the not found condition back to the calling program. Another method used to
test to see if the data are found is the JNE instruction. If JNE replaces JCXZ, it performs the
same function. After the SCASB instruction executes, the flags indicate a not-equal condition if
the data were not found in the table.
EXAMPLE 6–6

0017
001A
001C
001D
001F
0020
0022

B9 0064
B0 0A
FC
F2/AE
F9
E3 01

;Instructions that search a table of 100H bytes for 0AH
;The offset address of TABLE is assumed to be in SI
;
MOV CX,100
;load counter
MOV AL,0AH
;load AL with 0AH
CLD
;auto-increment
REPNE SCASB
;search for 0AH
STC
;set carry if found
JCXZ NOT_FOUND
;if not found
NOT_FOUND

The Conditional Set Instructions. In addition to the conditional jump instructions, the
80386 through the Core2 processors also contain conditional set instructions. The conditions
tested by conditional jumps are put to work with the conditional set instructions. The conditional set instructions set a byte to either 01H or clear a byte to 00H, depending on the outcome of the condition under test. Table 6–2 lists the available forms of the conditional set
instructions.
These instructions are useful where a condition must be tested at a point much later in the
program. For example, a byte can be set to indicate that the carry is cleared at some point in the
program by using the SETNC MEM instruction. This instruction places 01H into memory location MEM if carry is cleared, and 00H into MEM if carry is set. The contents of MEM can be

201

PROGRAM CONTROL INSTRUCTIONS

TABLE 6–2

Conditional set instructions.

Assembly Language

Tested Condition

Operation

SETA

Z = 0 and C = 0

Set if above

SETAE

C=0

Set if above or equal

SETB

C=1

Set if below

SETBE

Z = 1 or C = 1

Set if below or equal

SETC

C=1

Set if carry

SETE or SETZ

Z=1

Set if equal or set if zero

SETG

Z = 0 and S = 0

Set if greater than

SETGE

S=0

Set if greater than or equal

SETL

S != O

Set if less than

SETLE

Z = 1 or S != O

Set if less than or equal

SETNC

C=0

Set if no carry

SETNE or SETNZ

Z=0

Set if not equal or set if not zero

SETNO

O=0

Set if no overflow

SETNS

S=0

Set if no sign (positive)

SETNP or SETPO

P=0

Set if no parity or set if parity odd

SETO

O=1

Set if overflow

SETP or SETPE

P=1

Set if parity or set if parity even

SETS

S=1

Set if sign (negative)

tested at a later point in the program to determine if carry is cleared at the point where the
SETNC MEM instruction executed.

LOOP
The LOOP instruction is a combination of a decrement CX and the JNZ conditional jump. In the
8086 through the 80286 processors, LOOP decrements CX; if CX != 0, it jumps to the address
indicated by the label. If CX becomes 0, the next sequential instruction executes. In the 80386
and above, LOOP decrements either CX or ECX, depending upon the instruction mode. If the
80386 and above operate in the l6-bit instruction mode, LOOP uses CX; if operated in the 32-bit
instruction mode, LOOP uses ECX. This default is changed by the LOOPW (using CX) and
LOOPD (using ECX) instructions in the 80386 through the Core2. In the 64-bit mode, the loop
counter is in RCX and is 64 bits wide.
Example 6–7 shows how data in one block of memory (BLOCK1) add to data in a second block of memory (BLOCK2), using LOOP to control how many numbers add. The
LODSW and STOSW instructions access the data in BLOCK1 and BLOCK2. The ADD AX,
ES:[DI] instruction accesses the data in BLOCK2 located in the extra segment. The only reason that BLOCK2 is in the extra segment is that DI addresses extra segment data for the
STOSW instruction. The .STARTUP directive only loads DS with the address of the data segment. In this example, the extra segment also addresses data in the data segment, so the contents of DS are copied to ES through the accumulator. Unfortunately, there is no direct move
from segment register to segment register instruction.

202

CHAPTER 6

EXAMPLE 6–7

0000
0000 0064[
0000
]
00C8 0064[
0000
]
0000
0017
0019
001B
001C
001F
0022
0025
0026
0029
002A

8C D8
8E C0
FC
B9 0064
BE 0000 R
BF 00C8 R
AD
26:03 05
AB
E2 F9

;A program that sums the contents of BLOCK1 and BLOCK2
;and stores the results on top of the data in BLOCK2.
;
.MODEL SMALL
;select SMALL model
.DATA
;start data segment
BLOCK1 DW 100 DUP(?)
;100 words for BLOCK1

BLOCK2 DW 100 DUP(?)

;100 words for BLOCK2

.CODE
.STARTUP
MOV AX,DS
MOV ES,AX
CLD
MOV CX,100
MOV SI,OFFSET BLOCK1
MOV DI,OFFSET BLOCK2
L1:
LODSW
ADD AX,ES:[DI]
STOSW
LOOP L1
.EXIT
END

;start code segment
;start program
;overlap DS and ES
;select auto-increment
;load counter
;address BLOCK1
;address BLOCK2
;load AX with BLOCK1
;add BLOCK2
;save answer
;repeat 100 times

Conditional LOOPs. As with REP, the LOOP instruction also has conditional forms: LOOPE and
LOOPNE. The LOOPE (loop while equal) instruction jumps if CX != 0 while an equal condition
exists. It will exit the loop if the condition is not equal or if the CX register decrements to 0. The
LOOPNE (loop while not equal) instruction jumps if CX != 0 while a not-equal condition exists.
It will exit the loop if the condition is equal or if the CX register decrements to 0. In the 80386
through the Core2 processors, the conditional LOOP instruction can use either CX or ECX as the
counter. The LOOPEW/LOOPED or LOOPNEW/LOOPNED instructions override the instruction
mode if needed. Under 64-bit operation, the loop counter uses RCX and is 64 bits in width.
As with the conditional repeat instructions, alternates exist for LOOPE and LOOPNE. The
LOOPE instruction is the same as LOOPZ, and the LOOPNE instruction is the same as
LOOPNZ. In most programs, only the LOOPE and LOOPNE apply.

6–2

CONTROLLING THE FLOW OF THE PROGRAM
It is much easier to use the assembly language statements .IF, .ELSE, .ELSEIF, and .ENDIF to
control the flow of the program than it is to use the correct conditional jump statement. These
statements always indicate a special assembly language command to MASM. Note that the control flow assembly language statements beginning with a period are only available to MASM
version 6.xx, and not to earlier versions of the assembler such as 5.10. Other statements developed in this chapter include the .REPEAT–.UNTIL and .WHILE–.ENDW statements. These
statements (the dot commands) do not function when using the Visual C++ inline assembler.
Example 6–8(a) shows how these statements are used to control the flow of a program by
testing AL for the ASCII letters A through F. If the contents of AL are A through F, 7 is subtracted from AL.
Accomplishing the same task using the Visual C++ inline assembler is usually handled in
C++ rather than in assembly language. Example 6–8(b) shows the same task using the inline
assembler in Visual C++ and conditional jumps in assembly language. It also shows how to use

203

PROGRAM CONTROL INSTRUCTIONS

a label in an assembly block in Visual C++. This illustrates that it is more difficult to accomplish
the same task without the dot commands. Never use uppercase for assembly language commands
with the inline assembler because some of them are reserved by C++ and will cause problems.
EXAMPLE 6–8 (a)
.IF AL >= ‘A’ && AL <= ‘F’
SUB AL,7
.ENDIF
SUB AL,30H

EXAMPLE 6–8 (b)
char temp;
_asm{
mov
cmp
jb
cmp
ja
sub
Later:
sub
mov

al,temp
al,41h
Later
al,46h
Later
al,7
al,30h
temp,al

}

In Example 6–8(a) notice how the && symbol represents the AND function in the .IF
statement. There is no .if in Example 6–8(b) because the same operation was performed by using
a few compare (CMP) instructions to accomplish the same task. See Table 6–3 for a complete list
of relational operators used with the .IF statement. Note that many of these conditions (such as
&&) are also used by many high-level languages such as C/C++.
Example 6–9 shows another example of the conditional .IF directive that converts all
ASCII-coded letters to uppercase. First, the keyboard is read without echo using DOS INT 21H
function 06H, and then the .IF statement converts the character into uppercase, if needed. In this
example, the logical AND function (&&) is used to determine if the character is in lowercase. If
it is lowercase, 20H is subtracted, converting to uppercase. This program reads a key from the

TABLE 6–3 Relational
operators used with the
.IF statement in assembly
language.

Operator

Function

==

Equal or the same as

!=

Not equal

>

Greater than

>=

Greater than or equal

<

Less than

<=

Less than or equal

&

Bit test

!

Logical inversion

&&

Logical AND

||

Logical OR

|

Or

204

CHAPTER 6

keyboard and converts it to uppercase before displaying it. Notice also how the program terminates
when the control C key (ASCII = 03H) is typed. The .LISTALL directive causes all assemblergenerated statements to be listed, including the label @Startup generated by the .STARTUP
directive. The .EXIT directive also is expanded by .LISTALL to show the use of the DOS INT
21H function 4CH, which returns control to DOS.
EXAMPLE 6–9

0000
0100
0100
0102
0104
0106
0108
010A

B4
B2
CD
74
3C
74

06
FF
21
F8
03
10

;A DOS program that reads the keyboard and converts all
;lowercase data to uppercase before displaying it.
;
;This program is terminated with a control-C
;
.MODEL TINY
;select tiny model
.LISTALL
;list all statements
.CODE
;start code segment
.STARTUP
;start program
* @Startup
MAIN1: MOV AH,6
;read key without echo
MOV DL,0FFH
INT 21H
JE
MAIN1
;if no key
CMP AL,3
;test for control-C
JE
MAIN2
;if control-C
.IF AL >= ‘a’ && AL <= ‘z’

010C
010E
0110
0112

3C
72
3C
77

61
06
7A
02

*
*
*
*

cmp
jb
cmp
ja

0114 2C 20

al,’a’
@C0001
al,’z’
@C0001

SUB AL,20H
.ENDIF

0116
0116 8A D0
0118 CD 21
011A EB E4
011C

* @C0001:

011C B4 4C
011E CD 21

*
*

MOV
INT
JMP

DL,AL
21H
MAIN1

;echo character to display
;repeat

MAIN2:
.EXIT
MOV AH,4CH
INT 21H
END

In this program, a lowercase letter is converted to uppercase by the use of the .IF AL >= ‘a’ &&
AL <= ‘z’ statement. If AL contains a value that is greater than or equal to a lowercase a, and less than
or equal to a lowercase z (a value of a through z), the statement between the .IF and .ENDIF executes.
This statement (SUB AL,20H) subtracts 20H from the lowercase letter to change it to an uppercase
letter. Notice how the assembler program implements the .IF statement (see lines that begin with *).
The label @C0001 is an assembler-generated label used by the conditional jump statements placed in
the program by the .IF statement.
Another example that uses the conditional .IF statement appears in Example 6–10. This
program reads a key from the keyboard, and then converts it to hexadecimal code. This program
is not listed in expanded form.
In this example, the .IF AL >=‘a’ && AL<=‘f’ statement causes the next instruction
(SUB AL,57H) to execute if AL contains letters a through f, converting them to hexadecimal.
If it is not between letters a and f, the next .ELSEIF statement tests it for the letters A through
F. If it is the letters A through F, 37H is subtracted from AL. If neither condition is true, 30H

PROGRAM CONTROL INSTRUCTIONS

205

is subtracted from AL before AL is stored at data segment memory location TEMP. The same
conversion can be performed in a C++ function as illustrated in the program snippet of
Example 6–10(b).
EXAMPLE 6–10(a)

0000
0000 00
0000
0017 B4 01
0019 CD 21
0023 2C 57
002F 2C 37
0033 2C 30
0035 A2 0000 R

;A DOS program that reads key and stores its hexadecimal
;value in memory location TEMP
;
.MODEL SMALL
;select small model
.DATA
;start data segment
TEMP
DB
?
;define TEMP
.CODE
;start code segment
.STARTUP
;start program
MOV AH,1
;read keyboard
INT 21H
.IF AL >= ‘a’ && AL <= ‘f’
SUB AL,57H
;if lowercase
.ELSEIF .IF AL >= ‘A’ && AL <= ‘F’
SUB AL,37H
;if uppercase
.ELSE
SUB AL,30H
; otherwise
.ENDIF
MOV TEMP,AL
;save it in TEMP
.EXIT
END

EXAMPLE 6–10(b)
char Convert(char temp)
{
if ( temp >= ‘a’ && temp <= ‘f’ )
temp -= 0x57;
else if ( temp >= ‘A’ && temp <= ‘F’ )
temp -= 0x37;
else
temp -= 0x30;
return temp;
}

WHILE Loops
As with most high-level languages, the assembler also provides the WHILE loop construct,
available to MASM version 6.x. The .WHILE statement is used with a condition to begin the
loop, and the .ENDW statement ends the loop.
Example 6–11 shows how the .WHILE statement is used to read data from the keyboard
and store it into an array called BOP until the enter key (0DH) is typed. This program assumes
that BUF is stored in the extra segment because the STOSB instruction is used to store the keyboard data in memory. Note that the .WHILE loop portion of the program is shown in expanded
form so that the statements inserted by the assembler (beginning with a *) can be studied. After
the Enter key (0DH) is typed, the string is appended with a $ so it can be displayed with DOS
INT 21H function number 9.
EXAMPLE 6–11

0000
0000 0D 0A

;A DOS program that reads a character string from the
;keyboard and then displays it again.
;
.MODEL SMALL
;select small model
.DATA
;start data segment
MES
DB 13,10
;return and line feed

206

CHAPTER 6
0002 0100[

BUF

DB 256 DUP(?)

;character string buffer

00
]
0000
0017
0019
001B
001C

8C D8
8C C0
FC
BF 0002 R

001F
0021
0021
0023
0025

EB 05

0026
0026
0028
002A
002E
0031
0033

B4
CD
AA

3C
75
C6
BA
B4
CD

.CODE
.STARTUP
MOV
MOV
CLD
MOV

AX,DX
ES,AX
DI,OFFSET BUF

.WHILE AL != 0DH
*
jmp @C0001
* @C0002:
01
MOV AH,1
21
INT 21H
STOSB
.ENDW
* @C0001:
0D
*
cmp al,0dh
F7
*
jne @C0002
45 FF 24
MOV BYTE PTR[DI–1]’&’
0000 R
MOV DX,OFFSET MES
09
MOV AH,9
21
INT 21H
.EXIT
END

;start code segment
;start program
;overlap DS with ES
;select auto-increment
;address buffer
;loop while not enter

;read key
;store key code

;display MES

The program in Example 6–11 functions perfectly, as long as we arrive at the .WHILE
statement with AL containing some other value except 0DH. This can be corrected by adding
a MOV AL,0DH instruction before the .WHILE statement in Example 6–11. Although not
shown in an example, the .BREAK and .CONTINUE statements are available for use with the
while loop. The .BREAK statement is often followed by the .IF statement to select the break
condition as in .BREAK .IF AL == 0DH. The .CONTINUE statement, which can be used to
allow the DO–.WHILE loop to continue if a certain condition is met, can be used with
.BREAK. For example, .CONTINUE .IF AL == 15 allows the loop to continue if AL equals
15. Note that the .BREAK and .CONTINUE commands function in the same manner in a C++
program.

REPEAT-UNTIL Loops
Also available to the assembler is the REPEAT–UNTIL construct. A series of instructions is
repeated until some condition occurs. The .REPEAT statement defines the start of the loop; the
end is defined with the .UNTIL statement, which contains a condition. Note that .REPEAT and
.UNTIL are available to version 6.x of MASM.
If Example 6–11 is again reworked by using the REPEAT-UNTIL construct, this appears
to be the best solution. See Example 6–12 for the program that reads keys from the keyboard
and stores keyboard data into extra segment array BUF until the enter key is pressed. This program also fills the buffer with keyboard data until the Enter key (0DH) is typed. Once the
Enter key is typed, the program displays the character string using DOS INT 2lH function
number 9, after appending the buffer data with the required dollar sign. Notice how the
.UNTIL AL == 0DH statement generates code (statements beginning with *) to test for the
Enter key.
EXAMPLE 6–12
;A DOS program that reads a character string from the
;keyboard and then displays it again.
;

207

PROGRAM CONTROL INSTRUCTIONS

0000
0000 0D 0A
0002 0100[
00
]
0000
0017
0019
001B
001C

8C D8
8C C0
FC
BF 0002 R

.MODEL SMALL
.DATA
MES
DB 13,10
BUF
DB 256 DUP(?)

;select small model
;start data segment
;return and line feed
;character string buffer

.CODE
.STARTUP
MOV
MOV
CLD
MOV

;start code segment
;start program
;overlap DS with ES

AX,DX
ES,AX
DI,OFFSET BUF

.REPEAT
001F
001F B4 01
0021 CD 21
0023 AA

*

@C0001:
MOV AH,1
INT 21H
STOSB

;select auto-increment
;address buffer
;repeat until enter

;read key
;store key code

.UNTIL AL == 0DH
0025
0027
0028
002C
002E
0031

3C
75
C6
BA
B4
CD

0D
*
F7
*
45 FF 24
0000 R
09
21

cmp al,0dh
jne @C0001
MOV BYTE PTR[DI–1]’&’
MOV DX,OFFSET MES
MOV AH,9
INT 21H
;display MES
.EXIT
END

There is also an .UNTILCXZ instruction available that uses the LOOP instruction to
check CX for a repeat loop. The .UNTILCXZ instruction uses the CX register as a counter to
repeat a loop a fixed number of times. Example 6–13 shows a sequence of instructions that
uses the .UNTILCXZ instruction used to add the contents of byte-sized array ONE to bytesized array TWO. The sums are stored in array THREE. Note that each array contains 100
bytes of data, so the loop is repeated 100 times. This example assumes that array THREE is in
the extra segment, and that arrays ONE and TWO are in the data segment. Notice how the
LOOP instruction is inserted for the .UNTILCXZ.
EXAMPLE 6–13
012C
012F
0132
0135

B9
BF
BE
BB

0064
00C8 R
0000 R
0064 R

MOV
MOV
MOV
MOV

CX,100
DI,OFFSET THREE
SI,OFFSET ONE
BX,OFFSET TWO

.REPEAT
0138
0138
0139
013B
013C

*
AC
02 07
AA
43

@C0001:
LODSB
ADD AL,[BX]
STOSB
INC BX
.UNTILCXZ

013D E2 F9

*

LOOP @C0001

;set count
;address arrays

208

CHAPTER 6

6–3

PROCEDURES
The procedure (subroutine, method, or function) is an important part of any computer system’s
architecture. A procedure is a group of instructions that usually performs one task. A procedure
is a reusable section of the software that is stored in memory once, but used as often as necessary.
This saves memory space and makes it easier to develop software. The only disadvantage of a
procedure is that it takes the computer a small amount of time to link to the procedure and return
from it. The CALL instruction links to the procedure, and the RET (return) instruction returns
from the procedure.
The stack stores the return address whenever a procedure is called during the execution of
a program. The CALL instruction pushes the address of the instruction following the CALL
(return address) on the stack. The RET instruction removes an address from the stack so the
program returns to the instruction following the CALL.
With the assembler, there are specific rules for storing procedures. A procedure begins with
the PROC directive and ends with the ENDP directive. Each directive appears with the name of
the procedure. This programming structure makes it easy to locate the procedure in a program
listing. The PROC directive is followed by the type of procedure: NEAR or FAR. Example 6–16
shows how the assembler uses the definition of both a near (intrasegment) and far (intersegment)
procedure. In MASM version 6.x, the NEAR or FAR type can be followed by the USES statement. The USES statement allows any number of registers to be automatically pushed to the
stack and popped from the stack within the procedure. The USES statement is also illustrated in
Example 6–14.
EXAMPLE 6–14
0000
0000
0002
0004
0006
0007

03 C3
03 C1
03 C2
C3

SUMS

0007
0007
0009
000B
000D
000E

03 C3
03 C1
03 C2
CB

SUMS
SUMS1

SUMS1

000E
0011 03 C3
0013 03 C1
0015 03 C2

SUMS3

001B

SUMS

PROC
ADD
ADD
ADD
RET
ENDP

NEAR
AX,BX
AX,CX
AX,DX

PROC
ADD
ADD
ADD
RET
ENDP

FAR
AX,BX
AX,CX
AX,DX

PROC
ADD
ADD
ADD
RET
ENDP

NEAR USE BX CX DX
AX,BX
AX,CX
AX,DX

When these first two procedures are compared, the only difference is the opcode of the
return instruction. The near return instruction uses opcode C3H and the far return uses opcode
CBH. A near return removes a 16-bit number from the stack and places it into the instruction
pointer to return from the procedure in the current code segment. A far return removes a 32-bit
number from the stack and places it into both IP and CS to return from the procedure to any
memory location.
Procedures that are to be used by all software (global) should be written as far procedures.
Procedures that are used by a given task (local) are normally defined as near procedures. Most
procedures are near procedures.

209

PROGRAM CONTROL INSTRUCTIONS

FIGURE 6–6 The effect of a
near CALL on the stack and the
instruction pointer.

Memory

AFFFF

SP

AFFFE

00

AFFFD

03

Stack

11003
11002

(Procedure)

11001
SP before CALL = FFFF
SS before CALL = A000
IP before CALL = 0003

11000

10004
10003
10002

0F

10001

FF

10000

CALL

Near CALL

CALL
The CALL instruction transfers the flow of the program to the procedure. The CALL instruction
differs from the jump instruction because a CALL saves a return address on the stack. The return
address returns control to the instruction that immediately follows the CALL in a program when
a RET instruction executes.

Near CALL. The near CALL instruction is 3 bytes long; the first byte contains the opcode, and the
second and third bytes contain the displacement, or distance of ±32K in the 8086 through the 80286
processors. This is identical to the form of the near jump instruction. The 80386 and above use a 32bit displacement, when operating in the protected mode, that allows a distance of ±2G bytes. When
the near CALL executes, it first pushes the offset address of the next instruction onto the stack. The
offset address of the next instruction appears in the instruction pointer (IP or EIP). After saving this
return address, it then adds the displacement from bytes 2 and 3 to the IP to transfer control to the
procedure. There is no short CALL instruction. A variation on the opcode exists as CALLN, but this
should be avoided in favor of using the PROC statement to define the CALL as near.
Why save the IP or EIP on the stack? The instruction pointer always points to the next
instruction in the program. For the CALL instruction, the contents of IP/EIP are pushed onto the
stack, so program control passes to the instruction following the CALL after a procedure ends.
Figure 6–6 shows the return address (IP) stored on the stack and the call to the procedure.
Far CALL. The far CALL instruction is like a far jump because it can call a procedure stored in
any memory location in the system. The far CALL is a 5-byte instruction that contains an opcode
followed by the next value for the IP and CS registers. Bytes 2 and 3 contain the new contents of
the IP, and bytes 4 and 5 contain the new contents for CS.
The far CALL instruction places the contents of both IP and CS on the stack before jumping to the address indicated by bytes 2 through 5 of the instruction. This allows the far CALL to
call a procedure located anywhere in the memory and return from that procedure.

210

CHAPTER 6

FIGURE 6–7 The effect of a
far CALL instruction.

Memory

AFFFF

SP

AFFFE

10

AFFFD

00

AFFFC

00

AFFFB

05

Stack

11003
11002

(Procedure)

11001
SP before CALL = FFFF
SS before CALL = A000
IP before CALL = 0005

11000

10004

11

10003

00

10002

00

10001

02

10000

CALL

Far CALL

Figure 6–7 shows how the far CALL instruction calls a far procedure. Here, the contents of
IP and CS are pushed onto the stack. Next, the program branches to the procedure. A variant of
the far call exists as CALLF, but this should be avoided in favor of defining the type of call
instruction with the PROC statement.
In the 64-bit mode a far call is to any memory location and the information placed onto the
stack is an 8-byte number. Likewise, the far return instruction also retrieves an 8-byte return
address from the stack and places it into RIP.

CALLs with Register Operands. Like jump instructions, call instructions also may contain a
register operand. An example is the CALL BX instruction, which pushes the contents of IP onto
the stack. It then jumps to the offset address, located in register BX, in the current code segment.
This type of CALL always uses a 16-bit offset address, stored in any 16-bit register except the
segment registers.
Example 6–15 illustrates the use of the CALL register instruction to call a procedure that
begins at offset address DISP. (This call could also directly call the procedure by using the CALL
DISP instruction.) The OFFSET address DISP is placed into the BX register, and then the CALL
BX instruction calls the procedure beginning at address DISP. This program displays an “OK”
on the monitor screen.
EXAMPLE 6–15

0000

;A DOS program that displays OK using the DISP procedure.
;
.MODEL TINY
;select tiny model
.CODE
;start code segment
.STARTUP
;start program

211

PROGRAM CONTROL INSTRUCTIONS
0100
0103
0105
0107
0109

BB
B2
FF
B2
FF

0110 R
4F
D3
4B
D3

MOV BX,OFFSET DISP
MOV DL,’O’
CALL BX
MOV DL,’K’
CALL BX

;load BX with offset DISP
;display O
;display K

.EXIT
;
;Procedure that displays the ASCII character in DL
;
DISP
PROC
NEAR
MOV AH,2
;select function 2
INT 21H
;execute DOS function 2
RET
DISP
ENDP
END

0110
0110 B4 02
0112 CD 21
0114 C3
0115

CALLs with Indirect Memory Addresses. A CALL with an indirect memory address is particularly useful whenever different subroutines need to be chosen in a program. This selection process
is often keyed with a number that addresses a CALL address in a lookup table. This is essentially
the same as the indirect jump that used a lookup table for a jump address earlier in this chapter.
Example 6–16 shows how to access a table of addresses using an indirect CALL instruction. This table illustrated in the example contains three separate subroutine addresses referenced
by the numbers 0, 1, and 2. This example uses the scaled-index addressing mode to multiply the
number in EBX by 2 so it properly accesses the correct entry in the lookup table.
EXAMPLE 6–16

TABLE

;Instruction that calls procedure ZERO, ONE, or
;depending on the value in EBX
;
DW
ZERO
;address of procedure
DW
ONE
;address of procedure
DW
TWO
;address of procedure
CALL

TWO

ZERO
ONE
TWO

TABLE[2*EBX]

The CALL instruction also can reference far pointers if the instruction appears as CALL
FAR PTR [4*EBX] or as CALL TABLE [4*EBX], if the data in the table are defined as doubleword data with the DD directive. These instructions retrieve a 32-bit address (4 bytes long) from
the data segment memory location addressed by EBX and use it as the address of a far procedure.

RET
The return instruction (RET) removes a 16-bit number (near return) from the stack and places
it into IP, or removes a 32-bit number (far return) and places it into IP and CS. The near and far
return instructions are both defined in the procedure’s PROC directive, which automatically
selects the proper return instruction. With the 80386 through the Pentium 4 processors operating
in the protected mode, the far return removes 6 bytes from the stack. The first 4 bytes contain the
new value for EIP and the last 2 contain the new value for CS. In the 80386 and above, a protected mode near return removes 4 bytes from the stack and places them into EIP.
When IP/EIP or IP/EIP and CS are changed, the address of the next instruction is at a new
memory location. This new location is the address of the instruction that immediately follows the
most recent CALL to a procedure. Figure 6–8 shows how the CALL instruction links to a procedure and how the RET instruction returns in the 8086–Core2 operating in the real mode.
There is one other form of the return instruction, which adds a number to the contents of
the stack pointer (SP) after the return address is removed from the stack. A return that uses an
immediate operand is ideal for use in a system that uses the C/C++ or PASCAL calling conventions. (This is true even though the C/C++ and PASCAL calling conventions require the caller to
remove stack data for many functions.) These conventions push parameters on the stack before

212

CHAPTER 6

FIGURE 6–8 The effect of a
near return instruction on the
stack and instruction pointer.

Memory

SP

AFFFF
AFFFE

00

AFFFD

03

11003

RET

Stack

Near RET

11002
11001
11000

SP before CALL = FFFD
SS before CALL = A000
IP before CALL = 1004

10004
10003

(Return here)

10002

OF

10001

FF

10000

CALL

calling a procedure. If the parameters are to be discarded upon return, the return instruction contains a number that represents the number of bytes pushed to the stack as parameters.
Example 6–17 shows how this type of return erases the data placed on the stack by a few
pushes. The RET 4 adds a 4 to SP after removing the return address from the stack. Because the
PUSH AX and PUSH BX together place 4 bytes of data on the stack, this return effectively
deletes AX and BX from the stack. This type of return rarely appears in assembly language
programs, but it is used in high-level programs to clear stack data after a procedure. Notice how
parameters are addressed on the stack by using the BP register, which by default addresses the
stack segment. Parameter stacking is common in procedures written for C++ or PASCAL by
using the C++ or PASCAL calling conventions.
EXAMPLE 6–17
0000
0003
0006
0007
0008

B8 001E
BB 0028
50
53
E8 0066

0071
0071
0072
0074
0077
007A
007B
007E

55
8B
8B
03
5D
C2

ADDM
EC
46 04
46 06
0004
ADDM

MOV
MOV
PUSH
PUSH
CALL

AX,30
BX,40
AX
BX
ADDM

;stack parameter 1
;stack parameter 2
;add stack parameters

PROC
PUSH
MOV
MOV
ADD
POP
RET
ENDP

NEAR
BP
BP,SP
AX,[BP+4]
AX,[BP+6]
BP
4

;save BP
;address stack with BP
;get parameter 1
;add parameter 2
;restore BP
;return, dump parameters

As with the CALLN and CALLF instructions, there are also variants of the return instruction: RETN and RETF. As with the CALLN and CALLF instructions, these variants should also
be avoided in favor of using the PROC statement to define the type of call and return.

213

PROGRAM CONTROL INSTRUCTIONS

6–4

INTRODUCTION TO INTERRUPTS
An interrupt is either a hardware-generated CALL (externally derived from a hardware
signal) or a software-generated CALL (internally derived from the execution of an instruction
or by some other internal event). At times, an internal interrupt is called an exception. Either
type interrupts the program by calling an interrupt service procedure (ISP) or interrupt
handler.
This section explains software interrupts, which are special types of CALL instructions.
This section describes the three types of software interrupt instructions (INT, INTO, and INT 3),
provides a map of the interrupt vectors, and explains the purpose of the special interrupt return
instruction (IRET).

Interrupt Vectors
An interrupt vector is a 4-byte number stored in the first 1024 bytes of the memory
(00000H–003FFH) when the microprocessor operates in the real mode. In the protected mode,
the vector table is replaced by an interrupt descriptor table that uses 8-byte descriptors to
describe each of the interrupts. There are 256 different interrupt vectors, and each vector contains the address of an interrupt service procedure. Table 6–4 lists the interrupt vectors, with a
brief description and the memory location of each vector for the real mode. Each vector contains
a value for IP and CS that forms the address of the interrupt service procedure. The first 2 bytes
contain the IP, and the last 2 bytes contain the CS.

TABLE 6–4

Interrupt vectors defined by Intel.

Number

Address

Microprocessor

Function

0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
10
11
12
13–1F
20–FF

0H–3H
4H–7H
8–BH
CH–FH
10H–13H
14H–17H
18H–1BH
1CH–1FH
20H–23H
24H–27H
28H–2BH
2CH–2FH
30H–33H
34H–37H
38H–3BH
3CH–3FH
40H–43H
44H–47H
48H–4BH
4CH–7FH
80H–3FFH

All
All
All
All
All
80186–Core2
80186–Core2
80186–Core2
80386–Core2
80386
80386–Core2
80386–Core2
80386–Core2
80386–Core2
80386–Core2

80286–Core2
80486SX
Pentium–Core2



Divide error
Single-step
NMI pin
Breakpoint
Interrupt on overflow
Bound instruction
Invalid opcode
Coprocessor emulation
Double fault
Coprocessor segment overrun
Invalid task state segment
Segment not present
Stack fault
General protection fault (GPF)
Page fault
Reserved
Floating-point error
Alignment check interrupt
Machine check exception
Reserved
User interrupts

214

CHAPTER 6

Intel reserves the first 32 interrupt vectors for the present and future microprocessor products. The remaining interrupt vectors (32–255) are available for the user. Some of the reserved
vectors are for errors that occur during the execution of software, such as the divide error interrupt. Some vectors are reserved for the coprocessor. Still others occur for normal events in the
system. In a personal computer, the reserved vectors are used for system functions, as detailed
later in this section. Vectors 1–6, 7, 9, 16, and 17 function in the real mode and protected mode;
the remaining vectors function only in the protected mode.

Interrupt Instructions
The microprocessor has three different interrupt instructions that are available to the programmer: INT, INTO, and INT 3. In the real mode, each of these instructions fetches a vector from the
vector table, and then calls the procedure stored at the location addressed by the vector. In the
protected mode, each of these instructions fetches an interrupt descriptor from the interrupt
descriptor table. The descriptor specifies the address of the interrupt service procedure. The
interrupt call is similar to a far CALL instruction because it places the return address (IP/EIP and
CS) on the stack.

INTs. There are 256 different software interrupt instructions (INTs) available to the programmer.
Each INT instruction has a numeric operand whose range is 0 to 255 (00H–FFH). For example, the
INT 100 uses interrupt vector 100, which appears at memory address 190H–193H. The address of
the interrupt vector is determined by multiplying the interrupt type number by 4. For example, the
INT 10H instruction calls the interrupt service procedure whose address is stored beginning at
memory location 40H (10H × 4) in the real mode. In the protected mode, the interrupt descriptor is
located by multiplying the type number by 8 instead of 4 because each descriptor is 8 bytes long.
Each INT instruction is 2 bytes long. The first byte contains the opcode, and the second
byte contains the vector type number. The only exception to this is INT 3, a 1-byte special software interrupt used for breakpoints.
Whenever a software interrupt instruction executes, it (1) pushes the flags onto the stack,
(2) clears the T and I flag bits, (3) pushes CS onto the stack, (4) fetches the new value for CS
from the interrupt vector, (5) pushes IP/EIP onto the stack, (6) fetches the new value for IP/EIP
from the vector, and (7) jumps to the new location addressed by CS and IP/EIP.
The INT instruction performs as a far CALL except that it not only pushes CS and IP onto
the stack, but it also pushes the flags onto the stack. The INT instruction performs the operation
of a PUSHF, followed by a far CALL instruction.
Notice that when the INT instruction executes, it clears the interrupt flag (I), which controls the external hardware interrupt input pin INTR (interrupt request). When I = 0, the microprocessor disables the INTR pin; when I = 1, the microprocessor enables the INTR pin.
Software interrupts are most commonly used to call system procedures because the
address of the system function need not be known. The system procedures are common to all
system and application software. The interrupts often control printers, video displays, and disk
drives. Besides relieving the program from remembering the address of the system call, the INT
instruction replaces a far CALL that would otherwise be used to call a system function. The INT
instruction is 2 bytes long, whereas the far CALL is 5 bytes long. Each time that the INT instruction replaces a far CALL, it saves 3 bytes of memory in a program. This can amount to a sizable
saving if the INT instruction often appears in a program, as it does for system calls.
IRET/IRETD. The interrupt return instruction (IRET) is used only with software or hardware
interrupt service procedures. Unlike a simple return instruction (RET), the IRET instruction will
(1) pop stack data back into the IP, (2) pop stack data back into CS, and (3) pop stack data back
into the flag register. The IRET instruction accomplishes the same tasks as the POPF, followed
by a far RET instruction.

215

PROGRAM CONTROL INSTRUCTIONS

Whenever an IRET instruction executes, it restores the contents of I and T from the stack.
This is important because it preserves the state of these flag bits. If interrupts were enabled
before an interrupt service procedure, they are automatically re-enabled by the IRET instruction
because it restores the flag register.
In the 80386 through the Core2 processors, the IRETD instruction is used to return from an
interrupt service procedure that is called in the protected mode. It differs from the IRET because
it pops a 32-bit instruction pointer (EIP) from the stack. The IRET is used in the real mode and
the IRETD is used in the protected mode.

INT 3. An INT 3 instruction is a special software interrupt designed to function as a breakpoint.
The difference between it and the other software interrupts is that INT 3 is a 1-byte instruction,
while the others are 2-byte instructions.
It is common to insert an INT 3 instruction in software to interrupt or break the flow of the
software. This function is called a breakpoint. A breakpoint occurs for any software interrupt, but
because INT 3 is 1 byte long, it is easier to use for this function. Breakpoints help to debug faulty
software.
INTO. Interrupt on overflow (INTO) is a conditional software interrupt that tests the overflow
flag (O). If O = 0, the INTO instruction performs no operation; if O = 1 and an INTO instruction
executes, an interrupt occurs via vector type number 4.
The INTO instruction appears in software that adds or subtracts signed binary numbers.
With these operations, it is possible to have an overflow. Either the JO instruction or INTO
instruction detects the overflow condition.
An Interrupt Service Procedure. Suppose that, in a particular system, a procedure is required to
add the contents of DI, SI, BP, and BX and then save the sum in AX. Because this is a common
task in this system, it may occasionally be worthwhile to develop the task as a software interrupt.
Realize that interrupts are usually reserved for system events and this is merely an example
showing how an interrupt service procedure appears. Example 6–18 shows this software interrupt. The main difference between this procedure and a normal far procedure is that it ends with
the IRET instruction instead of the RET instruction, and the contents of the flag register are
saved on the stack during its execution. It is also important to save all registers that are changed
by the procedure using USES.
EXAMPLE 6–18
0000
0000
0002
0004
0006
0008
0009

INTS
03
03
03
03
CF

C3
05
C7
C6
INTS

PROC
ADD
ADD
ADD
ADD
IRET
ENDP

FAR USES AX
AX,BX
AX,BP
AX,DI
AX,SI

Interrupt Control
Although this section does not explain hardware interrupts, two instructions are introduced that
control the INTR pin. The set interrupt flag instruction (STI) places a 1 into the I flag bit, which
enables the INTR pin. The clear interrupt flag instruction (CLI) places a 0 into the I flag bit,
which disables the INTR pin. The STI instruction enables INTR and the CLI instruction disables
INTR. In a software interrupt service procedure, hardware interrupts are enabled as one of the
first steps. This is accomplished by the STI instruction. The reason interrupts are enabled early in
an interrupt service procedure is that just about all of the I/O devices in the personal computer
are interrupt-processed. If the interrupts are disabled too long, severe system problems result.

216

CHAPTER 6

FIGURE 6–9 Interrupts in a
typical personal computer.

Interrupts in the Personal Computer
The interrupts found in the personal computer differ somewhat from the ones presented
in Table 6–4. The reason that they differ is that the original personal computers are 8086/8088based systems. This meant that they only contained Intel-specified interrupts 0–4. This design
has been carried forward so that newer systems are compatible with the early personal
computers.
Access to the protected mode interrupt structure in use by Windows is accomplished
through kernel functions Microsoft provides and cannot be directly addressed. Protected mode
interrupts use an interrupt descriptor table, which is beyond the scope of the text at this point.
Protected mode interrupts are discussed completely in later chapters.
Figure 6–9 illustrates the interrupts available in the author’s computer. The interrupt
assignments are viewable in the control panel of Windows under Performance and Maintenance
by clicking on System and selecting Hardware and then Device Manager. Now click on View
and select Device by Type and finally Interrupts.

64-Bit Mode Interrupts
The 64-bit system uses the IRETQ instruction to return from an interrupt service procedure. The
main difference between IRET/IRETD and the IRETQ instruction is that IRETQ retrieves an
8-byte return address from the stack. The IRETQ instruction also retrieves the 32-bit EFLAG
register from the stack and places it into the RFLAG register. It appears that Intel has no plans for
using the leftmost 32 bits of the RFLAG register. Otherwise, 64-bit mode interrupts are the same
as 32-bit mode interrupts.

PROGRAM CONTROL INSTRUCTIONS

6–5

217

MACHINE CONTROL AND MISCELLANEOUS INSTRUCTIONS
The last category of real mode instructions found in the microprocessor is the machine control
and miscellaneous group. These instructions provide control of the carry bit, sample the
BUSY/TEST pin, and perform various other functions. Because many of these instructions are
used in hardware control, they need only be explained briefly at this point.

Controlling the Carry Flag Bit
The carry flag (C) propagates the carry or borrow in multiple-word/doubleword addition and subtraction. It also can indicate errors in assembly language procedures. Three instructions control
the contents of the carry flag: STC (set carry), CLC (clear carry), and CMC (complement carry).
Because the carry flag is seldom used except with multiple-word addition and subtraction,
it is available for other uses. The most common task for the carry flag is to indicate an error upon
return from a procedure. Suppose that a procedure reads data from a disk memory file. This operation can be successful, or an error such as file-not-found can occur. Upon return from this procedure, if C = 1, an error has occurred; if C = 0, no error occurred. Most of the DOS and BIOS
procedures use the carry flag to indicate error conditions. This flag is not available in Visual
C/C++ for use with C++.

WAIT
The WAIT instruction monitors the hardware BUSY pin on the 80286 and 80386, and the TEST
pin on the 8086/8088. The name of this pin was changed beginning with the 80286 microprocessor from TEST to BUSY. If the WAIT instruction executes while the BUSY pin = 1, nothing happens and the next instruction executes. If the BUSY pin = 0 when the WAIT instruction executes,
the microprocessor waits for the BUSY pin to return to a logic 1. This pin inputs a busy condition
when at a logic 0 level.
The BUSY/TEST pin of the microprocessor is usually connected to the BUSY pin of the
8087 through the 80387 numeric coprocessors. This connection allows the microprocessor to
wait until the coprocessor finishes a task. Because the coprocessor is inside an 80486 through the
Core2, the BUSY pin is not present in these microprocessors.

HLT
The halt instruction (HLT) stops the execution of software. There are three ways to exit a halt: by
an interrupt, by a hardware reset, or during a DMA operation. This instruction normally appears
in a program to wait for an interrupt. It often synchronizes external hardware interrupts with the
software system. Note that DOS and Windows both use interrupts extensively, so HLT will not
halt the computer when operated under these operating systems.

NOP
When the microprocessor encounters a no operation instruction (NOP), it takes a short time to
execute. In early years, before software development tools were available, a NOP, which performs absolutely no operation, was often used to pad software with space for future machine language instructions. If you are developing machine language programs, which are extremely rare,
it is recommended that you place 10 or so NOPS in your program at 50-byte intervals. This is
done in case you need to add instructions at some future point. A NOP may also find application
in time delays to waste time. Realize that a NOP used for timing is not very accurate because of
the cache and pipelines in modem microprocessors.

218

CHAPTER 6

LOCK Prefix
The LOCK prefix appends an instruction and causes the LOCK pin to become a logic 0. The
LOCK pin often disables external bus masters or other system components. The LOCK prefix
causes the LOCK pin to activate for only the duration of a locked instruction. If more than one
sequential instruction is locked, the LOCK pin remains a logic 0 for the duration of the sequence
of locked instructions. The LOCK:MOV AL,[SI] instruction is an example of a locked instruction.

ESC
The escape (ESC) instruction passes instructions to the floating-point coprocessor from the
microprocessor. Whenever an ESC instruction executes, the microprocessor provides the memory address, if required, but otherwise performs a NOP. Six bits of the ESC instruction provide
the opcode to the coprocessor and begin executing a coprocessor instruction.
The ESC opcode never appears in a program as ESC and in itself is considered obsolete as
an opcode. In its place are a set of coprocessor instructions (FLD, FST, FMUL, etc.) that assemble as ESC instructions for the coprocessor. More detail is provided in Chapter 13, which details
the 8087–Core2 numeric coprocessors.

BOUND
The BOUND instruction, first made available in the 80186 microprocessor, is a comparison
instruction that may cause an interrupt (vector type number 5). This instruction compares the
contents of any 16-bit or 32-bit register against the contents of two words or doublewords of
memory: an upper and a lower boundary. If the value in the register compared with memory is
not within the upper and lower boundary, a type 5 interrupt ensues. If it is within the boundary,
the next instruction in the program executes.
For example, if the BOUND SI,DATA instruction executes, word-sized location DATA
contains the lower boundary, and word-sized location DATA+2 bytes contains the upper boundary. If the number contained in SI is less than memory location DATA or greater than memory
location DATA+2 bytes, a type 5 interrupt occurs. Note that when this interrupt occurs, the return
address points to the BOUND instruction, not to the instruction following BOUND. This differs
from a normal interrupt, where the return address points to the next instruction in the program.

ENTER and LEAVE
The ENTER and LEAVE instructions, first made available to the 80186 microprocessor, are used
with stack frames, which are mechanisms used to pass parameters to a procedure through the
stack memory. The stack frame also holds local memory variables for the procedure. Stack
frames provide dynamic areas of memory for procedures in multiuser environments.
The ENTER instruction creates a stack frame by pushing BP onto the stack and then loading BP with the uppermost address of the stack frame. This allows stack frame variables to be
accessed through the BP register. The ENTER instruction contains two operands: The first
operand specifies the number of bytes to reserve for variables on the stack frame, and the second
specifies the level of the procedure.
Suppose that an ENTER 8,0 instruction executes. This instruction reserves 8 bytes of
memory for the stack frame and the zero specifies level 0. Figure 6–10 shows the stack frame set
up by this instruction. Note that this instruction stores BP onto the top of the stack. It then subtracts 8 from the stack pointer, leaving 8 bytes of memory space for temporary data storage. The
uppermost location of this 8-byte temporary storage area is addressed by BP. The LEAVE
instruction reverses this process by reloading both SP and BP with their prior values. The
ENTER and LEAVE instructions were used to call C++ functions in Windows 3.1, but since
then, CALL has been used in modern versions of Windows for C++ functions.

219

PROGRAM CONTROL INSTRUCTIONS

FIGURE 6–10 The stack
frame created by the ENTER
8,0 instruction. Notice that BP
is stored beginning at the top
of the stack frame. This is followed by an 8-byte area
called a stack frame.

Memory

Old SP location

0020
001F

BP (high)

001E

BP (low)

BP

001D
001C
001B
Stack frame

001A
0019
0018
0017
0016

6–6

New SP location

SUMMARY
1. There are three types of unconditional jump instructions: short, near, and far. The short jump
allows a branch to within +127 and -128 bytes. The near jump (using a displacement of
±32K) allows a jump to any location in the current code segment (intrasegment). The far
jump allows a jump to any location in the memory system (intersegment). The near jump in
an 80386 through a Core2 is within ±2G bytes because these microprocessors can use a
32-bit signed displacement.
2. Whenever a label appears with a JMP instruction or conditional jump, the label, located in
the label field, must be followed by a colon (LABEL:). For example, the JMP DOGGY
instruction jumps to memory location DOGGY:.
3. The displacement that follows a short or near jump is the distance from the next instruction
to the jump location.
4. Indirect jumps are available in two forms: (1) jump to the location stored in a register and
(2) jump to the location stored in a memory word (near indirect) or doubleword (far
indirect).
5. Conditional jumps are all short jumps that test one or more of the flag bits: C, Z, O, P,
or S. If the condition is true, a jump occurs; if the condition is false, the next sequential
instruction executes. Note that the 80386 and above allow a 16-bit signed displacement for
the conditional jump instructions. In 64-bit mode, the displacement is 32 bits allowing a
range of ±2G.
6. A special conditional jump instruction (LOOP) decrements CX and jumps to the label when
CX is not 0. Other forms of loop include LOOPE, LOOPNE, LOOPZ, and LOOPNZ. The
LOOPE instruction jumps if CX is not 0 and if an equal condition exists. In the 80386
through the Core2, the LOOPD, LOOPED, and LOOPNED instructions also use the
ECX register as a counter. In the 64-bit mode, these instructions use the RCX register as for
iteration.
7. The 80386 through the Core2 contain conditional set instructions that either set a byte to
01H or clear it to 00H. If the condition under test is true, the operand byte is set to 01H; if
the condition under test is false, the operand byte is cleared to 00H.

220

CHAPTER 6

8. The .IF and .ENDIF statements are useful in assembly language for making decisions. The
instructions cause the assembler to generate conditional jump statements that modify the
flow of the program.
9. The .WHILE and .ENDW statements allow an assembly language program to use the
WHILE construction, and the .REPEAT and .UNTIL statements allow an assembly language program to use the REPEAT-UNTIL construct.
10. Procedures are groups of instructions that perform one task and are used from any point in
a program. The CALL instruction links to a procedure and the RET instruction returns
from a procedure. In assembly language, the PROC directive defines the name and type of
procedure. The ENDP directive declares the end of the procedure.
11. The CALL instruction is a combination of a PUSH and a JMP instruction. When CALL executes, it pushes the return address on the stack and then jumps to the procedure. A near
CALL places the contents of IP on the stack, and a far CALL places both IP and CS on the
stack.
12. The RET instruction returns from a procedure by removing the return address from the stack
and placing it into IP (near return), or IP and CS (far return).
13. Interrupts are either software instructions similar to CALL or hardware signals used to call
procedures. This process interrupts the current program and calls a procedure. After the procedure, a special IRET instruction returns control to the interrupted software.
14. Real mode interrupt vectors are 4 bytes long and contain the address (IP and CS) of the interrupt service procedure. The microprocessor contains 256 interrupt vectors in the first 1K
bytes of memory. The first 32 are defined by Intel; the remaining 224 are user interrupts. In
protected mode operation, the interrupt vector is 8 bytes long and the interrupt vector table
may be relocated to any section of the memory system.
15. Whenever an interrupt is accepted by the microprocessor, the flags IP and CS are pushed
onto the stack. Besides pushing the flags, the T and I flag bits are cleared to disable both the
trace function and the INTR pin. The final event that occurs for the interrupt is that the
interrupt vector is fetched from the vector table and a jump to the interrupt service procedure
occurs.
16. Software interrupt instructions (INT) often replace system calls. Software interrupts save
3 bytes of memory each time they replace CALL instructions.
17. A special return instruction (IRET) must be used to return from an interrupt service procedure. The IRET instruction not only removes IP and CS from the stack, it also removes the
flags from the stack.
18. Interrupt on an overflow (INTO) is a conditional interrupt that calls an interrupt service
procedure if the overflow flag (O) = 1.
19. The interrupt enable flag (I) controls the INTR pin connection on the microprocessor. If the
STI instruction executes, it sets I to enable the INTR pin. If the CLI instruction executes, it
clears I to disable the INTR pin.
20. The carry flag bit (C) is clear, set, and complemented by the CLC, STC, and CMC
instructions.
21. The WAIT instruction tests the condition of the BUSY or TEST pin on the microprocessor.
If BUSY or TEST = 1, WAIT does not wait; but if BUSY or TEST = 0, WAIT continues testing the BUSY or TEST pin until it becomes a logic 1. Note that the 8086/8088 contains the
TEST pin, while the 80286–80386 contain the BUSY pin. The 80486 through the Core2 do
not contain a BUSY or TEST pin.
22. The LOCK prefix causes the LOCK pin to become a logic 0 for the duration of the locked
instruction. The ESC instruction passes instruction to the numeric coprocessor.
23. The BOUND instruction compares the contents of any 16-bit register against the contents
of two words of memory: an upper and a lower boundary. If the value in the register
compared with memory is not within the upper and lower boundary, a type 5 interrupt ensues.

PROGRAM CONTROL INSTRUCTIONS

221

24. The ENTER and LEAVE instructions are used with stack frames. A stack frame is a mechanism used to pass parameters to a procedure through the stack memory. The stack frame also
holds local memory variables for the procedure. The ENTER instruction creates the stack
frame, and the LEAVE instruction removes the stack frame from the stack. The BP register
addresses stack frame data.

6–7

QUESTIONS AND PROBLEMS
1. What is a short JMP?
2. Which type of JMP is used when jumping to any location within the current code
segment?
3. Which JMP instruction allows the program to continue execution at any memory location in
the system?
4. Which JMP instruction is 5 bytes long?
5. What is the range of a near jump in the 80386–Core2 microprocessors?
6. Which type of JMP instruction (short, near, or far) assembles for the following:
(a) if the distance is 0210H bytes
(b) if the distance is 0020H bytes
(c) if the distance is 10000H bytes
7. What can be said about a label that is followed by a colon?
8. The near jump modifies the program address by changing which register or registers?
9. The far jump modifies the program address by changing which register or registers?
10. Explain what the JMP AX instruction accomplishes. Also identify it as a near or a far jump
instruction.
11. Contrast the operation of a JMP DI with a JMP [DI].
12. Contrast the operation of a JMP [DI] with a JMP FAR PTR [DI].
13. List the five flag bits tested by the conditional jump instructions.
14. Describe how the JA instruction operates.
15. When will the JO instruction jump?
16. Which conditional jump instructions follow the comparison of signed numbers?
17. Which conditional jump instructions follow the comparison of unsigned numbers?
18. Which conditional jump instructions test both the Z and C flag bits?
19. When does the JCXZ instruction jump?
20. Which SET instruction is used to set AL if the flag bits indicate a zero condition?
21. The 8086 LOOP instruction decrements register ____________ and tests it for a 0 to decide
if a jump occurs.
22. The Pentium 4 LOOPD instruction decrements register ____________ and tests it for a 0 to
decide if a jump occurs.
23. The Core2 operated in 64-bit mode for a LOOP instruction decrements register
____________ and tests it for a 0 to decide if a jump occurs.
24. Develop a short sequence of instructions that stores 00H into 150H bytes of memory, beginning at extra segment memory location DATAZ. You must use the LOOP instruction to help
perform this task.
25. Explain how the LOOPE instruction operates.
26. Show the assembly language instructions are generated by the following sequence:
.IF AL==3
ADD AL,2
.ENDIF

222

CHAPTER 6

27. Develop a sequence of instructions that searches through a block of 100H bytes of memory.
This program must count all the unsigned numbers that are above 42H and all that are below
42H. Byte-sized data segment memory location UP must contain the count of numbers above
42H, and data segment location DOWN must contain the count of numbers below 42H.
28. Develop a short sequence of instructions that uses the REPEAT-UNTIL construct to copy
the contents of byte-sized memory BLOCKA into byte-sized memory BLOCKB until 00H
is moved.
29. What happens if the .WHILE 1 instruction is placed in a program?
30. Using the WHILE construct, develop a sequence of instructions that add the byte-sized contents of BLOCKA to BLOCKB while the sum is not 12H.
31. What is the purpose of the .BREAK directive?
32. What is a procedure?
33. Explain how the near and far CALL instructions function.
34. The last executable instruction in a procedure must be a(n) ____________.
35. How does the near RET instruction function?
36. How is a procedure identified as near or far?
37. Which directive identifies the start of a procedure?
38. Write a near procedure that cubes the contents of the CX register. This procedure may not
affect any register except CX.
39. Explain what the RET 6 instruction accomplishes.
40. Write a procedure that multiplies DI by SI and then divides the result by 100H. Make sure
that the result is left in AX upon returning from the procedure. This procedure may not
change any register except AX.
41. Write a procedure that sums EAX, EBX, ECX, and EDX. If a carry occurs, place a logic 1 in
EDI. If no carry occurs, place a 0 in EDI. The sum should be found in EAX after the execution of your procedure.
42. What is an interrupt?
43. Which software instructions call an interrupt service procedure?
44. How many different interrupt types are available in the microprocessor?
45. Illustrate the contents of an interrupt vector and explain the purpose of each part.
46. What is the purpose of interrupt vector type number 0?
47. How does the IRET instruction differ from the RET instruction?
48. What is the IRETD instruction?
49. What is the IRETQ instruction?
50. The INTO instruction only interrupts the program for what condition?
51. The interrupt vector for an INT 40H instruction is stored at which memory locations?
52. What instructions control the function of the INTR pin?
53. What instruction tests the BUSY pin?
54. When will the BOUND instruction interrupt a program?
55. An ENTER 16,0 instruction creates a stack frame that contains ____________ bytes.
56. Which register moves to the stack when an ENTER instruction executes?
57. Which instruction passes opcodes to the numeric coprocessor?

CHAPTER 7
Using Assembly Language with C/C++

INTRODUCTION
Today, it is rare to develop a complete system using only assembly language. We often use
C/C++ with some assembly language to develop a system. The assembly language portion usually solves tasks (difficult or inefficient to accomplish in C/C++) that often include control software for peripheral interfaces and driver programs that use interrupts. Another application of
assembly language in C/C++ programs is the MMX and SEC instructions that are part of the
Pentium class microprocessor and not supported in C/C++. Although C++ does have macros
for these commands, they are more complicated to use than using assembly language. This
chapter develops the idea of mixing C/C++ and assembly language. Many applications in later
chapters also illustrate the use of both assembly language and C/C++ to accomplish tasks for
the microprocessor.
This text uses Microsoft Visual C/C++ Express, but programs can often be adapted to any
version of C/C++, as long as it is standard ANSI (American National Standards Institute)
format C/C++. If you want, you can use C/C++ to enter and execute all the programming
applications in this text. The 16-bit applications are written by using Microsoft Visual C/C++
version 1.52 or newer (available [CL.EXE] for no cost as a legacy application in the Microsoft
Windows Driver Development Kit [DDK]); the 32-bit applications are written using Microsoft
Visual C/C++ version 6 or newer and preferably Microsoft Visual C/C++ version .NET 2003
or Visual C++ Express. The examples in the text are written assuming that you have the latest
version of Visual C++ Express, which is a free downloadable version of Visual C++. Please
visit http://msdn.com to obtain the Visual C++ Express program.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1.
2.
3.
4.

Use assembly language in _asm blocks within C/C+.
Learn the rules that apply to mixed language software development.
Use common C/C++ data and structures with assembly language.
Use both the 16-bit (DOS) interface and the 32-bit (Microsoft Windows) interface with
assembly language code.
5. Use assembly language objects with C/C++ programs.

223

224

CHAPTER 7

7–1

USING ASSEMBLY LANGUAGE WITH C++ FOR 16-BIT DOS APPLICATIONS
This section shows how to incorporate assembly language commands within a C/C++ program.
This is important because the performance of a program often depends on the incorporation of
assembly language sequences to speed its execution. As mentioned in the introduction to the
chapter, assembly language is also used for I/O operations in embedded systems. This text
assumes that you are using a version of the Microsoft C/C++ program, but any C/C++ program
should function as shown, if it supports inline assembly commands. The only change might be
setting up the C/C++ package to function with assembly language. This section of the text
assumes that you are building l6-bit applications for DOS. Make sure that your software can
build l6-bit applications before attempting any of the programs in this section. If you build a
32-bit application and attempt to use the DOS INT 21H function, the program will crash because
DOS calls are not directly allowed. In fact, they are inefficient to use in a 32-bit application.
To build a 16-bit DOS application, you will need the legacy 16-bit compiler usually found
in the C:\WINDDK\2600.1106\bin\win_me\bin16 directory of the Windows DDK. (The
Windows driver development kit can be obtained for a small shipping charge from Microsoft
Corporation.) The compiler is CL.EXE and the 16-bit linker program is LINK.EXE, both located
in the directory or folder listed. Because the path in the computer that you are using probably
points to the 32-bit linker program, it would be wise to work from this directory so the proper
linker is used when linking the object files generated by the compiler. Compilation and linking
must be performed at the command line because there is no visual interface or editor provided
with the compiler and linker. Programs are generated using either Notepad or DOS Edit.

Basic Rules and Simple Programs
Before assembly language code can be placed in a C/C++ program, some rules must be learned.
Example 7–1 shows how to place assembly code inside an assembly language block within a short
C/C++ program. Note that all the assembly code in this example is placed in the _asm block.
Labels are used as illustrated by the label big: in this example. It is also extremely important to use
lowercase characters for any inline assembly code. If you use uppercase, you will find that some of
the assembly language commands and registers are reserved or defined words in C/C++ language.
Example 7–1 uses no C/C++ commands except for the main procedure. Enter the program
using either WordPad or Edit. This program (Example 7–1) reads one character from the console
keyboard, and then filters it through assembly language so that only the numbers 0 through 9 are
sent back to the video display. Although this programming example does not accomplish much,
it does show how to set up and use some simple programming constructs in the C/C++ environment and also how to use the inline assembler.
EXAMPLE 7–1
//Accepts and displays one character of 1 through 9,
//all others are ignored.
void main(void)
{
_asm
{
mov
int
cmp
jb
cmp
ja
mov

ah,8
21h
al,‘0’
big
al,‘9’
big
dl,al

;read key no echo
;filter key code

;echo 0 – 9

225

USING ASSEMBLY LANGUAGE WITH C/C++
mov
int

ah,2
21h

big:
}
}

The register AX was not saved in Example 7–1, but it was used by the program. It is very
important to note that the AX, BX, CX, DX, and ES registers are never used by Microsoft
C/C++. (The function of AX on a return from a procedure is explained later in this chapter.)
These registers, which might be considered scratchpad registers, are available to use with
assembly language. If you wish to use any of the other registers, make sure that you save them
with a PUSH before they are used and restore them with a POP afterwards. If you fail to save the
registers used by a program, the program may not function correctly and can crash the computer.
If the 80386 or above microprocessor is used as a base for the program, the EAX, EBX, ECX,
EDX, and ES registers do not need to be saved. If any other registers are used, they must be saved
or the program will crash.
To compile the program, start the Command Prompt program located in the Start Menu
under Accessories. Change the path to C:\WINDDK\2600.1106\bin\win_me\bin16 if that is
where you have your Windows DDK. You will also need to go to the C:\WINDDK\2600.1106\
lib\win_me directory and copy slibce.lib to the C:\WINDDK\2600.1106\bin\win_me\bin16
directory. Make sure you saved the program in the same path and use the extension .c with the
file name. If using Notepad, make sure you select All Files under File Type when saving. To
compile the program, type CL /G3 filename.c>. This will generate the .exe file (/G3 is the 80386)
for the program. (See Table 7–1 for a list of the /G compiler switches.) Any errors that appear are
ignored by pressing the Enter key. These errors generate warnings that will not cause a problem
when the program is executed. When the program is executed, you will only see a number
echoed back to the DOS screen.
Example 7–2 shows how to use variables from C with a short assembly language program.
In this example, the char variable type (a byte in C) is used to save space for a few 8-bit bytes of
data. The program itself performs the operation X + Y = Z, where X and Y are two one-digit
numbers, and Z is the result. As you might imagine, you could use the inline assembly in C to
learn assembly language and write many of the programs in this textbook. The semicolon adds
comments to the listing in the _asm block, just as with the normal assembler.
EXAMPLE 7–2
void main(void)
{
char a, b;
_asm
{
mov
int
mov
mov

TABLE 7–1 Compiler
(16-bit) G options.

ah,1
21h
a,al
ah,1

;read first digit

;read a + sign

Compiler Switch

Function

/G1
/G2
/G3
/G4
/G5
/G6

Selects the 8088/8086
Selects the 80188/80186/80286
Selects the 80386
Selects the 80486
Selects the Pentium
Selects the Pentium Pro–Pentium 4

Note: The 32-bit C++ compiler does not recognize /G1 or /G2.

226

CHAPTER 7
int
cmp
jne
mov
int
mov
mov
mov
int
mov
mov
add
aaa
add
cmp
je
push
mov
mov
int
pop

21h
al,‘+’
end1
ah,1
21h
b,al
ah,2
dl,‘=’
21h
ah,0
al,a
al,b
ax,3030h
ah,‘0’
down
ax
dl,ah
ah,2
21h
ax

mov
mov
int

dl,al
ah,2
21h

;if not plus
;read second number
;display =

;generate sum
;ASCII adjust for addition

;display 10’s position

down:
;display units position

end1:
}
}

What Cannot Be Used from MASM Inside an _asm Block
Although MASM contains some nice features, such as conditional commands (.IF, .WHILE,
.REPEAT, etc.), the inline assembler does not include the conditional commands from MASM,
nor does it include the MACRO feature found in the assembler. Data allocation with the inline
assembler is handled by C instead of by using DB, DW, DD, etc. Just about all other features are
supported by the inline assembler. These omissions from the inline assembler can cause some
slight problems, as will be discussed in later sections of this chapter.

Using Character Strings
Example 7–3 illustrates a simple program that uses a character string defined with C and displays
it so that each word is listed on a separate line. Notice the blend of both C statements and assembly language statements. The WHILE statement repeats the assembly language commands until
the null (00H) is discovered at the end of the character string. If the null is not discovered, the
assembly language instructions display a character from the string unless a space is located. For
each space, the program displays a carriage return/line feed combination. This causes each word
in the string to be displayed on a separate line.
EXAMPLE 7–3
// Example that displays showing one word per line
void main(void)
{
char strings[] = “This is my first test application using _asm. \n”;
int sc = -1;
while (strings[sc++] != 0)
{
_asm
{
push si
mov si,sc
;get pointer

227

USING ASSEMBLY LANGUAGE WITH C/C++
mov
cmp
jne
mov
mov
int
mov
next: mov
int
pop
}

dl,strings[si]
dl,‘ ’
next
ah,2
dl,10
21h
dl,13
ah,2
21h
si

;get character
;if not space
;display new line

;display character

}
}

Suppose that you want to display more than one string in a program, but you still want
to use assembly language to develop the software to display a string. Example 7–4 illustrates a
program that creates a procedure displaying a character string. This procedure is called each time
that a string is displayed in the program. Note that this program displays one string on each line,
unlike Example 7–3.
EXAMPLE 7–4
// A program illustrating an assembly language procedure that
// displays C language character strings
char string1[] = “This is my first test program using _asm.”;
char string2[] = “This is the second line in this program.”;
char string3[] = “This is the third.”;
void main(void)
{
Str (string1);
Str (string2);
Str (string3);
}
Str (char *string_adr)
{
_asm
{
mov bx,string_adr
mov ah,2
top:
mov dl,[bx]
inc bx
cmp al,0
je
bot
int 21h
jmp top
bot:
mov dl,13
int 21h
mov dl,10
int 21h
}
}

;get address of string

;if null
;display character

;display CR + LF

Using Data Structures
Data structures are an important part of most programs. This section shows how to interface a data
structure created in C with an assembly language section that manipulates the data in the structure.
Example 7–5 illustrates a short program that uses a data structure to store names, ages, and

228

CHAPTER 7

salaries. The program then displays each of the entries by using a few assembly language procedures. Although the string procedure displays a character string, shown in Example 7–4, no carriage return/line feed combination is displayed—instead, a space is displayed. The Crlf
procedure displays a carriage return/line feed combination. The Numb procedure displays the
integer.

EXAMPLE 7–5
// Program illustrating an assembly language procedure that
// displays the contents of a C data structure.
// A simple data structure
typedef struct records
{
char first_name[16];
char last_name[16];
int age;
int salary;
} RECORD;
// Fill some records
RECORD record[4] =
{ {“Bill” ,”Boyd” , 56, 23000},
{“Page”, “Turner”, 32, 34000},
{“Bull”, “Dozer”, 39. 22000},
{“Hy”, “Society”, 48, 62000}
};
// Program
void main(void)
{
int pnt = -1;
while (pnt++ < 3)
{
Str(record[pnt].last_name);
Str(record[pnt].first_name);
Numb(record[pnt].age);
Numb(record[pnt].salary);
Crlf();
}
}
Str (char *string_adr[])
{
_asm
{
mov bx,string_adr
mov ah,2
top:
mov dl,[bx]
inc bx
cmp al,0
je
bot
int 21h
jmp top
bot:
mov al,20h
int 21h
}
}

USING ASSEMBLY LANGUAGE WITH C/C++

229

Crlf()
{
_asm
{
mov
mov
int
mov
int

ah,2
dl,13
21h
dl,10
21h

}
}
Numb (int temp)
{
_asm
{
mov
mov
push
L1:
mov
div
push
cmp
jne
L2:
pop
cmp
je
mov
add
int
jmp
L3:
mov
int
}
}

ax,temp
bx,10
bx
dx,0
bx
dx
ax,0
L1
dx
dl,bl
L3
ah,2
dl,30h
21h
L2
dl,20h
21h

An Example of a Mixed-Language Program
To see how this technique can be applied to any program, Example 7–6 shows how the program
can do some operations in assembly language and some in C language. Here, the only assembly
language portions of the program are the Dispn procedure that displays an integer and the
Readnum procedure, which reads an integer. The program in Example 7–6 makes no attempt to
detect or correct errors. Also, the program functions correctly only if the result is positive and
less than 64K. Notice that this example uses assembly language to perform the I/O; the C portion
performs all other operations to form the shell of the program.
EXAMPLE 7–6
/*
A program that functions as a simple calculator to perform addition,
subtraction, multiplication, and division. The format is X Y =.
*/
int temp;
void main(void)
{

230

CHAPTER 7
int temp1, oper;
while (1)
{
oper = Readnum();
//get first number and operation
temp1 = temp;
if ( Readnum() == ‘=’ )
//get second number
{
switch (oper)
{
case ‘+’:
temp += temp1;
break;
case ‘-’:
temp = temp1 – temp;
break;
case ‘/’:
temp = temp1 / temp;
break;
case ‘*’:
temp *= temp1;
break;
}
Dispn(temp);
//display result
}
else
Break;
}
}
}
int Readnum()
{
int a;
temp = 0;
_asm
{
Readnum1:
mov
int
cmp
jb
cmp
ja
sub
shl
mov
shl
add
add
adc
jmp
Readnum2:
Mov
mov
}
return a;
}

ah,1
21h
al,30h
Readnum2
al,39h
Readnum2
al,30h
temp,1
bx,temp
temp,2
temp,bx
byte ptr temp,al
byte ptr temp+1,0
Readnum1
ah,0
a,ax

Dispn (int DispnTemp)
{
_asm
{
mov ax,DispnTemp
mov bx,10
push bx
Dispn1:
mov dx,0

USING ASSEMBLY LANGUAGE WITH C/C++
div
push
cmp
jne
Dispn2:
pop
cmp
je
add
mov
int
jmp
Dispn3:
mov
int
mov
int
}

231

bx
dx
ax,0
Dispn1
dx
dl,bl
Dispn3
dl,30h
ah,2
21h
Dispn2
dl,13
21h
dl,10
21h

}

7–2

USING ASSEMBLY LANGUAGE WITH VISUAL C/C++ FOR 32-BIT APPLICATIONS
A major difference exists between l6-bit and 32-bit applications. The 32-bit applications are written
using Microsoft Visual C/C++ Express for Windows and the l6-bit applications are written using
Microsoft C++ for DOS. The main difference is that Visual C/C++ Express for Windows is more
common today, but Visual C/C++ Express cannot easily call DOS functions such as INT 2lH. It is
suggested that embedded applications that do not require a visual interface be written in l6-bit C or
C++, and applications that incorporate Microsoft Windows or Windows CE (available for use on a
ROM or Flash1 device for embedded applications) use 32-bit Visual C/C++ Express for Windows.
A 32-bit application is written by using any of the 32-bit registers, and the memory space
is essentially limited to 2G bytes for Windows. The free version of Visual C++ Express does not
support 64-bit applications written in assembly language at this time. The only difference is that
you may not use the DOS function calls; instead use the console getch() or getche() and putch
C/C++ language functions available for use with DOS console applications. Embedded applications use direct assembly language instructions to access I/O devices in an embedded system. In
the Visual interface, all I/O is handled by the Windows operating system framework.
Console applications in WIN32 run in native mode, which allow assembly language to be
included in the program without anything other than the _asm keyword. Windows forms applications are more challenging because they operate in the managed mode, which does not run in
the native mode of the microprocessor. Managed applications operate in a pseudo mode that does
not generate native code.

An Example that Uses Console I/O to Access the Keyboard and Display
Example 7–7 illustrates a simple console application that uses the console I/O commands to read
and write data from the console. To enter this application (assuming Visual Studio .NET 2003 or
Visual C++ Express is available), select a WIN32 console application in the new project option (see
Figure 7–1). Notice that instead of using the customary stdio.h library, we use the conio.h library in
this application. This example program displays any number between 0 and 1000 in all number
bases between base 2 and base 16. Notice that the main program is not called main as it was in earlier versions of C/C++, but is called _tmain in the current version of Visual C/C++ Express when
used with a console application. The argc is the argument count passed to the _tmain procedure
from the command line, and the argv[] is an array that contains the command line argument strings.
1Flash

is a trademark of Intel Corporation.

232

FIGURE 7–1

CHAPTER 7

The new project screen selection of a WIN32 console application.
EXAMPLE 7–7
// Program that displays any number in all numbers bases
// between base 2 and base 16.
#include “stdafx.h”
#include
char *buffer = “Enter a number between 0 and 1000: “;
char *buffer1 = “Base: “;
int a, b = 0;
void disps(int base, int data);
int _tmain(int argc, _TCHAR* argv[])
{
int i;
_cputs(buffer);
a = _getche();
while ( a >= ‘0’ && a <= ‘9’ )
{
_asm sub a, 30h;
b = b * 10 + a;
a = _getche();
}

233

USING ASSEMBLY LANGUAGE WITH C/C++
_putch(10);
_putch(10);
_putch(13);
for ( i = 2; i < 17; i++ )
{
_cputs(buffer1);
disps(10,i );
_putch(‘ ’);
_putch(‘=’);
_putch(‘ ’);
disps(i, b);
_putch(10);
_putch(13);
}
getche();
return 0;

//wait for any key

}
void disps(int base, int data)
{
int temp;
_asm
{
mov eax, data
mov ebx, base
push ebx
disps1:
mov edx,0
div ebx
push edx
cmp eax,0
jne disps1
disps2:
pop edx
cmp ebx,edx
je
disps4
add dl,30h
cmp dl,39h
jbe disps3
add dl,7
disps3:
mov temp,edx
}
_putch(temp);
_asm jmp disps2;
disps4:;
}

This example presents a mixture of assembly language and C/C++ language commands.
The procedure disps (base,data) does most of the work for this program. It allows any integer
(unsigned) to be displayed in any number base, which can be any value between base 2 and base 36.
The upper limit occurs because letters of the alphabet only extend to the letter Z. If you need to
convert larger number bases, a new scheme for bases over 36 must be developed. Perhaps
the lowercase letters a through z can be used for base 37 to 52. Example 7–7 only displays the
number that is entered in base 2 through base 16.

Directly Addressing I/O Ports
If a program is written that must access an actual port number, we can use console I/O commands
such as the _inp(port) command to input byte data, and the _outp(port,byte_data) command to output byte data. When writing software for the personal computer, it is rare to directly address an I/O
port, but when software is written for an embedded system, we often directly address an I/O port.
An alternate to using the _inp and _outp commands is assembly language, which is more efficient

234

CHAPTER 7

in most cases. Be aware that I/O ports may not be accessed in the Windows environment if you are
using Windows NT, Windows 2000, Windows XP, or Windows Vista. The only way to access the
I/O ports in these modern operating systems is to develop a kernel driver. At this point in the text it
would not be practical to develop such a driver. If you are using Windows 98 or even Windows 95,
you can use inp and outp instructions in C/C++ to access the I/O ports directly.

Developing a Visual C++ Application for Windows
This section of the text shows how to use Visual C++ Express to develop a dialog-based application for the Microsoft Foundation Classes library. The Microsoft Foundation Classes (MFC)
is a collection of classes that allows us to use the Windows interface without a great deal of difficulty. The MFC has been renamed to the common language runtime (CLR) in Visual C++
Express. The easiest application to learn and develop is a program that uses a forms application
as presented here. This basic application type is used to program and test all of the software
examples in this textbook written in the Visual C++ Express programming environment.
To create a Visual C++ form-based application, start Visual C++ Express and click on
Create Project near the upper left corner of the start screen. (If you do not have the Visual C++
Express program, it is available for free from Microsoft at http://msdn.com.) Download and
install the latest version, even of it is a beta version. Figure 7–2 illustrates what is displayed

FIGURE 7–2

Starting a C++ program for Windows in Visual C++ Express.

USING ASSEMBLY LANGUAGE WITH C/C++

FIGURE 7–3

235

Design window screen shot.
when the CLR Windows Forms application type is selected under Visual C++ Express
Projects. Enter a name for the project and select an appropriate path for the project, then click
on OK.
After a few moments the design screen should appear as in Figure 7–3. In the middle
section is the form created by this application. To test the application, as it appears, just find the
green arrow located somewhere above the form and below the Windows menu bar at the top
of the screen and click on it to compile, link, and execute the dialog application. (Answer yes to
“Would you like to build the application?”). Click on the X in the title bar to close the application. You have just created and tested your very first Visual C++ Express application.
When looking at the screen shot in Figure 7–3, several items are located in the image
that are important to program creation and development. The right margin of the screen
contains a Properties window, which contains the properties of the form. The left margin
contains Solution Explorer. The tabs, located at the bottom of the Solution Explorer window, allow other views to be displayed such as a class view and so forth in this area. The tabs
at the bottom of the Properties window allow the classes, properties, dynamic help, or output
to be displayed in this window. Your screen may or may not appear as the one illustrated
in Figure 7–3 because it can be modified and probably will be modified as you use the
program.
To create a simple application, select the toolbox by clicking on Tools at the top of the
screen or by opening the View dropdown menu and selecting Toolbox from the list. Windows is

236

CHAPTER 7

A button control
placed on the form.
FIGURE 7–4

an events-driven system so an object or a control is needed on the form to initiate an event. The
control could be a button or almost any control object selected from the toolbox. Click on the
button control near the top of the toolbox, which selects the button. Now move the mouse pointer
(do not drag the button) over to the dialog application in the middle of the screen and draw, by
left-clicking and resizing the button near the center (see Figure 7–4).
Once the button is placed on the screen, an event handler must be added to the application
so that the act of pressing or clicking on the button can be handled. The event handlers are
selected by going to the Properties window and clicking on the yellow lightning bolt . Make
sure that the item selected for events is the button1 object. To switch back to the Properties
window from the event window, click on the icon just to the left of the lightning bolt. Locate the
Click event (should be the first event) and then double-click on the textbox to the right to install
the event handler for Click. The view will now switch to the code view and change the location
of the button click software.
The software currently in view is the button1_Click function, which is called when the
user clicks on the button. This procedure is illustrated in Example 7–8. To test the button, change
the software in Example 7–8 to the software in Example 7–9(a). Click on the green arrow to
compile, link, and execute the dialog application and click on button1 when it is running. The
label on button1 will change to “Wow, Hello” if the button has been made wide enough. This is
the first working application, but it does not use any assembly code. Example 7–9(a) uses the
Text member property of the button1 object to change the text displayed on button1. A variant
that uses a character string object (String^) appears in Example 7–9(b) to display “Wow, Hello
World.”

USING ASSEMBLY LANGUAGE WITH C/C++

237

EXAMPLE 7–8
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
}

EXAMPLE 7–9
//Version (a)
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
button1->Text = “Wow, Hello”;
}
//Version (b)
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
String^ str1 = “Wow, Hello World”;
button1->Text = str1;
}

Now that a simple application has been written, we can modify it to illustrate a more complicated application as shown in Figure 7–5. The caption on the button has been changed to the word
“Convert.” To return to the design screen, select the tab at the top of the program window that is
labeled Form1.h[design]*. When in the Design window, change the caption on the button1 object
by clicking the button and then finding the Text property from the properties of button1 in the
Properties window. Change the Text property to “Convert.” In Figure 7–5 notice that there are three
Label controls and three textbox controls in the illustration below and to the left of the Convert button. These controls are located in the toolbox. Draw them on the screen in approximately the same
FIGURE 7–5

application.

The first

238

CHAPTER 7

place as in Figure 7–5. The labels are changed in properties for each label control. Change the text
for each label as indicated.
Our goal in this example is to display any decimal number entered in the Decimal Number
box as a number with any radix (number base) as selected by the number entered in the Radix
box. The result appears in the Result box when the Convert button is clicked. To switch the view
to the program view, click on the Form1.h tab at the top of the Design window.
To obtain the value from an edit control, use Text property to obtain a string version of the
number. The problem is that in this case an integer is needed and not a string. The string must be
converted to an integer. The Convert class provided in C++ performs conversion from most data
types to most data types. In this case, the Convert class member function ToInt32 is used to transform the string into an integer. The difficult portion of this example is the conversion from base 10
to any number base. Example 7–10 shows how the Convert class is used to convert the string from
the textbox into an integer. This will only function correctly if the number entered into textbox1 is
an integer. If a letter or anything else is entered, the program will crash and display
an error.
EXAMPLE 7–10
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
int number = Convert::ToInt32(textBox1->Text);
}

To handle input errors, a try-catch code block is used as illustrated in Example 7–11. The
try portion tries the code and the catch statement catches any error and displays a message using
the MessageBox class Show member function.
EXAMPLE 7–11
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
try
{
int number = Convert::ToInt32(textBox1->Text);
}
catch (...)
// catch any error
{
MessageBox::Show(“The input must be an integer!”);
}
}

The remainder of the application appears in the button1_Click function of Example 7–12.
This program uses the Horner’s algorithm to convert to any radix from 2 through 36. This conversion algorithm divides the number to be converted by the desired radix until the result is zero.
After each division, the remainder is saved as a significant digit in the result and the quotient is
divided again by the radix. Note that Windows does not use ASCII code, it uses Unicode so the
Char (16-bit Unicode) is needed in place of the 8-bit char. Notice how the order of the remainders is placed into the result string by concatenating each digit to the left of the string. A 0x30 is
added to each digit to convert to ASCII code in the example.
Horner’s algorithm:
1. Divide the number by the desired radix.
2. Save the remainder and replace the number with the quotient.
3. Repeat steps 1 and 2 until the quotient is zero.

USING ASSEMBLY LANGUAGE WITH C/C++

239

EXAMPLE 7–12
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
String^ result = “”;
int number;
int radix;
try
{
number = Convert::ToInt32(textBox1->Text);
radix = Convert::ToInt32(textBox2->Text);
}
catch (...)
// catch all Convert errors
{
MessageBox::Show(“All inputs must be integers!”);
}
if (radix < 2 || radix > 36)
{
MessageBox::Show(“The radix must range between 2 and 36”);
}
else
{
do
// conversion algorithm
{
char digit = number % radix;
number /= radix;
if (digit > 9)
// for letters
{
digit += 7;
// add bias
}
digit += 0x30;
// convert to ASCII
result = digit + result;
}
while (number != 0);
}
textBox3->Text = result;
}

Since this is an assembly language text, the Convert class is not going to be used for good
reason; the function is quite large. To see just how large, you can put a breakpoint in the software
to the left of a Convert function by clicking on the gray bar to the left of the line of code. A brown
circle, a breakpoint, will appear. If you run the program, it will break (stop) at this point and
enter the debugging mode so it can be viewed in assembly language form. To display the disassembled code, run the program until it breaks, and then go to the Debug menu and select
Windows. In the Windows menu, near the bottom, find “Disassembly.” The registers can also be
displayed to step through a program in assembly language.
As you can see, if the program is debugged as described, this is a substantial amount of
code that can be significantly reduced if it is rewritten using the inline assembler. Example
7–13 depicts the assembly language version of Convert::ToInt32 function. This function is
considerably shorter (if debugged and viewed in the Disassemble window) and executes
many times faster than the Convert in Example 7–12. This example points out the inefficiency of the code generated by a high-level language, which may not always be important,
but many cases require tight and efficient code, and that can only be written in assembly
language. My guess is that as a plateau is reached on processor speed, more things will be
written in assembly language. In addition, the new instructions such as MMX and SSE are
not available in high-level languages. They require a very good working knowledge of assembly code.
The main problem with using inline assembly code is that the code cannot be placed into a
Windows-managed forms application in a managed class. In order to use the assembler, the

240

FIGURE 7–6

CHAPTER 7

Changing to /clr for assembly language.

function must be placed before the managed class in order for it to compile. Therefore, in the
project properties, Common Runtime Support must also be changed to /clr from the default setting of /clr:pure so it will compile successfully. (Refer to Figure 7–6 for a screen shot of how to
change Common Language Runtime support to /clr.) A managed program runs under the virtual
machine called .net and an unmanaged application operated in the native mode of the computer.
The inline assembler generates native code for the microprocessor so it must be unmanaged and
reside before the managed class in a program.
Example 7–13 illustrates how to replace part of the Horner’s algorithm with assembly
code in a function called Adjust. The adjust function tests the number for 9, and if it’s greater
than 9, it adds 0x07 and then 0x30 to convert it to ASCII, which is returned. Notice in the example that the assembly code is placed immediately following the using statements at the top of the
program. This is where any assembly functions must be placed so a program can function correctly. The application starts in native mode and switches to managed mode when it encounters
the managed class. By placing the assembly code before the managed class, it is available to the
entire application and it executes in unmanaged or native mode.
At the end of Example 7–13 an alternative version of Adjust appears that is more efficient. The alternative version does not have a return instruction, so how can it function? What
does not appear is that any assembly language function returns the value in AL for a byte, AX
for a word or short, and EAX for an int. Note that the return value dictates the size of the value
returned.

USING ASSEMBLY LANGUAGE WITH C/C++

241

EXAMPLE 7–13
#pragma once
namespace FirstApp {
using
using
using
using
using
using

namespace
namespace
namespace
namespace
namespace
namespace

System;
System::ComponentModel;
System::Collections;
System::Windows::Forms;
System::Data;
System::Drawing;

// short is for a 16-bit variable.
short Adjust(short n)
{
_asm
{
mov ax,n
cmp ax,9
jle later
add ax,7
later:
add ax,30h
mov n,ax
}
return n;
}
/* as an alternative version
short Adjust(short n)
{
_asm
{
mov ax,n
add ax,30h
cmp ax,39h
jle later
add ax,7
later:
}
}
*/
// managed class follows

Figure 7–14 shows the modification to the button1_click function so that Adjust is called
in place of the code that appears in Example 7–12. The code used to set up the form application
that appears between Examples 7–13 and 7–14 is not shown. Notice that the assembly function
uses short in place of character. A short is a 16-bit number used in unmanaged mode and a Char
is a 16-bit number used in managed mode to represent a Unicode character. Here a cast is used to
convert to a Char because without it, the numeric values are displayed instead of ASCII code.
EXAMPLE 7–14
// The sole event handler in this application.
private: System::Void button1_Click(System::Object^ sender,
System::EventArgs^ e)
{
String^ result = “”;
try

242

CHAPTER 7
{
unsigned int number = Convert::ToUInt32(textBox1->Text);
unsigned int radix = Convert::ToUInt32(textBox2->Text);
if (radix < 2 || radix > 36)
{
MessageBox::Show(“The radix must range between 2 and 36”);
}
else
{
do
{
result = (Char)Adjust(number % radix) + result;
number /= radix;
}
while (number != 0);
textBox3->Text = result;
}
}
catch (...)
// catch any error
{
MessageBox::Show
(“The decimal input must between 0 and 4294967295!”);
}
}

7–3

MIXED ASSEMBLY AND C++ OBJECTS
As mentioned in the prior sections, the inline assembler is limited because it cannot use MACRO
sequences and the conditional program flow directives presented in Chapter 6. In some cases, it
is better to develop assembly language modules that are then linked with C++ for more flexibility. This is especially true if the application is being developed by a team of programmers. This
section of the chapter details the use of different objects that are linked to form a program using
both assembly language and C++.

Linking Assembly Language with Visual C++
Example 7–15 illustrates a flat model procedure that will be linked to a C++ program. We denote
that the assembly module is a C++ module by using the letter C after the word flat in the model
statement. The linkage specified by the letter C is the same for the C or C++ languages. The flat
model allows assembly language software to be any length up to 2G bytes. Note that the .586
switch appears before the model statement, which causes the assembler to generate code that
functions in the protected 32-bit mode. The Reverse procedure, shown in Example 7–15, accepts
a character string from a C++ program, reverses its order, and returns to the C++ program.
Notice how this program uses conditional program flow instructions, which are not available
with the inline assembler described in prior sections of this chapter. The assembly language
module can have any name and it can contain more than one procedure, as long as each procedure contains a PUBLIC statement defining the name of the procedure as public. Any parameters
that are transferred in the C++ program and the assembly language program are indicated with
the backslash following the name of the procedure. This names the parameter for the assembly
language program (it can be a different name in C++) and indicates the size of the parameter. The
only thing that is not different in the C++ calling program and the assembly program is the order
of the parameters. In this example, the parameter is a pointer to a character string and the result
is returned as a replacement for the original string.

243

USING ASSEMBLY LANGUAGE WITH C/C++

EXAMPLE 7–15
;
;External function that reverses the order of a string of characters
;
.586
;select Pentium and 32-bit model
.model flat, C
;select flat model with C/C++ linkage
.stack 1024
;allocate stack space
.code
;start code segment
public Reverse

;define Reverse as a public function

Reverse proc uses esi, \
arraychar:ptr

;define procedure
;define external pointer

mov esi,arraychar
mov eax,0
push eax

;address string
;indicate end of string

.repeat
mov al,[esi]
push eax
inc esi
.until byte ptr [esi] == 0

;push all the characters to the stack

mov esi,arraychar

;address string start

.while eax
pop
mov
inc
.endw
Ret

;pop in reverse order

Reverse
End

!= 0
’eax
[esi],al
esi

endp

Example 7–16 illustrates a C++ language program for DOS console applications that uses
the Reverse assembly language procedure. The EXTERN statement is used to indicate that an
external procedure called Reverse is to be used in the C++ program. The name of the procedure
is case-sensitive, so make sure that it is spelled the same in both the assembly language module
and the C++ language module. The EXTERN statement in Example 7–16 shows that the external assembly language procedure transfers a character string to the procedure and returns no
data. If data are returned from the assembly language procedure, data are returned as a value in
register EAX for bytes, words, or doublewords. If floating-point numbers are returned, they must
be returned on the floating-point coprocessor stack. If a pointer is returned, it must be in EAX.
EXAMPLE 7–16
/* Program that reverses the order of a character string */
#include
#include
extern “C” void Reverse(char *);
char chararray[17] = “So what is this?”;
int main(int argc, char* argv[]
{
printf (“%s \n”, chararray);
Reverse (char array);
printf (“%s\n”, chararray);
getche();
return 0;
}

//wait to see result

244

CHAPTER 7

Once both the C++ program and the assembly language program are written, the Visual
C++ development system must be set up to link the two together. For linking and assembly, we
will assume that the assembly language module is called Reverse.txt (you cannot add an .asm
extension file to the file list for inclusion into a project, so just use the .txt extension and add a
.txt file) and the C++ language module is called Main.cpp. Both modules are stored in the
C:\PROJECT\MINE directory or some other directory of your choosing. After the modules are
placed in the same project workspace, the Programmer’s Workbench program is used to edit both
assembly language and C++ language modules.
To set up the Visual C++ developer studio to compile, assemble, and link these files, follow
these steps:
1. Start the developer studio and select New from the File menu.
a. Choose New Project.
b. When the Application Wizard appears, click on Visual C++ Projects.
c. Select C++ Console Application, and name the project Mine.
d. Then click on OK.
2. You will see the project in the Solution window at the left margin in the center. It will have a
single file called Main.cpp, which is the C++ program file. Modify this to appear as in
Example 7–16.
3. To add the assembly language module, right-click on the line Source Files and select Add
from the menu. Choose Add New Item from the list. Scroll down the list of file types until
you find Text Files and select it, then enter the file name as Reverse and click on Open. This
creates the assembly module called Reverse.txt. You may enter the assembly code from
Example 7–15 in this file.
4. Under the Source Files listing in the Solution Explorer, right-click on Reverse.txt and select
Properties. Figure 7–7 shows what to enter in this wizard after you click on the Custom
Build step. Make sure you enter the object file name (Reverse.obj) in the Outputs box and
ml /c /Cx /coff Reverse.txt in the Command Line box. The Reverse assembly language file
will assemble and be included in the project.
5. Assuming both Examples 7–15 and 7–16 have been entered and you have completed all
steps, the program will function.
Using the
assembler to assemble a
module in Visual C++.
FIGURE 7–7

245

USING ASSEMBLY LANGUAGE WITH C/C++

TABLE 7–2

Morse code.

A
B
C
D
E
F
G
H
I

._
_...
_._.
_..
.
.._.
__.
....
..

J
K
L
M
N
O
P
Q
R

___
_._
._..
__
_.
___
.__.
__._
._.

S
T
U
V
W
X
Y
Z

...
_
.._
..._
.__
_.._
_.__
__..

At last, you can execute the program. Click on the green arrow. You should see two lines of
ASCII text data displayed. The first line is in correct forward order and the second is in reverse
order. Although this is a trivial application, it does illustrate how to create and link C++ language
with assembly language.
Now that we have a good understanding of interfacing assembly language with C++, we
need a longer example that uses a few assembly language procedures with a C++ language program. Example 7–17 illustrates an assembly language package that includes a procedure (Scan)
to test a character input against a lookup table and return a number that indicates the relative
position in the table. A second procedure (Look) uses a number transferred to it and returns
with a character string that represents Morse code. (The code is not important, but if you are
interested, Table 7–2 lists Morse code.)
EXAMPLE 7–17
.586
.model flat, C
.data
table db
db
db
db
db
db
db

2,1,4,8,4,10,3,4
1,0,4,2,3,6,4,0
2,0,4,7,3,5,4,4
2,3,2,2,3,7,4,6
4,13,3,2,3,0,1,1
3,1,4,1,3,3,4,9
4,11,4,12

.code
Public Scan
Public Look
Scan proc uses ebx,\
char:dword
mov ebx,char
.if bl >= ‘a’ && bl <= ‘z’
sub bl,20h
.endif
sub bl,41h
add bl,bl
add ebx,offset table
mov ax,word ptr[ebx]
ret
Scan

endp

Look proc uses ebx ecx,\
numb:dword,\
pntr:ptr

;ABCD
;EFGH
;IJKL
;MNOP
;QRST
;UVWX
;YZ

246

CHAPTER 7
mov ebx,pntr
mov eax,numb
mov ecx,0
mov cl,al
.repeat
shr ah,1
.if carry?
mov byte ptr[ebx],‘_’
.else
mov byte ptr[ebx],‘.’
.endif
inc ebx
.untilcxz
mov byte ptr[ebx],0
ret
Look
end

endp

The lookup table in Example 7–17 contains 2 bytes for each character between A and Z.
For example, the code for A is a 2 for a Morse-coded character two of any combination of dashes
or dots, and the 1 is the code for the letter a (. – ), where the binary equivalent 01 (for two digits)
is a dot followed by a dash. This lookup table is accessed by the Scan procedure to obtain the
correct Morse code from the lookup table, which is returned in AX as a parameter to the C++
language call. The remaining assembly code is mundane.
Example 7–18 lists the C++ program, which calls the two procedures listed in Example 7–17.
This software is simple to understand, so we do not explain it.
EXAMPLE 7–18
// Moorse.cpp : Defines the entry point for the console application.
#include
using namespace std;
extern “C” int Scan(int);
extern “C” void Look(int, char *);
int main(int argc, char* argv[])
{
int a = 0;
char chararray[] = “This, is the trick!\n”;
char chararray1[10];
while ( chararray[a] != ‘\n’ )
{
if ( chararray[a] < ‘A’ || chararray[a] > ‘z’ )
cout << chararray[a] << ‘\n’;
else
{
Look ( Scan ( chararray[a] ), chararray1 );
cout << chararray[a] << “ = ” << chararray1 << ‘\n’;
}
a++;
}
cout << “Type enter to quit!”;
cin.get();
return 0;
}

Although the examples presented here are for console applications, the same method of
instructing Visual Studio to assemble and link an assembly language module is also used for
Visual applications for Windows. The main difference is that Windows applications do not use
printf or cout. The next chapter explains how library files can also be used with Visual C++ and
also gives many more programming examples.

USING ASSEMBLY LANGUAGE WITH C/C++

247

Adding New Assembly Language Instructions to C/C++ Programs
From time to time, as new microprocessors are introduced by Intel, new assembly language
instructions are also introduced. These new instructions cannot be used in C++ unless you
develop a macro for C++ to include them in the program. An example is the CPUID assembly
language instruction. This will function in an _asm block within C++ because the inline assembler does not recognize it. Another group of newer instructions includes the MMX and SEC
instructions. These are also recognized, but in order to illustrate how a new instruction is added
that is not in the assembler, we show the technique. To use any new instructions, first look up the
machine language code from Appendix B or from Intel’s website at www.intel.com. For example, the machine code for the CPUlD instruction is 0F A2. This 2-byte instruction can be defined
as a C++ macro, as illustrated in Example 7–19. To use the new macro in a C++ program, all we
need to type is CPUID. The _emit macro stores the byte that follows it in the program.
EXAMPLE 7–19
#define CPUID _asm _emit 0x0f _asm _emit 0xa2

7–4

SUMMARY
1. The inline assembler is used to insert short, limited assembly language sequences into a C++
program. The main limitation of the inline assembler is that it cannot use macro sequences
or conditional program flow instructions.
2. Two versions of C++ language are available. One is designed for 16-bit DOS console applications and the other for 32-bit Windows applications. The type chosen for an application
depends on the environment, but in most cases programmers today use Windows and the
32-bit Visual Express version.
3. The 16-bit assembly language applications use the DOS INT 21H commands to access
devices in the system. The 32-bit assembly language applications cannot efficiently or easily
access the DOS INT 21H function calls even though many are available.
4. The most flexible and often-used method of interfacing assembly language in a C++
program is through separate assembly language modules. The only difference is that these
separate assembly language modules must be defined by using the C directive following the
.model statement to define the module linkage as C/C++ compatible.
5. The PUBLIC statement is used in an assembly language module to indicate that the procedure name is public and available to use with another module. External parameters are
defined in an assembly language module by using the name of the procedure in the PROC
statement. Parameters are returned through the EAX register to the calling C/C++ procedure
from the assembly language procedure.
6. Assembly language modules are declared external to the C++ program by using the extern
directive. If the extern directive is followed by the letter C, the directive is used in a C/C++
language program.
7. When using Visual Studio, we can instruct it to assemble an assembly language module by
clicking on Properties for the module and adding the assembler language program (ml /c /Cx /
coff Filename.txt) and output file as an object file (Filename.obj) in the Custom Build step for
the module.
8. Assembly language modules can contain many procedures, but can never contain programs
using the .startup directive.

248

CHAPTER 7

7–5

QUESTIONS AND PROBLEMS
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.

31.

Does the inline assembler support assembly language macro sequences?
Can a byte be defined in the inline assembler by using the DB directive?
How are labels defined in the inline assembler?
Which registers can be used in assembly language (either inline or linked modules) without
saving?
What register is used to return integer data from assembly language to the C++ language
caller?
What register is used to return floating-point data from assembler language to the C++
language caller?
Is it possible to use the .if statement in the inline assembler?
In Example 7–3, explain how the mov dl,strings[si] instruction accesses strings data.
In Example 7–3, why was the SI register pushed and popped?
Notice in Example 7–5 that no C++ libraries (#include) are used. Do you think that
compiled code for this program is smaller than a program to accomplish the same task in
C++ language? Why?
What is the main difference between the 16-bit and 32-bit versions of C/C++ when using the
inline assembler?
Can the INT 21H instruction, used to access DOS functions, be used in a program using the
32-bit version of the C/C++ compiler? Explain your answer.
What is the #include C/C++ library used for in a program?
Write a short C/C++ program that uses the _getche() function to read a key and the _putch()
function to display the key. The program must end if an ‘@’ is typed.
Would an embedded application that is not written for the PC ever use the conio.h library?
In Example 7–7, what is the purpose of the sequence of instructions _punch(10); followed
by _punch(13);?
In Example 7–7, explain how a number is displayed in any number base.
Which is more flexible in its application, the inline assembler or assembly language modules that are linked to C++?
What is the purpose of a PUBLIC statement in an assembly code module?
How is an assembly code module prepared for use with C++ language?
In a C++ language program, the extern void GetIt(int); statement indicates what about function GetIt?
How is a 16-bit word of data defined in C++?
What is a control in a C++ Visual program and where is it obtained?
What is an event in a C++ Visual program and what is an event handler?
In Example 7–13, what size parameter is short?
Can the edit screen of C++ Visual Studio be used to enter and edit an assembly language
programming module.
How are external procedures that are written in assembly language indicated to a C++
program?
Show how the RDTSC instruction (opcode is 0F 31) could be added to a C++ program using
the _emit macro.
In Example 7–17, explain what data type is used by Scan.
Write a short assembly language module to be used with C++ that rotates a number three
places to the left. Call your procedure RotateLeft3 and assume the number is an 8-bit char
(byte in assembly).
Repeat question 30, but write the same function in C++ without the assembler.

USING ASSEMBLY LANGUAGE WITH C/C++

249

32. Write a short assembly language module that receives a parameter (byte-sized) and returns a
byte-sized result to a caller. Your procedure must take this byte and convert it into an uppercase letter. If an uppercase letter or anything else appears, the byte should not be modified.
33. How is a CLR Visual C++ Express application executed from Visual Studio?
34. What are properties in a Visual C++ application?
35. What is an ActiveX control or object?
36. Show how a single instruction assembly language instruction, such as inc ptr, is inserted into
a Visual C++ program.

CHAPTER 8
Programming the Microprocessor

INTRODUCTION
This chapter develops programs and programming techniques using the inline assembler program from Visual C++ Express. The Visual C++ inline assembler has already been explained
and demonstrated in prior chapters, but there are still more features to learn at this point.
Some programming techniques explained in this chapter include assembly language modules, keyboard and display manipulation, program modules, library files, using the mouse,
using timers, and other important programming techniques. As an introduction to programming,
this chapter provides a wealth of background on valuable programming techniques so that
programs can be easily developed for the personal computer by using the inline assembler as a
springboard for Visual C++ Express applications created for Windows.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Use the MASM assembler and linker program to create programs that contain more than
one module.
2. Explain the use of EXTRN and PUBLIC as they apply to modular programming.
3. Set up a library file that contains commonly used subroutines and learn how to use the
DUMPBIN program.
4. Write and use MACRO and ENDM to develop macro sequences used with linear programming in modules that link to C++ code.
5. Show how both sequential and random access files are developed for use in a system.
6. Develop programs using event handlers to perform keyboard and display tasks.
7. Use conditional assembly language statements in programs.
8. Use the mouse in program examples.

250

PROGRAMMING THE MICROPROCESSOR

8–1

251

MODULAR PROGRAMMING
Many programs are too large to be developed by one person. This means that programs are
routinely developed by teams of programmers. The linker program is provided with Visual
Studio so that programming modules can be linked together into a complete program. Linking is
also available from the command prompt provided by Windows. This section of the text
describes the linker, the linking task, library files, EXTRN, and PUBLIC as they apply to program modules and modular programming.

The Assembler and Linker
The assembler program converts a symbolic source module (file) into a hexadecimal object file.
It is even a part of Visual Studio, located in the C:\Program Files\Microsoft Visual Studio .NET
2003\Vc7\bin folder. We have seen many examples of symbolic source files, written in assembly
language, in prior chapters. Example 8–1 shows how the assembler dialog that appears as a source
module named NEW.ASM is assembled. Note that this dialog is used with version 6.15 at the DOS
command line. The version that comes with Visual C will not work for 16-bit DOS programs. If a
16-bit assembler and linker are needed, they can be obtained in the Windows Driver Development
Kit (DDK). Whenever you create a source file, it should have the extension of ASM, but as we
learned in the last chapter, that is not always possible. Source files are created by using NotePad or
almost any other word processor or editor capable of generating an ASCII file.
EXAMPLE 8–1
C:\masm611\BIN>ml new.asm
Microsoft (R) Macro Assembler Version 6.11
Copyright (C) Microsoft Corp 1981–1993. All rights reserved.
Assembling: new.asm
Microsoft (R) Segmented Executable Linker Version 5.60.220 Sep 9 1994
Copyright (C) Microsoft Corp 1984–1993. All rights reserved.
Object Modules [.obj]: new.obj
Run File [new.exe]: “new.exe”
List File [nul.map]: NUL
Libraries [.lib]:
Definitions File [nul.def]:

The assembler program (ML) requires the source file name following ML. In Example 8–1,
the /Fl switch is used to create a listing file named NEW.LST. Although this is optional, it is recommended so that the output of the assembler can be viewed for troubleshooting problems. The
source listing file (.LST) contains the assembled version of the source file and its hexadecimal
machine language equivalent. The cross-reference file (.CRF), which is not generated in this
example, lists all labels and pertinent information required for cross-referencing. An object file is
also generated by ML as an input to the linker program. In many cases we only need to generate
an object file, which is accomplished by using the /c switch.
The linker program, which executes as the second part of ML, reads the object files that
are created by the assembler program and links them together into a single execution file. An
execution file is created with the file name extension EXE. Execution files are selected by typing the file name at the DOS prompt (C:\). An example execution file is FROG.EXE, which is
executed by typing FROG at the command prompt.
If a file is short enough (less than 64K bytes long), it can be converted from an execution
file to a command file (.COM). The command file is slightly different from an execution file in

252

CHAPTER 8

that the program must be originated at location 0100H before it can execute. This means that the
program must be no larger than 64K–100H in length. The ML program generates a command file
if the tiny model is used with a starting address of 100H. Command files are only used with DOS
or if a true binary version (for a EPROM/FLASH burner) is needed. The main advantage of a
command file is that it loads off the disk into the computer much more quickly than an execution
file. It also requires less disk storage space than the equivalent execution file.
Example 8–2 shows the linker program protocol when it is used to link the files NEW,
WHAT, and DONUT. The linker also links library files (LIBS) so procedures, located with LIBS,
can be used with the linked execution file. To invoke the linker, type LINK at the command
prompt, as illustrated in Example 8–2. Note that before files are linked, they must first be assembled and they must be error-free. ML not only links the files, but it also assembles them prior to
linking.
EXAMPLE 8–2
C:\masm611\BIN>ml new.asm what.asm donut.asm
Microsoft (R) Macro Assembler Version 6.11
Copyright (C) Microsoft Corp 1981–1993. All rights reserved.
Assembling: new.asm
Assembling: what.asm
Assembling: donut.asm
Microsoft (R) Segmented Executable Linker Version 5.60.220 Sep 9 1994
Copyright (C) Microsoft Corp 1984–1993. All rights reserved.
Object Modules [.obj]: new.obj+
Object Modules [.obj]: “what.obj”+
Object Modules [.obj]: “donut.obj”/t
Run File [new.com]: “new.com”
List File [nul.map]: NUL
Libraries [.lib]:
Definitions File [nul.def]:

In this example, after typing ML, the linker program asks for the “Object Modules,” which
are created by the assembler. In this example, we have three object modules: NEW, WHAT, and
DONUT. If more than one object file exists, type the main program file first (NEW, in this example), followed by any other supporting modules.
Library files are entered after the file name and after the switch /LINK. In this example,
library files were not entered. To use a library called NUMB.LIB while assembling a program
called NEW.ASM, type ML NEW.ASM /LINK NUMB.LIB.
In the Windows environment you cannot link a program—you can only assemble a program. You must use Visual Studio to link the program files during the build. You can assemble a
file or files and generate objects for use with Visual C++. Example 8–3 illustrates how a module
is compiled, but not linked with ML. The /c switch (lowercase c) tells the assembler to compile
and generate object files, /Cx preserves the case of all functions and variables, and /coff generates a common object file format output for the object files used in a 32-bit environment.
EXAMPLE 8–3
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin>ml /c /Cx /coff new.asm
Microsoft (R) Macro Assembler Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
Assembling: new.asm

253

PROGRAMMING THE MICROPROCESSOR

PUBLIC and EXTRN
The PUBLIC and EXTRN directives are very important to modular programming because they
allow communications between modules. We use PUBLIC to declare that labels of code, data, or
entire segments are available to other program modules. EXTRN (external) declares that labels
are external to a module. Without these statements, modules could not be linked together to create a program by using modular programming techniques. They might link, but one module
would not be able to communicate to another.
The PUBLIC directive is placed in the opcode field of an assembly language statement to
define a label as public, so that the label can be used (seen by) by other modules. The label
declared as public can be a jump address, a data address, or an entire segment. Example 8–4
shows the PUBLIC statement used to define some labels and make them public to other modules
in a program fragment. When segments are made public, they are combined with other public
segments that contain data with the same segment name.
EXAMPLE 8–4
.model
.data

flat,

c

public Data1
public Data2

0000 0064[
00
]
0064 0064[
00
]

Data1

db

100 dup(?)

Data2

db

100 dup(?)

;declare Data1 and Data2 public

.code
.startup
public Read
Read
0006 B4 06

;declare Read public

proc
far
mov ah,6

The EXTRN statement appears in both data and code segments to define labels as external
to the segment. If data are defined as external, their sizes must be defined as BYTE, WORD, or
DWORD. If a jump or call address is external, it must be defined as NEAR or FAR. Example 8–5
shows how the external statement is used to indicate that several labels are external to the program listed. Notice in this example that any external address or data is defined with the letter E
in the hexadecimal assembled listing. It is assumed that Example 8–4 and Example 8–5 are
linked together.
EXAMPLE 8–5

0005 Bf 0000 E
0008 B9 000A
000B

.model flat,
.data
extrn
extrn
extrn
extrn
.code
extrn
.startup
mov
mov
Start:

c
Data1:byte
Data2:byte
Data3:word
Data4:dword
Read:far
dx,offset Data1
cx,10

254

CHAPTER 8
000B 9A 0000 ---- E
0010 AA
0011 E2 F8

call
stosb
loop

Read
Start

.exit
End

Libraries
Library files are collections of procedures that are used by many different programs. These procedures are assembled and compiled into a library file by the LIB program that accompanies the
MASM assembler program. Libraries allow common procedures to be collected into one place
so they can be used by many different applications. You may have noticed when setting up Visual
C++ to build the assembly language modules in Chapter 7 that many library files were in the link
list used by Visual C++. The library file (FILENAME.LIB) is invoked when a program is linked
with the linker program.
Why bother with library files? A library file is a good place to store a collection of related
procedures. When the library file is linked with a program, only the procedures required by that
program are removed from the library file and added to the program. If any amount of assembly
language programming is to be accomplished efficiently, a good set of library files is essential
and saves many hours in recoding common functions.

Creating a Library File. A library file is created with the LIB command, which executes the
LIB.EXE program that is supplied with Visual Studio. A library file is a collection of assembled
.OBJ files that contains procedures or tasks written in assembly language or any other language.
Example 8–6 shows two separate functions (UpperCase and LowerCase) included in a module
that is written for Windows, which will be used to structure a library file. Please notice that the
name of the procedure must be declared PUBLIC in a library file and does not necessarily need
to match the file name, although it does in this example. A variable is transferred to each file, so
the EXTRN statement also appears in each procedure to gain access to an external variable.
Example 8–7 shows the C++ protocols that are required to use the functions in this library file in
a C++ program, provided the library is linked to the program.
EXAMPLE 8–6
.586
.model flat,c
.code
public UpperCase
public LowerCase
UpperCase proc ,\
Data1:byte
mov
al,Data1
.if al >= 'a' && al <= 'z'
sub al,20h
.endif
ret
UpperCase endp
LowerCase proc ,\
Data2:byte
mov
al,Data2
.if al >= 'A' && al <= 'Z'
add al,20h
.endif
ret
LowerCase endp
End

PROGRAMMING THE MICROPROCESSOR

255

EXAMPLE 8–7
extern “C” char UpperCase(char);
extern “C” char LowerCase(char);

The LIB program begins with the copyright message from Microsoft, followed by the
prompt Library name. The library name chosen is case for the CASE.LIB file. Because this is a
new file, the library program must be prompted with the object file name. You must first assemble CASE.ASM with ML. The actual LIB command is listed in Example 8–8. Notice that the
LIB program is invoked with the object name following it on the command line.
EXAMPLE 8–8
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin>lib case.obj
Microsoft (R) Library Manager Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.

A utility program called DUMPBIN.EXE is provided to display the contents of the library or
any other file. Example 8–9 shows the outcome of a binary dump using the /all switch to show the
library module CASE.LIB and all its components. Near the top of this listing are the public names
for _UpperCase and _LowerCase. The Raw Data #1 section contains the actual hexadecimal-coded
instructions for the two procedures.
EXAMPLE 8–9
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin>dumpbin /all case.lib
Microsoft (R) COFF/PE Dumper Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file case.lib
File Type: LIBRARY
Archive member name at 8: /
401D4A83 time/date Sun Feb 01 13:50:43 2004
uid
gid
0 mode
22 size
correct header end
2 public symbols
C8 _LowerCase
C8 _UpperCase
Archive member name at 66: /
401D4A83 time/date Sun Feb 01 13:50:43 2004
uid
gid
0 mode
26 size
correct header end
1 offsets
1

C8

2 public symbols
1 _LowerCase
1 _UpperCase

256

CHAPTER 8

Archive member name at C8: case.obj/
401D43A6 time/date Sun Feb 01 13:21:26 2004
uid
gid
100666 mode
228 size
correct header end
FILE HEADER VALUES
14C machine (x86)
3 number of sections
401D43A6 time date stamp Sun Feb 01 13:21:26 2004
124 file pointer to symbol table
D number of symbols
0 size of optional header
0 characteristics
SECTION HEADER #1
.text name
0 physical address
0 virtual address
24 size of raw data
8C file pointer to raw data (0000008C to 000000AF)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
60500020 flags
Code
16 byte align
Execute Read
RAW DATA #1
00000000: 55 8B EC 8A 45 08 3C 61 72 06 3C 7A 77 02 2C 20 U.ì.E. 00000010: C9 C3 55 8B EC 8A 45 08 3C 41 72 06 3C 5A 77 02 ÉAU.ì.E. 00000020: 04 20 C9 C3
. ÉA
SECTION HEADER #2
.data name
24 physical address
0 virtual address
0 size of raw data
0 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
C0500040 flags
Initialized Data
16 byte align
Read Write
SECTION HEADER #3
.debug$S name
24 physical address
0 virtual address
74 size of raw data
B0 file pointer to raw data (000000B0 to 00000123)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
42100040 flags
Initialized Data
Discardable
1 byte align
Read Only

257

PROGRAMMING THE MICROPROCESSOR

RAW DATA #3
00000000:
00000010:
00000020:
00000030:
00000040:
00000050:
00000060:
00000070:

04
00
4D
5C
16
0A
29
72

00
00
49
62
11
00
20
00

00
00
43
69
03
05
4D
00

00
00
52
6E
02
0C
61
00

F1
43
4F
5C
00
4D
63

00
3A
53
63
00
69
72

00
5C
7E
61
05
63
6F

00
50
31
73
00
72
20

00
52
2E
65
00
6F
41

00
4F
4E
2E
00
73
73

00
47
45
6F
00
6F
73

00
52
54
62
00
66
65

30
41
5C
6A
00
74
6D

00
7E
56
00
00
20
62

01
31
63
34
07
28
6C

11 ....ñ.......0...
5C ....C:\PROGRA~1\
37 MICROS~1.NET\Vc7
00 \bin\case.obj.4.
00 ................
52 ....Microsoft (R
65 ) Macro Assemble
r...

COFF SYMBOL TABLE
000 00000000 DEBUG notype
Filename
| .file
C:\PROGRA~1\MICROS~1.NET\Vc7\bin\case.asm
004 000F0C05 ABS
notype
Static
| @comp.id
005 00000000 SECT1 notype
Static
| .text
Section length
24, #relocs
0, #linenums
0, checksum
007 00000000 SECT2 notype
Static
| .data
Section length
0, #relocs
0, #linenums
0, checksum
009 00000000 SECT3 notype
Static
| .debug$S
Section length
74, #relocs
0, #linenums
0, checksum
00B 00000000 SECT1 notype ()
External
| _UpperCase
00C 00000012 SECT1 notype ()
External
| _LowerCase

0
0
0

String Table Size = 0x1A bytes
Summary
0 .data
74 .debug$S
24 .text

Once the library file is linked to your program file, only the library procedures actually
used by your program are placed in the execution file. Don’t forget to use the extern “C” statement in the C++ program to use a function from a library file.
In Visual C++ Express, a library is created by selecting the Class Library choice at the
Create menu. This feature creates a DLL (dynamic link library) file that can be included in any
C++ application. The DLL can contain C++ code or assembly code. To include the DLL in any
program, under Project, select the “add reference” choice and browse to the DLL file. Once the
DLL is added, place an #include at the start of the class where the DLL is to be used in a program.

Macros
A macro is a group of instructions that perform one task, just as a procedure performs one task. The
difference is that a procedure is accessed via a CALL instruction, whereas a macro, and all the instructions defined in the macro, is inserted in the program at the point of usage. Creating a macro is very
similar to creating a new opcode, which is actually a sequence of instructions, in this case, that can be
used in the program. You type the name of the macro and any parameters associated with it, and the
assembler then inserts them into the program. Macro sequences execute faster than procedures
because there is no CALL or RET instruction to execute. The instructions of the macro are placed in
your program by the assembler at the point where they are invoked. Be aware that macros will not
function using the inline assembler; they only function in external assembly language modules.
The MACRO and ENDM directives delineate a macro sequence. The first statement of a
macro is the MACRO instruction, which contains the name of the macro and any parameters
associated with it. An example is MOVE MACRO A,B, which defines the macro name as
MOVE. This new pseudo opcode uses two parameters: A and B. The last statement of a macro is
the ENDM instruction, which is placed on a line by itself. Never place a label in front of the
ENDM statement. If a label appears before ENDM, the macro will not assemble.

258

CHAPTER 8

Example 8–10 shows how a macro is created and used in a program. The first six lines of
code define the macro. This macro moves the word-sized contents of memory location B into
word-sized memory location A. After the macro is defined in the example, it is used twice. The
macro is expanded by the assembler in this example, so that you can see how it assembles to generate the moves. Any hexadecimal machine language statement followed by a number (1, in this
example) is a macro expansion statement. The expansion statements are not typed in the source
program; they are generated by the assembler (if .LISTALL is included in the program) to show
that the assembler has inserted them into the program. Notice that the comment in the macro is
preceded with ;; instead of ; as is customary. Macro sequences must always be defined before
they are used in a program, so they generally appear at the top of the code segment.
EXAMPLE 8–10
MOVE

MACRO
PUSH
MOV
MOV
POP
ENDM

A,B
AX
AX,B
A,AX
AX

MOVE VAR1,VAR2
0000
0001
0004
0007

50
A1 0002 R
A3 0000 R
58

1
1
1
1

PUSH
MOV
MOV
POP

AX
AX,VAR2
VAR1,AX
AX

0008
0009
000C
000F

50
A1 0006 R
A3 0004 R
58

1
1
1
1

MOVE
PUSH
MOV
MOV
POP

VAR3,VAR4
AX
AX,VAR4
VAR3,AX
AX

;;move VAR2 into VAR1

;;move VAR4 into VAR3

Local Variables in a Macro. Sometimes, macros contain local variables. A local variable is one
that appears in the macro, but is not available outside the macro. To define a local variable, we use
the LOCAL directive. Example 8–11 shows how a local variable, used as a jump address, appears
in a macro definition. If this jump address is not defined as local, the assembler will flag it with
errors on the second and subsequent attempts to use the macro.
EXAMPLE 8–11
FILL

MACRO
LOCAL
PUSH
PUSH
MOV
MOV
MOV
FILL1: MOV
INC
LOOP
POP
POP
ENDM
FILL

0014
0015
0016
0019
001C
0029

56
51
BE
B9
B0
88

0000 R
0005
00
04

1
1
1
1
1
1
1

LOCAL
PUSH
PUSH
MOV
MOV
MOV
??0000:MOV

WHERE, HOW_MANY
FILL1
SI
CX
SI,OFFSET WHERE
CX,HOW_MANY
AL,0
[SI],AL
SI
FILL1
CX
SI

MES1,5
FILL1
SI
CX
SI,OFFSET MES1
CX,5
AL,0
[SI],AL

;;fill memory

259

PROGRAMMING THE MICROPROCESSOR
002B
002C
002E
002F

0030
0031
0032
0035
0038
003A
003C
003D
003F
0040

46
E2 FB
59
5E

56
51
BE
B9
B0
88
46
E2
59
5E

0014 R
000A
00
04
FB

1
1
1
1

1
1
1
1
1
1
1
1
1
1
1

INC
LOOP
POP
POP

SI
??0000
CX
SI

FILL

MES2,10

LOCAL
PUSH
PUSH
MOV
MOV
MOV
??0001:MOV
INC
LOOP
POP
POP
.EXIT

FILL1
SI
CX
SI,OFFSET MES2
CX,10
AL,0
[SI],AL
SI
??0001
CX
SI

Example 8–11 shows a FILL macro that stores any number (parameter HOW_MANY) of
00H into the memory location addressed by parameter WHERE. Notice how the address FILL1
is treated when the macros are expanded. The assembler uses labels that start with ?? to designate
them are assembler-generated labels.
The LOCAL directive must always be used on the line immediately following the MACRO
statement or an error occurs. The LOCAL statement may have up to 35 labels, all separated with
commas.

Placing MACRO Definitions in Their Own Module. Macro definitions can be placed in the program
file as shown, or they can be placed in their own macro module. A file can be created that contains
only macros to be included with other program files. We use the INCLUDE directive to indicate that
a program file will include a module that contains external macro definitions. Although this is not a
library file, for all practical purposes it functions as a library of macro sequences.
When macro sequences are placed in a file (often with the extension INC or MAC), they do
not contain PUBLIC statements as does a library. If a file called MACRO.MAC contains macro
sequences, the INCLUDE statement is placed in the program file as INCLUDE
C:\ASSM\MACRO.MAC. Notice that the macro file is on drive C, subdirectory ASSM in this
example. The INCLUDE statement includes these macros, just as if you had typed them into the
file. No EXTRN statement is needed to access the macro statements that have been included.
Programs may contain both macro include files and library files.

8–2

USING THE KEYBOARD AND VIDEO DISPLAY
Today, there are few programs that don’t use the keyboard and video display. This section of the
text explains how to use the keyboard and video display connected to the IBM PC or any compatible computer running under Windows.

Reading the Keyboard
The keyboard of the personal computer is read by many different objects available to Visual
C++. Data read from the keyboard are either in ASCII-coded or in extended ASCII-coded form.
They are then either stored in 8-bit ASCII form or in 16-bit Unicode form. As mentioned in an
earlier chapter, Unicode contains ASCII code in the codes 0000H–00FFH. The remaining codes
are used for foreign language character sets. Do not use cin or getch to read keys in Visual C++
as we do in a DOS C++ console application; in place of cin or getch we use controls in Visual
C++ that accomplish the same task.

260

CHAPTER 8

TABLE 8–1 The keyboard
scanning and extended ASCII
codes as returned from the
keyboard.

Extended ASCII code with....
Key

Scan Code

Esc
1
2
3
4
5
6
7
8
9
0
+
Bksp
Tab
Q
W
E
R
T
Y
U
I
O
P
[
]
Enter
Enter
Lctrl
Rctrl
A
S
D
F
G
H
J
K
L
;


Lshft
\

01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1C
1D
1D
1E
1F
20
21
22
23
24
25
26
27
28
29
2A
2B

Nothing

Shift

Control

03

0F

94

Alternate
01
78
79
7A
7B
7C
7D
7E
7F
80
81
82
83
0E
A5
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
A6

1E
1F
20
21
22
23
24
25
26
27
28
29

(continued on next page)

261

PROGRAMMING THE MICROPROCESSOR

TABLE 8–1

(continued)
Extended ASCII code with....
Key

Scan Code

Z
X
C
V
B
N
M
,
.
/
Gray/
Rshft
PrtSc
L alt
R alt
Space
Caps
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
Num
Scroll
Home
Up
Pgup
GrayLeft
Center
Right
Gray +
End
Down
Pgdn
Ins
Del
Pause

2C
2D
2E
2F
30
31
32
33
34
35
35
36
E0 2A E0 37
38
38
39
3A
3B
3C
3D
3E
3F
40
41
42
43
44
57
58
45
46
E0 47
48
E0 49
4A
4B
4C
4D
4E
E0 4F
E0 50
E0 51
E0 52
E0 53
E0 10 45

Nothing

Shift

Control

Alternate

95

2C
2D
2E
2F
30
31
32
33
34
35
A4

3B
3C
3D
3E
3F
40
41
42
43
44
85
86

54
55
56
57
58
59
5A
5B
5C
5D
87
88

5E
5F
60
61
62
63
64
65
66
67
89
8A

68
69
6A
6B
6C
6D
6E
6F
70
71
8B
8C

47
48
49

47
48
49

77
8D
84

97
98
99

4B

4B

73

9B

4D

4D

74

9D

4F
50
51
52
53

4F
50
51
52
53

75
91
76
92
93

9F
A0
A1
A2
A3

262

CHAPTER 8

FIGURE 8–1 Using the
textbox with filtering.

The ASCII-coded data appear as outlined in Table 1–8 in Section 1–4. The extended character set of Table 1–9 applies to printed or displayed data only, and not to keyboard data. Notice
that the ASCII codes in Table 1–8 correspond to most of the keys on the keyboard. Also available
through the keyboard are extended ASCII-coded keyboard data. Table 8–1 lists most of the
extended ASCII codes obtained with various keys and key combinations. Notice that most keys
on the keyboard have alternative key codes. Each function key has four sets of codes selected by
the function key alone, the Shift-function key combination, the alternate-function key combination, and the Control-function key combination.
Creating a Visual C++ Express application that contains a simple textbox gives a better
understanding of reading a key in Windows. Figure 8–1 shows such an application written as a
forms-based application. Recall that to create a forms-based application:
1. Start Visual C++ Express.
2. Click on Create: Project.
3. Select a CLR Windows Forms Application, then give it a name and click on OK.
Once the new forms-based application is created, select the textbox control from the toolbox and draw it on the screen of the dialog box, as illustrated in Figure 8–1.

Setting Focus. The first thing that should be added to the application is a set focus to the
textbox control. When focus is set, the cursor moves to the object, in this case the textbox. Focus
is set to a control by using textBox1->Focus(), which in our case is because the textbox control
is named textBox1. This statement is placed in the Form1_Load function, which must be
installed by double-clicking on a blank area of the form. The Form1_Load function can also be
installed by clicking on the yellow lightning bolt and selecting Load and then adding it by doubleclicking on the blank textbox to its right. The application will now set focus to the textbox1 control when started. This means that the blinking cursor appears inside the textbox control.
When the application is executed and keys are typed into the textbox control, the program
reads the keyboard and displays each character as it is typed. In some cases this may be undesirable and may require some filtering. One such case is if the program requires that the user enter
only hexadecimal data. In order to intercept keystrokes as they are typed, the event handlers

PROGRAMMING THE MICROPROCESSOR

263

KeyDown and KeyPress are used for the textbox. The KeyDown event handler is called when the
key is pressed down, which is followed by a call to the KeyPress event handler. To insert these
functions into the application for the textbox control, click on the textbox and then select the
Properties window. Next find the yellow lightning bolt and click on it, and install KeyDown and
KeyPress events handlers for the textbox1 control.
To illustrate filtering, this application uses the KeyDown function to look at each keyboard
character that is typed before the program uses the keystroke. This allows the characters to be
modified. Here the program only allows the numbers 0 through 9 and the letters A through F to
be typed from the keyboard. If a lowercase letter is typed, it is converted into uppercase. If any
other key is typed, it is ignored.
To accomplish filtering, use the KeyEventArgsˆ class argument e, which is passed to the
KeyDown functon as illustrated in Example 8–12. In this example, C++ is used to accomplish the task
of filtering the keyboard entry into the textbox control. The variable keyHandled is used to indicate
whether or not the key is handled by the filtering. If keyHandled is set to false, the key has not been handled and it will appear in the textbox. Likewise, if keyhandled is set to true, the key has been handled
and will not appear in the textbox. The condition of keyHandled is passed to Windows in the KeyPress
event that also appears in Example 8–12. Note that Keys::D0 through Keys::D9 are the number keys on
the QWERTY keyboard and Keys::NumPad0 through Keys::NumPad9 are on the numeric keypad. A
D8 with the shift equal to false is the eight key and a D8 with shift equal to true is an asterisk key.
EXAMPLE 8–12
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
textBox1->Focus();
// set Focus to textbox1
}
bool keyHandled;
private: System::Void textBox1_KeyDown(System::Objectˆ sender,
System::Windows::Forms::KeyEventArgsˆ e)
{
// this is called first
keyHandled = true;
if (e->KeyCode >= Keys::NumPad0 && e->KeyCode <= Keys::NumPad9 ||
e->KeyCode >= Keys::D0 && e->KeyCode <= Keys::D9 &&
e->Shift == false ||
e->KeyCode >= Keys::A && e->KeyCode <= Keys::F ||
e->KeyCode == Keys::Back)
{
keyHandled = false;
}
}
private: System::Void textBox1_KeyPress(System::Objectˆ sender,
System::Windows::Forms::KeyPressEventArgsˆ e)
{
// this is called second
if (e->KeyChar >= ‘a’ && e->KeyChar <= ‘f’)
{
e->KeyChar -= 32;
// make uppercase
}
e->Handled = keyHandled;
}

The if statement in KeyPress event tests the e->KeyCode value for the letters a, b, c d, e,
and f, which can be either uppercase or lowercase. The KeyDown event tests for the numbers 0–9
on both the keyboard and the number pad. Also the backspace key is tested. If any of these are
typed, keyHandled is set to false to indicate that these keys are not handled by the KeyDown
function. The KeyPress event determines if the letter a–f is typed and converts it into uppercase
by subtracting 32. The 32 is the bias between uppercase and lowercase letters in the ASCII code.

264

CHAPTER 8

Next, a return with e->Handled set into true or false occurs. A return true causes Windows to dispose of the keystroke. In this case, if a number is typed, or the letters a through f or A through F,
the keystroke is passed to Windows framework by the normal return false at the end of the
KeyPress function. This filters the keystrokes so only A–F or 0–9 appears in the edit box.
Example 8–12 is repeated using the inline assembler to accomplish the same task in
Example 8–13. Here a function, called filter, returns a true or false that is passed to keyHandled
in the KeyDown function. In this example, C++ seems to require less typing than assembly language, but it is important to be able to visualize both forms. Don’t forget to change the project
property, Common Language Runtime Support to /CLR, so this will function correctly (see
Chapter 7). Notice that KeyValue is used with the assembly version to pass a char to the Filter
function. Also note that an integer return value of 0 is false and 1 is true.
EXAMPLE 8–13
// Placed at the top of the program following the uses statements
int Filter(char key)
{
int retval;
// 0 = false, 1 = true
_asm
{
mov eax,1
cmp key,8
; backspace
jb good
cmp key,30h
jb bad
cmp key,39h
jbe good
cmp key,41h
jb bad
cmp key,46h
jbe good
cmp key,61h
jb bad
cmp key,66h
jbe good
good:
dec eax
bad:
mov retval,eax
}
return retval;
}
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
textBox1->Focus();
}
bool keyHandled;
// new version of textbox1_KeyDown
private: System::Void textBox1_KeyDown(System::Objectˆ sender,
System::Windows::Forms::KeyEventArgsˆ e)
{
keyHandled = Filter(e->KeyValue);
}
private: System::Void textBox1_KeyPress(System::Objectˆ sender,
System::Windows::Forms::KeyPressEventArgsˆ e)
{
if (e->KeyChar >= ‘a’ && e->KeyChar <= ‘f’)
{
e->KeyChar -= 32;
}
e->Handled = keyHandled;
}

PROGRAMMING THE MICROPROCESSOR

265

FIGURE 8–2 Hexadecimal
to decimal conversion.

If this code is added to the application and executed, the only keys that will appear in the
textbox control are 0–9 and A–F. Any amount of filtering can be done in a likewise manner in the
Visual C++ Express environment. The properties of the textbox control include character casing,
which could have been set to uppercase to shorten the filtering task, but here software accomplished the uppercase feature.

Using the Video Display
As with the keyboard, in Visual C++ objects are used to display information. The textbox control
can be used to either read data or display data as can most objects. Modify the application presented in Figure 8–1 so it contains an additional textbox control as shown in Figure 8–2. Notice
that a few label controls have been added to the form to identify the contents of the textbox controls. In this new application the keyboard data is still read into textbox control textBox1, but
when the Enter key is typed, a decimal version of the data entered into textBox1 appears in
textBox2—the second textbox control. Make sure that the second control is named textBox2 to
maintain compatibility with the software presented here.
To cause the program to react to the Enter key, ASCII code 13 (0DH or 0x0d), modify the
KeyPress function of Example 8–13 as shown in Example 8–14. Notice how the Enter key is
detected using an else if. Once the Enter key is detected, the contents of textBox1 are converted
to decimal for display in textBox2, as shown in Example 8–15.
EXAMPLE 8–14
private: System::Void textBox1_KeyPress(System::Objectˆ sender,
System::Windows::Forms::KeyPressEventArgsˆ e)
{
if (e->KeyChar >= ‘a’ && e->KeyChar <= ‘f’)
{
e->KeyChar -= 32;
}
else if (e->KeyChar == 13)
{

266

CHAPTER 8
// software to display the decimal version in textBox2
keyHandled = true;
}
e->Handled = keyHandled;
}

A slight problem arises with textbox data; the data entered into a textbox control is
accessed as a string, but not as a hexadecimal number. In this example program (see Example
8–15), a function called Converts changes the hexadecimal character string into a number. The
program now has two functions that contain assembly code.
EXAMPLE 8–15
// placed after the using statements at the top of the program
int Filter(char key)
{
int retval;
_asm
{
mov eax,1
cmp key,8
je good
cmp key,30h
jb bad
cmp key,39h
jbe good
cmp key,41h
jb bad
cmp key,46h
jbe good
cmp key,61h
jb bad
cmp key,66h
jbe good
good:
dec al
bad:
mov retval,eax
}
return retval;
}

; backspace

int Converts(int number, short digit)
{
_asm
{
mov eax,number
shl eax,4
mov dx,digit
sub dx,30h
cmp dx,9
jbe later
sub dx,7
later:
or al,dl
mov number,eax
}
return number;
}
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
textBox1->Focus();
}
bool keyHandled;

PROGRAMMING THE MICROPROCESSOR

267

private: System::Void textBox1_KeyDown(System::Objectˆ sender,
System::Windows::Forms::KeyEventArgsˆ e)
{
keyHandled = Filter(e->KeyValue);
}
private: System::Void textBox1_KeyPress(System::Objectˆ sender,
System::Windows::Forms::KeyPressEventArgsˆ e)
{
if (e->KeyChar >= ‘a’ && e->KeyChar <= ‘f’)
{
e->KeyChar -= 32;
}
else if (e->KeyChar == 13)
{
int number = 0;
for (int a = 0; a < textBox1->Text->Length; a++)
{
number = Converts(number, textBox1->Text[a]);
}
textBox2->Text = Convert::ToString(number);
keyHandled = true;
}
e->Handled = keyHandled;
}

Example 8–15 shows the completed application. When the Enter key is pressed, the program obtains the character string from textBox1 and converts it, a character at a time, into an
integer using the Converts function. Once an integer is created, it is changed into a character
string using the Convert class and its member function ToString for display in textBox2.
The assembly language in the Converts function converts from ASCII to binary by subtracting 30h from each digit. This action converts ASCII numbers (0–9) 30H through 39H to the
binary numbers 0 through 9. It does not convert the letters 41H through 46H (A through F) to
binary because the result is 11H through 16H and not 0AH through 0FH. To adjust the values
obtained for the letters, use a cmp (compare) instruction to detect 11H through 16H and then
subtract an additional 7 to convert from 11H through 16H to 0AH to 0FH. Once the ASCII digit
is in binary form, the integer at number is shifted left four binary places and the binary version of
the ASCII digit is ORed to it to accumulate the converted digits in temp1.
Once the hexadecimal number from textBox1 is converted to binary in variable number, it
is displayed in textBox2 using the ToString function in the Convert class. As before, a return true
informs the Windows interface that the Enter key has been processed by the program. If a return
false is used in KeyPress for the Enter key, the computer generates an error beep.

Using a Timer in a Program
Timers are important in programming. A timer is programmed to fire or trigger after a known
amount of time, which is given in milliseconds. The allowable range for the timer is from 1 to 2G
milliseconds. This allows a timer to be programmed for just about any time needed. If programmed
with 1000, the timer fires in 1 second, and if programmed with 60000, the timer fires in 1 minute and
so forth. A timer may be used a single time or multiple times and as many timers as needed (up to
2 billion) may appear in a program. The timer is found in the toolbox at the Design window.
To illustrate the timer and its use in a program, a design appears in Figure 8–3 that allows
a user to demonstrate a shift or a rotate on a binary number. The shift or rotate is animated with
the timer in this program. The design contains two label controls and two button controls plus the
timer control. Add all five of these controls to the application. The timer does not appear on the
form, but in an area near the bottom of the design screen. Notice that a few properties of the form
are changed so the icon is not shown and the Text property of the form is changed to Shift/Rotate
instead of Form1. Also the Text properties of the labels and buttons are changed as indicated.

268

CHAPTER 8

FIGURE 8–3 The Shift/
Rotate application design
screen.

Once the form appears as shown in Figure 8–3, add event handler functions ( yellow lightning bolt) for the two command buttons (Click) and for the timer (Tick). To add an event handler,
click on the button or timer, go to the Properties window at the right of the screen, and click on
the yellow lightning bolt and select the event. The program contains three handlers, two for button clicks and one for a timer tick.
At the design screen go to the properties of the timer and set the interval to 500, which is 1⁄2
second. Do not enable the timer. The timer is enabled in the software when a button is clicked to
either rotate a number or shift a number in 1⁄2-second animation steps.
Example 8–16 illustrates the software added to the application in the three handlers
required to implement the application for Figure 8–3. The software for the two button click handlers is nearly identical except for the Boolean variable shift. The two statements in each place
text onto the labels. If the shift button is pressed, “Shifted” is displayed, and if the rotate button
is pressed, “Rotated” is displayed on label1. The second label has the test number 00011001 displayed. The Boolean variable shift is set to true for the shift button and false for the rotate button.
In both button handlers, count is set to 8 to shift or rotate the number 8 places. Finally, the last
statement in each button handler starts the timer. As an alternative, the enabled member could be
set to true to start the timer. Once the timer is started or enabled, it fires in 1⁄2 second and calls the
Tick handler for timer1 where all the work is accomplished in this program.
The first statement in the timer tick function sets the digit string to zero. This is the number
that shifts into the right end of label2. For a shift, it will remain a zero and for a rotate it depends
on the leftmost digit of the number in label2. If shift is false (rotate) and the leftmost digit of
label2 is 1, the digit is changed to 1. After the if statement, the number is shifted or rotated and
placed on label2. If the count reaches 0, after 8 iterations in 4 seconds, the timer is disabled by
setting the enabled member to false to stop the animation.
EXAMPLE 8–16
bool shift;
int count;
private: System::Void button1_Click(System::Objectˆ sender,
System::EventArgsˆ e)
{

269

PROGRAMMING THE MICROPROCESSOR
label1->Text = “Shifted”;
label2->Text = “00011001”;
shift = true;
count = 8;
timer1->Start();
}
private: System::Void button2_Click(System::Objectˆ sender,
System::EventArgsˆ e)
{
label1->Text = “Rotated”;
label2->Text = “00011001”;
shift = false;
count = 8;
timer1->Start();
}
private: System::Void timer1_Tick(System::Objectˆ sender,
System::EventArgsˆ e)
{
Stringˆ digit = “0”;
if (shift == false && label2->Text[0] == ‘1’)
{
digit = “1”;
}
label2->Text = label2->Text->Remove(0,1) + digit;
if (—count == 0)
{
timer1->Enabled = false;
}
}

This application is embellished by adding a pair of radio buttons to select right and left for
the direction of the shift or rotate. Label2 can also be replaced with a textbox so the user could
enter any binary number (with appropriate filtering) and shift or rotate it left or right.
The program does not use any assembly language, but if a breakpoint is inserted in the
timer function, the assembly code can be viewed in Visual C++ Express. When the program
Breaks, go to the Debug menu and select Windows. Next, select the Disassembly window to
show the assembly language for the timer tick function.

The Mouse
The mouse pointing device, as well as a track ball, is accessed from within the framework of
Visual C++ Express. Like many other devices under the control of Windows, the mouse can have
message handlers installed in an application so that mouse movement and other functions can be
used in a program. As we saw in prior examples, message handlers (event handlers) are installed
in an application in the Properties section by clicking on the icon to the right of the lightning bolt.
The mouse handlers are listed in Table 8–2.
For an illustration of how to use the mouse, refer to the example application presented in
Figure 8–4. This example shows the mouse pointer coordinates as the pointer is moved within
TABLE 8–2 Mouse
message handlers.

Handler
MouseDown
MouseEnter
MouseHover
MouseLeave
MouseMove
MouseUp

Trigger
Mouse button down
Mouse pointer enters the control
Mouse not moved for awhile
Mouse pointer leaves the control
Mouse moved
Mouse button released

270

CHAPTER 8

FIGURE 8–4 Displaying
the mouse coordinates.

the dialog application. Although the software listing (see Example 8–17) does not use any
assembly language, it does illustrate how to obtain and display the position of the mouse pointer.
Notice how the MouseEventArgsˆ are used to obtain the location of the mouse pointer using the
X and Y coordinates.
EXAMPLE 8–17
private: System::Void Form1_MouseMove(System::Objectˆ sender,
System::Windows::Forms::MouseEventArgsˆ e)
{
label1->Text = “X-coordinate = ” + Convert::ToString(e->Location.X);
label2->Text = “Y-coordinate = ” + Convert::ToString(e->Location.Y);
}

Example 8–17 illustrates the only part of the application that is modified to display the
mouse coordinates. The MouseMove function is installed when the MouseMove event handler is
installed in the program. This application uses two labels to display the mouse coordinates.
These two objects are named label1 and label2 for the application. The MouseMove function
returns the position of the mouse pointer in the Location data structure as members X and Y. This
example uses the Convert class to convert the integer returned as the mouse point X or Y into an
ASCII character string for placement on a label.
The mouse pointer does not track through the two labels in the application. In order to have
the application display the coordinates in the labels as well as the form, two additional
MouseMove handlers must be installed for the labels. Example 8–18 shows the addition of two

PROGRAMMING THE MICROPROCESSOR

271

more MouseMove functions and biases added to the X and Y coordinates so the mouse tracks
through the labels. Where are the bias numbers obtained? The biases are in the Location properties of each label where the label position is given as X, Y. The numbers in the application
will depend on where the labels are placed on the form and could be obtained by using
label1->Location.X and so forth.
EXAMPLE 8–18
private: System::Void Form1_MouseMove(System::Objectˆ sender,
System::Windows::Forms::MouseEventArgsˆ ’e)
{
label1->Text = “X-coordinate = ” + Convert::ToString(e->Location.X);
label2->Text = “Y-coordinate = ” + Convert::ToString(e->Location.Y);
}
private: System::Void label1_MouseMove(System::Objectˆ sender,
System::Windows::Forms::MouseEventArgsˆ e)
{
label1->Text = “X-coordinate = ” + Convert::ToString(e->Location.X+159);
label2->Text = “Y-coordinate = ” + Convert::ToString(e->Location.Y+232);
}
private: System::Void label2_MouseMove(System::Objectˆ sender,
System::Windows::Forms::MouseEventArgsˆ e)
{
label1->Text = “X-coordinate = ” + Convert::ToString(e->Location.X+159);
label2->Text = “Y-coordinate = ” + Convert::ToString(e->Location.Y+246);
}

Install a mouse handler for the MouseDown event. Modify the application by adding the
MouseDown event handler as illustrated in Example 8–19. The function causes the left button to
change the color of the labels to red when clicked and the right button changes the color of the
labels to blue when pushed. The color codes used with most functions in Visual C++ are found
in the Color class for most common colors. The application tests for the left and right mouse buttons using the Button member of the MouseEventArgs object as shown. (Microsoft chose the
name mouses for the MouseButtons enumerator.)
EXAMPLE 8–19
private: System::Void Form1_MouseDown(System::Objectˆ sender,
System::Windows::Forms::MouseEventArgsˆ e)
{
if (e->Button == ::mouses::MouseButtons::Left)
{
label1->ForeColor = Color::Red;
label2->ForeColor = Color::Red;
}
else if (e->Button == ::mouses::MouseButtons::Right)
{
label1->ForeColor = Color::Blue;
label2->ForeColor = Color::Blue;
}
}

8–3

DATA CONVERSIONS
In computer systems, data are seldom in the correct form. One main task of the system is to convert data from one form to another. This section of the chapter describes conversions between
binary and ASCII data. Binary data are removed from a register or memory and converted to
ASCII for the video display. In many cases, ASCII data are converted to binary as they are typed
on the keyboard. We also explain converting between ASCII and hexadecimal data.

272

CHAPTER 8

Converting from Binary to ASCII
Conversion from binary to ASCII is accomplished in three ways: (1) by the AAM instruction if
the number is less than 100 (provided the 64-bit extensions are not used for the conversion), (2)
by a series of decimal divisions (divide by 10), or (3) by using the C++ Convert class function
ToString. Techniques 1 and 2 are presented in this section.
The AAM instruction converts the value in AX into a two-digit unpacked BCD number in
AX. If the number in AX is 0062H (98 decimal) before AAM executes, AX contains 0908H after
AAM executes. This is not ASCII code, but it is converted to ASCII code by adding 3030H to
AX. Example 8–20 illustrates a program that uses the procedure that processes the binary value
in AL (0–99) and displays it on the video screen as a decimal number. The procedure blanks a
leading zero, which occurs for the numbers 0–9, with an ASCII space code. This example program displays the number 74 (testdata) on the video screen. To implement this program, create
a forms-based application in Visual C++ and place a single label called label1 on the form. The
number 74 will appear if the assembly language function in Example 8–20 is placed at the top of
the program after the last using statement and the project is changed to a /CLR program. The call
to the assembly language function is placed in the Load event handler for the form.
EXAMPLE 8–20
// place at top of program
// will not function in 64-bit mode
void ConvertAam(char number, char* data)
{
_asm
{
mov ebx,data
;pointer to ebx
mov al,number
;get test data
mov ah,0
;clear AH
aam
;convert to BCD
add ah,20h
cmp al,20h
;test for leading zero
je D1
;if leading zero
add ah,10h
;convert to ASCII
D1:
mov [ebx], ah
add al,30h
;convert to ASCII
mov [ebx+1], al
}
}

private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
char temp[2];
// place for result
ConvertAam(74, temp);
Char a = temp[0];
Char b = temp[1];
label1->Text = Convert::ToString(a) + Convert::ToString(b);
}

The reason that AAM converts any number between 0 and 99 to a two-digit unpacked
BCD number is because it divides AX by 10. The result is left in AX so AH contains the quotient
and AL the remainder. This same scheme of dividing by 10 can be expanded to convert any
whole number of any number system (if the divide-by number is changed) from binary to an
ASCII-coded character string that can be displayed on the video screen. For example, if AX is
divided by 8 instead of 10, the number is displayed in octal.

273

PROGRAMMING THE MICROPROCESSOR

The algorithm (called Horner’s algorithm) for converting from binary to decimal ASCII
code is:
1. Divide by 10, then save the remainder on the stack as a significant BCD digit.
2. Repeat step 1 until the quotient is a 0.
3. Retrieve each remainder and add 30H to convert to ASCII before displaying or printing.
Example 8–21 shows how the unsigned 32-bit number is converted to ASCII and displayed
on the video screen. Here, we divide EAX by 10 (for decimal) and save the remainder on the
stack after each division for later conversion to ASCII. After all the digits have been converted,
the result is displayed on the video screen by removing the remainders from the stack and converting them to ASCII code. This program also blanks any leading zeros that occur. As mentioned, any number base can be used by changing the radix variable in this example. Again, to
implement this example create a forms application with the /CLR option and a single label called
label1. If the number base is greater than 10, letters are used for the representation of characters
beyond 9. The software functions from base 2 to base 36.
EXAMPLE 8–21
void Converts(int number, int radix,
{
_asm
{
mov
ebx,data
push radix
mov
eax, number
L1:
mov
edx,0
div
radix
push edx
cmp
eax,0
jnz
L1
L2:
pop
edx
cmp
edx,radix
je
L4
add
dl,30h
cmp
dl,39h
jbe
L3
add
dl,7
L3:
mov
[ebx],dl
inc
ebx
jmp
l2
L4:
mov
byte ptr[ebx],0
}
}

char* data)

;initialize pointer
;get test data
;clear edx
;divide by base
;save remainder
;repeat until 0
;get remainder
;if finished
;convert to ASCII

;save digit
;point to next
;repeat until done
;save null in string

private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
char temp[32];
// place for result
Converts(7423, 10, temp);
Stringˆ a = “”;
int count = 0;
while (temp[count] != 0)
// convert to string
{
Char b = temp[count++];
a += b;
}
label1->Text = a;
}

274

CHAPTER 8

Converting from ASCII to Binary
Conversions from ASCII to binary usually start with keyboard entry. If a single key is typed,
the conversion occurs when 30H is subtracted from the number. If more than one key is
typed, conversion from ASCII to binary still requires 30H to be subtracted, but there is one
additional step. After subtracting 30H, the number is added to the result after the prior result
is first multiplied by 10.
The algorithm for converting from ASCII to binary is:
1.
2.
3.
4.

Begin with a binary result of 0.
Subtract 30H from the character to convert it to BCD.
Multiply the result by 10, and then add the new BCD digit.
Repeat steps 2 and 3 for each character of the number.

Example 8–22 illustrates a program that implements this algorithm. Here, the binary number is displayed from variable temp on label1 using the Convert class to convert it to a string.
Each time this program executes, it reads a number from the char variable array numb and converts it to binary for display on the label.
EXAMPLE 8–22
int ConvertAscii(char* data)
{
int number = 0;
_asm
{
mov ebx,data
mov ecx,0
B1:
mov cl,[ebx]
inc ebx
cmp cl,0
je
B2
sub cl,30h
mov eax,10
mul number
add eax,ecx
mov number,eax
jmp B1
B2:
}
return number;
}

;intialize pointer

;get digit
;address next digit
;if null found
;convert from ASCII to BCD
;x10
;add digit
;save result

private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
char temp[] = “2316”;
// string
int number = ConvertAscii(temp);
label1->Text = Convert::ToString(number);
}

Displaying and Reading Hexadecimal Data
Hexadecimal data are easier to read from the keyboard and display than decimal data. These
types of data are not used at the application level, but at the system level. System-level data are
often hexadecimal, and must either be displayed in hexadecimal form or read from the keyboard
as hexadecimal data.

PROGRAMMING THE MICROPROCESSOR

275

Reading Hexadecimal Data. Hexadecimal data appear as 0 to 9 and A to F. The ASCII
codes obtained from the keyboard for hexadecimal data are 30H to 39H for the numbers
0 through 9, and 41H to 46H (A–F) or 61H to 66H (a–f) for the letters. To be useful, a program that reads hexadecimal data must be able to accept both lowercase and uppercase letters
as well as numbers.
Example 8–23 shows two functions: One (Conv) converts the contents of an unsigned
char from ASCII code to a single hexadecimal digit, and the other (Readh) converts a String
with up to eight hexadecimal digits into a numeric value that is returned as a 32-bit unsigned
integer. This example illustrates a balanced mixture of C++ and assembly language to perform
the conversion.
EXAMPLE 8–23
unsigned char Conv(unsigned char temp)
{
_asm
{
cmp temp,‘9’
jbe Conv2
;if 0 – 9
cmp temp,‘a’
jb
Conv1
;if A - F
sub temp,20h
;to uppercase
Conv1:
sub temp,7
Conv2:
sub temp,30h
}
return temp;
}

private: System::UInt32 ReadH(Stringˆ temp)
{
unsigned int numb = 0;
for ( int a = 0; a < temp->Length; a++ )
{
numb <= 4;
numb += Conv(temp[a]);
}
return numb;
}

private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
unsigned int temp = ReadH(“2AB4”);
label1->Text = Convert::ToString(temp);
// display in decimal
}

Displaying Hexadecimal Data. To display hexadecimal data, a number must be divided into 2-,
4-, or 8-bit sections that are converted into hexadecimal digits. Conversion is accomplished by
adding 30H to the numbers 0 to 9 or 37H to the letters A to F for each section.
A function (Disph) stores a string of the contents of the unsigned integer parameter
passed to the function. This function converts the unsigned into a two-, four-, or eight-digit
character string as selected by parameter size. The function is listed in Example 8–24.
Disph(number, 2) converts an unsigned integer number into a two-digit hexadecimal String,
where Disph(number, 4) converts it to a four-digit hexadecimal string and Disph(number, 8)
converts to an eight-digit hexadecimal character string.

276

CHAPTER 8

EXAMPLE 8–24
void Disph(unsigned int number, unsigned int size, char* temp)
{
int a;
number //adjust position
for (a = 0; a < size; a++)
{
char temp1;
_asm
{
rol number, 4;
mov al,byte ptr number
and al,0fh
;make 0 – f
add al,30h
;convert to ASCII
cmp al,39h
jbe Disph1
add al,7
Disph1:
mov temp1,al
}
temp[a] = temp1;
//add digit to string
}
temp[a] = 0;
// null string end
}
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
char temp[9];
Disph(1000,4,temp);
Stringˆ a = “”;
int count = 0;
while (temp[count] != 0)
// convert to string
{
Char b = temp[count++];
a += b;
}
label1->Text = a;
}

Using Lookup Tables for Data Conversions
Lookup tables are often used to convert data from one form to another. A lookup table is formed
in the memory as a list of data that is referenced by a procedure to perform conversions. In many
lookup tables, the XLAT instruction is often used to look up data in a table, provided that the
table contains 8-bit-wide data and its length is less than or equal to 256 bytes.

Converting from BCD to Seven-Segment Code. One simple application that uses a lookup table
is BCD to seven-segment code conversion. Example 8–25 illustrates a lookup table that contains
the seven-segment codes for the numbers 0 to 9. These codes are used with the seven-segment
display pictured in Figure 8–5. This seven-segment display uses active high (logic 1) inputs to
light a segment. The lookup table code (array temp1) is arranged so that the a segment is in bit
position 0 and the g segment is in bit position 6. Bit position 7 is 0 in this example, but it can be
used for displaying a decimal point, if required.
EXAMPLE 8–25
unsigned char LookUp(unsigned char temp)
{
unsigned char temp1[] = {0x3f, 6, 0x5b, 0x4f, 0x66,
0x6d, 0x7d, 7, 0x7f, 0x6f};
_asm
{
lea ebx,temp1
mov al,temp

277

PROGRAMMING THE MICROPROCESSOR

FIGURE 8–5 The sevensegment display.

a
f

b
Control byte
0 g

f e d

c b a

g
e

c
d

xlat
mov temp,al
}
return temp;
}

The LookUp function, which performs the conversion, contains only a few instructions
and assumes that the temp parameter contains the BCD digit (0–9) to be converted to sevensegment code that is returned as an unsigned char. The first instruction addresses the lookup table
by loading its address into EBX, and the others perform the conversion and return the sevensegment code as an unsigned char. Here the temp1 array is indexed by the BCD passed to the
function in temp.

Using a Lookup Table to Access ASCII Data. Some programming techniques require that
numeric codes be converted to ASCII character strings. For example, suppose that you need to
display the days of the week for a calendar program. Because the number of ASCII characters in
each day is different, some type of lookup table must be used to reference the ASCII-coded days
of the week.
The program in Example 8–26 shows a table, formed as an array, which references
ASCII-coded character strings. Each character string contains an ASCII-coded day of the week.
The table contains references to each day of the week. The function that accesses the day of the
week uses the day parameter, with the numbers 0 to 6 to refer to Sunday through Saturday. If
day contains a 2 when this function is invoked, the word Tuesday is displayed on the video
screen. Please note that this function does not use any assembly code, since we are merely
accessing an element in an array using the day of the week as an index. It is shown so additional
uses for arrays can be presented, because they may have application in programs used with
embedded microprocessors.
EXAMPLE 8–26
private: System::Stringˆ GetDay(unsigned char day)
{
arrayˆ temp =
{
“Sunday”,
“Monday”,
“Tuesday”,
“Wednesday”,
“Thurday”,
“Friday”,
“Saturday”,
};
return temp[day];
}

278

CHAPTER 8

FIGURE 8–6 A sevensegment display.

An Example Program Using a Lookup Table
Figure 8–6 shows the screen of a dialog application called Display that displays the a sevensegment-style character on the screen for each numeric key typed on the keyboard. As we
learned in prior examples, the keyboard can be intercepted in a Visual C++ program using the
KeyDown and KeyPress handler functions, which is exactly what the program does to obtain the
key from the keyboard. Next the code typed is filtered so only 0–9 are accepted and a lookup
table is used to access the seven-segment code for display.
The display digit is drawn using panel control objects. The horizontal bars are drawn using
dimensions 120 × 25 and the vertical bars are drawn using dimensions 25 × 75. The dimensions
of an object appear in the extreme lower right corner of the resource screen in Visual Studio.
Make sure that you add the panels in the same order as the display; that is, add label a first,
followed by b, and so on, just as in the seven-segment display of Figure 8–5. Use panel1 through
panel7 for the variable names of the panels in this application and don’t forget to select a background color of black.
Add the function listed in Example 8–27 called Clear to the program to clear the display.
This is used to clear the digit from the screen when the program first executes and also before a
new digit is displayed. Notice that the Visible property of the panels is used to hide the digit. An
alternate method changes the color of the panel.
EXAMPLE 8–27
private: System::Void Clear()
{
panel1->Visible = false;

PROGRAMMING THE MICROPROCESSOR
panel2->Visible
panel3->Visible
panel4->Visible
panel5->Visible
panel6->Visible
panel7->Visible

=
=
=
=
=
=

279

false;
false;
false;
false;
false;
false;

}

Once a key is typed, the KeyDown function (see Example 8–28) filters the keystroke
and converts the keystroke into seven-segment code using the lookup table. After converting
to seven-segment code, the ShowDigit function is called to show the digit on the screen.
The ShowDigit function tests each bit of the seven-segment code and changes the visibility of each panel to display a digit. This program does not use any assembly code for its
operation.
EXAMPLE 8–28
private: System::Void Clear()
{
panel1->Visible = false;
panel2->Visible = false;
panel3->Visible = false;
panel4->Visible = false;
panel5->Visible = false;
panel6->Visible = false;
panel7->Visible = false;
}
private: System::Void Form1_KeyDown(System::Objectˆ sender,
System::Windows::Forms::KeyEventArgsˆ e)
{
char lookup[] = {0x3f, 6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 7, 0x7f, 0x6f};
if (e->KeyCode >= Keys::D0 && e->KeyCode <= Keys::D9)
{
ShowDigit(lookup[e->KeyValue - 0x30]); //display the digit
}
}
private: System::Void ShowDigit(unsigned char code)
{
Clear();
if (( code & 1 ) == 1)
//test a segment
panel1->Visible = true;
if (( code & 2 ) == 2)
//test b segment
panel4->Visible = true;
if (( code & 4 ) == 4)
//test c segment
panel5->Visible = true;
if (( code & 8 ) == 8)
//test d segment
panel3->Visible = true;
if (( code & 16 ) == 16)
//test e segment
panel6->Visible = true;
if (( code & 32 ) == 32)
//test f segment
panel7->Visible = true;
if (( code & 64 ) == 64)
//test g segment
panel2->Visible = true;
}
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
Clear();
}

280

CHAPTER 8

8–4

DISK FILES
Data are found stored on the disk in the form of files. The disk itself is organized in four main
parts: the boot sector, the file allocation table (FAT), the root directory, and the data storage
areas. The Windows NTFS (New Technology File System) contains a boot sector and a master
file table (MFT). The first sector on the disk is the boot sector, which is used to load the disk
operating system (DOS) from the disk into the memory when power is applied to the computer.
The FAT (or MFT) is where the names of files/subdirectories and their locations on the disk
are stored by the operating system. All references to any disk file are handled through
the FAT (or MFT). All other subdirectories and files are referenced through the root directory in
the FAT system. The NTFS system does not have a root directory even though the file system may
still appear to have a root directory. The disk files are all considered sequential access files, meaning that they are accessed a byte at a time, from the beginning of the file toward the end. Both the
NTFS file system and the FAT file system are in use, with the hard disk drive on most modern
Windows systems using NTFS and the floppy disk, CD-ROM, and DVD using the FAT system.

Disk Organization
Figure 8–7 illustrates the organization of sectors and tracks on the surface of the disk. This organization applies to both floppy and hard disk memory systems. The outer track is always track 0,
and the inner track is 39 (double density) or 79 (high density) on floppy disks. The inner track on
a hard disk is determined by the disk size, and could be 10,000 or higher for very large hard disks.
Figure 8–8 shows the organization of data on a disk. The length of the FAT is determined
by the size of the disk. In the NTFS system, the length of the MFT is determined by the number

Sector

Track 0

Index hole
Inner track

Drive hub

FIGURE 8–7

Structure of the disk.

281

PROGRAMMING THE MICROPROCESSOR

FIGURE 8–8 Main
data storage areas on
a disk.

Boot
MFT in NTFS

FAT

Root

Files and other directories

Track 0
Sector 0

of files stored on the disk. Likewise, the length of the root directory, in a FAT volume, is determined by the number of files and subdirectories located within it. The boot sector is always a single 512-byte-long sector located in the outer track at sector 0, the first sector.
The boot sector contains a bootstrap loader program that is read into RAM when the system is powered. The bootstrap loader then executes and loads the operating system into RAM.
Next, the bootstrap loader passes control to the operating system program, allowing the computer to be under the control of and execute Windows, in most cases. This same sequence of
events also occurs if the Linux operating system is found on the disk.
The FAT indicates which sectors are free, which are corrupted (unusable), and which contain data. The FAT table is referenced each time that the operating system writes data to the disk
so that it can find a free sector. Each free cluster is indicated by 0000H in the FAT and each occupied sector is indicated by the cluster number. A cluster can be anything from one sector to any
number of sectors in length. Many hard disk memory systems use four sectors per cluster, which
means that the smallest file is 512 bytes × 4, or 2048 bytes long. In a system that uses NTFS, the
cluster size is usually 4K bytes, which is eight sectors long.
Figure 8–9 shows the format of each directory entry in the root, or in any other directory or
subdirectory. Each entry contains the name, extension, attribute, time, date, location, and length.
The length of the file is stored as a 32-bit number. This means that a file can have a maximum
length of 4G bytes. The location is the starting cluster number.
Windows NTFS uses a much larger directory entry or record (1,024 bytes) than that of
the FAT system (32 bytes). The MFT record contains the file name, file date, attribute, and
data. The data can be the entire contents of the file, or a pointer to where the data is stored on
the disk called a file run. Generally files that are smaller than about 1500 bytes fit into the
MFT record. Longer files fit into a file run or file runs. A file run is a series of contiguous
clusters that store the file data. Figure 8–10 illustrates an MFT record in the Windows NTFS
file system. The information attribute contains the create date, last modification date, create
time, last modification time, and file attributes such as read-only, archive, and so forth. The
security attribute stores all security information for the file for limiting access to the file in the
Windows system. The header stores information about the record type, size, name (optional),
and whether it is resident or not.

File Names
Files and programs are stored on a disk and referenced both by a file name and an extension
to the file name. With the DOS operating system, the file name may only be from one to
eight characters long. The file name contains just about any ASCII character, except for spaces
or the “ \ . / [ ] * , : < > I ; ? = characters. In addition to the file name, the file can have an optional

282

CHAPTER 8
32-byte directory entry

1A

Length (high order)
(2 bytes)
Length (low order)
(2 bytes)
File cluster location
(2 bytes)

18

Date

16
15

Time

1E
1C

C

Attribute

A

Extension
(3 bytes)

0

FIGURE 8–9

Year*

16 8 4

Name
(8 bytes)

2

Month

1 32 16 8

Hours

2

1

Day

4 2 1 X X X X X

Minutes
17

Unused
(10 bytes)

B
8
7

8 4 2 1 16 8 4

Unused
16

A = Archive
D = Subdirectory
V = Volume label
S = System file
H = Hidden file
R = Read-only
*Note: year 8 = 1988, year 9 = 1989, year 10 = 1990, etc.
0

0

A D V S H R

Format of any FAT directory or subdirectory entry.

one- to three-digit extension to the file name. Note that the name of a file and its extension are
always separated by a period. If Windows 95 through Windows XP is in use, the file name can be
of any length (up to 255 characters) and can even contain spaces. This is an improvement over
the eight-character file name limitation of DOS. Also note that a Windows file can have more
than one extension.

Directory and Subdirectory Names. The DOS file management system arranges the data
and programs on a disk into directories and subdirectories. In Windows directories and subdirectories are called file folders. The rules that apply to file names also apply to file folder
names. The disk is structured so that it contains a root directory when first formatted. The root
directory or folder for a hard disk used as drive C is C:\. Any other folder is placed in the root
directory. For example, C:\DATA is folder DATA in the root directory. Each folder placed in
the root directory can also have subdirectories or subfolders. Examples are the subfolders
C:\DATA\AREA1 and C:\DATA\AREA2, in which the folder DATA contains two subfolders:
AREA1 and AREA2. Subfolders can also have additional subfolders. For example,
C:\DATA\AREA2\LIST depicts folder DATA, subfolder AREA, which contains a subfolder
called LIST.

Sequential Access Files
All DOS files and Windows files are sequential files. A sequential file is stored and accessed
from the beginning of the file toward the end, with the first byte and all bytes between it and the
last accessed to read the last byte. Fortunately, files are read and written in C++ using the File
class, which makes their access and manipulation easy. This section of the text describes how to

FIGURE 8–10 A record
in the Master File Table in
the NTFS system.

PROGRAMMING THE MICROPROCESSOR

283

create, read, write, delete, and rename a sequential access file. To gain access to the File class, a
new using must be added to the list of using statements at the top of the program. If file access is
needed, add a using namespace System::IO; statement to the program.

File Creation. Before a file can be used, it must exist on the disk. A file is created by the File
class using Create as an attribute that directs File to create a file. A file is created with create as
illustrated in Example 8–29. Here the name of the file that is created by the program is stored in
a Stringˆ called FileName. Next, the File class is used to test and see if the file already exists
before creating it. Finally, in the if statement the file is created.
In this example, if the file fails to open because the disk is full or the folder is not found,
a Windows message box displays Cannot create file followed by the file name, and an exit
from the program occurs when OK is clicked in the message box. To try this example, create a
dialog application and place the code in the Load event handler. Choose a folder name that
does not exist (test should probably work) and run the application. You should see the error
message. If you change the FileName so it does not include the folder, you will not get the
error message.
EXAMPLE 8–29
Stringˆ fileName = “C:\\Test.txt”;
if (File::Exists(fileName) == false)
{
// don’t forget using namespace System::IO;
try
{
File::Create(fileName);
}
catch (...)
{
MessageBox::Show(“Cannot create ” + fileName);
Application::Exit();
}
}
// Test.txt now exists with a length of 0 bytes

Writing to a File. Once a file exists, it can be written to. In fact, it would be highly unusual to
create a file without writing something to it. Data are written to a file one byte at a time. The
FileStream class is used to write a stream of data to the file. Data are always written starting at
the very first byte in a file. Example 8–30 lists a program that creates a file in the root directory
called Test1.txt and stores the letter A in each of its 256 bytes. If you execute this code and look
at Test1.txt with NotePad, you will see a file filled with 256 letter As. Note that the file stream
should be closed when finished using Close( ) function. Also notice in this example that an array
of size byte is created using the garbage collection class in C++. It is important to use this class
to create a managed array of data.
EXAMPLE 8–30
Stringˆ fileName = “C:\\Test1.txt”;
arrayˆ buffer = gcnew array(256);
try
{
FileStreamˆ fs = File::OpenWrite(fileName);
for (int a = 0; a < 256; a++)
{
buffer[a] = ‘A’;
}

284

CHAPTER 8
fs->Write(buffer, 0, buffer->Length);
fs->Close();
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}

Suppose that a 32-bit integer must be written to a file. Because only bytes can be written, a
method must be used to convert the four bytes of the integer into a form that can be written to a
file. In C++ shifts are used to place the byte into the proper location to store in the array.
Assembly language can also accomplish the same task in fewer bytes, as listed in Example 8–31.
If you look at the assembly code for each method, you see that the assembly language method is
much shorter and much faster. If speed and size are important, then the assembly code is by far
the best choice, although in this case the code generated by C++ is fairly efficient.
EXAMPLE 8–31
int number = 0x20000;
arrayˆ buf = gcnew array(4);
//C++ conversion
buf[0]
buf[1]
buf[2]
buf[3]

=
=
=
=

number;
number >> 8;
number >> 16;
number >> 24;

//Assembly language conversion
_asm
{
mov
eax,number
mov
buf[0],al
mov
buf[1],ah
bswap eax
;little endian to big endian
mov
buf[2],ah
mov
buf[3],al
}

Reading File Data. File data are read from the beginning of the file toward the end using the
OpenRead member of File. Example 8–32 shows an example that reads the file written in
Example 8–30 into a buffer called buffer1. The OpenRead function returns the number of bytes
actually read from the file, but not used in this example. This works fine if the size of the file is
known as it is here, but suppose that the length of the file is not known. The FileInfo class is used
to find the length of a file as illustrated in Example 8–33.
EXAMPLE 8–32
Stringˆ fileName = “C:\\Test1.txt”;
arrayˆ buffer1 = gcnew array(256);
try
{
FileStreamˆ fs = File::OpenRead(fileName);
fs->Read(buffer1, 0, 256);
fs->Close();
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}

EXAMPLE 8–33
Stringˆ fileName = “C:\\Test1.txt”;

PROGRAMMING THE MICROPROCESSOR

FIGURE 8–11

285

The HexDump program.

FileInfoˆ fi = gcnew FileInfo(fileName);
int fileLength = fi->Length;

An Example Binary Dump Program. One tool not available with Windows is a program that
displays the contents of a file in hexadecimal code. Although this may not be used by most programmers, it is used whenever software is developed so that the actual contents of a file can be
viewed in hexadecimal format. Start a forms application in Windows and call it HexDump. Place
a control called a Rich Textbox onto the form as illustrated in Figure 8–11. Under Properties for
the Rich Textbox Control, make sure you change Locked to true and Scroll bars to Vertical. If
you display a very large file, you will want to be able to scroll down through the code. Very large
files take some time to load in this program.
This program uses the function (Disph) shown earlier in Example 8–24 to display the address
as an eight-digit hexadecimal address and also to display the contents of the address in hexadecimal
form as a two-digit number. Add Disph function to the program so it returns a String at the location
addressed by char temp as the third parameter. The first two parameters contain two integers: one
for the number and one for the number of digits called size, as shown in Example 8–34.
Example 8–34 shows the entire program required to perform a hexadecimal dump. Most of
the program is generated by Visual C++, only the function at the top and a few at the end were
entered to create the application. Note that to change the file for this program requires a change
of the name of the file in the program. This can be modified by using an edit box to enter the file
name, but it was not done in this example for sake of brevity. In this program 16 bytes are read at
a time and formatted for display. This process continues until no bytes remain in the file. The

286

CHAPTER 8

ASCII data that are displayed at the end of the hexadecimal listing are filtered so that any ASCII
character under 32 (a space) are displayed as a period. This is important or control characters
such as line feed, backspace, and the like will destroy the screen formatting of the ASCII text,
and that is undesirable.
EXAMPLE 8–34
#pragma once
namespace HexDump1 {
using
using
using
using
using
using
using

namespace
namespace
namespace
namespace
namespace
namespace
namespace

System;
System::ComponentModel;
System::Collections;
System::Windows::Forms;
System::Data;
System::Drawing;
System::IO;

// assembly code here compiled using the /CLR switch
void Disph(unsigned int number, unsigned int size, char* temp)
{
int a;
number <= ( 8 - size ) * 4;
//adjust position
for (a = 0; a < size; a++)
{
char temp1;
_asm
{
rol number, 4;
mov al,byte ptr number
and al,0fh
;make 0 - f
add al,30h
;convert to ASCII
cmp al,39h
jbe Disph1
add al,7
Disph1:
mov temp1,al
}
temp[a] = temp1;
//add digit to string
}
temp[a] = 0;
// null string end
}
///


/// Summary for Form1
///
/// WARNING: If you change the name of this class, you will need to
change the
///
‘Resource File Name’ property for the managed resource
compiler tool
///
associated with all .resx files this class depends on.
Otherwise,
///
the designers will not be able to interact properly with
localized
///
resources associated with this form.
///

public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}

PROGRAMMING THE MICROPROCESSOR

287

protected:
///
/// Clean up any resources being used.
///

~Form1()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::RichTextBoxˆ richTextBox1;
private: System::Windows::Forms::OpenFileDialogˆ openFileDialog1;
private: System::Windows::Forms::Buttonˆ button1;
protected:
private:
///
/// Required designer variable.
///

System::ComponentModel::Container ˆcomponents;
#pragma region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///

void InitializeComponent(void)
{
this->richTextBox1 = (gcnew
System::Windows::Forms::RichTextBox());
this->openFileDialog1 = (gcnew
System::Windows::Forms::OpenFileDialog());
this->button1 = (gcnew System::Windows::Forms::Button());
this->SuspendLayout();
//
// richTextBox1
//
this->richTextBox1->Font = (gcnew
System::Drawing::Font(L“Courier New”, 9.75F,
System::Drawing::FontStyle::Regular,
System::Drawing::GraphicsUnit::Point,
static_cast(0)));
this->richTextBox1->Location = System::Drawing::Point(12,
12);
this->richTextBox1->Name = L“richTextBox1”;
this->richTextBox1->ScrollBars =
System::Windows::Forms::RichTextBoxScrollBars::Vertical;
this->richTextBox1->Size = System::Drawing::Size(657, 420);
this->richTextBox1->TabIndex = 0;
this->richTextBox1->Text = L“”;
//
// openFileDialog1
//
this->openFileDialog1->FileName = L“openFileDialog1”;
//
// button1
//
this->button1->Location = System::Drawing::Point(601, 438);
this->button1->Name = L“button1”;
this->button1->Size = System::Drawing::Size(68, 25);
this->button1->TabIndex = 1;
this->button1->Text = L“Open”;
this->button1->UseVisualStyleBackColor = true;
this->button1->Click += gcnew System::EventHandler(this,
&Form1::button1_Click);
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);

288

CHAPTER 8
this->AutoScaleMode =
System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(681, 468);
this->Controls->Add(this->button1);
this->Controls->Add(this->richTextBox1);
this->Name = L“Form1”;
this->ShowIcon = false;
this->StartPosition =
System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L“HexDump”;
this->ResumeLayout(false);
}
#pragma endregion
private: System::Stringˆ Disp(int number, int size)
{
char temp[9];
Disph(number,size,temp);
Stringˆ a = ““;
int count = 0;
while (temp[count] != 0)
// convert to string
{
Char b = temp[count++];
a += b;
}
return a;
}
private: System::Void button1_Click(System::Objectˆ sender,
System::EventArgsˆ e)
{
arrayˆ buffer = gcnew array(1000000);
int fileLength;
Stringˆ line = “”;
if (openFileDialog1->ShowDialog() ==
System::Windows::Forms::DialogResult::OK)
{
try
{
FileStreamˆ fs = File::OpenRead(openFileDialog1->FileName);
fs->Read(buffer, 0, 1000000);
fs->Close();
FileInfoˆ fi = gcnew FileInfo(openFileDialog1->FileName);
fileLength = fi->Length;
this->Text = “HexDump -- ” + openFileDialog1->FileName;
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}
for (int a = 0; a < fileLength; a++)
{
if (a % 16 == 0)
{
if (a != 0)
{
richTextBox1->Text += “ ” + line;
line = “”;
richTextBox1->Text += “\n”;
}
richTextBox1->Text += Disp(a, 8);
}
richTextBox1->Text += “ ” + Disp(buffer[a], 2);
if (buffer[a] >= 32 && buffer[a] < 128)
line += Convert::ToChar(buffer[a]);
else
line += “.”;

PROGRAMMING THE MICROPROCESSOR

289

}
richTextBox1->Text += “ ” + line;
}
else
{
this->Text = “HexDump”;
}
}
};
}

The File Pointer and Seek. When a file is opened, written, or read, the file pointer addresses
the current location in the sequential file. When a file is opened, the file pointer always addresses
the first byte of the file. If a file is 1024 bytes long, and a read function reads 1023 bytes, the file
pointer addresses the last byte of the file, but not the end of the file.
The file pointer is a 32-bit number that addresses any byte in a file. The File Append
member function is used to add new information to the end of a file. The file pointer can be
moved from the start of the file or from the end of the file. Open moves the pointer to the start of
the file. In practice, both are used to access different parts of the file. The FileStream member
function Seek allows the file pointer to be moved to the start of a file (SeekOrigin::Begin), the
end of a file (SeekOrigin::End), or the current location in the file (SeekOrigin::Current). The first
number in the Seek function is the offset. If the third byte in the file is accessed, it is accessed
with a Seek(2, SeekOrigin::Begin) function. (The third byte is at offset 2.) Note that the second
number in the Write function is also an offset and can be used in the same manner as a Seek.
Suppose that a file exists on the disk and that you must append the file with 256 bytes of
new information. When the file is opened, the file pointer addresses the first byte of the file. If
you attempt to write without moving the file pointer to the end of the file, the new data will
overwrite the first 256 bytes of the file. Example 8–35 shows a sequence of instructions for
Appends, which adds 256 bytes of data to the end of the file, and then closes the file. This file is
appended with 256 new bytes of data from area Buffer.
EXAMPLE 8–35
Stringˆ fileName = “C:\\Test1.txt”;
arrayˆ buffer = gcnew array(256);
try
{
FileStreamˆ fs = File::OpenWrite(fileName);
for (int a = 0; a < 256; a++)
{
buffer[a] = ‘S’;
}
fs->Seek(0, SeekOrigin::End);
fs->Write(buffer, 0, buffer->Length);
fs->Close();
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}

// or the same operation is performed using the offset number
// in the Write function as follows:
Stringˆ fileName = “C:\\Test1.txt”;
arrayˆ buffer = gcnew array(256);

290

CHAPTER 8

FIGURE 8–12 Inserting
new data within an old file.

Old file

New file

Old file

Insert data

Insert point

Insert data

Old file

try
{
FileStreamˆ fs = File::OpenWrite(fileName);
for (int a = 0; a < 256; a++)
{
buffer[a] = ‘S’;
}
fs->Write(buffer, 256, buffer->Length);
fs->Close();
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}

One of the more difficult file maneuvers is inserting new data in the middle of the file.
Figure 8–12 shows how this is accomplished by creating a second file. Notice that the part of the
file before the insertion point is copied into the new file. This is followed by the new information
before the remainder of the file is appended after the insertion in the new file. Once the new file
is complete, the old file is deleted and the new file is renamed to the old file name.
Example 8–36 shows a program that inserts new data into an old file. This program copies
the Data.new file into the Data.old file at a point after the first 256 bytes of the Data.old file. The
new data from buffer2 is next added to the file and then this is followed by the remainder of the
old file. New File member functions are used to delete the old file and rename the new file to the
old file name.
EXAMPLE 8–36
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
Stringˆ fileName1 = “C:\\Data.old”;
Stringˆ fileName2 = “C:\\Data.new”;
int fileLength;
arrayˆ buffer1 = gcnew array(256);
arrayˆ buffer2 = gcnew array(6);
try
{
FileStreamˆ fs1 = File::OpenWrite(fileName1);
FileStreamˆ fs2 = File::OpenWrite(fileName2);

291

PROGRAMMING THE MICROPROCESSOR
FileInfoˆ fi = gcnew FileInfo(fileName1);
fileLength = fi->Length;
fs1->Read(buffer1, 0, 256);
fs2->Write(buffer1, 0, 256);
fs2->Write(buffer2, 0, 6);
fileLength -= 256;
while (fileLength > 0)
{
fs1->Read(buffer1, 0, 256);
fs2->Write(buffer1, 0, 256);
fileLength -= 256;
}
fs1->Close();
fs2->Close();
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}
}

Random Access Files
Random access files are developed through software using sequential access files. A random
access file is addressed by a record number rather than by going through the file searching for
data. The Seek function becomes very important when random access files are created. Random
access files are much easier to use for large volumes of data, which are often called databases.

Creating a Random Access File. Planning is paramount when creating a random access file
system. Suppose that a random access file is required for storing the names of customers. Each
customer record requires 32 bytes for the last name, 32 bytes for the first name, and one byte for
the middle initial. Each customer record contains two street address lines of 64 bytes each, a city
line of 32 bytes, two bytes for the state code, and nine bytes for the Zip Code. The basic customer
information alone requires 236 bytes; additional information expands the record to 512 bytes.
Because the business is growing, provisions are made for 5000 customers. This means that the
total random access file is 2,560,000 bytes long.
Example 8–37 illustrates a short program that creates a file called CUST.FIL and inserts
5000 blank records of 512 bytes each. A blank record contains 00H in each byte. This appears be
a large file, but it fits on the smallest of hard disks.
EXAMPLE 8–37
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
Stringˆ fileName = “C:\\Cust.fil”;
arrayˆ buffer = gcnew array(512);
for ( int a = 0; a < 512; a++ )
{
buffer[a] = 0;
}

//fill buffer

try
{
FileStreamˆ fs = File::OpenWrite(fileName);
for (int a = 0; a < 5000; a++)
{
fs->Write(buffer, 0, 512);
}
fs->Close();

292

CHAPTER 8
}
catch (...)
{
MessageBox::Show(“Disk error”);
Application::Exit();
}
}

Reading and Writing a Record. Whenever a record must be read, the record number is found by
using a Seek. Example 8–38 lists a function that is used to Seek to a record. This function assumes
that a file has been opened as CustomerFile and that the CUST.FIL remains open at all times.
Notice how the record number is multiplied by 512 to obtain a count to move the file pointer
using a Seek. In each case, the file pointer is moved from the start of the file to the desired record.
EXAMPLE 8–38
void CCusDatabaseDlg::FindRecord(unsigned int RecordNumber)
{
File.Seek( RecordNumber * 512, CFile::begin );
}

Other functions (listed in Example 8–39) are needed to manage the customer database.
These include WriteRecord, ReadRecord, FindLastNameRecord, FindBlankRecord, and so on.
Some of these are listed in the example as well as the data structure that contains the information
for each record.
EXAMPLE 8–39
// class placed before the form1 class for containing a record
public ref class Customer
{
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
public: static arrayˆ
};

FirstName = gcnew array(32);
Mi = gcnew array(1);
LastName = gcnew array(32);
Street1 = gcnew array(64);
Street2 = gcnew array(64);
City = gcnew array(32);
State = gcnew array(2);
ZipCode = gcnew array(9);
Other = gcnew array(276);

// functions placed at the end of the form1 class
static
static
static
static

arrayˆ buffer = gcnew array(512);
Stringˆ fileName = “C:\\Cust.fil”;
FileStreamˆ fs;
Customer Record;

private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
// open the file when the application starts
Customer Record;
try
{
fs = File::OpenWrite(fileName);
for (int a = 0; a < 5000; a++)
{
fs->Write(buffer, 0, 512);
}
fs->Close();
}
catch (...)
{

PROGRAMMING THE MICROPROCESSOR
MessageBox::Show(“Disk error”);
Application::Exit();
}
}
private: System::Void FindRecord(unsigned int RecordNumber)
{
fs->Seek(RecordNumber * 512, SeekOrigin::Begin);
}
private: System::Void WriteRecord(unsigned int RecordNumber)
{
FindRecord(RecordNumber);
fs->Write(Record.FirstName, 0, 32);
fs->Write(Record.Mi, 0, 1);
fs->Write(Record.LastName, 0, 32);
fs->Write(Record.Street1, 0, 64);
fs->Write(Record.Street2, 0, 64);
fs->Write(Record.City, 0, 32);
fs->Write(Record.State, 0, 2);
fs->Write(Record.ZipCode, 0, 9);
}
private: System::Void ReadRecord(unsigned int RecordNumber)
{
FindRecord(RecordNumber);
fs->Read(Record.FirstName, 0, 32);
fs->Read(Record.Mi, 0, 1);
fs->Read(Record.LastName, 0, 32);
fs->Read(Record.Street1, 0, 64);
fs->Read(Record.Street2, 0, 64);
fs->Read(Record.City, 0, 32);
fs->Read(Record.State, 0, 2);
fs->Read(Record.ZipCode, 0, 9);
}
private: System::UInt32 FindFirstName(arrayˆ FirstName)
{
for ( int a = 0; a < 5000; a++ )
{
ReadRecord(a);
if (Record.FirstName == FirstName)
{
return a;
//if found return record number
}
}
return 5001;
//if not found return 5001
}
private: System::UInt32 FindBlankRecord()
{
for ( int a = 0; a < 5000; a++ )
{
ReadRecord(a);
if (Record.LastName[0] == 0 )
{
return a;
}
}
return 0;
}

293

294

CHAPTER 8

FIGURE 8–13 The
DataTime application.

8–5

EXAMPLE PROGRAMS
Now that many of the basic programming building blocks have been discussed, we present
some example application programs. Although these example programs may seem trivial,
they show some additional programming techniques and illustrate programming styles for the
microprocessor.

Time/Date Display Program
Although this program does not use assembly language, it does demonstrate how to obtain the
date and time from the Windows API and how to format it for display. It also illustrates how to
use a timer in Visual C++. Example 8–40 illustrates a program that uses a timer, set to interrupt
the program once per second, to display the time and date. The time and date are obtained by
using DateTime object to read the computer time and date into a variable called dt. The format
member TimeDate is used to format the dt variable. Create a dialog application called DateTime
and place two labels on it as shown in Figure 8–13.
EXAMPLE 8–40
private: System::Void Form1_Load(System::Objectˆ sender,
System::EventArgsˆ e)
{
ShowDateTime();
}
private: System::Void ShowDateTime()
{
DateTime dt = DateTime::Now;
// get current time
label1->Text = dt.Hour.ToString() + “:” + dt.Minute.ToString();
label2->Text = dt.Date.ToLongDateString();
}

PROGRAMMING THE MICROPROCESSOR

295

FIGURE 8–14 A bubble sort
showing data as they are
sorted. Note: Sorting five
numbers may require four
passes.

private: System::Void timer1_Tick(System::Objectˆ sender,
System::EventArgsˆ e)
{
ShowDateTime();
}

Numeric Sort Program
At times, numbers must be sorted into numeric order. This is often accomplished with a bubble
sort. Figure 8–14 shows five numbers that are sorted with a bubble sort. Notice that the set of five
numbers is tested four times with four passes. For each pass, two consecutive numbers are compared and sometimes exchanged. Also notice that during the first pass there are four comparisons, during the second three, and so forth.
Example 8–41 illustrates a program that accepts 10 numbers from the keyboard (32-bit
integers). After these 32-bit numbers are accepted and stored in memory section numbers, they
are sorted by using the bubble-sorting technique. This bubble sort uses a swap flag to determine
whether any numbers were exchanged in a pass. If no numbers were exchanged, the numbers are
in order and the sort terminates. This early termination normally increases the efficiency of the
sort because numbers are rarely completely out of order.
Once the numbers are sorted, they are displayed in ascending order. Figure 8–15 shows
how the application appears after it is executed.
FIGURE 8–15
sort.

The bubble

296

CHAPTER 8

EXAMPLE 8–41
void Sort(int* data)
{
char flag;
_asm
{
mov ecx,9
;9 for 10 numbers
L1:
mov flag,0
;clear flag
mov edx,0
L2:
mov ebx,data
mov eax,[ebx+edx*4]
cmp eax,[ebx+edx*4+4]
jbe L3
push eax
;swap
mov eax,[ebx+edx*4+4]
mov [ebx+edx*4], eax
pop dword ptr [ebx+edx*4+4]
mov flag,1
;set flag
L3:
inc edx
cmp edx,ecx
jne L2
cmp flag,0
jz
L4
;if no swaps
loop L1
L4:
}
}
bool isHandled;
private: System::Void button1_Click(System::Objectˆ sender,
System::EventArgsˆ e)
{
int numbers[10];
int count = 0;
int digit = 0;
int a;
for(a = 0; a < 10; a++)
{
numbers[a] = 0;
while (digit < textBox1->Text->Length && textBox1->Text[digit]
!= ‘,’)
{
numbers[a] = numbers[a] * 10 +
(int)(textBox1->Text[digit] - 0x30);
digit++;
}
digit++;
if (digit >= textBox1->Text->Length)
{
break;
}
}
if (a == 9)
{
Sort(numbers);
label2->Text = “”;
for (int a = 0; a < 9; a++)
{
label2->Text += numbers[a].ToString() + “, ”;
}

PROGRAMMING THE MICROPROCESSOR

297

label2->Text += numbers[9].ToString();
}
else
{
MessageBox::Show(
“10 numbers must be entered separated by commas”);
}
}
private: System::Void textBox1_KeyDown(System::Objectˆ sender,
System::Windows::Forms::KeyEventArgsˆ e)
{
isHandled = true;
if (e->KeyCode >= Keys::D0 && e->KeyCode <= Keys::D9 ||
e->KeyCode == Keys::Oemcomma || e->KeyCode == Keys::Back)
{
isHandled = false;
}
}
private: System::Void textBox1_KeyPress(System::Objectˆ sender,
System::Windows::Forms::KeyPressEventArgsˆ e)
{
e->Handled = isHandled;
}

Data Encryption
Data encryption seems to be the vogue at this time because of the security aspect of many systems. To illustrate simple data encryption for a character string, suppose that each character in a
string is exclusive-ORed with a number called an encryption key. This certainly changes the code
of the character, but to make it a bit more random, suppose that the encryption key is changed
after each character is encrypted. In this way patterns are much harder to detect in the encrypted
message, making it harder to decipher.
To illustrate this simple scheme, Figure 8–16 shows a screen shot of the program to test the
scheme, using a textbox control to accept a character string and a label to display the encrypted
message. This example was generated using an initial encryption key of 0×45. If the initial value
is changed, the encrypted message will change.
Example 8–42 lists the program used to generate the message in its encrypted form in a
rich textbox control. The button event handler reads the contents of the textbox control, used for
entering the character string to be encrypted, and uses a short assembly language function to
encrypt the string. Notice how the program uses assembly language to Exclusive-OR each character of the string with the EncryptionKey and then how the EncryptionKey is modified for the
next character. The technique used here increments the Encryption key and prevents the key from
becoming larger than 7FH. This technique can be made more intricate to make it even more difficult to decipher. For example, suppose that the key is incremented on every other character and
that is alternated with inverting the key, as shown in Example 8–43. Almost any combination of
operations can be used to modify the key between passes to make it very difficult to decode. In
practice we use a 128-bit key and the technique for modification is different, but nonetheless, this
is basically how encryption is performed. Because Example 8–40 uses an 8-bit key, the
encrypted message could be cracked by trying all 256 (28) possible keys, but if a 128-bit key is
used, it requires far many more attempts (2128) to crack—an almost impossible number of
attempts.

298

CHAPTER 8

FIGURE 8–16 Data encryption application.

EXAMPLE 8–42
char EncryptionKey = 0x45;
char Encrypt(char code)
{
_asm
{
mov al,code
xor al,EncryptionKey
mov code,al
mov al,EncryptionKey
inc al
and al,7fh
mov EncryptionKey,al
}
return code;
}
private: System::Void button1_Click(System::Objectˆ sender,
System::EventArgsˆ e)
{
richTextBox1->Text = “”;
for (int a = 0; a < textBox1->Text->Length; a++)
{
richTextBox1->Text += Convert::ToChar(Encrypt(textBox1->Text[a]));
}
}

EXAMPLE 8–43
//just the assembly language part of the program

PROGRAMMING THE MICROPROCESSOR

299

char EncryptionKey = 0x45;
char everyOther = 0;
char Encrypt(char code)
{
_asm
{
mov al,code
xor al,EncryptionKey
mov code,al
mov al,everyOther
inc al
and al,1
mov everyOther,al
mov bl,EncryptionKey
cmp al,0
jz
L1
inc bl
jmp L2
L1:
not bl
L2:
and bl,7fh
mov EncryptionKey,bl
}
return code;
}

8–6

SUMMARY
1. The assembler program (ML.EXE) assembles modules that contain PUBLIC variables and
segments, plus EXTRN (external) variables. The linker program (LINK.EXE) links modules and library files to create a run-time program executed from the DOS command line.
The run-time program usually has the extension EXE, but might contain the extension
COM.
2. The MACRO and ENDM directives create a new opcode for use in programs. These macros
are similar to procedures, except that there is no call or return. In place of them, the assembler inserts the code of the macro sequence into a program each time it is invoked. Macros
can include variables that pass information and data to the macro sequence.
3. Setting focus to an object is accomplished by using the Focus( ) member variable found with
most objects.
4. The Convert class in C++ is used to convert from one form to another in many cases, but not
in all cases.
5. The mouse driver is accessed from Windows by installing handlers for various Windows
events such as MouseMove, MouseDown, etc.
6. Conversion from binary to BCD is accomplished with the AAM instruction for numbers
that are less than 100 or by repeated division by 10 for larger numbers. Once the number is
converted to BCD, 30H is added to convert each digit to ASCII code for placement in a string.
7. When converting from an ASCII number to BCD, 30H is subtracted from each digit. To
obtain the binary equivalent, multiply by 10 and then add each new digit.
8. Lookup tables are used for code conversion with the XLAT instruction if the code is an 8-bit
code. If the code is wider than 8 bits, a short procedure that accesses a lookup table provides
the conversion. Lookup tables are also used to hold addresses so that different parts of a program or different procedures can be selected.
9. Conditional assembly language statements allow portions of a program to be assembled
if a condition is met. These are useful for tailoring software to an application. In Visual

300

CHAPTER 8

C++ Express, a program that contains assembly code must be compiled with the /CLR
switch.
10. The disk, memory system contains tracks that hold information stored in sectors. Many disk systems store 512 bytes of information per sector. Data on the disk are organized in a boot sector, file
allocation table, root directory, and data storage area. The boot sector loads the DOS system from
the disk into the computer memory system. The FAT or MFT indicates which sectors are present
and whether they contain data. The root directory contains file names and subdirectories through
which all disk files are accessed. The data storage area contains all subdirectories and data files.
11. Files are manipulated with the File object in Visual C++. To read a disk file, the file must be
opened, read, and then closed. To write to a disk file, it must be opened, written, and then
closed. When a file is opened, the file pointer addresses the first byte of the file. To access
data at other locations, the file pointer is moved using a Seek before data are read or written.
12. A sequential access file is a file that is accessed sequentially from the beginning to the end.
A random access file is a file that is accessed at any point. Although all disk files are sequential, they can be treated as random access files by using software.

8–7

QUESTIONS AND PROBLEMS
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

14.

15.
16.
17.

18.
19.
20.

The assembler converts a source file to a(n) _________ file.
What files are generated from the source file TEST.ASM if it is processed by ML.EXE?
The linker program links object files and _________ files to create an execution file.
What does the PUBLIC directive indicate when placed in a program module?
What does the EXTRN directive indicate when placed in a program module?
What directive appears with labels defined as external?
Describe how a library file works when it is linked to other object files by the linker program.
What assembler language directives delineate a macro sequence?
What is a macro sequence?
How are parameters transferred to a macro sequence?
Develop a macro called ADD32 that adds the 32-bit contents of DX-CX to the 32-bit contents of BX-AX.
How is the LOCAL directive used within a macro sequence?
Develop a macro called ADDLIST PARA1,PARA2 that adds the contents of PARA1 to
PARA2. Each of these parameters represents an area of memory. The number of bytes added
are indicated by register CX before the macro is invoked.
Develop a macro that sums a list of byte-sized data invoked by the macro ADDM LIST,LENGTH.
The label LIST is the starting address of the data block and LENGTH is the number of data added.
The result must be a 16-bit sum found in AX at the end of the macro sequence.
What is the purpose of the INCLUDE directive?
Modify the function in Example 8–12 so that it filters the numbers 0 through 9 from only the
keyboard and not the keypad and ignores all other characters.
Modify the function in Example 8–12 so that it generates a random 8-bit number in class
variable char Random. (Hint: To accomplish this, increment Random each time that the
KeyDown function is called.)
Modify the software you developed in question 17 so that it generates a random number
between 9 and 62.
Modify the function listed in Example 8–15 so that the hexadecimal numbers use lowercase
letters a through f instead of the uppercase letters.
Modify Example 8–16 so it will shift/rotate left or right. This is accomplished by adding a
pair of radio buttons to select the direction.

PROGRAMMING THE MICROPROCESSOR

301

21. What event handlers are used to access the mouse in the Visual C++ programming environment and what event causes each handler to be called?
22. How is the right mouse button detected in a program?
23. How is a double-click detected with the mouse?
24. Develop software that detects when both the right and left mouse buttons are pressed
simultaneously.
25. How is a color selected in a program using Visual C++?
26. What is the purpose of the ForeColor property?
27. When a number is converted from binary to BCD, the _________ instruction accomplishes
the conversion, provided the number is less than 100 decimal.
28. How is a large number (over 100 decimal) converted from binary to BCD?
29. How could a binary number be displayed as an octal number?
30. A BCD digit is converted to ASCII code by adding a(n) _________.
31. An ASCII-coded number is converted to BCD by subtracting _________.
32. Develop a function that reads an ASCII number from a textbox control as keys are typed
(use KeyDown) on the keyboard and returns it as an unsigned int. The number in the textbox
is an octal number that is converted to binary by the function.
33. Explain how a three-digit ASCII-coded number is converted to binary.
34. Develop a function that converts all lowercase ASCII-coded letters into uppercase ASCIIcoded letters. Your procedure may not change any other character except the letters a–z and
must return the converted character as a char.
35. Develop a lookup table that converts hexadecimal data 00H–0FH into the ASCII-coded
characters that represent the hexadecimal digits. Make sure to show the lookup table and any
software required for the conversion. It is suggested that a function is created to perform the
conversion.
36. Explain the purpose of a boot sector, FAT, and root directory in the FAT system.
37. Explain the purpose of the MFT in the NTFS file system.
38. The surface of a disk is divided into tracks that are further subdivided into _________.
39. What is a bootstrap loader and where is it found?
40. What is a cluster?
41. The NTFS file system often uses cluster of _________ bytes in length.
42. What is the maximum length of a file?
43. What code is used to store the name of a file when long file names are in use?
44. DOS file names are at most _________ characters in length.
45. How many characters normally appear in an extension?
46. How many characters may appear in a long file name?
47. Develop a program that opens a file called TEST.LST, reads 512 bytes from the file into
memory area Array, and closes the file.
48. Show how to rename file TEST.LST to TEST.LIS.
49. What is the purpose of the File Move member function?
50. What is a control?
51. Write a program that reads any decimal number between 0 and 2G and displays the 32-bit
binary version on the video display.
52. Write a program that displays the binary powers of 2 (in decimal) on the video screen for the
powers 0 through 7. Your display shows 2n = value for each power of 2.
53. Using a timer to generate a random number, develop a program that displays random numbers between 1 and 47 (or whatever) for your state’s lottery.
54. Modify the program in Example 8–28 so it also displays the letters A, b, C, d, E, and F for a
hexadecimal seven-segment display.
55. Modify Example 8–42 to encrypt the message using an algorithm of your own design.
56. Develop a Decryption function (for a String) to accompany the encryption of question 55.

CHAPTER 9
8086/8088 Hardware Specifications

INTRODUCTION
In this chapter, the pin functions of both the 8086 and 8088 microprocessors are detailed and
information is provided on the following hardware topics: clock generation, bus buffering, bus
latching, timing, wait states, and minimum mode operation versus maximum mode operation.
These simple microprocessors are explained first, because of their less intricate structures, as an
introduction to the Intel microprocessor family.
Before it is possible to connect or interface anything to the microprocessor, it is necessary
to understand the pin functions and timing. These rudimentary microprocessors contain the
same basic pins as the latest Pentium 4 or Core2 microprocessor. Thus, the information in this
chapter is essential to a complete understanding of memory and I/O interfacing, which we
cover in the later chapters of the text.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Describe the function of each 8086 and 8088 pin.
2. Understand the microprocessor’s DC characteristics and indicate its fan-out to common
logic families.
3. Use the clock generator chip (8284A) to provide the clock for the microprocessor.
4. Connect buffers and latches to the buses.
5. Interpret the timing diagrams.
6. Describe wait states and connect the circuitry required to cause various numbers of waits.
7. Explain the difference between minimum and maximum mode operation.

9–1

PIN-OUTS AND THE PIN FUNCTIONS
In this section, we explain the function and (in certain instances) the multiple functions of each
of the microprocessor’s pins. In addition, we discuss the DC characteristics to provide a basis for
understanding the later sections on buffering and latching.

302

303

8086/8088 HARDWARE SPECIFICATIONS

The Pin-Out
Figure 9–1 illustrates the pin-outs of the 8086 and 8088 microprocessors. As a close comparison
reveals, there is virtually no difference between these two microprocessors—both are packaged
in 40-pin dual in-line packages (DIPs).
As mentioned in Chapter 1, the 8086 is a 16-bit microprocessor with a 16-bit data bus and
the 8088 is a 16-bit microprocessor with an 8-bit data bus. (As the pin-outs show, the 8086 has
pin connections AD0–AD15, and the 8088 has pin connections AD0–AD7.) Data bus width therefore the only major difference between these microprocessors. This allows the 8086 to transfer
16-bit data more efficiently.
There is, however, a minor difference in one of the control signals. The 8086 has an M>IO
pin, and the 8088 has an IO/M pin. The only other hardware difference appears on Pin 34 of both
integrated circuits: on the 8088, it is an SS0 pin, whereas on the 8086, it is a BHE /S7 pin.

Power Supply Requirements
Both the 8086 and 8088 microprocessors require +5.0 V with a supply voltage tolerance of ±10
percent. The 8086 uses a maximum supply current of 360 mA, and the 8088 draws a maximum
of 340 mA. Both microprocessors operate in ambient temperatures of between 32° F and 180° F.
This range is not wide enough to be used outdoors in the winter or even in the summer, but
extended temperature-range versions of the 8086 and 8088 microprocessors are available. There
is also a CMOS version, which requires a very low supply current and has an extended temperature
range. The 80C88 and 80C86 are CMOS versions that require only 10 mA of power supply current and function in temperature extremes of -40° F through +225° F.

DC Characteristics
It is impossible to connect anything to the pins of the microprocessor without knowing the input
current requirement for an input pin and the output current drive capability for an output pin.
This knowledge allows the hardware designer to select the proper interface components for use
with the microprocessor without the fear of damaging anything.

FIGURE 9–1 (a) The
pin-out of the 8086 in
maximum mode; (b) the
pin-out of the 8086 in
minimum mode.

16
15
14
13
12
11
10
9
19
18
33
17
22
31
30
21
23

AD0
AD1
AD2
AD3
AD4
AD5
AD6
AD7

AD8
AD9
AD10
AD11
AD12
AD13
AD14
AD15

CLK

A16/S3
A17/S4
A18/S5
A19/S6

INTR
MX
NMI
READY
RQ/GT0
RQ/GT1
RST
TEST

S0
S1
S2
BHE/S7
LOCK
QS0
QS1
RD

8086MAX

8
7
6
5
4
3
2
39

16
15
14
13
12
11
10
9

38
37
36
35

19

26
27
28
34
29
25
24
32

31
18
33
17
22
21
23

AD0
AD1
AD2
AD3
AD4
AD5
AD6
AD7

AD8
AD9
AD10
AD11
AD12
AD13
AD14
AD15

CLK

A16/S3
A17/S4
A18/S5
A19/S6

HOLD
INTR
MN
NMI
READY
RST
TEST

ALE
BHE/S7
DEN
DT/R
HLDA
INTA
M/IO
RD
WR

8086MIN

(a)

(b)

8
7
6
5
4
3
2
39
38
37
36
35
25
34
26
27
30
24
28
32
29

304

CHAPTER 9

TABLE 9–1 Input
characteristics of
the 8086 and 8088
microprocessors.

Logic Level
0
1

Voltage

Current

0.8 V maximum
2.0 V minimum

±10 μA maximum
±10 μA maximum

Input Characteristics. The input characteristics of these microprocessors are compatible with all the
standard logic components available today. Table 9–1 depicts the input voltage levels and the input
current requirements for any input pin on either microprocessor. The input current levels are very
small because the inputs are the gate connections of MOSFETs and represent only leakage currents.
Output Characteristics. Table 9–2 illustrates the output characteristics of all the output pins of
these microprocessors. The logic 1 voltage level of the 8086/8088 is compatible with that of most
standard logic families, but the logic 0 level is not. Standard logic circuits have a maximum logic 0
voltage of 0.4 V, and the 8086/8088 has a maximum of 0.45 V. Thus, there is a difference of 0.05 V.
This difference reduces the noise immunity from a standard level of 400 mV (0.8 V – 0.45 V)
to 350 mV. (The noise immunity is the difference between the logic 0 output voltage and the
logic 0 input voltage levels.) The reduction in noise immunity may result in problems with long
wire connections or too many loads. It is therefore recommended that no more than 10 loads of
any type or combination be connected to an output pin without buffering. If this loading factor is
exceeded, noise will begin to take its toll in timing problems.
Table 9–3 lists some of the more common logic families and the recommended fan-out
from the 8086/8088. The best choice of component types for the connection to an 8086/8088
output pin is an LS, 74ALS, or 74HC logic component. Note that some of the fan-out currents
calculate to more than 10 unit loads. It is therefore recommended that if a fan-out of more than
10 unit loads is required, the system should be buffered.

Pin Connections
AD7–AD0

The 8088 address/data bus lines are the multiplexed address data bus of the
8088 and contain the rightmost 8 bits of the memory address or I/O port number
whenever ALE is active (logic 1) or data whenever ALE is inactive (logic 0).
These pins are at their high-impedance state during a hold acknowledge.

TABLE 9–2 Output
characteristics of
the 8086 and 8088
microprocessors.

TABLE 9–3 Recommended
fan-out from any 8086/8088 pin
connection.

Logic Level
0
1

Family
TTL (74)
TTL (74LS)
TTL (74S)
TTL (74ALS)
TTL (74AS)
TTL (74F)
CMOS (74HC)
CMOS (CD)
NMOS

Voltage

Current

0.45V maximum
2.4 V minimum

2.0 mA maximum
–400 μA maximum

Sink Current

Source Current

Fan-out

-1.6 mA
-0.4 mA
-2.0 mA
-0.1 mA
-0.5 mA
-0.5 mA
- 10 μA
- 10 μA
- 10 μA

40 μA
20 μA
50 μA
20 μA
25 μA
25 μA
10 μA
10 μA
10 μA

1
5
1
10
10
10
10
10
10

305

8086/8088 HARDWARE SPECIFICATIONS

TABLE 9–4 Function of
status bits S3 and S4.

A15–A8

AD15–AD8

A19/S6–A16/S3

RD

READY

INTR

TEST

NMI

RESET

CLK

VCC

S4

S3

Function

0
0
1
1

0
1
0
1

Extra segment
Stack segment
Code or no segment
Data segment

The 8088 address bus provides the upper-half memory address bits that are
present throughout a bus cycle. These address connections go to their highimpedance state during a hold acknowledge.
The 8086 address/data bus lines compose the upper multiplexed
address/data bus on the 8086. These lines contain address bits A15–A8 whenever ALE is a logic 1, and data bus connections D15–D8 when ALE is a logic 0.
These pins enter a high-impedance state when a hold acknowledge occurs.
The address/status bus bits are multiplexed to provide address signals
A19–A16 and also status bits S6–S3. These pins also attain a high-impedance
state during the hold acknowledge.
Status bit S6 is always a logic 0, bit S5 indicates the condition of the IF
flag bit, and S4 and S3 show which segment is accessed during the current
bus cycle. See Table 9–4 for the truth table of S4 and S3. These two status
bits could be used to address four separate 1M byte memory banks by
decoding them as A21 and A20.
Whenever the read signal is a logic 0, the data bus is receptive to data from
the memory or I/O devices connected to the system. This pin floats to its
high-impedance state during a hold acknowledge.
The READY input is controlled to insert wait states into the timing of the
microprocessor. If the READY pin is placed at a logic 0 level, the microprocessor enters into wait states and remains idle. If the READY pin is placed
at a logic 1 level, it has no effect on the operation of the microprocessor.
Interrupt request is used to request a hardware interrupt. If INTR is held
high when IF = 1, the 8086/8088 enters an interrupt acknowledge cycle
(INTA becomes active) after the current instruction has completed execution.
The Test pin is an input that is tested by the WAIT instruction. If TEST is a
logic 0, the WAIT instruction functions as an NOP and if TEST is a logic 1,
the WAIT instruction waits for TEST to become a logic 0. The TEST pin
is most often connected to the 8087 numeric coprocessor.
The non-maskable interrupt input is similar to INTR except that the NMI
interrupt does not check to see whether the IF flag bit is a logic 1. If NMI is
activated, this interrupt input uses interrupt vector 2.
The reset input causes the microprocessor to reset itself if this pin is held
high for a minimum of four clocking periods. Whenever the 8086 or 8088 is
reset, it begins executing instructions at memory location FFFFOH and disables future interrupts by clearing the IF flag bit.
The clock pin provides the basic timing signal to the microprocessor. The clock
signal must have a duty cycle of 33 % (high for one third of the clocking period
and low for two thirds) to provide proper internal timing for the 8086/8088.
This power supply input provides a +5.0 V, ±10 % signal to the microprocessor.

306

CHAPTER 9

GND

MN/MX

BHE S7

The ground connection is the return for the power supply. Note that the
8086/8088 microprocessors have two pins labeled GND—both must be
connected to ground for proper operation.
The minimum/maximum mode pin selects either minimum mode or maximum mode operation for the microprocessor. If minimum mode is
selected, the MN/MX pin must be connected directly to +5.0 V.
The bus high enable pin is used in the 8086 to enable the most-significant
data bus bits (D15–D8) during a read or a write operation. The state of S7 is
always a logic 1.

Minimum Mode Pins. Minimum mode operation of the 8086/8088 is obtained by connecting
the MN/MX pin directly to +5.0 V. Do not connect this pin to +5.0 V through a pull-up register,
or it will not function correctly.
IO/M or M/ IO

WR

INTA

ALE

DT/ R

DEN
HOLD

HLDA
SS0

The IO/M (8088) or the M/ IO (8086) pin selects memory or I/O. This pin
indicates that the microprocessor address bus contains either a memory
address or an I/O port address. This pin is at its high-impedance state during a hold acknowledge.
The write line is a strobe that indicates that the 8086/8088 is outputting
data to a memory or I/O device. During the time that the WR is a logic 0,
the data bus contains valid data for memory or I/O. This pin floats to a highimpedance during a hold acknowledge.
The interrupt acknowledge signal is a response to the INTR input pin.
The INTA pin is normally used to gate the interrupt vector number onto the
data bus in response to an interrupt request.
Address latch enable shows that the 8086/8088 address/data bus contains
address information. This address can be a memory address or an I/O port
number. Note that the ALE signal does not float during a hold acknowledge.
The data transmit/receive signal shows that the microprocessor data bus is
transmitting (DT/R ⫽ 1) or receiving (DT/R ⫽ 0) data. This signal is
used to enable external data bus buffers.
Data bus enable activates external data bus buffers.
The hold input requests a direct memory access (DMA). If the HOLD signal is a logic 1, the microprocessor stops executing software and places its
address, data, and control bus at the high-impedance state. If the HOLD pin
is a logic 0, the microprocessor executes software normally.
Hold acknowledge indicates that the 8086/8088 has entered the hold state.
The SS0 status line is equivalent to the S0 pin in maximum mode operation
of the microprocessor. This signal is combined with IO/M and DT/R to
decode the function of the current bus cycle (see Table 9–5).

Maximum Mode Pins. In order to achieve maximum mode for use with external coprocessors,
connect the MN/MX pin to ground.
S2, S1 , and S0

RQ /GT1 and
RQ>GT0

The status bits indicate the function of the current bus cycle. These signals are
normally decoded by the 8288 bus controller described later in this chapter.
Table 9–6 shows the function of these three status bits in the maximum mode.
The request/grant pins request direct memory accesses (DMA) during
maximum mode operation. These lines are bidirectional and are used to
both request and grant a DMA operation.

307

8086/8088 HARDWARE SPECIFICATIONS

TABLE 9–5
using SS0.

Bus cycle status (8088)

TABLE 9–6 Bus control function
generated by the bus controller
(8288).

TABLE 9–7

DT/R

SS0

Function

0
0
0
0
1
1
1
1

0
0
1
1
0
0
1
1

0
1
0
1
0
1
0
1

Interrupt acknowledge
Memory read
Memory write
Halt
Opcode fetch
I/O read
I/O write
Passive

S2

S1

S0

Function

0
0
0
0
1
1
1
1

0
0
1
1
0
0
1
1

0
1
0
1
0
1
0
1

Interrupt acknowledge
I/O read
I/O write
Halt
Opcode fetch
Memory read
Memory write
Passive

Queue status bits.

LOCK
QS1 and QS0

9–2

IO/M

QS1

QS0

Function

0
0
1
1

0
1
0
1

Queue is idle
First byte of opcode
Queue is empty
Subsequent byte of opcode

The lock output is used to lock peripherals off the system. This pin is activated by using the LOCK: prefix on any instruction.
The queue status bits show the status of the internal instruction queue.
These pins are provided for access by the numeric coprocessor (8087). See
Table 9–7 for the operation of the queue status bits.

CLOCK GENERATOR (8284A)
This section describes the clock generator (8284A) and the RESET signal, and introduces the
READY signal for the 8086/8088 microprocessors. (The READY signal and its associated circuitry are treated in detail in Section 9–5.)

The 8284A Clock Generator
The 8284A is an ancillary component to the 8086/8088 microprocessors. Without the clock generator, many additional circuits are required to generate the clock (CLK) in an 8086/8088-based
system. The 8284A provides the following basic functions or signals: clock generation, RESET
synchronization, READY synchronization, and a TTL-level peripheral clock signal. Figure 9–2
illustrates the pin-out of the 8284A clock generator.

308

CHAPTER 9

FIGURE 9–2 The pin-out of
the 8284A clock generator.

3
7
14
12
17
16
15
1
13
4
6
11

AEN1
AEN2
EFI
OSC
X1
X2

CLK
PCLK
READY
RESET

8
2
5
10

ASYNC
CSYNC
F/C
RDY1
RDY2
RES
8284A

Pin Functions. The 8284A is an 18-pin integrated circuit designed specifically for use with the
8086/8088 microprocessor. The following is a list of each pin and its function.
AEN1 and AEN2

RDY1 and RDY2
ASYNC
READY
X1 and X2
F/C

CLK

PCLK

OSC

RES
RESET
CSYNC

GND
VCC

The address enable pins are provided to qualify the bus ready signals,
RDY1 and RDY2, respectively. Section 9–5 illustrates the use of these two
pins, which are used to cause wait states, along with the RDY1 and RDY2
inputs. Wait states are generated by the READY pin of the 8086/8088
microprocessors, which is controlled by these two inputs.
The bus ready inputs are provided, in conjunction with the AEN1 and
AEN2 pins, to cause wait states in an 8086/8088-based system.
The ready synchronization selection input selects either one or two stages
of synchronization for the RDY1 and RDY2 inputs.
Ready is an output pin that connects to the 8086/8088 READY input. This
signal is synchronized with the RDY1 and RDY2 inputs.
The crystal oscillator pins connect to an external crystal used as the timing
source for the clock generator and all its functions.
The frequency/crystal select input chooses the clocking source for the
8284A. If this pin is held high, an external clock is provided to the EFI
input pin; if it is held low, the internal crystal oscillator provides the timing
signal. The external frequency input is used when the F/C pin is pulled
high. EFI supplies the timing whenever the F/C pin is high.
The clock output pin provides the CLK input signal to the 8086/8088
microprocessors and other components in the system. The CLK pin has an
output signal that is one third of the crystal or EFI input frequency, and has
a 33% duty cycle, which is required by the 8086/8088.
The peripheral clock signal is one sixth the crystal or EFI input frequency,
and has a 50% duty cycle. The PCLK output provides a clock signal to the
peripheral equipment in the system.
The oscillator output is a TTL-level signal that is at the same frequency as
the crystal or EFI input. The OSC output provides an EFI input to other
8284A clock generators in some multiple-processor systems.
The reset input is an active-low input to the 8284A. The RES pin is often
connected to an RC network that provides power-on resetting.
The reset output is connected to the 8086/8088 RESET input pin.
The clock synchronization pin is used whenever the EFI input provides
synchronization in systems with multiple processors. If the internal crystal
oscillator is used, this pin must be grounded.
The ground pin connects to ground.
This power supply pin connects to +5.0 V with a tolerance of ±10%.

8086/8088 HARDWARE SPECIFICATIONS

309

FIGURE 9–3 The internal
block diagram of the
8284A clock generator.

Operation of the 8284A
The 8284A is a relatively easy component to understand. Figure 9–3 illustrates the internal timing diagram of the 8284A clock generator.

Operation of the Clock Section. The top half of the logic diagram represents the clock and synchronization section of the 8284A clock generator. As the diagram shows, the crystal oscillator
has two inputs: X1 and X2. If a crystal is attached to X1 and X2, the oscillator generates a squarewave signal at the same frequency as the crystal. The square-wave signal is fed to an AND gate
and also to an inverting buffer that provides the OSC output signal. The OSC signal is sometimes
used as an EFI input to other 8284A circuits in a system.
An inspection of the AND gate reveals that when F/C is a logic 0, the oscillator output is steered
through to the divide-by-3 counter. If F/C is a logic 1, then EFI is steered through to the counter.
The output of the divide-by-3 counter generates the timing for ready synchronization, a
signal for another counter (divide-by-2), and the CLK signal to the 8086/8088 microprocessor.
The CLK signal is also buffered before it leaves the clock generator. Notice that the output of the
first counter feeds the second. These two cascaded counters provide the divide-by-6 output at
PCLK, the peripheral clock output.
Figure 9–4 shows how an 8284A is connected to the 8086/8088. Notice that F/ C and
CSYNC are grounded to select the crystal oscillator, and that a 15 MHz crystal provides the normal 5 MHz clock signal to the 8086/8088, as well as a 2.5 MHz peripheral clock signal.
Operation of the Reset Section. The reset section of the 8284A is very simple: It consists of a
Schmitt trigger buffer and a single D-type flip-flop circuit. The D-type flip-flop ensures that the
timing requirements of the 8086/8088 RESET input are met. This circuit applies the RESET signal to the microprocessor on the negative edge (1-to-0 transition) of each clock. The 8086/8088
microprocessors sample RESET at the positive edge (0-to-1 transition) of the clocks; therefore,
this circuit meets the timing requirements of the 8086/8088.
Refer to Figure 9–4. Notice that an RC circuit provides a logic 0 to the RES input pin
when power is first applied to the system. After a short time, the RES input becomes a logic 1
because the capacitor charges toward +5.0 V through the resistor. A pushbutton switch allows the
microprocessor to be reset by the operator. Correct reset timing requires the RESET input to
come a logic 1 no later than four clocks after system power is applied, and to be held high for at

310

CHAPTER 9

FIGURE 9–4 The clock generator (8284A) and the 8086 and 8088 microprocessors illustrating
the connection for the clock and reset signals. A 15 MHz crystal provides the 5 MHz clock for the
microprocessor.

least 50 μs. The flip-flop makes certain that RESET goes high in four clocks, and the RC time
constant ensures that it stays high for at least 50 μs.

9–3

BUS BUFFERING AND LATCHING
Before the 8086/8088 microprocessors can be used with memory or I/O interfaces, their multiplexed buses must be demultiplexed. This section provides the detail required to demultiplex the
buses and illustrates how the buses are buffered for very large systems. (Because the maximum
fan-out is 10, the system must be buffered if it contains more than 10 other components.)

Demultiplexing the Buses
The address/data bus on the 8086/8088 is multiplexed (shared) to reduce the number of pins required
for the 8086/8088 microprocessor integrated circuit. Unfortunately, this burdens the hardware
designer with the task of extracting or demultiplexing information from these multiplexed pins.
Why not leave the buses multiplexed? Memory and I/O require that the address remains
valid and stable throughout a read or write cycle. If the buses are multiplexed, the address
changes at the memory and I/O, which causes them to read or write data in the wrong locations.
All computer systems have three buses: (1) an address bus that provides the memory and I/O
with the memory address or the I/O port number, (2) a data bus that transfers data between the microprocessor and the memory and I/O in the system, and (3) a control bus that provides control signals
to the memory and I/O. These buses must be present in order to interface to memory and I/O.

Demultiplexing the 8088. Figure 9–5 illustrates the 8088 microprocessor and the components
required to demultiplex its buses. In this case, two 74LS373 or 74LS573 transparent latches are
used to demultiplex the address/data bus connections AD7–AD0 and the multiplexed address/
status connections A19/S6–A16/S3.
These transparent latches, which are like wires whenever the address latch enable pin
(ALE) becomes a logic 1, pass the inputs to the outputs. After a short time, ALE returns to its
logic 0 condition, which causes the latches to remember the inputs at the time of the change to a

8086/8088 HARDWARE SPECIFICATIONS

311

FIGURE 9–5 The 8088 microprocessor shown with a demultiplexed address bus. This is the
model used to build many 8088-based systems.

logic 0. In this case, A7–A0 are stored in the bottom latch and A19–A16 are stored in the top latch.
This yields a separate address bus with connections A19–A0. These address connections allow
the 8088 to address 1M byte of memory space. The fact that the data bus is separate allows it to
be connected to any 8-bit peripheral device or memory component.

Demultiplexing the 8086. Like the 8088, the 8086 system requires separate address, data, and
control buses. It differs primarily in the number of multiplexed pins. In the 8088, only AD7–AD0
and A19/S6–A16/S3 are multiplexed. In the 8086, the multiplexed pins include AD15–AD0
A19/S6–A16/S3, and BHE/S7. All of these signals must be demultiplexed.
Figure 9–6 illustrates a demultiplexed 8086 with all three buses: address (A19–A0 and
BHE), data (D15–D0), and control (M>IO, RD, and WR).
This circuit shown in Figure 9–6 is almost identical to the one pictured in Figure 9–5,
except that an additional 74LS373 latch has been added to demultiplex the address/data bus pins
AD15–AD8 and a BHE/S7 input has been added to the top 74LS373 to select the high-order
memory bank in the l6-bit memory system of the 8086. Here, the memory and I/O system see the

312

CHAPTER 9

FIGURE 9–6 The 8086 microprocessor shown with a demultiplexed address bus. This is the
model used to build many 8086-based systems.

8086 as a device with a 20-bit address bus (A19–A0), a l6-bit data bus (D15–D0), and a three-line
control bus (M>IO, RD, and WR).

The Buffered System
If more than 10 unit loads are attached to any bus pin, the entire 8086 or 8088 system must be
buffered. The demultiplexed pins are already buffered by the 74LS373 or 74LS573 latches,
which have been designed to drive the high-capacitance buses encountered in microcomputer

8086/8088 HARDWARE SPECIFICATIONS

313

systems. The buffer’s output currents have been increased so that more TTL unit loads may be
driven: A logic 0 output provides up to 32 mA of sink current, and a logic 1 output provides up
to 5.2 mA of source current.
A fully buffered signal will introduce a timing delay to the system. This causes no difficulty unless memory or I/O devices are used, which function at near the maximum speed of the
bus. Section 9–4 discusses this problem and the time delays involved in more detail.

The Fully Buffered 8088. Figure 9–7 depicts a fully buffered 8088 microprocessor. Notice that
the remaining eight address pins, A15–A8, use a 74LS244 octal buffer; the eight data bus pins,
D7–D0, use a 74LS245 octal bidirectional bus buffer; and the control bus signals, M>IO, RD,

FIGURE 9–7

A fully buffered 8088 microprocessor.

314

CHAPTER 9

and WR, use a 74LS244 buffer. A fully buffered 8088 system requires two 74LS244s, one
74LS245, and two 74LS373s. The direction of the 74LS245 is controlled by the DT/R signal and
is enabled and disabled by the DEN signal.

The Fully Buffered 8086. Figure 9–8 illustrates a fully buffered 8086 microprocessor. Its
address pins are already buffered by the 74LS373 address latches; its data bus employs two

FIGURE 9–8

A fully buffered 8086 microprocessor.

8086/8088 HARDWARE SPECIFICATIONS

315

74LS245 octal bidirectional bus buffers; and the control bus signals, M>IO, RD, and WR use
a 74LS244 buffer. A fully buffered 8086 system requires one 74LS244, two 74LS245s, and three
74LS373s. The 8086 requires one more buffer than the 8088 because of the extra eight data bus
connections, D15–D8. It also has a BHE signal that is buffered for memory-bank selection.

9–4

BUS TIMING
It is essential to understand system bus timing before choosing a memory or I/O device for interfacing to the 8086 or 8088 microprocessors. This section provides insight into the operation of
the bus signals and the basic read and write timing of the 8086/8088. It is important to note that
we discuss only the times that affect memory and I/O interfacing in this section.

Basic Bus Operation
The three buses of the 8086 and 8088—address, data, and control—function exactly the same way
as those of any other microprocessor. If data are written to the memory (see the simplified timing
for write in Figure 9–9), the microprocessor outputs the memory address on the address bus, outputs the data to be written into memory on the data bus, and issues a write (WR) to memory and
IO>M = 0 for the 8088 and M>IO = 1 for the 8086. If data are read from the memory (see the simplified timing for read in Figure 9–10), the microprocessor outputs the memory address on the
address bus, issues a read memory signal (RD), and accepts the data via the data bus.

Timing in General
The 8086/8088 microprocessors use the memory and I/O in periods called bus cycles. Each bus
cycle equals four system-clocking periods (T states). Newer microprocessors divide the bus
cycle into as few as two clocking periods. If the clock is operated at 5 MHz (the basic operating
frequency for these two microprocessors), one 8086/8088 bus cycle is complete in 800 ns. This
means that the microprocessor reads or writes data between itself and memory or I/O at a maximum rate of 1.25 million times a second. (Because of the internal queue, the 8086/8088 can execute 2.5 million instructions per second [MIPS] in bursts.) Other available versions of these
microprocessors operate at much higher transfer rates due to higher clock frequencies.

FIGURE 9–9

Simplified 8086/8088 write bus cycle.

316

CHAPTER 9

FIGURE 9–10

Simplified 8086/8088 read bus cycle.

During the first clocking period in a bus cycle, which is called T1, many things happen.
The address of the memory or I/O location is sent out via the address bus and the address/data
bus connections. (The address/data bus is multiplexed and sometimes contains memory-addressing
information, sometimes data.) During TI, control signals ALE, DT>R, and IO>M (8088) or
M>IO (8086) are also output. The IO>M or M>IO signal indicates whether the address bus contains a memory address or an I/O device (port) number.
During T2, the 8086/8088 microprocessors issue the RD or WR signal, DEN, and in the
case of a write, the data to be written appear on the data bus. These events cause the memory or
I/O device to begin to perform a read or a write. The DEN signal turns on the data bus buffers, if
they are present in the system, so the memory or I/O can receive data to be written, or so the
microprocessor can accept the data read from the memory or I/O for a read operation. If this happens to be a write bus cycle, the data are sent out to the memory or I/O through the data bus.
READY is sampled at the end of T2, as illustrated in Figure 9–11. If READY is low at this
time, T3 becomes a wait state (Tw). (More detail is provided in Section 9–5.) This clocking period
is provided to allow the memory time to access data. If the bus cycle happens to be a read bus
cycle, the data bus is sampled at the end of T3.
In T4, all bus signals are deactivated in preparation for the next bus cycle. This is also the
time when the 8086/8088 samples the data bus connections for data that are read from memory
or I/O. In addition, at this point, the trailing edge of the WR signal transfers data to the memory
or I/O, which activates and writes when the WR signal returns to a logic 1 level.

Read Timing
Figure 9–11 also depicts the read timing for the 8088 microprocessor. The 8086 read timing is
identical except that the 8086 has 16 rather than eight data bus bits. A close look at this timing
diagram should allow you to identify all the main events described for each T state.
The most important item contained in the read timing diagram is the amount of time allowed
for the memory or I/O to read the data. Memory is chosen by its access time, which is the fixed
amount of time that the microprocessor allows it to access data for the read operation. It is therefore
extremely important that the memory chosen complies with the limitations of the system.
The microprocessor timing diagram does not provide a listing for memory access time.
Instead, it is necessary to combine several times to arrive at the access time. To find memory

8086/8088 HARDWARE SPECIFICATIONS

FIGURE 9–11

317

Minimum mode 8088 bus timing for a read operation.

access time in this diagram, first locate the point in T3 when data are sampled. If you examine the
timing diagram closely, you will notice a line that extends from the end of T3 down to the data
bus. At the end of T3, the microprocessor samples the data bus.
Memory access time starts when the address appears on the memory address bus and continues until the microprocessor samples the memory data at T3. Approximately three T states
elapse between these times. (See Figure 9–12 for the following times.) The address does not
appear until TCLAV time (110 ns if the clock is 5 MHz) after the start of T1. This means that
TCLAV time must be subtracted from the three clocking states (600 ns) that separate the appearance of the address (T1) and the sampling of the data (T3). One other time must also be subtracted: the data setup time (TDVCL), which occurs before T3. Memory access time is thus three
clocking states minus the sum of TCLAV and TDVCL. Because TDVCL is 30 ns with a 5 MHz clock,
the allowed memory access time is only 460 ns (access time = 600 ns - 110 ns - 30 ns).
The memory devices chosen for connection to the 8086/8088 operating at 5 MHz must be
able to access data in less than 460 ns, because of the time delay introduced by the address
decoders and buffers in the system. At least a 30- or 40-ns margin should exist for the operation
of these circuits. Therefore, the memory speed should be no slower than about 420 ns to operate
correctly with the 8086/8088 microprocessors.

FIGURE 9–12 8088 AC
characteristics.

318

8086/8088 HARDWARE SPECIFICATIONS

319

The only other timing factor that may affect memory operation is the width of the RD
strobe. On the timing diagram, the read strobe is given as TRLRH. The time for this strobe is 325 ns
(5 MHz clock rate), which is wide enough for almost all memory devices manufactured with an
access time of 400 ns or less.

Write Timing
Figure 9–13 illustrates the write-timing diagram for the 8088 microprocessor. (Again, the 8086
is nearly identical, so it need not be presented here in a separate timing diagram.)
The main differences between read and write timing are minimal. The RD strobe is replaced
by the WR strobe, the data bus contains information for the memory rather than information from
the memory, and DT>R remains a logic 1 instead of a logic 0 throughout the bus cycle.
When interfacing some memory devices, timing may be especially critical between the
point at which WR becomes a logic 1 and the time when the data are removed from the data bus.
This is the case because, as you will recall, memory data are written at the trailing edge of the
WR strobe. According to the timing diagram, this critical period is TWHDX or 88 ns when the
8088 is operated with a 5 MHz clock. Hold time is often much less than this; it is, in fact, often 0 ns
for memory devices. The width of the WR strobe is TWLWH or 340 ns at a 5 MHz clock rate. This
rate is compatible with most memory devices that have an access time of 400 ns or less.

FIGURE 9–13

Minimum mode 8088 write bus timing.

320

CHAPTER 9

9–5

READY AND THE WAIT STATE
As we mentioned earlier in this chapter, the READY input causes wait states for slower memory
and I/O components. A wait state (Tw) is an extra clocking period, inserted between T2 and T3 to
lengthen the bus cycle. If one wait state is inserted, then the memory access time, normally 460 ns
with a 5 MHz clock, is lengthened by one clocking period (200 ns) to 660 ns.
In this section, we discuss the READY synchronization circuitry inside the 8284A clock
generator, show how to insert one or more wait states selectively into the bus cycle, and examine
the READY input and the synchronization times it requires.

The READY Input
The READY input is sampled at the end of T2 and again, if applicable, in the middle of Tw. If
READY is a logic 0 at the end of T2, T3 is delayed and Tw is inserted between T2 and T3.
READY is next sampled at the middle of Tw to determine whether the next state is Tw or T3. It is
tested for a logic 0 on the 1-to-0 transition of the clock at the end of T2, and for a 1 on the 0-to-1
transition of the clock in the middle of Tw.
The READY input to the 8086/8088 has some stringent timing requirements. The timing
diagram in Figure 9–14 shows READY causing one wait state (Tw), along with the required
setup and hold times from the system clock. The timing requirement for this operation is met by
the internal READY synchronization circuitry of the 8284A clock generator. When the 8284A is
used for READY, the RDY (ready input to the 8284A) input occurs at the end of each T state.

RDY and the 8284A
RDY is the synchronized ready input to the 8284A clock generator. The timing diagram for this
input is provided in Figure 9–15. Although it differs from the timing for the READY input to the

FIGURE 9–14

8086/8088 READY input timing.

FIGURE 9–15

8284A RDY input timing.

8086/8088 HARDWARE SPECIFICATIONS

321

FIGURE 9–16 The internal
block diagram of the 8284A
clock generator. (Courtesy of
Intel Corporation.)

8086/8088, the internal 8284A circuitry guarantees the accuracy of the READY synchronization
provided to the 8086/8088 microprocessors.
Figure 9–16 again depicts the internal structure of the 8284A. The bottom half of this diagram is the READY synchronization circuitry. At the leftmost side, the RDY1 and AEN1 inputs
are ANDed, as are the RDY2 and AEN2 inputs. The outputs of the AND gates are then ORed to
generate the input to the one or two stages of synchronization. In order to obtain a logic 1 at the
inputs to the flip-flops, RDY1 ANDed with AEN1 must be active or RDY2 ANDed with AEN2
must be active.
The ASYNC input selects one stage of synchronization when it is a logic 1 and two stages
when it is a logic 0. If one stage is selected, then the RDY signal is kept from reaching the
8086/8088 READY pin until the next negative edge of the clock. If two stages are selected, the
first positive edge of the clock captures RDY in the first flip-flop. The output of this flip-flop is
fed to the second flip-flop, so on the next negative edge of the clock, the second flip-flop
captures RDY.
Figure 9–17 illustrates a circuit used to introduce almost any number of wait states for the
8086/8088 microprocessors. Here, an 8-bit serial shift register (74LS164) shifts a logic 0 for one
or more clock periods from one of its Q outputs through to the RDY1 input of the 8284A. With
appropriate strapping, this circuit can provide various numbers of wait states. Notice also how
the shift register is cleared back to its starting point. The output of the register is forced high
when the RD, WR, and INTA pins are all logic 1s. These three signals are high until state T2, so
the shift register shifts for the first time when the positive edge of the T2 arrives. If one wait is
desired, output QB is connected to the OR gate. If two waits are desired, output QC is connected,
and so forth.
Notice in Figure 9–17 that this circuit does not always generate wait states. It is enabled
from the memory only for memory devices that require the insertion of waits. If the selection
signal from a memory device is a logic 0, the device is selected; then this circuit will generate a
wait state.
Figure 9–18 illustrates the timing diagram for this shift register wait state generator when
it is wired to insert one wait state. The timing diagram also illustrates the internal contents of the
shift register’s flip-flops to present a more detailed view of its operation. In this example, one
wait state is generated.

322

FIGURE 9–17

A circuit that will cause between 0 and 7 wait states.

FIGURE 9–18

Wait state generation timing of the circuit of Figure 9–17.

8086/8088 HARDWARE SPECIFICATIONS

9–6

323

MINIMUM MODE VERSUS MAXIMUM MODE
There are two available modes of operation for the 8086/8088 microprocessors: minimum mode
and maximum mode. Minimum mode operation is obtained by connecting the mode selection
pin MN>MX to +5.0 V, and maximum mode is selected by grounding this pin. Both modes
enable different control structures for the 8086/8088 microprocessors. The mode of operation
provided by minimum mode is similar to that of the 8085A, the most recent Intel 8-bit microprocessor. The maximum mode is unique and designed to be used whenever a coprocessor exists
in a system. Note that the maximum mode was dropped from the Intel family beginning with the
80286 microprocessor.

Minimum Mode Operation
Minimum mode operation is the least expensive way to operate the 8086/8088 microprocessors
(see Figure 9–19 for the minimum mode 8088 system). It costs less because all the control signals for the memory and I/O are generated by the microprocessor. These control signals are identical to those of the Intel 8085A, an earlier 8-bit microprocessor. The minimum mode allows the
8085A 8-bit peripherals to be used with the 8086/8088 without any special considerations.

Maximum Mode Operation
Maximum mode operation differs from minimum mode in that some of the control signals must
be externally generated. This requires the addition of an external bus controller—the 8288 bus
controller (see Figure 9–20 for the maximum mode 8088 system). There are not enough pins on
the 8086/8088 for bus control during maximum mode because new pins and new features have
replaced some of them. Maximum mode is used only when the system contains external
coprocessors such as the 8087 arithmetic coprocessor.

FIGURE 9–19

Minimum mode 8088 system.

324

FIGURE 9–20

CHAPTER 9

Maximum mode 8088 system.

The 8288 Bus Controller
An 8086/8088 system that is operated in maximum mode must have an 8288 bus controller
to provide the signals eliminated from the 8086/8088 by the maximum mode operation.
Figure 9–21 illustrates the block diagram and pin-out of the 8288 bus controller.
Notice that the control bus developed by the 8288 bus controller contains separate signals
for I/O (IORC and IOWC) and memory (MRDC and MWTC). It also contains advanced memory (AMWC) and I/O (AIOWC) write strobes, and the INTA signal. These signals replace the
minimum mode ALE, WR, IO/M, DT/R, DEN, and INTA, which are lost when the 8086/8088
microprocessors are operated in the maximum mode.

FIGURE 9–21

The 8288 bus controller; (a) block diagram and (b) pin-out.

8086/8088 HARDWARE SPECIFICATIONS

325

Pin Functions
The following list provides a description of each pin of the 8288 bus controller.
S2, S1, and S0

CLK
ALE
DEN

DT>R
AEN
CEN
IOB
AIOWC
IORC
IOWC
AMWT
MWTC
MRDC
INTA
MCE>PDEN

9–7

Status inputs are connected to the status output pins on the 8086/8088
microprocessor. These three signals are decoded to generate the timing signals for the system.
The clock input provides internal timing and must be connected to the CLK
output pin of the 8284A clock generator.
The address latch enable output is used to demultiplex the address/data
bus.
The data bus enable pin controls the bidirectional data bus buffers in the
system. Note that this is an active high output pin that is the opposite polarity from the DEN signal found on the microprocessor when operated in the
minimum mode.
The data transmit/receive signal is output by the 8288 to control the direction of the bidirectional data bus buffers.
The address enable input causes the 8288 to enable the memory control
signals.
The control enable input enables the command output pins on the 8288.
The I/O bus mode input selects either the I/O bus mode or system bus
mode operation.
The advanced I/O write is a command output used to provide I/O with an
advanced I/O write control signal.
The I/O read command output provides I/O with its read control signal.
The I/O write command output provides I/O with its main write signal.
The advanced memory write control pin provides memory with an early
or advanced write signal.
The memory write control pin provides memory with its normal write control signal.
The memory read control pin provides memory with a read control signal.
The interrupt acknowledge output acknowledges an interrupt request
input applied to the INTR pin.
The master cascade/peripheral data output selects cascade operation for
an interrupt controller if IOB is grounded, and enables the I/O bus transceivers if IOB is tied high.

SUMMARY
1. The main differences between the 8086 and 8088 are (1) an 8-bit data bus on the 8088 and a
16-bit data bus on the 8086, (2) an SS0 pin on the 8088 in place of BHE /S7 on the 8086, and
(3) an IO/M pin on the 8088 instead of an M/ IO on the 8086.
2. Both the 8086 and 8088 require a single +5.0 V power supply with a tolerance of ±10%.
3. The 8086/8088 microprocessors are TTL-compatible if the noise immunity figure is derated
to 350 mV from the customary 400 mV.
4. The 8086/8088 microprocessors can drive one 74XX, five 74LSXX, one 74SXX, ten
74ALSXX, and ten 74HCXX unit loads.

326

CHAPTER 9

5. The 8284A clock generator provides the system clock (CLK), READY synchronization, and
RESET synchronization.
6. The standard 5 MHz 8086/8088 operating frequency is obtained by attaching a 15 MHz
crystal to the 8284A clock generator. The PCLK output contains a TTL-compatible signal at
one half the CLK frequency.
7. Whenever the 8086/8088 microprocessors are reset, they begin executing software at memory location FFFF0H (FFFF:0000) with the interrupt request pin disabled.
8. Because the 8086/8088 buses are multiplexed and most memory and I/O devices aren’t, the
system must be demultiplexed before interfacing with memory or I/O. Demultiplexing is
accomplished by an 8-bit latch whose clock pulse is obtained from the ALE signal.
9. In a large system, the buses must be buffered because the 8086/8088 microprocessors are
capable of driving only 10 unit loads, and large systems often have many more.
10. Bus timing is very important to the remaining chapters in the text. A bus cycle that consists
of four clocking periods acts as the basic system timing. Each bus cycle is able to read or
write data between the microprocessor and the memory or I/O system.
11. A bus cycle is broken into four states, or T periods: T1 is used by the microprocessor to send
the address to the memory or I/O and the ALE signal to the demultiplexers; T2 is used to
send data to memory for a write and to test the READY pin and activate control signals RD
or WR; T3 allows the memory time to access data and allows data to be transferred between
the microprocessor and the memory or I/O; and T4 is where data are written.
12. The 8086/8088 microprocessors allow the memory and I/O 460 ns to access data when they
are operated with a 5 MHz clock.
13. Wait states (Tw) stretch the bus cycle by one or more clocking periods to allow the memory
and I/O additional access time. Wait states are inserted by controlling the READY input to
the 8086/8088. READY is sampled at the end of T2 and during Tw.
14. Minimum mode operation is similar to that of the Intel 8085A microprocessor, whereas
maximum mode operation is new and specifically designed for the operation of the 8087
arithmetic coprocessor.
15. The 8288 bus controller must be used in the maximum mode to provide the control bus signals to the memory and I/O. This is because the maximum mode operation of the 8086/8088
removes some of the system’s control signal lines in favor of control signals for the
coprocessors. The 8288 reconstructs these removed control signals.

9–8

QUESTIONS AND PROBLEMS
1. List the differences between the 8086 and the 8088 microprocessors.
2. Is the 8086/8088 TTL-compatible? Explain your answer.
3. What is the fan-out from the 8086/8088 to the following devices:
(a) 74XXX TTL
(b) 74ALSXXX TTL
(c) 74HCXXX CMOS
4. What information appears on the address/data bus of the 8088 while ALE is active?
5. What are the purposes of status bits S3 and S4?
6. What condition does a logic 0 on the 8086/8088 RD pin indicate?
7. Explain the operation of the TEST pin and the WAIT instruction.
8. Describe the signal that is applied to the CLK input pin of the 8086/8088 microprocessors.
9. What mode of operation is selected when MN>MX is grounded?
10. What does the WR strobe signal from the 8086/8088 indicate about the operation of the
8086/8088?

8086/8088 HARDWARE SPECIFICATIONS

327

11. When does ALE float to its high-impedance state?
12. When DT>R is a logic 1, what condition does it indicate about the operation of the
8086/8088?
13. What happens when the HOLD input to the 8086/8088 is placed at its logic 1 level?
14. What three minimum mode 8086/8088 pins are decoded to discover whether the processor is
halted?
15. Explain the operation of the LOCK pin.
16. What conditions do the QS1 and QS0 pins indicate about the 8086/8088?
17. What three housekeeping chores are provided by the 8284A clock generator?
18. By what factor does the 8284A clock generator divide the crystal oscillator’s output
frequency?
19. If the F>C pin is placed at a logic 1 level, the crystal oscillator is disabled. Where is the
timing input signal attached to the 8284A under this condition?
20. The PCLK output of the 8284A is ____________ MHz if the crystal oscillator is operating
at 14 MHz.
21. The RES input to the 8284A is placed at a logic ____________ level in order to reset the
8086/8088.
22. Which bus connections on the 8086 microprocessor are typically demultiplexed?
23. Which bus connections on the 8088 microprocessor are typically demultiplexed?
24. Which TTL-integrated circuit is often used to demultiplex the buses on the 8086/8088?
25. What is the purpose of the demultiplexed BHE signal on the 8086 microprocessor?
26. Why are buffers often required in an 8086/8088-based system?
27. What 8086/8088 signal is used to select the direction of the data flows through the 74LS245
bidirectional bus buffer?
28. A bus cycle is equal to clocking ____________ periods.
29. If the CLK input to the 8086/8088 is 4 MHz, how long is one bus cycle?
30. What two 8086/8088 operations occur during a bus cycle?
31. How many MIPS is the 8086/8088 capable of obtaining when operated with a 10 MHz
clock?
32. Briefly describe the purpose of each T state listed:
(a) T1
(b) T2
(c) T3
(d) T4
(e) Tw
33. How much time is allowed for memory access when the 8086/8088 is operated with a
5 MHz clock?
34. How wide is DEN if the 8088 is operated with a 5 MHz clock?
35. If the READY pin is grounded, it will introduce____________ states into the bus cycle of
the 8086/8088.
36. What does the ASYNC input to the 8284A accomplish?
37. What logic levels must be applied to AEN1 and RDY1 to obtain a logic 1 at the READY pin?
(Assume that AEN2 is at a logic 1 level.)
38. Contrast minimum and maximum mode 8086/8088 operation.
39. What main function is provided by the 8288 bus controller when used with 8086/8088 maximum mode operation?

CHAPTER 10
Memory Interface

INTRODUCTION
Whether simple or complex, every microprocessor-based system has a memory system. The
Intel family of microprocessors is no different from any other in this respect. Almost all systems contain two main types of memory: read-only memory (ROM) and random access
memory (RAM) or read/write memory. Read-only memory contains system software and
permanent system data, while RAM contains temporary data and application software. This
chapter explains how to interface both memory types to the Intel family of microprocessors.
We demonstrate memory interface to an 8-, 16-, 32-, and 64-bit data bus by using various
memory address sizes. This allows virtually any microprocessor to be interfaced to any
memory system.

CHAPTER OBJECTIVES
Upon completion of this chapter, you will be able to:
1. Decode the memory address and use the outputs of the decoder to select various memory
components.
2. Use programmable logic devices (PLDs) to decode memory addresses.
3. Explain how to interface both RAM and ROM to a microprocessor.
4. Explain how error correction code (ECC) is used with memory.
5. Interface memory to an 8-, 16-, 32-, and 64-bit data bus.
6. Explain the operation of a dynamic RAM controller.
7. Interface dynamic RAM to the microprocessor.

10–1

MEMORY DEVICES
Before attempting to interface memory to the microprocessor, it is essential to completely understand the operation of memory components. In this section, we explain the functions of the four
common types of memory: read-only memory (ROM), flash memory (EEPROM), static random
access memory (SRAM), and dynamic random access memory (DRAM).

328

MEMORY INTERFACE

329

Memory Pin Connections
Pin connections common to all memory devices are the address inputs, data outputs or input/
outputs, some type of selection input, and at least one control input used to select a read or write
operation. See Figure 10–1 for ROM and RAM generic-memory devices.

Address Connections. All memory devices have address inputs that select a memory location
within the memory device. Address inputs are almost always labeled from A0, the least significant address input, to An where subscript n can be any value but is always labeled as one less
than the total number of address pins. For example, a memory device with 10 address pins has its
address pins labeled from A0 to A9. The number of address pins found on a memory device is
determined by the number of memory locations found within it.
Today, the more common memory devices have between 1K (1024) to 1G (1,073,741,824)
memory locations, with 4G and larger memory location devices on the horizon. A 1K memory
device has 10 address pins (A0–A9); therefore, 10 address inputs are required to select any of its
1024 memory locations. It takes a 10-bit binary number (1024 different combinations) to select
any single location on a 1024-location device. If a memory device has 11 address connections
(A0–A11), it has 2048 (2K) internal memory locations. The number of memory locations can
thus be extrapolated from the number of address pins. For example, a 4K memory device has
12 address connections, an 8K device has 13, and so forth. A device that contains 1M locations
requires a 20-bit address (A0–A19).
The number 400H represents a 1K-byte section of the memory system. If a memory device is
decoded to begin at memory address 10000H and it is a 1K device, its last location is at address
103FFH—one location less than 400H. Another important hexadecimal number to remember is
1000H, because 1000H is 4K. A memory device that contains a starting address of 14000H that is 4K
bytes long ends at location 14FFFH—one location less than 1000H. A third number is 64K, or
10000H. A memory that starts at location 30000H and ends at location 3FFFFH is a 64K-byte memory. Finally, because 1M of memory is common, a 1M memory contains 100000H memory locations.
Data Connections. All memory devices have a set of data outputs or input/outputs. The device
illustrated in Figure 10–1 has a common set of input/output (I/O) connections. Today, many
memory devices have bidirectional common I/O pins.
The data connections are the points at which data are entered for storage or extracted for
reading. Data pins on memory devices are labeled D0 through D7 for an 8-bit-wide memory
FIGURE 10–1 A pseudomemory component illustrating the address, data, and
control connections.

330

CHAPTER 10

device. In this sample memory device there are 8 I/O connections, meaning that the memory
device stores 8 bits of data in each of its memory locations. An 8-bit-wide memory device is
often called a byte-wide memory. Although most devices are currently 8 bits wide, some devices
are 16 bits, 4 bits, or just 1 bit wide.
Catalog listings of memory devices often refer to memory locations times bits per location.
For example, a memory device with 1K memory locations and 8 bits in each location is often
listed as a 1K × 8 by the manufacturer. A 16K × 1 is a memory device containing 16K 1-bit memory locations. Memory devices are often classified according to total bit capacity. For example, a
1K × 8-bit memory device is sometimes listed as an 8K memory device, or a 64K × 4 memory is
listed as a 256K device. These variations occur from one manufacturer to another.

Selection Connections. Each memory device has an input—sometimes more than one—that
selects or enables the memory device. This type of input is most often called a chip select (CS),
chip enable (CE), or simply select (S) input. RAM memory generally has at least one CS or S
input, and ROM has at least one CE. If the CE, CS, or S input is active (a logic 0, in this case,
because of the overbar), the memory device performs a read or write operation; if it is inactive (a
logic 1, in this case), the memory device cannot do a read or a write because it is turned off or
disabled. If more than one CS connection is present, all must be activated to read or write data.
Control Connections. All memory devices have some form of control input or inputs. A ROM
usually has only one control input, while a RAM often has one or two control inputs.
The control input most often found on a ROM is the output enable (OE) or gate (G) connection, which allows data to flow out of the output data pins of the ROM. If OE and the selection input (CE) are both active, the output is enabled; if OE is inactive, the output is disabled at
its high-impedance state. The OE connection enables and disables a set of three-state buffers
located within the memory device and must be active to read data.
A RAM memory device has either one or two control inputs. If there is only one control input,
it is often called R>W. This pin selects a read operation or a write operation only if the device is
selected by the selection input (CS). If the RAM has two control inputs, they are usually labeled WE
(or W), and OE (or G). Here, WE (write enable) must be active to perform a memory write, and OE
must be active to perform a memory read operation. When these two controls (WE and OE) are present, they must never both be active at the same time. If both control inputs are inactive (logic 1s),
data are neither written nor read, and the data connections are at their high-impedance state.

ROM Memory
The read-only memory (ROM) permanently stores programs and data that are resident to the system and must not change when power supply is disconnected. The ROM is permanently programmed so that data are always present, even when power is disconnected. This type of memory is
often called nonvolatile memory, because its contents do not change even if power is disconnected.
Today, the ROM is available in many forms. A device we call a ROM is purchased in mass
quantities from a manufacturer and programmed during its fabrication at the factory. The
EPROM (erasable programmable read-only memory), a type of ROM, is more commonly
used when software must be changed often or when too few are in demand to make the ROM
economical. For a ROM to be practical, we usually must purchase at least 10,000 devices to
recoup the factory programming charge. An EPROM is programmed in the field on a device
called an EPROM programmer. The EPROM is also erasable if exposed to high-intensity ultraviolet light for about 20 minutes or so, depending on the type of EPROM.
PROM memory devices are also available, although they are not as common today. The
PROM (programmable read-only memory) is also programmed in the field by burning open
tiny NI-chrome or silicon oxide fuses; but once it is programmed, it cannot be erased.

331

MEMORY INTERFACE
MODE SELECTION
PINS

CS
(20)

VPP
(21)

V IL
V IH

+5

+5

D OUT

Don't care

+5

+5

High Z

V IH

Don't care

+5

+5

High Z

Pulsed V IL to V IH
V IL

V IH

+25

+5

DIN

Program Verify

V IL

+25

+5

DOUT

Program Inhibit

V IL

V IH

+25

+5

High Z

MODE
PIN CONFIGURATION

Read

V IL

Deselect
A7
A6
A5
A4
A3
A2
A1
A0
O0
O1
O2
GND

24
23
22
21
20
19
18
17
16
15
14
13

1
2
3
4
5
6
7
8
9
10
11
12

V CC
A8
A9
V PP
CS
A 10
PD/PGM
O7
O6
O5
O4
O3

Power Down
Program

ADDRESSES
POWER DOWN/PROGRAM

CS

CHIP SELECT

O0 –O 7

OUTPUTS

FIGURE 10–2

DATA OUTPUTS
O0 –O7

V CC
GND
V PP
CS
PD/PGM

A 0 –A 10

VCC OUTPUTS
(24) (9-11, 13-17)

BLOCK DIAGRAM

PIN NAMES

PD/PGM

PD/PGM
(18)

A 0 –A 10
ADDRESS
INPUTS

CHIP SELECT,
POWER DOWN,
AND PROG. LOGIC

OUTPUT BUFFERS

Y
DECODER

Y-GATING

X
DECODER

16,384-BIT
CELL MATRIX

The pin-out of the 2716, 2K × 8 EPROM. (Courtesy of Intel Corporation.)

Still another, newer type of read-mostly memory (RMM) is called the flash memory. The
flash memory1 is also often called an EEPROM (electrically erasable programmable ROM),
EAROM (electrically alterable ROM), or a NOVRAM (nonvolatile RAM). These memory
devices are electrically erasable in the system, but they require more time to erase than a normal
RAM. The flash memory device is used to store setup information for systems such as the video
card in the computer. It has all but replaced the EPROM in most computer systems for the BIOS
memory. Some systems contain a password stored in the flash memory device. Flash memory
has its biggest impact in memory cards for digital cameras and memory in MP3 audio players.
Figure 10–2 illustrates the 2716 EPROM, which is representative of most common
EPROMs. This device contains 11 address inputs and eight data outputs. The 2716 is a 2K × 8
read-only memory device. The 27XXX series of the EPROMs includes the following part numbers: 2704 (512 × 8), 2708 (1K × 8), 2716 (2K × 8), 2732 (4K × 8), 2764 (8K × 8), 27128
(16K × 8), 27256 (32K × 8), 27512 (64K × 8), and 271024 (128K × 8). Each of these parts contains address pins, eight data connections, one or more chip selection inputs (CE), and an output
enable pin (OE).
Figure 10–3 illustrates the timing diagram for the 2716 EPROM. Data appear on the output connections only after a logic 0 is placed on both CE and OE pin connections. If CE and OE
are not both logic 0s, the data output connections remain at their high-impedance or off states.
Note that the VPP pin must be placed at a logic 1 level for data to be read from the EPROM. In
some cases, the VPP pin is in the same position as the WE pin on the SRAM. This will allow a
single socket to hold either an EPROM or an SRAM. An example is the 27256 EPROM and the
62256 SRAM, both 32K × 8 devices that have the same pin-out, except for VPP on the EPROM
and WE on the SRAM.
1Flash

memory is a trademark of Intel Corporation.

332

CHAPTER 10

FIGURE 10–3 The timing diagram of AC characteristics of the 2716 EPROM. (Courtesy of
Intel Corporation.)

One important piece of information provided by the timing diagram and data sheet is the memory access time—the time that it takes the memory to read information. As Figure 10–3 illustrates,
memory access time (TACC) is measured from the appearance of the address at the address inputs until
the appearance of the data at the output connections. This is based on the assumption that the CE input
goes low at the same time that the address inputs become stable. Also, OE must be a logic 0 for the
output connections to become active. The basic speed of this EPROM is 450 ns. (Recall that the
8086/8088 operated with a 5 MHz clock allowed memory 460 ns to access data.) This type of memory component requires wait states to operate properly with the 8086/8088 microprocessors because
of its rather long access time. If wait states are not desired, higher-speed versions of the EPROM are
available at an additional cost. Today, EPROM memory is available with access times of as little as
100 ns. Obviously, wait states are required in modern microprocessors for any EPROM device.

Static RAM (SRAM) Devices
Static RAM memory devices retain data for as long as DC power is applied. Because no special
action (except power) is required to retain stored data,