home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1991-03-01 | 1.1 MB | 20,649 lines
Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
%@1@%Microsoft(R) QuickAssembler Programmer's Guide Version 2.01%@EH@%%@NL@% %@NL@% %@NL@% %@AB@%════════════════════════════════════════════════════════════════════════════%@AE@%%@NL@% %@NL@% %@NL@% %@AB@%Microsoft(R) QuickAssembler Programmer's Guide Version 2.01%@AE@%%@NL@% %@NL@% %@NL@% %@AB@%════════════════════════════════════════════════════════════════════════════%@AE@%%@NL@% %@NL@% %@4@%%@EH@%%@NL@% Information in this document is subject to change without notice and does not represent a commitment on the part of Microsoft Corporation. The software described in this document is furnished under a license agreement or nondisclosure agreement. The software may be used or copied only in accordance with the terms of the agreement. It is against the law to copy the software on any medium except as specifically allowed in the license or nondisclosure agreement. No part of this manual may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying and recording, for any purpose without the express written permission of Microsoft.%@NL@% %@NL@% (C)Copyright Microsoft Corporation, 1989. All rights reserved.%@NL@% Simultaneously published in the U.S. and Canada.%@NL@% %@NL@% Printed and bound in the United States of America.%@NL@% %@NL@% Microsoft, MS, MS-DOS, GW-BASIC, QuickC, and XENIX are registered trademarks of Microsoft Corporation.%@NL@% %@NL@% IBM is a registered trademark of International Business Machines Corporation.%@NL@% %@NL@% Intel is a registered trademark of Intel Corporation.%@NL@% %@NL@% Document No. LN0114-201-R00-0689%@NL@% Part No. 06792%@NL@% 10 9 8 7 6 5 4 3 2 1%@NL@% %@NL@% %@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:Contents @%%@AB@%Table of Contents%@AE@%%@EH@%%@NL@% %@NL@% %@AB@%Introduction%@AE@%%@BO: 71b3@%%@NL@% %@NL@% %@AB@%Chapter 1 The QuickAssembler Interface%@AE@%%@BO: bdb6@%%@NL@% 1.1 Creating the Program%@BO: c2ee@%%@NL@% 1.2 Building and Running a Program%@BO: d078@%%@NL@% 1.3 Assembling from the Command Line%@BO: e4be@%%@NL@% 1.4 Choosing C or Assembler Defaults%@BO: ece3@%%@NL@% 1.5 Using the Quick Advisor (Help)%@BO: f864@%%@NL@% 1.6 Debugging Assembly Code%@BO: 10827@%%@NL@% 1.6.1 Debugging .COM Files%@BO: 10d74@%%@NL@% 1.6.2 Specifying Expressions%@BO: 11102@%%@NL@% 1.6.3 Tracing Execution%@BO: 13131@%%@NL@% 1.6.4 Modifying Registers and Flags%@BO: 13ded@%%@NL@% 1.7 Viewing a Listing File%@BO: 146ad@%%@NL@% %@NL@% %@AB@%Chapter 2 Introducing 8086 Assembly Language%@AE@%%@BO: 151ff@%%@NL@% 2.1 Programming the 8086 Family%@BO: 156a6@%%@NL@% 2.2 Instructions, Directives, and Operands%@BO: 1602a@%%@NL@% 2.2.1 The Name Field%@BO: 16928@%%@NL@% 2.2.2 The Operation Field%@BO: 16feb@%%@NL@% 2.2.3 The Operand Field%@BO: 1736e@%%@NL@% 2.2.4 The Comment Field%@BO: 17a6d@%%@NL@% 2.2.5 Entering Numbers in Different Bases%@BO: 183c3@%%@NL@% 2.2.6 Line-Continuation Character%@BO: 188aa@%%@NL@% 2.3 8086-Family Instructions%@BO: 18e01@%%@NL@% 2.3.1 Data-Manipulation Instructions%@BO: 1923f@%%@NL@% 2.3.1.1 The MOV Instruction%@BO: 19666@%%@NL@% 2.3.1.2 The ADD Instruction%@BO: 19c57@%%@NL@% 2.3.1.3 The SUB Instruction%@BO: 19f8c@%%@NL@% 2.3.1.4 The INC and DEC Instructions%@BO: 1a1b6@%%@NL@% 2.3.1.5 The AND Instruction%@BO: 1a40c@%%@NL@% 2.3.1.6 The MUL Instruction%@BO: 1a61b@%%@NL@% 2.3.2 Control-Flow Instructions%@BO: 1a91f@%%@NL@% 2.3.2.1 The JMP Instruction%@BO: 1ad5f@%%@NL@% 2.3.2.2 The CMP Instruction%@BO: 1af9f@%%@NL@% 2.3.2.3 The Conditional Jump Instructions%@BO: 1b4ae@%%@NL@% 2.4 Declaring Simple Data Objects%@BO: 1c0ef@%%@NL@% 2.5 8086-Family Registers%@BO: 1e475@%%@NL@% 2.5.1 The General-Purpose Registers%@BO: 1ef06@%%@NL@% 2.5.1.1 The AX Register%@BO: 1f77d@%%@NL@% 2.5.1.2 The BX Register%@BO: 2003e@%%@NL@% 2.5.1.3 The CX Register%@BO: 204b0@%%@NL@% 2.5.1.4 The DX Register%@BO: 20a0f@%%@NL@% 2.5.2 The Index Registers%@BO: 2112f@%%@NL@% 2.5.3 The Pointer Registers%@BO: 21724@%%@NL@% 2.5.3.1 The BP Register%@BO: 219b0@%%@NL@% 2.5.3.2 The SP Register%@BO: 21e9c@%%@NL@% 2.5.3.3 The IP Register%@BO: 227c9@%%@NL@% 2.5.4 The Flags Register%@BO: 22b46@%%@NL@% 2.6 Addressing Modes%@BO: 2404d@%%@NL@% 2.6.1 Immediate Operands%@BO: 24a4c@%%@NL@% 2.6.2 Register Operands%@BO: 25a37@%%@NL@% 2.6.3 Direct Memory Operands%@BO: 263fe@%%@NL@% 2.6.4 Indirect Memory Operands%@BO: 27375@%%@NL@% 2.7 Segmented Addressing and Segment Registers%@BO: 28c1c@%%@NL@% %@NL@% %@AB@%Chapter 3 Writing Assembly Modules for C Programs%@AE@%%@BO: 2aca0@%%@NL@% 3.1 A Skeleton for Procedure Modules%@BO: 2b09b@%%@NL@% 3.1.1 The .MODEL Directive%@BO: 2b5d8@%%@NL@% 3.1.2 The .CODE Directive%@BO: 2bcd7@%%@NL@% 3.1.3 The PROC Directive%@BO: 2c048@%%@NL@% 3.1.4 The ENDP and END Statements%@BO: 2ce1f@%%@NL@% 3.2 Instructions Used in This Chapter%@BO: 2d0aa@%%@NL@% 3.3 Decimal Conversion Example%@BO: 2da0c@%%@NL@% 3.4 Decimal Conversion with Far Data Pointers%@BO: 30c03@%%@NL@% 3.4.1 Writing a Model-Independent Procedure%@BO: 31d77@%%@NL@% 3.4.2 Accessing Far Data through ES%@BO: 32809@%%@NL@% 3.5 Hexadecimal Conversion Example%@BO: 32dd7@%%@NL@% %@NL@% %@AB@%Chapter 4 Writing Stand-Alone Assembly Programs%@AE@%%@BO: 34dba@%%@NL@% 4.1 A Skeleton for Stand-Alone Programs%@BO: 352d5@%%@NL@% 4.1.1 The .MODEL Directive%@BO: 35c29@%%@NL@% 4.1.2 The .STACK, .CODE, and .DATA Directives%@BO: 36319@%%@NL@% 4.1.3 The .STARTUP Directive%@BO: 368bc@%%@NL@% 4.2 Instructions Used in This Chapter%@BO: 36f98@%%@NL@% 4.3 A Program That Says Hello%@BO: 3793a@%%@NL@% 4.4 Inside the Stack Segment%@BO: 3817c@%%@NL@% 4.5 Inside the Data Segment%@BO: 386bf@%%@NL@% 4.6 Inside the Code Segment%@BO: 391bd@%%@NL@% 4.7 Making the Program Repeat Itself%@BO: 3b04f@%%@NL@% 4.8 Creating .COM Files%@BO: 3c62a@%%@NL@% 4.9 Creating .COM Files with Full Segment Definitions%@BO: 3e206@%%@NL@% %@NL@% %@AB@%Chapter 5 Defining Segment Structure%@AE@%%@BO: 3ffad@%%@NL@% 5.1 Simplified Segment Directives%@BO: 406ed@%%@NL@% 5.1.1 Understanding Memory Models%@BO: 40c7f@%%@NL@% 5.1.2 Specifying DOS Segment Order%@BO: 41df5@%%@NL@% 5.1.3 Defining Basic Attributes of the Module%@BO: 42b44@%%@NL@% 5.1.4 Defining Simplified Segments%@BO: 44e63@%%@NL@% 5.1.4.1 How to Use Simplified Segments%@BO: 45417@%%@NL@% 5.1.4.2 How Simplified Segments Are Implemented%@BO: 46b2b@%%@NL@% 5.1.5 Using Predefined Segment Equates%@BO: 47722@%%@NL@% 5.1.6 Simplified Segment Defaults%@BO: 48b18@%%@NL@% 5.1.7 Default Segment Names%@BO: 493ed@%%@NL@% 5.2 Full Segment Definitions%@BO: 4b9a0@%%@NL@% 5.2.1 Setting the Segment-Order Method%@BO: 4bb75@%%@NL@% 5.2.2 Defining Full Segments%@BO: 4c98d@%%@NL@% 5.2.2.1 Controlling Alignment with Align Type%@BO: 4d1c0@%%@NL@% 5.2.2.2 Defining Segment Combinations with Combine Type%@BO: 4d84e@%%@NL@% 5.2.2.3 Controlling Segment Structure with Class Type%@BO: 4f990@%%@NL@% 5.3 Defining Segment Groups%@BO: 512db@%%@NL@% 5.4 Associating Segments with Registers%@BO: 52503@%%@NL@% 5.5 Initializing Segment Registers%@BO: 53a35@%%@NL@% 5.5.1 Initializing the CS and IP Registers%@BO: 54127@%%@NL@% 5.5.2 Initializing the DS Register%@BO: 54d57@%%@NL@% 5.5.3 Initializing the SS and SP Registers%@BO: 55651@%%@NL@% 5.5.4 Initializing the ES Register%@BO: 5655f@%%@NL@% 5.6 Nesting Segments%@BO: 5678c@%%@NL@% %@NL@% %@AB@%Chapter 6 Defining Constants, Labels, and Variables%@AE@%%@BO: 5749b@%%@NL@% 6.1 Constants%@BO: 57adf@%%@NL@% 6.1.1 Integer Constants%@BO: 57c8a@%%@NL@% 6.1.1.1 Specifying Integers with Radix Specifiers%@BO: 58381@%%@NL@% 6.1.1.2 Setting the Default Radix%@BO: 58a87@%%@NL@% 6.1.2 Packed Binary Coded Decimal Constants%@BO: 59313@%%@NL@% 6.1.3 Real-Number Constants%@BO: 5971d@%%@NL@% 6.1.4 String Constants%@BO: 5a65b@%%@NL@% 6.1.5 Determining Floating-Point Format%@BO: 5ac90@%%@NL@% 6.2 Assigning Names to Symbols%@BO: 5b018@%%@NL@% 6.3 Using Type Specifiers%@BO: 5cd7a@%%@NL@% 6.4 Defining Code Labels%@BO: 5d761@%%@NL@% 6.4.1 Near-Code Labels%@BO: 5d8ad@%%@NL@% 6.4.2 Anonymous Labels%@BO: 5e370@%%@NL@% 6.4.3 Procedure Labels%@BO: 5ea93@%%@NL@% 6.4.4 Code Labels Defined with the LABEL Directive%@BO: 5f8c9@%%@NL@% 6.5 Defining and Initializing Data%@BO: 5ff03@%%@NL@% 6.5.1 Variables%@BO: 60128@%%@NL@% 6.5.1.1 Integer Variables%@BO: 6093b@%%@NL@% 6.5.1.2 Binary Coded Decimal Variables%@BO: 61d5d@%%@NL@% 6.5.1.3 String Variables%@BO: 624c4@%%@NL@% 6.5.1.4 Real-Number Variables%@BO: 62baf@%%@NL@% 6.5.2 Arrays and Buffers%@BO: 65365@%%@NL@% 6.5.3 Labeling Variables%@BO: 66496@%%@NL@% 6.5.4 Pointer Variables%@BO: 668f9@%%@NL@% 6.6 Setting the Location Counter%@BO: 67109@%%@NL@% 6.7 Aligning Data%@BO: 67efe@%%@NL@% %@NL@% %@AB@%Chapter 7 Using Structures and Records%@AE@%%@BO: 69661@%%@NL@% 7.1 Structures%@BO: 69ad4@%%@NL@% 7.1.1 Declaring Structure Types%@BO: 69fa7@%%@NL@% 7.1.2 Defining Structure Variables%@BO: 6abcb@%%@NL@% 7.1.3 Using Structure Operands%@BO: 6bb03@%%@NL@% 7.2 Records%@BO: 6c3c6@%%@NL@% 7.2.1 Declaring Record Types%@BO: 6c897@%%@NL@% 7.2.2 Defining Record Variables%@BO: 6d883@%%@NL@% 7.2.3 Using Record Operands and Record Variables%@BO: 6e991@%%@NL@% 7.2.4 Record Operators%@BO: 6f341@%%@NL@% 7.2.4.1 The MASK Operator%@BO: 6f465@%%@NL@% 7.2.4.2 The WIDTH Operator%@BO: 6fc80@%%@NL@% 7.2.5 Using Record-Field Operands%@BO: 7047a@%%@NL@% %@NL@% %@AB@%Chapter 8 Creating Programs from Multiple Modules%@AE@%%@BO: 70f6e@%%@NL@% 8.1 Declaring Symbols Public%@BO: 71a9a@%%@NL@% 8.2 Declaring Symbols External%@BO: 7299e@%%@NL@% 8.3 Using Multiple Modules%@BO: 74c1a@%%@NL@% 8.4 Declaring Symbols Communal%@BO: 75dae@%%@NL@% 8.5 Specifying Library Files%@BO: 78240@%%@NL@% %@NL@% %@AB@%Chapter 9 Using Operands and Expressions%@AE@%%@BO: 78a1d@%%@NL@% 9.1 Using Operands with Directives%@BO: 78f56@%%@NL@% 9.2 Using Operators%@BO: 799d6@%%@NL@% 9.2.1 Calculation Operators%@BO: 79f46@%%@NL@% 9.2.1.1 Arithmetic Operators%@BO: 7a0b8@%%@NL@% 9.2.1.2 Structure-Field-Name Operator%@BO: 7afe8@%%@NL@% 9.2.1.3 Index Operator%@BO: 7b606@%%@NL@% 9.2.1.4 Shift Operators%@BO: 7c2cb@%%@NL@% 9.2.1.5 Bitwise Logical Operators%@BO: 7c89d@%%@NL@% 9.2.2 Relational Operators%@BO: 7d2f0@%%@NL@% 9.2.3 Segment-Override Operator%@BO: 7e029@%%@NL@% 9.2.4 Type Operators%@BO: 7e884@%%@NL@% 9.2.4.1 PTR Operator%@BO: 7e982@%%@NL@% 9.2.4.2 SHORT Operator%@BO: 7f627@%%@NL@% 9.2.4.3 THIS Operator%@BO: 7faf7@%%@NL@% 9.2.4.4 HIGH and LOW Operators%@BO: 8003d@%%@NL@% 9.2.4.5 SEG Operator%@BO: 80576@%%@NL@% 9.2.4.6 OFFSET Operator%@BO: 809dd@%%@NL@% 9.2.4.7 .TYPE Operator%@BO: 813fa@%%@NL@% 9.2.4.8 TYPE Operator%@BO: 81de8@%%@NL@% 9.2.4.9 LENGTH Operator%@BO: 821b8@%%@NL@% 9.2.4.10 SIZE Operator%@BO: 828bd@%%@NL@% 9.2.5 Operator Precedence%@BO: 83028@%%@NL@% 9.3 Using the Location Counter%@BO: 83a2d@%%@NL@% 9.4 Using Forward References%@BO: 84253@%%@NL@% 9.4.1 Forward References to Labels%@BO: 845be@%%@NL@% 9.4.2 Forward References to Variables%@BO: 855f0@%%@NL@% 9.5 Strong Typing for Memory Operands%@BO: 85b10@%%@NL@% %@NL@% %@AB@%Chapter 10 Assembling Conditionally%@AE@%%@BO: 867d7@%%@NL@% 10.1 Using Conditional-Assembly Directives%@BO: 86c68@%%@NL@% 10.1.1 Testing Expressions with IF and IFE Directives%@BO: 875ae@%%@NL@% 10.1.2 Testing the Pass with IF1 and IF2 Directives%@BO: 87b1f@%%@NL@% 10.1.3 Testing Symbol Definition with IFDEF and IFNDEF Directives%@BO: 880e2@%%@NL@% 10.1.4 Verifying Macro Parameters with IFB and IFNB Directives%@BO: 889a1@%%@NL@% 10.1.5 Comparing Macro Arguments with IFIDN and IFDIF Directives%@BO: 89144@%%@NL@% 10.1.6 ELSEIF Directives%@BO: 89a62@%%@NL@% 10.2 Using Conditional-Error Directives%@BO: 8a75c@%%@NL@% 10.2.1 Generating Unconditional Errors with .ERR, .ERR1, and .ERR2 Directives%@BO: 8b0d5@%%@NL@% 10.2.2 Testing Expressions with .ERRE or .ERRNZ Directives%@BO: 8b9ee@%%@NL@% 10.2.3 Verifying Symbol Definition with .ERRDEF and .ERRNDEF Directives%@BO: 8c16c@%%@NL@% 10.2.4 Testing for Macro Parameters with .ERRB and .ERRNB Directives%@BO: 8c715@%%@NL@% 10.2.5 Comparing Macro Arguments with .ERRIDN and .ERRDIF Directives%@BO: 8ce3c@%%@NL@% %@NL@% %@AB@%Chapter 11 Using Equates, Macros, and Repeat Blocks%@AE@%%@BO: 8d6e4@%%@NL@% 11.1 Using Equates%@BO: 8dc18@%%@NL@% 11.1.1 Redefinable Numeric Equates%@BO: 8ddc1@%%@NL@% 11.1.2 Nonredefinable Numeric Equates%@BO: 8e80f@%%@NL@% 11.1.3 String Equates%@BO: 8f1c6@%%@NL@% 11.1.4 Predefined Equates%@BO: 8ff00@%%@NL@% 11.2 Using Macros%@BO: 91763@%%@NL@% 11.2.1 Defining Macros%@BO: 91f51@%%@NL@% 11.2.2 Calling Macros%@BO: 92d97@%%@NL@% 11.2.3 Using Local Symbols%@BO: 93a20@%%@NL@% 11.2.4 Exiting from a Macro%@BO: 94af2@%%@NL@% 11.3 Text-Macro String Directives%@BO: 95341@%%@NL@% 11.3.1 The SUBSTR Directive%@BO: 95c51@%%@NL@% 11.3.2 The CATSTR Directive%@BO: 962fa@%%@NL@% 11.3.3 The SIZESTR Directive%@BO: 96760@%%@NL@% 11.3.4 The INSTR Directive%@BO: 96c6e@%%@NL@% 11.3.5 Using String Directives Inside Macros%@BO: 97329@%%@NL@% 11.4 Defining Repeat Blocks%@BO: 982dc@%%@NL@% 11.4.1 The REPT Directive%@BO: 9891a@%%@NL@% 11.4.2 The IRP Directive%@BO: 98e70@%%@NL@% 11.4.3 The IRPC Directive%@BO: 99749@%%@NL@% 11.5 Using Macro Operators%@BO: 9a1b8@%%@NL@% 11.5.1 Substitute Operator%@BO: 9a58b@%%@NL@% 11.5.2 Literal-Text Operator%@BO: 9b48f@%%@NL@% 11.5.3 Literal-Character Operator%@BO: 9bf61@%%@NL@% 11.5.4 Expression Operator%@BO: 9c525@%%@NL@% 11.5.5 Macro Comments%@BO: 9e12a@%%@NL@% 11.6 Using Recursive, Nested, and Redefined Macros%@BO: 9e5c9@%%@NL@% 11.6.1 Using Recursion%@BO: 9e78b@%%@NL@% 11.6.2 Nesting Macro Definitions%@BO: 9ec85@%%@NL@% 11.6.3 Nesting Macro Calls%@BO: 9f74e@%%@NL@% 11.6.4 Redefining Macros%@BO: 9ffc4@%%@NL@% 11.6.5 Avoiding Inadvertent Substitutions%@BO: a0677@%%@NL@% 11.7 Managing Macros and Equates%@BO: a0af7@%%@NL@% 11.7.1 Using Include Files%@BO: a0d21@%%@NL@% 11.7.2 Purging Macros from Memory%@BO: a1a12@%%@NL@% %@NL@% %@AB@%Chapter 12 Controlling Assembly Output%@AE@%%@BO: a2236@%%@NL@% 12.1 Sending Messages to the Standard Output Device%@BO: a2455@%%@NL@% 12.2 Controlling Page Format in Listings%@BO: a2ad0@%%@NL@% 12.2.1 Setting the Listing Title%@BO: a2dc2@%%@NL@% 12.2.2 Setting the Listing Subtitle%@BO: a32b6@%%@NL@% 12.2.3 Controlling Page Breaks%@BO: a39b9@%%@NL@% 12.2.4 Naming the Module%@BO: a4677@%%@NL@% 12.3 Controlling the Contents of Listings%@BO: a47ae@%%@NL@% 12.3.1 Suppressing and Restoring Listing Output%@BO: a4c32@%%@NL@% 12.3.2 Controlling Listing of Conditional Blocks%@BO: a518f@%%@NL@% 12.3.3 Controlling Listing of Macros%@BO: a5d0c@%%@NL@% %@NL@% %@AB@%Chapter 13 Loading, Storing, and Moving Data%@AE@%%@BO: a71ac@%%@NL@% 13.1 Transferring Data%@BO: a7396@%%@NL@% 13.1.1 Copying Data%@BO: a759d@%%@NL@% 13.1.2 Exchanging Data%@BO: a8091@%%@NL@% 13.1.3 Looking Up Data%@BO: a8377@%%@NL@% 13.1.4 Transferring Flags%@BO: a90c8@%%@NL@% 13.2 Converting between Data Sizes%@BO: a94e2@%%@NL@% 13.2.1 Extending Signed Values%@BO: a9772@%%@NL@% 13.2.2 Extending Unsigned Values%@BO: a9de1@%%@NL@% 13.3 Loading Pointers%@BO: aa128@%%@NL@% 13.3.1 Loading Near Pointers%@BO: aa259@%%@NL@% 13.3.2 Loading Far Pointers%@BO: aa7dc@%%@NL@% 13.4 Transferring Data to and from the Stack%@BO: aaec9@%%@NL@% 13.4.1 Pushing and Popping%@BO: ab441@%%@NL@% 13.4.2 Using the Stack%@BO: ac5c8@%%@NL@% 13.4.3 Saving Flags on the Stack%@BO: acdc0@%%@NL@% 13.4.4 Saving All Registers on the Stack%@BO: ad126@%%@NL@% 13.5 Transferring Data to and from Ports%@BO: ad681@%%@NL@% %@NL@% %@AB@%Chapter 14 Doing Arithmetic and Bit Manipulations%@AE@%%@BO: ae658@%%@NL@% 14.1 Adding%@BO: ae8c6@%%@NL@% 14.1.1 Adding Values Directly%@BO: aed84@%%@NL@% 14.1.2 Adding Values in Multiple Registers%@BO: afe06@%%@NL@% 14.2 Subtracting%@BO: b06f6@%%@NL@% 14.2.1 Subtracting Values Directly%@BO: b0c2b@%%@NL@% 14.2.2 Subtracting with Values in Multiple Registers%@BO: b1c0d@%%@NL@% 14.3 Multiplying%@BO: b2553@%%@NL@% 14.4 Dividing%@BO: b3ae1@%%@NL@% 14.5 Calculating with Binary Coded Decimals%@BO: b4ede@%%@NL@% 14.5.1 Unpacked BCD Numbers%@BO: b53c9@%%@NL@% 14.5.2 Packed BCD Numbers%@BO: b69dc@%%@NL@% 14.6 Doing Logical Bit Manipulations%@BO: b74b7@%%@NL@% 14.6.1 AND Operations%@BO: b8064@%%@NL@% 14.6.2 OR Operations%@BO: b8a3f@%%@NL@% 14.6.3 XOR Operations%@BO: b9322@%%@NL@% 14.6.4 NOT Operations%@BO: b9cba@%%@NL@% 14.7 Shifting and Rotating Bits%@BO: ba3f6@%%@NL@% 14.7.1 Multiplying and Dividing by Constants%@BO: bb18b@%%@NL@% 14.7.2 Moving Bits to the Least-Significant Position%@BO: bbea4@%%@NL@% 14.7.3 Adjusting Masks%@BO: bc14a@%%@NL@% 14.7.4 Shifting Multiword Values%@BO: bc6f5@%%@NL@% %@NL@% %@AB@%Chapter 15 Controlling Program Flow%@AE@%%@BO: bcdd3@%%@NL@% 15.1 Jumping%@BO: bcff4@%%@NL@% 15.1.1 Jumping Unconditionally%@BO: bd2e4@%%@NL@% 15.1.2 Jumping Conditionally%@BO: be4fa@%%@NL@% 15.1.2.1 Comparing and Jumping%@BO: bf2c9@%%@NL@% 15.1.2.2 Jumping Based on Flag Status%@BO: c0ebd@%%@NL@% 15.1.2.3 Testing Bits and Jumping%@BO: c19f4@%%@NL@% 15.2 Looping%@BO: c265d@%%@NL@% 15.3 Using Procedures%@BO: c3a3e@%%@NL@% 15.3.1 Calling Procedures%@BO: c470d@%%@NL@% 15.3.2 Defining Procedures%@BO: c4c26@%%@NL@% 15.3.3 Passing Arguments on the Stack%@BO: c60b0@%%@NL@% 15.3.4 Declaring Parameters with the PROC Directive%@BO: c7284@%%@NL@% 15.3.5 Using Local Variables%@BO: c9deb@%%@NL@% 15.3.6 Creating Locals Automatically%@BO: cb138@%%@NL@% 15.3.7 Variable Scope%@BO: cc4c8@%%@NL@% 15.3.8 Setting Up Stack Frames%@BO: cce8a@%%@NL@% 15.4 Using Interrupts%@BO: cdbc9@%%@NL@% 15.4.1 Calling Interrupts%@BO: ce52a@%%@NL@% 15.4.2 Defining and Redefining Interrupt Routines%@BO: cf793@%%@NL@% 15.5 Checking Memory Ranges%@BO: d0ebd@%%@NL@% %@NL@% %@AB@%Chapter 16 Processing Strings%@AE@%%@BO: d1833@%%@NL@% 16.1 Setting Up String Operations%@BO: d1f06@%%@NL@% 16.2 Moving Strings%@BO: d40ac@%%@NL@% 16.3 Searching Strings%@BO: d52a2@%%@NL@% 16.4 Comparing Strings%@BO: d60d5@%%@NL@% 16.5 Filling Strings%@BO: d7174@%%@NL@% 16.6 Loading Values from Strings%@BO: d79db@%%@NL@% 16.7 Transferring Strings to and from Ports%@BO: d8b5c@%%@NL@% %@NL@% %@AB@%Chapter 17 Calculating with a Math Coprocessor%@AE@%%@BO: d965a@%%@NL@% 17.1 Coprocessor Architecture%@BO: d9b76@%%@NL@% 17.1.1 Coprocessor Data Registers%@BO: da015@%%@NL@% 17.1.2 Coprocessor Control Registers%@BO: da706@%%@NL@% 17.2 Emulation%@BO: dae73@%%@NL@% 17.3 Using Coprocessor Instructions%@BO: db35c@%%@NL@% 17.3.1 Using Implied Operands in the Classical-Stack Form%@BO: dc0bc@%%@NL@% 17.3.2 Using Memory Operands%@BO: dc9e0@%%@NL@% 17.3.3 Specifying Operands in the Register Form%@BO: dd3d0@%%@NL@% 17.3.4 Specifying Operands in the Register-Pop Form%@BO: ddb1e@%%@NL@% 17.4 Coordinating Memory Access%@BO: de11e@%%@NL@% 17.5 Transferring Data%@BO: dedf8@%%@NL@% 17.5.1 Transferring Data to and from Registers%@BO: defb7@%%@NL@% 17.5.1.1 Real Transfers%@BO: df628@%%@NL@% 17.5.1.2 Integer Transfers%@BO: dff78@%%@NL@% 17.5.1.3 Packed BCD Transfers%@BO: e054c@%%@NL@% 17.5.2 Loading Constants%@BO: e11e1@%%@NL@% 17.5.3 Transferring Control Data%@BO: e1971@%%@NL@% 17.6 Doing Arithmetic Calculations%@BO: e24a3@%%@NL@% 17.7 Controlling Program Flow%@BO: e64cc@%%@NL@% 17.7.1 Comparing Operands to Control Program Flow%@BO: e6d88@%%@NL@% 17.7.1.1 Compare%@BO: e74e1@%%@NL@% 17.7.1.2 Compare and Pop%@BO: e79bf@%%@NL@% 17.7.2 Testing Control Flags after Other Instructions%@BO: e8aba@%%@NL@% 17.8 Using Transcendental Instructions%@BO: e90ee@%%@NL@% 17.9 Controlling the Coprocessor%@BO: e9fb7@%%@NL@% %@NL@% %@AB@%Chapter 18 Controlling the Processor%@AE@%%@BO: eab36@%%@NL@% 18.1 Controlling Timing and Alignment%@BO: eae62@%%@NL@% 18.2 Controlling the Processor%@BO: eb42c@%%@NL@% 18.3 Processor Directives%@BO: ebd6e@%%@NL@% %@NL@% %@AB@%Appendix A Mixed-Language Mechanics%@AE@%%@BO: ed697@%%@NL@% A.1 Writing the Assembly Procedure%@BO: edd84@%%@NL@% A.1.1 Setting Up the Procedure%@BO: ee356@%%@NL@% A.1.2 Entering the Procedure%@BO: eea11@%%@NL@% A.1.3 Allocating Local Data (Optional)%@BO: ef0a0@%%@NL@% A.1.4 Preserving Register Values%@BO: ef916@%%@NL@% A.1.5 Accessing Parameters%@BO: efea0@%%@NL@% A.1.6 Returning a Value (Optional)%@BO: f10ef@%%@NL@% A.1.7 Exiting the Procedure%@BO: f1e90@%%@NL@% A.2 Calls from Modules Using C Conventions%@BO: f27fe@%%@NL@% A.3 Calls from Non-C Modules%@BO: f3f49@%%@NL@% A.4 Calling High-Level Languages from Assembly Language%@BO: f5679@%%@NL@% A.5 Using Full Segment Definitions%@BO: f5e57@%%@NL@% %@NL@% %@AB@%Appendix B Using Assembler Options with QCL%@AE@%%@BO: f653b@%%@NL@% B.1 Specifying the Segment-Order Method%@BO: f7a41@%%@NL@% B.2 Checking Code for Tiny Model%@BO: f829e@%%@NL@% B.3 Selecting Case Sensitivity%@BO: f8648@%%@NL@% B.4 Defining Assembler Symbols%@BO: f8ea4@%%@NL@% B.5 Displaying Error Lines on the Screen%@BO: f96cf@%%@NL@% B.6 Creating Code for a Floating-Point Emulator%@BO: f9a75@%%@NL@% B.7 Creating Listing Files%@BO: fa7e3@%%@NL@% B.8 Enabling One-Pass Assembly%@BO: fab38@%%@NL@% B.9 Listing All Lines of Macro Expansions%@BO: fafe0@%%@NL@% B.10 Creating a Pass 1 Listing%@BO: fb2f8@%%@NL@% B.11 Specifying an Editor-Oriented Listing%@BO: fb5b2@%%@NL@% B.12 Suppressing Tables in the Listing File%@BO: fb87f@%%@NL@% B.13 Adding a Line-Number Index to the Listing%@BO: fbb4c@%%@NL@% B.14 Listing False Conditionals%@BO: fbf3d@%%@NL@% B.15 Controlling Display of Assembly Statistics%@BO: fc55d@%%@NL@% B.16 Setting the Warning Level%@BO: fc9ca@%%@NL@% %@NL@% %@AB@%Appendix C Reading Assembly Listings%@AE@%%@BO: fd1fd@%%@NL@% C.1 Reading Code in a Listing%@BO: fd684@%%@NL@% C.2 Reading a Macro Table%@BO: ff16e@%%@NL@% C.3 Reading a Structure and Record Table%@BO: ff39a@%%@NL@% C.4 Reading a Segment and Group Table%@BO: ffde3@%%@NL@% C.5 Reading a Symbol Table%@BO: 10061e@%%@NL@% C.6 Reading Assembly Statistics%@BO: 1017b0@%%@NL@% C.7 Reading a Pass 1 Listing%@BO: 101a0c@%%@NL@% %@NL@% %@AB@%Index%@AE@%%@BO: 102280@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH0 @%%@AB@%Introduction%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% If you're a C programmer who has been wanting to try out the full power of%@EH@% assembly language, this is the product for you.%@NL@% %@NL@% %@4@% Microsoft(R) QuickC(R) with QuickAssembler is a package you install along%@EH@% with Microsoft QuickC Version 2.0 in order to create a single powerful environment in which you can develop C, assembly, and mixed-language programs. What's more, QuickAssembler is an integrated environment, containing tools for editing, assembling, compiling, and linking. Integrated tools help you achieve faster development of assembly-language programs.%@NL@% %@NL@% %@4@% Each MS-DOS(R) and IBM(R) PC-DOS computer is driven by one of the%@EH@% processors in the 8086 family. A processor is the central motor of a computer. It responds to its own numeric language, called "machine code." Assembly language is very close to machine code, but it lets you use meaningful keywords and variable names instead of difficult-to-remember numeric codes. As a result, assembly language is convenient to use, but gives you the ultimate in ability to control hardware and optimize code.%@NL@% %@NL@% %@4@% To support the low-level operations of assembly language, QuickAssembler%@EH@% expands the general power of the QuickC environment. Increased debugging capabilities let you change flag settings and modify registers──including registers of the 8087 math coprocessor. Furthermore, the Quick Advisor (the on-line Help system) is expanded to provide help on QuickAssembler keywords as well as DOS and ROM-BIOS services.%@NL@% %@NL@% %@3@% %@AB@%A Note about Operating-System Terms%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Microsoft documentation uses the term "OS/2" to refer to the OS/2 system──%@EH@% Microsoft Operating System/2 (MS(R) OS/2) and IBM OS/2. Similarly, the term "DOS" refers to both the MS-DOS and IBM Personal Computer DOS operating systems. The name of a specific operating system is used when it is necessary to note features unique to that system.%@NL@% %@NL@% %@NL@% %@2@%%@AB@%General Features%@AE@%%@EH@%%@NL@% %@NL@% %@4@% QuickAssembler does not replace the QuickC in-line assembler, which you%@EH@% can continue to use inside .C files. The joint QuickC/QuickAssembler environment puts both QuickAssember and the in-line assembler at your disposal. But Microsoft QuickAssembler supports a number of features beyond those supported by the in-line assembler:%@NL@% %@NL@% ■ You can write stand-alone assembly programs. These programs begin and end with assembly code and do not include the C start-up code. Unlike programs written from within C modules, useful stand-alone assembly programs can be 1K (kilobyte) or even smaller.%@NL@% %@NL@% ■ You can use the assembler's rich set of macro-definition capabilities, which go far beyond the macro capabilities supported by C. An assembly-language macro can handle variable parameter lists, recursion, and repeated operations. These macros are roughly as powerful and flexible as procedure calls, but execute faster.%@NL@% %@NL@% ■ Your assembly modules can be shared by many different programs. Since an assembly-language module is in its own file, you can write the module once and link it to any program you want.%@NL@% %@NL@% ■ QuickAssembler is a full implementation of 8086 assembly language. You can use the full set of the Microsoft Macro Assembler 5.1 directives and operators.%@NL@% %@NL@% %@4@% In addition, QuickAssembler provides the best set of keywords yet%@EH@% available for simplifying tedious programming tasks, such as initializing registers at the beginning of a program or determining how to access parameters on the stack. (Part 1%@BO: b8f5@% of this manual focuses on the use of these keywords.)%@NL@% %@NL@% %@4@% QuickAssembler for QuickC is a DOS-based product, and it does not include%@EH@% the following extensions to 8086 assembly language:%@NL@% %@NL@% ■ 80386 extended registers and special instructions%@NL@% %@NL@% ■ 80387 extended instructions%@NL@% %@NL@% ■ OS/2 protected-mode operation%@NL@% %@NL@% %@4@% QuickAssembler does support the 80286 extended instruction set, as well as%@EH@% the 8087 and 80287 coprocessors. The 80386 processor can run all QuickAssembler programs; the only limitation is that QuickAssembler does not support extended capabilities of the 80386.%@NL@% %@NL@% %@4@% The Microsoft Macro Assembler supports 80386 extended features and%@EH@% development of protected-mode applications.%@NL@% %@NL@% %@NL@% %@2@%%@AB@%System Requirements%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX0.1 @%%@CR:IX0.2 @%%@CR:IX0.3 @% In addition to a computer with one of the 8086-family processors, you must%@EH@% have Version 2.1 or later of the MS-DOS or IBM PC-DOS operating system. You can also run QuickAssembler in the 3.x compatibility box of OS/2 systems. Your computer system must have approximately 512K of memory. A hard-disk setup is strongly recommended.%@NL@% %@NL@% %@4@% To enable the use of QuickAssembler, you should first choose Full Menus%@EH@% from the Options menu.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The 8086 family is a set of processors that all support the same basic instruction set. This family includes the 8088, 8086, 80188, 80186, 80286, and 80386 chips. All of these processors support the entire instruction set of the 8086 itself; some support additional instructions. Rather than list the entire set of chips, this manual often discusses the core instruction set by referring only to the 8086.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@2@%%@AB@%Installing QuickAssembler%@AE@%%@EH@%%@NL@% %@NL@% %@4@% If you purchased QuickC and QuickAssembler together, the installation%@EH@% procedure described in %@AI@%Up%@AE@% %@AI@%and%@AE@% %@AI@%Running%@AE@% automatically installs both products. A few of the questions shown in that booklet are reworded in the install program to make more sense for the joint QuickC/QuickAssembler installation.%@NL@% %@NL@% %@4@% If you purchased QuickAssembler separately, run the installation program%@EH@% on the QuickAssembler distribution disks. The first screen asks you the following questions:%@NL@% %@NL@% %@AS@%Source of assembler files [A:]:%@AE@%%@NL@% %@AS@%Installing on a hard disk drive [Y]:%@AE@%%@NL@% %@AS@%Copy QuickAssembler documentation files [Y]:%@AE@%%@NL@% %@AS@%Copy sample Assembler programs [N]:%@AE@%%@NL@% %@NL@% %@AS@%Do you want to change any of the above options? [N]%@AE@%%@NL@% %@NL@% %@4@% As with the QuickC installation program, the default responses are%@EH@% indicated in brackets ([]). Each of these questions is accompanied by an explanation at the bottom of the screen. To accept a default response, press ENTER. If you enter an incorrect response, just answer no (N) to the last question.%@NL@% %@NL@% %@4@% The second screen asks you the following questions:%@EH@%%@NL@% %@NL@% %@AS@%Directory for QuickC executable files [C:\QC2\BIN]:%@AE@%%@NL@% %@AS@%Directory for Sample files [C:\QC2\SAMPLES]:%@AE@%%@NL@% %@NL@% %@AS@%Do you want to change any of the above options? [N]%@AE@%%@NL@% %@NL@% %@4@% The QuickAssembler installation program replaces some of the existing%@EH@% QuickC files. QuickAssembler must be installed in the directory that currently contains QC.EXE. Make sure you enter the location of your current QuickC executable files. If you're not sure, press CTRL+C to stop the installation and examine your setup.%@NL@% %@NL@% %@NL@% %@2@%%@AB@%Getting Information about Assembly Language%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The combined paper and on-line documentation with QuickAssembler gives you%@EH@% a complete reference to the language. This manual provides three basic kinds of information:%@NL@% %@NL@% ■ Part 1%@BO: b8f5@%, "Introducing QuickAssembler," provides a basic introduction to programming in assembly language. Chapter 1%@BO: bdb6@% describes how the interface changes when you install QuickAssembler. Chapter 2%@BO: 151ff@% gives a general background to 8086 architecture and assembly-language concepts. Chapters 3%@BO: 2aca0@% and 4%@BO: 34dba@% demonstrate how to use special QuickAssembler keywords to simplify programming. Even if you have used assembly language before, you should take a look at these chapters.%@NL@% %@NL@% ■ Parts 2%@BO: 3faf8@% ("Using Directives") and 3%@BO: a6c61@% ("Using Instructions") give a reference to the use of directives and instructions. This material is much less tutorial than Part 1%@BO: b8f5@%, but it does illustrate the use of each directive and instruction in context.%@NL@% %@NL@% ■ The appendixes explain low-level mixed-language techniques, the use of assembly options with the QCL driver, and how to read listing files.%@NL@% %@NL@% %@4@% This manual does not teach systems programming or advanced programming%@EH@% techniques. Even with the tutorial material provided in this manual, you may want to purchase other books on assembly language, such as the ones listed in the next section.%@NL@% %@NL@% %@4@% In addition, this manual assumes you understand certain basic concepts of%@EH@% programming, such as modules, variables, and pointers. If you need more background in one of these topics, you should first read the appropriate sections in %@AI@%C%@AE@% %@AI@%For%@AE@% %@AI@%Yourself%@AE@%. Part 1%@BO: b8f5@% of this manual often explains concepts by comparing a language feature to C.%@NL@% %@NL@% %@4@% The Quick Advisor (the on-line Help system) is an integral part of the%@EH@% overall documentation. As explained in Section 1.5%@BO: f864@%, "Using the Quick Advisor (Help)," QuickAssembler provides help on all keywords──in particular, you get instant reference information on each instruction, including timing, encoding, and flag settings. The Help Contents and Index screens also provide information on each DOS service.%@NL@% %@NL@% %@NL@% %@2@%%@AB@%Books on Assembly Language%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX0.4 @%%@CR:IX0.5 @%%@CR:IX0.6 @% The following books may be useful in learning to program in assembly%@EH@% language:%@NL@% %@NL@% %@4@% Duncan, Ray. %@AI@%Advanced%@AE@% %@AI@%MS-DOS%@AE@%. Redmond, WA: Microsoft Corporation, 1986.%@EH@%%@NL@% %@NL@% %@4@% An intermediate book on writing C and assembly-language programs that%@EH@% interact with MS-DOS (includes DOS and BIOS function descriptions)%@NL@% %@NL@% %@4@% Jourdain, Robert. %@AI@%Programmer's%@AE@% %@AI@%Problem%@AE@% %@AI@%Solver%@AE@% %@AI@%for%@AE@% %@AI@%the%@AE@% %@AI@%IBM%@AE@% %@AI@%PC,%@AE@% %@AI@%XT%@AE@% %@AI@%and%@AE@% %@AI@%AT%@AE@%.%@EH@% New York: Brady Communications Company, Inc., 1986.%@NL@% %@NL@% %@4@% Reference of routines and techniques for interacting with hardware%@EH@% devices through DOS, BIOS, and ports (high-level routines in BASIC and low- or medium-level routines in assembler)%@NL@% %@NL@% %@4@% Lafore, Robert. %@AI@%Assembly%@AE@% %@AI@%Language%@AE@% %@AI@%Primer%@AE@% %@AI@%for%@AE@% %@AI@%the%@AE@% %@AI@%IBM%@AE@% %@AI@%PC%@AE@% %@AI@%&%@AE@% %@AI@%XT%@AE@%. New York:%@EH@% Plume/Waite, 1984.%@NL@% %@NL@% %@4@% An introduction to assembly language, including some information on DOS%@EH@% function calls and IBM-type BIOS%@NL@% %@NL@% %@4@% Metcalf, Christopher D., and Sugiyama, Marc B. %@AI@%COMPUTE!'s%@AE@% %@AI@%Beginner's%@AE@% %@AI@%Guide%@AE@%%@EH@% %@AI@%to%@AE@% %@AI@%Machine%@AE@% %@AI@%Language%@AE@% %@AI@%on%@AE@% %@AI@%the%@AE@% %@AI@%IBM%@AE@% %@AI@%PC%@AE@% %@AI@%&%@AE@% %@AI@%PCjr%@AE@%. Greensboro, NC: COMPUTE! Publications, Inc., 1985.%@NL@% %@NL@% %@4@% Beginning discussion of assembly language, including information on the%@EH@% instruction set and MS-DOS function calls%@NL@% %@NL@% %@4@% %@AI@%Microsoft%@AE@% %@AI@%MS-DOS%@AE@% %@AI@%Programmer's%@AE@% %@AI@%Reference%@AE@%. Redmond, WA: Microsoft Press,%@EH@% 1986, 1987.%@NL@% %@NL@% %@4@% Reference manual for MS-DOS%@EH@%%@NL@% %@NL@% %@4@% Morgan, Christopher, and the Waite Group. %@AI@%Bluebook%@AE@% %@AI@%of%@AE@% %@AI@%Assembly%@AE@% %@AI@%Routines%@AE@%%@EH@% %@AI@%for%@AE@% %@AI@%the%@AE@% %@AI@%IBM%@AE@% %@AI@%PC%@AE@%. New York: New American Library, 1984.%@NL@% %@NL@% %@4@% Sample assembly routines that can be integrated into assembly or%@EH@% high-level-language programs%@NL@% %@NL@% %@4@% Norton, Peter. %@AI@%The%@AE@% %@AI@%Peter%@AE@% %@AI@%Norton%@AE@% %@AI@%Programmer's%@AE@% %@AI@%Guide%@AE@% %@AI@%to%@AE@% %@AI@%the%@AE@% %@AI@%IBM%@AE@% %@AI@%PC%@AE@%. Redmond,%@EH@% WA: Microsoft Press, 1985.%@NL@% %@NL@% %@4@% Information on using IBM-type BIOS and MS-DOS function calls%@EH@%%@NL@% %@NL@% %@4@% Scanlon, Leo J. %@AI@%IBM%@AE@% %@AI@%PC%@AE@% %@AI@%Assembly%@AE@% %@AI@%Language:%@AE@% %@AI@%A%@AE@% %@AI@%Guide%@AE@% %@AI@%for%@AE@% %@AI@%Programmers%@AE@%. Bowie,%@EH@% MD: Robert J. Brady Co., 1983.%@NL@% %@NL@% %@4@% An introduction to assembly language, including information on DOS%@EH@% function calls%@NL@% %@NL@% %@4@% Schneider, Al. %@AI@%Fundamentals%@AE@% %@AI@%of%@AE@% %@AI@%IBM%@AE@% %@AI@%PC%@AE@% %@AI@%Assembly%@AE@% %@AI@%Language%@AE@%. Blue Ridge%@EH@% Summit, PA: Tab Books Inc., 1984.%@NL@% %@NL@% %@4@% An introduction to assembly language, including information on DOS%@EH@% function calls%@NL@% %@NL@% %@4@% These books are listed for your convenience only. Microsoft Corporation%@EH@% does not endorse these books (with the exception of those published by Microsoft) or recommend them over others on the same subjects.%@NL@% %@NL@% %@NL@% %@2@%%@AB@%Document Conventions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX0.7 @% The following document conventions are used throughout this manual:%@EH@%%@NL@% %@NL@% %@AB@%Example of%@AE@% %@AB@%Description%@AE@% %@AB@%Convention%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% SAMPLE2.ASM Uppercase letters indicate file names, segment names, registers, and terms used at the DOS-command level.%@NL@% %@NL@% %@AB@%.MODEL%@AE@% Boldface type indicates assembly-language directives, instructions, type specifiers, and predefined equates, as well as keywords in other programming languages.%@NL@% %@NL@% %@AI@%placeholders%@AE@% Italic letters indicate placeholders for information you must supply, such as a file name. Italics are also occasionally used for emphasis in the text.%@NL@% %@NL@% %@AS@%target%@AE@% This font is used to indicate example programs, user input, and screen output.%@NL@% %@NL@% SHIFT Names of keys on the keyboard appear in small capital letters. Notice that a plus (+) indicates a combination of keys. For example, CTRL+E means to hold down the CTRL key while pressing the E key.%@NL@% %@NL@% [[%@AI@%argument%@AE@% ]] Items inside double square brackets are optional.%@NL@% %@NL@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%} Braces and a vertical bar indicate a choice between two or more items. You must choose one of the items unless double square brackets surround the braces.%@NL@% %@NL@% Repeating Three dots following an item indicate that more items elements... having the same form may appear.%@NL@% %@NL@% %@AS@%Program%@AE@% A column of three dots tells you that part of a %@AS@%.%@AE@% program has been intentionally omitted.%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%Fragment%@AE@%%@NL@% %@NL@% "processor flag" The first time a new term is defined, it is enclosed in quotation marks.%@NL@% %@NL@% Color Graphics The first time an acronym is used, it is spelled out. Adapter (CGA)%@NL@% %@NL@% %@NL@% %@2@%%@AB@%Getting Assistance or Reporting Problems%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX0.8 @%%@CR:IX0.9 @%%@CR:IX0.10 @%%@CR:IX0.11 @% If you need help or feel you have discovered a problem in the software,%@EH@% please provide the following information to help us locate the problem:%@NL@% %@NL@% ■ The version of DOS you are running (use the DOS VER command)%@NL@% %@NL@% ■ Your system configuration (the type of machine you are using, its total memory, and its total free memory at assembler execution time, as well as any other information you think might be useful)%@NL@% %@NL@% ■ The assembly command line used (or the link command line if the problem occurred during linking)%@NL@% %@NL@% ■ Any object files or libraries you linked with if the problem occurred at link time%@NL@% %@NL@% %@4@% If your program is very large, please try to reduce its size to the%@EH@% smallest possible program that still produces the problem.%@NL@% %@NL@% %@4@% Use the Product Assistance Request form at the back of this manual to send%@EH@% this information to Microsoft.%@NL@% %@NL@% %@4@%%@CR:IX0.12 @% If you have comments or suggestions regarding any of the manuals%@EH@% accompanying this product, please indicate them on the Document Feedback card at the back of this manual.%@NL@% %@NL@% %@4@%%@CR:IX0.13 @% If you are not already a registered QuickAssembler owner, you should fill%@EH@% out and return the Registration Card. This enables Microsoft to keep you informed of updates and other information about the assembler.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:PT1 @%%@AB@%PART 1: Using Assembler Programs%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% Part 1%@BO: b8f5@% of the Programmer's Guide (comprising Chapters 1%@BO: bdb6@%-4%@BO: 34dba@%) will help%@EH@% you start using assembly language quickly.%@NL@% %@NL@% %@4@% Chapter 1%@BO: bdb6@% summarizes all the differences between the standard QuickC%@EH@% interface and the expanded QuickC/QuickAssembler interface. Read this chapter to learn how to enter, assemble, and run an assembly-language program.%@NL@% %@NL@% %@4@% Read Chapter 2%@BO: 151ff@% if you are new to 8086 assembly language or need to review%@EH@% basic concepts. Chapter 2%@BO: 151ff@% explains the architecture of 8086-family processors, as well as how to write simple code and data statements.%@NL@% %@NL@% %@4@% Whether or not you're new to assembly language, you'll want to read%@EH@% Chapters 3%@BO: 2aca0@% and 4%@BO: 34dba@%, which show the use of QuickAssembler's simplified keywords in useful examples. These keywords make programming easier.%@NL@% %@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH1 @%%@AB@%Chapter 1: The QuickAssembler Interface%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX1.1 @% After you install Microsoft QuickC with QuickAssembler, you'll have a%@EH@% single environment for both compiling and assembling. You can create C programs, assembly-language programs, and programs that combine both languages.%@NL@% %@NL@% %@4@% The environment completely supports the standard QuickC features,%@EH@% including all editing commands as well as mouse, keyboard, and menu techniques. This manual assumes you have read QuickC %@AI@%Up%@AE@% %@AI@%and%@AE@% %@AI@%Running%@AE@% and have used the on-line Help system to learn how to use each menu. Refer to these sources of information for basic help on using the interface.%@NL@% %@NL@% %@4@% The combined QuickC/QuickAssembler interface provides some new menu%@EH@% selections and dialog boxes to support development of assembly-language programs. This chapter describes the new features, focusing on areas where the interface adds new functionality: creating a program, building a program, getting help, debugging, and viewing a listing file. To enable all the features described in this chapter, you should first choose Full Menus from the Options menu if you are not already using full menus.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.1 @%%@AB@%1.1 Creating the Program%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.2 @%%@CR:IX1.3 @% Start the environment with the QC command, regardless of whether you're%@EH@% creating a C or assembly-language source file. You can type %@AS@%QC %@AE@%by itself or %@AS@%QC %@AE@%followed by the name of a file.%@NL@% %@NL@% %@4@% By default, QC assumes that a file name on the command line has a .C%@EH@% extension. You'll learn how to change this behavior later (by choosing Display from the Options menu), but for now, make sure you include the .ASM file extension when you want to create an assembly-language module:%@NL@% %@NL@% %@AS@%QC SAMPLE.ASM%@AE@%%@NL@% %@NL@% %@4@% If the file is new, the QuickC/QuickAssembler environment asks you if you%@EH@% would like to create the file.%@NL@% %@NL@% %@4@%%@CR:IX1.4 @% Once inside the QuickC/QuickAssembler environment, you can enter a program%@EH@% by using all the QuickC editing commands. You can get started by entering the following stand-alone assembly program. By default, QuickAssembler is not case sensitive (except for external symbols), so you can enter statements as uppercase or lowercase.%@NL@% %@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .STACK%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@AS@% mov ah,2%@AE@%%@NL@% %@AS@% mov dl,7%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% mov ax,4c00h%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% Enter the program above in a file with a .ASM extension. No other modules%@EH@% and no special assembly or link flags are required. When run, the program beeps and exits.%@NL@% %@NL@% %@4@% For now, you may just want to run the program to see how the%@EH@% QuickC/Quick-Assembler environment works. However, you can read the rest of this section to get a brief explanation of why the program works.%@NL@% %@NL@% %@4@% The four statements are directives──nonexecutable statements that give%@EH@% basic structure to the program by declaring a memory model, stack segment, and code segment.%@NL@% %@NL@% %@4@% The next five statements perform the actions of the program. The first%@EH@% three set up a call to a DOS function that prints the beep character. (The QuickAssembler Advisor, which you access through the Help menu, provides information on each DOS function.) The first three statements are shown below, with comments:%@NL@% %@NL@% %@AS@% mov ah,2 ; Move 2 to AH (select Print function)%@AE@%%@NL@% %@AS@% mov dl,7 ; Move 7 to DL (select Beep character)%@AE@%%@NL@% %@AS@% int 21h ; Call DOS function%@AE@%%@NL@% %@NL@% %@4@% The next two statements, shown below with comments, call DOS to exit%@EH@% gracefully. Unlike C programs, assembly-language programs must make an explicit function call to exit, or else cause the processor to execute meaningless instructions beyond the end of the program.%@NL@% %@NL@% %@AS@% mov ax,4c00h ; Move 4c00h to AX (select Exit%@AE@%%@NL@% %@AS@% ; function and 0 return code)%@AE@%%@NL@% %@AS@% int 21h ; Call DOS function%@AE@%%@NL@% %@NL@% %@4@% The last statement ends the module.%@EH@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.2 @%%@AB@%1.2 Building and Running a Program%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.5 @%%@CR:IX1.6 @%%@CR:IX1.7 @% Once inside the QuickC/QuickAssembler environment, you build an%@EH@% assembly-language program the same way you build a C program. Choose the Go command from the Run menu, or press the F5 key.%@NL@% %@NL@% %@4@% The environment assembles and links the program if it needs to be built.%@EH@% Then, if there are no errors, it executes the program. You can also assemble a program by using the Make menu. The Compile File command assembles your file rather than compiling it, assuming the current file has a .ASM extension.%@NL@% %@NL@% %@4@% To help you create assembly-language programs, the QuickC/QuickAssembler%@EH@% interface adds the following extensions to QuickC:%@NL@% %@NL@% ■ A program list can now have .ASM files as well as .C, .OBJ, and .LIB files if you work with multiple modules.%@NL@% %@NL@% ■ The Make dialog box from the Options menu has a new option button: Assembler Flags.%@NL@% %@NL@% ■ The Assembler Flags dialog box lets you control how .ASM files are assembled.%@NL@% %@NL@% %@4@% If your program has multiple modules, you can add .ASM files to the%@EH@% program list as well as other kinds of files. When you build the program, the environment compiles each .C file module that needs to be built and assembles each .ASM module that needs to be built.%@NL@% %@NL@% %@4@%%@CR:IX1.8 @%%@CR:IX1.9 @%%@CR:IX1.10 @% For example, the program list in Figure 1.1 creates a mixed-language%@EH@% program with both C and assembly-language source files.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 1.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX1.11 @%%@CR:IX1.12 @% The environment sets the default file extension by looking at the%@EH@% extension of the last file loaded. If the last file loaded had a .ASM extension, the File List field now displays all the .ASM files for the current directory. If the last file loaded had a .C extension, the File List field displays all .C files.%@NL@% %@NL@% %@4@% You can alter this behavior by choosing Display from the Options menu, as%@EH@% explained in Section 1.4%@BO: ece3@%, "Choosing C or Assembler Defaults." In any case, you can always control which files are displayed by entering a wildcard expression, such as %@AS@%*.asm%@AE@%, in the File Name field.%@NL@% %@NL@% %@4@%%@CR:IX1.13 @%%@CR:IX1.14 @%%@CR:IX1.15 @% The environment lets you set assembler options as well as compiler%@EH@% options. When you open the Options menu and choose Make, the dialog box shown in Figure 1.2 appears.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 1.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX1.16 @% This dialog box contains one new field: Assembler Flags. When you choose%@EH@% this field, a new dialog box, shown in Figure 1.3, appears.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 1.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX1.17 @% By setting flags in the Assembler Flags dialog box, you control the action%@EH@% of the assembler whenever it builds a program. These settings have no effect on .C modules, but do affect how each .ASM module is assembled.%@NL@% %@NL@% %@4@%%@CR:IX1.18 @%%@CR:IX1.19 @% This dialog box contains a Debug Flags section, which has options that%@EH@% apply only to Debug builds, and a Global Flags section, which has options that apply to every build. Choose the Help button for an explanation of each option.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% You control the type of build operation (Debug or Release) by choosing the appropriate option button in the dialog box shown in Figure 1.2. You can return to that dialog box by choosing the OK or Cancel command button. By choosing Debug (the default), you can use all of the QuickC debugging commands while running the program. By choosing Release, you produce a program that cannot be debugged but is somewhat smaller.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX1.20 @% The Custom Flags section lets you enter additional options. In the three%@EH@% Custom Flags text boxes, you can type any of the assembly options accepted by the QCL driver. See Appendix B%@BO: f653b@% for a description of these options. The next section describes how to use the QCL driver to assemble programs.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.3 @%%@AB@%1.3 Assembling from the Command Line%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.21 @%%@CR:IX1.22 @% You can run QuickAssembler from the command line, just as you can run%@EH@% QuickC. One utility, QCL, invokes both the assembler and compiler. You can even use it to compile, assemble, and link mixed-language programs in one step. However, make sure you use the version of QCL copied during QuickAssembler installation.%@NL@% %@NL@% %@4@% If you type a file name that has a .C extension, QCL invokes the C%@EH@% compiler. For example, the following command compiles and links the file SAMPLE1.C:%@NL@% %@NL@% %@AS@%QCL SAMPLE1.C%@AE@%%@NL@% %@NL@% %@4@% If you type a file name that has a .ASM extension, QCL invokes the%@EH@% QuickAssembler. For example, the following command assembles and links the file SAMPLE2.ASM:%@NL@% %@NL@% %@AS@%QCL SAMPLE2.ASM%@AE@%%@NL@% %@NL@% %@4@% In any case, QCL links all resulting object files to create a .EXE file,%@EH@% unless you specify /c on the command line. (You can also create a .COM file if the program is written entirely in assembly language.) For example, the following command compiles SAMPLE1.C and assembles SAMPLE2.ASM, but does not link the resulting object files:%@NL@% %@NL@% %@AS@%QCL /c SAMPLE1.C /Cl SAMPLE2.ASM%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX1.23 @% As always, you can specify .LIB files and .OBJ files on the QCL command%@EH@% line. A file with no extension is assumed to have a .OBJ extension by default. For example, the following QCL command compiles M1.C, assembles M2.ASM (with lowercase symbols preserved), and links M1.OBJ, M2.OBJ, and M3.OBJ. Finally, QCL searches M4.LIB for any unresolved references.%@NL@% %@NL@% %@AS@%QCL /Cx M1.C M2.ASM M3 M4.LIB%@AE@%%@NL@% %@NL@% %@4@% You can specify a number of QuickAssembler options, in addition to the%@EH@% ones provided specifically for C. See Appendix B%@BO: f653b@%, "Using Assembler Options with QCL," for a description of all these options.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.4 @%%@AB@%1.4 Choosing C or Assembler Defaults%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.24 @% At all times, you can use the QuickC/QuickAssembler environment to create%@EH@% either C modules or assembly-language modules. However, there are some details of operation that make it a little easier to work with one language or another.%@NL@% %@NL@% %@4@%%@CR:IX1.25 @% For example, one consideration is whether the dialog box starts by%@EH@% displaying all the C files in the directory (%@AS@%*.c%@AE@%) or all the assembly-language files (%@AS@%*.asm%@AE@%) when you choose the Open command from the File menu. You can control this behavior by choosing Display from the Options menu. Figure 1.4 shows the dialog box that appears.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 1.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX1.26 @%%@CR:IX1.27 @%%@CR:IX1.28 @% In the Language section of this dialog box, select either C, Assembler, or%@EH@% Auto. The Auto selection uses C or Assembler defaults, depending on what file was last loaded into the active window. For example, if you load the PROG.ASM file into the source window, all the defaults (described below) change to assembly-language settings.%@NL@% %@NL@% %@AB@%──────────────────────────────────────────────────────────────────────────%@AE@%%@NL@% %@AB@%NOTE%@AE@% When you first use QuickAssembler, the environment starts up in Auto mode. Thereafter, it looks at the settings in QC.INI to determine what mode to start in; this feature has the effect of saving display-mode settings between sessions.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX1.29 @% The following items change when the display mode changes──either because%@EH@% you change the mode manually or because you are in Auto mode and load a different kind of file:%@NL@% %@NL@% %@CR:IX1.30 @% ■ For commands on the File menu, the default file name changes to %@AS@%*.c %@AE@%or %@AS@%*.asm%@AE@%.%@NL@% %@NL@% %@CR:IX1.31 @%%@CR:IX1.32 @% ■ The Include command on the View menu responds to .H files if the display mode is C, or .INC files if the display mode is Assembler.%@NL@% %@NL@% %@CR:IX1.33 @%%@CR:IX1.34 @% ■ The Index and Contents items from the Help menu bring up lists of topics for either C or Assembly, as determined by the display mode.%@NL@% %@NL@% %@4@% Auto display mode assumes C defaults until you load a .ASM file. When you%@EH@% start the environment with the QC command, QC assumes that file names on the command line have .C extensions, unless the environment is in Assembler display mode.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.5 @%%@AB@%1.5 Using the Quick Advisor (Help)%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.35 @%%@CR:IX1.36 @%%@CR:IX1.37 @%%@CR:IX1.38 @% QuickAssembler extends the number of topics you can get information on,%@EH@% and updates QCENV.HLP so you can get context-sensitive help on the new menu items and dialog boxes. In addition, you still continue to get help on all of the C-language topics. The new topics, added for use with assembly language, are shown below:%@NL@% %@NL@% %@CR:IX1.39 @% ■ QuickAssembler instructions%@NL@% %@NL@% %@CR:IX1.40 @% ■ QuickAssembler directives and operators%@NL@% %@NL@% %@CR:IX1.41 @%%@CR:IX1.42 @% ■ DOS and ROM-BIOS services%@NL@% %@NL@% %@4@% You can get help on assembly-language topics by using one of two different%@EH@% methods:%@NL@% %@NL@% 1. Topical Help (press F1)%@NL@% %@NL@% 2. The Help menu%@NL@% %@NL@% %@4@%%@CR:IX1.43 @% At all times, the expanded environment provides topical Help for both%@EH@% assembler and C keywords. Place the cursor on the keyword, then press F1. You can also get topical Help by moving the mouse cursor to the desired word and clicking the Right mouse button. The display mode (described in the previous section) determines whether C help files or assembly help files are searched first.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% If the keyword starts with a dot (.), do not place the cursor on the dot or click on the dot to get topical Help. Place the cursor on the keyword or click on the keyword.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% QuickAssembler keywords include instructions, directives, and operators.%@EH@% Chapter 2%@BO: 151ff@%, "Introducing 8086 Assembly Language" provides information on each of these concepts. An "instruction" is a specific action that the processor executes. Instructions are the primary building blocks of an assembly-language program.%@NL@% %@NL@% %@4@%%@CR:IX1.44 @% The Help screens on instructions are particularly useful, because they%@EH@% provide detailed information on timing, syntax, and processor flags. This manual features a topical discussion of instructions, but provides only limited information on timing and flags. To write the most efficient assembly-language programs, you should refer often to the on-line Help for instructions.%@NL@% %@NL@% %@4@%%@CR:IX1.45 @%%@CR:IX1.46 @%%@CR:IX1.47 @% To get help on DOS or ROM-BIOS services, select Contents or Index from the%@EH@% Help menu. These menu items give you help on assembly-language topics rather than C topics whenever the display mode (described in the previous section) is set to Assembler.%@NL@% %@NL@% %@4@% The Help system offers other paths to get to information on DOS and BIOS%@EH@% functions. Move the mouse cursor to an interrupt number (such as 21H or 33) and click the Right mouse button, or move the cursor to the number and press F1. The Help system responds by showing a screen listing of all the functions accessed through that interrupt number. You can then go to the specific Help screen you want. You can also get help on interrupt functions by selecting context-sensitive help for the %@AB@%INT%@AE@% keyword.%@NL@% %@NL@% %@4@% You call these DOS and BIOS functions by using the %@AB@%INT%@AE@% instruction, as%@EH@% described in Chapter 4%@BO: 34dba@%. These services perform basic input and output functions for you, giving you access to DOS and to hardware.%@NL@% %@NL@% %@4@%%@CR:IX1.48 @% By default, the Smart Help display option is on. This option makes the%@EH@% system more flexible by ignoring the presence or absence of a leading underscore (_) in front of a name. Consequently, pressing F1 while on %@AS@%_printf %@AE@%gives you help for the %@AB@%printf%@AE@% function.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.6 @%%@AB@%1.6 Debugging Assembly Code%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.49 @%%@CR:IX1.50 @% You can run a Debug build by choosing Debug in the dialog box opened by%@EH@% the Options menu's Make command. Debug is the default setting, so you probably won't need to choose it.%@NL@% %@NL@% %@4@% You can use all of QuickC's debugging commands with programs written in%@EH@% assembly language. But keep in mind these considerations:%@NL@% %@NL@% ■ You must use an extra file with a .DBG file extension to debug programs in .COM format.%@NL@% %@NL@% ■ You must use C syntax to specify expressions to watch or modify, even when you debug assembly code. In addition, you can use the %@AB@%BY%@AE@%, %@AB@%WO%@AE@%, and %@AB@%DW%@AE@% memory operators, register names, and the colon (%@AB@%:%@AE@%) operator. The colon operator helps to specify segmented addresses.%@NL@% %@NL@% ■ When you trace execution of an assembly-language module, the behavior of the environment changes. Screen swapping is turned on by default, and the first line of code is never highlighted.%@NL@% %@NL@% ■ You can alter flag values and registers from within the environment.%@NL@% %@NL@% %@4@% Sections 1.6.1%@BO: 10d74@%-1.6.4 discuss each debugging feature in turn.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC1.6.1 @%%@AB@%1.6.1 Debugging .COM Files%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.51 @%%@CR:IX1.52 @% Section 4.8%@BO: 3c62a@%, "Creating .COM Files," explains how to use tiny memory%@EH@% model, along with a linker flag, to generate a program in the .COM-file format. A .COM file has a total size limitation of 64K, but is slightly smaller and loads faster than a similar .EXE file.%@NL@% %@NL@% %@4@%%@CR:IX1.53 @%%@CR:IX1.54 @% When you run a Debug build that creates a .COM file, the linker places%@EH@% debugging information in a separate file with the same base name as the program and with a .DBG extension. If you delete the .DBG file, you cannot debug your program until you run another Debug build.%@NL@% %@NL@% %@4@% Otherwise, all the considerations that apply to debugging .EXE files apply%@EH@% to .COM files as well.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC1.6.2 @%%@AB@%1.6.2 Specifying Expressions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.55 @%%@CR:IX1.56 @%%@CR:IX1.57 @%%@CR:IX1.58 @% The Debug menu provides two commands──Watch Value and Watchpoint──that let%@EH@% you specify an expression for the QuickC/QuickAssembler environment to dynamically update and display. The environment displays the updated values in the Watch window. When you choose one of these commands, a dialog box appears, prompting you to enter an expression. Figure 1.5 shows the dialog box for the Watch Value command.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 1.6.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% You can enter any combination of variable names, constants, and C-language%@EH@% syntax. You cannot enter assembly-language keywords. However, the environment does recognize all valid register names (including names of both 8-bit and 16-bit registers). See Chapter 2%@BO: 151ff@%, "Introducing 8086 Assembly Language," for information on registers.%@NL@% %@NL@% %@4@%%@CR:IX1.59 @% In addition to register names, the expanded environment supports the%@EH@% optional use of the colon operator (%@AB@%:%@AE@%) for specifying segmented addresses: %@NL@% %@4@% %@AI@%segment%@AE@%%@AB@%:%@AE@%%@AI@%offset%@AE@%%@EH@%%@NL@% %@NL@% %@4@% In the syntax display above, %@AI@%segment%@AE@% can be a constant or a register;%@EH@% %@AI@%offset%@AE@% can be any expression. The QuickC/QuickAssembler environment combines the segment and offset addresses to determine a physical address, as described in Section 2.7%@BO: 28c1c@%, "Segmented Addressing and Segment Registers."%@NL@% %@NL@% %@4@%%@CR:IX1.60 @% The following examples demonstrate the use of the colon in valid%@EH@% expressions. Note that you use C-language syntax to specify hexadecimal numbers:%@NL@% %@NL@% %@AS@%0xb000:0x0000%@AE@%%@NL@% %@AS@%es:0x0100%@AE@%%@NL@% %@AS@%es:(array[2])%@AE@%%@NL@% %@AS@%ss:bp%@AE@%%@NL@% %@NL@% %@4@% The QuickC/QuickAssembler environment considers a segmented-address%@EH@% expression to be a pointer to a character, which the Watch window evaluates by displaying the character pointed to. However, you can use QuickC type specifiers to alter how an expression is displayed. For example, the Watch window evaluates the following expression by displaying the numeric value of the address %@AS@%es:(warray+3)%@AE@%:%@NL@% %@NL@% %@AS@%es:(warray+3),p%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX1.61 @%%@CR:IX1.62 @%%@CR:IX1.63 @%%@CR:IX1.64 @% You can use the three memory operators──%@AB@%BY%@AE@%, %@AB@%WO%@AE@%, and %@AB@%DW%@AE@%──to view the byte,%@EH@% word, or doubleword of memory at a given address.%@NL@% %@NL@% %@4@% With pointer expressions and registers, %@AB@%BY%@AE@% returns the byte pointed to by%@EH@% the expression. (Segmented addresses are pointer expressions, as are procedure parameters declared with %@AB@%PTR%@AE@%.) With nonpointer variables, %@AB@%BY%@AE@% returns the byte at the same address as the variable. %@AB@%WO%@AE@% and %@AB@%DW%@AE@% work the same way, but return a word or doubleword, respectively.%@NL@% %@NL@% %@4@% The rest of this section demonstrates how to use the three memory%@EH@% operators to specify useful expressions.%@NL@% %@NL@% %@4@%%@CR:IX1.65 @% To watch the contents of a register, enter just the register's name. To%@EH@% examine the value that the register points to, enter the %@AB@%BY%@AE@%, %@AB@%WO%@AE@%, and %@AB@%DW%@AE@% operators followed by the register name.%@NL@% %@NL@% %@AB@%Example%@AE@% %@AB@%Value Specified%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AS@%bx%@AE@% The contents of the BX register%@NL@% %@NL@% %@AS@%BY bx%@AE@% The byte pointed to by the BX register%@NL@% %@NL@% %@AS@%WO bx%@AE@% The word pointed to by the BX register%@NL@% %@NL@% %@AS@%DW es:si%@AE@% The doubleword pointed to by the SI register, relative to the segment address in ES%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX1.66 @% To watch the value of a variable, enter the variable's name. To watch the%@EH@% byte, word, or doubleword at the same address as the variable, use the %@AB@%BY%@AE@%, %@AB@%WO%@AE@%, and %@AB@%DW%@AE@% operators. In this context, these operators function as the QuickAssembler %@AB@%PTR%@AE@% operator does: they change the size of data to be examined. They are similar, but not identical, to C type casts. In the following examples, assume that %@AS@%Var %@AE@%is a word variable defined with%@AB@% DW%@AE@%:%@NL@% %@NL@% %@AB@%Example%@AE@% %@AB@%Value Specified%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AS@%Var%@AE@% The variable %@AS@%Var%@AE@% (the word at the address of %@AS@%Var%@AE@%)%@NL@% %@NL@% %@AS@%BY Var%@AE@% The byte at the address of %@AS@%Var%@AE@%%@NL@% %@NL@% %@AS@%DW Var%@AE@% The doubleword at the address of %@AS@%Var%@AE@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX1.67 @% You can use %@AB@%BY%@AE@%, %@AB@%WO%@AE@%, and %@AB@%DW%@AE@% to specify an array element, but you must%@EH@% understand that expressions in the Debug window are treated like C expressions rather than assembler expressions. As a result, the syntax you use to watch a memory location in the Debug window is often different from the syntax in your assembly source. For example, assume you have the following data and code:%@NL@% %@NL@% %@AS@%warr DW 1, 2, 3, 4, 5, 6%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov bx,0%@AE@%%@NL@% %@AS@% mov cx,5%@AE@%%@NL@% %@NL@% %@AS@%loop1: add ax,warr[bx]%@AE@%%@NL@% %@AS@% add bx,2%@AE@%%@NL@% %@AS@% loop loop1%@AE@%%@NL@% %@NL@% %@4@% You cannot watch the assembler expression %@AS@%warr[bx] %@AE@%directly. However, you%@EH@% can put an equivalent C expression in the Debug window:%@NL@% %@NL@% %@AS@%WO (char*)&warr + bx%@AE@%%@NL@% %@NL@% %@4@% The address-of operator is necessary to make the C debugger look at the%@EH@% MASM array as a C array──that is, as an address. The value must cast to a character pointer because the debugger looks at it as a scaled C index rather than an unscaled assembler index. In this case, the assembler code adds 2 to the pointer BX to adjust for the variable size. You must tell the debugger to ignore its normal word scaling.%@NL@% %@NL@% %@4@% Expressions are only scaled when there is a variable in the expression. In%@EH@% the expression %@AS@%WO BP+6 %@AE@%the %@AS@%6 %@AE@%is not scaled──the expression means, "look at the word six bytes beyond the address that is in BP." However, in the expression %@AS@%WO &warr+6%@AE@%, the %@AS@%6 %@AE@%is scaled because of the word size of the variable. Note that the variable size, not the expression type (%@AB@% BY%@AE@%, %@AB@%WO%@AE@%, or %@AB@%DW%@AE@%), determines the size of scaling.%@NL@% %@NL@% %@4@% If you are comfortable with C, you can also use C expressions to look at%@EH@% assembler expressions. Here are some examples you might find useful:%@NL@% %@NL@% %@AB@%Example%@AE@% %@AB@%Value Specified%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AS@%&Var%@AE@% The address of%@AS@% Var%@AE@%%@NL@% %@NL@% %@AS@%es:0x81,s%@AE@% The string at%@AS@% es:[81h] %@AE@%(the DOS command line when a program is started)%@NL@% %@NL@% %@AS@%&Arr[3]%@AE@% The third element of an array (note that the%@AS@% 3%@AE@% will be scaled)%@NL@% %@NL@% %@AS@%*(&Arr+3)%@AE@% Equivalent to the previous expression%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC1.6.3 @%%@AB@%1.6.3 Tracing Execution%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.68 @%%@CR:IX1.69 @%%@CR:IX1.70 @%%@CR:IX1.71 @%%@CR:IX1.72 @% The Run menu's Trace Into, Animate, and Step Over commands execute one%@EH@% statement of your program at a time. These commands are fully functional with assembly-language programs. However, debugging commands behave differently when you trace execution of an assembly-language module, as summarized below:%@NL@% %@NL@% ■ By default, screen swapping is on.%@NL@% %@NL@% ■ If the main module of the program is an assembly-language module, the first line of the program is never highlighted.%@NL@% %@NL@% ■ The Calls menu does not function unless you write your program according to certain guidelines.%@NL@% %@NL@% %@4@% The rest of this section elaborates on these differences.%@EH@%%@NL@% %@NL@% %@4@% When you trace execution of an assembly-language module, screen swapping%@EH@% is turned on. The environment does not support an Auto screen-swapping mode for assembly-language programs because it cannot detect when a program writes to the screen. Therefore, when executing a .ASM file, the environment equates the Auto screen-swapping selection with screen swapping turned on.%@NL@% %@NL@% %@4@% You can always turn screen swapping off manually by choosing the Run/Debug%@EH@% command from the Options menu. When a dialog box appears, choose the Off option button in the Screen Swapping field.%@NL@% %@NL@% %@4@%%@CR:IX1.73 @% Screen swapping causes the environment to switch to a full output screen%@EH@% each time the program executes code. The effect is particularly noticeable when you choose the Animate command. Leaving screen swapping on preserves program output. However, if large portions of your program do not write to the video display, you may want to turn screen swapping off temporarily.%@NL@% %@NL@% %@4@%%@CR:IX1.74 @% The second debugging feature that operates differently for assembly-%@EH@%%@NL@% %@4@% language programs is current-line highlighting. When you restart a%@EH@% program, the environment does not highlight the first line of code. The debugging facility does not know which line of code is the first to be executed, since this information is stored in the executable-file header. After you execute a trace, the second program line is highlighted, and thereafter current-line highlighting works as you would expect.%@NL@% %@NL@% %@4@%%@CR:IX1.75 @%%@CR:IX1.76 @% The third feature that operates differently is the Calls command from the%@EH@% Debug menu. To ensure that the command works with assembly-language modules, either use the %@AB@%PROC%@AE@% directive with an argument list or local variables, as described in Chapter 3%@BO: 2aca0@%, "Writing Assembly Modules for C Programs," or else set up the framepointer (the BP register) as described in Appendix A%@BO: ed697@%, "Mixed-Language Mechanics." Both these methods set up a stack frame for each procedure, using the standard Microsoft methods. The environment checks stack frames to see what procedures have been called, and with what parameters.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC1.6.4 @%%@AB@%1.6.4 Modifying Registers and Flags%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.77 @%%@CR:IX1.78 @%%@CR:IX1.79 @% With the expanded QuickC/QuickAssembler environment, you can get much%@EH@% greater use from the Registers window. The Registers window displays more information than it does in the simple QuickC environment, and you can also use the window to alter register and flag values.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% By default, the environment does not display the Registers window. To open this window, choose the Window command from the View menu. A dialog box appears that lists all windows. Move the cursor to Registers and press the ENTER key, or move the mouse cursor to Registers and double click the Left mouse button. To close the window, repeat the procedure.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX1.80 @% The Registers window displays the contents of both 8086 and 8087%@EH@% registers. You can remove 8087 registers from the Registers window by choosing Display from the Options menu. When the dialog box appears, turn the Show 8087 option button off. The environment only displays 8087 registers if you have a math coprocessor or have a program that calls floating-point emulator routines from a high-level language.%@NL@% %@NL@% %@4@% You can alter values in the window by either using the mouse or the%@EH@% keyboard. To alter a value, you first select the item you want to change:%@NL@% %@NL@% ■ To alter a value with the mouse, select an item by clicking the Left mouse button.%@NL@% %@NL@% ■ To alter a value with the keyboard, first place the cursor on an item in the window. (Press TAB or SHIFT+TAB to cycle quickly through the items.) Then select the item by pressing the ENTER key. The List field has no function in this context and should be ignored.%@NL@% %@NL@% %@4@% Choosing a flag toggles the flag to the opposite setting. Choosing a%@EH@% register brings up a dialog box. Type the new value for the register and press ENTER.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC1.7 @%%@AB@%1.7 Viewing a Listing File%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX1.81 @%%@CR:IX1.82 @% When you assemble a module with the Debug build setting (the default),%@EH@% QuickAssembler can create a listing file. Choose the type of listing by using the Assembler Flags dialog box. (To access this dialog box, choose Make from the Options menu, then choose Assembler Flags.) You should also make sure that the One Pass Assembly option is not selected.%@NL@% %@NL@% %@4@% A QuickAssembler listing file shows precisely how the assembler translated%@EH@% each line of code during the last program build. Each instruction in the source code is listed next to its corresponding numeric code (machine instruction).%@NL@% %@NL@% %@4@%%@CR:IX1.83 @% Listing files are particularly useful if your program uses macro calls or%@EH@% include files. The listing file displays each statement generated by a macro call and each line of code copied from an include file. Tables at the end of the listing file give information on macros, symbols, structures, groups, and records. Part 2%@BO: 3faf8@% of this manual describes each of these features of assembly language.%@NL@% %@NL@% %@4@%%@CR:IX1.84 @% To view the listing file, assemble the source code at least once. You can%@EH@% view the listing file for the current module by choosing the Listing command from the View menu. You can also view the file with the CTRL+F2 shortcut key.%@NL@% %@NL@% %@4@%%@CR:IX1.85 @% The listing file is then displayed in the Source window, as shown in%@EH@% Figure 1.6. You can page through this file by using all the normal cursor-movement commands. When you want to return to the previous file, press F2 or use the File menu. You can also leave the listing file by choosing the Listing command again; this action causes the environment to switch to the original line of source code that generated the current line of code. In particular, if you are in a listing file and move the cursor to a line generated from an include file (.INC), the Listing command switches directly to that include file.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 1.7 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% Normally, you would choose the Listing command when in a .LST file or in a%@EH@% .ASM file with a corresponding .LST file (previously generated by a program build). If you are not in either of these types of files, the environment responds by displaying a dialog box for opening a file; %@AS@%*.lst%@AE@% is the default file name.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH2 @%%@AB@%Chapter 2: Introducing 8086 Assembly Language%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX2.1 @% Assembly-language programs control hardware directly, giving you the%@EH@% ability to write the fastest, smallest programs possible and to execute any operation. But assembly-language programming also requires an understanding of the architecture of 8086-family processors.%@NL@% %@NL@% %@4@%%@CR:IX2.2 @% Assembly language is close to machine code──the processor's numeric%@EH@% language of 1's and 0's. Each QuickAssembler instruction corresponds to an 8086 instruction but consists of a meaningful name (mnemonic) instead of a number. For example, the %@AB@%ADD%@AE@% instruction computes the sum of two items. QuickAssembler translates this instruction to produce a numeric code, such as 10000010 binary. The processor responds to this code when you run the program.%@NL@% %@NL@% %@4@% This process of translation is called "assembling." Before you can%@EH@% assemble a program, you need to understand the basic concepts of the processor and of assembly language. This chapter presents these concepts.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.1 @%%@AB@%2.1 Programming the 8086 Family%@AE@%%@EH@%%@NL@% %@NL@% %@4@% If you have programmed in C, you can get a good grasp of 8086 assembly%@EH@% language by focusing on the differences between the two languages:%@NL@% %@NL@% %@CR:IX2.3 @%%@CR:IX2.4 @% 1. A C statement may combine many complex operations, but each line of assembly language specifies just one limited action called an "instruction." QuickAssembler also supports a number of nonexecutable statements called "directives," which provide structure to the program, declare data objects, and provide other information.%@NL@% %@NL@% Sections 2.2%@BO: 1602a@%-2.4 explain the basics of writing instructions and directives.%@NL@% %@NL@% %@CR:IX2.5 @% 2. C programs deal with memory locations (known as variables), but assembly-language programs must deal with registers as well. A "register" is a special memory location inside the processor itself, having a permanent name rather than a numeric address.%@NL@% %@NL@% Section 2.5%@BO: 1e475@%, "8086-Family Registers," describes the use of each register.%@NL@% %@NL@% %@CR:IX2.6 @% 3. A data object in a C program can be arbitrarily complicated. Assembly-language statements work on objects accessed through four specific modes: immediate, register, direct memory, and indirect memory. Each mode has specific properties and limitations imposed by the processor.%@NL@% %@NL@% Section 2.6%@BO: 2404d@%, "Addressing Modes," explains each of these four modes and gives examples.%@NL@% %@NL@% 4. The processor combines two 16-bit addresses to access each memory location. This mechanism is called "segmented addressing." Assembly language often requires a more complete understanding of segmented addressing than C does.%@NL@% %@NL@% Section 2.7%@BO: 28c1c@%, "Segmented Addressing and Segment Registers," explains the full implications of segmented addressing.%@NL@% %@NL@% Of the features listed above, segmented addressing is unique to the 8086 family. The 8086 is further distinguished from other processors by its set of string operations, which permit fast initialization and copying of blocks of data. You can read more about the string operations in Chapter 16%@BO: d1833@%, "Processing Strings."%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.2 @%%@AB@%2.2 Instructions, Directives, and Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The 8086-family processors understand only one kind of statement: an%@EH@% instruction. QuickAssembler understands two kinds of statements: instructions and directives.%@NL@% %@NL@% %@4@%%@CR:IX2.7 @% As explained above, an instruction corresponds to a specific action that%@EH@% the processor executes at run time. The fundamental task of the assembler is to correctly translate each of these statements to specific machine-code instructions.%@NL@% %@NL@% %@4@%%@CR:IX2.8 @% As nonexecutable statements, directives are not translated to machine%@EH@% actions. However, they give information to the assembler that affects how other statements are translated. For example, some of the most important directives declare data. These directives, in turn, help the assembler correctly interpret instructions that refer to the data.%@NL@% %@NL@% %@4@% The rest of this section explains each part of an assembly-language%@EH@% statement; the general syntax applies to both instructions and directives. The section ends by stating the basics of entering numbers in different radixes.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Each line of source code consists of a blank line or a statement. Each%@EH@% statement is an instruction or directive, and can contain as many as 512 characters. Statements can have up to four fields, as shown below:%@NL@% %@NL@% %@4@% [[%@AI@%name%@AE@%]] [[%@AI@%operation%@AE@%]] [[%@AI@%operands%@AE@%]] [[%@AB@%;%@AE@%%@AI@%comment%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.9 @%%@CR:IX2.10 @% Each field (except the comment field) must be separated from the other%@EH@% fields by a space or tab character. You can enter statements in uppercase or lowercase letters. By default, QuickAssembler is not case sensitive, but it does preserve case for external variables──thus providing compatibility with C, which is case sensitive. You can control case sensitivity by using the Assembler Flags dialog box.%@NL@% %@NL@% %@4@% As a convention, sample code in this manual uses uppercase letters for%@EH@% directives, hexadecimal letter digits, and segment definitions.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.2.1 @%%@AB@%2.2.1 The Name Field%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% field labels the statement with a symbolic name that other parts%@EH@% of the program can reference. The meaning of the name depends on the type of statement.%@NL@% %@NL@% %@4@% One of the most important uses of this field occurs in data declarations.%@EH@% These declarations are much like variable declarations in C. The statement defines the type and initial value. You use the name elsewhere in the program, when you want to access the data.%@NL@% %@NL@% %@4@% QuickAssembler is different from C, however, in that the symbolic name%@EH@% occurs in the first field. For example, the following %@AB@%DB%@AE@% directive (Declare Bytes) associates the name %@AS@%string %@AE@%with a series of characters:%@NL@% %@NL@% %@AS@%string DB "Hello, world"%@AE@%%@NL@% %@NL@% %@4@% In instructions, the %@AI@%name%@AE@% field functions like a program label in C: it%@EH@% provides a target for a jump or call instruction elsewhere in the program.%@NL@% %@NL@% %@4@% To label an instruction, follow the %@AI@%name%@AE@% field with a colon (%@AB@%:%@AE@%). You can%@EH@% place the name on the same line as the rest of the instruction or, to improve readability, on a separate line. The following example shows the latter case:%@NL@% %@NL@% %@AS@%top: ; This label marks the top of the loop%@AE@%%@NL@% %@AS@% mov ax,1 ; This is first instruction in the loop%@AE@%%@NL@% %@NL@% %@4@% There are other ways to label instructions. See Section 6.4%@BO: 5d761@%, "Defining%@EH@% Code Labels," for more information on how to declare labels.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.2.2 @%%@AB@%2.2.2 The Operation Field%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%operation%@AE@% field states the action of the statement. This field%@EH@% determines the fundamental type of the statement──instruction or directive. It also determines what additional syntax, if any, is required. Some operations require an entry in the %@AI@%name%@AE@% field; most do not. If the operation is an instruction, it strictly determines how many and what kind of operands are legal.%@NL@% %@NL@% %@4@% This field contains exactly one item──an instruction or directive%@EH@% mnemonic. "Mnemonics" are abbreviated, easy-to-remember names that each symbolize a different operation (for instance, %@AB@%ADD%@AE@%, %@AB@%SUB%@AE@%, and %@AB@%OR%@AE@%). Examples of directive mnemonics include %@AB@%EQU%@AE@% (Equate) and %@AB@%DB%@AE@% (Declare Bytes).%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.2.3 @%%@AB@%2.2.3 The Operand Field%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%operand%@AE@% field lists the objects on which the statement operates.%@EH@% Multiple operands are separated by commas. These objects can be registers, constants, or memory locations. A memory location is typically represented as a variable, although it can also be expressed as a numeric address or complex expression.%@NL@% %@NL@% %@4@% Registers and constants require no previous declaration. To refer to a%@EH@% variable, however, you should first declare the name with a data directive, such as %@AB@%DB%@AE@% (Declare Bytes). The following example declares the variable %@AS@%count %@AE@%and then uses it in an instruction:%@NL@% %@NL@% %@AS@% count DB 7 ; Declare count as a byte variable%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% inc count ; count = count + 1%@AE@%%@NL@% %@NL@% %@4@% In the first statement, %@AS@%count %@AE@%appears in the %@AI@%name%@AE@% field and the number %@AS@%7%@AE@%%@EH@% appears in the %@AI@%operand%@AE@% field. The %@AB@%DB%@AE@% directive associates %@AS@%count %@AE@%with the address of a byte initialized to %@AS@%7%@AE@%. In the second statement,%@AS@% count %@AE@%appears in the %@AI@%operand%@AE@% field. The %@AB@%INC%@AE@% instruction (increment) adds 1 to %@AS@%count%@AE@%, thus increasing the value of the data to %@AS@%8%@AE@%.%@NL@% %@NL@% %@4@% The next section gives more information on how to declare memory locations%@EH@% as data types. Section 2.6%@BO: 2404d@%, "Addressing Modes," gives a complete description of all the different methods for specifying operands.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.2.4 @%%@AB@%2.2.4 The Comment Field%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%comment%@AE@% field lets you add text that appears in source code but is%@EH@% ignored by the assembler. You can enter any text you want in this field. Typically, you would use it to document the purpose of the statement. The purpose of an assembly-language statement is not always self-explanatory, and for this reason, programs often contain at least one comment for each instruction.%@NL@% %@NL@% %@4@% Single-line comments always begin with a semicolon (%@AB@%;%@AE@%). You can also%@EH@% create a multiline comment by one of two methods. You can enter successive comment lines as shown below:%@NL@% %@NL@% %@AS@% add count,5 ; Add 5 to count.%@AE@%%@NL@% %@AS@% ; ADD is the operation.%@AE@%%@NL@% %@AS@% ; count and 5 are operands.%@AE@%%@NL@% %@AS@% sub Sum,12 ; Subtract 12 from Sum.%@AE@%%@NL@% %@AS@% ; SUB is the operation.%@AE@%%@NL@% %@AS@% ; Sum and 12 are operands.%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.11 @%%@CR:IX2.12 @% You can also use the %@AB@%COMMENT%@AE@% directive, which lets you enter multiline%@EH@% comments without using the semicolon. This directive has the following syntax:%@NL@% %@NL@% %@4@% %@AB@%COMMENT %@AE@%%@AI@%delimiter%@AE@% [[%@AI@%text%@AE@%]]%@EH@%%@NL@% %@AI@%text%@AE@%%@NL@% [[%@AI@%text%@AE@%]] %@AI@%delimiter%@AE@% [[%@AI@%text%@AE@%]]%@NL@% %@NL@% %@4@% All %@AI@%text%@AE@% between the first %@AI@%delimiter%@AE@% and the line containing a second%@EH@% %@AI@%delimiter%@AE@% is ignored by the assembler. The %@AI@%delimiter%@AE@% character is the first nonblank character after the %@AB@%COMMENT%@AE@% directive. The %@AI@%text%@AE@% includes the comments up to and including the line containing the next occurrence of the delimiter.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% COMMENT + The plus%@AE@%%@NL@% %@AS@% sign is the delimiter. The%@AE@%%@NL@% %@AS@% assembler ignores the statement%@AE@%%@NL@% %@AS@% following the last delimiter%@AE@%%@NL@% %@AS@%+ mov ax,1 (ignored)%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.2.5 @%%@AB@%2.2.5 Entering Numbers in Different Bases%@AE@%%@EH@%%@NL@% %@NL@% %@4@% As with C, you can enter assembly-language constants as decimal,%@EH@% hexadecimal, or octal. You can also enter binary constants. By default, all constants are decimal, but you specify a different default with the %@AB@%RADIX%@AE@% directive.%@NL@% %@NL@% %@4@% Hexadecimal constants appear frequently in assembly-language programs. To%@EH@% indicate a hexadecimal constant, add an uppercase or lowercase H suffix. If the first digit is one of the letters A-F, prefix the constant with a leading 0 to indicate that the number is not a symbolic name.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%100H%@AE@%%@NL@% %@AS@%10FAh%@AE@%%@NL@% %@AS@%0be03H%@AE@%%@NL@% %@AS@%0FFh%@AE@%%@NL@% %@NL@% %@4@% You may often want to enter binary constants as well, particularly when%@EH@% constructing bit masks. To indicate a binary constant, simply add an uppercase or lowercase B suffix.%@NL@% %@NL@% %@4@% For more information on using different bases and using the %@AB@%RADIX%@AE@%%@EH@% directive, see Section 6.1.1.2%@BO: 58a87@%, "Setting the Default Radix."%@NL@% %@NL@% %@CR:IX2.13 @%%@NL@% %@3@%%@CR:SC2.2.6 @%%@AB@%2.2.6 Line-Continuation Character%@AE@%%@EH@%%@NL@% %@NL@% %@4@% You can create program lines that extend over more than one physical line%@EH@% by using the backslash (\) as a line-continuation character. The backslash must be the last character on the line. Comments cannot follow it. A backslash is not considered a continuation character if it occurs in a comment.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%BigProc PROC FAR \%@AE@%%@NL@% %@AS@% USES DS SI DI, \%@AE@%%@NL@% %@AS@% IntArg:WORD, \%@AE@%%@NL@% %@AS@% String:FAR PTR BYTE, \%@AE@%%@NL@% %@AS@% Ptr:FAR PTR BIGSTRUC, \%@AE@%%@NL@% %@AS@% Long:DWORD%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%BigProc ENDP%@AE@%%@NL@% %@NL@% %@4@% In this example, the line continuation-character is used to specify%@EH@% multiple procedure arguments with the extended %@AB@%PROC%@AE@% syntax. All the arguments must be placed on a single logical line, but they would go past the edge of the editor screen if not placed on separate lines. The continuation character is also useful for long macro calls and data initializations.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.3 @%%@AB@%2.3 8086-Family Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.14 @% The 8086-family processors support more than 80 instructions, but you%@EH@% don't need to memorize the entire instruction set. Once inside the expanded QuickC environment, you can get instant information on any instruction. Move the cursor to an instruction keyword on the screen, then press F1. To find the appropriate instruction for the action you want to perform, refer to Part 3%@BO: a6c61@% of this book, which provides a topical survey of instructions.%@NL@% %@NL@% %@4@% Many programs can be written with just a few of the most common%@EH@% instructions. Sections 2.3.1%@BO: 1923f@% and 2.3.2%@BO: 1a91f@% introduce some of these instructions, grouping them into two sets: instructions that manipulate data and instructions that control program flow. The programs in Chapters 3%@BO: 2aca0@% and 4%@BO: 34dba@% use these same instructions to illustrate basic concepts of 8086 assembly language.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.3.1 @%%@AB@%2.3.1 Data-Manipulation Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.15 @%%@CR:IX2.16 @% The first group of instructions manipulate data. Each causes the processor%@EH@% to copy data or perform a calculation at run time. Some of the simpler C statements translate directly into a single instruction, so this section uses C statements for illustration. Here are the six basic data- manipulation instructions introduced in this section:%@NL@% %@NL@% ■ %@AB@%MOV%@AE@% (move data)%@NL@% %@NL@% ■ %@AB@%ADD%@AE@% (add second operand to first)%@NL@% %@NL@% ■ %@AB@%SUB%@AE@% (subtract second operand from first)%@NL@% %@NL@% ■ %@AB@%INC%@AE@% (increment operand)%@NL@% %@NL@% ■ %@AB@%DEC%@AE@% (decrement operand)%@NL@% %@NL@% ■ %@AB@%MUL%@AE@% (integer multiplication)%@NL@% %@NL@% %@4@% The processor supports a great many other data-manipulation instructions,%@EH@% which are covered in Part 3%@BO: a6c61@% of this manual.%@NL@% %@NL@% %@CR:IX2.17 @%%@CR:IX2.18 @%%@NL@% %@3@%%@CR:SC2.3.1.1 @%%@AB@%2.3.1.1 The MOV Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%MOV%@AE@% instruction, probably the most frequently used 8086 instruction,%@EH@% copies data from one location to another. The instruction leaves the source data unaffected, so it is more a copy than a move. The %@AB@%MOV%@AE@% instruction takes two operands:%@NL@% %@NL@% %@4@% %@AB@%MOV%@AE@% %@AI@%destination%@AE@%%@AB@%,%@AE@%%@AI@%source%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.19 @%%@CR:IX2.20 @% The instruction copies the value of the %@AI@%source%@AE@% to the %@AI@%destination%@AE@%. It%@EH@% might seem more logical to place the %@AI@%source%@AE@% operand first, until you consider that C and BASIC assignments use the same order. For example, the instruction%@NL@% %@NL@% %@AS@%mov count,5%@AE@%%@NL@% %@NL@% %@4@% places the value %@AS@%5 %@AE@%at the memory location %@AS@%count %@AE@%and thus performs the same%@EH@% action as the C statement%@NL@% %@NL@% %@AS@%count = 5;%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.21 @%%@CR:IX2.22 @% The destination operand is similar to an "lvalue" in C. Instructions that%@EH@% have two operands always interpret the leftmost operand as the destination, or lvalue. The destination is the operand that the instruction can alter; thus, it can't be a constant. Another limitation on instructions with two operands is that the operands cannot both be memory locations.%@NL@% %@NL@% %@CR:IX2.23 @%%@CR:IX2.24 @%%@NL@% %@3@%%@CR:SC2.3.1.2 @%%@AB@%2.3.1.2 The ADD Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%ADD%@AE@% instruction, like %@AB@%MOV%@AE@%, takes two operands: a destination and a%@EH@% source. The processor adds the two operands together, storing the result in the destination (on the left). This action will be familiar to C programmers, since the instruction%@NL@% %@NL@% %@AS@%add sum,10%@AE@%%@NL@% %@NL@% %@4@% adds %@AS@%10 %@AE@%to the memory location %@AS@%sum %@AE@%and thus performs the same action as%@EH@% the C statement%@NL@% %@NL@% %@AS@%sum += 10;%@AE@%%@NL@% %@NL@% %@4@% The 8086 does not perform automatic scaling for pointer addition as C%@EH@% does. The program itself must perform scaling for all pointer arithmetic.%@NL@% %@NL@% %@CR:IX2.25 @%%@CR:IX2.26 @%%@NL@% %@3@%%@CR:SC2.3.1.3 @%%@AB@%2.3.1.3 The SUB Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%SUB%@AE@% instruction is the counterpart of %@AB@%ADD%@AE@%: it subtracts the source%@EH@% operand from the destination operand, storing the result in the destination (on the left). Thus, the instruction%@NL@% %@NL@% %@AS@%sub total,7%@AE@%%@NL@% %@NL@% %@4@% performs the same action as the C statement%@EH@%%@NL@% %@NL@% %@AS@%total -= 7;%@AE@%%@NL@% %@NL@% %@CR:IX2.27 @%%@CR:IX2.28 @%%@CR:IX2.29 @%%@CR:IX2.30 @%%@NL@% %@3@%%@CR:SC2.3.1.4 @%%@AB@%2.3.1.4 The INC and DEC Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%INC%@AE@% (Increment) and %@AB@%DEC%@AE@% (Decrement) instructions add and subtract 1,%@EH@% respectively. They are similar to, but faster than, %@AB@%ADD%@AE@% and %@AB@%SUB%@AE@%, and are provided because adding and subtracting by 1 are such common operations. The instruction%@NL@% %@NL@% %@AS@%inc count%@AE@%%@NL@% %@NL@% %@4@% performs the same action as the C statement%@EH@%%@NL@% %@NL@% %@AS@%count++;%@AE@%%@NL@% %@NL@% %@CR:IX2.31 @%%@CR:IX2.32 @%%@NL@% %@3@%%@CR:SC2.3.1.5 @%%@AB@%2.3.1.5 The AND Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%AND%@AE@% instruction is one of several bitwise logic operations supported%@EH@% by the 8086. %@AB@%AND%@AE@% provides an efficient way to mask out bits. The instruction%@NL@% %@NL@% %@AS@%and stuff,0FFF0h%@AE@%%@NL@% %@NL@% %@4@% masks out the four lowest bits of %@AS@%stuff%@AE@%, as does the C statement%@EH@%%@NL@% %@NL@% %@AS@%stuff &= 0x0FFF0;%@AE@%%@NL@% %@NL@% %@CR:IX2.33 @%%@CR:IX2.34 @%%@NL@% %@3@%%@CR:SC2.3.1.6 @%%@AB@%2.3.1.6 The MUL Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%MUL%@AE@% instruction multiplies two items, but one of these items is an%@EH@% "implied operand"──that is, an operand you do not specify. For example, the 16-bit version of the %@AB@%MUL%@AE@% instruction takes one explicit 16-bit operand:%@NL@% %@NL@% %@AS@%mul factor%@AE@%%@NL@% %@NL@% %@4@% The other operand is the AX register. The processor multiplies %@AS@%factor %@AE@%by%@EH@% the value of AX, storing the low 16 bits of the result in AX. The description of the AX register in Section 2.5.1%@BO: 1ef06@%, "The General-Purpose Registers," gives more information on %@AB@%MUL%@AE@%.%@NL@% %@NL@% %@CR:IX2.35 @%%@CR:IX2.36 @%%@NL@% %@3@%%@CR:SC2.3.2 @%%@AB@%2.3.2 Control-Flow Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The control-flow instructions enable the program to execute loops and to%@EH@% make decisions. Some of these instructions transfer control of the program to a new address. The conditional jump instructions let you provide program logic: they look at the result of a previous operation, and then decide whether to jump or not. Here are the five basic control-flow instructions introduced in this section:%@NL@% %@NL@% ■ %@AB@%JMP%@AE@% (Jump unconditionally)%@NL@% %@NL@% ■ %@AB@%CMP%@AE@% (Compare──subtract without storing result)%@NL@% %@NL@% ■ %@AB@%JE%@AE@% (Jump If Equal)%@NL@% %@NL@% ■ %@AB@%JA%@AE@% (Jump If Above)%@NL@% %@NL@% ■ %@AB@%JB%@AE@% (Jump If Below)%@NL@% %@NL@% %@4@% The processor supports a number of other control-flow instructions,%@EH@% including several conditional jumps. See Section 15.1.2%@BO: be4fa@%, "Jumping Conditionally," for a description of these instructions.%@NL@% %@NL@% %@CR:IX2.37 @%%@CR:IX2.38 @%%@NL@% %@3@%%@CR:SC2.3.2.1 @%%@AB@%2.3.2.1 The JMP Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%JMP%@AE@% instruction causes the processor to jump to a new program address.%@EH@% Like the C %@AB@%goto%@AE@% statement, %@AB@%JMP%@AE@% takes one operand: a label associated with another statement. The instruction%@NL@% %@NL@% %@AS@%jmp begin%@AE@%%@NL@% %@NL@% %@4@% jumps to the label %@AS@%begin%@AE@%, and thus performs the same action as the C%@EH@% statement%@NL@% %@NL@% %@AS@%goto begin;%@AE@%%@NL@% %@NL@% %@CR:IX2.39 @%%@CR:IX2.40 @%%@NL@% %@3@%%@CR:SC2.3.2.2 @%%@AB@%2.3.2.2 The CMP Instruction%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%CMP%@AE@% instruction, like %@AB@%SUB%@AE@%, performs a subtraction. But %@AB@%CMP%@AE@% doesn't%@EH@% store the result; instead, it just sets processor flags in preparation for a conditional jump (such as %@AB@%JE%@AE@%, %@AB@%JA%@AE@%, or %@AB@%JB%@AE@%).%@NL@% %@NL@% %@4@% A "processor flag" is a bit that resides in the processor and indicates%@EH@% whether a specific condition is on or off. For example, the Zero flag indicates that the result of the last operation produced zero. The %@AB@%JE%@AE@% instruction (Jump If Equal) checks this one flag only, jumping if it is set. Other conditional jumps determine a result by checking a %@AI@%combination%@AE@% of flag settings. See Section 2.5.4%@BO: 22b46@%, "The Flags Register," for a description of all the flags.%@NL@% %@NL@% %@4@% Many instructions, including %@AB@%SUB%@AE@%, set processor flags. However, some of%@EH@% these instructions have strong side effects. Use %@AB@%ADD%@AE@% or %@AB@%SUB%@AE@% to prepare for a conditional jump when convenient. But use %@AB@%CMP%@AE@% when you need to make a simple comparison without altering data.%@NL@% %@NL@% %@CR:IX2.41 @%%@CR:IX2.42 @%%@NL@% %@3@%%@CR:SC2.3.2.3 @%%@AB@%2.3.2.3 The Conditional Jump Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%JE%@AE@%, %@AB@%JA%@AE@%, and %@AB@%JB%@AE@% instructions are conditional jumps (meaning Jump On%@EH@% Equal, Jump If Above, and Jump If Below, respectively). Like %@AB@%JMP%@AE@%, they each take one argument: a program label to which to jump. Unlike %@AB@%JMP%@AE@%, they cause the processor to jump only when certain flag settings are detected. The result is that when you use %@AB@%CMP%@AE@% in combination with a conditional jump instruction, you create an if-then relationship similar to an %@AB@%if%@AE@% statement in a high-level language. Consider the following instructions:%@NL@% %@NL@% %@AS@% cmp sum,10 ; Compare sum to 10%@AE@%%@NL@% %@AS@% ja top ; If sum > 10, jump to top%@AE@%%@NL@% %@NL@% %@4@% This logic is a little different from a C program. The first instruction%@EH@% makes the comparison. The second states, "If the result of the %@AI@%previous%@AE@% instruction was above zero, then jump." Taken together, these two instructions perform the same action as the C statement%@NL@% %@NL@% %@AS@%if( sum > 10 )%@AE@%%@NL@% %@AS@% goto top;%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.43 @% Of course, most C programmers do not use many%@AB@% goto %@AE@%statements. Typically,%@EH@% you would test for a condition and execute a series of statements if the condition is true, as in the following code:%@NL@% %@NL@% %@AS@%if( sum >= 10 )%@AE@%%@NL@% %@AS@%{%@AE@%%@NL@% %@AS@% sum = 1;%@AE@%%@NL@% %@AS@% count += 2;%@AE@%%@NL@% %@AS@% delta = 5;%@AE@%%@NL@% %@AS@%}%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.44 @% To implement this code in assembly language, test for the opposite%@EH@% condition, then jump past statements if they should not be executed. For example, the following code executes the three statements inside the if block only if %@AS@%sum %@AE@%is greater than or equal to %@AS@%10%@AE@%:%@NL@% %@NL@% %@AS@%TopOfBlock:%@AE@%%@NL@% %@AS@% cmp sum,10 ; Compare sum to 10%@AE@%%@NL@% %@AS@% jb SumNotGreater ; If sum < 10, do NOT do%@AE@%%@NL@% %@AS@% ; next three statements%@AE@%%@NL@% %@AS@% mov sum,1 ; sum = 1%@AE@%%@NL@% %@AS@% add count,2 ; count = count + 2%@AE@%%@NL@% %@AS@% mov delta,5 ; delta = 5%@AE@%%@NL@% %@AS@%SumNotGreater:%@AE@%%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% %@AB@%JA%@AE@% (Jump If Above) and %@AB@%JB%@AE@% (Jump If Below) each work properly when you compare unsigned integers. To compare signed integers, use %@AB@%JG%@AE@% (Jump If Greater) and %@AB@%JL%@AE@% (Jump If Less Than). See Section 15.1.2%@BO: be4fa@%, "Jumping Conditionally," for a complete list of conditional jump instructions.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.4 @%%@AB@%2.4 Declaring Simple Data Objects%@AE@%%@EH@%%@NL@% %@NL@% %@4@% This section describes how to declare global variables──often called%@EH@% "static" because each corresponds to a fixed memory location.%@NL@% %@NL@% %@4@% Programs generally require data. If you wrote a program in machine code,%@EH@% you'd have to reserve locations in memory for data, determine the address of each data object, and remember these addresses whenever you operated on memory. Fortunately, the assembler reserves memory locations for you and associates each location with a symbolic name.%@NL@% %@NL@% %@4@%%@CR:IX2.45 @%%@CR:IX2.46 @% You use data directives to tell the assembler how to allocate and refer to%@EH@% memory. The most common data directives for characters and integers are:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX2.47 @%%@CR:IX2.48 @% %@AB@%DB%@AE@% Declare byte (either a small integer or a character)%@NL@% %@NL@% %@CR:IX2.49 @%%@CR:IX2.50 @% %@AB@%DW%@AE@% Declare word (2-byte integer)%@NL@% %@NL@% %@CR:IX2.51 @%%@CR:IX2.52 @% %@AB@%DD%@AE@% Declare doubleword (4-byte integer)%@NL@% %@NL@% %@NL@% %@4@% To use these directives, place the name of the variable first, then enter%@EH@% the data directive. The third column (operand field) contains one or more initial values. Use a question mark to indicate an item with no initial value.%@NL@% %@NL@% %@AS@%aByte DB 1 ; aByte is a 1-byte integer, initialized to 1%@AE@%%@NL@% %@AS@%area DW 500 ; area is a 2-byte integer, initialized to 500%@AE@%%@NL@% %@AS@%population DD ? ; population is a 4-byte integer, no initial value%@AE@%%@NL@% %@NL@% %@4@% These directives correspond roughly to the following C statements:%@EH@%%@NL@% %@NL@% %@AS@%char aByte = 1;%@AE@%%@NL@% %@AS@%int area = 500;%@AE@%%@NL@% %@AS@%long population;%@AE@%%@NL@% %@NL@% %@4@% Assembly data declarations are different from C declarations, however, in%@EH@% that assembly data declarations are not declared signed or unsigned. Instead, you must remember whether you intend to treat a variable as signed or unsigned, and choose the appropriate operations.%@NL@% %@NL@% %@4@%%@CR:IX2.53 @% Data directives reserve memory in the object file. They also associate%@EH@% each variable with a name and a size attribute.%@NL@% %@NL@% %@4@% The assembler uses this information to correctly assemble instructions%@EH@% that operate on variables. For example, at the machine-code level, the %@AB@%INC%@AE@% instruction can be encoded to increment either a byte or a word of data. The way the assembler encodes the instruction%@NL@% %@NL@% %@AS@% inc myvar%@AE@%%@NL@% %@NL@% %@4@% depends on whether %@AS@%myvar %@AE@%was declared as a byte or word. (If it was%@EH@% declared a doubleword, the instruction is illegal.) Another important use of size attributes is in checking the validity of two operands. For example, the following instruction causes the assembler to print a warning message, because %@AS@%aByte %@AE@%and %@AS@%bx %@AE@%do not share the size attribute:%@NL@% %@NL@% %@AS@% mov bx,aByte ; Move aByte into a word register%@AE@%%@NL@% %@NL@% %@4@% Moving a byte into a word location is not possible. After issuing the%@EH@% warning, the assembler adjusts the instruction as if it were written as follows:%@NL@% %@NL@% %@AS@% mov bx,WORD PTR aByte ; Move the word at aByte to BX%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.54 @%%@CR:IX2.55 @% The %@AB@%PTR%@AE@% operator temporarily modifies the size attribute of the object%@EH@% that follows it. %@AB@%PTR%@AE@% can be used with a number of different data types, as shown below:%@NL@% %@NL@% %@AB@%Keywords%@AE@% %@AB@%Refers to%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%BYTE PTR%@AE@% %@AI@%object%@AE@% The byte at address of %@AI@%object%@AE@%%@NL@% %@NL@% %@AB@%WORD PTR%@AE@% %@AI@%object%@AE@% The word at address of %@AI@%object%@AE@%%@NL@% %@NL@% %@AB@%DWORD PTR%@AE@% %@AI@%object%@AE@% The doubleword at address of %@AI@%object%@AE@%%@NL@% %@NL@% %@NL@% %@4@% However, this adjustment may not produce the action you really want. The%@EH@% %@AB@%PTR%@AE@% operator is not quite the same as a type cast in C. The C %@AS@%(int)%@AE@% type cast manipulates data so that it represents the same value, but in a different format. %@AB@%WORD PTR%@AE@% does no data manipulation──it simply causes the instruction to operate on the word at the given address. In the example above, the use of %@AB@%WORD PTR%@AE@% causes two adjacent bytes of data to be loaded from memory into BX. If what you really want is to move a single byte of data to BX, but convert it to a word, use the following code:%@NL@% %@NL@% %@AS@% mov bl,aByte ; Lower byte of BX = aByte%@AE@%%@NL@% %@AS@% sub bh,bh ; Higher byte of BX = 0%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.56 @% The example above only works properly when handling unsigned numbers. When%@EH@% working with signed quantities, use the %@AB@%CBW%@AE@% instruction, as described in Section 13.2.1%@BO: a9772@%, "Extending Signed Values."%@NL@% %@NL@% %@4@%%@CR:IX2.57 @% By far the most common use of %@AB@%WORD PTR%@AE@% is in operations on objects 32 bits%@EH@% or longer. An 8086 instruction can operate only on a byte or a word. You use %@AB@%WORD PTR%@AE@% to tell the assembler to operate on one word at a time. For example, the following code uses two moves to copy the 32-bit integer %@AS@%X %@AE@%to a similar integer, %@AS@%Y%@AE@%:%@NL@% %@NL@% %@AS@%X DD 80000 ; X is a long integer = 80,000%@AE@%%@NL@% %@AS@%Y DD ? ; Y is a long integer%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax, WORD PTR X ; Move word at X to word at Y%@AE@%%@NL@% %@AS@% mov WORD PTR Y, ax ; (using AX as intermediate register)%@AE@%%@NL@% %@AS@% mov ax, WORD PTR X[2] ; Move word 2 bytes past X to%@AE@%%@NL@% %@AS@% mov WORD PTR Y[2], ax ; word 2 bytes past Y%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.58 @%%@CR:IX2.59 @% Brackets ([ ]) are used with arrays as well as portions of large data%@EH@% objects as shown here; they also let you add a displacement to an address. The use of brackets is further explained in the next few paragraphs.%@NL@% %@NL@% %@4@%%@CR:IX2.60 @% Assembly language makes almost no distinction between simple variables and%@EH@% arrays. You refer to the first element of an array just as you would a simple variable──index brackets are optional. To declare an array or string, just give a series of initial values:%@NL@% %@NL@% %@AS@%warray DW ?,?,?,?%@AE@%%@NL@% %@AS@%xarray DW 1,2,3,4%@AE@%%@NL@% %@AS@%mystring DB "Hello, there."%@AE@%%@NL@% %@NL@% %@4@% To refer to the first element of %@AS@%warray%@AE@%, type %@AS@%warray %@AE@%into your program (no%@EH@% brackets required). To refer to the next element, use either of these two forms, each of which refers to the object two bytes past the beginning of %@AS@%warray%@AE@%:%@NL@% %@NL@% %@AS@%warray+2%@AE@%%@NL@% %@AS@%warray[2]%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.61 @%%@CR:IX2.62 @% When used with a variable name, the brackets do nothing but add a number%@EH@% to the address. If %@AS@%warray %@AE@%refers to the address %@AS@%2400h%@AE@%, then %@AS@%warray[2]%@AE@% refers to the address %@AS@%2402h%@AE@%. However, the brackets have an additional function when used with registers. See Section 2.6.4%@BO: 27375@%, "Indirect Memory Operands," for more information.%@NL@% %@NL@% %@4@% In assembly language, array indexes are zero-based, as in C; but unlike C,%@EH@% they are unscaled. The number inside brackets always represents an absolute distance in bytes.%@NL@% %@NL@% %@4@%%@CR:IX2.63 @% In practical terms, the fact that indexes are unscaled means that if the%@EH@% size of an element is larger than one byte, you must multiply the index of the element by its size (in this case, 2), then add the result to the address of the array. Thus, the expression %@AS@%warray[4] %@AE@%represents the third element, which is 4 bytes past the beginning of the array. Similarly, the expression %@AS@%warray[6] %@AE@%represents the fourth element.%@NL@% %@NL@% %@4@% In general, the numeric offset required to access an array element can be%@EH@% calculated as shown in the following formula:%@NL@% %@NL@% %@4@% %@AS@%Nth element of Array = Array[(N-1) * size of element]%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.5 @%%@AB@%2.5 8086-Family Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.64 @%%@CR:IX2.65 @% A "register" is a special memory location inside the processor itself.%@EH@% Operations on registers execute faster than operations on main memory. The processor has a limited number of registers. Moreover, many operations on the 8086 are impossible without the use of registers at some point. For example, you cannot copy data between two memory locations without first moving it into a register.%@NL@% %@NL@% %@4@%%@CR:IX2.66 @% Figure 2.1 shows the registers common to all the 8086-family processors.%@EH@% The 8086 registers can be grouped by function into the following sets: general-purpose registers, index registers, pointer registers, and segment registers. Each set corresponds to a different ending letter (X, I, P, or S). The registers in each set are as follows:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 2.5 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@CR:IX2.67 @% ■ The four general-purpose registers are AX, BX, CX, and DX. These registers exist for the general use of the program. You can use these registers to store temporary values and perform calculations.%@NL@% %@NL@% %@CR:IX2.68 @% ■ The two index registers are SI (Source Index) and DI (Destination Index). These registers can also be used for general storage, but are less flexible than the general-purpose registers. SI and DI have a special purpose in string instructions.%@NL@% %@NL@% %@CR:IX2.69 @% ■ The pointer registers are IP (Instruction Pointer), SP (Stack Pointer), and BP (Base Pointer). These registers should not be confused with BX, which is the register normally used for pointer indirection. IP, SP, and BP each have a special purpose in conjunction with procedure calls. SP and BP should be altered with care; IP cannot be altered or referenced directly at all.%@NL@% %@NL@% ■ The segment registers are CS, DS, SS, and ES. This section does not describe these registers. You generally don't alter or reference them except when starting the program or accessing data from multiple segments. Section 2.7%@BO: 28c1c@%, "Segmented Addressing and Segment Registers," describes each segment register and how it is important to programs.%@NL@% %@NL@% %@4@%%@CR:IX2.70 @% In addition, there is a flags register that indicates the status of the%@EH@% process.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.1 @%%@AB@%2.5.1 The General-Purpose Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.71 @%%@CR:IX2.72 @%%@CR:IX2.73 @% The general-purpose registers have many important uses in an 8086%@EH@% assembly-language program, including:%@NL@% %@NL@% ■ Storing the values most frequently used. Operations on registers are much faster than operations on memory. Therefore, place the program's principal values in registers. In larger programs, you will probably have too many variables to place them all into registers. You can, however, place a value in a register while it is in heavy use.%@NL@% %@NL@% ■ Supporting operations with two or more variables. Direct memory-to-memory operations are illegal with 8086 processors. To operate on two memory locations, you need to first load one of the values into a register.%@NL@% %@NL@% ■ Enabling use of all the instructions. Many instructions require the use of a particular register. For example, the %@AB@%MUL%@AE@% instruction always works with the AX register (or AL, if you specify a byte operand).%@NL@% %@NL@% ■ Passing or returning values in a procedure or interrupt call.%@NL@% %@CR:IX2.74 @%%@CR:IX2.75 @%%@CR:IX2.76 @%%@CR:IX2.77 @%%@CR:IX2.78 @%%@CR:IX2.79 @%%@CR:IX2.80 @%%@CR:IX2.81 @%%@CR:IX2.82 @%%@CR:IX2.83 @%%@NL@% %@4@%%@CR:IX2.84 @%%@CR:IX2.85 @%%@CR:IX2.86 @%%@CR:IX2.87 @%%@CR:IX2.88 @%%@CR:IX2.89 @% Each of the general-purpose registers──AX, BX, CX, and DX──can be accessed%@EH@% as single 16-bit registers, or as two 8-bit registers. As shown in Figure 2.1, the AH, BH, CH, and DH registers represent the high-order 8 bits of the corresponding registers. Similarly, AL, BL, CL, and DL represent the low-order 8 bits.%@NL@% %@NL@% %@4@% This design lets you operate directly on two-byte and one-byte objects. It%@EH@% also lets you load a two-byte object and then manipulate one byte at a time.%@NL@% %@NL@% %@4@% Each of the general-purpose registers has special uses, discussed below.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.1.1 @%%@AB@%2.5.1.1 The AX Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.90 @%%@CR:IX2.91 @%%@CR:IX2.92 @%%@CR:IX2.93 @% The AX (Accumulator) register is ideal for repeated calculations. It%@EH@% accumulates totals as well as the results of multiplication and division. Using AX can add speed to your program, because some instructions have special encodings optimized for use with AX.%@NL@% %@NL@% %@4@% Multiplication instructions always use AX. In the 16-bit version of the%@EH@% %@AB@%MUL%@AE@% instruction, you specify one 16-bit value. The processor multiplies this value by the contents of AX and stores the 16 least significant binary digits of the result in AX. (The 16 most significant digits are stored in DX.)%@NL@% %@NL@% %@4@%%@CR:IX2.94 @%%@CR:IX2.95 @% The following example multiplies %@AS@%base %@AE@%times %@AS@%height%@AE@%, and stores the result%@EH@% in %@AS@%area%@AE@%. These instructions are sufficient if the result does not exceed the limit for two-byte numbers (otherwise, the DX register will contain the overflow):%@NL@% %@NL@% %@AS@%base DW 5 ; base is a word, initialized to 5%@AE@%%@NL@% %@AS@%height DW 3 ; height is a word, initialized to 3%@AE@%%@NL@% %@AS@%area DW ? ; area stores 16-bit (word) product%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,base ; AX = base%@AE@%%@NL@% %@AS@% mul height ; AX = AX * height%@AE@%%@NL@% %@AS@% mov area,ax ; area = result%@AE@%%@NL@% %@NL@% %@4@% AX has a similar use in division instructions (%@AB@%DIV%@AE@% and %@AB@%IDIV%@AE@%). See Section%@EH@% 14.4%@BO: b3ae1@%, "Dividing," for examples of division. Also, in port I/O instructions, AX holds the data to write to a port and receives data read from a port.%@NL@% %@NL@% %@4@% By convention, AX has another special use. Microsoft high-level languages%@EH@% expect AX to contain a function's return value. If the return value is longer than four bytes, the high-level languages expect DX:AX to point to the location of the return value.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.1.2 @%%@AB@%2.5.1.2 The BX Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.96 @%%@CR:IX2.97 @%%@CR:IX2.98 @%%@CR:IX2.99 @%%@CR:IX2.100 @% The BX (Base) register has great importance as a pointer or address%@EH@% register. All 16-bit registers can hold addresses, but not all registers can be used to retrieve the contents of an address. In C this operation is called "pointer dereferencing," or "indirection." The C source code to implement this action might look like this:%@NL@% %@NL@% %@AS@%value = *pVar;%@AE@%%@NL@% %@NL@% %@4@% The following assembly code achieves the same effect:%@EH@%%@NL@% %@NL@% %@AS@% mov bx,pVar ; BX = pVar%@AE@%%@NL@% %@AS@% mov value,[bx] ; value = object pointed to by BX%@AE@%%@NL@% %@NL@% %@4@% The brackets around BX in the second instruction direct QuickAssembler to%@EH@% consider BX a pointer to the actual operand. The item %@AS@%[bx] %@AE@%is an example of an indirect memory operand. See Section 2.6.4%@BO: 27375@%, "Indirect Memory Operands," for more information.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.1.3 @%%@AB@%2.5.1.3 The CX Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.101 @%%@CR:IX2.102 @%%@CR:IX2.103 @% The CX (Count) register has special meaning to instructions with a%@EH@% repeat-operation feature. The contents of CX indicate how many times to repeat execution. Loops, string operations, certain jump instructions, and shifts and rotates all use CX this way.%@NL@% %@NL@% %@4@%%@CR:IX2.104 @%%@CR:IX2.105 @%%@CR:IX2.106 @% A common instruction that uses CX to repeat execution is %@AB@%LOOP%@AE@%, which is%@EH@% analogous to the C %@AB@%for%@AE@% statement. This instruction subtracts one from CX, then jumps to the given label if CX is not equal to 0. Thus, the following loop executes 20 times:%@NL@% %@NL@% %@AS@% mov cx,20%@AE@%%@NL@% %@AS@%top:%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop top%@AE@%%@NL@% %@NL@% %@4@% In the case of shifts and rotates, CL (the lower byte of CX) indicates how%@EH@% many bit positions to shift. See Section 14.7%@BO: ba3f6@%, "Shifting and Rotating Bits," for more information. Also, when an instruction has a %@AB@%REP%@AE@% (repeat) prefix, the value in CX determines how many times the instruction is executed.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.1.4 @%%@AB@%2.5.1.4 The DX Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.107 @%%@CR:IX2.108 @% The DX (Data) register often is used only for storage of temporary values.%@EH@% However, DX has a special function in some versions of the multiplication, division, and port instructions. Each of these uses is closely related to AX. In fact, DX is located next to AX in the actual physical layout of the 8086 chip. (Figure 2.1 places the registers in the order AX, BX, CX, and DX merely for ease of reference.)%@NL@% %@NL@% %@4@%%@CR:IX2.109 @%%@CR:IX2.110 @% When you multiply 16-bit values with %@AB@%MUL%@AE@%, DX holds the high 16 bits of the%@EH@% 32-bit result. The following example is a variation of the one given for AX. In this example, %@AS@%Area %@AE@%is a 32-bit value (a long integer), and it stores the entire 32-bit result of the %@AB@%MUL%@AE@% instruction:%@NL@% %@NL@% %@AS@%base DW 500 ; base is a word, initialized to 500%@AE@%%@NL@% %@AS@%height DW 300 ; height is a word, initialized to 300%@AE@%%@NL@% %@AS@%area DD ? ; area stores doubleword product%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,base ; AX = base%@AE@%%@NL@% %@AS@% mul height ; DX:AX = AX * height%@AE@%%@NL@% %@AS@% mov WORD PTR area[0],ax ; Store low 16 bits%@AE@%%@NL@% %@AS@% mov WORD PTR area[2],dx ; Store high 16 bits%@AE@%%@NL@% %@NL@% %@4@% By convention, Microsoft high-level languages use both DX and AX to return%@EH@% four-byte values from procedures. The high 16 bits are placed in DX.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.2 @%%@AB@%2.5.2 The Index Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.111 @%%@CR:IX2.112 @%%@CR:IX2.113 @%%@CR:IX2.114 @%%@CR:IX2.115 @%%@CR:IX2.116 @% The two index registers are SI (Source Index) and DI (Destination Index).%@EH@% These registers are similar to the general-purpose registers, but cannot be accessed one byte at a time. Index registers are efficient places to store general data, pointers, array indexes, and pointers to blocks of memory. They have the following special uses:%@NL@% %@NL@% ■ You can use both SI and DI for pointer indirection, as you can BX and BP. "Pointer indirection" is the process of retrieving the value that a pointer points to.%@NL@% %@NL@% ■ You can use SI or DI to hold an array index. Indirect memory operands can combine this index with a base address stored in BX or BP.%@NL@% %@NL@% ■ You prepare for string instructions, which execute highly efficient block operations, by loading SI with a source address and DI with a destination address.%@NL@% %@NL@% %@4@% See Chapter 16%@BO: d1833@%, "Processing Strings," for information on how to use%@EH@% string instructions.%@NL@% %@NL@% %@4@%%@CR:IX2.117 @%%@CR:IX2.118 @%%@CR:IX2.119 @% When you write a procedure to be called by C, be careful to leave SI and%@EH@% DI in the same state they were in before C called your procedure. Microsoft QuickC allocates register variables in SI and DI.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.3 @%%@AB@%2.5.3 The Pointer Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.120 @%%@CR:IX2.121 @% The pointer registers──BP, SP, and IP──are all special-purpose registers%@EH@% that help implement procedure calls. The processor alters SP (Stack Pointer) and IP (Instruction Pointer) whenever you call a procedure, and you can use BP (Base Pointer) to access parameters placed on the stack.%@NL@% %@NL@% %@4@% Despite their names, pointer registers are not good places to store%@EH@% pointer variables or other general program data; you should generally use BX, SI, and DI for that purpose.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.3.1 @%%@AB@%2.5.3.1 The BP Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.122 @%%@CR:IX2.123 @% You can use BP (Base Pointer) to retrieve the contents pointed to by an%@EH@% address. However, by default, the BP register points into the stack segment rather than the data segment. Therefore, BP is typically used to access items on the stack.%@NL@% %@NL@% %@4@% The "stack" is the area of memory that holds parameters, local variables,%@EH@% and return addresses for each procedure being executed. Although you can store general data in BP, it is commonly used to access parameters of the current procedure.%@NL@% %@NL@% %@4@% When you use the %@AB@%PROC%@AE@% statement with a parameter list as explained in the%@EH@% next chapter, avoid altering the value of BP. The %@AB@%PROC%@AE@% directive generates instructions that set BP to point to the procedure's local stack area, and then use BP to access parameters and local data. If BP changes, all your references to parameters will be wrong.%@NL@% %@NL@% %@4@% To learn how to set BP yourself, see Section 15.3.3%@BO: c60b0@%, "Passing Arguments%@EH@% on the Stack," or Appendix A%@BO: ed697@%, "Mixed-Language Mechanics."%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.3.2 @%%@AB@%2.5.3.2 The SP Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.124 @%%@CR:IX2.125 @% The SP (Stack Pointer) register points to the current location within the%@EH@% stack segment. As you add or remove items from the stack, the processor changes the value of SP, so that SP always points to the top of the stack. %@NL@% %@4@% The processor stack works like a stack of dishes: you push items onto the%@EH@% top of the stack as you need to save them, then pop them off the stack when you're ready to use them again. The stack is a last-in-first-out mechanism. You can only remove the item currently at the top of the stack. Items must be removed in the reverse order they were placed there.%@NL@% %@NL@% %@4@% The processor automatically pushes and pops return addresses for you when%@EH@% you call or return from a procedure. A "return address" is the place a procedure or routine returns to when done. You can also place other values on the stack by using the %@AB@%PUSH%@AE@% and %@AB@%POP%@AE@% instructions.%@NL@% %@NL@% %@4@% The %@AB@%PUSH%@AE@% instruction saves the value of a register or memory location by%@EH@% placing it on the stack. %@AB@%POP%@AE@% removes the value from the stack and places it back in the original location. (You can also pop the contents into some other location if you wish.) Use these instructions when you need to preserve a value. In the following example, BX holds an important value, but the program needs temporary use of BX:%@NL@% %@NL@% %@AS@% push bx ; Save BX on the stack%@AE@%%@NL@% %@AS@% mov bx,pointer ; Load pointer into BX%@AE@%%@NL@% %@AS@% mov value,[bx] ; value = *pointer%@AE@%%@NL@% %@AS@% pop bx ; Pop old value back into BX%@AE@%%@NL@% %@NL@% %@4@% The stack also holds parameters and local variables during procedure%@EH@% calls. Sections 13.4.2%@BO: ac5c7@%, "Using the Stack," and 15.3.3%@BO: ac5c8@%, "Passing Arguments on the Stack," provide more information on using the stack. Appendix A%@BO: ed697@%, "Mixed-Language Mechanics," explains how to manipulate the stack to make room for local variables──one of the few times you should change the value of SP directly.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.3.3 @%%@AB@%2.5.3.3 The IP Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@% You cannot adjust the IP (Instruction Pointer) register directly; it can%@EH@% only be adjusted indirectly, through control-flow instructions. For this reason, Quick-Assembler does not even recognize IP as a keyword.%@NL@% %@NL@% %@4@%%@CR:IX2.126 @%%@CR:IX2.127 @%%@CR:IX2.128 @% The IP register contains the address of the next instruction to execute.%@EH@% The instructions that control program flow (calls, jumps, loops, and interrupts) automatically set the instruction pointer to the proper value. The processor pushes the address of the next instruction onto the stack when you call a procedure. The processor pops this instruction into IP when the procedure returns. Normally, the processor increments IP to point to the next instruction in memory.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.5.4 @%%@AB@%2.5.4 The Flags Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.129 @%%@CR:IX2.130 @%%@CR:IX2.131 @%%@CR:IX2.132 @% The flags register, shown in Figure 2.2, is a 16-bit register made up of%@EH@% bits that each indicate some specific condition. Most of the flags help determine the behavior of conditional jump instructions. Many instructions──most notably %@AB@%CMP%@AE@%──set these flags in a meaningful way. Other flags (Trap, Interrupt Enable, and Direction) do not affect conditional jump instructions but control the processor's general operation.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 2.5.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The nine flags common to all 8086-family processors are summarized below,%@EH@% progressing from the low-order to high-order flags. In these descriptions, the term "set" means the bit value is 1, and "cleared" means the bit value is 0.%@NL@% %@NL@% %@4@% Instructions actively set and clear various flags. For example, if the%@EH@% result of a %@AB@%SUB%@AE@% or %@AB@%CMP%@AE@% instruction is zero, it sets the Zero flag. This flag setting can, in turn, affect subsequent instructions──in particular, conditional jumps. Some instructions do not set the flags at all, or have random effects on some flags. Consult on-line Help for each instruction to see precisely how it affects flag settings.%@NL@% %@NL@% %@AB@%Flag%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX2.133 @% Carry Is set if an operation generates a carry to or a borrow from a destination operand. (Operation viewed as unsigned.)%@NL@% %@NL@% %@CR:IX2.134 @% Parity Is set if the low-order bits of the result of an operation contain an even number of set bits.%@NL@% %@NL@% %@CR:IX2.135 @% Auxiliary Carry Is set if an operation generates a carry to or a borrow from the low-order four bits of an operand. This flag is used for binary coded decimal arithmetic. %@NL@% %@CR:IX2.136 @% Zero Is set if the result of an operation is 0.%@NL@% %@NL@% %@CR:IX2.137 @% Sign Equal to the high-order bit of the result of an operation (0 is positive, 1 is negative).%@NL@% %@NL@% %@CR:IX2.138 @% Trap If set, the processor generates a single-step interrupt after each instruction. Debugging programs, including the QuickC/QuickAssembler debugging facility, use this feature to execute a program one instruction at a time.%@NL@% %@NL@% %@CR:IX2.139 @% Interrupt Enable If set, interrupts will be recognized and acted on as they are received. The bit can be cleared to temporarily turn off interrupt processing.%@NL@% %@NL@% %@CR:IX2.140 @% Direction Can be set to make string operations process down from high addresses to low addresses, or can be cleared to make string operations process up from low addresses to high addresses.%@NL@% %@NL@% %@CR:IX2.141 @% Overflow Is set if the result of an operation is too large or small to fit in the destination operand. (Operation viewed as signed.)%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX2.142 @%%@CR:IX2.143 @% The Carry and Overflow flags are similar, but have one major difference:%@EH@% the Carry flag is set according to the rules of unsigned operations, and the Overflow flag is set according to the rules of signed operations. A signed operation uses two's complement arithmetic to represent negative numbers. One of the features of this system is that a number is negative if the most significant bit is set. Unsigned operations do not view any number as negative.%@NL@% %@NL@% %@4@% Thus, the same %@AB@%ADD%@AE@% operation can be viewed as adding FFFFH to FFFEH%@EH@% (unsigned) or -1 to -2 (signed). This operation would set the Carry flag (because the maximum unsigned value is FFFFH), but not the Overflow flag.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% This manual does not describe the details of two's-complement arithmetic. For more information, see one of the references listed in the Introduction.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% Each of the conditional jump instructions responds to a particular flag or%@EH@% combination of flags. For example, the %@AB@%JZ %@AE@%(Jump If Zero) instruction jumps if the Zero flag is set. The %@AB@%JBE%@AE@% (Jump If Below or Equal) jumps if either the Zero flag or the Carry flag is set. For a description of all the conditional jump instructions, see Section 15.1.2%@BO: be4fa@%, "Jumping Conditionally."%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.6 @%%@AB@%2.6 Addressing Modes%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.144 @% You can specify several kinds of operands: immediate, register, direct%@EH@% memory, and indirect memory. Each type of operand corresponds to a different addressing mode. The "addressing mode" is the method that the processor uses to calculate the actual value of the operand at run time.%@NL@% %@NL@% %@4@% You don't specify addressing modes explicitly. You simply give an operand,%@EH@% and the assembler determines the corresponding addressing mode.%@NL@% %@NL@% %@4@%%@CR:IX2.145 @%%@CR:IX2.146 @% The four types of operands are summarized below, and described at length%@EH@% in the rest of this section.%@NL@% %@NL@% %@AB@%Operand Type%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Immediate A constant value contained in the instruction itself%@NL@% %@NL@% Register A 16-bit or 8-bit register%@NL@% %@NL@% Direct memory A fixed location in memory%@NL@% %@NL@% Indirect memory A memory location determined at run time by using the address stored in one or two registers%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX2.147 @%%@CR:IX2.148 @% Direct memory and indirect memory operands are closely related. Syntax%@EH@% displays in this manual, as well as in on-line Help, often refer to %@AI@%memory%@AE@% operands. You can use either type of memory operand wherever %@AI@%memory%@AE@% is specified. From the processor's viewpoint, the only difference between these types of operands is how the address is determined. The address specified in the memory operand is called the "effective address" of the instruction.%@NL@% %@NL@% %@4@% Most two-operand instructions require operands of the same size. When one%@EH@% of the operands is a register, QuickAssembler adjusts the size of the other, if possible, to be the size of the register──either 8 or 16 bits. An instruction that operates on AX and BL is illegal, since these registers are different sizes.%@NL@% %@NL@% %@4@% If the sizes conflict, you can sometimes use the %@AB@%PTR%@AE@% operator to override%@EH@% the size attribute of an operand.%@NL@% %@NL@% %@4@%%@CR:IX2.149 @%%@CR:IX2.150 @%%@CR:IX2.151 @%%@CR:IX2.152 @%%@CR:IX2.153 @%%@CR:IX2.154 @% Sections 2.6.1-2.6.4 discuss each of the four operand types (and%@EH@% corresponding addressing modes) in detail.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.6.1 @%%@AB@%2.6.1 Immediate Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.155 @%%@CR:IX2.156 @%%@CR:IX2.157 @% An "immediate operand" is a constant value on which the instruction%@EH@% operates directly. This is the only addressing mode that involves no further access of registers or memory. The data follows the instruction right inside the executable code, thus giving rise to the name "immediate."%@NL@% %@NL@% %@4@% Use immediate operands for the same reasons you would use a literal or%@EH@% symbolic constant in C. The value of an immediate operand never changes.%@NL@% %@NL@% %@4@% An immediate operand can be a symbolic constant declared with the %@AB@%EQU%@AE@%%@EH@% operand. This operand is often used for the same purpose as the C%@AB@% #define%@AE@% directive. For example, consider the constant declaration:%@NL@% %@NL@% %@AS@%magic EQU 7243%@AE@%%@NL@% %@NL@% %@4@% You could use this the same way as the C statement:%@EH@%%@NL@% %@NL@% %@AS@%#define magic 7243%@AE@%%@NL@% %@NL@% %@4@% Chapter 11%@BO: 8d6e4@%, "Using Equates, Macros, and Repeat Blocks," tells more about%@EH@% defining constants with the%@AB@% EQU%@AE@% or %@AB@%=%@AE@% operator.%@NL@% %@NL@% %@4@% An immediate operand can also be an expression made up of constants. For%@EH@% example, the following code directs QuickAssembler to calculate the difference between two ASCII values, then use this difference as the source (rightmost) operand:%@NL@% %@NL@% %@AS@% mov bigdiff,'a'-'A'%@AE@%%@NL@% %@NL@% %@4@% The assembler interprets the one-byte strings %@AS@%'a' %@AE@%and %@AS@%'A' %@AE@%as the ASCII%@EH@% values 97 and 65. The assembler calculates the difference──in this case, 32──and places the resulting value into the object code. At run time, this value is fixed. Each time the instruction is executed, the processor moves the value 32 into the memory location %@AS@%bigdiff%@AE@%. This instruction is precisely equivalent to, but more readable than, the following:%@NL@% %@NL@% %@AS@% mov bigdiff,32%@AE@%%@NL@% %@NL@% %@4@% One-byte and two-byte strings can be immediate operands. Larger strings%@EH@% cannot be processed by a single 8086 instruction. Chapter 3%@BO: 2aca0@%, "Writing Assembly Modules for C Programs," explains how to process longer strings, one character at a time.%@NL@% %@NL@% %@4@% The %@AB@%OFFSET%@AE@% and %@AB@%SEG%@AE@% operators turn variable names (which normally are%@EH@% memory operands) into immediate operands. These operators are similar to the address operator (%@AB@%&%@AE@%) in C. In Chapter 4%@BO: 34dba@%, "Writing Stand-Alone Assembly Programs," you'll see how to use the %@AB@%OFFSET%@AE@% operator to treat an address as immediate data.%@NL@% %@NL@% %@4@% When an instruction has two operands, you cannot place immediate data in%@EH@% the destination (leftmost) operand. (The %@AB@%OUT%@AE@% instruction is the one exception.)%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%var DW ?%@AE@%%@NL@% %@AS@%college DW 1636%@AE@%%@NL@% %@AS@%nine EQU 5+4 ; Declare nine as symbolic constant%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov var,nine ; Move immediate data to memory%@AE@%%@NL@% %@AS@% mov bx,'ab' ; Move ASCII values for 'a' and 'b'%@AE@%%@NL@% %@AS@% ; into BH and BL%@AE@%%@NL@% %@AS@% mov college,1701 ; Move immediate data to memory%@AE@%%@NL@% %@AS@% mov ax,1+2+3+4 ; Move immediate data to AX%@AE@%%@NL@% %@AS@% mov ax,OFFSET var ; Move address of var to AX%@AE@%%@NL@% %@AS@% int 21h ; Immediate data is single operand%@AE@%%@NL@% %@AS@% ; 21 hexadecimal (33 decimal)%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.6.2 @%%@AB@%2.6.2 Register Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.158 @%%@CR:IX2.159 @%%@CR:IX2.160 @% A register operand consists of one of the 20 register names. The processor%@EH@% operates directly on the data stored in the register. "Register-direct" mode refers to the direct use of the value of the register rather than a memory location. Registers can also be used indirectly, to point to memory locations as described in Section 2.6.4%@BO: 27375@%, "Indirect Memory Operands."%@NL@% %@NL@% %@4@% Most instructions can take one or more register operands. You generally%@EH@% can use any of the general-purpose registers with these instructions, although some instructions require specific registers. The use of segment registers (CS, DS, SS, and ES) is restricted. You can refer to segment registers only under special circumstances.%@NL@% %@NL@% %@4@%Table 2.1 shows all the valid register names for 8086 processors. You can%@EH@% use any of these names as a register-direct operand.%@NL@% %@NL@% %@AB@%Table 2.1 Register Operands%@AE@%%@NL@% %@NL@% %@AB@%Register Type%@AE@% %@AB@%Register%@AE@% %@AB@%Name%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 8-bit high registers AH BH CH DH%@NL@% %@NL@% 8-bit low registers AL BL CL DL%@NL@% %@NL@% 16-bit general AX BX CX DX purpose%@NL@% 16-bit pointer and SP BP SI DI index%@NL@% 16-bit segment CS DS SS ES%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% Section 2.5%@BO: 1e475@%, "8086-Family Registers," discusses registers in more detail.%@EH@% Limitations on register use for specific instructions are discussed in sections on the specific instructions throughout Part 3%@BO: a6c61@%, "Using Instructions."%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ds,ax ; Both operands are register direct%@AE@%%@NL@% %@AS@% mov stuff,dx ; Source operand is register direct%@AE@%%@NL@% %@AS@% mov ax,1 ; Destination is register direct%@AE@%%@NL@% %@AS@% mul bx ; Single operand, register direct%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.6.3 @%%@AB@%2.6.3 Direct Memory Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.161 @%%@CR:IX2.162 @%%@CR:IX2.163 @%%@CR:IX2.164 @% A direct memory operand specifies a fixed address in main memory%@EH@% containing the data to operate on. At the machine level, a direct memory operand is a numeric address. In your QuickAssembler source code, you usually represent a direct memory operand by entering a symbolic name previously declared with a data directive such as %@AB@%DB%@AE@% (Declare Bytes).%@NL@% %@NL@% %@4@%%@CR:IX2.165 @% A direct memory operand is similar to a simple variable in C or an array%@EH@% element with a constant index. Any object in memory can be a direct memory operand as long as the exact location is fixed in the executable code. The data at the location can change, but the location itself is the same each time the processor executes the instruction. This fact gives direct memory operands a static character. For more dynamic operations, use indirect memory operands.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,count ; Source operand is direct memory%@AE@%%@NL@% %@AS@% mov count,ax ; Destination operand is direct memory%@AE@%%@NL@% %@AS@% inc total ; Single operand is direct memory%@AE@%%@NL@% %@NL@% %@4@% Typically, a direct memory operand is a simple label. As with immediate%@EH@% operands, you can specify a direct memory operand by entering an expression. As long as the address can be determined at assembly time, the operand is direct memory.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Technically, a program address is not determined until link time (in the case of near addresses) or load time (in the case of segment addresses). These adjustments are necessary to support multiple modules and to enable the program to run anywhere in memory. However, you can ignore these details. If the assembler can determine the operand's address relative to the rest of the module, the operand is direct memory.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% The following example uses an expression that translates to a direct%@EH@% memory operand. This example could be used to load the value of DX into the third element of an array of bytes. QuickAssembler considers %@AS@%area[2]%@AE@% as equivalent to %@AS@%area+2%@AE@%.%@NL@% %@NL@% %@AS@% mov area[2],dx ; Move DX to memory location 2 bytes%@AE@%%@NL@% %@AS@% ; past the address of "area"%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.166 @%%@CR:IX2.167 @% In the statement above, the assembler calculates an address by adding %@AS@%2 %@AE@%to%@EH@% the address of %@AS@%area%@AE@%. The resulting address will be the same no matter what values are stored in registers. At run time, the address is fixed. Thus, the operand is direct memory.%@NL@% %@NL@% %@4@%%@CR:IX2.168 @% You can use a numeric constant as a direct memory operand. Normally,%@EH@% Quick-Assembler interprets a numeric constant as an immediate operand. To ensure interpretation as a memory operand, prefix the number with a segment register and colon (%@AB@%:%@AE@%). Brackets are optional. The following instructions each load AX with the contents of memory address 100 hexadecimal in the data segment:%@NL@% %@NL@% %@AS@% mov ax,ds:[100h]%@AE@%%@NL@% %@AS@% mov ax,ds:100h%@AE@%%@NL@% %@NL@% %@4@% Section 2.7%@BO: 28c1c@%, "Segmented Addressing and Segment Registers," provides more%@EH@% information on segment registers and the use of the colon (%@AB@%:%@AE@%). By default, the processor assumes that data references lie in the segment pointed to by DS.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC2.6.4 @%%@AB@%2.6.4 Indirect Memory Operands%@AE@%%@EH@%%@NL@% %@CR:IX2.169 @%%@CR:IX2.170 @%%@CR:IX2.171 @%%@CR:IX2.172 @%%@CR:IX2.173 @%%@CR:IX2.174 @%%@CR:IX2.175 @%%@NL@% %@4@%%@CR:IX2.176 @%%@CR:IX2.177 @%%@CR:IX2.178 @%%@CR:IX2.179 @%%@CR:IX2.180 @% With indirect memory operands, the processor calculates the address of the%@EH@% data at execution time, by referring to the contents of one or two registers. Since values in the registers can change at run time, indirect memory operands provide the most dynamic method for accessing data.%@NL@% %@NL@% %@4@% Indirect memory operands make possible run-time operations such as pointer%@EH@% indirection, dynamic indexing of array elements──including indexing of multi-dimensional arrays──and dynamic accessing of members of a structure. All these operations are similar to operations in high-level languages. The major difference is that assembly language requires you to use one of several specific registers: BX, BP, SI, and DI.%@NL@% %@NL@% %@4@% You indicate an indirect memory operand by using at least one pair of%@EH@% brackets. Use of the index operator (%@AB@%[ ]%@AE@%) is explained in more detail in Section 9.2.1.3%@BO: 7b606@%.%@NL@% %@NL@% %@4@%%@CR:IX2.181 @%%@CR:IX2.182 @%%@CR:IX2.183 @%%@CR:IX2.184 @% When you place a register name in brackets, the processor uses the data%@EH@% pointed to by the register. For example, the following instruction accesses the data at the address contained in BX, and then moves this data into AX:%@NL@% %@NL@% %@AS@% mov ax,[bx]%@AE@%%@NL@% %@NL@% %@4@% When you specify more than one register, the processor adds the contents%@EH@% together to determine the effective address (the address of the data to operate on). One register must be a base register (BX or BP), and the other must be an index register (SI or DI):%@NL@% %@NL@% %@AS@% mov ax,[bx+si]%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.185 @%%@CR:IX2.186 @% You can specify one or more displacements. A "displacement" is a constant%@EH@% value to add to the effective address. A simple use of a displacement is to add a base address to a register:%@NL@% %@NL@% %@AS@% mov ax,table[si]%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX2.187 @%%@CR:IX2.188 @% In the example above, the displacement %@AS@%table %@AE@%is the address of an array;%@EH@% SI holds an index to an array element. (Unlike C, an assembly-language index always indicates the distance in bytes between the beginning of the array and the element.) Each time the instruction executes, it may load a different element into AX. The value of SI determines which array element to load.%@NL@% %@NL@% %@4@% Each displacement can be an address or numeric constant. If there is more%@EH@% than one displacement, the assembler adds them all together at assembly time, and places the total displacement into the executable code. For example, in the statement%@NL@% %@NL@% %@AS@% mov ax,table[bx][di]+6%@AE@%%@NL@% %@NL@% %@4@% both %@AS@%table %@AE@%and %@AS@%6 %@AE@%are displacements. The assembler adds the value of %@AS@%table%@AE@%%@EH@% to %@AS@%6 %@AE@%to get the total displacement.%@NL@% %@NL@% %@4@%%@CR:IX2.189 @% Table 2.2 shows the modes in which registers can be used to specify%@EH@% indirect memory operands.%@NL@% %@NL@% %@AB@%Table 2.2 Indirect Addressing Modes%@AE@%%@NL@% %@NL@% %@AB@%Mode%@AE@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Register indirect %@AB@%[%@AE@%BX%@AB@%]%@AE@% %@AB@%[%@AE@%BP%@AB@%]%@AE@% %@AB@%[%@AE@%DI%@AB@%]%@AE@% %@AB@%[%@AE@%DI%@AB@%]%@AE@% Effective address is contents of register%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Based or indexed %@AI@%displacement%@AE@%%@AB@%[%@AE@%BX%@AB@%]%@AE@% Effective address is contents %@AI@%displacement%@AE@%%@AB@%[%@AE@%BP%@AB@%]%@AE@% of register plus %@AI@%displacement%@AE@% %@AI@%displacement%@AE@%%@AB@%[%@AE@%DI%@AB@%]%@AE@% %@AI@%displacement%@AE@%%@AB@%[%@AE@%SI%@AB@%]%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Based indexed %@AB@%[%@AE@%BX%@AB@%][%@AE@%DI%@AB@%]%@AE@% %@AB@%[%@AE@%BP%@AB@%][%@AE@%DI%@AB@%]%@AE@% Effective address is contents %@AB@%[%@AE@%BX%@AB@%][%@AE@%SI%@AB@%]%@AE@% %@AB@%[%@AE@%BP%@AB@%][%@AE@%SI%@AB@%]%@AE@% of base register plus contents of index register%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Based indexed with %@AI@%displacement%@AE@%%@AB@%[%@AE@%BX%@AB@%][%@AE@%DI%@AB@%]%@AE@% Effective address is the sum displacement %@AI@%displacement%@AE@%%@AB@%[%@AE@%BP%@AB@%][%@AE@%DI%@AB@%]%@AE@% of base register, index %@AI@%displacement%@AE@%%@AB@%[%@AE@%BX%@AB@%][%@AE@%SI%@AB@%]%@AE@% register, plus %@AI@%displacement%@AE@% %@AI@%displacement%@AE@%%@AB@%[%@AE@%BP%@AB@%][%@AE@%SI%@AB@%]%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX2.190 @%%@CR:IX2.191 @%%@CR:IX2.192 @%%@CR:IX2.193 @% You can enclose each register in its own pair of brackets, or you can%@EH@% place the registers in the same pair of brackets separated by a plus sign (%@AB@%+%@AE@%). The period (%@AB@%.%@AE@%) is normally used with structures, but it also indicates addition. The following statements are equivalent:%@NL@% %@NL@% %@AS@% mov ax,table[bx][di]%@AE@%%@NL@% %@AS@% mov ax,table[bx+di]%@AE@%%@NL@% %@AS@% mov ax,[table+bx+di]%@AE@%%@NL@% %@AS@% mov ax,[bx][di].table%@AE@%%@NL@% %@AS@% mov ax,[bx][di]+table%@AE@%%@NL@% %@AS@% mov ax,table[di][bx]%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC2.7 @%%@AB@%2.7 Segmented Addressing and Segment Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.194 @%%@CR:IX2.195 @% "Segmented addressing" is the internal mechanism that enables the%@EH@% processor to address up to one megabyte of main memory. This mechanism accesses each physical memory location by combining two 16-bit addresses. The two addresses can be represented in source code as follows:%@NL@% %@NL@% %@4@% %@AI@%segment%@AE@%:%@AI@%offset%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The first 16-bit address is the "segment address." The second 16-bit%@EH@% address is the "offset address." In effect, the segment address selects a 64K region of memory, and the offset address selects a byte within this region. Here's how it works:%@NL@% %@NL@% 1. The processor shifts the segment address left by four places, producing a 20-bit address ending in four zeros. This operation has the effect of multiplying the segment address by 16.%@NL@% %@NL@% 2. The processor adds this 20-bit address to the 16-bit offset address. The offset address is not shifted.%@NL@% %@NL@% 3. The processor uses the resulting 20-bit address, often called the "physical address," to access an actual location in the one-megabyte address space.%@NL@% %@NL@% %@4@%%@CR:IX2.196 @% Figure 2.3 illustrates this process. The 8086-family processors were%@EH@% developed to use this mechanism because 16 bits (the size of an 8086 register) can only address 64K at a time. However, the combined 20-bit address is sufficient to address a full megabyte. Note that DOS and ROM BIOS reserve part of this area, so that no more than 640K is available for program addresses.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 2.7 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX2.197 @% A "segment" consists of a series of addresses that share the same segment%@EH@% address, but different offsets. Segments can be no more than 64K in size. To create large programs, you need to divide your program into multiple segments. Even with smaller programs, it is convenient to have separate code, data, and stack segments. (With tiny-model programs, the linker combines these segments into a single physical segment.)%@NL@% %@NL@% %@4@% The following example helps illustrate segmented-address calculations%@EH@% further. The processor calculates the address %@AS@%53C2:107A %@AE@%by multiplying the segment portion of the address by 16 (10H), and then adding the offset portion, as shown below:%@NL@% %@NL@% %@AS@% 53C20h Segment times 10h%@AE@%%@NL@% %@NL@% %@AS@% + 107Ah Offset%@AE@%%@NL@% %@NL@% %@AS@% 54C9Ah Physical address%@AE@%%@NL@% %@NL@% %@4@% The use of segmented architecture doesn't mean that you have to specify%@EH@% two addresses every time you access memory. The 8086-family processors use four segment registers, which simplify programming in the following ways:%@NL@% %@NL@% ■ Normally, you don't specify a segment address when you access data. Every data reference is relative to one of the four segment registers──CS, DS, SS, or ES──so the segment address is implied.%@NL@% %@NL@% ■ Most of the time, you don't need to tell the processor which segment register to use. By default, the processor uses CS for code addresses, DS for data addresses, and SS for stack addresses, except where otherwise noted in this section.%@NL@% %@NL@% ■ You initialize segment registers at the beginning of your program. Once initialized, you can continue to use the segment addresses stored in those registers.%@NL@% %@NL@% %@4@% If the program uses medium, large, huge, or compact model, you may need to%@EH@% periodically reload one or more of the segment registers. These memory models let you use more than 64K of code or 64K of data.%@NL@% %@NL@% %@4@% However, if the program uses small or tiny model, you never reload a%@EH@% segment register except in the following situations: to access a special hardware-defined location in memory, such as the video-display area, or to access far memory allocated to the program by DOS function 48H.%@NL@% %@NL@% %@4@%%@CR:IX2.198 @% Although each memory operand has a default segment register (usually DS,%@EH@% unless the operand uses BP), you can specify another segment register by using the segment override operator (%@AB@%:%@AE@%). The following example loads the variable %@AS@%far_away %@AE@%residing in the segment pointed to by ES:%@NL@% %@NL@% %@AS@% mov ax,es:far_away%@AE@%%@NL@% %@NL@% %@4@% For more information on this operator, see Section 9.2.3%@BO: 7e029@%,%@EH@% "Segment-Override Operator."%@NL@% %@NL@% %@3@% %@AB@%The CS Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.199 @%%@CR:IX2.200 @%%@CR:IX2.201 @%%@CR:IX2.202 @%%@CR:IX2.203 @% The processor always uses the CS (Code Segment) register as the segment%@EH@% address of the next instruction to execute; IP (Instruction Pointer) holds the offset address. CS:IP represents the full address of the next instruction.%@NL@% %@NL@% %@4@% Near jumps and procedure calls alter the value of IP. Far jumps and%@EH@% procedure calls alter both CS and IP. You never alter CS directly because the far jump and call instructions do so automatically. Furthermore, DOS initializes CS for you at the beginning of the program.%@NL@% %@NL@% %@3@% %@AB@%The DS Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.204 @%%@CR:IX2.205 @%%@CR:IX2.206 @% By default, the processor uses the DS (Data Segment) register as the%@EH@% segment address for program data. String instructions and indirect memory operands present some exceptions to this rule. With indirect memory operands, the use of BP anywhere in the operand causes SS to be the default segment register. Otherwise, DS is the default.%@NL@% %@NL@% %@4@% All the Microsoft standard memory models place the most frequently used%@EH@% data in an area pointed to by DS. This area is commonly called the "default data area," and it can be no larger than 64K. These memory models use the ES register to access data outside the default data area. Your own programs can either use this technique, or else reload DS whenever you enter a new module. The standard method has the advantage of providing fast access to the most frequently used data.%@NL@% %@NL@% %@3@% %@AB@%The SS Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.207 @%%@CR:IX2.208 @%%@CR:IX2.209 @% When the processor accesses data on the stack, it uses the SS (Stack%@EH@% Segment) register as the segment register. (See the description of SP in Section 2.5.3%@BO: 21724@% for more information about the stack.) Thus, SS:SP always points to the current stack position. Indirect memory operands involving BP also use SS as the default segment register.%@NL@% %@NL@% %@4@% The Microsoft standard memory models set SS equal to DS. This setting%@EH@% makes some programming tasks easier. In particular, it lets you address stack or data addresses with either register. If you have to reload DS, you can always access items in the default data area by using an SS override.%@NL@% %@NL@% %@3@% %@AB@%The ES Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX2.210 @%%@CR:IX2.211 @%%@CR:IX2.212 @%%@CR:IX2.213 @% The ES (Extra Segment) register is convenient for accessing data outside%@EH@% of the default data area. As demonstrated in Section 3.4%@BO: 30c03@%, "Decimal Conversion with Far Data Pointers," you access far data by loading ES with the desired segment address, and then giving a segment override. Section 13.3.2%@BO: aa7dc@%, "Loading Far Pointers," provides further information.%@NL@% %@NL@% %@4@% ES also plays a role in string instructions. With these instructions, the%@EH@% DI (Destination Index) register is always relative to the segment address in ES.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH3 @%%@AB@%Chapter 3: Writing Assembly Modules for C Programs%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% As a C programmer, you can take advantage of the superior speed and%@EH@% compactness of assembly-language routines. You can write most of your program in C, then write time-critical routines in assembly language.%@NL@% %@NL@% %@4@%%@CR:IX3.1 @% This chapter presents QuickAssembler programming techniques for%@EH@% interfacing to C. You can use similar techniques to interface with other languages. By using C with assembly language, however, you gain the advantage of being able to develop the entire program from within the integrated environment.%@NL@% %@NL@% %@4@% If you've read Chapter 2%@BO: 151ff@%, read this chapter to see how to use assembly%@EH@% language in a complete example module. If you skipped over Chapter 2%@BO: 151ff@%, you may want to refer to it occasionally for basic concepts, such as instructions and registers.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC3.1 @%%@AB@%3.1 A Skeleton for Procedure Modules%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Let's start by looking at the skeleton of a module with one procedure. The%@EH@% "skeleton" consists of statements that give basic structure to the module. Within this structure, you can supply most any instructions you want. Later sections of this chapter flesh out the skeleton by supplying useful code.%@NL@% %@NL@% %@4@% The following skeleton assumes that the module is called by a small-model%@EH@% C program, and consists of one procedure which takes a single parameter, a pointer to a byte:%@NL@% %@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@AS@%dectoint PROC Array:PTR BYTE%@AE@%%@NL@% %@AS@%;%@AE@%%@NL@% %@AS@%; (supply executable code here)%@AE@%%@NL@% %@AS@%;%@AE@%%@NL@% %@AS@%dectoint ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% Some features of the skeleton change when you write different procedures.%@EH@% Other parts may remain the same. In particular, you'll need to add a %@AB@%PROC%@AE@% and %@AB@%ENDP%@AE@% statement each time you add another procedure to the module.%@NL@% %@NL@% %@4@% Before looking at a full program example, let's examine each part of the%@EH@% skeleton.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC3.1.1 @%%@AB@%3.1.1 The .MODEL Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX3.2 @%%@CR:IX3.3 @% The %@AB@%.MODEL%@AE@% directive gives general information about the module. It uses%@EH@% the following syntax:%@NL@% %@NL@% %@4@% %@AB@%.MODEL %@AE@%%@AI@%memorymodel%@AE@% [[%@AB@%,%@AE@%%@AI@%langtype%@AE@% [[%@AB@%,%@AE@%%@AI@%stacktype%@AE@%]]]]%@EH@%%@NL@% %@NL@% %@4@% The last two fields are optional. Commas are field separators and are only%@EH@% required if you use more than one field. Usually, you'll want to enter values in the first two fields.%@NL@% %@NL@% %@4@%%@CR:IX3.4 @%%@CR:IX3.5 @%%@CR:IX3.6 @% The %@AI@%memorymodel%@AE@% and %@AI@%langtype%@AE@% fields correspond to the memory model and%@EH@% language, respectively, of the calling module. If your C program declares your procedure to be of type %@AB@%pascal%@AE@% or %@AB@%fortran%@AE@%, use %@AB@%Pascal%@AE@%, %@AB@%BASIC%@AE@%, or %@AB@%FORTRAN%@AE@% in the %@AI@%langtype%@AE@% field. These keywords specify the use of the non-C calling and naming conventions. Otherwise, specify%@AB@% C%@AE@% as the %@AI@%langtype%@AE@%. Although the %@AI@%langtype%@AE@% field is optional, you should supply it since the %@AB@%PROC%@AE@% features described later in this chapter require it.%@NL@% %@NL@% %@4@%%@CR:IX3.7 @%%@CR:IX3.8 @%%@CR:IX3.9 @% Don't use the %@AI@%stacktype%@AE@% field unless the calling C program is compiled%@EH@% with SS not equal to DS, in which case you should type in %@AB@%farStack%@AE@%. (QuickC does not generate code that sets SS not equal to DS, but other versions of Microsoft C do support this option.) The default is %@AB@%nearStack%@AE@%, which assumes SS is equal to DS.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC3.1.2 @%%@AB@%3.1.2 The .CODE Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX3.10 @%%@CR:IX3.11 @% The %@AB@%.CODE%@AE@% directive marks the beginning of the code segment, which is the%@EH@% section of your program that contains the actual steps to execute:%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@4@% Statements that follow this directive are considered part of the code%@EH@% segment. The segment continues to the end of the module or the next segment directive. Typically, the code segment consists of instructions and procedure definitions. It can also contain macro calls.%@NL@% %@NL@% %@4@% Some procedures work with static data. In Chapter 4%@BO: 34dba@%, "Writing Stand-Alone%@EH@% Assembly Programs," you'll see how to declare a data segment in which you can place data declarations.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC3.1.3 @%%@AB@%3.1.3 The PROC Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX3.12 @%%@CR:IX3.13 @% Use the %@AB@%PROC%@AE@% directive to define a procedure. The name of the procedure%@EH@% appears in the first column:%@NL@% %@NL@% %@AS@%dectoint PROC Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@4@% Because the %@AB@%.MODEL%@AE@% statement specified C-language conventions, the%@EH@% assembler prefixes the name %@AS@%dectoint %@AE@%with an underscore (_), and places the name into object code as a public code label.%@NL@% %@NL@% %@4@%%@CR:IX3.14 @%%@CR:IX3.15 @% If your procedure alters registers that should be preserved, the optional%@EH@% %@AB@%USES%@AE@% keyword automatically generates code to push the value of these registers on the stack and pop them when the procedure returns. Procedures called by C should not corrupt the value of SI, DI, or the segment registers CS, DS, or SS. (The value of BP is automatically preserved.) The following example shows how to preserve SI and DI for a procedure that changes these registers:%@NL@% %@NL@% %@AS@%dectoint PROC USES si di, Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX3.16 @%%@CR:IX3.17 @% The last part of the statement declares one or more parameters. In this%@EH@% case, the procedure declares a single parameter, %@AS@%Array%@AE@%, as a pointer to a byte. The most common parameter types you can declare are listed below:%@NL@% %@NL@% %@AB@%Declaration%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%WORD%@AE@% Word (two bytes)%@NL@% %@NL@% %@AB@%DWORD%@AE@% Doubleword (four bytes)%@NL@% %@NL@% %@AB@%PTR BYTE%@AE@% Pointer to a byte; most commonly, a pointer to a character string%@NL@% %@NL@% %@AB@%PTR WORD%@AE@% Pointer to a word; typically, the address of an array of integers%@NL@% %@NL@% %@AB@%PTR DWORD%@AE@% Pointer to a doubleword%@NL@% %@NL@% %@NL@% %@4@% For example, the following procedure definition declares a procedure named%@EH@% %@AS@%MidStr%@AE@%, which takes as parameters two pointers to character strings and one integer:%@NL@% %@NL@% %@AS@%MidStr PROC Str1:PTR BYTE, Str2:PTR BYTE, Index:WORD%@AE@%%@NL@% %@NL@% %@4@% References to parameters are really references to locations on the stack.%@EH@% C modules pass parameters by pushing them on the stack just before calling the procedure. The BP register serves as a framepointer (a pointer to the procedure's stack area), and each parameter is an offset from BP. The exact offset of each parameter depends on the memory model and calling convention, both established by the %@AB@%.MODEL%@AE@% directive.%@NL@% %@NL@% %@4@% When you use QuickAssembler procedure definitions, the assembler automates%@EH@% the work of referring to parameters. Instead of setting up the framepointer or calculating parameter offsets, you simply refer to parameters by name. You can also use these names with debugging commands.%@NL@% %@NL@% %@4@% Appendix A%@BO: ed697@%, "Mixed-Language Mechanics," shows the actual code that%@EH@% establishes BP as the framepointer. It also shows how to calculate parameter offsets.%@NL@% %@NL@% %@4@% Section 6.4.3%@BO: 5ea93@%, "Procedure Labels," gives the complete syntax and rules%@EH@% for using the %@AB@%PROC%@AE@% statement.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC3.1.4 @%%@AB@%3.1.4 The ENDP and END Statements%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX3.18 @%%@CR:IX3.19 @%%@CR:IX3.20 @%%@CR:IX3.21 @% The module ends with two statements: %@AB@%ENDP%@AE@%, which declares the end of a%@EH@% procedure, and %@AB@%END%@AE@%, which declares the end of the module:%@NL@% %@NL@% %@AS@%dectoint ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% You can place any number of procedures in the same module. Each time you%@EH@% end a procedure, use %@AB@%ENDP%@AE@%. However, %@AB@%END%@AE@% should only occur once, at the end of the module.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC3.2 @%%@AB@%3.2 Instructions Used in This Chapter%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The instructions below were introduced in Chapter 2%@BO: 151ff@%, "Introducing 8086%@EH@% Assembly Language." They are summarized here briefly for review. The first group of instructions manipulates data:%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%MOV%@AE@% %@AI@%destination%@AE@%, %@AI@%source%@AE@% Copies value of %@AI@%source%@AE@% to %@AI@%destination%@AE@%%@NL@% %@NL@% %@AB@%ADD%@AE@% %@AI@%destination%@AE@%, %@AI@%source%@AE@% Adds %@AI@%source%@AE@% to %@AI@%destination%@AE@%, storing result in %@AI@%destination%@AE@%%@NL@% %@NL@% %@AB@%SUB%@AE@% %@AI@%destination%@AE@%, %@AI@%source%@AE@% Subtracts %@AI@%source%@AE@% from %@AI@%destination%@AE@%, storing result in %@AI@%destination%@AE@%%@NL@% %@NL@% %@AB@%INC%@AE@% %@AI@%destination%@AE@% Increment──adds 1 to %@AI@%destination%@AE@%%@NL@% %@NL@% %@AB@%DEC%@AE@% %@AI@%destination%@AE@% Decrement──subtracts 1 from %@AI@%destination%@AE@%%@NL@% %@NL@% %@AB@%MUL%@AE@% %@AI@%source%@AE@% Multiplies %@AI@%source%@AE@% by AX (if operand is 16 bits), storing high 16 bits in DX and low 16 bits in AX%@NL@% %@NL@% %@NL@% %@4@% The second group of instructions controls the flow of program execution:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%CMP%@AE@% %@AI@%destination%@AE@%, Compare──subtracts %@AI@%source%@AE@% from %@AI@%destination%@AE@%, ignoring %@AI@%source%@AE@% result but setting processor flags appropriately%@NL@% %@NL@% %@AB@%JE%@AE@% %@AI@%label%@AE@% Jumps to %@AI@%label%@AE@% if result of last operation was equal to zero%@NL@% %@NL@% %@AB@%JAE%@AE@% %@AI@%label%@AE@% Jumps to %@AI@%label%@AE@% if result of last operation was equal to or above zero (unsigned operations)%@NL@% %@NL@% %@AB@%JMP%@AE@% %@AI@%label%@AE@% Jumps unconditionally to %@AI@%label%@AE@%%@NL@% %@NL@% %@NL@% %@CR:IX3.22 @%%@NL@% %@2@%%@CR:SC3.3 @%%@AB@%3.3 Decimal Conversion Example%@AE@%%@EH@%%@NL@% %@NL@% %@4@% This section uses a decimal-conversion example to illustrate the use of%@EH@% some basic instructions and directives. It features an assembly module that takes a pointer to a null-terminated string of characters as input and returns an unsigned integer value. This chapter assumes that the value is unsigned.%@NL@% %@NL@% %@4@% You can compute the value of a decimal string by multiplying each digit by%@EH@% a power of 10:%@NL@% %@NL@% %@AS@%2035 = 2 x 10 cubed + 0 x 10 squared + 3 x 10 + 5%@AE@%%@NL@% %@NL@% %@4@% One way to calculate the value of the number is to calculate each power of%@EH@% 10 separately, then multiply each digit by the corresponding power. For example, you can calculate 10 cubed, and then multiply by 2.%@NL@% %@NL@% %@4@% A much more efficient algorithm %@AI@%combines%@AE@% the calculations for powers of%@EH@% 10. The algorithm adds each digit to a running total, then multiplies the total by 10 after every digit but the last. The following pseudo-code represents this algorithm, and assumes that the first character in the string is the most significant digit:%@NL@% %@NL@% %@AS@%initialize total to 0%@AE@%%@NL@% %@AS@%while there's another digit%@AE@%%@NL@% %@AS@% add value of digit to total%@AE@%%@NL@% %@AS@% advance to next digit%@AE@%%@NL@% %@AS@% if no more digits%@AE@%%@NL@% %@AS@% we're done%@AE@%%@NL@% %@AS@% else%@AE@%%@NL@% %@AS@% multiply total by 10%@AE@%%@NL@% %@NL@% %@4@% A simple C program that calls the procedure might look like this:%@EH@%%@NL@% %@NL@% %@AS@%extern unsigned int dectoint( char * );%@AE@%%@NL@% %@AS@%main()%@AE@%%@NL@% %@AS@%{%@AE@%%@NL@% %@AS@% char digits[81];%@AE@%%@NL@% %@NL@% %@AS@% gets( digits );%@AE@%%@NL@% %@AS@% printf( "Numeric value is: %d", dectoint( digits ) );%@AE@%%@NL@% %@AS@%}%@AE@%%@NL@% %@NL@% %@4@% The procedure itself could be written in C as:%@EH@%%@NL@% %@NL@% %@AS@%unsigned int dectoint( char *Array)%@AE@%%@NL@% %@AS@%{%@AE@%%@NL@% %@AS@% unsigned int total = 0; /* Initialize total */%@AE@%%@NL@% %@NL@% %@AS@% while( *Array != '\0' ) /* While there's another digit%@AE@%%@NL@% %@AS@% {%@AE@%%@NL@% %@AS@% total += *Array - '0'; /* Add value to total */%@AE@%%@NL@% %@AS@% Array++; /* Advance to next digit */%@AE@%%@NL@% %@AS@% if( *Array == '\0' ) /* If no more digits, */%@AE@%%@NL@% %@AS@% break; /* we're done */%@AE@%%@NL@% %@AS@% total *= 10; /* Else, multiply by 10 */%@AE@%%@NL@% %@AS@% }%@AE@%%@NL@% %@AS@% return( total );%@AE@%%@NL@% %@AS@%}%@AE@%%@NL@% %@NL@% %@4@% This chapter shows how to write the same procedure in assembly language.%@EH@% The assembly-language version will be faster because it can make strategic use of registers and choose optimal instructions. You can write a main module with C code, place the assembly routine in a separate module with a .ASM extension, then link them together by creating a program list.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% You can build mixed-language programs by placing both .C and .ASM files in a program list. Place the main module first. In the Assembler Flags dialog box, make sure that you select either Preserve Case or Preserve Extrn (the default). From the QCL command line, use the /Cl (preserve case) or /Cx (preserve case of external symbols) option. QC calls the linker with case sensitivity on, so C and assembler symbols must match exactly.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX3.23 @% Before writing the assembly procedure, we first need to develop a strategy%@EH@% for using registers.%@NL@% %@NL@% %@4@% The AX (Accumulator) register is ideal for keeping the running total. The%@EH@% algorithm changes this total through both addition and multiplication. The %@AB@%MUL%@AE@% instruction requires the use of AX. By keeping the total in AX at all times, the procedure avoids having to constantly reload this register.%@NL@% %@NL@% %@4@% The BX register should be used to access the individual digits. The%@EH@% procedure receives the address of the digit string, and then retrieves each ASCII byte through pointer indirection. BX is one of the few registers that supports this operation. SI and DI could also be used this way, but C-generated code requires that SI and DI be preserved. BX can be freely altered.%@NL@% %@NL@% %@4@% The procedure needs to allocate two more registers: one for holding the%@EH@% multiplication factor (10), and another for adjusting the binary value of the digit. The procedure uses CX and DX for these purposes. In this case, CX and DX are interchangeable. However, we use CX for multiplication now, because in the hex conversion example, CX will be needed for a special kind of multiplication──shifting bits. We use DX as an intermediate location to receive a byte and then add a word to AX.%@NL@% %@NL@% %@4@% The complete assembly-language module is shown below:%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@AS@%dectoint PROC Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@AS@% sub ax,ax ; ax = 0%@AE@%%@NL@% %@AS@% mov bx,Array ; bx = Array%@AE@%%@NL@% %@AS@% mov cx,10 ; factor = CX = 10%@AE@%%@NL@% %@AS@% sub dx,dx ; dx = 0%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@AS@%top:%@AE@%%@NL@% %@AS@% mov dl,BYTE PTR [bx] ; Get next digit%@AE@%%@NL@% %@AS@% sub dl,'0' ; Convert numeral%@AE@%%@NL@% %@AS@% add ax,dx ; Add to total%@AE@%%@NL@% %@AS@% inc bx ; Point to next byte%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@AS@% mul cx ; AX = AX * 10%@AE@%%@NL@% %@AS@% jmp SHORT top ; Goto top of loop%@AE@%%@NL@% %@AS@%done:%@AE@%%@NL@% %@AS@% ret ; Exit procedure%@AE@%%@NL@% %@AS@%dectoint ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% We'll examine each section of the module in turn. The first three%@EH@% statements are directives that form part of the module's skeleton. The %@AB@%PROC%@AE@% directive, when used with one or more parameters as it is here, generates code to set the framepointer (BP) properly so that you can access parameters.%@NL@% %@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@AS@%dectoint PROC Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@4@% The rest of the module consists of instructions──the actual core of the%@EH@% program. The first four instructions initialize the registers AX, BX, CX, and DX. Note that when initializing a register to 0, the procedure uses %@AB@%SUB%@AE@% in preference to %@AB@%MOV%@AE@%. Any value subtracted from itself leaves zero in the destination operand. Although the result is the same, the %@AB@%SUB%@AE@% instruction is smaller and faster because it involves no immediate data.%@NL@% %@NL@% %@AS@% sub ax,ax ; ax = 0%@AE@%%@NL@% %@AS@% mov bx,Array ; bx = Array%@AE@%%@NL@% %@AS@% mov cx,10 ; factor = CX = 10%@AE@%%@NL@% %@AS@% sub dx,dx ; dx = 0%@AE@%%@NL@% %@NL@% %@4@% The next two instructions handle a special case──that of a string%@EH@% containing no digits at all. Recall that the procedure is passed a null-terminated string. The operand %@AS@%BYTE PTR [bx] %@AE@%is a memory operand referring to the byte pointed to by BX. If the string is empty, %@AS@%Array%@AE@% points to a null byte. The two instructions test for a 0 (null) value and jump to the end of the procedure if 0 is detected:%@NL@% %@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@NL@% %@4@% In the %@AB@%CMP%@AE@% instruction above, the %@AS@%BYTE PTR %@AE@%operator is strictly required,%@EH@% because otherwise the assembler would have no way of knowing whether to compare 0 to the byte or a word pointed to by BX. However, when one of the operands is a register (as is the case with the %@AB@%MOV%@AE@% instruction below), the %@AS@%BYTE PTR%@AE@% operator is optional.%@NL@% %@NL@% %@4@% The next eight instructions consist of a loop executed once for every%@EH@% digit character in the string. The label %@AS@%top %@AE@%indicates the top of the loop, and the first three instructions add the value of the digit to AX:%@NL@% %@NL@% %@AS@%top:%@AE@%%@NL@% %@AS@% mov dl,BYTE PTR [bx] ; Get next digit%@AE@%%@NL@% %@AS@% sub dl,'0' ; Convert numeral%@AE@%%@NL@% %@AS@% add ax,dx ; Add to total%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX3.24 @% The first instruction above retrieves the digit. The next instruction%@EH@% converts the digit's ASCII value to the numeric value by subtracting the value of the character %@AS@%'0' %@AE@%(48 decimal). This statement works because the ASCII character set places all digit characters in sequence from 0 to 9. Finally, the procedure adds the resulting value to the running total stored in AX. Note that the operands in each case are the same size. The first two instructions above access DL, the low byte of DX.%@NL@% %@NL@% %@4@% The next three instructions advance to the next byte in the string, and%@EH@% test it for equality to zero. Getting the next byte is just a matter of adding the value 1 to BX (with the %@AB@%INC%@AE@% instruction), so that BX points to the next byte. The other two instructions are identical to previous instructions that tested for zero value.%@NL@% %@NL@% %@AS@% inc bx ; Point to next byte%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@NL@% %@4@% If the next byte is a null byte, the processor jumps to the end of the%@EH@% program. Otherwise, the processor continues executing the bottom of the loop, which multiplies the current total by 10 (stored in CX), and then jumps to the top:%@NL@% %@NL@% %@AS@% mul CX ; AX = AX * 10%@AE@%%@NL@% %@AS@% jmp SHORT top ; Goto top of loop%@AE@%%@NL@% %@NL@% %@4@% Notice the operator%@AB@% SHORT%@AE@% used with the%@AS@% jmp %@AE@%instruction. This optional%@EH@% operator makes the encoded instruction smaller and faster, but it can be used only if the destination of the jump is less than 128 bytes away. %@AB@%SHORT%@AE@% is explained in more detail in Section 9.2.4.2%@BO: 7f627@%.%@NL@% %@NL@% %@4@% The loop is now complete. The rest of the module exits and marks the end%@EH@% of the segment and the module. The %@AB@%RET%@AE@% statement causes the assembler to generate instructions to do the following: restore the stack, restore the framepointer (BP), and return properly for the memory model (small) and calling convention (C).%@NL@% %@NL@% %@AS@%done:%@AE@%%@NL@% %@AS@% ret ; Exit procedure%@AE@%%@NL@% %@AS@%dectoint ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX3.25 @% Microsoft high-level languages always look for function return values in%@EH@% AX, if two bytes long, or in DX and AX, if four bytes long. If the return value is longer than four bytes, DX:AX points to the value returned. If the return value is one byte, AL contains the value.%@NL@% %@NL@% %@4@% The C module that calls this procedure looks in AX for the return%@EH@% value──as does all high-level-language code that calls a function returning a two-byte value. In this case, AX already contains the results of the calculation. No further action is required.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC3.4 @%%@AB@%3.4 Decimal Conversion with Far Data Pointers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX3.26 @%%@CR:IX3.27 @% This section uses the same basic algorithm introduced in the last section,%@EH@% but presents coding techniques for different memory models.%@NL@% %@NL@% %@4@%%@CR:IX3.28 @%%@CR:IX3.29 @% The %@AB@%.MODEL%@AE@% directive resolves all differences in the size of code%@EH@% addresses. However, when you use memory models that use far data pointers (compact, large, and huge), you must make some additional adjustments.%@NL@% %@NL@% %@4@% The program below shows the module rewritten for large memory model. This%@EH@% example works for compact model if %@AS@%large %@AE@%in the first line is replaced with %@AS@%compact%@AE@%.%@NL@% %@NL@% %@AS@% .MODEL large,c%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@AS@%dectoint PROC USES ds, Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@AS@% sub ax,ax ; ax = 0%@AE@%%@NL@% %@AS@% lds bx,Array ; ds:bx = Array%@AE@%%@NL@% %@AS@% mov cx,10 ; factor = CX = 10%@AE@%%@NL@% %@AS@% sub dx,dx ; dx = 0%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@AS@%top:%@AE@%%@NL@% %@AS@% mov dl,BYTE PTR [bx] ; Get next digit%@AE@%%@NL@% %@AS@% sub dl,'0' ; Convert numeral%@AE@%%@NL@% %@AS@% add ax,dx ; Add to total%@AE@%%@NL@% %@AS@% inc bx ; Point to next byte%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@AS@% mul cx ; AX = AX * 10%@AE@%%@NL@% %@AS@% jmp SHORT top ; Goto top of loop%@AE@%%@NL@% %@AS@%done:%@AE@%%@NL@% %@AS@% ret ; Exit procedure%@AE@%%@NL@% %@AS@%dectoint ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% This procedure is the same as the one in the last section, except for two%@EH@% lines. The %@AB@%PROC%@AE@% directive now includes a %@AB@%USES%@AE@% clause, and the %@AB@%LDS%@AE@% instruction replaces the first %@AB@%MOV%@AE@% instruction.%@NL@% %@NL@% %@4@% The procedure loads the DS register with the segment address of %@AS@%Array%@AE@%,%@EH@% thus causing subsequent data references to be relative to the new segment address. However, procedures called from C must preserve DS. The %@AB@%PROC%@AE@% statement, therefore, includes %@AS@%USES ds%@AE@%, which generates code to place DS on the stack.%@NL@% %@NL@% %@4@%%@CR:IX3.30 @%%@CR:IX3.31 @% The %@AB@%LDS%@AE@% instruction (Load Data Segment) does the actual loading of the DS%@EH@% register. This instruction is similar to the %@AB@%MOV%@AE@% instruction:%@NL@% %@NL@% %@AS@% mov bx,Array ; bx = Array%@AE@%%@NL@% %@AS@% ; 2-byte data pointer%@AE@%%@NL@% %@AS@% lds bx,Array ; ds:bx = Array%@AE@%%@NL@% %@AS@% ; 4-byte data pointer%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%LDS%@AE@% instruction accomplishes two moves. First, it loads the offset%@EH@% portion of the pointer into the specified register (BX). Second, it loads the segment portion of the pointer into DS.%@NL@% %@NL@% %@CR:IX3.32 @%%@CR:IX3.33 @%%@CR:IX3.34 @% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% For the %@AB@%LDS%@AE@% and %@AB@%LES%@AE@% instructions to work properly, the segment portion must be stored in the upper word of the four-byte (far) pointer. C meets this requirement by always pushing the segment portion of the pointer on the stack first. (The stack grows downward.) In your own programs, you declare far pointers with the %@AB@%DD%@AE@% directive. You initialize them by loading a segment address into the upper word of the pointer variable and an offset address into the lower word.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC3.4.1 @%%@AB@%3.4.1 Writing a Model-Independent Procedure%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX3.35 @% In the case of this procedure, the use of the %@AB@%LDS%@AE@% instruction is most%@EH@% convenient. Once DS is loaded with the new segment address, all subsequent memory references are automatically correct. No further adjustments are needed.%@NL@% %@NL@% %@4@% The simplicity of this technique makes it easy to write a module that is%@EH@% completely independent of memory models. This module can then be linked with any C program. To adjust memory model, you simply change the %@AB@%.MODEL%@AE@% directive, and recompile. In fact, the memory model itself can even be specified with a compile flag so that source code never need change.%@NL@% %@NL@% %@4@% The model-independent version contains only a few lines different from the%@EH@% previous example:%@NL@% %@NL@% %@AS@%% .MODEL mem,c%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@AS@%dectoint PROC USES ds, Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@AS@% sub ax,ax ; ax = 0%@AE@%%@NL@% %@AS@% IF @DataSize%@AE@%%@NL@% %@AS@% lds bx,Array ; ds:bx = Array%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% mov bx,Array ; bx = Array%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX3.36 @% The %@AB@%.MODEL%@AE@% directive operates on an undefined variable, %@AS@%mem%@AE@%. You define%@EH@% this variable on the QCL command line or in the Assembler Flags dialog box. For example, to assemble with QCL in compact model, enter the following text in the defines text box:%@NL@% %@NL@% %@AS@%/Dmem=compact%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX3.37 @% The %@AB@%IF%@AE@%, %@AB@%ELSE%@AE@%, and %@AB@%ENDIF%@AE@% directives cause conditional assembly. The%@EH@% %@AB@%@DataSize%@AE@% predefined macro is equal to 1 (true) if the memory model uses far data pointers, and 0 (false) otherwise. The statement %@AS@%IF @DataSize%@AE@% begins a conditional-assembly block that assembles the %@AB@%LDS%@AE@% instruction if the memory model uses far data pointers; it assembles the %@AB@%MOV%@AE@% instruction otherwise.%@NL@% %@NL@% %@4@% For more information on conditional assembly, see Chapter 10%@BO: 867d7@%, "Assembling%@EH@% Conditionally."%@NL@% %@NL@% %@4@% The %@AB@%USES%@AE@% clause is retained for all memory models, since even with small%@EH@% model it does no harm. However, to increase efficiency, you may wish to include the %@AB@%PROC%@AE@% statement inside conditional-assembly blocks.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC3.4.2 @%%@AB@%3.4.2 Accessing Far Data through ES%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%LDS%@AE@% instruction is inconvenient if you need to access items in the%@EH@% default data segment, because you have no guarantee that DS still points to that area of memory. Therefore, it's sometimes more efficient to leave DS alone and use the ES register to access far data.%@NL@% %@NL@% %@4@%%@CR:IX3.38 @%%@CR:IX3.39 @% The standard C memory models all use the %@AB@%LES%@AE@% instruction to access far%@EH@% data. You can also use this method, but it is not required, since it has no effect on the interface between modules. Give the %@AB@%LES%@AE@% instruction to load a far data pointer, which will load the ES register with the new segment address. Then give the ES override whenever you refer to data in the far segment. This method requires alteration of all instructions that access the string data:%@NL@% %@NL@% %@AS@% les bx,Array ; es:bx = Array%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cmp es:BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@NL@% %@4@% Once ES is loaded with the segment address of far data, access objects in%@EH@% the default data area (the segment containing near data) as you normally would. Use the ES override to access the far data.%@NL@% %@NL@% %@CR:IX3.40 @%%@NL@% %@2@%%@CR:SC3.5 @%%@AB@%3.5 Hexadecimal Conversion Example%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following example builds on the decimal example in Section 3.3%@BO: 2da0c@%,%@EH@% adding the additional logic needed to convert hexadecimal rather than decimal strings.%@NL@% %@NL@% %@4@% Hexadecimal conversion can use an algorithm similar to the one used%@EH@% earlier for decimal conversion, with these adjustments made:%@NL@% %@NL@% ■ The procedure multiplies the running total by 16, not 10.%@NL@% %@NL@% ■ The procedure converts the letters A-F to numeric values, in addition to converting the numerals 0-9.%@NL@% %@NL@% %@4@%%@CR:IX3.41 @%%@CR:IX3.42 @%%@CR:IX3.43 @%%@CR:IX3.44 @% You could make the first adjustment by loading CX with 16 instead of 10. A%@EH@% much more efficient method is to use the %@AB@%SHL%@AE@% (Shift Left) instruction to shift an object's bits left by four places. This effectively multiplies the object by 16.%@NL@% %@NL@% %@4@% The second adjustment requires more complex logic. Hexadecimal digits can%@EH@% consist of either letters or numerals. The procedure must consider three different cases──one for each sequence of hexadecimal characters:%@NL@% %@NL@% %@AB@%Range of Characters%@AE@% %@AB@%Conversion Required%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 0-9 Convert to face value. Subtract ASCII value of %@AS@%'0'%@AE@%.%@NL@% %@NL@% A-F, and a-f Convert to values 10-15. Convert all letters to uppercase, then subtract ASCII value of %@AS@%'A'%@AE@% and add 10.%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX3.45 @%%@CR:IX3.46 @%%@CR:IX3.47 @%%@CR:IX3.48 @% We convert all letters to uppercase in an optimized fashion by taking%@EH@% advantage of the ASCII coding sequence. Uppercase letters are coded as 41H onward. Lowercase letters are coded as 61H onward. Consequently, each lowercase letter differs from the corresponding uppercase letter by exactly one bit. We use the %@AB@%AND%@AE@% instruction, with the immediate operand 0DFH, to mask out this bit. This operation has the effect of setting the third highest bit to 0.%@NL@% %@NL@% %@AS@% 0110 0001 61h = 'a' 0100 0001 41h = 'A'%@AE@%%@NL@% %@AS@%AND 1101 1111 DFh 1101 1111 DFh%@AE@%%@NL@% %@AS@% ====================== ======================%@AE@%%@NL@% %@AS@%result 0100 0001 41h = 'A' 0100 0001 41h = 'A'%@AE@%%@NL@% %@NL@% %@AS@% 0110 0010 62h = 'b' 0100 0010 42h = 'B'%@AE@%%@NL@% %@AS@%AND 1101 1111 DFh 1101 1111 DFh%@AE@%%@NL@% %@AS@% ====================== ======================%@AE@%%@NL@% %@AS@%result 0100 0010 42h = 'B' 0100 0010 42h = 'B'%@AE@%%@NL@% %@NL@% %@4@% The beauty of the operation is that it converts lowercase letters to%@EH@% uppercase, but leaves uppercase letters alone. If the third highest bit is already 0 (as is the case with uppercase letters), doing an %@AB@%AND%@AE@% operation with 0DFH has no effect. This operation removes the need to handle lowercase letters as a separate case.%@NL@% %@NL@% %@4@% The revised algorithm does the following:%@EH@%%@NL@% %@NL@% %@AS@%initialize total to zero%@AE@%%@NL@% %@AS@%while there's another digit%@AE@%%@NL@% %@AS@% move byte to temporary location%@AE@%%@NL@% %@AS@% if ascii value < 'A'%@AE@%%@NL@% %@AS@% Subtract '0'%@AE@%%@NL@% %@AS@% else%@AE@%%@NL@% %@AS@% Convert lowercase to uppercase%@AE@%%@NL@% %@AS@% Subtract 'A'-10%@AE@%%@NL@% %@AS@% add byte value to total%@AE@%%@NL@% %@AS@% advance to next digit%@AE@%%@NL@% %@AS@% if no more digits%@AE@%%@NL@% %@AS@% we're done%@AE@%%@NL@% %@AS@% else%@AE@%%@NL@% %@AS@% shift total left by four bits%@AE@%%@NL@% %@NL@% %@4@% The assembly-language code below implements this algorithm. The code tests%@EH@% for each range, performing a different conversion for each case. Note the use of %@AB@%JB%@AE@% (Jump If Below), which jumps to the specified label if the previous comparison or subtraction produced a negative value──that is, if the first operand is less than the second.%@NL@% %@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@AS@%hextoint PROC Array:PTR BYTE%@AE@%%@NL@% %@NL@% %@AS@% sub ax,ax ; ax = 0%@AE@%%@NL@% %@AS@% mov bx,Array ; bx = Array%@AE@%%@NL@% %@AS@% mov cl,4 ; Prepare to shift left by 4%@AE@%%@NL@% %@AS@% sub dx,dx ; dx = 0%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; if byte=0 we're done%@AE@%%@NL@% %@AS@%top:%@AE@%%@NL@% %@AS@% mov dl,BYTE PTR [bx] ; Move byte to DL%@AE@%%@NL@% %@AS@% cmp dl,'A' ; ASCII value >= 'A'?%@AE@%%@NL@% %@AS@% jae isletter ; If so, goto isletter%@AE@%%@NL@% %@AS@% sub dl,'0' ; Convert ascii to numeric%@AE@%%@NL@% %@AS@% jmp addbyte ; Go add value of byte%@AE@%%@NL@% %@AS@%isletter:%@AE@%%@NL@% %@AS@% and dl,0DFh ; Convert to uppercase%@AE@%%@NL@% %@AS@% sub dl,'A'-10 ; Convert ascii to numeric%@AE@%%@NL@% %@AS@%addbyte:%@AE@%%@NL@% %@AS@% add ax,dx ; Add value to total%@AE@%%@NL@% %@AS@% inc bx ; Point to next byte%@AE@%%@NL@% %@AS@% cmp BYTE PTR [bx],0 ; Compare byte to NULL%@AE@%%@NL@% %@AS@% je done ; If byte=0 we're done%@AE@%%@NL@% %@AS@% shl ax,cl ; AX = AX * 16%@AE@%%@NL@% %@AS@% jmp SHORT top ; Goto top of loop%@AE@%%@NL@% %@AS@%done:%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%hextoint ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX3.49 @%%@CR:IX3.50 @%%@CR:IX3.51 @%%@CR:IX3.52 @% The beginning of the procedure initializes the CL register to %@AS@%4%@AE@%. This step%@EH@% is necessary, because you can use the %@AB@%SHL%@AE@% instruction (Shift Left) in only two ways: you can shift by exactly one bit, or you can shift by the number of places indicated in CL. Clearly, using CL is more efficient than a sequence of four shift instructions.%@NL@% %@NL@% %@4@% The main loop reads a character, tests it, and makes one basic decision:%@EH@% is the character a letter or not? This test takes advantage of the ASCII coding sequence. If the value of the character is equal to or greater than %@AS@%'A'%@AE@%, it cannot be one of the digits 0-9. The procedure uses the %@AB@%JAE%@AE@% instruction (Jump If Above or Equal) to test for this condition.%@NL@% %@NL@% %@AS@%top:%@AE@%%@NL@% %@AS@% mov dl,BYTE PTR [bx] ; Move byte to DL%@AE@%%@NL@% %@AS@% cmp dl,'a' ; ASCII value >= 'A'?%@AE@%%@NL@% %@AS@% jae isletter ; If so, goto isletter%@AE@%%@NL@% %@NL@% %@4@% If the character is a letter, the procedure first converts the letter to%@EH@% uppercase──using an %@AB@%AND%@AE@% instruction that converts lowercase letters but leaves uppercase letters unchanged. The following instruction can then properly handle all letters the same way, regardless of their original case:%@NL@% %@NL@% %@AS@%isletter:%@AE@%%@NL@% %@AS@% and dl,0DFh ; Convert to uppercase%@AE@%%@NL@% %@AS@% sub dl,'A'-10 ; Convert ascii to numeric%@AE@%%@NL@% %@NL@% %@4@% For simplicity, the procedure accepts invalid letters. You could easily%@EH@% enhance it to verify that the letters are hexadecimal.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH4 @%%@AB@%Chapter 4: Writing Stand-Alone Assembly Programs%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX4.1 @% With QuickAssembler, you can write stand-alone assembly programs to%@EH@% produce small, efficient utilities. For example, you might write a utility in assembly language to count the number of lines or paragraphs in a file. These programs start and end with assembly code and generally do not involve any links to high-level languages.%@NL@% %@NL@% %@4@% Stand-alone assembly programs can yield remarkably small .EXE files. They%@EH@% require relatively little space, because they do not include the start-up code for a high-level language. And often you can make your assembly program even smaller by converting it to a .COM file as shown in this chapter. Some useful .COM files take up less than 100 bytes of memory.%@NL@% %@NL@% %@4@% This chapter first describes the directives you need to write stand-alone%@EH@% assembly programs, reviews instructions used in the chapter's examples, and then presents a simple stand-alone program. Next, Sections 4.4%@BO: 3817c@%-4.6 look closely at each segment of the program: stack, data, and code. Finally, the chapter describes how to create a program in the .COM format.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.1 @%%@AB@%4.1 A Skeleton for Stand-Alone Programs%@AE@%%@EH@%%@NL@% %@NL@% %@4@% This chapter uses the simplified segment directives described in the%@EH@% previous chapter, and introduces three more directives──%@AB@%.STACK%@AE@%, %@AB@%.DATA%@AE@%, and %@AB@%.STARTUP%@AE@%. The simplified segment directives produce programs using the Microsoft standard segment format.%@NL@% %@NL@% %@4@% This format is not required, since your stand-alone program need not be%@EH@% compatible with a high-level-language module. However, the standard format is convenient because you can specify a number of different memory models, and you are freed from having to specify segment names, attributes, and register assumptions.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Occasionally, you may need a customized segment structure. Linking assembly code to a non-Microsoft language is the most common situation that requires customized segments. QuickAssembler lets you use full segment definitions any time you need to customize segments. However, you should find that simplified segment directives support the vast majority of assembly-language programming you do──even when you write .COM files.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% The skeleton for the programs in this chapter includes a stack, data, and%@EH@% code segment. Note that one of the directives, %@AB@%.MODEL%@AE@%, will change when you alter the memory model. The other statements remain the same.%@NL@% %@NL@% %@AS@% .MODEL small ; Use small memory model%@AE@%%@NL@% %@NL@% %@AS@% .STACK 100h ; Declare 256-byte stack%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%;%@AE@%%@NL@% %@AS@%; (place data declarations here)%@AE@%%@NL@% %@AS@%;%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP ; Set up DS, SS, and SP registers%@AE@%%@NL@% %@AS@%;%@AE@%%@NL@% %@AS@%; (place executable code here)%@AE@%%@NL@% %@AS@%;%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% Sections 4.1.1%@BO: 35c29@%-4.1.3 examine each of the statements in this skeleton more%@EH@% closely.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC4.1.1 @%%@AB@%4.1.1 The .MODEL Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%.MODEL%@AE@% directive performs the same role that it did in the previous%@EH@% chapter; it defines the overall attributes of the module. Note, however, that with a stand-alone program, a language type is not always required. A language type is useful when a module contains one or more procedures. Otherwise, you need only type %@AB@%.MODEL%@AE@% followed by a memory model:%@NL@% %@NL@% %@AS@% .MODEL small ; Use small memory model%@AE@%%@NL@% %@NL@% %@4@% The memory model can be %@AB@%TINY%@AE@%, %@AB@%SMALL%@AE@%, %@AB@%MEDIUM%@AE@%, %@AB@%COMPACT%@AE@%, %@AB@%LARGE%@AE@%, or %@AB@%HUGE%@AE@%. Most%@EH@% of these memory models may be familiar to you if you have used QuickC. For a complete description of each memory model, see Section 5.1.1%@BO: 40c7f@%.%@NL@% %@NL@% %@4@% The %@AB@%TINY%@AE@% memory model is new; it alone results in the creation of a .COM%@EH@% file rather than a .EXE file. Section 4.8%@BO: 3c62a@%, "Creating .COM Files," gives a complete example featuring the use of tiny memory model.%@NL@% %@NL@% %@4@% Generally, to change memory model you change the %@AB@%.MODEL%@AE@% directive. You%@EH@% also change the way you load and use data pointers, as described in Chapter 3%@BO: 2aca0@%, "Writing Assembly Modules for C Programs." With these changes made, many programs can readily be reassembled for a new memory model. (However, as you'll see in Chapter 5%@BO: 3ffad@%, "Defining Segment Structure," you cannot use %@AB@%.FARDATA%@AE@% segments in tiny, small, or medium model, and this may require further revision of code in some cases.)%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC4.1.2 @%%@AB@%4.1.2 The .STACK, .CODE, and .DATA Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.2 @%%@CR:IX4.3 @% Each of the segment directives──%@AB@%.STACK%@AE@%, %@AB@%.CODE%@AE@%, and %@AB@%.DATA%@AE@%──declares the%@EH@% beginning of a segment.%@NL@% %@NL@% %@4@% The code and data segments begin with %@AB@%.CODE%@AE@% and %@AB@%.DATA%@AE@%, respectively. Each%@EH@% of these segments continues to the next segment directive or the end of the program. The data segment contains data and symbolic constant declarations. The code segment contains instructions.%@NL@% %@NL@% %@4@% However, the stack segment consists of only one line:%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.STACK%@AE@% [[%@AI@%size%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.4 @%%@CR:IX4.5 @%%@CR:IX4.6 @%%@CR:IX4.7 @% By default, QuickAssembler interprets %@AI@%size%@AE@% according to the current radix,%@EH@% which by default is decimal. You can specify a hexadecimal constant by using the H suffix. (Example: %@AS@%200h%@AE@%.) The %@AI@%size%@AE@% argument is optional. If you leave it out, the assembler creates a stack 1024 bytes long.%@NL@% %@NL@% %@4@% Unless the program is written in tiny memory model, you should always%@EH@% declare a stack segment in your main module. Section 4.4%@BO: 3817c@%, "Inside the Stack Segment," explains the purpose of this segment.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC4.1.3 @%%@AB@%4.1.3 The .STARTUP Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.8 @%%@CR:IX4.9 @% Unlike C programs, assembly-language programs have to initialize register%@EH@% values. Specifically, the program has to initialize DS, the Data Segment register; CS and IP, which point to the first instruction to execute; and SS and SP, the stack registers.%@NL@% %@NL@% %@4@% By far the easiest way to initialize all these registers is to just%@EH@% include %@AB@%.STARTUP%@AE@%, a simple directive that takes no arguments:%@NL@% %@NL@% %@AS@% .STARTUP ; Set up DS, SS, and SP registers%@AE@%%@NL@% %@NL@% %@4@% When you use this directive, the assembler generates code to initialize%@EH@% your registers the way Microsoft high-level languages do. The generated code is similar to some of the instructions in the C start-up code. The directive takes care of minimal start-up, but many programs will need to do additional start-up tasks, such as releasing unused memory.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The start-up sequence adjusts SS and SP so that SS is equal to DS. This starting condition gives you some advantages. If you later have to alter the value of DS, you can always access a data object as an indirect operand using BP, or through an SS segment override. To avoid this starting sequence, so that the stack and data are separate physical segments, use the %@AB@%farStack%@AE@% keyword with the %@AB@%.MODEL %@AE@%directive, as described in Section 5.1.3%@BO: 42b44@%.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.2 @%%@AB@%4.2 Instructions Used in This Chapter%@AE@%%@EH@%%@NL@% %@NL@% %@4@% This section summarizes the instructions used in this chapter. Because the%@EH@% program examples are simple, only a very few of the 80-odd instructions of the 8086 are featured here.%@NL@% %@NL@% %@4@% This chapter features four instructions:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%MOV%@AE@% %@AI@%destination%@AE@%, Moves %@AI@%source%@AE@% to %@AI@%destination%@AE@% %@AI@%source%@AE@%%@NL@% %@AB@%INT%@AE@% %@AI@%number%@AE@% Generates the indicated interrupt signal, causing processor to call a memory-resident interrupt routine%@NL@% %@NL@% %@AB@%DEC%@AE@% %@AI@%destination%@AE@% Decrement──subtracts 1 from %@AI@%destination%@AE@%%@NL@% %@NL@% %@AB@%JNZ%@AE@% %@AI@%label%@AE@% Jump If Not Zero──jumps to %@AI@%label%@AE@% if result of last operation was not zero%@NL@% %@NL@% %@NL@% %@4@% Most of the instructions above were introduced in Chapter 2%@BO: 151ff@%, "Introducing%@EH@% 8086 Assembly Language." The new instruction is %@AB@%INT%@AE@%.%@NL@% %@NL@% %@4@%%@CR:IX4.10 @%%@CR:IX4.11 @%%@CR:IX4.12 @% The %@AB@%INT%@AE@% instruction generates a software interrupt signal, causing the%@EH@% processor to call an interrupt service routine usually residing in a DOS or ROM-BIOS memory area. This call is much like a procedure call; the processor executes a specific function and returns to the program when the routine is complete.%@NL@% %@NL@% %@4@% There are two major differences between an interrupt call and a procedure%@EH@% call. First, instead of calling a procedure you have written, an %@AB@%INT%@AE@% instruction calls a DOS system routine or ROM-BIOS service. These low-level routines carry out a variety of basic operations, such as reading the keyboard, writing to the screen, or using the file system. Most DOS services are accessed through interrupt 21H (33 decimal).%@NL@% %@NL@% %@4@% The second major difference is syntactic. You follow the %@AB@%INT%@AE@% keyword by an%@EH@% interrupt number (in the range 0 to 255), rather than a procedure name. In many cases, you further specify the interrupt routine by loading AH with a function number.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.3 @%%@AB@%4.3 A Program That Says Hello%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following sample program prints %@AS@%Hello world %@AE@%and then successfully%@EH@% exits back to DOS. You can use this program as a template and insert your own code and data.%@NL@% %@NL@% %@AS@% .MODEL small ; Use small model%@AE@%%@NL@% %@NL@% %@AS@% .STACK 100h ; Allocate 256-byte stack%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%message DB "Hello, world.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP ; Use standard startup code%@AE@%%@NL@% %@NL@% %@AS@% mov bx,1 ; Load 1 - file handle%@AE@% %@AS@% ; for standard output%@AE@%%@NL@% %@AS@% mov cx,lmessage ; Load length of message%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Load address of message%@AE@%%@NL@% %@AS@% mov ah,40h ; Load no. of DOS Write function%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% mov ax,4c00h ; Load no. of DOS Exit function%@AE@%%@NL@% %@AS@% ; in AH, and 0 exit code in AL%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% The first statement determines the memory model of the program:%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small ; Use small model%@AE@%%@NL@% %@NL@% %@4@% This statement specifies small memory model, which places code and data in%@EH@% two separate segments, each of which cannot exceed 64K.%@NL@% %@NL@% %@4@% The next few sections consider the rest of this program──stack, data, and%@EH@% code.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.4 @%%@AB@%4.4 Inside the Stack Segment%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.13 @%%@CR:IX4.14 @% The stack segment is the easiest to create, because with simplified%@EH@% segment directives you enter only one statement:%@NL@% %@NL@% %@AS@% .STACK 100h ; Allocate 256-byte stack%@AE@%%@NL@% %@NL@% %@4@% Each processor or interrupt call uses up stack space. The stack stores%@EH@% return addresses, parameters, and local variables for each procedure called. When a procedure or interrupt routine returns, the stack space it used is restored. The more procedure calls your program makes without returning, the more stack area it requires. Programs that nest many procedures or use recursion (in which a procedure calls itself repeatedly) may require large stacks. Unfortunately, there is no formula for determining how large a stack is needed.%@NL@% %@NL@% %@4@% A 256-byte stack (100 hexadecimal) is adequate for most small programs.%@EH@% For this sample program, which makes one interrupt call but no procedure calls, 256 bytes provides an ample margin of error.%@NL@% %@NL@% %@4@% You can also create a stack by using full segment definitions. See Section%@EH@% 5.2%@BO: 4b9a0@%, "Full Segment Definitions," for more information.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.5 @%%@AB@%4.5 Inside the Data Segment%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.15 @% A single keyword declares the beginning of the segment:%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@NL@% %@4@% QuickAssembler considers all statements following this line to lie in the%@EH@% data segment, up until the next segment declaration or %@AB@%END%@AE@% directive. The %@AB@%END%@AE@% directive marks the end of the source file.%@NL@% %@NL@% %@4@%%@CR:IX4.16 @%%@CR:IX4.17 @% The next two statements are directives that declare a string of characters%@EH@% and a symbolic constant:%@NL@% %@NL@% %@AS@%message DB "Hello, world.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.18 @%%@CR:IX4.19 @% The first statement above declares a series of bytes. The label %@AS@%message %@AE@%is%@EH@% a symbolic name that QuickAssembler associates with the string's starting address.%@NL@% %@NL@% %@4@% The assembler allocates 15 bytes in the data segment, and initializes%@EH@% these bytes to the ASCII values for %@AS@%H%@AE@%, %@AS@%e%@AE@%, %@AS@%l%@AE@%, %@AS@%l%@AE@%, %@AS@%o%@AE@%, and so forth. The values %@AS@%13 %@AE@%and %@AS@%10 %@AE@%indicate a carriage return and line feed, respectively, causing the program to move the cursor to the beginning of the next line when it prints the string.%@NL@% %@NL@% %@4@% The second directive in the data segment declares a symbolic constant%@EH@% equal to the length of the string:%@NL@% %@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.20 @%%@CR:IX4.21 @% Again, the item in the first column, %@AS@%lmessage%@AE@%, is the label of the%@EH@% statement. The %@AB@%EQU%@AE@% directive equates the label with the value of the operand itself. %@AB@%EQU%@AE@% does not allocate memory.%@NL@% %@NL@% %@4@% The operand field contains %@AS@%$ - message%@AE@%, which in this case equals 15. We%@EH@% could just as easily have entered 15 in the operand field. However, the item %@AS@%$ - message%@AE@% is guaranteed to be equal to the length of the string, even if you later rewrite the initial string value.%@NL@% %@NL@% %@4@%%@CR:IX4.22 @%%@CR:IX4.23 @% The dollar sign (%@AS@%$%@AE@%) is the "location counter," which represents the%@EH@% current address of the statement. QuickAssembler translates the full expression as "Take the current address (%@AS@%$%@AE@%) and subtract the address of %@AS@%message%@AE@%." The current address is one byte after the end of the string. Thus, %@AS@%$ - message %@AE@%is automatically equal to the length of the string.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.6 @%%@AB@%4.6 Inside the Code Segment%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.24 @% A single keyword declares the beginning of the code segment:%@EH@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.25 @% The code segment consists of all statements between %@AB@%.CODE%@AE@% and the %@AB@%END%@AE@%%@EH@% statement, which marks the end of the source code. In this example, all the statements in the code segment, aside from %@AB@%.STARTUP%@AE@%, are instructions.%@NL@% %@NL@% %@4@% The program has three basic tasks. Each instruction helps carry out one of%@EH@% these operations:%@NL@% %@NL@% 1. Initialize registers%@NL@% %@NL@% 2. Call a DOS function to print the message%@NL@% %@NL@% 3. Call a DOS function to exit the program gracefully%@NL@% %@NL@% %@4@%%@CR:IX4.26 @%%@CR:IX4.27 @% The %@AB@%.STARTUP%@AE@% directive initializes registers. If you write a main module%@EH@% without this directive, you must explicitly initialize DS, CS, and IP. Furthermore, if you want SS to equal DS (which gives some programming advantages), you must adjust both SS and SP.%@NL@% %@NL@% %@4@% To see how to initialize registers without the use of %@AB@%.STARTUP%@AE@%, see%@EH@% Chapter 5%@BO: 3ffad@%, "Defining Segment Structure."%@NL@% %@NL@% %@4@% After registers are initialized, a series of five instructions makes the%@EH@% call to DOS that prints the message:%@NL@% %@NL@% %@AS@% mov bx,1 ; Load 1 - file handle for%@AE@%%@NL@% %@AS@% ; standard output%@AE@%%@NL@% %@AS@% mov cx,lmessage ; Load length of message%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Load address of message%@AE@%%@NL@% %@AS@% mov ah,40h ; Load no. of DOS Write function%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.28 @%%@CR:IX4.29 @% The first four instructions prepare for the DOS call. Interrupt calls%@EH@% generally use registers to receive parameters. Unlike procedure calls, they do not reference the stack for this information. The DOS Write function uses the following registers to receive data:%@NL@% %@NL@% %@AB@%Register%@AE@% %@AB@%Data%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% AH Selects the DOS function. 40H is the Write function.%@NL@% %@NL@% BX File handle to which to write. The number 1 is a reserved file handle that always corresponds to standard output. "Standard output" is normally synonymous with the computer screen, unless you redirect program output. If you were writing to a file, you would first open the file and use the file handle returned by the DOS open-file function.%@NL@% %@NL@% %@CR:IX4.30 @%%@CR:IX4.31 @% CX Length of the message. The second statement in the data segment determined this length.%@NL@% %@NL@% DS:DX The beginning address of the actual message text. Remember that DS was loaded earlier with the address of the data segment, so it does not need to be reloaded now.%@NL@% %@NL@% %@NL@% %@4@% This procedure uses the %@AB@%OFFSET%@AE@% operator to load DX with the address of the%@EH@% message. Although variables are translated to addresses, the processor normally interprets a variable address as a memory operand──that is, the processor operates on the data at the address, not the address itself.%@NL@% %@NL@% %@4@% The %@AB@%OFFSET%@AE@% operator extracts the offset portion of the address and turns%@EH@% it into an immediate operand. If the %@AB@%OFFSET%@AE@% operator was not used the DOS routine would not receive the address of %@AS@%message%@AE@%, but would instead receive the value of the first byte. The %@AB@%OFFSET%@AE@% operator is similar to the address operator (%@AB@%&%@AE@%) in C. Use it whenever you need to pass an address rather than a value.%@NL@% %@NL@% %@4@% After the interrupt service returns, the AX register contains the number%@EH@% of bytes written. The programs in this chapter do not use this return value, but a more sophisticated program might. In particular, if AX (number of bytes written) is less than CX (number of bytes requested to be written), then an error has occurred.%@NL@% %@NL@% %@4@% Each DOS function has its own conventions for receiving data in different%@EH@% registers. Consult the %@AI@%Microsoft%@AE@% %@AI@%MS-DOS%@AE@% %@AI@%Programmer's%@AE@% %@AI@%Reference%@AE@% for a complete description of each function. The Assembler Contents selection from the Help menu also describes the major DOS functions.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Each DOS function has conventions for getting and returning values in registers and flags. Bear in mind that values placed in any of these registers may change. If you need to preserve register values before making a DOS call, use the %@AB@%PUSH%@AE@% and %@AB@%POP%@AE@% instructions. See Section 13.4.1%@BO: ab441@%, "Pushing and Popping," for more information on how to preserve register values.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% The%@AB@% INT%@AE@% instruction makes the actual call to DOS. The interrupt number for%@EH@% the majority of DOS functions is 21H. You use different interrupt numbers to call ROM-BIOS services.%@NL@% %@NL@% %@4@% The final two instructions cause the program to terminate operation and%@EH@% return control to DOS. High-level language programmers can ignore the need to exit a program explicitly, if they like. But when you write a stand-alone assembly program, you don't have this luxury. The program must exit explicitly. Otherwise, the processor continues to execute random instructions after the end of the program, making the system appear to crash.%@NL@% %@NL@% %@4@%%@CR:IX4.32 @%%@CR:IX4.33 @%%@CR:IX4.34 @%%@CR:IX4.35 @%%@CR:IX4.36 @%%@CR:IX4.37 @% The DOS Exit function (service 4CH) is the preferred method for exiting%@EH@% back to DOS. This function uses two register values:%@NL@% %@NL@% %@CR:IX4.38 @%%@CR:IX4.39 @%%@CR:IX4.40 @% %@AB@%Register%@AE@% %@AB@%Data%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% AH Selects the DOS function. 4CH is the Exit function.%@NL@% %@NL@% AL Exit code. Batch files can use this exit code as an "errorlevel" indicator. An exit code of 0 usually indicates no error.%@NL@% %@NL@% %@NL@% %@4@% A single instruction loads both registers:%@EH@%%@NL@% %@NL@% %@AS@% mov ax,4c00h ; Load number of DOS Exit function%@AE@%%@NL@% %@AS@% ; in AH, and 0 exit code in AL%@AE@%%@NL@% %@NL@% %@4@% A single %@AB@%MOV%@AE@% instruction actually moves data into two registers──AH and%@EH@% AL. AH is loaded with 4CH, the function number for the DOS exit function, and AL is loaded with 0, an exit code indicating no error.%@NL@% %@NL@% %@4@% Finally, another %@AB@%INT%@AE@% instruction calls DOS.%@EH@%%@NL@% %@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.7 @%%@AB@%4.7 Making the Program Repeat Itself%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Once you understand the template for writing stand-alone programs, you can%@EH@% alter the sample program given above and generate your own code. This section alters the sample program so that it prints out a different message, and prints it ten times.%@NL@% %@NL@% %@4@% The new sample program is listed below:%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small ; Use small model%@AE@%%@NL@% %@NL@% %@AS@% .STACK 100h ; Allocate 256-byte stack%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%message DB "Hello, ten times.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@AS@%count DW 10%@AE@%%@NL@% %@AS@%%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP ; Use standard startup code%@AE@%%@NL@% %@NL@% %@AS@% mov bx,1 ; Load 1 - file handle for%@AE@%%@NL@% %@AS@% ; standard output%@AE@%%@NL@% %@AS@% mov cx,lmessage ; Load length of message%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Load address of message%@AE@%%@NL@% %@AS@%printit: mov ah,40h ; Load no. of DOS Write function%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% dec count ; count = count-1%@AE@%%@NL@% %@AS@% jnz printit ; if count > 0, print again%@AE@%%@NL@% %@NL@% %@AS@% mov ax,4c00h ; Load DOS 4C function number%@AE@%%@NL@% %@AS@% ; in AH, and 0 exit code in AL%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% Note the following changes:%@EH@%%@NL@% %@NL@% ■ The string data is different.%@NL@% %@NL@% ■ The data segment includes a new variable, %@AS@%count%@AE@%.%@NL@% %@NL@% ■ One of the instructions is now labeled %@AS@%printit%@AE@%.%@NL@% %@NL@% ■ Two additional instructions decrement %@AS@%count%@AE@%, then loop back to the label %@AS@%printit %@AE@%if %@AS@%count %@AE@%is greater than zero.%@NL@% %@NL@% %@4@% The string data is longer than before, and QuickAssembler must allocate%@EH@% more bytes than in the previous version of the program. However, the %@AB@%EQU%@AE@% statement that follows guarantees that the assembler still calculates string length correctly:%@NL@% %@NL@% %@AS@%message DB "Hello, ten times.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.41 @%%@CR:IX4.42 @% The new variable is actually a memory location of word size (two bytes).%@EH@% QuickAssembler allocates another two bytes in the data segment, and initializes these bytes:%@NL@% %@NL@% %@AS@%count DW 10%@AE@%%@NL@% %@NL@% %@4@% The label %@AS@%count %@AE@%becomes associated with the address of the data, and the%@EH@% number %@AS@%10 %@AE@%is the initial value placed at this memory location. However, the value can change.%@NL@% %@NL@% %@4@% The instruction %@AS@%mov ah,40h %@AE@%now has a label, because the program needs to%@EH@% return here to repeat the print operation. Not all instructions need a label──only those that the program may need to jump to directly.%@NL@% %@NL@% %@4@% The two new instructions cause the program to repeat the print operation%@EH@% ten times:%@NL@% %@NL@% %@AS@% dec count ; count = count-1%@AE@%%@NL@% %@AS@% jnz printit ; if count > 0, print again%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.43 @%%@CR:IX4.44 @% The %@AB@%DEC%@AE@% instruction subtracts 1 from the memory location %@AS@%count%@AE@%, and sets%@EH@% processor flags according to the result of the operation. %@AB@%JNZ%@AE@% then jumps to the specified label if the result was not zero. The combined effect of these two instructions is to repeat the previous instructions (from %@AS@%printit %@AE@%onward) ten times. To change the number of repetitions, initialize %@AS@%count %@AE@%with a different value.%@NL@% %@NL@% %@4@% Note that the DOS print function returns a value in the register%@EH@% AX──specifically, the number of bytes written. The program jumps back to %@AS@%printit %@AE@%so that AH is reloaded before the call to DOS.%@NL@% %@NL@% %@4@% You can optimize this program further by using a register instead of the%@EH@% memory location %@AS@%count%@AE@%. For example, to use the register SI as the counter, follow these steps:%@NL@% %@NL@% ■ Remove the declaration of %@AS@%count%@AE@%.%@NL@% %@NL@% ■ Initialize SI to %@AS@%10 %@AE@%at the beginning of the program with the instruction %@AS@%mov si,10%@AE@%.%@NL@% %@NL@% ■ Decrement SI instead of %@AS@%count %@AE@%near the bottom of the loop.%@NL@% %@NL@% %@4@% With this program, it's safe to use SI as the counter, since SI is not%@EH@% needed for any other purpose. However, some programs make special use of SI. In these cases, it may be more efficient to place the count in a variable.%@NL@% %@NL@% %@CR:IX4.45 @%%@NL@% %@2@%%@CR:SC4.8 @%%@AB@%4.8 Creating .COM Files%@AE@%%@EH@%%@NL@% %@NL@% %@4@% You can use QuickAssembler to produce .COM files as well as .EXE files.%@EH@% (However, these programs cannot contain any C modules.) Most of the memory models, ranging from small to large, produce a .EXE file. The tiny memory model is special because it alone supports creation of a .COM file.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% To produce a .COM file, you must not only use tiny memory model, but also select Generate COM File from the Linker Flags dialog box (choose Make from the Options menu), or else give the /TINY linker option on the QCL command line.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% Each .COM file has only one physical segment and is limited in size to a%@EH@% total of 64K. A .COM file has no executable-file header or relocation-table entries. Because DOS doesn't have to examine a file header or adjust relocatable segment addresses, it loads the .COM file slightly faster.%@NL@% %@NL@% %@4@% DOS initializes all segment registers (including DS) to point to the first%@EH@% available memory address. The Stack Pointer, SP, is set to 64K above the start of the program. Unlike .EXE files, .COM files have no definite stack area. Instead, the stack starts at offset address FFFE hexadecimal and continues to grow downward until it overlaps code and data areas. At that point, program failure is likely.%@NL@% %@NL@% %@4@% Simplified segment directives in QuickAssembler now provide direct support%@EH@% for .COM files. The template is, in fact, smaller than the template for a .EXE file.%@NL@% %@NL@% %@4@%%@CR:IX4.46 @% The code below shows the example in Section 4.3%@BO: 3793a@%, "A Program That Says%@EH@% Hello," revised to produce a .COM file:%@NL@% %@NL@% %@AS@% .MODEL tiny ; Produce a .COM file%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%message DB "Hello, world.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@AS@% mov bx,1 ; Load 1 - file handle for%@AE@%%@NL@% %@AS@% ; standard output%@AE@%%@NL@% %@AS@% mov cx,lmessage ; Load length of message%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Load address of message%@AE@%%@NL@% %@AS@% mov ah,40h ; Load no. of DOS Write function%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% mov ax,4c00h ; Load no. of DOS Exit function%@AE@%%@NL@% %@AS@% ; in AH, and 0 exit code in AL%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX4.47 @% A tiny-model program could be produced by simply taking the small-model%@EH@% version from earlier in the chapter, and changing the first line to the following:%@NL@% %@NL@% %@AS@% .MODEL tiny%@AE@%%@NL@% %@NL@% %@4@% The code would then run correctly. However, the sample code in this%@EH@% section takes advantage of tiny model by eliminating the stack segment. DOS initializes the SS (Stack Segment) register and SP (Stack Pointer) register for you, so you need not declare a stack. The assembler ignores stack segments in tiny model.%@NL@% %@NL@% %@4@% The program still includes the %@AB@%.STARTUP%@AE@% directive. With tiny model, all%@EH@% this directive does is generate the statement %@AS@%ORG 100h%@AE@%.%@NL@% %@NL@% %@CR:IX4.48 @%%@CR:IX4.49 @% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% The statement %@AS@%ORG 100h %@AE@%is necessary for programs in the .COM format, and must appear just before the first line of executable code. %@AS@%ORG%@AE@% %@AS@%100h %@AE@%starts the location counter at 100 hexadecimal, reflecting the way that DOS loads .COM files into memory. (DOS reserves the first 256 bytes for the Program Segment Prefix (PSP).) See Section 6.6%@BO: 67109@%, "Setting the Location Counter," for more information on the %@AB@%ORG%@AE@% directive.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX4.50 @% With tiny-model programs, QuickAssembler lets you define separate code and%@EH@% data segments, but combines these segments into a single physical segment, called a "group." QuickAssembler places the code segment first regardless of how you write your source code. The resulting .COM file assumes a single segment address for the whole program (as required by the structure of a .COM file), and execution automatically begins at the proper address. Finally, Quick-Assembler directs the linker to output a file in the .COM format rather than the .EXE format.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% "Groups" are a standard concept in 8086 assembly language. You can place a series of segments into a group. The total size must not exceed 64K. The linker responds by combining all the segments into a single physical segment in which all addresses share the same segment address. For a fuller explanation of groups and segments, see Chapter 5%@BO: 3ffad@%.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% When you write .COM files, you must observe some important restrictions.%@EH@% You cannot use program-defined segment addresses. Similarly, you have no access to defined segment addresses, such as %@AB@%@data%@AE@% and %@AB@%@code%@AE@%.%@NL@% %@NL@% %@4@%%@CR:IX4.51 @% Because .COM files lack relocation-table entries, DOS cannot adjust%@EH@% segment addresses at load time. The program must use absolute segment addresses or else assume the loading segment address that DOS assigns. The principal restriction is that you cannot refer to program-defined segment addresses. Therefore, memory references can be of three kinds:%@NL@% %@NL@% 1. Any memory location within the 64K program area. For these memory references, you do not load a new value into any of the segment registers.%@NL@% %@NL@% 2. Hard-coded locations in memory that have special meaning at the system or hardware level. A video-page address, such as %@AS@%B800:0000%@AE@%, is such a special segment address.%@NL@% %@NL@% 3. An address returned to you by a DOS or ROM-BIOS function. For example, DOS function 48H, Allocate Memory, returns a pointer to a block of dynamically allocated memory.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC4.9 @%%@AB@%4.9 Creating .COM Files with Full Segment Definitions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX4.52 @%%@CR:IX4.53 @% You don't generally need to use full segment definitions to create .COM%@EH@% files. However, when you do use these directives with programs written in .COM format, you need to follow certain rules. The assembler automatically follows most of these rules when you use simplified segment directives.%@NL@% %@NL@% %@4@% The guidelines for .COM format are listed below:%@EH@%%@NL@% %@NL@% ■ Place the entire program into one physical segment. It's possible to divide your program into separate logical segments, then group them into one physical segment with the %@AB@%GROUP%@AE@% directive. Simplified segment directives, in fact, use this technique with tiny model.%@NL@% %@NL@% However, you must ensure that code, not data, appears at the beginning of the .COM file. A number of different factors affect segment ordering, so it may be hard to ensure that the code segment appears first. Thus, creating just one segment is the more reliable method.%@NL@% %@NL@% In contrast, when you use simplified segment directives with tiny model, the assembler %@AI@%always%@AE@% places the code segment at the beginning of the .COM file.%@NL@% %@NL@% ■ Use the %@AB@%ASSUME%@AE@% directive to inform the assembler that all segment registers will point to the beginning of the segment. At load time, DOS sets all segment registers to this address. The %@AB@%ASSUME%@AE@% directive informs the assembler of this fact so that it can correctly calculate offset addresses. This directive is not necessary when you use simplified segment directives.%@NL@% %@NL@% ■ Use the %@AB@%ORG%@AE@% directive to set the location counter. At load time, DOS sets the starting address to 100H. The first 100H bytes are reserved for the Program Segment Prefix (PSP). The statement %@AS@%ORG 100h %@AE@%is necessary for the assembler to assign addresses in a way consistent with run-time conditions. Otherwise, jump instructions and data references will be wrong.%@NL@% %@NL@% When you use simplified segments directives with tiny model, the assembler automatically sets the location counter to 100H.%@NL@% %@NL@% ■ Use the %@AB@%END%@AE@% statement to take one argument: a starting address. This argument is not necessary if you use the %@AB@%.STARTUP%@AE@% simplified segment directive, because the program automatically begins execution wherever you place %@AB@%.STARTUP%@AE@%.%@NL@% %@NL@% %@4@% The modified procedure is shown below:%@EH@%%@NL@% %@NL@% %@AS@%_TEXT SEGMENT 'CODE' ; Define code segment%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT,ds:_TEXT,ss:_TEXT%@AE@%%@NL@% %@AS@% ORG 100h%@AE@%%@NL@% %@AS@%start: jmp begin%@AE@%%@NL@% %@NL@% %@AS@%message DB "Hello, world.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@AS@%begin: mov bx,1 ; Load 1 - file handle%@AE@%%@NL@% %@AS@% ; for standard output%@AE@%%@NL@% %@AS@% mov cx,lmessage ; Load length of message%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Load address of message%@AE@%%@NL@% %@AS@% mov ah,40h ; Load no. of DOS Write function%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@% mov ax,4c00h ; Load no. of DOS Exit function%@AE@%%@NL@% %@AS@% ; in AH, and 0 exit code in AL%@AE@%%@NL@% %@AS@% int 21h ; Call interrupt 21H (DOS)%@AE@%%@NL@% %@NL@% %@AS@%_TEXT ENDS%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@4@% The first three statements are new. The %@AB@%SEGMENT%@AE@% statement defines the%@EH@% beginning of a segment named %@AS@%_TEXT%@AE@%. (Instead of using the name %@AS@%_TEXT%@AE@%, you can choose any other valid symbolic name.) The %@AB@%ASSUME%@AE@% statement then informs the assembler that the CS, DS, and SS segment registers will all point to the beginning of this segment at run time. Finally, the %@AB@%ORG%@AE@% statement informs the assembler that the instruction pointer will be set to 100H.%@NL@% %@NL@% %@AS@%_TEXT SEGMENT ; Define code segment%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT,ds:_TEXT,ss:_TEXT%@AE@%%@NL@% %@AS@% ORG 100h%@AE@%%@NL@% %@NL@% %@4@% The body of the procedure now includes code and data together in the same%@EH@% segment. The first item in the segment %@AI@%must%@AE@% be an instruction, because .COM files always begin execution at the start of the file. Attempting to execute data would almost certainly cause program failure. Since there is no separate data segment, the first instruction jumps around the data declarations.%@NL@% %@NL@% %@AS@%start: jmp begin%@AE@%%@NL@% %@NL@% %@AS@%message DB "Hello, world.",13,10 ; Message to print%@AE@%%@NL@% %@AS@%lmessage EQU $ - message ; Determine length of message%@AE@%%@NL@% %@AS@%begin: mov bx,1 ; Load 1 - file handle for%@AE@%%@NL@% %@AS@% ; standard output%@AE@%%@NL@% %@NL@% %@4@% Another way to write a program for .COM format is to place data%@EH@% declarations after the end of the instructions. However, the assembler often produces better results if you place data declarations early in the source file. That way, you avoid forward references to data.%@NL@% %@NL@% %@4@% The source file ends by giving an argument to the %@AB@%END%@AE@% statement. This%@EH@% statement is necessary because the program does not use the %@AB@%.STARTUP%@AE@% directive. The argument to %@AB@%END%@AE@% must be the label of the first instruction executed:%@NL@% %@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:PT2 @%%@AB@%PART 2: Using Directives%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% Part 2%@BO: 3faf8@% of the Programmer's Guide (comprising Chapters 5%@BO: 3ffad@%-12) describes%@EH@% the directives and operators recognized by the Microsoft QuickAssembler. Directives are nonexecutable statements that give general information to the assembler. Some of the more important directives declare program structure, define data, and create macros. Operators indicate calculations to be performed at assembly time.%@NL@% %@NL@% %@4@% Chapters 5%@BO: 3ffad@%-8 present the basic directives you need to write a program,%@EH@% including segment, data, multimodule, and structure directives. Chapter 9%@BO: 78a1d@% deals specifically with operators. Chapter 10%@BO: 867d7@% describes conditional assembly, and Chapter 11%@BO: 8d6e4@% presents macros, a technique for replacing a series of frequently used instructions with a single statement. The directives that control your output are covered in Chapter 12%@BO: a2236@%.%@NL@% %@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH5 @%%@AB@%Chapter 5: Defining Segment Structure%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX5.1 @% A segment is an area in memory up to 64K in size, in which all locations%@EH@% share the same segment address. The 8086 assembly-language modules use segments for two reasons:%@NL@% %@NL@% ■ Segments provide a convenient means for dividing a program into its major divisions──code, data, constant data, and stack.%@NL@% %@NL@% ■ The architecture of the 8086 requires some use of segments. Every reference to memory must be relative to one of the four segment registers, as described in Section 2.7%@BO: 28c1c@%, "Segmented Addressing and Segment Registers." Segment definitions make it possible for QuickAssembler to assume the use of the same segment register for a large number of different addresses.%@NL@% %@NL@% %@4@% You can define segments by using simplified segment directives or full%@EH@% segment definitions.%@NL@% %@NL@% %@4@%%@CR:IX5.2 @% In most cases, simplified segment directives are a better choice. They are%@EH@% easier to use and more consistent, yet you seldom sacrifice any functionality by using them. Simplified segment directives automatically define the segment structure required when combining assembler modules with modules prepared with Microsoft high-level languages.%@NL@% %@NL@% %@4@%%@CR:IX5.3 @% Although more difficult to use, full segment definitions give more%@EH@% complete control over segments. A few complex programs may require full segment definitions in order to get unusual segment orders and types.%@NL@% %@NL@% %@4@% This chapter describes both methods. If you choose to use simplified%@EH@% segment directives, you will probably not need to read about full segment definitions.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC5.1 @%%@AB@%5.1 Simplified Segment Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Simplified segment directives provide an easy way to write%@EH@% assembly-language programs. They handle some of the difficult aspects of segment definition automatically, and assume the same conventions adopted by Microsoft high-level languages.%@NL@% %@NL@% %@4@% When you write stand-alone assembler programs, the simplified segment%@EH@% directives make programming easier. The Microsoft conventions are flexible enough to work for most kinds of programs.%@NL@% %@NL@% %@4@% When you write assembler routines to be linked with Microsoft high-level%@EH@% languages, the simplified segment directives ensure against mistakes that would make your modules incompatible. The names are automatically defined consistently and correctly.%@NL@% %@NL@% %@4@%%@CR:IX5.4 @%%@CR:IX5.5 @% The simplified segment directives automatically generate the same %@AB@%ASSUME%@AE@%%@EH@% and %@AB@%GROUP%@AE@% statements used by Microsoft high-level languages. You can learn more about the %@AB@%ASSUME%@AE@% and %@AB@%GROUP%@AE@% directives in Sections 5.3%@BO: 512db@% and 5.4%@BO: 52503@%.%@NL@% %@NL@% %@4@% However, for most programs you do not need to understand these directives.%@EH@% Simply use the simplified segment directives in the format shown in the examples.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.1 @%%@AB@%5.1.1 Understanding Memory Models%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.6 @% To use simplified segment directives, you must declare a memory model for%@EH@% your program. The memory model specifies the default size of data and code used in a program.%@NL@% %@NL@% %@4@%%@CR:IX5.7 @% Microsoft high-level languages require that each program have a default%@EH@% size (or memory model). Any assembly-language routine called from a high-level language program should have the same memory model as the calling program. The C compiler provided with QuickAssembler supports all models except tiny. If you use assembly modules with a different compiler, the compiler documentation should tell what memory models are supported.%@NL@% %@NL@% %@4@%%@CR:IX5.8 @% The most commonly used memory models are described below:%@EH@%%@NL@% %@NL@% %@AB@%Model%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX5.9 @%%@CR:IX5.10 @% Tiny All data and code fit in a single physical segment (group). Tiny-model programs can be converted to .COM-file format with the Generate COM File option in the Linker Flags dialog box (or the linker /TINY option used with QCL). Tiny-model programs have restrictions described in Chapter 4%@BO: 34dba@%, "Writing Stand-Alone Assembly Programs."%@NL@% %@NL@% %@CR:IX5.11 @%%@CR:IX5.12 @%%@CR:IX5.13 @% Small All data fits within a single 64K segment, and all code fits within a 64K segment. Therefore, all code and data can be accessed as near. This is the most common model for stand-alone assembler programs. C is the only Microsoft language that supports this model.%@NL@% %@NL@% %@CR:IX5.14 @% Medium All data fits within a single 64K segment, but code may be greater than 64K. Therefore, data is near, but code is far. Most recent versions of Microsoft high-level languages support this model.%@NL@% %@NL@% %@CR:IX5.15 @%%@CR:IX5.16 @% Compact All code fits within a single 64K segment, but the total amount of data may be greater than 64K (although no array can be larger than 64K). Therefore, code is near, but data is far. C is the only Microsoft high-level language that supports this model.%@NL@% %@NL@% %@CR:IX5.17 @% Large Both code and data may be greater than 64K (although no array can be larger than 64K). Therefore, both code and data are far. All Microsoft high-level languages support this model.%@NL@% %@NL@% %@CR:IX5.18 @% Huge Both code and data may be greater than 64K. In addition, any individual data array can be larger than 64K. From the standpoint of QuickAssembler, this memory model is almost equivalent to large model (the only exception is the meaning of the predefined equate %@AB@%@DataSize%@AE@%). If you want to support arrays larger than 64K, you must provide the program logic to support these arrays.%@NL@% %@NL@% %@NL@% %@4@% Stand-alone assembler programs can have any model. Tiny and small model%@EH@% are adequate for most programs written entirely in assembly language. Since near data or code can be accessed more quickly, the smallest memory model that can accommodate your code and data is usually the most efficient.%@NL@% %@NL@% %@4@% Mixed-model programs use the default size for most code and data but%@EH@% override the default for particular data items. Stand-alone assembler programs can be written as mixed-model programs by making specific procedures or variables near or far. Some Microsoft high-level languages have %@AB@%NEAR%@AE@%, %@AB@%FAR%@AE@%, and %@AB@%HUGE%@AE@% keywords that enable you to override the default size of individual data or code items.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.2 @%%@AB@%5.1.2 Specifying DOS Segment Order%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.19 @%%@CR:IX5.20 @%%@CR:IX5.21 @%%@CR:IX5.22 @%%@CR:IX5.23 @% The %@AB@%DOSSEG%@AE@% directive specifies that segments be ordered according to the%@EH@% DOS segment-order convention. This is the convention used by Microsoft high-level-language compilers.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%DOSSEG%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Using the %@AB@%DOSSEG%@AE@% directive enables you to maintain a consistent, logical%@EH@% segment order without actually defining segments in that order in your source file. Without this directive, the final segment order of the executable file depends on a variety of factors, such as segment order, class name, and order of linking. These factors are described in Section 5.2%@BO: 4b9a0@%, "Full Segment Definitions."%@NL@% %@NL@% %@4@% Since segment order is not crucial to the proper functioning of most%@EH@% stand-alone assembler programs, you can simply use the %@AB@%DOSSEG%@AE@% directive and ignore the whole issue of segment order.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Using the %@AB@%DOSSEG %@AE@%directive (or the /DOSSEG linker option) has two side effects. The linker generates symbols called _end and _edata. You should not use these names in programs that contain the %@AB@%DOSSEG %@AE@%directive. Also, the linker increases the offset of the first byte of the code segment by 16 bytes in small and compact models. This is to give proper alignment to executable files created with Microsoft compilers.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% If you want to use the DOS segment-order convention in stand-alone%@EH@% assembler programs, you should use the %@AB@%DOSSEG%@AE@% argument in the main module. Modules called from the main module need not use the %@AB@%DOSSEG%@AE@% directive.%@NL@% %@NL@% %@4@% You do not need to use the %@AB@%DOSSEG%@AE@% directive for modules called from%@EH@% Microsoft high-level languages, since the compiler already defines DOS segment order.%@NL@% %@NL@% %@4@% Under the DOS segment-order convention, segments have the following order:%@EH@%%@NL@% %@NL@% %@CR:IX5.24 @% 1. All segment names having the class name 'CODE'%@NL@% %@NL@% %@CR:IX5.25 @% 2. Any segments that do not have class name 'CODE' and are not part of the group DGROUP%@NL@% %@NL@% 3. Segments that are part of DGROUP, in the following order:%@NL@% %@NL@% a. Any segments of class BEGDATA (this class name is reserved for Microsoft use)%@NL@% %@NL@% b. Any segments not of class BEGDATA, BSS, or STACK%@NL@% %@NL@% c. Segments of class BSS%@NL@% %@NL@% d. Segments of class STACK%@NL@% %@NL@% %@4@%%@CR:IX5.26 @% Using the %@AB@%DOSSEG%@AE@% directive has the same effect as using the /DOSSEG linker%@EH@% option.%@NL@% %@NL@% %@4@%%@CR:IX5.27 @%%@CR:IX5.28 @% The directive works by writing to the comment record of the object file.%@EH@% The Intel(R) title for this record is %@AB@%COMENT%@AE@%. If the linker detects a certain sequence of bytes in this record, it automatically puts segments in the DOS order.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.3 @%%@AB@%5.1.3 Defining Basic Attributes of the Module%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.29 @%%@CR:IX5.30 @% The %@AB@%.MODEL%@AE@% directive defines attributes that affect the entire module:%@EH@% memory model, default calling and naming conventions, and stack type. This directive should appear before any other simplified segment directive.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.MODEL%@AE@% %@AI@%memorymodel%@AE@%[[[[%@AB@%,%@AE@%%@AI@%language%@AE@%]]%@AB@%,%@AE@%%@AI@%stacktype%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@% Each of the three fields defines a basic attribute. The %@AI@%memorymodel%@AE@% field%@EH@% defines the segment structure of the module. The %@AI@%language%@AE@% field defines the default calling and naming conventions assumed by %@AB@%PROC%@AE@% statements. These conventions correspond to the high-level language you specify. The %@AI@%stacktype%@AE@% field determines whether or not the assembler assumes that the SS register is equal to the DS register.%@NL@% %@NL@% %@4@%%@CR:IX5.31 @%%@CR:IX5.32 @%%@CR:IX5.33 @%%@CR:IX5.34 @%%@CR:IX5.35 @%%@CR:IX5.36 @% The %@AI@%memorymodel%@AE@% field can be %@AB@%TINY%@AE@%, %@AB@%SMALL%@AE@%,%@AB@% MEDIUM%@AE@%,%@AB@% COMPACT%@AE@%, %@AB@%LARGE%@AE@%, or %@AB@%HUGE%@AE@%.%@EH@% The assembler defines segments the same way for large and huge models, but the %@AB@%@DataSize%@AE@% equate (explained in Section 5.1.5%@BO: 47722@%, "Using Predefined Segment Equates") gives a different value for these two models.%@NL@% %@NL@% %@4@%%@CR:IX5.37 @% If you write an assembler routine for a high-level language, the%@EH@% %@AI@%memorymodel%@AE@% field should match the memory model used by the compiler or interpreter. If you write a stand-alone assembler program, you can use any model. Section 5.1.1%@BO: 40c7f@% describes each memory model.%@NL@% %@NL@% %@4@%%@CR:IX5.38 @%%@CR:IX5.39 @%%@CR:IX5.40 @%%@CR:IX5.41 @% The optional %@AI@%language%@AE@% field tells the assembler to follow the naming,%@EH@% calling, and return conventions appropriate to the indicated language. In addition, if you use the %@AI@%language%@AE@% argument, the assembler automatically makes all procedure names public. You can use %@AB@%C%@AE@%, %@AB@%Pascal%@AE@%, %@AB@%FORTRAN%@AE@%, or %@AB@%BASIC%@AE@% as the %@AI@%language%@AE@% argument. The last three are equivalent, since these languages share the same naming and calling conventions.%@NL@% %@NL@% %@4@% Note that although the %@AI@%language%@AE@% field is optional, you will not be able to%@EH@% use the high-level language features of the %@AB@%PROC%@AE@% directive if you do not give it. Normally, you should specify a language with %@AB@%.MODEL%@AE@%. If you use %@AB@%C%@AE@% for the language argument, all public and external names are by default prefixed with an underscore (_) in the .OBJ file. Specifying any other language has no effect on the names.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The assembler does not truncate names in order to match the conventions of specific languages, such as FORTRAN or Pascal. Moreover, using the C type specifier does not cause the assembler to preserve case. To preserve lowercase names in public symbols, choose one of the assembler flags that preserves case (Preserve Extrn or Preserve Case), or assemble with /Cx or /Cl on the QCL command line. Within the environment, the Preserve Extrn flag is on by default.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% See Appendix A%@BO: ed697@% for an explanation of how the different calling%@EH@% conventions are implemented. You should also note that each language has different defaults for passing parameters by value or by reference. Depending on which method is used, a high-level language passes a parameter either as a value or as a pointer to the value.%@NL@% %@NL@% %@4@% The optional %@AI@%stacktype%@AE@% field determines whether or not the assembler%@EH@% assumes that SS is equal to DS. The default value is %@AB@%nearStack%@AE@%, which assumes that SS is part of the default data area, so that SS is equal to DS, and SP is set to the top of the data area. You can also use %@AB@%farStack%@AE@%, which assumes that the stack segment is in a separate physical segment from the default data area.%@NL@% %@NL@% %@4@%%@CR:IX5.42 @% If you write a module called from QuickC, you should always use the%@EH@% default (in other words, just leave the field blank), since QuickC always assumes DS equals SS. If you write modules for a compiler (such as the Microsoft Optimized C Compiler) that supports customized memory models, use %@AB@%farStack%@AE@% for models in which SS does not equal DS. If you write a stand-alone assembler program, you can choose either setting. If you use the %@AB@%.STARTUP%@AE@% directive, the assembler automatically generates the proper code for setting up the indicated stack type.%@NL@% %@NL@% %@4@%%@CR:IX5.43 @%%@CR:IX5.44 @%%@CR:IX5.45 @% If you write a stand-alone module without using %@AB@%.STARTUP%@AE@%, you should%@EH@% exercise caution. If you initialize DS but do not adjust SS and SP (as described in Section 5.5.3%@BO: 55651@%, "Initializing the SS and SP Registers), use the %@AB@%farStack%@AE@% keyword. If you do adjust SS and SP as described in Section 5.5.3%@BO: 55651@%, you can use the default value, %@AB@%nearStack%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@NL@% %@4@% This statement defines default segments for small-model programs and%@EH@% creates the %@AB@%ASSUME%@AE@% and %@AB@%GROUP%@AE@% statements used by small-model programs. The segments are automatically ordered according to the Microsoft convention. The example statements might be used at the start of the main (or only) module of a stand-alone assembler program.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL large,pascal%@AE@%%@NL@% %@NL@% %@4@% This statement defines default segments for large-model programs and%@EH@% creates the %@AB@%ASSUME%@AE@% and %@AB@%GROUP%@AE@% statements used by large-model programs. It does not automatically order segments according to the Microsoft convention. The example statement might be used at the start of an assembly module that would be called from a large-model Pascal program or a C program in which the Pascal calling convention was specified.%@NL@% %@NL@% %@4@% %@AB@%Example 3%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small,c,farStack%@AE@%%@NL@% %@NL@% %@4@% This statement defines default segments for a small-model program and%@EH@% creates the appropriate %@AB@%ASSUME%@AE@% and %@AB@%GROUP%@AE@% statements. In addition, this statement makes all procedures public, and directs the assembler to prefix an underscore to the beginning of each public name, so that the naming convention is compatible with C. If you later use the %@AB@%PROC%@AE@% statement to declare parameters, the assembler will assume that the parameters are placed on the stack in the order specified by the C calling convention. In addition, the statement uses %@AB@%farStack%@AE@%, indicating that SS is not equal to DS.%@NL@% %@NL@% %@4@% The last example would be appropriate for a module called by a C module%@EH@% with a customized memory model, compiled with a setting that did not assume SS equal to DS. Note that QuickC does not support customized memory models.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The assembler does not normally display the code generated by the high-level-language support features. You can see the code produced by these features by using the %@AB@%.LALL%@AE@% directive or the /LA command-line option.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% To write procedures for use with more than one language and memory model,%@EH@% you can use text macros for the memory model and language arguments, and define the values from the command line or in the Assembler Flags dialog box. For example, the following %@AB@%.MODEL%@AE@% directive uses text macros for the %@AI@%memorymodel%@AE@% and %@AI@%language%@AE@% arguments:%@NL@% %@NL@% %@AS@%% .MODEL memmodel,lang ; Use % to evaluate memmodel, lang%@AE@%%@NL@% %@NL@% %@4@% The values of the two text macros can be defined from the command line%@EH@% using the /D switch:%@NL@% %@NL@% %@AS@%QCL /Dmemmodel=MEDIUM /Dlang=C /AM /Cx main.c proc.asm%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.4 @%%@AB@%5.1.4 Defining Simplified Segments%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Each of the directives %@AB@%.CODE%@AE@%, %@AB@%.STACK%@AE@%, %@AB@%.DATA%@AE@%, %@AB@%.DATA?%@AE@%, %@AB@%.CONST%@AE@%, %@AB@%.FARDATA%@AE@%,%@EH@% %@AB@%.FARDATA?%@AE@%, and %@AB@%.STARTUP%@AE@% indicate the start of a segment. They also end the immediately preceding segment definition.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.CODE%@AE@% [[%@AI@%name%@AE@%]] Code segment%@EH@%%@NL@% %@AB@%.STACK%@AE@% [[%@AI@%size%@AE@%]] Stack segment%@NL@% %@AB@%.DATA%@AE@% Initialized near-data segment%@NL@% %@AB@%.DATA?%@AE@% Uninitialized near-data segment%@NL@% %@AB@%.CONST%@AE@% Constant-data segment%@NL@% %@AB@%.FARDATA%@AE@% [[%@AI@%name%@AE@%]] Initialized far-data segment%@NL@% %@AB@%.FARDATA?%@AE@% [[%@AI@%name%@AE@%]] Uninitialized far-data segment%@NL@% %@AB@%.STARTUP%@AE@% Code to initialize segment registers%@NL@% %@NL@% %@4@%%@CR:IX5.46 @% For segments that take an optional %@AI@%name%@AE@%, the base file name of the source%@EH@% module is used if you do not specify a value yourself.%@NL@% %@NL@% %@4@%%@CR:IX5.47 @%%@CR:IX5.48 @%%@CR:IX5.49 @% Each new segment directive ends the previous segment. The %@AB@%END%@AE@% directive%@EH@% closes the last segment in the source file.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.4.1 @%%@AB@%5.1.4.1 How to Use Simplified Segments%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%.CODE%@AE@%, %@AB@%.DATA%@AE@%, and %@AB@%.STACK%@AE@% directives create the three basic segments%@EH@% that programs generally need to have. Chapter 4%@BO: 34dba@%, "Writing Stand-Alone Assembly Programs," demonstrates how to use these directives to write code, data, and stack segments. Chapter 4%@BO: 34dba@% also explains the purpose of each of these segments.%@NL@% %@NL@% %@4@% The %@AB@%.STARTUP %@AE@%directive initializes segment registers to the appropriate%@EH@% segment values. Chapter 4%@BO: 34dba@% describes the use of %@AB@%.STARTUP%@AE@%, and Section 5.5%@BO: 53a35@% tells more about how %@AB@%.STARTUP%@AE@% works and what code it generates.%@NL@% %@NL@% %@4@%%@CR:IX5.50 @%%@CR:IX5.51 @%%@CR:IX5.52 @% When you write a mixed-language program, you generally don't need to%@EH@% declare a stack segment, because the start-up code in the C main module creates a stack for you. When you write a stand-alone program, you should declare a stack segment in the main module only.%@NL@% %@NL@% %@4@%%@CR:IX5.53 @% Your programs can also use the %@AB@%.DATA?%@AE@% and %@AB@%.CONST%@AE@% directives to create%@EH@% segments for uninitialized and constant data, respectively. With stand-alone assembler programs, the use of these directives is optional, because you can place all data in the segment defined by %@AB@%.DATA%@AE@% if you want. With mixed-language programs, use %@AB@%.DATA?%@AE@% and %@AB@%.CONST%@AE@% to ensure compatibility with the way C handles uninitialized and constant data. Once you define these segments, it is up to you to place the appropriate data in each segment.%@NL@% %@NL@% %@4@%%@CR:IX5.54 @%%@CR:IX5.55 @%%@CR:IX5.56 @%%@CR:IX5.57 @%%@CR:IX5.58 @%%@CR:IX5.59 @%%@CR:IX5.60 @%%@CR:IX5.61 @%%@CR:IX5.62 @%%@CR:IX5.63 @% If your program is written in compact, large, or huge model, you can use%@EH@% the %@AB@%.FARDATA%@AE@% and %@AB@%.FARDATA?%@AE@% directives to define additional data segments. All the data in the other data segments (defined by %@AB@%.DATA%@AE@%, %@AB@%.DATA?%@AE@%, and %@AB@%.CONST%@AE@%) must not exceed a total of 64K across all modules. In addition, the stack segment is also placed into this 64K area unless you specify %@AB@%farStack%@AE@% with the %@AB@%.MODEL%@AE@% directive.%@NL@% %@NL@% %@4@% Data in the %@AB@%.FARDATA%@AE@% and %@AB@%.FARDATA?%@AE@% segments takes slightly longer to%@EH@% access. However, there is generally much more room in these segments for data definitions. For each module, the %@AB@%.FARDATA%@AE@% and %@AB@%.FARDATA?%@AE@% directives each create a separate physical segment that can be up to 64K in size. The recommended procedure is to use %@AB@%.FARDATA%@AE@% for initialized data, and %@AB@%.FARDATA?%@AE@% for uninitialized data, although this is optional.%@NL@% %@NL@% %@4@% With medium, large, and huge model, you can use the %@AI@%name%@AE@% attribute to%@EH@% create multiple code segments within a source module. With compact, large, and huge model, you can also use the %@AI@%name%@AE@% attribute to create multiple far-data segments.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .STACK 100h%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%ivariable DB 5%@AE@%%@NL@% %@AS@%iarray DW 50 DUP (5)%@AE@%%@NL@% %@AS@%string DB "This is a string"%@AE@%%@NL@% %@AS@%uarray DW 50 DUP (?)%@AE@%%@NL@% %@AS@% EXTRN xvariable:WORD%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@AS@% EXTRN xprocedure:NEAR%@AE@%%@NL@% %@AS@% call xprocedure%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% This code uses simplified segment directives for a small-model,%@EH@% stand-alone assembler program. Notice that initialized data, uninitialized data, and a string constant are all defined in the same data segment. See Section 5.1.7%@BO: 493ed@%, "Default Segment Names," for an equivalent version that uses full segment definitions.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL, large,c%@AE@%%@NL@% %@AS@% .FARDATA?%@AE@%%@NL@% %@AS@%fuarray DW 10 DUP (?) ; Far uninitialized data%@AE@%%@NL@% %@AS@% .CONST%@AE@%%@NL@% %@AS@%string DB "This is a string" ; String constant%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%niarray DB 100 DUP (5) ; Near initialized data%@AE@%%@NL@% %@AS@% .FARDATA%@AE@%%@NL@% %@AS@% EXTRN xvariable:FAR%@AE@%%@NL@% %@AS@%fiarray DW 100 DUP (10) ; Far initialized data%@AE@%%@NL@% %@AS@% .CODE TASK%@AE@%%@NL@% %@AS@% EXTRN xprocedure:PROC%@AE@%%@NL@% %@AS@%task PROC%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%task ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% This example uses simplified segment directives to create a module that%@EH@% might be called from a large-model, high-level-language program. Notice that different types of data are put in different segments to conform to Microsoft compiler conventions. See Section 5.1.7%@BO: 493ed@%, "Default Segment Names," for an equivalent version using full segment definitions.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.4.2 @%%@AB@%5.1.4.2 How Simplified Segments Are Implemented%@AE@%%@EH@%%@NL@% %@NL@% %@4@% When you use the simplified segment directives described above, the%@EH@% assembler defines segments in a way compatible with Microsoft high-level languages.%@NL@% %@NL@% %@4@% This section makes a number of references to groups and %@AB@%ASSUME%@AE@% statements.%@EH@% Both of these concepts arise from the need to deal with the 8086 segmented architecture. A "group" consists of one or more segments, totaling no more than 64K. When multiple segments are placed into a group, the linker combines these segments into a single physical segment. All addresses in the physical segment are adjusted so that they share the same segment address. Use of groups is convenient because it removes the need to constantly reload the DS register.%@NL@% %@NL@% %@4@%%@CR:IX5.64 @%%@CR:IX5.65 @% The %@AB@%ASSUME%@AE@% directive is described at greater length in Section 5.4%@BO: 52503@%,%@EH@% "Associating Segments with Registers." This directive informs the assembler where a segment register will point to at run time so that the assembler can correctly calculate offset addresses relative to the value in the appropriate segment register.%@NL@% %@NL@% %@4@% Unless you use tiny model, the code segment (defined with %@AB@%.CODE%@AE@%) is placed%@EH@% in its own physical segment, separate from all the data and stack segments. With medium, large, or huge model, you can define multiple code segments within one source model by using %@AB@%.CODE%@AE@% repeatedly, each time with a different %@AI@%name%@AE@% attribute. When you use this technique, each %@AB@%.CODE%@AE@% directive generates a new %@AB@%ASSUME%@AE@% statement so that the assembler knows where CS points to at run time.%@NL@% %@NL@% %@4@%%@CR:IX5.66 @% Segments defined with the %@AB@%.STACK%@AE@%, %@AB@%.CONST%@AE@%, %@AB@%.DATA,%@AE@% or %@AB@%.DATA?%@AE@% directives are%@EH@% placed in a group called DGROUP. Segments defined with the %@AB@%.FARDATA%@AE@% or %@AB@%.FARDATA?%@AE@% directives are not placed in any group. See Section 5.3%@BO: 512db@% for more information on segment groups. When initializing the DS register to access data in a group-associated segment, the value of DGROUP should be loaded into DS. The %@AB@%.STARTUP%@AE@% directive does this initialization automatically.%@NL@% %@NL@% %@4@% The %@AB@%.MODEL%@AE@% directive generates %@AB@%ASSUME%@AE@% statements to inform the assembler%@EH@% that at run time, DS, SS, and ES will all point to the beginning of DGROUP. You don't need to write these %@AB@%ASSUME%@AE@% statements yourself.%@NL@% %@NL@% %@4@% If you specify %@AB@%farStack%@AE@% with the %@AB@%.MODEL%@AE@% directive, the stack is placed in%@EH@% a separate physical segment and the %@AB@%.MODEL%@AE@% directive generates an %@AB@%ASSUME%@AE@% statement to inform the assembler that SS does not point to the same segment address that DS does.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.5 @%%@AB@%5.1.5 Using Predefined Segment Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Several equates are predefined for you. You can use the equate names at%@EH@% any point in your code to represent the equate values. You should not assign equates having these names. The predefined equates are listed below:%@NL@% %@NL@% %@AB@%Name%@AE@% %@AB@%Value%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX5.67 @%%@CR:IX5.68 @% %@AB@%@CodeSize %@AE@%and If the %@AB@%.MODEL%@AE@% directive has been used, the value of %@AB@%@DataSize%@AE@% %@AB@%@CodeSize%@AE@% is 0 for the models that use near-code labels (tiny, small, and compact) or 1 for models that use far-code labels (medium, large, and huge). The value of%@AB@% @DataSize%@AE@% is 0 for models that use near-data labels (tiny, small, and medium), 1 for compact and large models, and 2 for huge models. These values can be used in conditional-assembly statements.%@NL@% %@NL@% %@AS@% IF @DataSize%@AE@%%@NL@% %@AS@% les bx,pointer ; Load far pointer%@AE@%%@NL@% %@AS@% mov ax,es:WORD PTR [bx]%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% mov bx,WORD PTR pointer ; Load near pointer%@AE@%%@NL@% %@AS@% mov ax,WORD PTR [bx]%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX5.69 @% %@AB@%@CurSeg%@AE@% This name has the segment name of the current segment.%@EH@% This value may be convenient for %@AB@%ASSUME%@AE@% statements, segment overrides, or other cases in which you need to access the current segment. It can also be used to end a segment.%@NL@% %@NL@% %@4@%%@CR:IX5.70 @%%@CR:IX5.71 @% %@AB@%@FileName%@AE@% This value represents the base name of the current%@EH@% source file. For example, if the current source file is TASK.ASM, the value of %@AB@%@FileName%@AE@% is TASK. This value can be used in any name you would like to change if the file name changes. For example, it can be used as a procedure name:%@NL@% %@NL@% %@NL@% %@AS@%@FileName PROC%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%@FileName ENDP%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX5.72 @%%@CR:IX5.73 @% %@AB@%@Model%@AE@% As with the %@AB@%@CodeSize%@AE@% and %@AB@%@DataSize%@AE@% predefined%@EH@% equates, you must first use the %@AB@%.MODEL%@AE@% directive before using the %@AB@%@Model%@AE@% equate. The value of %@AB@%@Model%@AE@% is 1 for tiny model, 2 for small, 3 for compact, 4 for medium, 5 for large, and 6 for huge. %@AB@%@Model%@AE@% can be used in conditional-assembly statements.%@NL@% %@NL@% %@4@%%@CR:IX5.74 @%%@CR:IX5.75 @%%@CR:IX5.76 @%%@CR:IX5.77 @%%@CR:IX5.78 @%%@CR:IX5.79 @%%@CR:IX5.80 @% Segment equates For each of the primary segment directives, there is a%@EH@% corresponding equate with the same name, except that the equate starts with an "at sign" (%@AB@%@%@AE@%) instead of a period. For example, the %@AB@%@code%@AE@% equate represents the segment name defined by the %@AB@%.CODE%@AE@% directive. Similarly, %@AB@%@fardata %@AE@%represents the%@AB@% .FARDATA%@AE@% segment name and %@AB@%@fardata?%@AE@% represents the %@AB@%.FARDATA?%@AE@% segment name. The %@AB@%@data%@AE@% equate represents the group name shared by all the near-data segments. It can be used to access the segments created by the %@AB@%.DATA%@AE@%, %@AB@%.DATA?%@AE@%, %@AB@%.CONST%@AE@%, and %@AB@%.STACK%@AE@% segments.%@NL@% %@NL@% %@4@% These equates can be used in %@AB@%ASSUME%@AE@% statements and at%@EH@% any other time a segment must be referred to by name.%@NL@% %@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Although predefined equates are part of the simplified segment system, the %@AB@%@CurSeg%@AE@% and %@AB@%@FileName %@AE@%equates are also available when using full segment definitions. If you use the /Cl option or set Preserve Case in the Assembler Flags dialog box, predefined equates will be case sensitive with the exact names shown above.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.6 @%%@AB@%5.1.6 Simplified Segment Defaults%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.81 @%%@CR:IX5.82 @% Although your program can combine full segment definitions and simplified%@EH@% segment directives, the %@AB@%.MODEL%@AE@% directive enables certain features of simplified segment directives that change defaults. Defaults that change are listed below:%@NL@% %@NL@% %@CR:IX5.83 @%%@CR:IX5.84 @% ■ If you do not use the %@AB@%.MODEL%@AE@% directive, the default size for the %@AB@%PROC%@AE@% directive is always %@AB@%NEAR%@AE@%. If you use the %@AB@%.MODEL%@AE@% directive, the %@AB@%PROC%@AE@% directive is associated with the specified memory model: %@AB@%NEAR%@AE@% for tiny, small, and compact models and %@AB@%FAR%@AE@% for medium, large, and huge models. See Section 6.4.3%@BO: 5ea93@%, "Procedure Labels," for further discussion of the %@AB@%PROC%@AE@% directive.%@NL@% %@NL@% %@CR:IX5.85 @%%@CR:IX5.86 @%%@CR:IX5.87 @% ■ If you use the %@AB@%.MODEL%@AE@% directive, the %@AB@%OFFSET%@AE@% operator returns an offset relative to the beginning of a group, whenever a data item is defined within a group. If you do not use the %@AB@%.MODEL%@AE@% directive, the %@AB@%OFFSET%@AE@% operator always returns an offset relative to the beginning of the segment. The simplified segment directives %@AB@%.DATA%@AE@%, %@AB@%.DATA?%@AE@%, and %@AB@%.STACK%@AE@% all create segments that are part of the group DGROUP.%@NL@% %@NL@% For example, assume the variable %@AS@%test1 %@AE@%was declared in a segment defined with the %@AB@%.DATA%@AE@% directive and %@AS@%test2 %@AE@%was declared in a segment defined with the %@AB@%.FARDATA%@AE@% directive. The statement%@NL@% %@NL@% %@NL@% %@AS@% mov ax,OFFSET test1%@AE@%%@NL@% %@NL@% loads the address of %@AS@%test1 %@AE@%relative to DGROUP. The statement%@NL@% %@NL@% %@NL@% %@AS@% mov ax,OFFSET test2%@AE@%%@NL@% %@NL@% loads the address of %@AS@%test2 %@AE@%relative to the segment defined by the %@AB@%.FARDATA%@AE@% directive. See Section 5.3%@BO: 512db@% for more information on groups.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.1.7 @%%@AB@%5.1.7 Default Segment Names%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.88 @% If you use the simplified segment directives by themselves, you do not%@EH@% need to know the names assigned for each segment. However, it is possible to mix full segment definitions with simplified segment directives. Therefore, some programmers may wish to know the actual names assigned to all segments.%@NL@% %@NL@% %@4@%%@CR:IX5.89 @% Table 5.1 shows the default segment names created by each directive.%@EH@%%@NL@% %@NL@% %@AB@%Table 5.1 Default Segments and Types for Standard Memory Models%@AE@%%@NL@% %@NL@% %@AB@%Model%@AE@% %@AB@%Directive%@AE@% %@AB@%Name%@AE@% %@AB@%Align%@AE@% %@AB@%Combine%@AE@% %@AB@%Class%@AE@% %@AB@%Group%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Tiny %@AB@%.CODE%@AE@% _TEXT %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CODE' DGROUP%@NL@% %@NL@% %@AB@%.DATA%@AE@% _DATA %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'DATA' DGROUP%@NL@% %@NL@% %@AB@%.CONST%@AE@% CONST %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CONST' DGROUP%@NL@% %@NL@% %@AB@%.DATA?%@AE@% _BSS %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'BSS' DGROUP%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Small %@AB@%.CODE%@AE@% _TEXT %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CODE'%@NL@% %@NL@% %@AB@%.DATA%@AE@% _DATA %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'DATA' DGROUP%@NL@% %@NL@% %@AB@%.CONST%@AE@% CONST %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CONST' DGROUP%@NL@% %@NL@% %@AB@%.DATA?%@AE@% _BSS %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'BSS' DGROUP%@NL@% %@NL@% %@AB@%.STACK%@AE@% STACK %@AB@%PARA%@AE@% %@AB@%STACK%@AE@% 'STACK' DGROUP%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Medium %@AB@%.CODE%@AE@% %@AI@%name%@AE@%_TEXT %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CODE'%@NL@% %@NL@% %@AB@%.DATA%@AE@% _DATA %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'DATA' DGROUP%@NL@% %@NL@% %@AB@%.CONST%@AE@% CONST %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CONST' DGROUP%@NL@% %@NL@% %@AB@%.DATA?%@AE@% _BSS %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'BSS' DGROUP%@NL@% %@NL@% %@AB@%.STACK%@AE@% STACK %@AB@%PARA%@AE@% %@AB@%STACK%@AE@% 'STACK' DGROUP%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Compact %@AB@%.CODE%@AE@% _TEXT %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CODE'%@NL@% %@NL@% %@AB@%.FARDATA%@AE@% FAR_DATA %@AB@%PARA%@AE@% private 'FAR_DATA'%@NL@% %@NL@% %@AB@%.FARDATA?%@AE@% FAR_BSS %@AB@%PARA%@AE@% private 'FAR_BSS'%@NL@% %@NL@% %@AB@%.DATA%@AE@% _DATA %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'DATA' DGROUP%@NL@% %@NL@% %@AB@%.CONST%@AE@% CONST %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CONST' DGROUP%@NL@% %@NL@% %@AB@%.DATA?%@AE@% _BSS %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'BSS' DGROUP%@NL@% %@NL@% %@AB@%.STACK%@AE@% STACK %@AB@%PARA%@AE@% %@AB@%STACK%@AE@% 'STACK' DGROUP%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Large or %@AB@%.CODE%@AE@% %@AI@%name%@AE@%_TEXT %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CODE'%@NL@% %@NL@% huge %@AB@%.FARDATA%@AE@% FAR_DATA %@AB@%PARA%@AE@% private 'FAR_DATA'%@NL@% %@NL@% %@AB@%.FARDATA?%@AE@% FAR_BSS %@AB@%PARA%@AE@% private 'FAR_BSS'%@NL@% %@NL@% %@AB@%.DATA%@AE@% _DATA %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'DATA' DGROUP%@NL@% %@NL@% %@AB@%.CONST%@AE@% CONST %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'CONST' DGROUP%@NL@% %@NL@% %@AB@%.DATA?%@AE@% _BSS %@AB@%WORD%@AE@% %@AB@%PUBLIC%@AE@% 'BSS' DGROUP%@NL@% %@NL@% %@AB@%.STACK%@AE@% STACK %@AB@%PARA%@AE@% %@AB@%STACK%@AE@% 'STACK' DGROUP%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% The name used as part of far-code segment names is the file name of the%@EH@% module. The default name associated with the %@AB@%.CODE%@AE@% directive can be overridden in medium and large models. The default names for the %@AB@%.FARDATA%@AE@% and %@AB@%.FARDATA?%@AE@% directives can always be overridden.%@NL@% %@NL@% %@4@% The segment and group table at the end of listings always shows the actual%@EH@% segment names. However, the %@AB@%GROUP%@AE@% and %@AB@%ASSUME%@AE@% statements generated by the %@AB@%.MODEL%@AE@% directive are not shown in listing files. For a program that uses all possible segments, group statements equivalent to the following would be generated:%@NL@% %@NL@% %@AS@%DGROUP GROUP _DATA,CONST,_BSS,STACK%@AE@%%@NL@% %@NL@% %@4@% For tiny model, the following would be generated:%@EH@%%@NL@% %@NL@% %@AS@% ASSUME cs:DGROUP,ds:DGROUP,ss:DGROUP%@AE@%%@NL@% %@NL@% %@4@% For small and compact models, the following would be generated:%@EH@%%@NL@% %@NL@% %@AS@% ASSUME cs:_TEXT,ds:DGROUP,ss:DGROUP%@AE@%%@NL@% %@NL@% %@4@% For medium, large, and huge models, the following statement is given:%@EH@%%@NL@% %@NL@% %@AS@% ASSUME cs: name_TEXT,ds:DGROUP,ss:DGROUP%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% EXTRN xvariable:WORD%@AE@%%@NL@% %@AS@% EXTRN xprocedure:NEAR%@AE@%%@NL@% %@AS@%DGROUP GROUP _DATA,_BSS%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT,ds:DGROUP,ss:DGROUP%@AE@%%@NL@% %@AS@%_TEXT SEGMENT WORD PUBLIC 'CODE'%@AE@%%@NL@% %@AS@%start: mov ax,DGROUP ; Initialize data segment%@AE@%%@NL@% %@AS@% mov ds,ax%@AE@%%@NL@% %@AS@% cli%@AE@%%@NL@% %@AS@% mov ss,ax ; Move DGROUP into SS%@AE@%%@NL@% %@NL@% %@AS@% add sp,OFFSET STACK ; Adjust SP to top of stack%@AE@%%@NL@% %@AS@% sti%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%TEXT ENDS%@AE@%%@NL@% %@AS@%_DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@%ivariable DB 5%@AE@%%@NL@% %@AS@%iarray DW 50 DUP (5)%@AE@%%@NL@% %@AS@%string DB "This is a string"%@AE@%%@NL@% %@AS@%uarray DW 50 DUP (?)%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@AS@%STACK SEGMENT PARA STACK 'STACK'%@AE@%%@NL@% %@AS@% DB 100h DUP (?)%@AE@%%@NL@% %@AS@%STACK ENDS%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@4@% This example is equivalent to Example 1 in Section 5.1.4%@BO: 44e63@%, "Defining%@EH@% Simplified Segments." Notice that the segment order must be different in this version to achieve the segment order specified by using the %@AB@%DOSSEG%@AE@% directive in the first Section 5.1.4%@BO: 44e63@% example. The external variables are declared at the start of the source code in this example. With simplified segment directives, external variables can be declared in the segment in which they are used. The code generated by%@AB@% .STARTUP%@AE@% is discussed in more detail in Section 5.5.3%@BO: 55651@%.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%DGROUP GROUP _DATA,CONST,STACK%@AE@%%@NL@% %@AS@% ASSUME cs:TASK_TEXT,ds:FAR_DATA,ss:STACK%@AE@%%@NL@% %@AS@% EXTRN xprocedure:FAR%@AE@%%@NL@% %@AS@% EXTR xvariable:FAR%@AE@%%@NL@% %@AS@%FAR_BSS SEGMENT PARA 'FAR_DATA'%@AE@%%@NL@% %@AS@%fuarray DW 10 DUP (?) ; Far uninitialized data%@AE@%%@NL@% %@AS@%FAR_BSS ENDS%@AE@%%@NL@% %@AS@%CONST SEGMENT WORD PUBLIC 'CONST'%@AE@%%@NL@% %@AS@%string DB "This is a string" ; String constant%@AE@%%@NL@% %@AS@%CONST ENDS%@AE@%%@NL@% %@AS@%_DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@%niarray DB 100 DUP (5) ; Near initialized data%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@AS@%FAR_DATA SEGMENT WORD 'FAR_DATA'%@AE@%%@NL@% %@AS@%fiarray DW 100 DUP (10)%@AE@%%@NL@% %@AS@%FAR_DATA ENDS%@AE@%%@NL@% %@AS@%TASK_TEXT SEGMENT WORD PUBLIC 'CODE'%@AE@%%@NL@% %@AS@%task PROC FAR%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%task ENDP%@AE@%%@NL@% %@AS@%TASK_TEXT ENDS%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% This example is equivalent to Example 2 in Section 5.1.4%@BO: 44e63@%, "Defining%@EH@% Simplified Segments." Notice that the segment order is the same in both versions. The segment order shown here is written to the object file, but it is different in the executable file. The segment order specified by the compiler (the DOS segment order) overrides the segment order in the module object file.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC5.2 @%%@AB@%5.2 Full Segment Definitions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.90 @%%@CR:IX5.91 @%%@CR:IX5.92 @%%@CR:IX5.93 @%%@CR:IX5.94 @%%@CR:IX5.95 @% If you need complete control over segments, you may want to give complete%@EH@% segment definitions. The section below explains all aspects of segment definitions, including how to order segments and how to define all the segment types.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.2.1 @%%@AB@%5.2.1 Setting the Segment-Order Method%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.96 @% The order in which QuickAssembler writes segments to the object file can%@EH@% be either sequential or alphabetical. If the sequential method is specified, segments are written in the order in which they appear in the source code. If the alphabetical method is specified, segments are written in the alphabetical order of their segment names.%@NL@% %@NL@% %@4@% The default is sequential. If no segment-order directive or option is%@EH@% given, segments are ordered sequentially. The segment-order method is only one factor in determining the final order of segments in memory. The %@AB@%DOSSEG%@AE@% directive (see Section 5.1.2%@BO: 41df5@%, "Specifying DOS Segment Order") and class type (see Section 5.2.2.3%@BO: 4f990@%, "Controlling Segment Structure with Class Type") can also affect segment order.%@NL@% %@NL@% %@4@%%@CR:IX5.97 @%%@CR:IX5.98 @%%@CR:IX5.99 @%%@CR:IX5.100 @%%@CR:IX5.101 @%%@CR:IX5.102 @%%@CR:IX5.103 @%%@CR:IX5.104 @% The ordering method can be set by using the %@AB@%.ALPHA%@AE@% or %@AB@%.SEQ%@AE@% directive in%@EH@% the source code. The method can also be set using the /s (sequential) or /a (alphabetical) assembler options (see Appendix B%@BO: f653b@%, Section B.1%@BO: f7a41@%, "Specifying the Segment-Order Method"). The directives have precedence over the options. For example, if the source code contains the %@AB@%.ALPHA%@AE@% directive, but the /s option is given on the command line, the segments are ordered alphabetically.%@NL@% %@NL@% %@4@%%@CR:IX5.105 @%%@CR:IX5.106 @% Changing the segment order is an advanced technique. In most cases, you%@EH@% can simply leave the default sequential order in effect. If you are linking with high-level-language modules, the compiler automatically sets the segment order. The %@AB@%DOSSEG%@AE@% directive also overrides any segment-order directives or options.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% Some previous versions of the IBM Macro Assembler ordered segments alphabetically by default. If you have trouble assembling and linking source-code listings from books or magazines, try using the /a option. Listings written for previous IBM versions of the assembler may not work without this option. The distinction between %@AB@%ENDS %@AE@%as the end of a segment and %@AB@%ENDS%@AE@% as the end of a structure is also made by the content of the program.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .SEQ%@AE@%%@NL@% %@AS@%DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@%DATA ENDS%@AE@%%@NL@% %@AS@%CODE SEGMENT WORD PUBLIC 'CODE'%@AE@%%@NL@% %@AS@%CODE ENDS%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .ALPHA%@AE@%%@NL@% %@AS@%DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@%DATA ENDS%@AE@%%@NL@% %@AS@%CODE SEGMENT WORD PUBLIC 'CODE'%@AE@%%@NL@% %@AS@%CODE ENDS%@AE@%%@NL@% %@NL@% %@4@% In Example 1, the %@AS@%DATA %@AE@%segment is written to the object file first because%@EH@% it appears first in the source code. In Example 2, the %@AS@%CODE %@AE@%segment is written to the object file first because its name comes first alphabetically.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.2.2 @%%@AB@%5.2.2 Defining Full Segments%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.107 @%%@CR:IX5.108 @%%@CR:IX5.109 @%%@CR:IX5.110 @% The beginning of a program segment is defined with the %@AB@%SEGMENT%@AE@% directive,%@EH@% and the end of the segment is defined with the %@AB@%ENDS%@AE@% directive.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%SEGMENT%@AE@% [[%@AI@%align%@AE@%]] [[%@AI@%combine%@AE@%]] [[%@AI@%use%@AE@%]] [[%@AB@%'%@AE@%%@AI@%class%@AE@%%@AB@%'%@AE@%]]%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AI@%name%@AE@% %@AB@%ENDS%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% defines the name of the segment. This name can be unique, or it%@EH@% can be the same name given to other segments in the program. Segments with identical names are treated as the same segment. For example, if it is convenient to put different portions of a single segment in different source modules, the segment is given the same name in both modules.%@NL@% %@NL@% %@4@% The optional %@AI@%align%@AE@%, %@AI@%combine%@AE@%, %@AI@%use%@AE@%, and '%@AI@%class%@AE@%' types give the linker and%@EH@% the assembler instructions on how to set up and combine segments. Types can be specified in any order; it is not necessary to enter all types, or any type, for a given segment.%@NL@% %@NL@% %@4@% Defining segment types is an advanced technique. Beginning%@EH@% assembly-language programmers might try using the simplified segment directives discussed in Section 5.1%@BO: 406ed@%.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% Don't confuse the %@AB@%PAGE %@AE@%align type and the %@AB@%PUBLIC%@AE@% combine type with the %@AB@%PAGE %@AE@%and %@AB@%PUBLIC %@AE@%directives. The distinction should be clear from context since the align and combine types are only used on the same line as the %@AB@%SEGMENT %@AE@%directive.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.2.2.1 @%%@AB@%5.2.2.1 Controlling Alignment with Align Type%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.111 @%%@CR:IX5.112 @%%@CR:IX5.113 @%%@CR:IX5.114 @%%@CR:IX5.115 @% The optional %@AI@%align%@AE@% type defines the range of memory addresses from which a%@EH@% starting address for the segment can be selected. The %@AI@%align%@AE@% type can be any one of the following:%@NL@% %@NL@% %@AB@%Align Type%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX5.116 @% %@AB@%BYTE%@AE@% Uses the next available byte address%@NL@% %@NL@% %@CR:IX5.117 @% %@AB@%WORD%@AE@% Uses the next available word address (2 bytes per word)%@NL@% %@NL@% %@AB@%DWORD%@AE@% Uses the next available doubleword address (4 bytes per doubleword)%@NL@% %@NL@% %@CR:IX5.118 @% %@AB@%PARA%@AE@% Uses the next available paragraph address (16 bytes per paragraph)%@NL@% %@NL@% %@CR:IX5.119 @% %@AB@%PAGE%@AE@% Uses the next available page address (256 bytes per page)%@NL@% %@NL@% %@NL@% %@4@% If no %@AI@%align%@AE@% type is given, %@AB@%PARA%@AE@% is used by default.%@EH@%%@NL@% %@NL@% %@4@% The linker uses the alignment information to determine the relative start%@EH@% address for each segment. DOS uses the information to calculate the actual start address when the program is loaded.%@NL@% %@NL@% %@4@% Align types are illustrated in Figure 5.1 in the next section.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.2.2.2 @%%@AB@%5.2.2.2 Defining Segment Combinations with Combine Type%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.120 @%%@CR:IX5.121 @%%@CR:IX5.122 @% The optional %@AI@%combine%@AE@% type defines how to combine segments having the same%@EH@% name. The combine type can be any one of the following:%@NL@% %@NL@% %@AB@%Combine Type%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX5.123 @% %@AB@%PUBLIC%@AE@% Concatenates all segments having the same name to form a single, contiguous segment. The total size of the resulting segment is equal to the sum of all contributing segments.%@NL@% %@NL@% All instruction and data addresses in the new segment are relative to a single segment register, and all offsets are adjusted to represent the distance from the beginning of the segment.%@NL@% %@NL@% %@CR:IX5.124 @%%@CR:IX5.125 @% %@AB@%STACK%@AE@% Concatenates all segments having the same name to form a single, contiguous segment. This combine type is the same as the %@AB@%PUBLIC%@AE@% combine type, except that all addresses in the new segment are relative to the SS segment register. The total size of the resulting segment is equal to the sum of all contributing segments.%@NL@% %@NL@% The Stack Pointer (SP) register is initialized to the length of the segment. The stack segment of your program should normally use the %@AB@%STACK%@AE@% type, since this automatically initializes the SS register, as described in Section 5.5.3%@BO: 55651@%. If you create a stack segment and do not use the %@AB@%STACK%@AE@% type, you must give instructions to initialize the SS and SP registers.%@NL@% %@NL@% For each individual segment, all initialized data is placed at the high end of the resulting stack segment. Consequently, if more than one stack segment contains initialized data, the linker overwrites this data as it links in each segment. Note that stack data cannot be initialized with simplified segment directives.%@NL@% %@NL@% %@CR:IX5.126 @% %@AB@%COMMON%@AE@% Creates overlapping segments by placing the start of all segments having the same name at the same address. %@NL@% The length of the resulting area is the length of the longest segment. All addresses in the segments are relative to the same base address. If variables are initialized in more than one segment having the same name and %@AB@%COMMON%@AE@% type, the most recently initialized data replaces any previously initialized data.%@NL@% %@NL@% %@CR:IX5.127 @%%@CR:IX5.128 @% %@AB@%MEMORY%@AE@% Concatenates all segments having the same name to form a single, contiguous segment.%@NL@% %@NL@% The Microsoft Overlay Linker treats %@AB@%MEMORY%@AE@% segments exactly the same as %@AB@%PUBLIC%@AE@% segments. QuickAssembler allows you to use %@AB@%MEMORY%@AE@% type even though LINK does not recognize a separate %@AB@%MEMORY%@AE@% type. This feature is compatible with other linkers that may support a combine type conforming to the Intel definition of %@AB@%MEMORY%@AE@% type.%@NL@% %@NL@% %@CR:IX5.129 @%%@CR:IX5.130 @%%@CR:IX5.131 @% %@AB@%AT%@AE@% %@AI@%address%@AE@% Causes all label and variable addresses defined in the segment to be relative to %@AI@%address%@AE@%.%@NL@% %@NL@% %@CR:IX5.132 @% The %@AI@%address%@AE@% can be any valid expression but must not contain a forward reference──that is, a reference to a symbol defined later in the source file. An %@AB@%AT%@AE@% segment typically contains no code or initialized data. Instead, it represents an address template that can be placed over code or data already in memory, such as a screen buffer or other absolute memory locations defined by hardware. The linker will not generate any code or data for %@AB@%AT%@AE@% segments, but existing code or data can be accessed by name if it is given a label in an %@AB@%AT%@AE@% segment. Section 6.6%@BO: 67109@%, "Setting the Location Counter," shows an example of a segment with %@AB@%AT%@AE@% combine type.%@NL@% %@NL@% %@NL@% %@4@% If no %@AI@%combine%@AE@% type is given, the segment has private type. Segments having%@EH@% the same name are not combined. Instead, each segment receives its own physical segment when loaded into memory.%@NL@% %@NL@% %@CR:IX5.133 @% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% Although a given segment name can be used more than once in a source file, each segment definition using that name must have either exactly the same attributes, or attributes that do not conflict. If types are given for an initial segment definition, subsequent definitions for that segment need not specify any types.%@NL@% %@NL@% Normally, you should provide at least one stack segment (having %@AB@%STACK%@AE@% combine type) in a program. If no stack segment is declared, LINK displays a warning message. You can ignore this message if you have a specific reason for not declaring a stack segment. For example, you would not have a separate stack segment in a program in the %@AB@%.COM%@AE@% format.%@NL@% %@CR:IX5.134 @%%@CR:IX5.135 @%%@CR:IX5.136 @% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following source-code shell illustrates one way in which the %@AI@%combine%@AE@%%@EH@% and %@AI@%align%@AE@% types can be used. Figure 5.1 shows the way LINK would load the sample program into memory.%@NL@% %@NL@% %@AS@% NAME module_1%@AE@%%@NL@% %@NL@% %@AS@%ASEG SEGMENT BYTE PUBLIC 'CODE'%@AE@%%@NL@% %@AS@%start: .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%ASEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%BSEG SEGMENT WORD COMMON 'DATA'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%BSEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%CSEG SEGMENT PARA STACK 'STACK'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%CSEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%DSEG SEGMENT AT 0B800H%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%DSEG ENDS%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@AS@% NAME module_2%@AE@%%@NL@% %@NL@% %@AS@%ASEG SEGMENT BYTE PUBLIC 'CODE'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%ASEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%BSEG SEGMENT WORD COMMON 'DATA'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%BSEG ENDS%@AE@%%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 5.2.2.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@CR:IX5.137 @%%@CR:IX5.138 @%%@NL@% %@3@%%@CR:SC5.2.2.3 @%%@AB@%5.2.2.3 Controlling Segment Structure with Class Type%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.139 @%%@CR:IX5.140 @% Class type is a means of associating segments that have different names,%@EH@% but similar purposes. It can be used to control segment order and to identify the code segment.%@NL@% %@NL@% %@4@%%@CR:IX5.141 @%%@CR:IX5.142 @% The %@AI@%class%@AE@% name must be enclosed in single quotation marks (%@AB@%'%@AE@%). Class names%@EH@% are not case sensitive unless the /Cl or /Cx option is used during assembly.%@NL@% %@NL@% %@4@% All segments belong to a class. Segments for which no class name is%@EH@% explicitly stated have the null class name. LINK imposes no restriction on the number or size of segments in a class. The total size of all segments in a class can exceed 64K.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% The names assigned for class types of segments should not be used for other symbol definitions in the source file. For example, if you give a segment the class name %@AS@%'CONSTANT'%@AE@%, you should not give the name %@AS@%constant%@AE@% to variables or labels in the source file.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% The linker expects segments having the class name CODE or a class name%@EH@% with the suffix CODE to contain program code. You should always assign this class name to segments containing code.%@NL@% %@NL@% %@4@%%@CR:IX5.143 @% Class type is one of two factors that control the final order of segments%@EH@% in an executable file. The other factor is the order of the segments in the source file (with the /s option or the %@AB@%.SEQ%@AE@% directive) or the alphabetical order of segments (with the /a option or the %@AB@%.ALPHA%@AE@% directive).%@NL@% %@NL@% %@4@% These factors control different internal behavior, but both affect the%@EH@% final order of segments in the executable file. The sequential or alphabetical order of segments in the source file determines the order in which the assembler writes segments to the object file. The class type can affect the order in which the linker writes segments from object files to the executable file.%@NL@% %@NL@% %@4@% Segments having the same class type are loaded into memory together,%@EH@% regardless of their sequential or alphabetical order in the source file.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% The %@AB@%DOSSEG %@AE@%directive (see Section 5.1.2%@BO: 41df5@%, "Specifying DOS Segment Order") overrides all other factors in determining segment order.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%A_SEG SEGMENT 'SEG_1'%@AE@%%@NL@% %@AS@%A_SEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%B_SEG SEGMENT 'SEG_2'%@AE@%%@NL@% %@AS@%B_SEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%C_SEG SEGMENT 'SEG_1'%@AE@%%@NL@% %@AS@%C_SEG ENDS%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX5.144 @%%@CR:IX5.145 @% When QuickAssembler assembles the preceding program fragment, it writes%@EH@% the segments to the object file in sequential or alphabetical order, depending on whether the /a option or the %@AB@%.ALPHA%@AE@% directive was used. In the example above, the sequential and alphabetical order are the same, so the order will be %@AS@%A_SEG%@AE@%, %@AS@%B_SEG%@AE@%, %@AS@%C_SEG %@AE@%in either case.%@NL@% %@NL@% %@4@% When the linker writes the segments to the executable file, it first%@EH@% checks to see if any segments have the same class type. If they do, it writes them to the executable file together. Thus, %@AS@%A_SEG %@AE@%and %@AS@%C_SEG %@AE@%are placed together because they both have class type %@AS@%'SEG_1'%@AE@%. The final order in memory is %@AS@%A_SEG%@AE@%, %@AS@%C_SEG%@AE@%, %@AS@%B_SEG%@AE@%.%@NL@% %@NL@% %@4@% Since LINK processes modules in the order it receives them on the command%@EH@% line, you may not always be able to easily specify the order in which you want segments to be loaded. For example, assume your program has four segments that you want loaded in the following order: %@AS@%_TEXT%@AE@%, %@AS@%_DATA%@AE@%, %@AS@%CONST%@AE@%, and %@AS@%STACK%@AE@%.%@NL@% %@NL@% %@4@% The %@AS@%_TEXT%@AE@%, %@AS@%CONST%@AE@%, and %@AS@%STACK%@AE@% segments are defined in the first module of%@EH@% your program, but the %@AS@%_DATA%@AE@% segment is defined in the second module. LINK will not put the segments in the proper order because it first loads the segments encountered in the first module.%@NL@% %@NL@% %@4@%%@CR:IX5.146 @% You can avoid this problem by starting your program with dummy segment%@EH@% definitions in the order you wish to load your real segments. The dummy segments can either go at the start of the first module, or they can be placed in a separate include file that is called at the start of the first module. You can then put the actual segment definitions in any order or any module you find convenient.%@NL@% %@NL@% %@4@% For example, you might call the following include file at the start of the%@EH@% first module of your program:%@NL@% %@NL@% %@AS@%_TEXT SEGMENT WORD PUBLIC 'CODE'%@AE@%%@NL@% %@AS@%_TEXT ENDS%@AE@%%@NL@% %@AS@%_DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@AS@%CONST SEGMENT WORD PUBLIC 'CONST'%@AE@%%@NL@% %@AS@%CONST ENDS%@AE@%%@NL@% %@AS@%STACK SEGMENT PARA STACK 'STACK'%@AE@%%@NL@% %@AS@%STACK ENDS%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%DOSSEG%@AE@% directive may be more convenient for defining segment order if%@EH@% you are willing to accept the %@AB@%DOS%@AE@% segment-order conventions.%@NL@% %@NL@% %@4@% Once a segment has been defined, you do not need to specify the align,%@EH@% combine, use, and class types on subsequent definitions. For example, if your code defined dummy segments as shown above, you could define an actual data segment with the following statements:%@NL@% %@NL@% %@AS@%_DATA SEGMENT%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC5.3 @%%@AB@%5.3 Defining Segment Groups%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.147 @%%@CR:IX5.148 @%%@CR:IX5.149 @%%@CR:IX5.150 @% A group is a collection of segments associated with the same starting%@EH@% address. You may wish to use a group if you want several types of data to be organized in separate segments in your source code, but want them all to be accessible from a single, common segment register at run time.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%GROUP%@AE@% %@AI@%segment%@AE@% [[%@AB@%,%@AE@%%@AI@%segment%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% is the symbol assigned to the starting address of the group. All%@EH@% labels and variables defined within the segments of the group are relative to the start of the group, rather than to the start of the segments in which they are defined.%@NL@% %@NL@% %@4@%%@CR:IX5.151 @%%@CR:IX5.152 @%%@CR:IX5.153 @% The %@AI@%segment%@AE@% can be any previously defined segment or a %@AB@%SEG%@AE@% expression (see%@EH@% Section 9.2.4.5%@BO: 80576@%).%@NL@% %@NL@% %@4@% Segments can be added to a group one at a time. For example, you can%@EH@% define and add segments to a group one by one.%@NL@% %@NL@% %@4@% The %@AB@%GROUP%@AE@% directive does not affect the order in which segments of a group%@EH@% are loaded. Loading order depends on each segment's class, or on the order in which object modules are given to the linker.%@NL@% %@NL@% %@4@%%@CR:IX5.154 @% Segments in a group need not be contiguous. Segments that do not belong to%@EH@% the group can be loaded between segments that do. The only restriction is that the distance (in bytes) between the first byte in the first segment of the group and the last byte in the last segment must not exceed 65,535 bytes.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX5.155 @%%@CR:IX5.156 @%%@CR:IX5.157 @% %@AB@%NOTE %@AE@% When the %@AB@%.MODEL %@AE@%directive is used, the offset of a group-relative segment refers to the ending address of the segment, not the beginning. For example, the expression %@AS@%OFFSET STACK%@AE@% evaluates to the end of the stack segment.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX5.158 @%%@CR:IX5.159 @% Group names can be used with the %@AB@%ASSUME%@AE@% directive (discussed in Section%@EH@% 5.4%@BO: 52503@%, "Associating Segments with Registers") and as an operand prefix with the segment-override operator (discussed in Section 9.2.3%@BO: 7e029@%).%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%DGROUP GROUP ASEG,CSEG%@AE@%%@NL@% %@AS@% ASSUME ds:DGROUP%@AE@%%@NL@% %@NL@% %@AS@%ASEG SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%asym .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%ASEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%BSEG SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%bsym .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%BSEG ENDS%@AE@%%@NL@% %@NL@% %@AS@%CSEG SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%csym .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%CSEG ENDS%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX5.160 @%%@CR:IX5.161 @% Figure 5.2 shows the order of the example segments in memory. They are%@EH@% loaded in the order in which they appear in the source code (or in alphabetical order if the %@AB@%.ALPHA%@AE@% directive or /s option is specified).%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 5.3 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% Since %@AS@%ASEG %@AE@%and %@AS@%CSEG %@AE@%are declared part of the same group, they have the%@EH@% same base despite their separation in memory. This means that the symbols %@AS@%asym %@AE@%and %@AS@%csym %@AE@%have offsets from the beginning of the group, which is also the beginning of %@AS@%ASEG%@AE@%. The offset of %@AS@%bsym %@AE@%is from the beginning of %@AS@%BSEG%@AE@%, since it is not part of the group. This sample illustrates the way LINK organizes segments in a group. It is not intended as a typical use of a group.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC5.4 @%%@AB@%5.4 Associating Segments with Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.162 @%%@CR:IX5.163 @%%@CR:IX5.164 @%%@CR:IX5.165 @%%@CR:IX5.166 @%%@CR:IX5.167 @%%@CR:IX5.168 @%%@CR:IX5.169 @%%@CR:IX5.170 @%%@CR:IX5.171 @%%@CR:IX5.172 @% Many of the assembler instructions assume a default segment. For example,%@EH@% %@AB@%JMP%@AE@% instructions assume the segment associated with the CS register; %@AB@%PUSH%@AE@% and %@AB@%POP%@AE@% instructions assume the segment associated with the SS register; %@AB@%MOV%@AE@% instructions assume the segment associated with the DS register.%@NL@% %@NL@% %@4@% When the assembler needs to reference an address, it must know what%@EH@% segment the address is in. It does this by using the default segment or group addresses assigned with the %@AB@%ASSUME%@AE@% directive.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE %@AE@% Using the %@AB@%ASSUME %@AE@%directive to tell the assembler which segment to associate with a segment register is not the same as telling the processor. The %@AB@%ASSUME%@AE@% directive only affects assembly-time assumptions. You may need to use instructions to change run-time assumptions. Initializing segment registers at run time is discussed in Section 5.5%@BO: 53a35@%.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%ASSUME%@AE@% %@AI@%segmentregister%@AE@%:%@AI@%name%@AE@% [[%@AB@%,%@AE@%%@AI@%segmentregister%@AE@%:%@AI@%name%@AE@%]]...%@EH@%%@NL@% %@AB@%ASSUME%@AE@% %@AI@%segmentregister%@AE@%:%@AB@%NOTHING%@AE@%%@NL@% %@AB@%ASSUME NOTHING%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% must be the name of the segment or group that is to be associated%@EH@% with %@AI@%segmentregister%@AE@%. Subsequent instructions that assume a default register for referencing labels or variables automatically assume that if the default segment is %@AI@%segmentregister%@AE@%, the label or variable is in the %@AI@%name%@AE@% segment or group.%@NL@% %@NL@% %@4@% The %@AB@%ASSUME%@AE@% directive can define a segment for each of the segment%@EH@% registers. The %@AI@%segmentregister%@AE@% can be CS, DS, ES, or SS. The %@AI@%name%@AE@% must be one of the following:%@NL@% %@NL@% ■ The name of a segment defined in the source file with the %@AB@%SEGMENT%@AE@% directive%@NL@% %@NL@% ■ The name of a group defined in the source file with the %@AB@%GROUP%@AE@% directive%@NL@% %@NL@% ■ The keyword %@AB@%NOTHING%@AE@%%@NL@% %@NL@% ■ A %@AB@%SEG%@AE@% expression (see Section 9.2.4.5%@BO: 80576@%, "SEG Operator")%@NL@% %@NL@% ■ A string equate that evaluates to a segment or group name (but not a string equate that evaluates to a %@AB@%SEG%@AE@% expression)%@NL@% %@NL@% %@4@% The keyword %@AB@%NOTHING%@AE@% cancels the current segment selection. For example,%@EH@% the statement %@AB@%ASSUME NOTHING%@AE@% cancels all register selections made by previous %@AB@%ASSUME%@AE@% statements.%@NL@% %@NL@% %@4@% Usually, a single %@AB@%ASSUME %@AE@%statement defines all four segment registers at%@EH@% the start of the source file. However, you can use the %@AB@%ASSUME%@AE@% directive at any point to change segment assumptions.%@NL@% %@NL@% %@4@% Using the %@AB@%ASSUME%@AE@% directive to change segment assumptions is often%@EH@% equivalent to changing assumptions with the segment-override operator (%@AB@%:%@AE@%) (see Section 9.2.3%@BO: 7e029@%). The segment-override operator is more convenient for one-time overrides, whereas the %@AB@%ASSUME%@AE@% directive may be more convenient if previous assumptions must be overridden for a sequence of instructions.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL large ; DS automatically assumed to @data%@AE@%%@NL@% %@AS@% .STACK 100h%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%d1 DW 7%@AE@%%@NL@% %@AS@% .FARDATA%@AE@%%@NL@% %@AS@%d2 DW 9%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%start: mov ax,@data ; Initialize near data%@AE@%%@NL@% %@AS@% mov ds,ax%@AE@%%@NL@% %@AS@% mov ax,@fardata ; Initialize far data%@AE@%%@NL@% %@AS@% mov es,ax%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@AS@%; Method 1 for series of instructions that need override%@AE@%%@NL@% %@AS@%; Use segment override for each statement%@AE@%%@NL@% %@NL@% %@AS@% mov ax,es:d2%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov es:d2,bx%@AE@%%@NL@% %@NL@% %@AS@%; Method 2 for series of instructions that need override%@AE@%%@NL@% %@AS@%; Use ASSUME at beginning of series of instructions%@AE@%%@NL@% %@NL@% %@AS@% ASSUME es:@fardata%@AE@%%@NL@% %@AS@% mov cx,d2%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov d2,dx%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC5.5 @%%@AB@%5.5 Initializing Segment Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.173 @%%@CR:IX5.174 @% Assembly-language programs must initialize segment values for each segment%@EH@% register before instructions that reference the segment register can be used in the source program.%@NL@% %@NL@% %@4@% Initializing segment registers is different from assigning default values%@EH@% for segment registers with the %@AB@%ASSUME%@AE@% statement. The %@AB@%ASSUME%@AE@% directive tells the assembler what segments to use at assembly time. Initializing segments gives them an initial value that will be used at run time.%@NL@% %@NL@% %@4@% The %@AB@%.STARTUP%@AE@% directive generates all the initialization code described in%@EH@% this section. This directive must be preceded by the %@AB@%.MODEL%@AE@% directive. If the %@AB@%.MODEL%@AE@% directive was followed by the %@AB@%farStack%@AE@% attribute, %@AB@%.STARTUP%@AE@% does not adjust SS and SP. Otherwise, it assumes the %@AB@%nearStack%@AE@% default, which sets SS equal to DS as described in Section 5.5.3%@BO: 55651@%, "Initializing the SS and SP Registers." When you use this default, the combined stack and near data must not exceed 64K.%@NL@% %@NL@% %@4@% If you use %@AB@%.STARTUP%@AE@%, you don't need to enter any of the code in this%@EH@% section, except for the %@AB@%END%@AE@% statement. (However, if you use %@AB@%.STARTUP%@AE@%, you don't need to specify a starting address.) Make sure that you place the %@AB@%.STARTUP%@AE@% directive at the point you want your program to start executing, because the assembler automatically initializes CS:IP to point to the beginning of the code generated by %@AB@%.STARTUP%@AE@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.5.1 @%%@AB@%5.5.1 Initializing the CS and IP Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.175 @% The CS and IP registers are initialized by specifying a starting address%@EH@% with the %@AB@%END%@AE@% directive.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%END%@AE@% [[%@AI@%startaddress%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.176 @%%@CR:IX5.177 @% The %@AI@%startaddress%@AE@% is a label or expression identifying the address where%@EH@% you want execution to begin when the program is loaded. Normally, a label for the start address should be placed at the address of the first instruction in the code segment.%@NL@% %@NL@% %@4@%%@CR:IX5.178 @%%@CR:IX5.179 @%%@CR:IX5.180 @% The CS segment is initialized to the value of %@AI@%startaddress%@AE@%. The IP%@EH@% register is normally initialized to 0. You can change the initial value of the IP register by using the %@AB@%ORG%@AE@% directive (see Section 6.6%@BO: 67109@%, "Setting the Location Counter") just before the %@AI@%startaddress%@AE@% label. For example, programs in the %@AB@%.COM%@AE@% format use %@AS@%ORG 100h %@AE@%to initialize the IP register to 256 (100 hexadecimal).%@NL@% %@NL@% %@4@% If a program consists of a single source module, the start address is%@EH@% required for that module. If a program has several modules, all modules must terminate with an %@AB@%END%@AE@% directive, but only one of them can define a start address.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%WARNING %@AE@% One, and only one, module must define a start address. If you do not specify a start address, none is assumed. Neither QuickAssembler nor LINK will generate an error message, but your program will probably start execution at the wrong address.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Module 1%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%start: . ; First executable instruction%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% EXTRN task:NEAR%@AE@%%@NL@% %@AS@% call task%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% END start ; Starting address defined in main module%@AE@%%@NL@% %@NL@% %@AS@%; Module 2%@AE@%%@NL@% %@AS@% PUBLIC task%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%task PROC%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%task ENDP%@AE@%%@NL@% %@AS@% END ; No starting address in secondary module%@AE@%%@NL@% %@NL@% %@4@% If %@AS@%Module 1 %@AE@%and %@AS@%Module 2%@AE@% are linked into a single program, it is essential%@EH@% that only the calling module define a starting address.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.5.2 @%%@AB@%5.5.2 Initializing the DS Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.181 @% The DS register must be initialized to the address of the segment that%@EH@% will be used for data.%@NL@% %@NL@% %@4@% The address of the segment or group for the initial data segment must be%@EH@% loaded into the DS register. This is done in two statements because a memory value cannot be loaded directly into a segment register. The segment-setup lines typically appear at the start or very near the start of the code segment.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%_DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@AS@%_TEXT SEGMENT BYTE PUBLIC 'CODE'%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT,ds:_DATA%@AE@%%@NL@% %@AS@%start: mov ax,_DATA ; Load start of data segment%@AE@%%@NL@% %@AS@% mov ds,ax ; Transfer to DS register%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%_TEXT ENDS%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX5.182 @% If you are using the Microsoft naming convention and segment order, the%@EH@% address loaded into the DS register is not a segment address but the address of DGROUP, as shown in Example 2. With simplified segment directives, the address of DGROUP is represented by the predefined equate %@AB@%@data%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL SMALL%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%start: mov ax,@data ; Load start of DGROUP (@data)%@AE@%%@NL@% %@AS@% mov ds,ax ; Transfer to DS register%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.5.3 @%%@AB@%5.5.3 Initializing the SS and SP Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.183 @% At load time, DOS sets SS to the segment address of the last segment%@EH@% having combine type %@AB@%STACK%@AE@%, and SP to the size of the stack. (The linker actually determines the value of SS:SP and places this value in the executable-file header. DOS sets SS and SP as indicated in the file header.)%@NL@% %@NL@% %@4@% If you use a stack segment with combine type %@AB@%STACK%@AE@% or use the %@AB@%.STACK%@AE@%%@EH@% directive, the program automatically loads with SS and SP initialized, as described above.%@NL@% %@NL@% %@4@% However, this basic initialization does not set SS equal to DS. If the%@EH@% program contains the statement %@AS@%ASSUME SS:DGROUP%@AE@%, it will be prone to errors. The following code resets SS and SP so that SS has the same value as DS. The code then adjusts SP upward so that SS:SP points to the same physical address it did before. Since hardware interrupts use the same stack as the program, you should turn off interrupts while changing the stack. Most 8086-family processors turn off interrupts automatically when you adjust SS or SP, but early versions of the 8088 do not.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .STACK 100h ; Initialize "STACK"%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%start: mov ax,@data ; Load segment location%@AE@%%@NL@% %@AS@% mov ds,ax ; into DS register%@AE@%%@NL@% %@AS@% cli ; Turn off interrupts%@AE@%%@NL@% %@AS@% mov ss,ax ; Load same value as DS into SS%@AE@%%@NL@% %@AS@% mov sp,OFFSET STACK ; Give SP new stack size%@AE@%%@NL@% %@AS@% sti ; Turn interrupts back on%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@4@% This example reinitializes SS so that it has the same value as DS, and it%@EH@% adjusts SP to reflect the new stack offset. Microsoft high-level-language compilers do this so that stack variables in near procedures can be accessed relative to either SS or DS.%@NL@% %@NL@% %@4@%%@CR:IX5.184 @%%@CR:IX5.185 @% However, this code only works correctly if you use %@AB@%.MODEL%@AE@% and you declare%@EH@% a stack segment in just one module. The following code handles the more general case. The %@AB@%.STARTUP%@AE@% directive generates this code:%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%start_label:%@AE@%%@NL@% %@AS@% mov dx,DGROUP ; Move DGROUP into DS and DX%@AE@%%@NL@% %@AS@% mov ds,dx%@AE@%%@NL@% %@AS@% mov bx,ss ; BX = STACK - DGROUP%@AE@%%@NL@% %@AS@% sub bx,dx ;%@AE@%%@NL@% %@AS@% shl bx,1 ; Multiply difference by 16%@AE@%%@NL@% %@AS@% shl bx,1 ; and leave result in BX%@AE@%%@NL@% %@AS@% shl bx,1%@AE@%%@NL@% %@AS@% shl bx,1%@AE@%%@NL@% %@AS@% cli%@AE@%%@NL@% %@AS@% mov ss,dx ; Move DGROUP into SS%@AE@%%@NL@% %@AS@% add sp,bx ; Adjust SP upward by%@AE@%%@NL@% %@AS@% sti ; (STACK - DGROUP) * 16%@AE@%%@NL@% %@NL@% %@4@% The code above sets SS and SP so that SS equals DS. This code works%@EH@% correctly no matter how many modules declare a stack segment.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC5.5.4 @%%@AB@%5.5.4 Initializing the ES Register%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The ES register is not automatically initialized. If your program uses the%@EH@% ES register, you must initialize it by moving the appropriate segment value into the register.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% ASSUME es:@fardata ; Tell the assembler%@AE@%%@NL@% %@AS@% mov ax,@fardata ; Tell the processor%@AE@%%@NL@% %@AS@% mov es,ax%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC5.6 @%%@AB@%5.6 Nesting Segments%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX5.186 @%%@CR:IX5.187 @% Segments can be nested. When QuickAssembler encounters a nested segment,%@EH@% it temporarily suspends assembly of the enclosing segment and begins assembly of the nested segment. When the nested segment has been assembled, Quick-Assembler continues assembly of the enclosing segment.%@NL@% %@NL@% %@4@% Nesting of segments makes it possible to mix segment definitions in%@EH@% programs that use simplified segment directives for most segment definitions. When a full segment definition is given, the new segment is nested in the simplified segment in which it is defined.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Macro to print message on the screen%@AE@%%@NL@% %@AS@%; Uses full segment definitions - segments nested%@AE@%%@NL@% %@NL@% %@AS@%message MACRO text%@AE@%%@NL@% %@AS@% LOCAL symbol%@AE@%%@NL@% %@AS@%_DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@%symbol DB &text%@AE@%%@NL@% %@AS@% DB 13,10,"$"%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@AS@% mov ah,09h%@AE@%%@NL@% %@AS@% mov dx,OFFSET symbol%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@%_TEXT SEGMENT BYTE PUBLIC 'CODE'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% message "Please insert disk"%@AE@%%@NL@% %@NL@% %@4@% In the example above, a macro called from inside of the code segment%@EH@% (%@AS@%_TEXT%@AE@%) allocates a variable within a nested data segment (%@AS@%_DATA%@AE@%). This has the effect of allocating more data space on the end of the data segment each time the macro is called. The macro can be used for messages appearing only once in the source code.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Macro to print message on the screen%@AE@%%@NL@% %@AS@%; Uses simplified segment directives - segments not nested%@AE@%%@NL@% %@NL@% %@AS@%message MACRO text%@AE@%%@NL@% %@AS@% LOCAL symbol%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%symbol DB &text%@AE@%%@NL@% %@AS@% DB 13,10,"$"%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% mov ah,09h%@AE@%%@NL@% %@AS@% mov dx,OFFSET symbol%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% message "Please insert disk"%@AE@%%@NL@% %@NL@% %@4@% Although Example 2 has the same practical effect as Example 1,%@EH@% Quick-Assembler handles the two macros differently. In Example 1, assembly of the outer (code) segment is suspended rather than terminated. In Example 2, assembly of the code segment terminates, assembly of the data segment starts and terminates, and then assembly of the code segment is restarted.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH6 @%%@AB@%Chapter 6: Defining Constants, Labels, and Variables%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% This chapter explains how to define constants, labels, variables, and%@EH@% other symbols that refer to instruction and data locations within segments.%@NL@% %@NL@% %@4@% Constants are important in QuickAssembler, just as they are in other%@EH@% languages. You can use constants as immediate operands in instructions and as initial values in data declarations. QuickAssembler supports a number of useful radixes (including binary and hexadecimal), as described in Section 6.1%@BO: 57adf@%.%@NL@% %@NL@% %@4@% QuickAssembler lets you use symbols as well as constants. Sections 6.2%@BO: 5b018@%,%@EH@% "Assigning Names to Symbols," and 6.3%@BO: 5b018@%, "Using Type Specifiers," present the basic principles of generating symbolic names.%@NL@% %@NL@% %@4@% Most symbols are either code labels or variable names. Section 6.4%@BO: 5d761@%,%@EH@% "Defining Code Labels," and Section 6.5%@BO: 5ff03@%, "Defining and Initializing Data," describe how to define these symbols.%@NL@% %@NL@% %@4@% This chapter tells you how to assign labels and most kinds of variables.%@EH@% (Multifield variables, such as structures and records, are discussed in Chapter 7%@BO: 69661@%, "Using Structures and Records.") Chapter 6%@BO: 5749b@% also discusses related directives, including those that control the location counter directly. The assembler uses the location counter to assign addresses to symbols.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.1 @%%@AB@%6.1 Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.1 @% Constants can be used in source files to specify numbers or strings that%@EH@% are set or initialized at assembly time. The assembler recognizes four types of constant values:%@NL@% %@NL@% 1. Integers%@NL@% %@NL@% 2. Packed binary coded decimals%@NL@% %@NL@% 3. Real numbers%@NL@% %@NL@% 4. Strings%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.1 @%%@AB@%6.1.1 Integer Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.2 @%%@CR:IX6.3 @% Integer constants represent integer values. They can be used in a variety%@EH@% of contexts in assembly-language source code. For example, they can be used in data declarations and equates, or as immediate operands.%@NL@% %@NL@% %@4@%%@CR:IX6.4 @%%@CR:IX6.5 @% Packed decimal integers are a special kind of integer constant that can%@EH@% only be used to initialize binary coded decimal (BCD) variables. They are described in Sections 6.1.2%@BO: 59313@%, "Packed Binary Coded Decimal Constants," and 6.5.1.2%@BO: 59313@%, "Binary Coded Decimal Variables."%@NL@% %@NL@% %@4@% Integer constants can be specified in binary, octal, decimal, or%@EH@% hexadecimal values. Table 6.1 shows the legal digits for each of these radixes. For hexadecimal radix, the digits can be either uppercase or lowercase letters.%@NL@% %@NL@% %@AB@%Table 6.1 Digits Used with Each Radix%@AE@%%@NL@% %@NL@% %@AB@%Radix%@AE@% %@AB@%Base%@AE@% %@AB@%Digits%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Binary 2 0 1%@NL@% %@NL@% Octal 8 0 1 2 3 4 5 6 7%@NL@% %@NL@% Decimal 10 0 1 2 3 4 5 6 7 8 9%@NL@% %@NL@% Hexadecimal 16 0 1 2 3 4 5 6 7 8 9 A B C D E F%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% The radix for an integer can be defined for a specific integer by using%@EH@% radix specifiers, or a default radix can be defined globally with the %@AB@%.RADIX%@AE@% directive.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.1.1 @%%@AB@%6.1.1.1 Specifying Integers with Radix Specifiers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.6 @%%@CR:IX6.7 @%%@CR:IX6.8 @%%@CR:IX6.9 @%%@CR:IX6.10 @%%@CR:IX6.11 @% The radix for an integer constant can be given by putting one of the%@EH@% following radix specifiers after the last digit of the number:%@NL@% %@NL@% %@AB@%Radix%@AE@% %@AB@%Specifier%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Binary B%@NL@% %@NL@% Octal Q or O%@NL@% %@NL@% Decimal D%@NL@% %@NL@% Hexadecimal H%@NL@% %@NL@% %@NL@% %@4@% Radix specifiers can be given in either uppercase or lowercase letters;%@EH@% sample code in this manual uses lowercase letters.%@NL@% %@NL@% %@4@%%@CR:IX6.12 @% Hexadecimal numbers must always start with a decimal digit (0-9). If%@EH@% necessary, put a leading 0 at the left of the number to distinguish between symbols and hexadecimal numbers that start with a letter. For example, %@AS@%0ABCh %@AE@%is interpreted as a hexadecimal number, but %@AS@%ABCh %@AE@%is interpreted as a symbol. The hexadecimal digits A through F can be either uppercase or lowercase letters. Sample code in this manual uses uppercase letters.%@NL@% %@NL@% %@4@% If no radix is given, the assembler interprets the integer by using the%@EH@% current default radix. The initial default radix is decimal, but you can change the default with the %@AB@%.RADIX%@AE@% directive.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%n360 EQU 01011010b + 132q + 5Ah + 90d ; 4 * 90%@AE@%%@NL@% %@AS@%n60 EQU 00001111b + 17o + 0Fh + 15d ; 4 * 15%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.1.2 @%%@AB@%6.1.1.2 Setting the Default Radix%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.13 @%%@CR:IX6.14 @%%@CR:IX6.15 @%%@CR:IX6.16 @%%@CR:IX6.17 @%%@CR:IX6.18 @%%@CR:IX6.19 @%%@CR:IX6.20 @%%@CR:IX6.21 @% The %@AB@%.RADIX%@AE@% directive sets the default radix for integer constants in the%@EH@% source file.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% .%@AB@%RADIX%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%expression%@AE@% must evaluate to a number in the range 2-16. It defines%@EH@% whether the numbers are binary, octal, decimal, hexadecimal, or numbers of some other base.%@NL@% %@NL@% %@4@% Numbers given in %@AI@%expression%@AE@% are always considered decimal, regardless of%@EH@% the current default radix. The initial default radix is decimal.%@NL@% %@NL@% %@4@% Note that the %@AB@%.RADIX%@AE@% directive does not affect real numbers initialized as%@EH@% variables with the %@AB@%DD%@AE@%, %@AB@%DQ%@AE@%, or %@AB@%DT%@AE@% directive. Initial values for real-number variables declared with these directives are always evaluated as decimal unless a radix specifier is appended.%@NL@% %@NL@% %@4@% Also, the %@AB@%.RADIX %@AE@%directive does not affect the optional radix specifiers,%@EH@% B and D, used with integer numbers. When the letters B or D appear at the end of any integer, they are always considered to be a radix specifier even if the current radix is 16.%@NL@% %@NL@% %@4@% For example, if the input radix is 16, the number %@AS@%0ABCD %@AE@%will be%@EH@% interpreted as 0ABC decimal, an illegal number, instead of as 0ABCD hexadecimal, as intended. Type %@AS@%0ABCDh %@AE@%to specify 0ABCD in hexadecimal. Similarly, the number %@AS@%11B %@AE@%will be treated as 11 binary, a legal number, but not as 11B hexadecimal as intended. Type %@AS@%11Bh %@AE@%to specify 11B in hexadecimal.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .RADIX 16 ; Set default radix to hexadecimal%@AE@%%@NL@% %@AS@% .RADIX 2 ; Set default radix to binary%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.2 @%%@AB@%6.1.2 Packed Binary Coded Decimal Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.22 @%%@CR:IX6.23 @%%@CR:IX6.24 @% When an integer constant is used with the%@AB@% DT%@AE@% directive, the number is%@EH@% interpreted by default as a packed binary coded decimal (BCD) number. You can use the D radix specifier to override the default and initialize 10-byte integers as binary-format integers.%@NL@% %@NL@% %@4@% The syntax for specifying binary coded decimals is exactly the same as for%@EH@% other integers. However, the assembler encodes binary coded decimals in a completely different way. See Section 6.5.1.2%@BO: 61d5d@%, "Binary Coded Decimal Variables," for complete information on storage of binary coded decimals.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%positive DT 1234567890 ; Encoded as 00000000001234567890h%@AE@%%@NL@% %@AS@%negative DT -1234567890 ; Encoded as 80000000001234567890h%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.3 @%%@AB@%6.1.3 Real-Number Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.25 @%%@CR:IX6.26 @% A real number is a number consisting of an integer part, a fractional%@EH@% part, and an exponent. Real numbers are usually represented in decimal format.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AB@%+%@AE@% | %@AB@%-%@AE@%]] %@AI@%integer%@AE@%%@AB@%.%@AE@%%@AI@%fraction%@AE@%[[%@AB@%E%@AE@%[[%@AB@%+%@AE@% | %@AB@%-%@AE@%]]%@AI@%exponent%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.27 @%%@CR:IX6.28 @% The %@AI@%integer%@AE@% and %@AI@%fraction%@AE@% parts combine to form the value of the number.%@EH@% This value is stored internally as a unit and is called the mantissa. It may be signed. The optional %@AI@%exponent%@AE@% follows the exponent indicator (E). It represents the magnitude of the value and is stored internally as a unit. If no exponent is given, 1 is assumed. If an exponent is given, it may be signed.%@NL@% %@NL@% %@4@%%@CR:IX6.29 @%%@CR:IX6.30 @% During assembly, the assembler converts real-number constants given in%@EH@% decimal format to a binary format. The sign, exponent, and mantissa of the real number are encoded as bit fields within the number. See Section 6.5.1.4%@BO: 62baf@%, "Real-Number Variables," for an explanation of how real numbers are encoded.%@NL@% %@NL@% %@4@% You can specify the encoded format directly using hexadecimal digits (0-9%@EH@% or A-F). The number must begin with a decimal digit (0-9) and cannot be signed. It must be followed by the real-number designator (R). This designator is used the same as a radix designator except it specifies that the given hexadecimal number should be interpreted as a real number.%@NL@% %@NL@% %@4@% Real numbers can only be used to initialize variables with the %@AB@%DD%@AE@%, %@AB@%DQ%@AE@%, and%@EH@% %@AB@%DT%@AE@% directives. They cannot be used in expressions. The maximum number of digits in the number and the maximum range of exponent values depend on the directive. The number of digits for encoded numbers used with%@AB@% DD%@AE@%, %@AB@%DQ%@AE@%, and %@AB@%DT%@AE@% must be 8, 16, and 20 digits, respectively. (If a leading 0 is supplied, the number must be 9, 17, or 21 digits.) See Section 6.5.1.4%@BO: 62baf@%, "Real-Number Variables," for an explanation of how real numbers are encoded.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX6.31 @%%@CR:IX6.32 @% %@AB@%NOTE%@AE@% Real numbers will be encoded differently depending upon whether you use the %@AB@%.MSFLOAT %@AE@%directive. By default, real numbers are encoded in the IEEE format. The %@AB@%.MSFLOAT %@AE@%directive overrides the default and specifies Microsoft Binary format. See Section 6.5.1.4%@BO: 62baf@%, "Real-Number Variables," for a description of these formats.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Real numbers%@AE@%%@NL@% %@AS@%shrt DD 25.23%@AE@%%@NL@% %@AS@%long DQ 2.523E1%@AE@%%@NL@% %@AS@%ten_byte DT 2523.0E-2%@AE@%%@NL@% %@NL@% %@AS@%; Assumes .MSFLOAT%@AE@%%@NL@% %@AS@%mbshort DD 81000000r ; 1.0 as Microsoft Binary short%@AE@%%@NL@% %@AS@%mblong DQ 8100000000000000r ; 1.0 as Microsoft Binary long%@AE@%%@NL@% %@NL@% %@AS@%; Assumes default IEEE format%@AE@%%@NL@% %@AS@%ieeeshort DD 3F800000r ; 1.0 as IEEE short%@AE@%%@NL@% %@AS@%ieeelong DQ 3FF0000000000000r ; 1.0 as IEEE long%@AE@%%@NL@% %@NL@% %@AS@%; The same regardless of processor directives%@AE@%%@NL@% %@AS@%temporary DT 3FFF8000000000000000r ; 1.0 as 10-byte temporary real%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.4 @%%@AB@%6.1.4 String Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.33 @%%@CR:IX6.34 @%%@CR:IX6.35 @% A string constant consists of one or more ASCII characters enclosed in%@EH@% single or double quotation marks. Strings are interpreted as lists of characters having the ASCII values of the characters in the string.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%'%@AE@%%@AI@%characters%@AE@%%@AB@%'%@AE@%%@EH@%%@NL@% %@AB@%"%@AE@%%@AI@%characters%@AE@%%@AB@%"%@AE@%%@NL@% %@NL@% %@4@% String constants are case sensitive. A string constant consisting of a%@EH@% single character is sometimes called a character constant.%@NL@% %@NL@% %@4@% Single quotation marks must be encoded twice when used literally within%@EH@% string constants that are also enclosed by single quotation marks. Similarly, double quotation marks must be encoded twice when used in string constants that are also enclosed by double quotation marks.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%char DB 'a'%@AE@%%@NL@% %@AS@%char2 DB "a"%@AE@%%@NL@% %@AS@%message DB "This is a message."%@AE@%%@NL@% %@AS@%warn DB 'Can"t find file.' ; Can't find file.%@AE@%%@NL@% %@AS@%warn2 DB "Can't find file." ; Can't find file.%@AE@%%@NL@% %@AS@%string DB "This ""value"" not found." ; This "value" not found.%@AE@%%@NL@% %@AS@%string2 DB 'This "value" not found.' ; This "value" not found.%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.1.5 @%%@AB@%6.1.5 Determining Floating-Point Format%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.36 @%%@CR:IX6.37 @% The %@AB@%.MSFLOAT%@AE@% directive disables all coprocessor instructions and specifies%@EH@% that initialized real-number variables be encoded in the Microsoft Binary format. Without this directive, initialized real-number variables are encoded in the IEEE format. This is a change from Versions 4.0 and earlier of the Microsoft Macro Assembler, which used Microsoft Binary format by default and required a coprocessor directive or the /R option to specify IEEE format. %@AB@%.MSFLOAT %@AE@%must be used for programs that require real-number data in the Microsoft Binary format. Section 6.5.1.4%@BO: 62baf@%, "Real-Number Variables," describes real-number data formats and the factors to consider in choosing a format.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.2 @%%@AB@%6.2 Assigning Names to Symbols%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.38 @% A symbol is a name that represents a value. Symbols are one of the most%@EH@% important elements of assembly-language programs. Elements that must be represented symbolically in assembly-language source code include variables, address labels, macros, segments, procedures, records, and structures. Constants, expressions, and strings can also be represented symbolically.%@NL@% %@NL@% %@4@%%@CR:IX6.39 @%%@CR:IX6.40 @% Symbol names are combinations of letters (both uppercase and lowercase),%@EH@% digits, and special characters. The QuickAssembler recognizes the following character set:%@NL@% %@NL@% %@4@% %@AS@%A-Z a-z 0-9%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AS@%? @ _ $ : . [ ] ( ) < > { } + - / *%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AS@%& % ! ' ~ | \ = # ^ ; , ` "%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Letters, digits, and some characters can be used in symbol names, but some%@EH@% restrictions on how certain characters can be used or combined are listed below:%@NL@% %@NL@% ■ A name can have any combination of uppercase and lowercase letters. Within the QC integrated environment, the default behavior (Preserve Extrn) is for the assembler to convert all symbol names to uppercase unless they are public or external. When you use simplified segment directives, all procedure labels declared with %@AB@%PROC%@AE@% are automatically public.%@NL@% %@NL@% When you use QCL, all lowercase letters are converted to uppercase by the assembler, unless you give the /Cl assembly option, or you declare the name with a %@AB@%PROC%@AE@%, %@AB@%PUBLIC%@AE@%, or %@AB@%EXTRN%@AE@% directive and you give the /Cx option. The /Cl and /Cx options correspond to the assembler flags Preserve Case and Preserve Extrn, respectively, within the QC environment.%@NL@% %@NL@% ■ Digits may be used within a name, but not as the first character.%@NL@% %@NL@% ■ A name can be given any number of characters, but only the first 31 are used. All other characters are ignored.%@NL@% %@NL@% %@CR:IX6.41 @%%@CR:IX6.42 @%%@CR:IX6.43 @%%@CR:IX6.44 @%%@CR:IX6.45 @%%@CR:IX6.46 @%%@CR:IX6.47 @% ■ The following characters may be used at the beginning of a name or within a name: underscore (_), question mark (?), dollar sign ($), and at sign (@).%@NL@% %@NL@% %@CR:IX6.48 @%%@CR:IX6.49 @% ■ The period (.) is an operator and cannot be used within a name, but it can be used as the first character of a name.%@NL@% %@NL@% ■ A name may not be the same as any reserved name. Note that two special characters, the question mark (?) and the dollar sign ($), are reserved names and therefore can't stand alone as symbol names.%@NL@% %@NL@% %@4@%%@CR:IX6.50 @%%@CR:IX6.51 @% A reserved name is any name with a special, predefined meaning to the%@EH@% assembler. Reserved names include instruction and directive mnemonics, register names, and operator names. All uppercase and lowercase letter combinations of these names are treated as the same name.%@NL@% %@NL@% %@4@% The following is a list of names that are always reserved by the%@EH@% assembler. Using any of these names for a symbol results in an error.%@NL@% %@NL@% %@4@%%@AB@% $ DWORD GE %OUT%@AE@%%@EH@%%@NL@% %@4@%%@AB@% * ELSE GROUP PAGE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% + ELSEIF GT PROC%@AE@%%@EH@%%@NL@% %@4@%%@AB@% - ELSEIF1 HIGH PTR%@AE@%%@EH@%%@NL@% %@4@%%@AB@% . ELSEIF2 IF PUBLIC%@AE@%%@EH@%%@NL@% %@4@%%@AB@% / ELSEIFB IF1 PURGE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% = ELSEIFDEF IF2 QWORD%@AE@%%@EH@%%@NL@% %@4@%%@AB@% ? ELSEIFDIF IFB .RADIX%@AE@%%@EH@%%@NL@% %@4@%%@AB@% [] ELSEIFDIFI IFDEF RECORD%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .186 ELSEIFE IFDIF REPT%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .286 ELSEIFIDN IFE .SALL%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .286P ELSEIFIDNI IFIDN SEG%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .287 ELSEIFNB IFNB SEGMENT%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .386 ELSEIFNDEF IFNDEF .SEQ%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .386P END INCLUDE .SFCOND%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .387 ENDIF INCLUDELIB SHL%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .8086 ENDM IRP SHORT%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .8087 ENDP IRPC SHR%@AE@%%@EH@%%@NL@% %@4@%%@AB@% ALIGN ENDS LABEL SIZE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .ALPHA EQ .LALL SIZESTR%@AE@%%@EH@%%@NL@% %@4@%%@AB@% AND EQU LE .STACK%@AE@%%@EH@%%@NL@% %@4@%%@AB@% ASSUME .ERR LENGTH .STARTUP%@AE@%%@EH@%%@NL@% %@4@%%@AB@% BYTE .ERR1 .LFCOND STRUC%@AE@%%@EH@%%@NL@% %@4@%%@AB@% CATSTR .ERR2 .LIST SUBSTR%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .CODE .ERRB LOCAL SUBTTL%@AE@%%@EH@%%@NL@% %@4@%%@AB@% COMM .ERRDEF LOW TBYTE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% COMMENT .ERRDIF LT .TFCOND%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .CONST .ERRE MACRO THIS%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .CREF .ERRIDN MASK TITLE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .DATA .ERRNB MOD TYPE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% .DATA? .ERRNDEF .MODEL .TYPE%@AE@%%@EH@%%@NL@% %@4@%%@AB@% DB .ERRNZ NAME WIDTH%@AE@%%@EH@%%@NL@% %@4@%%@AB@% DD EVEN NE WORD%@AE@%%@EH@%%@NL@% %@4@%%@AB@% DOSSEG EXITM NEAR .XALL%@AE@%%@EH@%%@NL@% %@4@%%@AB@% DQ EXTRN NOT .XCREF%@AE@%%@EH@%%@NL@% %@4@%%@AB@% DS FAR OFFSET .XLIST%@AE@%%@EH@%%@NL@% %@4@%%@AB@% DT .FARDATA OR XOR%@AE@%%@EH@%%@NL@% %@NL@% %@4@% In addition to the names listed above, instruction mnemonics and register%@EH@% names are considered reserved names. Instructions can vary depending on the processor directives given in the source file. For example,%@AB@% ENTER%@AE@% is recognized as a reserved word if you have enabled 286 instructions with the %@AB@%.286%@AE@% directive. Section 18.3%@BO: ebd6e@% describes processor directives. Instruction mnemonics for each processor are listed in the on-line Help system. Register names are listed in Section 2.6.2%@BO: 25a37@%, "Register Operands."%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.3 @%%@AB@%6.3 Using Type Specifiers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.52 @% Some statements require type specifiers to give the size or type of an%@EH@% operand. There are two kinds of type specifiers: those that specify the size of a variable or other memory operand, and those that specify the distance of a label.%@NL@% %@NL@% %@4@%%@CR:IX6.53 @%%@CR:IX6.54 @%%@CR:IX6.55 @%%@CR:IX6.56 @%%@CR:IX6.57 @%%@CR:IX6.58 @%%@CR:IX6.59 @% The type specifiers that give the size of a memory operand are listed%@EH@% below with the number of bytes specified by each:%@NL@% %@NL@% %@AB@%Specifier%@AE@% %@AB@%Number of Bytes%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%BYTE%@AE@% 1%@NL@% %@NL@% %@AB@%WORD%@AE@% 2%@NL@% %@NL@% %@AB@%DWORD%@AE@% 4%@NL@% %@NL@% %@AB@%QWORD%@AE@% 8%@NL@% %@NL@% %@AB@%TBYTE%@AE@% 10%@NL@% %@NL@% %@NL@% %@4@% In some contexts, %@AB@%ABS%@AE@% can also be used as a type specifier that indicates%@EH@% an operand is a constant rather than a memory operand.%@NL@% %@NL@% %@4@%%@CR:IX6.60 @%%@CR:IX6.61 @%%@CR:IX6.62 @% The type specifiers that give the distance of a label are listed below:%@EH@%%@NL@% %@NL@% %@AB@%Specifier%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%FAR%@AE@% The label references both the segment and offset of the label.%@NL@% %@NL@% %@AB@%NEAR%@AE@% The label references only the offset of the label.%@NL@% %@NL@% %@AB@%PROC%@AE@% The label has the default type (%@AB@%NEAR%@AE@% or%@AB@% FAR%@AE@%) of the current memory model. The default size is always %@AB@%NEAR%@AE@% if you use full segment definitions. If you use simplified segment directives (see Section 5.1%@BO: 406ed@%), the default type is%@AB@% NEAR%@AE@% for small and compact models or %@AB@%FAR%@AE@% for medium, large, and huge models.%@NL@% %@NL@% %@NL@% %@4@% Directives that use type specifiers include %@AB@%LABEL%@AE@%, %@AB@%PROC%@AE@%, %@AB@%EXTRN%@AE@%, and %@AB@%COMM%@AE@%.%@EH@% Operators that use type specifiers include %@AB@%PTR%@AE@% and %@AB@%THIS%@AE@%.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.4 @%%@AB@%6.4 Defining Code Labels%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Code labels give symbolic names to the addresses of instructions in the%@EH@% code segment. These labels can be used as the operands to jump, call, and loop instructions to transfer program control to a new instruction.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.4.1 @%%@AB@%6.4.1 Near-Code Labels%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.63 @% Near-label definitions create instruction labels that have %@AB@%NEAR%@AE@% type.%@EH@% These instruction labels can be used to access the address of the label from other statements.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@%%@AB@%:%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% must be followed by a colon (%@AB@%:%@AE@%). The segment containing the%@EH@% definition must be the one that the assembler currently associates with the CS register. The %@AB@%ASSUME%@AE@% directive is used to associate a segment with a segment register (see Section 5.4%@BO: 52503@%, "Associating Segments with Registers"). A near label can appear on a line by itself or on a line with an instruction.%@NL@% %@NL@% %@4@% Near-code labels have different behavior depending on whether they are%@EH@% used in a procedure with the extended %@AB@%PROC%@AE@% syntax. When the extended %@AB@%PROC%@AE@% feature is used (which requires that %@AB@%.MODEL%@AE@% and a language must be specified), near labels are local to the procedure. This functionality is explained in Section 15.3.7%@BO: cc4c8@%, "Variable Scope."%@NL@% %@NL@% %@4@%%@CR:IX6.64 @%%@CR:IX6.65 @%%@CR:IX6.66 @%%@CR:IX6.67 @%%@CR:IX6.68 @% If the full segments are used or if the language argument is not supplied%@EH@% to the %@AB@%.MODEL%@AE@% directive, near labels are known throughout the module in which they occur. The same label name can be used in different modules as long as each label is only referenced by instructions in its own module. If a label must be referenced by instructions in another module, it must be given a unique name and declared with the %@AB@%PUBLIC%@AE@% and %@AB@%EXTRN%@AE@% directives, as described in Chapter 8%@BO: 70f6e@%, "Creating Programs from Multiple Modules."%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% cmp ax,5 ; Compare with 5%@AE@%%@NL@% %@AS@% ja bigger%@AE@%%@NL@% %@AS@% jb smaller%@AE@%%@NL@% %@AS@% . ; Instructions if AX = 5%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% jmp done%@AE@%%@NL@% %@AS@%bigger: . ; Instructions if AX > 5%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% jmp done%@AE@%%@NL@% %@AS@%smaller: . ; Instructions if AX < 5%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%done:%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.4.2 @%%@AB@%6.4.2 Anonymous Labels%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The assembler provides a way to generate automatic labels for jump%@EH@% instructions. To define a label, use two at signs (@@) followed by a colon (:). To jump to the nearest preceding anonymous label, use @B (back) in the jump instruction's operand field; to jump to the nearest following anonymous label, use @F (forward) in the operand field.%@NL@% %@NL@% %@4@% You can use two at signs (@@) to define any number of anonymous labels in%@EH@% your program. The items @B and @F always refer to the nearest occurrences of @@, so there is never any conflict between different anonymous labels.%@NL@% %@NL@% %@4@% Anonymous labels are best used for conditionally executing a few lines of%@EH@% code. The advantage is that you do not need to continually think up new label names. The disadvantage is that they do not provide a meaningful name. You should mark major divisions of a program with actual named labels.%@NL@% %@NL@% %@4@% The following example shows a typical sequence of code with a%@EH@% jump-to-label instruction:%@NL@% %@NL@% %@AS@%; DX is 20, unless CX is less than -20, then make DX 30%@AE@%%@NL@% %@AS@% mov dx,20%@AE@%%@NL@% %@AS@% cmp cx,-20%@AE@%%@NL@% %@AS@% jge greatequ%@AE@%%@NL@% %@AS@% mov dx,30%@AE@%%@NL@% %@AS@%greatequ:%@AE@%%@NL@% %@NL@% %@4@% Here are the same lines rewritten to use an anonymous label:%@EH@%%@NL@% %@NL@% %@AS@%; DX is 20, unless CX is less than -20, then make DX 30%@AE@%%@NL@% %@AS@% mov dx,20%@AE@%%@NL@% %@AS@% cmp cx,-20%@AE@%%@NL@% %@AS@% jge @F%@AE@%%@NL@% %@AS@% mov dx,30%@AE@%%@NL@% %@AS@%@@:%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.4.3 @%%@AB@%6.4.3 Procedure Labels%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.69 @%%@CR:IX6.70 @%%@CR:IX6.71 @%%@CR:IX6.72 @% The easiest way to declare a procedure is to use the %@AB@%PROC%@AE@% and %@AB@%ENDP%@AE@%%@EH@% directives. The former declares the beginning of the procedure, and the latter declares the end.%@NL@% %@NL@% %@4@% The%@AB@% PROC%@AE@% directive has the following syntax:%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@% %@AB@%PROC%@AE@% [[%@AB@%NEAR%@AE@%|%@AB@%FAR%@AE@%]]%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%RET%@AE@% [[%@AI@%constant%@AE@%]]%@NL@% %@AI@%label%@AE@% ENDP%@NL@% %@NL@% %@4@%%@CR:IX6.73 @%%@CR:IX6.74 @% The %@AI@%label%@AE@% assigns a symbol to the procedure. The distance can be %@AB@%NEAR%@AE@% or%@EH@% %@AB@%FAR%@AE@%. Any %@AB@%RET%@AE@% instructions within the procedure automatically have the same distance (%@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%) as the procedure.%@NL@% %@NL@% %@4@% The syntax shown here is always available. In addition, there is an%@EH@% extended %@AB@%PROC%@AE@% syntax available if you use %@AB@%.MODEL%@AE@% and specify a language. The extended %@AB@%PROC%@AE@% syntax is explained in Section 15.3.4%@BO: c7284@%, "Declaring Parameters with the PROC Directive."%@NL@% %@NL@% %@4@%%@CR:IX6.75 @%%@CR:IX6.76 @%%@CR:IX6.77 @% The %@AB@%ENDP%@AE@% directive labels the address where the procedure ends. Every%@EH@% procedure label must have a matching %@AB@%ENDP%@AE@% label to mark the end of the procedure. QuickAssembler generates an error message if it does not find an %@AB@%ENDP%@AE@% directive to match each %@AB@%PROC%@AE@% directive.%@NL@% %@NL@% %@4@% When the %@AB@%PROC%@AE@% label definition is encountered, the assembler sets the%@EH@% label's value to the current value of the location counter and sets its type to %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%. If the label has %@AB@%FAR%@AE@% type, the assembler also sets its segment value to that of the enclosing segment. If you have specified full segment definitions, the default distance is %@AB@%NEAR%@AE@%. If you are using simplified segment directives, the default distance is the distance associated with the declared memory model──that is, %@AB@%NEAR%@AE@% for small and compact models or %@AB@%FAR%@AE@% for medium, large, and huge models.%@NL@% %@NL@% %@4@%%@CR:IX6.78 @%%@CR:IX6.79 @% The procedure label can be used in a %@AB@%CALL%@AE@% instruction to direct execution%@EH@% control to the first instruction of the procedure. Control can be transferred to a %@AB@%NEAR%@AE@% procedure label from any address in the same segment as the label. Control can be transferred to a %@AB@%FAR%@AE@% procedure label from an address in any segment.%@NL@% %@NL@% %@4@%%@CR:IX6.80 @% Procedure labels must be declared with the %@AB@%PUBLIC%@AE@% and %@AB@%EXTRN%@AE@% directives if%@EH@% they are located in one module but called from another module, as described in Chapter 8%@BO: 70f6e@%, "Creating Programs from Multiple Modules."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% call task ; Call procedure%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%task PROC NEAR ; Start of procedure%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%task ENDP ; End of procedure%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.4.4 @%%@AB@%6.4.4 Code Labels Defined with the LABEL Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.81 @%%@CR:IX6.82 @% The %@AB@%LABEL%@AE@% directive provides an alternative method of defining code%@EH@% labels.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%LABEL%@AE@% %@AI@%distance%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% is the symbol name assigned to the label. The %@AI@%distance%@AE@% can be a%@EH@% type specifier, such as %@AB@%NEAR%@AE@%, %@AB@%FAR%@AE@%, or %@AB@%PROC%@AE@%. %@AB@%PROC%@AE@% means %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%, depending on the default memory model, as described in Section 5.1.3%@BO: 42b44@%, "Defining Basic Attributes of the Module." You can use the %@AB@%LABEL%@AE@% directive to define a second entry point into a procedure. %@AB@%FAR%@AE@% code labels can also be the destination of far jumps or of far calls that use the %@AB@%RETF%@AE@% instruction (see Section 15.3.2%@BO: c4c26@%, "Defining Procedures").%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%task PROC FAR ; Main entry point%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%task1 LABEL FAR ; Secondary entry point%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%task ENDP ; End of procedure%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.5 @%%@AB@%6.5 Defining and Initializing Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.83 @% The data-definition directives enable you to allocate memory for data. At%@EH@% the same time, you can specify the initial values for the allocated data. Data can be specified as numbers, strings, or expressions that evaluate to constants. The assembler translates these constant values into binary bytes, words, or other units of data. The encoded data is written to the object file at assembly time.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.1 @%%@AB@%6.5.1 Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.84 @% Variables consist of one or more named data objects of a specified size.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%name%@AE@%]] %@AI@%directive%@AE@% %@AI@%initializer%@AE@% [[%@AB@%,%@AE@%%@AI@%initializer%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% is the symbol name assigned to the variable. If no name is%@EH@% assigned, the data is allocated; but the starting address of the variable has no symbolic name.%@NL@% %@NL@% %@4@%%@CR:IX6.85 @%%@CR:IX6.86 @%%@CR:IX6.87 @%%@CR:IX6.88 @%%@CR:IX6.89 @%%@CR:IX6.90 @%%@CR:IX6.91 @%%@CR:IX6.92 @%%@CR:IX6.93 @%%@CR:IX6.94 @% The size of the variable is determined by %@AI@%directive%@AE@%. The directives that%@EH@% can be used to define single-item data objects are listed below:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%DB%@AE@% Defines byte%@NL@% %@NL@% %@AB@%DW%@AE@% Defines word (2 bytes)%@NL@% %@NL@% %@AB@%DD%@AE@% Defines doubleword (4 bytes)%@NL@% %@NL@% %@AB@%DQ%@AE@% Defines quadword (8 bytes)%@NL@% %@NL@% %@AB@%DT%@AE@% Defines 10-byte variable%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX6.95 @%%@CR:IX6.96 @% The optional %@AI@%initializer%@AE@% can be a constant, an expression that evaluates%@EH@% to a constant, or a question mark (%@AB@%?%@AE@%). The question mark is the symbol indicating that the value of the variable is undefined. You can define multiple values by using multiple initializers separated by commas, or by using the %@AB@%DUP%@AE@% operator, as explained in Section 6.5.2%@BO: 65365@%, "Arrays and Buffers."%@NL@% %@NL@% %@4@% Simple data types can allocate memory for integers, strings, addresses, or%@EH@% real numbers.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.1.1 @%%@AB@%6.5.1.1 Integer Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.97 @% When defining an integer variable, you can specify an initial value as an%@EH@% integer constant or as a constant expression. QuickAssembler generates an error if you specify an initial value too large for the specified variable.%@NL@% %@NL@% %@4@%%@CR:IX6.98 @%%@CR:IX6.99 @% Integer values for all sizes except 10-byte variables are stored in binary%@EH@% form. They can be interpreted as either signed or unsigned numbers. For instance, the hexadecimal value 0FFCD can be interpreted either as the signed number -51 or the unsigned number 65,485.%@NL@% %@NL@% %@4@% The processor cannot tell the difference between signed and unsigned%@EH@% numbers. Some instructions are designed specifically for signed numbers. It is the programmer's responsibility to decide whether a value is to be interpreted as signed or unsigned, and then to use the appropriate instructions to handle the value correctly.%@NL@% %@NL@% %@4@% Figure 6.1 shows various integer storage formats.%@EH@%%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 6.5.1.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX6.100 @% The directives for defining integer variables are listed below with the%@EH@% sizes of integer they can define:%@NL@% %@NL@% %@CR:IX6.101 @%%@CR:IX6.102 @%%@CR:IX6.103 @%%@CR:IX6.104 @%%@CR:IX6.105 @%%@CR:IX6.106 @%%@CR:IX6.107 @% %@AB@%Directive%@AE@% %@AB@%Size of Directive%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%DB%@AE@% (bytes) Allocates unsigned numbers from 0 to 255 or signed numbers from -128 to 127.%@NL@% %@NL@% %@AB@%DW%@AE@% (words) Allocates unsigned numbers from 0 to 65,535 or signed numbers from -32,768 to 32,767. The bytes of a word integer are stored in the format shown in Figure 6.1. Note that in assembler listings and in most debuggers the bytes of a word are shown in the opposite order──high byte first──since this is the way most people think of numbers.%@NL@% %@NL@% Word values can be used directly in 8086-family instructions. They can also be loaded, used in calculations, and stored with 8087-family instructions.%@NL@% %@NL@% %@AB@%DD%@AE@% (doublewords) Allocates unsigned numbers from 0 to 4,294,967,295 or signed numbers from -2,147,483,648 to 2,147,483,647. The words of a doubleword integer are stored in the format shown in Figure 6.1.%@NL@% %@NL@% These 32-bit values (called long integers) can be loaded, used in calculations, and stored with 8087-family instructions. Some calculations can be done on these numbers directly with 16-bit 8086-family processors; others involve an indirect method of doing calculations on each word separately (see Chapter 14%@BO: ae658@%, "Doing Arithmetic and Bit Calculations").%@NL@% %@NL@% %@AB@%DQ%@AE@% (quadwords) Allocates 64-bit integers. The doublewords of a quadword integer are stored in the format shown in Figure 6.1.%@NL@% %@NL@% These values can be loaded, used in calculations, and stored with 8087-family instructions. You must write your own routines to use them with 16-bit 8086-family processors.%@NL@% %@NL@% %@AB@%DT%@AE@% Allocates 10-byte (80-bit) integers if the D radix specifier is used.%@NL@% %@NL@% By default, %@AB@%DT%@AE@% allocates packed binary coded decimal (BCD) numbers, as described in Section 6.5.1.2%@BO: 61d5d@%, "Binary Coded Decimal Variables." If you define binary 10-byte integers, you must write your own routines to use routines in calculations.%@NL@% %@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%integer DB 16 ; Initialize byte to 16%@AE@%%@NL@% %@AS@%expression DW 4*3 ; Initialize word to 12%@AE@%%@NL@% %@AS@%empty DQ ? ; Allocate uninitialized long integer%@AE@%%@NL@% %@AS@% DB 1,2,3,4,5,6 ; Initialize six unnamed bytes%@AE@%%@NL@% %@AS@%long DD 4294967295 ; Initialize double word to 4,294,967,295%@AE@%%@NL@% %@AS@%tb DT 2345d ; Initialize 10-byte binary integer%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.1.2 @%%@AB@%6.5.1.2 Binary Coded Decimal Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.108 @% Binary coded decimal (BCD) numbers provide a method of doing calculations%@EH@% on large numbers without rounding errors. They are sometimes used in financial applications. There are two kinds: packed and unpacked.%@NL@% %@NL@% %@4@%%@CR:IX6.109 @% Unpacked BCD numbers are stored one digit to a byte, with the value in the%@EH@% lower four bits. They can be defined with the %@AB@%DB%@AE@% directive. For example, an unpacked BCD number could be defined and initialized as shown below:%@NL@% %@NL@% %@AS@%unpackedr DB 1,5,8,2,5,2,9 ; Initialized to 9,252,851%@AE@%%@NL@% %@AS@%unpackedf DB 9,2,5,2,8,5,1 ; Initialized to 9,252,851%@AE@%%@NL@% %@NL@% %@4@% Whether least-significant digits can come either first or last depends on%@EH@% how you write the calculation routines that handle the numbers. Calculations with unpacked BCD numbers are discussed in Section 14.5.1%@BO: b53c9@%.%@NL@% %@NL@% %@4@%%@CR:IX6.110 @% Packed BCD numbers are stored two digits to a byte, with one digit in the%@EH@% lower four bits and one in the upper four bits. The leftmost bit holds the sign (0 for positive or 1 for negative).%@NL@% %@NL@% %@4@% Packed BCD variables can be defined with the %@AB@%DT%@AE@% directive as shown below:%@EH@%%@NL@% %@NL@% %@AS@%packed DT 9252851 ; Allocate 9,252,851%@AE@%%@NL@% %@NL@% %@4@% The 8087-family processors can do fast calculations with packed BCD%@EH@% numbers, as described in Chapter 17%@BO: d965a@%, "Calculating with a Math Coprocessor." The 8086-family processors can also do some calculations with packed BCD numbers, but the process is slower and more complicated. See Section 14.5.2%@BO: b69dc@% for details.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.1.3 @%%@AB@%6.5.1.3 String Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.111 @%%@CR:IX6.112 @%%@CR:IX6.113 @% Strings are normally initialized with the %@AB@%DB%@AE@% directive. The initializing%@EH@% value is specified as a string constant. Strings can also be initialized by specifying each value in the string. For example, the following definitions are equivalent:%@NL@% %@NL@% %@AS@%version1 DB 97,98,99 ; As ASCII values%@AE@%%@NL@% %@AS@%version2 DB 'a','b','c' ; As characters%@AE@%%@NL@% %@AS@%version3 DB "abc" ; As a string%@AE@%%@NL@% %@NL@% %@4@% One- and two-character strings can also be initialized with any of the%@EH@% other data-definition directives. The last (or only) character in the string is placed in the byte with the lowest address. Either 0 or the first character is placed in the next byte. The unused portion of such variables is filled with zeros.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%function9 DB 'Hello',13,10,'$' ; Use with DOS INT 21h%@AE@%%@NL@% %@AS@% ; function 9%@AE@%%@NL@% %@AS@%asciiz DB "\ASM\TEST.ASM",0 ; Use as ASCIIZ string%@AE@%%@NL@% %@NL@% %@AS@%message DB "Enter file name: " ; Use with DOS INT 21h%@AE@%%@NL@% %@AS@%l_message EQU $-message ; function 40h%@AE@%%@NL@% %@AS@%a_message EQU OFFSET message%@AE@%%@NL@% %@NL@% %@AS@%str1 DB "ab" ; Stored as 61 62%@AE@%%@NL@% %@AS@%str2 DD "ab" ; Stored as 62 61 00 00%@AE@%%@NL@% %@AS@%str3 DD "a" ; Stored as 61 00 00 00%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.1.4 @%%@AB@%6.5.1.4 Real-Number Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Real numbers must be stored in binary format. However, when initializing%@EH@% variables, you can specify decimal or hexadecimal constants and let the assembler automatically encode them into their binary equivalents. QuickAssembler can use two different binary formats for real numbers: IEEE or Microsoft Binary. You can specify the format by using directives (IEEE is the default).%@NL@% %@NL@% %@4@% This section tells you how to initialize real-number variables, describes%@EH@% the two binary formats, and explains real-number encoding.%@NL@% %@NL@% %@3@% %@AB@%Initializing and Allocating Real-Number Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.114 @%%@CR:IX6.115 @%%@CR:IX6.116 @% Real numbers can be defined by initializing them either with real-number%@EH@% constants or with encoded hexadecimal constants. The real-number designator (R) must follow numbers specified in encoded format.%@NL@% %@NL@% %@4@%%@CR:IX6.117 @%%@CR:IX6.118 @%%@CR:IX6.119 @%%@CR:IX6.120 @%%@CR:IX6.121 @% The directives for defining real numbers are listed below with the sizes%@EH@% of the numbers they can allocate:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Size%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%DD%@AE@% Allocates short (32-bit) real numbers in either the IEEE or Microsoft Binary format.%@NL@% %@NL@% %@AB@%DQ%@AE@% Allocates long (64-bit) real numbers in either the IEEE or Microsoft Binary format.%@NL@% %@NL@% %@AB@%DT%@AE@% Allocates temporary or 10-byte (80-bit) real numbers. The format of these numbers is similar to the IEEE format. They are always encoded the same regardless of the real-number format. Their size is nonstandard and incompatible with most Microsoft high-level languages. Temporary-real format is provided for those who want to initialize real numbers in the format used internally by 8087-family processors.%@NL@% %@NL@% %@NL@% %@4@% The 8086-family microprocessors do not have any instructions for handling%@EH@% real numbers. You must write your own routines, use a library that includes real-number calculation routines, or use a coprocessor. The 8087-family coprocessors can load real numbers in the IEEE format; they can also use the values in calculations and store the results back to memory, as explained in Chapter 17%@BO: d965a@%, "Calculating with a Math Coprocessor."%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%shrt DD 98.6 ; QuickAsm automatically encodes%@AE@%%@NL@% %@AS@%long DQ 5.391E-4 ; in current format%@AE@%%@NL@% %@AS@%ten_byte DT -7.31E7%@AE@%%@NL@% %@AS@%eshrt DD 87453333r ; 98.6 encoded in Microsoft%@AE@%%@NL@% %@AS@% ; Binary format%@AE@%%@NL@% %@AS@%elong DQ 3F41AA4C6F445B7Ar ; 5.391E-4 encoded in IEEE format%@AE@%%@NL@% %@NL@% %@4@% The real-number designator (R) used to specify encoded numbers is%@EH@% explained in Section 6.1.3%@BO: 5971d@%, "Real-Number Constants."%@NL@% %@NL@% %@3@% %@AB@%Selecting a Real-Number Format%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.122 @%%@CR:IX6.123 @% QuickAssembler can encode four-byte and eight-byte real numbers in two%@EH@% different formats: IEEE and Microsoft Binary. Your choice depends on the type of program you are writing. The four primary alternatives are listed below:%@NL@% %@NL@% 1. If your program requires a coprocessor for calculations, you must use the IEEE format.%@NL@% %@NL@% %@CR:IX6.124 @%%@CR:IX6.125 @%%@CR:IX6.126 @% 2. Most high-level languages use the IEEE format. If you are writing modules that will be called from such a language, your program should use the IEEE format. All versions of the C, FORTRAN, and Pascal compilers sold by Microsoft and IBM use the IEEE format.%@NL@% %@NL@% 3. If you are writing a module that will be called from early versions of Microsoft or IBM BASIC, your program should use the Microsoft Binary format. Versions that support only the Microsoft Binary format include:%@NL@% %@NL@% %@CR:IX6.127 @% ■ Microsoft QuickBASIC through Version 2.01%@NL@% %@NL@% ■ Microsoft BASIC Compiler through Version 5.3%@NL@% %@NL@% ■ IBM BASIC Compiler through Version 2.0%@NL@% %@NL@% ■ Microsoft GW-BASIC(R) interpreter (all versions)%@NL@% %@NL@% ■ IBM BASICA interpreter (all versions)%@NL@% %@NL@% Microsoft QuickBASIC Version 3.0 supported both the Microsoft Binary and IEEE formats as options. Current and future versions of Microsoft QuickBASIC and the Microsoft and IBM BASIC compilers support only the IEEE format.%@NL@% %@NL@% 4. If you are creating a stand-alone program that does not use a coprocessor, you can choose either format. The IEEE format is better for overall compatibility with high-level languages. The Microsoft Binary format may be necessary for compatibility with existing source code.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% When you interface assembly-language modules with high-level languages, the real-number format only matters if you initialize real-number variables in the assembly module. If your assembly module does not use real numbers, or if all real numbers are initialized in the high-level-language module, the real-number format does not make any difference.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% By default, QuickAssembler assembles real-number data in the IEEE format.%@EH@% If you wish to use the Microsoft Binary format, you must put the %@AB@%.MSFLOAT%@AE@% directive at the start of your source file before initializing any real-number variables.%@NL@% %@NL@% %@3@%%@CR:IX6.128 @%%@CR:IX6.129 @%%@CR:IX6.130 @%%@CR:IX6.131 @% %@AB@%Real-Number Encoding%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.132 @%%@CR:IX6.133 @%%@CR:IX6.134 @% The IEEE format for encoding four- and eight-byte real numbers is%@EH@% illustrated in Figure 6.2. Although this figure places the most-significant bit first for illustration, low bytes actually appear first in memory.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 6.5.1.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The parts of the real numbers are described below:%@EH@%%@NL@% %@NL@% 1. Sign bit (0 for positive or 1 for negative) in the upper bit of the first byte.%@NL@% %@NL@% 2. Exponent in the next bits in sequence (8 bits for short real number or 11 bits for long real number).%@NL@% %@NL@% 3. All except the first set bit of mantissa in the remaining bits of the variable. Since the first significant bit is known to be set, it need not be actually stored. The length is 23 bits for short real numbers and 52 bits for long real numbers.%@NL@% %@NL@% %@4@% The Microsoft Binary format for encoding real numbers is illustrated in%@EH@% Figure 6.3.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 6.5.1.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The three parts of real numbers are described below:%@EH@%%@NL@% %@NL@% 1. Biased exponent (8 bits) in the high-address byte. The bias is 81H for short real numbers and 401H for long real numbers.%@NL@% %@NL@% 2. Sign bit (0 for positive or 1 for negative) in the upper bit of the second-highest byte.%@NL@% %@NL@% 3. All except the first set bit of mantissa in the remaining 7 bits of the second-highest byte and in the remaining bytes of the variable. Since the first significant bit is known to be set, it need not be actually stored. The length is 23 bits for short real numbers and 55 bits for long real numbers.%@NL@% %@NL@% %@4@%%@CR:IX6.135 @%%@CR:IX6.136 @% QuickAssembler also supports the 10-byte temporary-real format used%@EH@% internally by 8087-family coprocessors. This format is similar to IEEE format. The size is nonstandard and is not used by Microsoft compilers or interpreters. Since the coprocessors can load and automatically convert numbers in the more standard 4-byte and 8-byte formats, the 10-byte format is seldom used in assembly-language programming.%@NL@% %@NL@% %@4@% The temporary-real format for encoding real numbers is illustrated in%@EH@% Figure 6.4.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 6.5.1.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The four parts of the real numbers are described below:%@EH@%%@NL@% %@NL@% 1. Sign bit (0 for positive or 1 for negative) in the upper bit of the first byte.%@NL@% %@NL@% 2. Exponent in the next bits in sequence (15 bits for 10-byte real).%@NL@% %@NL@% 3. The integer part of mantissa in the next bit in sequence (bit 63).%@NL@% %@NL@% 4. Remaining bits of mantissa in the remaining bits of the variable. The length is 63 bits.%@NL@% %@NL@% %@4@% Notice that the 10-byte temporary-real format stores the integer part of%@EH@% the mantissa. This differs from the 4-byte and 8-byte formats, in which the integer part is implicit.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.2 @%%@AB@%6.5.2 Arrays and Buffers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.137 @%%@CR:IX6.138 @%%@CR:IX6.139 @%%@CR:IX6.140 @% Arrays, buffers, and other data structures consisting of multiple data%@EH@% objects of the same size can be defined with the %@AB@%DUP%@AE@% operator. This operator can be used with any of the data-definition directives described in this chapter.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%count%@AE@% %@AB@%DUP%@AE@% %@AB@%(%@AE@%%@AI@%initialvalue%@AE@%[[%@AB@%,%@AE@%%@AI@%initialvalue%@AE@%]]...%@AB@%)%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.141 @% The %@AI@%count%@AE@% sets the number of times to define %@AI@%initialvalue%@AE@%. The initial%@EH@% value can be any expression that evaluates to an integer value, a character constant, or another %@AB@%DUP%@AE@% operator. It can also be the undefined symbol (%@AB@%?%@AE@%) if there is no initial value.%@NL@% %@NL@% %@4@% Multiple initial values must be separated by commas. If multiple values%@EH@% are specified within the parentheses, the sequence of values is allocated %@AI@%count%@AE@% times. For example, the statement%@NL@% %@NL@% %@AS@% DB 5 DUP ("Text ")%@AE@%%@NL@% %@NL@% %@4@% allocates the string %@AS@%"Text " %@AE@%five times for a total of 25 bytes.%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.142 @% %@AB@%DUP%@AE@% operators can be nested up to 17 levels. The initial value (or values)%@EH@% must always be placed within parentheses.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%array DD 10 DUP (1) ; 10 doublewords%@AE@%%@NL@% %@AS@% ; initialized to 1%@AE@%%@NL@% %@NL@% %@AS@%buffer DB 256 DUP (?) ; 256 byte buffer%@AE@%%@NL@% %@NL@% %@AS@%masks DB 20 DUP (040h,020h,04h,02h) ; 80 byte buffer%@AE@%%@NL@% %@AS@% ; with bit masks%@AE@%%@NL@% %@AS@% DB 32 DUP ("I am here ") ; 320 byte buffer with%@AE@%%@NL@% %@AS@% ; signature for debugging%@AE@%%@NL@% %@AS@%three_d DD 5 DUP (5 DUP (5 DUP (0))) ; 125 doublewords%@AE@%%@NL@% %@AS@% ; initialized to 0%@AE@%%@NL@% %@NL@% %@4@% Note that QuickAssembler %@AI@%s%@AE@%ometimes generates different object code when%@EH@% the %@AB@%DUP %@AE@%operator is used rather than when multiple values are given. For example, the statement%@NL@% %@NL@% %@AS@%test1 DB ?,?,?,?,? ; Indeterminate%@AE@%%@NL@% %@NL@% %@4@% is "indeterminate." It causes QuickAssembler to write five zero-value%@EH@% bytes to the object file. The statement%@NL@% %@NL@% %@AS@%test2 DB 5 DUP (?) ; Undefined%@AE@%%@NL@% %@NL@% %@4@% is "undefined." It causes QuickAssembler to increase the offset of the%@EH@% next record in the object file by five bytes. Therefore, an object file created with the first statement will be larger than one created with the second statement.%@NL@% %@NL@% %@4@%%@CR:IX6.143 @%%@CR:IX6.144 @%%@CR:IX6.145 @%%@CR:IX6.146 @% In most cases, the distinction between indeterminate and undefined%@EH@% definitions is trivial. The linker adjusts the offsets so that the same executable file is generated in either case. However, the difference is significant in segments with the %@AB@%COMMON%@AE@% combine type. If %@AB@%COMMON%@AE@% segments in two modules contain definitions for the same variable, one with an indeterminate value and one with an explicit value, the actual value in the executable file varies depending on link order. If the module with the indeterminate value is linked last, the 0 initialized for it overrides the explicit value. You can prevent this by always using undefined rather than indeterminate values in %@AB@%COMMON%@AE@% segments. For example, use the first of the following statements:%@NL@% %@NL@% %@AS@%test3 DB 1 DUP (?) ; Undefined - doesn't initialize%@AE@%%@NL@% %@AS@%test4 DB ? ; Indeterminate - initializes 0%@AE@%%@NL@% %@NL@% %@4@% If you use the undefined definition, the explicit value is always used in%@EH@% the executable file regardless of link order.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.3 @%%@AB@%6.5.3 Labeling Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.147 @%%@CR:IX6.148 @% The %@AB@%LABEL%@AE@% directive can be used to define a variable of a given size at a%@EH@% specified location. It is useful if you want to refer to the same data as variables of different sizes.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%LABEL%@AE@% %@AI@%type%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% is the symbol assigned to the variable, and %@AI@%type%@AE@% is the variable%@EH@% size. The type can be any one of the following type specifiers: %@AB@%BYTE%@AE@%, %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%, %@AB@%QWORD%@AE@%, or %@AB@%TBYTE%@AE@%. It can also be the name of a previously defined structure.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%warray LABEL WORD ; Access array as 50 words%@AE@%%@NL@% %@AS@%darray LABEL DWORD ; Access same array as 25 doublewords%@AE@%%@NL@% %@AS@%barray DB 100 DUP(?) ; Access same array as 100 bytes%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC6.5.4 @%%@AB@%6.5.4 Pointer Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.149 @%%@CR:IX6.150 @% The assembler lets you explicitly allocate pointers. A pointer (address)%@EH@% variable is either two or four bytes in size; consequently, any word or doubleword data definition can create a pointer variable. However, declaring pointer variables explicitly gives more accurate debugging information to the environment.%@NL@% %@NL@% %@4@% Pointer-variable definitions have the following form:%@EH@%%@NL@% %@NL@% %@4@% %@AI@%symbol%@AE@% [[%@AB@%DW%@AE@% |%@AB@% DD%@AE@%]] %@AI@%type%@AE@% %@AB@%PTR%@AE@% %@AI@%initialvalue%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%type%@AE@% can consist of the name of a record, structure, or one of the%@EH@% standard types described in Section 6.3%@BO: 5cd7a@%, "Using Type Specifiers."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%string DB "from swerve of shore to bend of bay", 0%@AE@%%@NL@% %@AS@%pstring DW BYTE PTR string ; Declares a near pointer to string.%@AE@%%@NL@% %@AS@%fpstring DD BYTE PTR string ; Declares a far pointer to string.%@AE@%%@NL@% %@NL@% %@4@% In this example, near (two-byte) and far (four-byte) pointers are declared%@EH@% and initialized to the value of a null terminated string. This is the format used in C language, and the pointer variables in the example could be used in C functions that process strings.%@NL@% %@NL@% %@4@% Using an explicit pointer declaration generates debugging information,%@EH@% causing the variable to be viewed as a pointer during debugging. Consequently, the environment properly interprets the variable when you enter it as a Watch expression. No special syntax is required.%@NL@% %@NL@% %@4@% This use of %@AB@%PTR%@AE@% is distinct from the use of%@AB@% PTR %@AE@%to alter the type of a%@EH@% variable during an instruction. The assembler uses the context of the program to determine which way you are using the %@AB@%PTR%@AE@% keyword.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.6 @%%@AB@%6.6 Setting the Location Counter%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.151 @%%@CR:IX6.152 @%%@CR:IX6.153 @% As the assembler encounters labels and data declarations, it needs to know%@EH@% what address to assign. This function is fulfilled by the location counter, which indicates the offset address corresponding to the current line of source code. This value is generally equal to the value that IP will have at run time.%@NL@% %@NL@% %@4@% The assembler increments the location counter as it processes each%@EH@% statement. However, you can set the location counter directly by using the %@AB@%ORG%@AE@% directive.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%ORG %@AE@%%@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Subsequent code and data offsets begin at the new offset specified set by%@EH@% %@AI@%expression%@AE@%. The expression must resolve to a constant number. In other words, all symbols used in the expression must be known on the first pass of the assembler.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX6.154 @%%@CR:IX6.155 @%%@CR:IX6.156 @% %@AB@%NOTE%@AE@% The value of the location counter, represented by the dollar sign (%@AB@%$%@AE@%), can be used in the expression, as described in Section 9.3%@BO: 83a2d@%, "Using the Location Counter."%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Labeling absolute addresses%@AE@%%@NL@% %@NL@% %@AS@%STUFF SEGMENT AT 0 ; Segment has constant value 0%@AE@%%@NL@% %@AS@% ORG 410h ; Offset has constant value 410h%@AE@%%@NL@% %@AS@%equipment LABEL WORD ; Value at 0000:0410 labeled "equipment"%@AE@%%@NL@% %@AS@% ORG 417h ; Offset has constant value 417h%@AE@%%@NL@% %@AS@%keyboard LABEL WORD ; Value at 0000:0417 labeled "keyboard"%@AE@%%@NL@% %@AS@%STUFF ENDS%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ASSUME ds:STUFF ; Tell the assembler%@AE@%%@NL@% %@AS@% mov ax,STUFF ; Tell the processor%@AE@%%@NL@% %@AS@% mov ds,ax%@AE@%%@NL@% %@NL@% %@AS@% mov dx,equipment%@AE@%%@NL@% %@AS@% mov keyboard,ax%@AE@%%@NL@% %@NL@% %@4@% Example 1 illustrates one way of assigning symbolic names to absolute%@EH@% addresses.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Format for .COM files%@AE@%%@NL@% %@NL@% %@AS@%_TEXT SEGMENT%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT,ds:_TEXT,ss:_TEXT,es:_TEXT%@AE@%%@NL@% %@AS@% ORG 100h ; Skip 100h bytes of DOS header%@AE@%%@NL@% %@NL@% %@AS@%entry: jmp begin ; Jump over data%@AE@%%@NL@% %@AS@%variable DW ? ; Put more data here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%begin: . ; First line of code%@AE@%%@NL@% %@AS@% . ; Put more code here%@AE@%%@NL@% %@AS@%_TEXT ENDS%@AE@%%@NL@% %@AS@% END entry%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates how the %@AB@%ORG%@AE@% directive is used to initialize the%@EH@% starting execution point in %@AB@%.COM%@AE@% files.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC6.7 @%%@AB@%6.7 Aligning Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX6.157 @%%@CR:IX6.158 @%%@CR:IX6.159 @%%@CR:IX6.160 @%%@CR:IX6.161 @%%@CR:IX6.162 @% Some operations are more efficient when the variable used in the operation%@EH@% is lined up on a boundary of a particular size. The %@AB@%EVEN%@AE@% and %@AB@%ALIGN%@AE@% directives can be used to pad the object file so that the next variable is aligned on a specified boundary.%@NL@% %@NL@% %@4@% %@AB@%Syntax 1%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%EVEN%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax 2%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%ALIGN%@AE@% %@AI@%number%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%EVEN%@AE@% directive always aligns on the next even byte. The %@AB@%ALIGN%@AE@%%@EH@% directive aligns on the next byte that is a multiple of %@AI@%number%@AE@%. The number must be a power of 2. For example, use %@AS@%ALIGN 2 %@AE@%or %@AS@%EVEN %@AE@%to align on word boundaries, or use %@AS@%ALIGN 4 %@AE@%to align on doubleword boundaries.%@NL@% %@NL@% %@4@%%@CR:IX6.163 @% If the value of the location counter is not on the specified boundary when%@EH@% an %@AB@%ALIGN%@AE@% directive is encountered, the location counter is incremented to a value on the boundary. If the location counter is already on the boundary, the directive has no effect.%@NL@% %@NL@% %@4@% When the assembler increments the location counter, it also pads the%@EH@% skipped memory locations by inserting certain values. If the segment is a data segment, the assembler always pads these locations with zeros. If the segment is a code segment, the assembler pads skipped memory locations with a two-byte no-op instruction (instruction that performed no operation) where possible:%@NL@% %@NL@% %@AS@% xchg bx,bx%@AE@%%@NL@% %@NL@% %@4@% This instruction, which assembles as 87D8 hexadecimal, does nothing, but%@EH@% it executes faster than two %@AB@%NOP%@AE@% instructions. Where there is no room for the two-byte no-op, the assembler inserts the one-byte%@AB@% NOP%@AE@% instruction.%@NL@% %@NL@% %@4@% The %@AB@%ALIGN%@AE@% and %@AB@%EVEN%@AE@% directives give no efficiency improvements on%@EH@% processors that have an 8-bit data bus (such as the 8088). These processors always fetch data one byte at a time, regardless of the alignment. However, using %@AB@%EVEN%@AE@% can speed certain operation on processors that have a 16-bit data bus (such as the 8086), since the processor can fetch a word if the data is word aligned, but must do two memory fetches if the data is not word aligned.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The %@AB@%ALIGN %@AE@%directive is a new feature of recent versions of Microsoft assemblers, starting with 5.0. In previous versions, data could be word aligned by using the %@AB@%EVEN%@AE@% directive, but other alignments could not be specified.%@NL@% %@NL@% The %@AB@%EVEN %@AE@%directive should not be used in segments with %@AB@%BYTE%@AE@% align type. Similarly, the number specified with the %@AB@%ALIGN %@AE@%directive should be no greater than the size of the align type of the current segment (thus ensuring that number is a divisor of the align type of the segment).%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .STACK 100h%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ALIGN 2 ; For faster data access%@AE@%%@NL@% %@AS@%stuff DW 66,124,573,99,75%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ALIGN 2 ; For faster data access%@AE@%%@NL@% %@AS@%evenstuff DW ?,?,?,?,?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%start: mov ax,@data ; Load segment location%@AE@%%@NL@% %@AS@% mov ds,ax ; into DS%@AE@%%@NL@% %@AS@% mov es,ax ; and ES registers%@AE@%%@NL@% %@NL@% %@AS@% mov cx,5 ; Load count%@AE@%%@NL@% %@AS@% mov si,OFFSET stuff ; Point to source%@AE@%%@NL@% %@AS@% mov di,OFFSET evenstuff ; and destination%@AE@%%@NL@% %@AS@% ALIGN 2 ; Align for faster loop access%@AE@%%@NL@% %@AS@%mloop: lodsw ; Load a word%@AE@%%@NL@% %@AS@% inc ax ; Make it even by incrementing%@AE@%%@NL@% %@AS@% and ax,NOT 1 ; and turning off first bit%@AE@%%@NL@% %@AS@% stosw ; Store%@AE@%%@NL@% %@AS@% loop mloop ; Again%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@4@% In this example, the words at %@AS@%stuff %@AE@%and %@AS@%evenstuff %@AE@%are forced to word%@EH@% boundaries. This makes access to the data faster with processors that have a 16-bit data bus. Without this alignment, the initial data might start on an odd boundary and the processor would have to fetch half of each word at a time.%@NL@% %@NL@% %@4@% Similarly, the alignment in the code segment speeds up repeated access to%@EH@% the code at the start of the loop. The sample code sacrifices program size in order to achieve moderate improvements on the 8086 and 80286. There is no speed advantage on the 8088.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH7 @%%@AB@%Chapter 7: Using Structures and Records%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% QuickAssembler can define and use two kinds of multifield variables:%@EH@% structures and records.%@NL@% %@NL@% %@4@%%@CR:IX7.1 @% "Structures" are templates for data objects made up of smaller data%@EH@% objects. A structure can be used to define structure variables, which are made up of smaller variables called fields. Fields within a structure can be different sizes, and each can be accessed individually.%@NL@% %@NL@% %@4@%%@CR:IX7.2 @%%@CR:IX7.3 @%%@CR:IX7.4 @% "Records" are templates for data objects whose bits can be described as%@EH@% groups of bits called fields. A record can be used to define record variables. Each bit field in a record variable can be used separately in constant operands or expressions. The processor cannot access bits individually at run time, but bit fields can be used with logical bit instructions to change bits indirectly.%@NL@% %@NL@% %@4@% This chapter describes structures and records and tells how to use them.%@EH@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC7.1 @%%@AB@%7.1 Structures%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.5 @% A structure variable is a collection of data objects that can be accessed%@EH@% symbolically as a single data object. Objects within the structure can have different sizes and can be accessed symbolically.%@NL@% %@NL@% %@4@% There are two steps in using structure variables:%@EH@%%@NL@% %@NL@% 1. Declare a structure type. A structure type is a template for data. It declares the sizes and, optionally, the initial values for objects in the structure. By itself the structure type does not define any data. The structure type is used by QuickAssembler during assembly but is not saved as part of the object file.%@NL@% %@NL@% %@CR:IX7.6 @% 2. Define one or more variables having the structure type. For each variable defined, memory is allocated to the object file in the format declared by the structure type.%@NL@% %@NL@% %@4@% The structure variable can then be used as an operand in assembler%@EH@% statements. The structure variable can be accessed as a whole by using the structure name, or individual fields can be accessed by using structure and field names.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.1.1 @%%@AB@%7.1.1 Declaring Structure Types%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.7 @%%@CR:IX7.8 @%%@CR:IX7.9 @%%@CR:IX7.10 @%%@CR:IX7.11 @%%@CR:IX7.12 @%%@CR:IX7.13 @%%@CR:IX7.14 @% The %@AB@%STRUC%@AE@% and %@AB@%ENDS%@AE@% directives mark the beginning and end of a type%@EH@% declaration for a structure.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%STRUC%@AE@%%@EH@%%@NL@% %@AI@%fielddeclarations%@AE@%%@NL@% %@AI@%name%@AE@% %@AB@%ENDS%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.15 @%%@CR:IX7.16 @% The %@AI@%name%@AE@% declares the name of the structure type. It must be unique. The%@EH@% %@AI@%fielddeclarations%@AE@% declare the fields of the structure. Any number of field declarations may be given. They must follow the form of data definitions described in Section 6.5%@BO: 5ff03@%, "Defining and Initializing Data." Default initial values may be declared individually or with the %@AB@%DUP%@AE@% operator.%@NL@% %@NL@% %@4@%%@CR:IX7.17 @% The names given to fields must be unique within the source file where they%@EH@% are declared. When variables are defined, the field names will represent the offset from the beginning of the structure to the corresponding field. %@NL@% %@4@%%@CR:IX7.18 @% When declaring strings in a structure type, make sure the initial values%@EH@% are long enough to accommodate the largest possible string. Strings smaller than the field size can be placed in the structure variable, but larger strings will be truncated.%@NL@% %@NL@% %@4@% A structure declaration can contain both field declarations and comments.%@EH@% Conditional-assembly statements are allowed in structure declarations; no other kinds of statements are allowed. Since the %@AB@%STRUC%@AE@% directive is not allowed inside structure declarations, structures cannot be nested.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The %@AB@%ENDS%@AE@% directive that marks the end of a structure has the same mnemonic as the %@AB@%ENDS %@AE@%directive that marks the end of a segment. The assembler recognizes the meaning of the directive from context. Make sure each %@AB@%SEGMENT %@AE@%directive and each %@AB@%STRUC%@AE@% directive has its own %@AB@%ENDS%@AE@% directive.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%student STRUC ; Structure for student records%@AE@%%@NL@% %@AS@%id DW ? ; Field for identification #%@AE@%%@NL@% %@AS@%sname DB "Last, First Middle "%@AE@%%@NL@% %@AS@%scores DB 10 DUP (100) ; Field for 10 scores%@AE@%%@NL@% %@AS@%student ENDS%@AE@%%@NL@% %@NL@% %@4@% Within the sample structure %@AS@%student%@AE@%, the fields %@AS@%id%@AE@%, %@AS@%sname%@AE@%, and %@AS@%scores %@AE@%have%@EH@% the offset values 0, 2, and 24, respectively.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.1.2 @%%@AB@%7.1.2 Defining Structure Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.19 @%%@CR:IX7.20 @%%@CR:IX7.21 @%%@CR:IX7.22 @% A structure variable is a variable with one or more fields of different%@EH@% sizes. The sizes and initial values of the fields are determined by the structure type with which the variable is defined.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%name%@AE@%]] %@AI@%structurename%@AE@% %@AB@%<%@AE@%[[%@AI@%initialvalue%@AE@% [[%@AB@%,%@AE@%%@AI@%initialvalue%@AE@%]]...]]%@AB@%>%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% is the name assigned to the variable. If no name is given, the%@EH@% assembler allocates space for the variable, but does not give it a symbolic name. The %@AI@%structurename%@AE@% is the name of a structure type previously declared by using the %@AB@%STRUC%@AE@% and %@AB@%ENDS%@AE@% directives.%@NL@% %@NL@% %@4@% An %@AI@%initialvalue%@AE@% can be given for each field in the structure. Its type%@EH@% must not be incompatible with the type of the corresponding field. The angle brackets (%@AB@%< >%@AE@%) are required even if no initial value is given. If initial values are given for more than one field, the values must be separated by commas.%@NL@% %@NL@% %@4@%%@CR:IX7.23 @%%@CR:IX7.24 @% If the %@AB@%DUP%@AE@% operator (see Section 6.5.2%@BO: 65365@%, "Arrays and Buffers") is used to%@EH@% initialize multiple structure variables, only the angle brackets and initial values, if given, need to be enclosed in parentheses. For example, you can define an array of structure variables as shown below:%@NL@% %@NL@% %@AS@%war date 365 DUP (<,,1940>)%@AE@%%@NL@% %@NL@% %@4@% You need not initialize all fields in a structure. If an initial value is%@EH@% left blank, the assembler automatically uses the default initial value of the field, which was originally determined by the structure type. If there is no default value, the field is undefined.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following examples use the student type declared in the example in%@EH@% Section 7.1.1%@BO: 69fa7@%, "Declaring Structure Types":%@NL@% %@NL@% %@AS@%s1 student <> ; Uses default values of type%@AE@%%@NL@% %@NL@% %@NL@% %@AS@%s2 student <1467,"White, Robert D.",>%@AE@%%@NL@% %@AS@% ; Override default values of first two%@AE@%%@NL@% %@AS@% ; fields--use default value of third%@AE@%%@NL@% %@NL@% %@AS@%sarray student 100 DUP (<>) ; Declare 100 student variables%@AE@%%@NL@% %@AS@% ; with default initial values%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.25 @%%@CR:IX7.26 @% Note that you cannot initialize any structure field that has multiple%@EH@% values if this field was given a default initial value when the structure was declared. For example, assume the following structure declaration:%@NL@% %@NL@% %@AS@%stuff STRUC%@AE@%%@NL@% %@AS@%buffer DB 100 DUP (?) ; Can't override%@AE@%%@NL@% %@AS@%crlf DB 13,10 ; Can't override%@AE@%%@NL@% %@AS@%query DB 'Filename: ' ; String <= can override%@AE@%%@NL@% %@AS@%endmark DB 36 ; Can override%@AE@%%@NL@% %@AS@%stuff ENDS%@AE@%%@NL@% %@NL@% %@4@% The %@AS@%buffer %@AE@%and %@AS@%crlf %@AE@%fields cannot be overridden by initial values in the%@EH@% structure definition because they have multiple values. The %@AS@%query %@AE@%field can be overridden as long as the overriding string is no longer than %@AS@%query%@AE@% (10 bytes). A longer string would generate an error. The %@AS@%endmark %@AE@%field can be overridden by any byte value.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.1.3 @%%@AB@%7.1.3 Using Structure Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.27 @%%@CR:IX7.28 @%%@CR:IX7.29 @% Like other variables, structure variables can be accessed by name. Fields%@EH@% within structure variables can also be accessed by using the syntax shown below:%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%variable%@AE@%%@AB@%.%@AE@%%@AI@%field%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%variable%@AE@% must be the name of a structure (or an operand that resolves%@EH@% to the address of a structure). The %@AI@%field%@AE@% must be the name of a field within that structure. The variable is separated from the field by a period. The period is discussed as a structure-field-name operator in Section 9.2.1.2%@BO: 7afe8@%.%@NL@% %@NL@% %@4@% The address of a structure operand is the sum of the offsets of %@AI@%variable%@AE@%%@EH@% and %@AI@%field%@AE@%. The address is relative to the segment or group in which the variable is declared.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%date STRUC ; Declare structure%@AE@%%@NL@% %@AS@%month DB ?%@AE@%%@NL@% %@AS@%day DB ?%@AE@%%@NL@% %@AS@%year DW ?%@AE@%%@NL@% %@AS@%date ENDS%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%yesterday date <9,30,1987> ; Declare structure%@AE@%%@NL@% %@AS@%today date <10,1,1987> ; variables%@AE@%%@NL@% %@AS@%tomorrow date <10,2,1987>%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov al,yesterday.day ; Use structure variables%@AE@%%@NL@% %@AS@% mov ah,today.month ; as operands%@AE@%%@NL@% %@AS@% mov tomorrow.year,dx%@AE@%%@NL@% %@AS@% mov bx,OFFSET yesterday ; Load structure address%@AE@%%@NL@% %@AS@% mov ax,[bx].month ; Use as indirect operand%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC7.2 @%%@AB@%7.2 Records%@AE@%%@EH@%%@NL@% %@NL@% %@4@% A record variable is a byte or word variable in which specific bit fields%@EH@% can be accessed symbolically. Bit fields within the record can have different sizes.%@NL@% %@NL@% %@4@% There are two steps in declaring record variables:%@EH@%%@NL@% %@NL@% 1. Declare a record type. A record type is a template for data. It declares the sizes and, optionally, the initial values for bit fields in the record. By itself, the record type does not define any data. The record type is used by Quick-Assembler during assembly but is not saved as part of the object file.%@NL@% %@NL@% 2. Define one or more variables having the record type. For each variable defined, memory is allocated to the object file in the format declared by the type.%@NL@% %@NL@% %@4@% The record variable can then be used as an operand in assembler%@EH@% statements. The record variable can be accessed as a whole by using the record name, or individual fields can be specified by using the record name and a field name combined with the field-name operator. A record type can also be used as a constant (immediate data).%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.1 @%%@AB@%7.2.1 Declaring Record Types%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.30 @%%@CR:IX7.31 @%%@CR:IX7.32 @%%@CR:IX7.33 @%%@CR:IX7.34 @%%@CR:IX7.35 @%%@CR:IX7.36 @%%@CR:IX7.37 @%%@CR:IX7.38 @% The %@AB@%RECORD%@AE@% directive declares a record type for an 8-bit or 16-bit record%@EH@% that contains one or more bit fields.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%recordname%@AE@% %@AB@%RECORD%@AE@% %@AI@%field%@AE@% [[%@AB@%,%@AE@%%@AI@%field%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%recordname%@AE@% is the name of the record type to be used when creating the%@EH@% record. The %@AI@%field%@AE@% declares the name, width, and initial value for the field.%@NL@% %@NL@% %@4@% The syntax for each %@AI@%field%@AE@% is shown below:%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%fieldname%@AE@%%@AB@%:%@AE@%%@AI@%width%@AE@%[[%@AB@%=%@AE@%%@AI@%expression%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%fieldname%@AE@% is the name of a field in the record, %@AI@%width%@AE@% is the number of%@EH@% bits in the field, and %@AI@%expression%@AE@% is the initial (or default) value for the field.%@NL@% %@NL@% %@4@% Any number of field combinations can be given for a record, as long as%@EH@% each is separated from its predecessor by a comma. The sum of the widths for all fields must not exceed 16 bits.%@NL@% %@NL@% %@4@%%@CR:IX7.39 @% The width must be a constant. If the total width of all declared fields is%@EH@% larger than eight bits, the assembler uses two bytes. Otherwise, only one byte is used.%@NL@% %@NL@% %@4@% If %@AI@%expression%@AE@% is given, it declares the initial value for the field. An%@EH@% error message is generated if an initial value is too large for the width of its field. If the field is at least seven bits wide, you can use an ASCII character for %@AI@%expression%@AE@%. The expression must not contain a forward reference to any symbol.%@NL@% %@NL@% %@4@% In all cases, the first field you declare goes into the most significant%@EH@% bits of the record. Successively declared fields are placed in the succeeding bits to the right. If the fields you declare do not total exactly 8 bits or exactly 16 bits, the entire record is shifted right so that the last bit of the last field is the lowest bit of the record. Unused bits in the high end of the record are initialized to 0.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.40 @% Example 1 creates a byte record type %@AS@%color %@AE@%having four fields: %@AS@%blink%@AE@%,%@EH@% %@AS@%back%@AE@%, %@AS@%intense%@AE@%, and %@AS@%fore%@AE@%. The contents of the record type are shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 7.2.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% Since no initial values are given, all bits are set to 0. Note that this%@EH@% is only a template maintained by the assembler. No data is created.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%cw RECORD r1:3=0,ic:1=0,rc:2=0,pc:2=3,r2:2=1,masks:6=63%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.41 @% Example 2 creates a record type %@AS@%cw %@AE@%having six fields. Each record declared%@EH@% by using this type occupies 16 bits of memory. The bit diagram below shows the contents of the record type:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 7.2.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% Default values are given for each field. They can be used when data is%@EH@% declared for the record.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.2 @%%@AB@%7.2.2 Defining Record Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.42 @%%@CR:IX7.43 @%%@CR:IX7.44 @%%@CR:IX7.45 @% A record variable is an 8-bit or 16-bit variable whose bits are divided%@EH@% into one or more fields.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%name%@AE@%]] %@AI@%recordname%@AE@% %@AB@%<%@AE@%[[%@AI@%initialvalue%@AE@% [[%@AB@%,%@AE@%%@AI@%initialvalue%@AE@%]]...]]%@AB@%>%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% is the symbolic name of the variable. If no name is given, the%@EH@% assembler allocates space for the variable, but does not give it a symbolic name. The %@AI@%recordname%@AE@% is the name of a record type that was previously declared by using the %@AB@%RECORD%@AE@% directive.%@NL@% %@NL@% %@4@% An %@AI@%initialvalue%@AE@% for each field in the record can be given as an integer, a%@EH@% character constant, or an expression that resolves to a value compatible with the size of the field. Angle brackets (%@AB@%< >%@AE@%) are required even if no initial value is given. If initial values for more than one field are given, the values must be separated by commas.%@NL@% %@NL@% %@4@%%@CR:IX7.46 @%%@CR:IX7.47 @% If the %@AB@%DUP%@AE@% operator (see Section 6.5.2%@BO: 65365@%, "Arrays and Buffers") is used to%@EH@% initialize multiple record variables, only the angle brackets and initial values, if given, need to be enclosed in parentheses. For example, you can define an array of record variables as shown below:%@NL@% %@NL@% %@AS@%xmas color 50 DUP (<1,2,0,4>)%@AE@%%@NL@% %@NL@% %@4@% You do not have to initialize all fields in a record. If an initial value%@EH@% is left blank, the assembler automatically uses the default initial value of the field. This is declared by the record type. If there is no default value, each bit in the field is cleared.%@NL@% %@NL@% %@4@% Sections 7.2.3%@BO: 6e990@%, "Using Record Operands and Record Variables," and 7.2.4%@BO: 6e991@%,%@EH@% "Record Operators," illustrate ways to use record data after it has been declared.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3 ; Record declaration%@AE@%%@NL@% %@AS@%warning color <1,0,1,4> ; Record definition%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.48 @% The definition above creates a variable named %@AS@%warning %@AE@%whose type is given%@EH@% by the record type %@AS@%color%@AE@%. The initial values of the fields in the variable are set to the values given in the record definition. The initial values would override the default record values, had any been given in the declaration. The contents of the record variable are shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 7.2.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3 ; Record declaration%@AE@%%@NL@% %@AS@%colors color 16 DUP (<>) ; Record declaration%@AE@%%@NL@% %@NL@% %@4@% Example 2 creates an array named %@AS@%colors %@AE@%containing 16 variables of type%@EH@% %@AS@%color%@AE@%. Since no initial values are given in either the declaration or the definition, the variables have undefined (0) values.%@NL@% %@NL@% %@4@% %@AB@%Example 3%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%cw RECORD r1:3=0,ic:1=0,rc:2=0,pc:2=3,r2:2=1,masks:6=63%@AE@%%@NL@% %@AS@%newcw cw <,,2,,,>%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.49 @% Example 3 creates a variable named %@AS@%newcw %@AE@%with type %@AS@%cw%@AE@%. The default values%@EH@% set in the type declaration are used for all fields except the %@AS@%rc %@AE@%field. This field is set to 2. The contents of the variable are shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 7.2.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.3 @%%@AB@%7.2.3 Using Record Operands and Record Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.50 @%%@CR:IX7.51 @%%@CR:IX7.52 @%%@CR:IX7.53 @%%@CR:IX7.54 @% A record operand refers to the value of a record type. It should not be%@EH@% confused with a record variable. A record operand is a constant; a record variable is a value stored in memory. A record operand can be used with the following syntax:%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%recordname%@AE@% %@AB@%<%@AE@%[[%@AI@%value%@AE@%[[%@AB@%,%@AE@%%@AI@%value%@AE@%]]...]]%@AB@%>%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%recordname%@AE@% must be the name of a record type declared in the source%@EH@% file. The optional %@AI@%value%@AE@% is the value of a field in the record. If more than one %@AI@%value%@AE@% is given, each value must be separated by a comma. Values can include expressions or symbols that evaluate to constants. The enclosing angle brackets (%@AB@%<>%@AE@%) are required, even if no value is given. If no value for a field is given, the default value for that field is used.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3 ; Record declaration%@AE@%%@NL@% %@AS@%window color <0,6,1,6> ; Record definition%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ah,color <0,3,0,2> ; Load record operand%@AE@%%@NL@% %@AS@% ; (constant value 32h)%@AE@%%@NL@% %@AS@% mov bh,window ; Load record variable%@AE@%%@NL@% %@AS@% ; (memory value 6Eh)%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX7.55 @%%@CR:IX7.56 @% In this example, the record operand %@AS@%color <0,3,0,2> %@AE@%and the record%@EH@% variable %@AS@%warning %@AE@%are loaded into registers. The contents of the values are shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 7.2.3 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.4 @%%@AB@%7.2.4 Record Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%WIDTH%@AE@% and %@AB@%MASK%@AE@% operators are used exclusively with records to return%@EH@% constant values representing different aspects of previously declared records.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.4.1 @%%@AB@%7.2.4.1 The MASK Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.57 @%%@CR:IX7.58 @%%@CR:IX7.59 @% The %@AB@%MASK%@AE@% operator returns a bit mask for the bit positions in a record%@EH@% occupied by the given record field. A bit in the mask contains a 1 if that bit corresponds to a field bit. All other bits contain 0.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%MASK%@AE@% {%@AI@%recordfieldname%@AE@% | %@AI@%record%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%recordfieldname%@AE@% may be the name of any field in a previously defined%@EH@% record. The %@AI@%record%@AE@% may be the name of any previously defined record. The %@AB@%NOT%@AE@% operator is sometimes used with the %@AB@%MASK%@AE@% operator to reverse the bits of a mask.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3%@AE@%%@NL@% %@AS@%message color <0,5,1,1>%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ah,message ; Load initial 0101 1001%@AE@%%@NL@% %@AS@% and ah,NOT MASK back ; Turn off AND 1000 1111%@AE@%%@NL@% %@AS@% ; "back" ---------%@AE@%%@NL@% %@AS@% ; 0000 1001%@AE@%%@NL@% %@AS@% or ah,MASK blink ; Turn on OR 1000 0000%@AE@%%@NL@% %@AS@% ; "blink" ---------%@AE@%%@NL@% %@AS@% ; 1000 1001%@AE@%%@NL@% %@AS@% xor ah,MASK intense ; Toggle XOR 0000 1000%@AE@%%@NL@% %@AS@% ; "intense" ---------%@AE@%%@NL@% %@AS@% ; 1000 0001%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.4.2 @%%@AB@%7.2.4.2 The WIDTH Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.60 @%%@CR:IX7.61 @%%@CR:IX7.62 @% The %@AB@%WIDTH%@AE@% operator returns the width (in bits) of a record or record%@EH@% field.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%WIDTH%@AE@% {%@AI@%recordfieldname%@AE@% | %@AI@%record%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%recordfieldname%@AE@% may be the name of any field defined in any record.%@EH@% The %@AI@%record%@AE@% may be the name of any defined record.%@NL@% %@NL@% %@4@% Note that the width of a field is the number of bits assigned for that%@EH@% field; the value of the field is the starting position (from the right) of the field.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3%@AE@%%@NL@% %@NL@% %@AS@%wblink EQU WIDTH blink ; "wblink" = 1 "blink" = 7%@AE@%%@NL@% %@AS@%wback EQU WIDTH back ; "wback" = 3 "back" = 4%@AE@%%@NL@% %@AS@%wintense EQU WIDTH intense ; "wintense" = 1 "intense" = 3%@AE@%%@NL@% %@AS@%wfore EQU WIDTH fore ; "wfore" = 3 "fore" = 0%@AE@%%@NL@% %@AS@%wcolor EQU WIDTH color ; "wcolor" = 8%@AE@%%@NL@% %@NL@% %@AS@%prompt color <1,5,1,1>%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% IF (WIDTH color) GE 8 ; If color is 16 bit, load%@AE@%%@NL@% %@AS@% mov ax,prompt ; into 16-bit register%@AE@%%@NL@% %@AS@% ELSE ; else%@AE@%%@NL@% %@AS@% mov al,prompt ; load into low 8-bit register%@AE@%%@NL@% %@AS@% xor ah,ah ; and clear high 8-bit register%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC7.2.5 @%%@AB@%7.2.5 Using Record-Field Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX7.63 @%%@CR:IX7.64 @% Record-field operands represent the location of a field in its%@EH@% corresponding record. The operand evaluates to the bit position of the low-order bit in the field and can be used as a constant operand. The field name must be from a previously declared record.%@NL@% %@NL@% %@4@% Record-field operands are often used with the %@AB@%WIDTH%@AE@% and %@AB@%MASK%@AE@% operators, as%@EH@% described in Sections 7.2.4.1%@BO: 6f465@% and 7.2.4.2%@BO: 6fc80@%.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%color RECORD blink:1,back:3,intense:1,fore:3 ; Record declaration%@AE@%%@NL@% %@AS@%cursor color <1,5,1,1> ; Record definition%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@AS@%; Rotate "back" of "cursor" without changing other values%@AE@%%@NL@% %@NL@% %@AS@% mov al,cursor ; Load value from memory%@AE@%%@NL@% %@AS@% mov ah,al ; Save a copy for work 1101 1001=ah/al%@AE@%%@NL@% %@AS@% and al,NOT MASK back ; Mask out old bits AND 1000 1111=mask%@AE@%%@NL@% %@AS@% ; to save old cursor ---------%@AE@%%@NL@% %@AS@% ; 1000 1001=al%@AE@%%@NL@% %@AS@% mov cl,back ; Load bit position%@AE@%%@NL@% %@AS@% shr ah,cl ; Shift to right 0000 1101=ah%@AE@%%@NL@% %@AS@% inc ah ; Increment 0000 1110=ah%@AE@%%@NL@% %@NL@% %@AS@% shl ah,cl ; Shift left again 1110 0000=ah%@AE@%%@NL@% %@AS@% and ah,MASK back ; Mask off extra bits AND 0111 0000=mask%@AE@%%@NL@% %@AS@% ; to get new cursor ---------%@AE@%%@NL@% %@AS@% ; 0110 0000 ah%@AE@%%@NL@% %@AS@% or ah,al ; Combine old and new OR 1000 1001 al%@AE@%%@NL@% %@AS@% ; ---------%@AE@%%@NL@% %@AS@% mov cursor,ah ; Write back to memory 1110 1001 ah%@AE@%%@NL@% %@NL@% %@4@% This example illustrates several ways in which record fields can be used%@EH@% as operands and in expressions.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH8 @%%@AB@%Chapter 8: Creating Programs from Multiple Modules%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX8.1 @%%@CR:IX8.2 @%%@CR:IX8.3 @%%@CR:IX8.4 @% Most medium and large assembly-language programs are created from several%@EH@% source files or modules. When several modules are used, the scope of symbols becomes important. This chapter discusses the scope of symbols and explains how to declare global symbols that can be accessed from any module. It also tells you how to specify a module that will be accessed from a library.%@NL@% %@NL@% %@4@%%@CR:IX8.5 @%%@CR:IX8.6 @%%@CR:IX8.7 @% Symbols, such as labels and variable names, can be either local or global%@EH@% in scope. By default, all symbols are local; they are specific to the source file in which they are defined. Symbols must be declared global if they must be accessed from modules other than the one in which they are defined.%@NL@% %@NL@% %@4@%%@CR:IX8.8 @%%@CR:IX8.9 @%%@CR:IX8.10 @%%@CR:IX8.11 @%%@CR:IX8.12 @%%@CR:IX8.13 @%%@CR:IX8.14 @%%@CR:IX8.15 @% To declare symbols global, they must be declared public in the source%@EH@% module in which they are defined. They must also be declared external in any module that must access the symbol. If the symbol represents uninitialized data, it can be declared communal──meaning that the symbol is both public and external. The %@AB@%PUBLIC%@AE@%, %@AB@%EXTRN%@AE@%, and %@AB@%COMM %@AE@%directives are used to declare symbols public, external, and communal, respectively.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The term "local" often has a different meaning in assembly language than in many high-level languages. Local symbols in compiled languages are symbols that are known only within a procedure (called a function, routine, subprogram, or subroutine, depending on the language). You can use QuickAssembler to generate these kinds of variables, as explained in Section 15.3.6%@BO: cb138@%, "Creating Locals Automatically."%@NL@% %@CR:IX8.16 @%%@CR:IX8.17 @%%@NL@% By default, the assembler converts all lowercase letters in names declared with the %@AB@%PUBLIC%@AE@%, %@AB@%EXTRN%@AE@%, and %@AB@%COMM%@AE@% directives to uppercase letters before copying the name to the object file. To preserve lowercase names in public symbols, choose Preserve Case or Preserve Extrn from the Assembler Flags dialog box, or assemble with /Cx or /Cl on the QCL command line. This should be done when preparing assembler modules to be linked with modules from C and other case-sensitive languages.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC8.1 @%%@AB@%8.1 Declaring Symbols Public%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX8.18 @%%@CR:IX8.19 @%%@CR:IX8.20 @%%@CR:IX8.21 @%%@CR:IX8.22 @%%@CR:IX8.23 @%%@CR:IX8.24 @% The %@AB@%PUBLIC%@AE@% directive is used to declare symbols public so that they can be%@EH@% accessed from other modules. If a symbol is not declared public, the symbol name is not written to the object file. The symbol has the value of its offset address during assembly, but the name and address are not available to the linker.%@NL@% %@NL@% %@4@% If the symbol is declared public, its name is associated with its offset%@EH@% address in the object file. During linking, symbols in different modules──but with the same name──are resolved to a single address.%@NL@% %@NL@% %@4@%%@CR:IX8.25 @% Public symbol names are also used by some symbolic debuggers (such as%@EH@% SYMDEB) to associate addresses with symbols.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%PUBLIC%@AE@% %@AI@%declaration%@AE@% [[%@AB@%,%@AE@%%@AI@%declaration%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% Each %@AI@%declaration%@AE@% has the following syntax:%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%lang%@AE@%]] %@AI@%name%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX8.26 @%%@CR:IX8.27 @% The optional %@AI@%lang%@AE@% field contains a language specifier that overrides the%@EH@% language specified by the %@AB@%.MODEL%@AE@% directive. With this statement, the language specifier determines naming conventions for the variable that it precedes. The specifier can be %@AB@%C%@AE@%, %@AB@%FORTRAN%@AE@%, %@AB@%Pascal%@AE@%, or %@AB@%BASIC%@AE@%. The C naming convention prefixes each variable with an underscore (_); the other conventions do not. If you specify %@AI@%lang%@AE@% with the %@AB@%.MODEL%@AE@% directive, all procedures are automatically public. However, you must use the %@AB@%PUBLIC%@AE@% directive for any data that you want to access from other modules.%@NL@% %@NL@% %@4@% Note that using the %@AB@%C%@AE@% type specifier does not preserve case. You must%@EH@% choose one of the assembler flags or options that preserve case.%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% must be the name of a variable, label, or numeric equate defined%@EH@% within the current source file. %@AB@%PUBLIC%@AE@% declarations can be placed anywhere in the source file. Equate names, if given, can only represent one-byte or two-byte integer or string values. Text macros (or text equates) cannot be declared public.%@NL@% %@NL@% %@4@% Note that although absolute symbols can be declared public, aliases for%@EH@% public symbols may cause errors. For example, the following statements are illegal:%@NL@% %@NL@% %@AS@% PUBLIC lines ; Declare absolute symbol public%@AE@%%@NL@% %@AS@%lines EQU rows ; Declare alias for lines%@AE@%%@NL@% %@AS@%rows EQU 25 ; Illegal - Assign value to alias%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% PUBLIC true,status,first,clear%@AE@%%@NL@% %@AS@%true EQU -1 ; Public constant%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%status DB 1 ; Public variable%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%first LABEL FAR ; Public label%@AE@%%@NL@% %@AS@%clear PROC ; Procedure names are automatically public%@AE@%%@NL@% %@AS@% . ; with .MODEL model, lang%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%clear ENDP%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC8.2 @%%@AB@%8.2 Declaring Symbols External%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX8.28 @%%@CR:IX8.29 @%%@CR:IX8.30 @%%@CR:IX8.31 @%%@CR:IX8.32 @%%@CR:IX8.33 @%%@CR:IX8.34 @% If a symbol undeclared in a module must be accessed by instructions in%@EH@% that module, it must be declared with the %@AB@%EXTRN%@AE@% directive.%@NL@% %@NL@% %@4@%%@CR:IX8.35 @% This directive tells the assembler not to generate an error, even though%@EH@% the symbol is not in the current module. The assembler assumes that the symbol occurs in another module. However, the symbol must actually exist and must be declared public in some module. Otherwise, the linker generates an error.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%EXTRN%@AE@% %@AI@%declaration%@AE@%%@AB@% %@AE@%[[%@AB@%,%@AE@%%@AI@%declaration%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% Each %@AI@%declaration%@AE@% has the following syntax:%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%lang%@AE@%]]%@AI@%name%@AE@%%@AB@%:%@AE@%%@AI@%type%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The optional %@AI@%lang%@AE@% field contains a language specifier that overrides the%@EH@% language specified by the %@AB@%.MODEL%@AE@% directive. With this statement, the language specifier determines naming conventions for the variable that it precedes. The specifier can be %@AB@%C%@AE@%, %@AB@%FORTRAN%@AE@%, %@AB@%Pascal%@AE@%, or %@AB@%BASIC%@AE@%. The C naming convention prefixes each variable with an underscore (_); the other conventions do not.%@NL@% %@NL@% %@4@% Note that using the %@AB@%C%@AE@% type specifier does not preserve case. You must%@EH@% choose one of the assembler flags or options that preserve case.%@NL@% %@NL@% %@4@% The %@AB@%EXTRN%@AE@% directive defines an external variable, label, or symbol of the%@EH@% specified %@AI@%name%@AE@% and %@AI@%type%@AE@%. The %@AI@%type%@AE@% must match the type given to the item in its actual definition in some other module. It can be any one of the following:%@NL@% %@NL@% %@AB@%Description%@AE@% %@AB@%Types%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Distance specifier %@AB@%NEAR%@AE@%, %@AB@%FAR%@AE@%, or %@AB@%PROC%@AE@%%@NL@% %@NL@% Size specifier %@AB@%BYTE%@AE@%, %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%, %@AB@%QWORD%@AE@%, or %@AB@%TBYTE%@AE@%%@NL@% %@NL@% Absolute %@AB@%ABS%@AE@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX8.36 @%%@CR:IX8.37 @%%@CR:IX8.38 @%%@CR:IX8.39 @%%@CR:IX8.40 @%%@CR:IX8.41 @%%@CR:IX8.42 @% The %@AB@%ABS%@AE@% type is for symbols that represent constant numbers, such as%@EH@% equates declared with the %@AB@%EQU%@AE@% and %@AB@%=%@AE@% directives (see Section 11.1%@BO: 8dc18@%, "Using Equates").%@NL@% %@NL@% %@4@%%@CR:IX8.43 @%%@CR:IX8.44 @%%@CR:IX8.45 @%%@CR:IX8.46 @%%@CR:IX8.47 @% The %@AB@%PROC%@AE@% type represents the default type for a procedure. For programs%@EH@% that use simplified segment directives, the type of an external symbol declared with %@AB@%PROC%@AE@% will be %@AB@%NEAR%@AE@% for small or compact model, or %@AB@%FAR%@AE@% for medium, large, or huge model. Section 5.1.3%@BO: 42b44@%, "Defining Basic Attributes of the Model," tells you how to declare the memory model using the %@AB@%.MODEL%@AE@% directive. If full segment definitions are used, the default type represented by %@AB@%PROC%@AE@% is always %@AB@%NEAR%@AE@%.%@NL@% %@NL@% %@4@% Although the actual address of an external symbol is not determined until%@EH@% link time, the assembler assumes a default segment for the item, based on where the %@AB@%EXTRN%@AE@% directive is placed in the source code. Placement of %@AB@%EXTRN%@AE@% directives should follow these rules:%@NL@% %@NL@% ■ %@AB@%NEAR%@AE@% code labels (such as procedures) must be declared in the code segment from which they are accessed.%@NL@% %@NL@% ■ %@AB@%FAR%@AE@% code labels can be declared anywhere in the source code. It may be convenient to declare them in the code segment from which they are accessed if the label may be %@AB@%FAR%@AE@% in one context or %@AB@%NEAR%@AE@% in another.%@NL@% %@NL@% ■ Data must be declared in the segment in which it occurs. This may require that you define a dummy data segment for the external declaration.%@NL@% %@NL@% ■ Absolute symbols can be declared anywhere in the source code.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% EXTRN max:ABS,act:FAR ; Constant or FAR label anywhere%@AE@%%@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .STACK 100h%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% EXTRN nvar:BYTE ; NEAR variable in near data%@AE@%%@NL@% %@AS@% .FARDATA%@AE@%%@NL@% %@AS@% EXTRN fvar:WORD ; FAR variable in far data%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@AS@% EXTRN task:PROC ; PROC or NEAR in near code%@AE@%%@NL@% %@AS@% ASSUME es:SEG fvar ; Tell assembler%@AE@%%@NL@% %@AS@% mov ax,SEG fvar ; Tell processor that ES%@AE@%%@NL@% %@AS@% mov es,ax ; has far data segment%@AE@%%@NL@% %@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ah,nvar ; Load external NEAR variable%@AE@%%@NL@% %@AS@% mov bx,fvar ; Load external FAR variable%@AE@%%@NL@% %@AS@% mov cx,max ; Load external constant%@AE@%%@NL@% %@AS@% call task ; Call procedure (NEAR or FAR)%@AE@%%@NL@% %@AS@% jmp act ; Jump to FAR label%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% The example above shows how each type of external symbol could be declared%@EH@% and used in a small-model program that uses simplified segment directives. Notice the use of the %@AB@%PROC%@AE@% type specifier to make the external-procedure memory model independent. The jump and its external declaration are written so that they will be %@AB@%FAR%@AE@% regardless of the memory model. Using these techniques, you can change the memory model without breaking code.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% EXTRN max:ABS,act:FAR ; Constant or FAR label anywhere%@AE@%%@NL@% %@AS@%STACK SEGMENT PARA STACK 'STACK'%@AE@%%@NL@% %@AS@% DB 100h DUP (?)%@AE@%%@NL@% %@AS@%STACK ENDS%@AE@%%@NL@% %@AS@%_DATA SEGMENT WORD PUBLIC 'DATA'%@AE@%%@NL@% %@AS@% EXTRN nvar:BYTE ; NEAR variable in near data%@AE@%%@NL@% %@AS@%_DATA ENDS%@AE@%%@NL@% %@AS@%FAR_DATA SEGMENT PARA 'FAR_DATA'%@AE@%%@NL@% %@AS@% EXTRN fvar:WORD ; FAR variable in far data%@AE@%%@NL@% %@AS@%FAR_DATA ENDS%@AE@%%@NL@% %@NL@% %@AS@%DGROUP GROUP _DATA,STACK%@AE@%%@NL@% %@AS@%_TEXT SEGMENT BYTE PUBLIC 'CODE'%@AE@%%@NL@% %@AS@% EXTRN task:NEAR ; NEAR procedure in near code%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT,ds:DGROUP,ss:STACK%@AE@%%@NL@% %@AS@%start: mov ax,DGROUP ; Load segment%@AE@%%@NL@% %@AS@% mov ds,ax ; into DS%@AE@%%@NL@% %@AS@% ASSUME es:SEG fvar ; Tell assembler%@AE@%%@NL@% %@AS@% mov ax,SEG fvar ; Tell processor that ES%@AE@%%@NL@% %@AS@% mov es,ax ; has far data segment%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ah,nvar ; Load external NEAR variable%@AE@%%@NL@% %@AS@% mov bx,fvar ; Load external FAR variable%@AE@%%@NL@% %@AS@% mov cx,max ; Load external constant%@AE@%%@NL@% %@AS@% call task ; Call NEAR procedure%@AE@%%@NL@% %@AS@% jmp act ; Jump to FAR label%@AE@%%@NL@% %@AS@%_TEXT ENDS%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@4@% Example 2 shows a fragment similar to the one in Example 1, but with full%@EH@% segment definitions. Notice that the types of code labels must be declared specifically. If you wanted to change the memory model, you would have to specifically change each external declaration and each call or jump.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC8.3 @%%@AB@%8.3 Using Multiple Modules%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX8.48 @%%@CR:IX8.49 @%%@CR:IX8.50 @% The following source files illustrate a program that uses public and%@EH@% external declarations to access instruction labels. The program consists of two modules called %@AS@%hello %@AE@%and %@AS@%display%@AE@%.%@NL@% %@NL@% %@4@% The %@AS@%hello %@AE@%module is the program's initializing module. Execution starts at%@EH@% the instruction labeled %@AS@%start %@AE@%in the %@AS@%hello %@AE@%module. After initializing the data segment, the program calls the procedure %@AS@%display %@AE@%in the %@AS@%display%@AE@% module, where a DOS call is used to display a message on the screen. Execution then returns to the address after the call in the %@AS@%hello %@AE@%module.%@NL@% %@NL@% %@4@% The %@AS@%hello %@AE@%module is shown below:%@EH@%%@NL@% %@NL@% %@AS@% TITLE hello%@AE@%%@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% .STACK 256%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% PUBLIC message, lmessage%@AE@%%@NL@% %@AS@%message DB "Hello, world.",13,10%@AE@%%@NL@% %@AS@%lmessage EQU $ - message%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% EXTRN display:PROC ; Declare in near code segment%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@AS@% call display ; Call other module%@AE@%%@NL@% %@AS@% mov ax,04C00h ; Terminate with exit code 0%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% The %@AS@%display %@AE@%module is shown below:%@EH@%%@NL@% %@NL@% %@AS@% TITLE display%@AE@%%@NL@% %@AS@% EXTRN lmessage:ABS ; Declare anywhere%@AE@%%@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% EXTRN message:BYTE ; Declare in near data segment%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%display PROC%@AE@%%@NL@% %@AS@% mov bx,1 ; File handle for standard output%@AE@%%@NL@% %@AS@% mov cx,lmessage ; Message length%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Message address%@AE@%%@NL@% %@AS@% mov ah,40h ; Write function%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@NL@% %@AS@%display ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% The sample program is a variation of the HELLO.ASM program used in the%@EH@% examples in Chapter 4%@BO: 34dba@%, "Writing Stand-Alone Assembly Programs," except that it uses an external procedure to display to the screen. Notice that all symbols defined in one module but used in another are declared %@AB@%PUBLIC%@AE@% in the defining module and declared %@AB@%EXTRN%@AE@% in the using module.%@NL@% %@NL@% %@4@% For instance, %@AS@%message %@AE@%and %@AS@%lmessage %@AE@%are declared %@AB@%PUBLIC%@AE@% in the program%@EH@% HELLO.ASM and declared %@AB@%EXTRN%@AE@% in DISPLAY.ASM. The procedure %@AS@%display %@AE@%is declared %@AB@%EXTRN%@AE@% in HELLO.ASM. The symbol display is automatically public in the simplified segment version, but you would have to specifically declare it %@AB@%PUBLIC%@AE@% if you used full segments.%@NL@% %@NL@% %@4@% To create an executable file for these modules, you can add both files to%@EH@% the environment's Program List dialog box. You can also assemble the modules with the following command line:%@NL@% %@NL@% %@AS@%QCL hello.asm display.asm%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX8.51 @%%@CR:IX8.52 @% The output is placed in the executable file HELLO.EXE.%@EH@%%@NL@% %@NL@% %@4@% For each source module, QuickAssembler writes a module name to the object%@EH@% file. The module name is used by some debuggers and by the linker when it displays error messages. With QuickAssembler, the module name is always the base name of the source module file.%@NL@% %@NL@% %@4@%%@CR:IX8.53 @% For compatibility, QuickAssembler recognizes the %@AB@%NAME%@AE@% directive. However,%@EH@% %@AB@%NAME%@AE@% has no effect. Arguments to the directive are ignored.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC8.4 @%%@AB@%8.4 Declaring Symbols Communal%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX8.54 @%%@CR:IX8.55 @%%@CR:IX8.56 @%%@CR:IX8.57 @%%@CR:IX8.58 @% Communal variables are uninitialized variables that are both public and%@EH@% external. They are often declared in include files.%@NL@% %@NL@% %@4@% If a variable must be used by several assembly routines, you can declare%@EH@% the variable communal in an include file, and then include the file in each of the assembly routines. Although the variable is declared in each source module, it exists at only one address. Using a communal variable in an include file and including it in several source modules is an alternative to defining the variable and declaring it public in one source module and then declaring it external in other modules.%@NL@% %@NL@% %@4@% If a variable is declared communal in one module and public in another,%@EH@% the public declaration takes precedence and the communal declaration has the same effect as an external declaration.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%COMM %@AE@%%@AI@%definition%@AE@%[[%@AB@%,%@AE@%%@AI@%definition%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% Each %@AI@%definition%@AE@% has the following syntax:%@EH@%%@NL@% %@NL@% %@4@% [[%@AB@%NEAR%@AE@% | %@AB@%FAR%@AE@%]] [[%@AI@%lang%@AE@%]] %@AI@%label%@AE@%:%@AI@%size%@AE@%[[:%@AI@%count%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@% A communal variable can be %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%. If neither is specified, the type%@EH@% will be that of the default memory model. If you use simplified segment directives, the default type is %@AB@%NEAR%@AE@% for small and medium models, or %@AB@%FAR%@AE@% for compact, large, and huge models. If you use full segment definitions, the default type is %@AB@%NEAR%@AE@%.%@NL@% %@NL@% %@4@%%@CR:IX8.59 @% The optional %@AI@%lang%@AE@% field can be %@AB@%C%@AE@%, %@AB@%BASIC%@AE@%, %@AB@%FORTRAN%@AE@%, or %@AB@%Pascal%@AE@%. The use of%@EH@% the %@AB@%C%@AE@% keyword turns on the C naming convention──the assembler prefixes the name of the variable with an underscore (_). The use of any of the other language types turns off the C naming convention, even if you specified %@AB@%C%@AE@% with the %@AB@%.MODEL%@AE@% directive. Note that the use of %@AB@%C%@AE@% does not preserve case. You must choose one of the assembler flags or options that preserve case.%@NL@% %@NL@% %@4@% The %@AI@%label%@AE@% is the name of the variable. The %@AI@%size%@AE@% can be %@AB@%BYTE%@AE@%, %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%,%@EH@% %@AB@%QWORD%@AE@%, %@AB@%TBYTE%@AE@%, or the name of a structure. The %@AI@%count%@AE@% is the number of elements. If no %@AI@%count%@AE@% is given, one element is assumed. Multiple variables can be defined with one %@AB@%COMM%@AE@% statement by separating each definition with a comma.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% C variables declared outside functions (except static variables) are communal unless explicitly initialized; they are the same as assembly-language communal variables. If you are writing assembly-language modules for C, you can declare the same communal variables in C include files and in QuickAssembler include files.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% QuickAssembler cannot tell whether a communal variable has been used in%@EH@% another module. Allocation of communal variables is handled by LINK. As a result, communal variables have the following limitations that other variables declared in assembly language do not have:%@NL@% %@NL@% ■ Communal variables cannot be initialized. Under DOS, initial values are not guaranteed to be 0 or any other value. The variables can be used for data, such as file buffers, that is not given a value until run time.%@NL@% %@NL@% ■ Communal variables are not guaranteed to be allocated in the sequence in which they are declared. Assembly-language techniques that depend on the sequence and position in which data is defined should not be used with communal variables. For example, the following statements do not work:%@NL@% %@NL@% %@NL@% %@AS@% COMM wbuffer:WORD:128%@AE@%%@NL@% %@AS@%lbuffer EQU $ - buffer ; "lbuffer" won't have desired value%@AE@%%@NL@% %@NL@% %@AS@%bbuffer LABEL BYTE ; "bbuffer" won't have desired address%@AE@%%@NL@% %@AS@% COMM wbuffer:WORD:40%@AE@%%@NL@% %@NL@% ■ If a communal variable references a variable that is allocated and declared public inside a module, the variable has the segment of the allocated instance. If all references to the variable are communal, the variable will be placed in one of the segments described below.%@NL@% %@NL@% %@CR:IX8.60 @% Near communal variables are placed in a segment called c_common, which is part of DGROUP. This group is created and initialized automatically if you use simplified segment directives. If you use full segment definitions, you must create a group called DGROUP and use the %@AB@%ASSUME%@AE@% directive to associate it with the DS register.%@NL@% %@NL@% %@CR:IX8.61 @%%@CR:IX8.62 @% Far communal variables are placed in a segment called FAR_BSS. This segment has combine type private and class type 'FAR_BSS'. This means that multiple segments with the same name can be created. Such segments cannot be accessed by name. They must be initialized indirectly using the %@AB@%SEG%@AE@% operator. For example, if a far communal variable (with word size) is called %@AS@%comvar%@AE@%, its segment can be initialized with the following lines:%@NL@% %@NL@% %@AS@% ASSUME ds:SEG comvar ; Tell the assembler%@AE@%%@NL@% %@AS@% mov ax,SEG comvar ; Tell the processor%@AE@%%@NL@% %@AS@% mov ds,ax%@AE@%%@NL@% %@AS@% mov bx,comvar ; Use the variable%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% COMM temp:BYTE:128%@AE@%%@NL@% %@NL@% %@AS@%ASCIIZ MACRO address ;; Name of address for string%@AE@%%@NL@% %@AS@% mov temp,128 ;; Insert maximum length%@AE@%%@NL@% %@AS@% mov dx,OFFSET temp ;; Address of string buffer%@AE@%%@NL@% %@AS@% mov ah,0Ah ;; Get string%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% mov dl,temp[1] ;; Get length of string%@AE@%%@NL@% %@AS@% xor dh,dh%@AE@%%@NL@% %@AS@% mov bx,dx%@AE@%%@NL@% %@AS@% mov temp[bx+2],0 ;; Overwrite CR with null%@AE@%%@NL@% %@AS@%address EQU OFFSET temp+2%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% Example 1 shows an include file that declares a buffer for temporary data.%@EH@% The buffer is then used in a macro in the same include file. An example of how the macro could be used in a source file is shown below:%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small,c%@AE@%%@NL@% %@AS@% INCLUDE communal.inc%@AE@%%@NL@% %@AS@% .STACK%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%message DB "Enter file name: $"%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@AS@% mov dx,OFFSET message ; Load offset of file prompt%@AE@%%@NL@% %@AS@% mov ah,09h ; Display prompt%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@NL@% %@AS@% ASCIIZ place ; Get file name and%@AE@%%@NL@% %@AS@% ; return address as "place"%@AE@%%@NL@% %@NL@% %@AS@% mov al,00000010b ; Load access code%@AE@%%@NL@% %@AS@% mov dx,place ; Load address of ASCIIZ string%@AE@%%@NL@% %@AS@% mov ah,3Dh ; Open the file%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@4@% Note that once the macro is written, the user does not need to know the%@EH@% name of the temporary buffer or how it is used in the macro.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%date STRUC%@AE@%%@NL@% %@NL@% %@AS@% month DB ?%@AE@%%@NL@% %@AS@% day DB ?%@AE@%%@NL@% %@AS@% year DB ?%@AE@%%@NL@% %@NL@% %@AS@%date ENDS%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% COMM today:date%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@4@% The example above uses the %@AB@%COMM%@AE@% directive to make the structure variable%@EH@% %@AS@%today %@AE@%a communal variable.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC8.5 @%%@AB@%8.5 Specifying Library Files%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX8.63 @%%@CR:IX8.64 @%%@CR:IX8.65 @%%@CR:IX8.66 @% The %@AB@%INCLUDELIB%@AE@% directive instructs the linker to link with a specified%@EH@% library file. If you are writing a program that calls library routines, you can use this directive to specify the library file in the assembly source file rather than in the LINK command line.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%INCLUDELIB%@AE@% %@AI@%libraryname%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%libraryname%@AE@% is written to the comment record of the object file. The%@EH@% Intel title for this record is %@AB@%COMENT%@AE@%. At link time, the linker reads this record and links with the specified library file.%@NL@% %@NL@% %@4@% The %@AI@%libraryname%@AE@% must be a file name rather than a complete file%@EH@% specification. If you do not specify an extension, the default extension .LIB is assumed. LINK searches directories for the library file in the following order:%@NL@% %@NL@% 1. The current directory%@NL@% %@NL@% 2. Any directories given in the library field of the LINK command line%@NL@% %@NL@% 3. Any directories listed in the LIB environment variable%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% INCLUDELIB graphics%@AE@%%@NL@% %@NL@% %@4@% This statement passes a message from QuickAssembler telling LINK to use%@EH@% library routines from the file GRAPHICS.LIB. If this statement is included in a source file called DRAW.ASM, the program might be linked with the following command line:%@NL@% %@NL@% %@AS@%LINK draw;%@AE@%%@NL@% %@NL@% %@4@% Without the %@AB@%INCLUDELIB%@AE@% directive, the program would have to be linked with%@EH@% the following command line:%@NL@% %@NL@% %@AS@%LINK draw,,,graphics;%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH9 @%%@AB@%Chapter 9: Using Operands and Expressions%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX9.1 @%%@CR:IX9.2 @% Operands are the arguments that define values to be acted on by%@EH@% instructions or directives. Operands can be constants, variables, expressions, or keywords, depending on the instruction or directive and the context of the statement.%@NL@% %@NL@% %@4@%%@CR:IX9.3 @% A common type of operand is an expression. An expression consists of%@EH@% several operands that are combined to describe a value or memory location. Operators indicate the operations to be performed when combining the operands of an expression.%@NL@% %@NL@% %@4@% Expressions are evaluated at assembly time. By using expressions, you can%@EH@% instruct the assembler to calculate values that would be difficult or inconvenient to calculate when you are writing source code.%@NL@% %@NL@% %@4@% This chapter discusses operands, expressions, and operators as they are%@EH@% evaluated at assembly time. See Section 2.6%@BO: 2404d@%, "Addressing Modes," for a discussion of the addressing modes that can be used to calculate operand values at run time. This chapter also discusses the location-counter operand, forward references, and strong typing of operands.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC9.1 @%%@AB@%9.1 Using Operands with Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Each directive requires a specific type of operand. Most directives take%@EH@% string or numeric constants, or symbols or expressions that evaluate to such constants.%@NL@% %@NL@% %@4@% The type of operand varies for each directive, but the operand must always%@EH@% evaluate to a value that is known at assembly time. This differs from instructions, whose operands may not be known at assembly time and may vary at run time. Operands used with instructions are discussed in Section 2.6%@BO: 2404d@%, "Addressing Modes."%@NL@% %@NL@% %@4@% Some directives, such as those used in data declarations, accept labels or%@EH@% variables as operands. When a symbol that refers to a memory location is used as an operand to a directive, the symbol represents the address of the symbol rather than its contents. This is because the contents may change at run time and are therefore not known at assembly time.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% ORG 100h ; Set address to 100h%@AE@%%@NL@% %@AS@%var DB 10h ; Address of "var" is 100h%@AE@%%@NL@% %@AS@% ; Value of "var" is 10h%@AE@%%@NL@% %@AS@%pvar DW var ; Address of "pvar" is 101h%@AE@%%@NL@% %@AS@% ; Value of "pvar" is%@AE@%%@NL@% %@AS@% ; address of "var" (100h)%@AE@%%@NL@% %@NL@% %@4@% In Example 1, the operand of the %@AB@%DW%@AE@% directive in the third statement%@EH@% represents the address of %@AS@%var %@AE@%(%@AS@%100h%@AE@%) rather than its contents (%@AS@%10h%@AE@%). The address is relative to the start of the segment in which%@AS@% var%@AE@% is defined.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% TITLE doit ; String%@AE@%%@NL@% %@AS@%_TEXT SEGMENT BYTE PUBLIC 'CODE' ; Key words%@AE@%%@NL@% %@AS@% INCLUDE \include\bios.inc ; Pathname%@AE@%%@NL@% %@AS@% .RADIX 16 ; Numeric constant%@AE@%%@NL@% %@AS@%tst DW a / b ; Numeric expression%@AE@%%@NL@% %@AS@% PAGE + ; Special character%@AE@%%@NL@% %@AS@%sum EQU x * y ; Numeric expression%@AE@%%@NL@% %@AS@%here LABEL WORD ; Type specifier%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates the different kinds of values that can be used as%@EH@% directive operands.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC9.2 @%%@AB@%9.2 Using Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The assembler provides a variety of operators for combining, comparing,%@EH@% changing, or analyzing operands. Some operators work with integer constants, some with memory values, and some with both. Operators cannot be used with floating-point constants since QuickAssembler%@AB@% %@AE@%does not recognize real numbers in expressions.%@NL@% %@NL@% %@4@% It is important to understand the difference between operators and%@EH@% instructions. Operators handle calculations of constant values that are known at assembly time. Instructions handle calculations of values that may not be known until run time. For example, the addition operator (%@AB@%+%@AE@%) handles assembly-time addition, while the %@AB@%ADD %@AE@%and %@AB@%ADC%@AE@% instructions handle run-time addition.%@NL@% %@NL@% %@4@% This section describes the different kinds of operators used in%@EH@% assembly-language statements and gives examples of expressions formed with them. In addition to the operators described in this chapter, you can use the %@AB@%DUP%@AE@% operator (Section 6.5.2%@BO: 65365@%, "Arrays and Buffers"), the record operators (Section 7.2.4%@BO: 6f341@%, "Record Operators"), and the macro operators (Section 11.4%@BO: 982dc@%, "Using Macro Operators").%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.1 @%%@AB@%9.2.1 Calculation Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.4 @% QuickAssembler provides the common arithmetic operators as well as several%@EH@% other operators for adding, shifting, or doing bit manipulations. The sections below describe operators that can be used for doing numeric calculations.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.1.1 @%%@AB@%9.2.1.1 Arithmetic Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.5 @%%@CR:IX9.6 @% QuickAssembler recognizes a variety of arithmetic operators for common%@EH@% mathematical operations. Table 9.1 lists the arithmetic operators.%@NL@% %@NL@% %@AB@%Table 9.1 Arithmetic Operators%@AE@%%@NL@% %@NL@% %@AB@%Operator%@AE@% %@AB@%Syntax%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%+%@AE@% %@AB@%+%@AE@%%@AI@%expression%@AE@% Positive (unary)%@NL@% %@NL@% %@AB@%-%@AE@% %@AB@%-%@AE@%%@AI@%expression%@AE@% Negative (unary)%@NL@% %@NL@% %@AB@%*%@AE@% %@AI@%expression1%@AE@%%@AB@% * %@AE@%%@AI@%expression2%@AE@% Multiplication%@NL@% %@NL@% %@AB@%/%@AE@% %@AI@%expression1%@AE@%%@AB@% / %@AE@%%@AI@%expression2%@AE@% Integer division%@NL@% %@NL@% %@AB@%MOD%@AE@% %@AI@%expression1%@AE@% %@AB@%MOD%@AE@% %@AI@%expression2%@AE@% Remainder (modulus)%@NL@% %@NL@% %@AB@%+%@AE@% %@AI@%expression1%@AE@%%@AB@% + %@AE@%%@AI@%expression2%@AE@% Addition%@NL@% %@NL@% %@AB@%-%@AE@% %@AI@%expression1%@AE@%%@AB@% - %@AE@%%@AI@%expression2%@AE@% Subtraction%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% For all arithmetic operators except the addition operator (%@AB@%+%@AE@%) and the%@EH@% subtraction operator (%@AB@%-%@AE@%), the expressions operated on must be integer constants.%@NL@% %@NL@% %@4@% The addition and subtraction operators can be used to add or subtract an%@EH@% integer constant and a memory operand. The result can be used as a memory operand.%@NL@% %@NL@% %@4@% The subtraction operator can also be used to subtract one memory operand%@EH@% from another, but only if the operands refer to locations within the same segment. The result will be a constant, not a memory operand.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The unary plus and minus operators (used to designate positive or negative numbers) are not the same as the binary plus and minus operators (used to designate addition or subtraction). The unary plus and minus operators have a higher level of precedence, as described in Section 9.2.5%@BO: 83028@%, "Operator Precedence."%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%intgr = 14 * 3 ; = 42%@AE@%%@NL@% %@AS@%intgr = intgr / 4 ; 42 / 4 = 10%@AE@%%@NL@% %@AS@%intgr = intgr MOD 4 ; 10 mod 4 = 2%@AE@%%@NL@% %@AS@%intgr = intgr + 4 ; 2 + 4 = 6%@AE@%%@NL@% %@AS@%intgr = intgr - 3 ; 6 - 3 = 3%@AE@%%@NL@% %@AS@%intgr = -intgr - 8 ; -3 - 8 = -11%@AE@%%@NL@% %@AS@%intgr = -intgr - intgr ; 11 - -11 = 22%@AE@%%@NL@% %@NL@% %@4@% Example 1 illustrates arithmetic operators used in integer expressions.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% ORG 100h%@AE@%%@NL@% %@AS@%a DB ? ; Address is 100h%@AE@%%@NL@% %@AS@%b DB ? ; Address is 101h%@AE@%%@NL@% %@AS@%mem1 EQU a + 5 ; mem1 = 100h + 5 = 105h%@AE@%%@NL@% %@AS@%mem2 EQU a - 5 ; mem2 = 100h - 5 = 0FBh%@AE@%%@NL@% %@AS@%const EQU b - a ; const = 101h - 100h = 1%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates arithmetic operators used in memory expressions.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.1.2 @%%@AB@%9.2.1.2 Structure-Field-Name Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.7 @%%@CR:IX9.8 @% The structure-field-name operator (%@AB@%.%@AE@%) indicates addition. It is used to%@EH@% designate a field within a structure.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%variable%@AE@%%@AB@%.%@AE@%%@AI@%field%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%variable%@AE@% is a memory operand (usually a previously declared structure%@EH@% variable), and %@AI@%field%@AE@% is the name of a field within the structure. See Section 7.1%@BO: 69ad4@%, "Structures," for more information.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%date STRUC ; Declare structure%@AE@%%@NL@% %@AS@% month DB ?%@AE@%%@NL@% %@AS@% day DB ?%@AE@%%@NL@% %@AS@% year DW ?%@AE@%%@NL@% %@AS@%date ENDS%@AE@%%@NL@% %@AS@%yesterday date <12,31,1987> ; Define structure variables%@AE@%%@NL@% %@AS@%today date <1,1,1988>%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov bh,yesterday.day ; Load structure variable%@AE@%%@NL@% %@AS@% mov bx,OFFSET today ; Load structure variable address%@AE@%%@NL@% %@AS@% inc [bx].year ; Use in indirect memory operand%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.1.3 @%%@AB@%9.2.1.3 Index Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.9 @%%@CR:IX9.10 @%%@CR:IX9.11 @%%@CR:IX9.12 @% The index operator (%@AB@%[ ]%@AE@%) indicates addition. It is similar to the addition%@EH@% (%@AB@%+%@AE@%) operator. When used with a register, the index operator also indicates that the operand is an indirect memory operand rather than a register-direct operand.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%expression1%@AE@%]][%@AI@%expression2%@AE@%]%@EH@%%@NL@% %@NL@% %@4@% In most cases %@AI@%expression1%@AE@% is simply added to %@AI@%expression2%@AE@%. The limitations%@EH@% of the addition operator for adding memory operands also apply to the index operator. For example, two direct memory operands cannot be added. The expression %@AS@%label1[label2]%@AE@% is illegal if both are memory operands.%@NL@% %@NL@% %@4@% The index operator has an extended function in specifying indirect memory%@EH@% operands. Section 2.6.4%@BO: 27375@% explains the use of indirect memory operands. The index brackets must be outside the register or registers that specify the indirect displacement. However, any of the three operators that indicate addition (the addition operator, the index operator, or the structure-field-name operator) may be used for multiple additions within the expression.%@NL@% %@NL@% %@4@% For example, the following statements are equivalent:%@EH@%%@NL@% %@NL@% %@AS@% mov ax,table[bx][di]%@AE@%%@NL@% %@AS@% mov ax,table[bx+di]%@AE@%%@NL@% %@AS@% mov ax,[table+bx+di]%@AE@%%@NL@% %@AS@% mov ax,[table][bx][di]%@AE@%%@NL@% %@NL@% %@4@% The following statements are illegal because the index operator does not%@EH@% enclose the registers that specify indirect displacement:%@NL@% %@NL@% %@AS@% mov ax,table+bx+di ; Illegal - no index operator%@AE@%%@NL@% %@AS@% mov ax,[table]+bx+di ; Illegal - registers not%@AE@%%@NL@% %@AS@% ; inside index operator%@AE@%%@NL@% %@NL@% %@4@% The index operator is typically used to index elements of a data object,%@EH@% such as variables in an array or characters in a string.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov al,string[3] ; Get 4th element of string%@AE@%%@NL@% %@AS@% add ax,array[4] ; Add 5th element of array%@AE@%%@NL@% %@AS@% mov string[7],al ; Load into 8th element of string%@AE@%%@NL@% %@NL@% %@4@% Example 1 illustrates the index operator used with direct memory operands.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,[bx] ; Get element BX points to%@AE@%%@NL@% %@AS@% add ax,array[si] ; Add element SI points to%@AE@%%@NL@% %@AS@% mov string[di],al ; Load element DI points to%@AE@%%@NL@% %@AS@% cmp cx,table[bx][di] ; Compare to element BX and DI point to%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates the index operator used with indirect memory%@EH@% operands.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.1.4 @%%@AB@%9.2.1.4 Shift Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.13 @%%@CR:IX9.14 @%%@CR:IX9.15 @%%@CR:IX9.16 @%%@CR:IX9.17 @%%@CR:IX9.18 @% The %@AB@%SHR%@AE@% and %@AB@%SHL%@AE@% operators can be used to shift bits in constant values.%@EH@% Both perform logical shifts. Bits on the right for %@AB@%SHL%@AE@% and on the left for %@AB@%SHR%@AE@% are zero-filled as their contents are shifted out of position.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%expression%@AE@% %@AB@%SHR%@AE@% %@AI@%count%@AE@%%@EH@%%@NL@% %@AI@%expression%@AE@% %@AB@%SHL%@AE@% %@AI@%count%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%expression%@AE@% is shifted right or left by %@AI@%count%@AE@% number of bits. Bits%@EH@% shifted off either end of the expression are lost. If %@AI@%count%@AE@% is greater than or equal to 16, the result is 0.%@NL@% %@NL@% %@4@% Do not confuse the %@AB@%SHR%@AE@% and %@AB@%SHL%@AE@% operators with the processor instructions%@EH@% having the same names. The operators work on integer constants only at assembly time. The processor instructions work on register or memory values at run time. The assembler can tell the difference between instructions and operands from context.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%mov ax,01110111b SHL 3 ; Load 01110111000b%@AE@%%@NL@% %@AS@%mov ah,01110111b SHR 3 ; Load 01110b%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.1.5 @%%@AB@%9.2.1.5 Bitwise Logical Operators%@AE@%%@EH@%%@NL@% %@CR:IX9.19 @%%@CR:IX9.20 @%%@CR:IX9.21 @%%@CR:IX9.22 @%%@CR:IX9.23 @%%@CR:IX9.24 @%%@CR:IX9.25 @%%@NL@% %@4@%%@CR:IX9.26 @%%@CR:IX9.27 @%%@CR:IX9.28 @%%@CR:IX9.29 @%%@CR:IX9.30 @% The bitwise operators perform logical operations on each bit of an%@EH@% expression. The expressions must resolve to constant values. Table 9.2 lists the logical operators and their meanings.%@NL@% %@NL@% %@AB@%Table 9.2 Logical Operators%@AE@%%@NL@% %@NL@% %@AB@%Operator%@AE@% %@AB@%Syntax%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOT%@AE@% %@AB@%NOT%@AE@% %@AI@%expression%@AE@% Bitwise complement%@NL@% %@NL@% %@AB@%AND%@AE@% %@AI@%expression1%@AE@% %@AB@%AND%@AE@% %@AI@%expression2%@AE@% Bitwise %@AB@%AND%@AE@%%@NL@% %@NL@% %@AB@%OR%@AE@% %@AI@%expression1%@AE@%%@AB@% OR%@AE@% %@AI@%expression2%@AE@% Bitwise inclusive %@AB@%OR%@AE@%%@NL@% %@NL@% %@AB@%XOR%@AE@% %@AI@%expression1%@AE@%%@AB@% XOR%@AE@% %@AI@%expression2%@AE@% Bitwise exclusive %@AB@%XOR%@AE@%%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% Do not confuse the %@AB@%NOT%@AE@%, %@AB@%AND%@AE@%, %@AB@%OR%@AE@%, and %@AB@%XOR%@AE@% operators with the processor%@EH@% instructions having the same names. The operators work on integer constants only at assembly time. The processor instructions work on register or memory values at run time. The assembler can tell the difference between instructions and operands from context.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Although calculations on expressions using the %@AB@%AND%@AE@%, %@AB@%OR,%@AE@% and %@AB@%XOR%@AE@% operators are done using 17-bit numbers, the results are truncated to 16 bits.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,NOT 11110000b ; Load 1111111100001111b%@AE@%%@NL@% %@AS@% mov ah,NOT 11110000b ; Load 00001111b%@AE@%%@NL@% %@AS@% mov ah,01010101b AND 11110000b ; Load 01010000b%@AE@%%@NL@% %@AS@% mov ah,01010101b OR 11110000b ; Load 11110101b%@AE@%%@NL@% %@AS@% mov ah,01010101b XOR 11110000b ; Load 10100101b%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.2 @%%@AB@%9.2.2 Relational Operators%@AE@%%@EH@%%@NL@% %@CR:IX9.31 @%%@CR:IX9.32 @%%@CR:IX9.33 @%%@CR:IX9.34 @%%@CR:IX9.35 @%%@CR:IX9.36 @%%@CR:IX9.37 @%%@CR:IX9.38 @%%@CR:IX9.39 @%%@CR:IX9.40 @%%@NL@% %@4@%%@CR:IX9.41 @%%@CR:IX9.42 @%%@CR:IX9.43 @%%@CR:IX9.44 @%%@CR:IX9.45 @%%@CR:IX9.46 @% The relational operators compare two expressions and return true (-1) if%@EH@% the condition specified by the operator is satisfied, or false (0) if it is not. The expressions must resolve to constant values. Relational operators are typically used with conditional directives. Table 9.3 lists the operators and the values they return if the specified condition is satisfied.%@NL@% %@NL@% %@AB@%Table 9.3 Relational Operators%@AE@%%@NL@% %@NL@% %@AB@%Operator%@AE@% %@AB@%Syntax%@AE@% %@AB@%Returned Value%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%EQ%@AE@% %@AI@%expression1%@AE@% %@AB@%EQ%@AE@% %@AI@%expression2%@AE@% True if expressions are equal%@NL@% %@NL@% %@AB@%NE%@AE@% %@AI@%expression1%@AE@% %@AB@%NE%@AE@% %@AI@%expression2%@AE@% True if expressions are not equal%@NL@% %@NL@% %@AB@%LT%@AE@% %@AI@%expression1%@AE@% %@AB@%LT%@AE@% %@AI@%expression2%@AE@% True if left expression is less than right%@NL@% %@NL@% %@AB@%LE%@AE@% %@AI@%expression1%@AE@% %@AB@%LE%@AE@% %@AI@%expression2%@AE@% True if left expression is less than or equal to right%@NL@% %@NL@% %@AB@%GT%@AE@% %@AI@%expression1%@AE@% %@AB@%GT%@AE@% %@AI@%expression2%@AE@% True if left expression is greater than right%@NL@% %@NL@% %@AB@%GE%@AE@% %@AI@%expression1%@AE@% %@AB@%GE%@AE@% %@AI@%expression2%@AE@% True if left expression is greater than or equal to right%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% Note that the %@AB@%EQ%@AE@% and %@AB@%NE%@AE@% operators treat their arguments as 16-bit numbers.%@EH@% Numbers specified with the 16th bit set are considered negative. For example, the expression %@AS@%-1 EQ OFFFFh %@AE@%is true, but the expression %@AS@%-1 NE%@AE@% %@AS@%OFFFFh %@AE@%is false.%@NL@% %@NL@% %@4@% The %@AB@%LT%@AE@%, %@AB@%LE%@AE@%, %@AB@%GT%@AE@%, and %@AB@%GE%@AE@% operators treat their arguments as 17-bit numbers,%@EH@% in which the 17th bit specifies the sign. For example, %@AS@%OFFFFh %@AE@%is 65,535, not -1. The expression %@AS@%1 GT -1 %@AE@%is true, but the expression %@AS@%1 GT OFFFFh %@AE@%is false.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,4 EQ 3 ; Load false( 0)%@AE@%%@NL@% %@AS@% mov ax,4 NE 3 ; Load true (-1)%@AE@%%@NL@% %@AS@% mov ax,4 LT 3 ; Load false( 0)%@AE@%%@NL@% %@AS@% mov ax,4 LE 3 ; Load false( 0)%@AE@%%@NL@% %@AS@% mov ax,4 GT 3 ; Load true (-1)%@AE@%%@NL@% %@AS@% mov ax,4 GE 3 ; Load true(-1)%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.3 @%%@AB@%9.2.3 Segment-Override Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.47 @%%@CR:IX9.48 @%%@CR:IX9.49 @%%@CR:IX9.50 @%%@CR:IX9.51 @%%@CR:IX9.52 @%%@CR:IX9.53 @%%@CR:IX9.54 @%%@CR:IX9.55 @% The segment-override operator (%@AB@%:%@AE@%) forces the address of a variable or%@EH@% label to be computed relative to a specific segment.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%segment%@AE@%%@AB@%:%@AE@%%@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%segment%@AE@% can be specified in several ways. It can be one of the segment%@EH@% registers: CS,%@AB@% %@AE@%DS,%@AB@% %@AE@%SS, or%@AB@% %@AE@%ES. It can also be a segment or group name. In this case, the name must have been previously defined with a %@AB@%SEGMENT%@AE@% or %@AB@%GROUP%@AE@% directive and assigned to a segment register with an %@AB@%ASSUME%@AE@% directive. The %@AI@%expression%@AE@% can be a constant, expression, or a %@AB@%SEG%@AE@% expression. See Section 9.2.4.5%@BO: 80576@% for more information on the %@AB@%SEG%@AE@% operator.%@NL@% %@NL@% %@4@% Note that when a segment override is given with an indexed operand, the%@EH@% segment must be specified outside the index operators. For example, %@AS@%es:[di] %@AE@%is correct, but %@AS@%[es:di] %@AE@%generates an error.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,ss:[bx+4] ; Override default assume (DS)%@AE@%%@NL@% %@AS@% mov al,es:082h ; Load from ES%@AE@%%@NL@% %@NL@% %@AS@% ASSUME ds:FAR_DATA ; Tell the assembler and%@AE@%%@NL@% %@AS@% mov bx,FAR_DATA:count ; load from a far segment%@AE@%%@NL@% %@NL@% %@4@% As shown in the last two statements, a segment override with a segment%@EH@% name is not enough if no segment register is assumed for the segment name. You must use the %@AB@%ASSUME%@AE@% directive to assign a segment register, as explained in Section 5.4%@BO: 52503@%, "Associating Segments with Registers."%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4 @%%@AB@%9.2.4 Type Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.56 @% This section describes the assembler operators that specify or analyze the%@EH@% types of memory operands and other expressions.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.1 @%%@AB@%9.2.4.1 PTR Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.57 @%%@CR:IX9.58 @% The %@AB@%PTR%@AE@% operator specifies the type for a variable or label.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%type%@AE@% %@AB@%PTR%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The operator forces %@AI@%expression%@AE@% to be treated as having %@AI@%type%@AE@%. The%@EH@% expression can be any operand. The type can be %@AB@%BYTE%@AE@%, %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%, %@AB@%QWORD%@AE@%, or %@AB@%TBYTE%@AE@% for memory operands. It can be %@AB@%NEAR%@AE@%, %@AB@%FAR%@AE@%, or %@AB@%PROC%@AE@% for labels.%@NL@% %@NL@% %@4@% The %@AB@%PTR%@AE@% operator is typically used with forward references to define%@EH@% explicitly what size or distance a reference has. If it is not used, the assembler assumes a default size or distance for the reference. See Section 9.4%@BO: 84253@% for more information on forward references.%@NL@% %@NL@% %@4@% The %@AB@%PTR%@AE@% operator is also used to enable instructions to access variables%@EH@% in ways that would otherwise generate errors. For example, you could use the %@AB@%PTR%@AE@% operator to access the high-order byte of a %@AB@%WORD%@AE@% size variable. The %@AB@%PTR%@AE@% operator is required for %@AB@%FAR%@AE@% calls and jumps to forward-referenced labels.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%stuff DD ?%@AE@%%@NL@% %@AS@%buffer DB 20 DUP (?)%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% call FAR PTR task ; Call a far procedure%@AE@%%@NL@% %@AS@% jmp FAR PTR place ; Jump far%@AE@%%@NL@% %@NL@% %@AS@% mov bx,WORD PTR stuff[0] ; Load a word from a%@AE@%%@NL@% %@AS@% ; doubleword variable%@AE@%%@NL@% %@AS@% add ax,WORD PTR buffer[bx] ; Add a word from a byte variable%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%PTR%@AE@% operator can be used to specify the size of an indirect register%@EH@% operand for a %@AB@%CALL%@AE@% or %@AB@%JMP%@AE@% instruction. However, the size cannot be specified with %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%. Use %@AB@%WORD%@AE@% or %@AB@%DWORD%@AE@% instead. Examples are shown below:%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% jmp WORD PTR [bx] ; Legal near jump%@AE@%%@NL@% %@AS@% call NEAR PTR [bx] ; Illegal near call%@AE@%%@NL@% %@AS@% call DWORD PTR [bx] ; Legal far call%@AE@%%@NL@% %@AS@% jmp FAR PTR [bx] ; Illegal far jump%@AE@%%@NL@% %@NL@% %@4@% This limitation only applies to indirect register operands. %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%%@EH@% can be applied to operands associated with labels, as shown in Example 1. Furthermore, use %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@% with an indirect operand that combines a register with a label.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.2 @%%@AB@%9.2.4.2 SHORT Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.59 @%%@CR:IX9.60 @% The %@AB@%SHORT%@AE@% operator sets the type of a specified label to %@AB@%SHORT%@AE@%. Short%@EH@% labels can be used in %@AB@%JMP%@AE@% instructions whenever the distance from the label to the instruction is less than 128 bytes.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%SHORT%@AE@% %@AI@%label%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Instructions using short labels are a byte smaller than identical%@EH@% instructions using the default near labels. See Section 9.4.1%@BO: 845be@%, "Forward References to Labels," for information on using the %@AB@%SHORT%@AE@% operator with jump instructions.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% jmp again ; Jump 128 bytes or more%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% jmp SHORT again ; Jump less than 128 bytes%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%again:%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.3 @%%@AB@%9.2.4.3 THIS Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.61 @%%@CR:IX9.62 @% The %@AB@%THIS%@AE@% operator creates an operand whose offset and segment values are%@EH@% equal to the current location-counter value and whose type is specified by the operator.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%THIS%@AE@% %@AI@%type%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%type%@AE@% can be %@AB@%BYTE%@AE@%, %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%, %@AB@%QWORD%@AE@%, or %@AB@%TBYTE%@AE@% for memory operands. It%@EH@% can be %@AB@%NEAR%@AE@%, %@AB@%FAR%@AE@%, or %@AB@%PROC%@AE@% for labels.%@NL@% %@NL@% %@4@% The %@AB@%THIS%@AE@% operator is typically used with the %@AB@%EQU%@AE@% or equal-sign (%@AB@%=%@AE@%)%@EH@% directive to create labels and variables. The result is similar to using the %@AB@%LABEL%@AE@% directive.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%tag1 EQU THIS BYTE ; Both represent the same variable%@AE@%%@NL@% %@AS@%tag2 LABEL BYTE%@AE@%%@NL@% %@NL@% %@AS@%check1 EQU THIS NEAR ; All represent the same address%@AE@%%@NL@% %@AS@%check2 LABEL NEAR%@AE@%%@NL@% %@AS@%check3:%@AE@%%@NL@% %@AS@%check4 PROC NEAR%@AE@%%@NL@% %@AS@%check4 ENDP%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.4 @%%@AB@%9.2.4.4 HIGH and LOW Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.63 @%%@CR:IX9.64 @%%@CR:IX9.65 @%%@CR:IX9.66 @% The %@AB@%HIGH%@AE@% and %@AB@%LOW%@AE@% operators return the high and low bytes, respectively, of%@EH@% an expression.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%HIGH%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@AB@%LOW%@AE@% %@AI@%expression%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%HIGH%@AE@% operator returns the high-order eight bits of %@AI@%expression%@AE@%; the %@AB@%LOW%@AE@%%@EH@% operator returns the low-order eight bits. The expression must evaluate to a constant. You cannot use the %@AB@%HIGH%@AE@% and %@AB@%LOW%@AE@% operators on the contents of a memory operand since the contents may change at run time.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%stuff EQU 0ABCDh%@AE@%%@NL@% %@AS@% mov ah,HIGH stuff ; Load 0ABh%@AE@%%@NL@% %@AS@% mov al,LOW stuff ; Load 0CDh%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%HIGH%@AE@% and %@AB@%LOW%@AE@% operators work reliably only with constants and with%@EH@% offsets to external symbols. %@AB@%HIGH%@AE@% and %@AB@%LOW%@AE@% operations are not supported for offsets to local symbols.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.5 @%%@AB@%9.2.4.5 SEG Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.67 @%%@CR:IX9.68 @% The %@AB@%SEG%@AE@% operator returns the segment address of an expression.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%SEG%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%expression%@AE@% can be any label, variable, segment name, group name, or%@EH@% other memory operand. The %@AB@%SEG%@AE@% operator cannot be used with constant expressions. The returned value can be used as a memory operand.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%var DB ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ASSUME ds:SEG var ; Assume segment of variable%@AE@%%@NL@% %@AS@% mov ax,SEG var ; Get address of segment%@AE@%%@NL@% %@AS@% mov ds,ax ; where variable is declared%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.6 @%%@AB@%9.2.4.6 OFFSET Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.69 @%%@CR:IX9.70 @% The %@AB@%OFFSET%@AE@% operator returns the offset address of an expression.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%OFFSET%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%expression%@AE@% can be any label, variable, or other direct memory operand.%@EH@% Constant expressions return meaningless values. The value returned by the %@AB@%OFFSET%@AE@% operand is an immediate (constant) operand.%@NL@% %@NL@% %@4@% If the %@AB@%MODEL%@AE@% directive is used, the value returned by the %@AB@%OFFSET%@AE@% operator%@EH@% is relative to a group, whenever the data item is declared in a segment that is part of a group. The %@AB@%OFFSET%@AE@% operator returns the number of bytes between the beginning of the group and the data object. If the object is declared in a segment not part of a group, %@AB@%OFFSET%@AE@% returns the number of bytes between the beginning of the segment and the data object.%@NL@% %@NL@% %@4@% If the %@AB@%MODEL%@AE@% directive is not used, %@AB@%OFFSET%@AE@% returns a value relative to the%@EH@% beginning of the segment, regardless of whether the segment is part of a group.%@NL@% %@NL@% %@4@% If full segment definitions are given, the returned value is a memory%@EH@% operand equal to the number of bytes between the item and the beginning of the segment in which it is defined.%@NL@% %@NL@% %@4@%%@CR:IX9.71 @%%@CR:IX9.72 @% The segment-override operator (%@AB@%:%@AE@%) can be used to force %@AB@%OFFSET%@AE@% to return%@EH@% the number of bytes between the item in %@AI@%expression%@AE@% and the beginning of a named segment or group. This is the method used to generate valid offsets for items in a group when full segment definitions are used. For example, the statement%@NL@% %@NL@% %@AS@% mov bx,OFFSET DGROUP:array%@AE@%%@NL@% %@NL@% %@4@% is not the same as%@EH@%%@NL@% %@NL@% %@AS@% mov bx,OFFSET array%@AE@%%@NL@% %@NL@% %@4@% if %@AS@%array %@AE@%is not in the first segment in %@AS@%DGROUP%@AE@%.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%string DB "This is it."%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov dx,OFFSET string ; Load offset of variable%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.7 @%%@AB@%9.2.4.7 .TYPE Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.73 @%%@CR:IX9.74 @% The %@AB@%.TYPE%@AE@% operator returns a byte that defines the mode and scope of an%@EH@% expression.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.TYPE%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% If %@AI@%expression%@AE@% is not valid, %@AB@%.TYPE%@AE@% returns 0. Otherwise, %@AB@%.TYPE%@AE@% returns a%@EH@% byte having the bit setting shown in Table 9.4. The %@AB@%.%@AE@%TYPE operator sets all bits except bit 6. Future versions of the assembler may reserve a use for this bit.%@NL@% %@NL@% %@AB@%Table 9.4 .TYPE Operator and Variable Attributes%@AE@%%@NL@% %@NL@% %@AB@%Bit Position%@AE@% %@AB@%If Bit = 0%@AE@% %@AB@%If Bit = 1%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 0 Not program related Program related%@NL@% %@NL@% 1 Not data related Data related%@NL@% %@NL@% 2 Not a constant value Constant value%@NL@% %@NL@% 3 Addressing mode is not direct Addressing mode is direct%@NL@% %@NL@% 4 Not a register Expression is a register%@NL@% %@NL@% 5 Not defined Defined%@NL@% %@NL@% 7 Local or public scope External scope%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% The %@AB@%.TYPE%@AE@% operator is typically used in macros in which different kinds of%@EH@% arguments may need to be handled differently.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%display MACRO string%@AE@%%@NL@% %@AS@% IF ((.TYPE string) SHL 14) NE 8000h%@AE@%%@NL@% %@AS@% IF2%@AE@%%@NL@% %@AS@% %OUT Argument must be a variable%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% mov dx,OFFSET string%@AE@%%@NL@% %@AS@% mov ah,09h%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This macro checks to see if the argument passed to it is data related (a%@EH@% variable). It does this by shifting all bits except the relevant bits (1 and 0) to the left so that they can be checked. If the data bit is not set, an error message is generated.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.8 @%%@AB@%9.2.4.8 TYPE Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.75 @%%@CR:IX9.76 @% The %@AB@%TYPE%@AE@% operator returns a number that represents the type of an%@EH@% expression.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%TYPE%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% If %@AI@%expression%@AE@% evaluates to a variable, the operator returns the number of%@EH@% bytes in each data object in the variable. Each byte in a string is considered a separate data object, so the %@AB@%TYPE%@AE@% operator returns 1 for strings.%@NL@% %@NL@% %@4@% If %@AI@%expression%@AE@% evaluates to a structure or structure variable, the operator%@EH@% returns the number of bytes in the structure. If the expression is a label, the operator returns 0FFFFH for %@AB@%NEAR%@AE@% labels and 0FFFEH for %@AB@%FAR%@AE@% labels. If the expression is a constant, the operator returns 0.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.9 @%%@AB@%9.2.4.9 LENGTH Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.77 @%%@CR:IX9.78 @% The %@AB@%LENGTH%@AE@% operator returns the number of data elements in an array or%@EH@% other variable defined with the %@AB@%DUP%@AE@% operator.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LENGTH%@AE@% %@AI@%variable%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The returned value is the number of elements of the declared size in%@EH@% %@AI@%variable%@AE@%. If the variable was declared with nested %@AB@%DUP%@AE@% operators, only the value given for the outer %@AB@%DUP%@AE@% operator is returned. If the variable was not declared with the %@AB@%DUP%@AE@% operator, the value returned is always 1.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%array DD 100 DUP(0FFFFFFh)%@AE@%%@NL@% %@AS@%table DW 100 DUP(1,10 DUP(?))%@AE@%%@NL@% %@AS@%string DB 'This is a string'%@AE@%%@NL@% %@AS@%var DT ?%@AE@%%@NL@% %@AS@%larray EQU LENGTH array ; 100 - number of elements%@AE@%%@NL@% %@AS@%ltable EQU LENGTH table ; 100 - inner DUP not counted%@AE@%%@NL@% %@AS@%lstring EQU LENGTH string ; 1 - string is one element%@AE@%%@NL@% %@AS@%lvar EQU LENGTH var ; 1%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cx,LENGTH array ; Load number of elements%@AE@%%@NL@% %@AS@%again: . ; Perform some operation on%@AE@%%@NL@% %@AS@% . ; each element%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop again%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.4.10 @%%@AB@%9.2.4.10 SIZE Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.79 @%%@CR:IX9.80 @% The %@AB@%SIZE%@AE@% operator returns the total number of bytes allocated for an array%@EH@% or other variable defined with the %@AB@%DUP%@AE@% operator.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%SIZE%@AE@% %@AI@%variable%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The returned value is equal to the value of %@AB@%LENGTH%@AE@% %@AI@%variable%@AE@% times the%@EH@% value of %@AB@%TYPE%@AE@% %@AI@%variable%@AE@%. If the variable was declared with nested %@AB@%DUP%@AE@% operators, only the value given for the outside %@AB@%DUP%@AE@% operator is considered. If the variable was not declared with the %@AB@%DUP%@AE@% operator, the value returned is always %@AB@%TYPE%@AE@% %@AI@%variable%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%array DD 100 DUP(1)%@AE@%%@NL@% %@AS@%table DW 100 DUP(1,10 DUP(?))%@AE@%%@NL@% %@AS@%string DB 'This is a string'%@AE@%%@NL@% %@AS@%var DT ?%@AE@%%@NL@% %@AS@%sarray EQU SIZE array ; 400 - elements times size%@AE@%%@NL@% %@AS@%stable EQU SIZE table ; 200 - inner DUP ignored%@AE@%%@NL@% %@AS@%sstring EQU SIZE string ; 1 - string is one element%@AE@%%@NL@% %@AS@%svar EQU SIZE var ; 10 - bytes in variable%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cx,SIZE array ; Load number of bytes%@AE@%%@NL@% %@AS@%again: . ; Perform some operation on%@AE@%%@NL@% %@AS@% . ; each byte%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop again%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.2.5 @%%@AB@%9.2.5 Operator Precedence%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.81 @%%@CR:IX9.82 @% Expressions are evaluated according to the following rules:%@EH@%%@NL@% %@NL@% ■ Operations of highest precedence are performed first.%@NL@% %@NL@% ■ Operations of equal precedence are performed from left to right.%@NL@% %@NL@% ■ The order of evaluation can be overridden by using parentheses. Operations in parentheses are always performed before any adjacent operations.%@NL@% %@NL@% %@4@% The order of precedence for all operators is listed in Table 9.5.%@EH@% Operators on the same line have equal precedence.%@NL@% %@NL@% %@AB@%Table 9.5 Operator Precedence%@AE@%%@NL@% %@NL@% %@AB@%Precedence%@AE@% %@AB@%Operators%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% (Highest)%@NL@% %@NL@% 1 %@AB@%LENGTH%@AE@%, %@AB@%SIZE%@AE@%, %@AB@%WIDTH%@AE@%, %@AB@%MASK%@AE@%, (), [],<>%@NL@% %@NL@% 2 %@AB@%.%@AE@% (structure-field-name operator)%@NL@% %@NL@% 3 %@AB@%:%@AE@%%@NL@% %@NL@% 4 %@AB@%PTR%@AE@%, %@AB@%OFFSET%@AE@%, %@AB@%SEG%@AE@%, %@AB@%TYPE%@AE@%, %@AB@%THIS%@AE@%%@NL@% %@NL@% 5 %@AB@%HIGH%@AE@%, %@AB@%LOW%@AE@%%@NL@% %@NL@% 6 %@AB@%+%@AE@%,%@AB@%-%@AE@% (unary)%@NL@% %@NL@% 7 %@AB@%*%@AE@%,%@AB@%/%@AE@%, %@AB@%MOD%@AE@%, %@AB@%SHL%@AE@%, %@AB@%SHR%@AE@%%@NL@% %@NL@% 8 %@AB@%+%@AE@%, %@AB@%-%@AE@% (binary)%@NL@% %@NL@% 9 %@AB@%EQ%@AE@%, %@AB@%NE%@AE@%, %@AB@%LT%@AE@%, %@AB@%LE%@AE@%, %@AB@%GT%@AE@%, %@AB@%GE%@AE@%%@NL@% %@NL@% 10 %@AB@%NOT%@AE@%%@NL@% %@NL@% 11 %@AB@%AND%@AE@%%@NL@% %@NL@% 12 %@AB@%OR%@AE@%, %@AB@%XOR%@AE@%%@NL@% %@NL@% 13 %@AB@%SHORT%@AE@%, %@AB@%.TYPE%@AE@%%@NL@% %@NL@% (Lowest)%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%a EQU 8 / 4 * 2 ; Equals 4%@AE@%%@NL@% %@AS@%b EQU 8 / (4 * 2) ; Equals 1%@AE@%%@NL@% %@AS@%c EQU 8 + 4 * 2 ; Equals 16%@AE@%%@NL@% %@AS@%d EQU (8 + 4) * 2 ; Equals 24%@AE@%%@NL@% %@AS@%e EQU 8 OR 4 AND 2 ; Equals 8%@AE@%%@NL@% %@AS@%f EQU (8 OR 4) AND 3 ; Equals 0%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC9.3 @%%@AB@%9.3 Using the Location Counter%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.83 @%%@CR:IX9.84 @% The location counter is a special operand that, during assembly,%@EH@% represents the address of the statement currently being assembled. At assembly time, the location counter keeps changing, but when used in source code, it resolves to a constant representing an address.%@NL@% %@NL@% %@4@% The location counter has the same attributes as a near label. It%@EH@% represents an offset that is relative to the current segment and is equal to the number of bytes generated for the segment to that point.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%string DB "Who wants to count every byte in a string, "%@AE@%%@NL@% %@AS@% DB "especially if you might change it later."%@AE@%%@NL@% %@AS@%lstring EQU $-string ; Let the assembler do it%@AE@%%@NL@% %@NL@% %@4@% Example 1 shows one way of using the location-counter operand in%@EH@% expressions relating to data.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% cmp ax,bx%@AE@%%@NL@% %@AS@% jl shortjump ; If ax < bx, go to "shortjump"%@AE@%%@NL@% %@AS@% . ; else if ax >= bx, continue%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%shortjump: .%@AE@%%@NL@% %@NL@% %@AS@% cmp ax,bx%@AE@%%@NL@% %@AS@% jge $+5 ; If ax >= bx, continue%@AE@%%@NL@% %@AS@% jmp longjump ; else if ax < bx, go to "longjump"%@AE@%%@NL@% %@AS@% . ; This is "$+5"%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%longjump: .%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates how you can use the location counter to do%@EH@% conditional jumps of more than 128 bytes. The first part shows the normal way of coding jumps of less than 128 bytes, and the second part shows how to code the same jump when the label is more than 128 bytes away.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC9.4 @%%@AB@%9.4 Using Forward References%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.85 @% The assembler permits you to refer to labels, variable names, segment%@EH@% names, and other symbols before they are declared in the source code. Such references are called forward references.%@NL@% %@NL@% %@4@% The assembler handles forward references by making assumptions about them%@EH@% on the first pass and then attempting to correct the assumptions, if necessary, on the second pass. Checking and correcting assumptions on the second pass takes processing time, so source code with forward references assembles more slowly than source code with no forward references.%@NL@% %@NL@% %@4@% In addition, the assembler may make incorrect assumptions that it cannot%@EH@% correct, or corrects at a cost in program efficiency.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.4.1 @%%@AB@%9.4.1 Forward References to Labels%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.86 @% Forward references to labels may result in incorrect or inefficient code.%@EH@%%@NL@% %@NL@% %@4@% In the statement below, the label %@AS@%target %@AE@%is a forward reference:%@EH@%%@NL@% %@NL@% %@AS@% jmp target ; Generates 3 bytes in 16-bit segment%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%target:%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX9.87 @%%@CR:IX9.88 @% Since the assembler processes source files sequentially, %@AS@%target %@AE@%is unknown%@EH@% when it is first encountered. It could be one of three types: short (-128 to 127 bytes from the jump), near (-32,768 to 32,767 bytes from the jump), or far (in a different segment than the jump). QuickAssembler assumes that %@AS@%target %@AE@%is a near label, and assembles the number of bytes necessary to specify a near label: one byte for the instruction and two bytes for the operand.%@NL@% %@NL@% %@4@%%@CR:IX9.89 @%%@CR:IX9.90 @% If, on the second pass, the assembler learns that %@AS@%target%@AE@% is a short label,%@EH@% it will need only two bytes: one for the instruction and one for the operand. However, it will not be able to change its previous assembly and the three-byte version of the assembly will stand. If the assembler learns that %@AS@%target%@AE@% is a far label, it will need five bytes. Since it can't make this adjustment, it will generate a phase error.%@NL@% %@NL@% %@4@%%@CR:IX9.91 @%%@CR:IX9.92 @% You can override the assembler's assumptions by specifying the exact size%@EH@% of the jump. For example, if you know that a %@AB@%JMP%@AE@% instruction refers to a label less than 128 bytes from the jump, you can use the %@AB@%SHORT%@AE@% operator, as shown below:%@NL@% %@NL@% %@AS@% jmp SHORT target ; Generates 2 bytes%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%target:%@AE@%%@NL@% %@NL@% %@4@% Using the %@AB@%SHORT%@AE@% operator makes the code smaller and slightly faster. If%@EH@% the assembler has to use the three-byte form when the two-byte form would be acceptable, it will generate a warning message if the warning level is 2. (The warning level can be set with the /W option, as described in Appendix B%@BO: f653b@%, Section B.16%@BO: fc9ca@%.) You can ignore the warning, or you can go back to the source code and change the code to eliminate the forward references.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The %@AB@%SHORT%@AE@% operator in the example above would not be needed if %@AS@%target %@AE@%were located before the jump. The assembler would have already processed %@AS@%target%@AE@% and would be able to make adjustments based on its distance.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX9.93 @% If you use the %@AB@%SHORT%@AE@% operator when the label being jumped to is more than%@EH@% 128 bytes away, QuickAssembler generates an error message. You can either remove the %@AB@%SHORT%@AE@% operator, or try to reorganize your program to reduce the distance.%@NL@% %@NL@% %@4@%%@CR:IX9.94 @% If a far jump to a forward-referenced label is required, you must override%@EH@% the assembler's assumptions with the %@AB@%FAR%@AE@% and %@AB@%PTR%@AE@% operators, as shown below:%@NL@% %@NL@% %@AS@% jmp FAR PTR target ; Generates 5 bytes%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%target: ; In different segment%@AE@%%@NL@% %@NL@% %@4@% If the type of a label has been established earlier in the source code%@EH@% with an %@AB@%EXTRN%@AE@% directive, the type does not need to be specified in the jump statement.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC9.4.2 @%%@AB@%9.4.2 Forward References to Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.95 @% When QuickAssembler encounters code referencing variables that have not%@EH@% yet been defined in pass 1, it makes assumptions about the segment where the variable will be defined. If on pass 2 the assumptions turn out to be wrong, an error will occur.%@NL@% %@NL@% %@4@% These problems usually occur with complex segment structures that do not%@EH@% follow the Microsoft segment conventions. The problems never appear if simplified segment directives are used.%@NL@% %@NL@% %@4@% By default, QuickAssembler assumes that variables are referenced to the DS%@EH@% register. If a statement must access a variable in a segment not associated with the DS register, and if the variable has not been defined earlier in the source code, you must use the segment-override operator to specify the segment.%@NL@% %@NL@% %@4@% The situation is different if neither the variable nor the segment in%@EH@% which it is defined has been defined earlier in the source code. In this case, you must assign the segment to a group earlier in the source code. QuickAssembler will then know about the existence of the segment even though it has not yet been defined.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC9.5 @%%@AB@%9.5 Strong Typing for Memory Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX9.96 @%%@CR:IX9.97 @%%@CR:IX9.98 @% The assembler carries out strict syntax checks for all instruction%@EH@% statements, including strong typing for operands that refer to memory locations. This means that when an instruction uses two operands with implied data types, the operand types must match. Warning messages are generated for nonmatching types.%@NL@% %@NL@% %@4@% For example, in the following fragment, the variable %@AS@%string%@AE@% is incorrectly%@EH@% used in a move instruction:%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%string DB "A message."%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,string[1]%@AE@%%@NL@% %@NL@% %@4@% The %@AS@%ax%@AE@% register has %@AB@%WORD%@AE@% type, but %@AS@%string%@AE@% has %@AB@%BYTE%@AE@% type. Therefore, the%@EH@% statement generates the following warning message:%@NL@% %@NL@% %@AS@%Operand types must match%@AE@%%@NL@% %@NL@% %@4@% To avoid all ambiguity and prevent the warning error, use the %@AB@%PTR%@AE@% operator%@EH@% to override the variable's type, as shown below:%@NL@% %@NL@% %@AS@% mov ax,WORD PTR string[1]%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX9.99 @% You can ignore the warnings if you are willing to trust the assembler's%@EH@% assumptions. When a register and memory operand are mixed, the assembler assumes that the register operand is always the correct size. For example, in the statement%@NL@% %@NL@% %@AS@% mov ax,string[1]%@AE@%%@NL@% %@NL@% %@4@% the assembler assumes that the programmer wishes the word size of the%@EH@% register to override the byte size of the variable. A word starting at %@AS@%string[1]%@AE@% will be moved into AX. In the statement%@NL@% %@NL@% %@AS@% mov string[1],ax%@AE@%%@NL@% %@NL@% %@4@% the assembler assumes that the programmer wishes to move the word value in%@EH@% AX into the word starting at %@AS@%string[1]%@AE@%. However, the assembler's assumptions are not always as clear as in these examples. You should not ignore warnings about type mismatches unless you are sure you understand how your code will be assembled.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX9.100 @%%@CR:IX9.101 @%%@CR:IX9.102 @%%@CR:IX9.103 @% %@AB@%NOTE%@AE@% Some assemblers (including early versions of the IBM Macro Assembler) do not do strict type checking. For compatibility with these assemblers, type errors are warnings rather than severe errors. Many assembly-language program listings in books and magazines are written for assemblers with weak type checking. Such programs may produce warning messages, but assemble correctly. You can use the /W option to turn off type warnings if you are sure the code is correct.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH10 @%%@AB@%Chapter 10: Assembling Conditionally%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX10.1 @%%@CR:IX10.2 @% QuickAssembler provides two types of conditional directives,%@EH@% conditional-assembly and conditional-error directives. Conditional-assembly directives test for a specified condition and assemble a block of statements if the condition is true. Conditional-error directives test for a specified condition and generate an assembly error if the condition is true.%@NL@% %@NL@% %@4@% Both kinds of conditional directives test assembly-time conditions. They%@EH@% cannot test run-time conditions. Only expressions that evaluate to constants during assembly can be compared or tested.%@NL@% %@NL@% %@4@% Since macros and conditional-assembly directives are often used together,%@EH@% you may need to refer to Chapter 11%@BO: 8d6e4@%, "Using Equates, Macros, and Repeat Blocks," to understand some of the examples in this chapter. In particular, conditional directives are frequently used with the operators described in Section 11.5%@BO: 9a1b8@%, "Using Macro Operators."%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC10.1 @%%@AB@%10.1 Using Conditional-Assembly Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.3 @% The conditional-assembly directives include the following:%@EH@%%@NL@% %@NL@% %@4@% %@AB@%ELSE IFB IFIDN%@AE@%%@EH@%%@NL@% %@AB@%ENDIF IFDEF IFIDNI%@AE@%%@NL@% %@AB@%IF IFDIF IFNB%@AE@%%@NL@% %@AB@%IF1 IFDIFI IFNDEF%@AE@%%@NL@% %@AB@%IF2 IFE%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%IF%@AE@% directives and the %@AB@%ENDIF%@AE@% and %@AB@%ELSE%@AE@% directives can be used to enclose%@EH@% the statements to be considered for conditional assembly.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IF%@AE@%%@AI@%condition%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% [[%@AB@%ELSEIF%@AE@%%@AI@%condition%@AE@%%@NL@% %@AI@%statements%@AE@%]]%@NL@% %@AB@%.%@AE@%%@NL@% %@AB@%.%@AE@%%@NL@% %@AB@%.%@AE@%%@NL@% [[%@AB@%ELSE%@AE@%%@NL@% %@AI@%statements%@AE@%]]%@NL@% %@AB@%ENDIF%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%statements%@AE@% following the %@AB@%IF%@AE@% directive can be any valid statements,%@EH@% including other conditional blocks. The %@AB@%ELSEIF%@AE@% and %@AB@%ELSE%@AE@% blocks are optional. The conditional block can contain any number of %@AB@%ELSEIF%@AE@% blocks. (The %@AB@%ELSEIF%@AE@% directives are listed in Section 10.1.6%@BO: 89a62@%.) %@AB@%ENDIF%@AE@% ends the block.%@NL@% %@NL@% %@4@%%@CR:IX10.4 @%%@CR:IX10.5 @%%@CR:IX10.6 @%%@CR:IX10.7 @% The statements following the %@AB@%IF%@AE@% directive are assembled only if the%@EH@% corresponding %@AI@%condition%@AE@% is true. If the condition is not true and an %@AB@%ELSEIF%@AE@% directive is used, the assembler checks to see if the corresponding condition is true. If so, it assembles the statements following the %@AB@%ELSEIF%@AE@% directive. If no %@AB@%IF%@AE@% or %@AB@%ELSEIF%@AE@% conditions are satisifed, the statements following the %@AB@%ELSE%@AE@% directive are assembled.%@NL@% %@NL@% %@4@%%@CR:IX10.8 @%%@CR:IX10.9 @% %@AB@%IF%@AE@% statements can be nested up to 20 levels. A nested %@AB@%ELSE%@AE@% or %@AB@%ELSEIF%@AE@%%@EH@% directive always belongs to the nearest preceding %@AB@%IF%@AE@% statement that does not have its own %@AB@%ELSE%@AE@% directive.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.1.1 @%%@AB@%10.1.1 Testing Expressions with IF and IFE Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.10 @%%@CR:IX10.11 @%%@CR:IX10.12 @%%@CR:IX10.13 @%%@CR:IX10.14 @% The %@AB@%IF%@AE@% and %@AB@%IFE%@AE@% directives test the value of an expression and grant%@EH@% assembly based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IF%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@AB@%IFE%@AE@% %@AI@%expression%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%IF%@AE@% directive grants assembly if the value of %@AI@%expression%@AE@% is true%@EH@% (nonzero). The %@AB@%IFE%@AE@% directive grants assembly if the value of %@AI@%expression%@AE@% is false (0). The expression must evaluate to a constant value and must not contain forward references.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% IF debug GT 20%@AE@%%@NL@% %@AS@% push debug%@AE@%%@NL@% %@AS@% call adebug%@AE@%%@NL@% %@AS@% ELSEIF debug GT 10%@AE@%%@NL@% %@AS@% call bdebug%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% call cdebug%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% In this example, a different debug routine will be called, depending on%@EH@% the value of %@AS@%debug%@AE@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.1.2 @%%@AB@%10.1.2 Testing the Pass with IF1 and IF2 Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.15 @%%@CR:IX10.16 @%%@CR:IX10.17 @%%@CR:IX10.18 @%%@CR:IX10.19 @% The %@AB@%IF1%@AE@% and %@AB@%IF2%@AE@% directives test the current assembly pass and grant%@EH@% assembly only on the pass specified by the directive. Multiple passes of the assembler are discussed in Appendix C%@BO: fd1fd@%, Section C.7%@BO: 101a0c@%, "Reading a Pass 1 Listing."%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IF1%@AE@%%@EH@%%@NL@% %@AB@%IF2%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%IF1%@AE@% directive grants assembly only on pass 1. The %@AB@%IF2%@AE@% directive grants%@EH@% assembly only on pass 2. The directives take no arguments. If you turn on the One-Pass Assembly option, the %@AB@%IF2%@AE@% directive produces an error.%@NL@% %@NL@% %@4@% Macros usually only need to be processed once. You can enclose blocks of%@EH@% macros in %@AB@%IF1%@AE@% blocks to prevent them from being reprocessed on the second pass.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% IF1 ; Define on first pass only%@AE@%%@NL@% %@AS@%dostuff MACRO argument%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.1.3 @%%@AB@%10.1.3 Testing Symbol Definition with IFDEF and IFNDEF Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.20 @%%@CR:IX10.21 @%%@CR:IX10.22 @%%@CR:IX10.23 @%%@CR:IX10.24 @% The %@AB@%IFDEF%@AE@% and %@AB@%IFNDEF%@AE@% directives test whether a symbol has been defined and%@EH@% grant assembly based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IFDEF%@AE@% %@AI@%name%@AE@%%@EH@%%@NL@% %@AB@%IFNDEF%@AE@% %@AI@%name%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%IFDEF%@AE@% directive grants assembly only if %@AI@%name%@AE@% is a defined label,%@EH@% variable, or symbol. The %@AB@%IFNDEF%@AE@% directive grants assembly if %@AI@%name%@AE@% has not yet been defined.%@NL@% %@NL@% %@4@% The name can be any valid name. Note that if %@AI@%name%@AE@% is a forward reference,%@EH@% it is considered undefined on pass 1, but defined on pass 2.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% IFDEF buffer%@AE@%%@NL@% %@AS@%buff DB buffer DUP(?)%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% In this example, %@AS@%buff%@AE@% is allocated only if %@AS@%buffer%@AE@% has been previously%@EH@% defined.%@NL@% %@NL@% %@4@% One way to use this conditional block is to leave %@AS@%buffer %@AE@%undefined in the%@EH@% source file and define it if needed by using the /D%@AI@%symbol%@AE@% option (see Appendix B%@BO: f653b@%, Section B.4%@BO: f8ea4@%, "Defining Assembler Symbols") when you start QuickAssembler. For example, if the conditional block is in TEST.ASM, you could start the assembler with the following command line:%@NL@% %@NL@% %@AS@%QCL /Dbuffer=1024 test.asm%@AE@%%@NL@% %@NL@% %@4@% You could also define the symbol %@AS@%buffer %@AE@%by entering %@AS@%buffer=1024 %@AE@%in the%@EH@% Defines field of the Assembler Flags dialog box.%@NL@% %@NL@% %@4@% The command line would define the symbol %@AS@%buffer%@AE@%. As a result, the%@EH@% conditional-assembly block would allocate %@AS@%buff%@AE@%. However, if you didn't need %@AS@%buff%@AE@%, you could use the following command line:%@NL@% %@NL@% %@AS@%QCL test.asm%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.1.4 @%%@AB@%10.1.4 Verifying Macro Parameters with IFB and IFNB Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.25 @%%@CR:IX10.26 @%%@CR:IX10.27 @%%@CR:IX10.28 @%%@CR:IX10.29 @%%@CR:IX10.30 @%%@CR:IX10.31 @%%@CR:IX10.32 @% The %@AB@%IFB%@AE@% and %@AB@%IFNB%@AE@% directives test to see if a specified argument was passed%@EH@% to a macro and grant assembly based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IFB%@AE@% %@AB@%<%@AE@%%@AI@%argument%@AE@% %@AB@%>%@AE@%%@EH@%%@NL@% %@AB@%IFNB%@AE@% %@AB@%<%@AE@%%@AI@%argument%@AE@%%@AB@%>%@AE@%%@NL@% %@NL@% %@4@% These directives are always used inside macros, and they always test%@EH@% whether a real argument was passed for a specified dummy argument. The %@AB@%IFB%@AE@% directive grants assembly if %@AI@%argument%@AE@% is blank. The %@AB@%IFNB%@AE@% directive grants assembly if %@AI@%argument%@AE@% is not blank. The arguments can be any name, number, or expression. Angle brackets (%@AB@%< >%@AE@%) are required.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%Write MACRO buffer,bytes,handle%@AE@%%@NL@% %@AS@% IFNB <handle>%@AE@%%@NL@% %@AS@% mov bx,handle ; (1=stdout,2=stderr,3=aux,4=printer)%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% mov bx,1 ; Default standard out%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% mov dx,OFFSET buffer ; Address of buffer to write to%@AE@%%@NL@% %@AS@% mov cx,bytes ; Number of bytes to write%@AE@%%@NL@% %@AS@% mov ah,40h%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX10.33 @% In this example, a default value is used if no value is specified for the%@EH@% third macro argument.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.1.5 @%%@AB@%10.1.5 Comparing Macro Arguments with IFIDN and IFDIF Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.34 @%%@CR:IX10.35 @%%@CR:IX10.36 @%%@CR:IX10.37 @%%@CR:IX10.38 @%%@CR:IX10.39 @% The %@AB@%IFIDN%@AE@% and %@AB@%IFDIF%@AE@% directives compare two macro arguments and grant%@EH@% assembly based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IFIDN%@AE@%[[%@AB@%I%@AE@%]] %@AB@%<%@AE@%%@AI@%argument1%@AE@%%@AB@%>,<%@AE@%%@AI@%argument2%@AE@%%@AB@%>%@AE@%%@EH@%%@NL@% %@AB@%IFDIF%@AE@%[[%@AB@%I%@AE@%]] %@AB@%<%@AE@%%@AI@%argument1%@AE@%%@AB@%>,<%@AE@%%@AI@%argument2%@AE@%%@AB@%>%@AE@%%@NL@% %@NL@% %@4@% These directives are always used inside macros, and they always test%@EH@% whether real arguments passed for two specified arguments are the same. The %@AB@%IFIDN%@AE@% directive grants assembly if %@AI@%argument1%@AE@% and %@AI@%argument2%@AE@% are identical. The %@AB@%IFDIF%@AE@% directive grants assembly if %@AI@%argument1%@AE@% and %@AI@%argument2%@AE@% are different. The arguments can be names, numbers, or expressions. They must be enclosed in angle brackets and separated by a comma.%@NL@% %@NL@% %@4@% The optional %@AB@%I%@AE@% at the end of the directive name specifies that the%@EH@% directive is case insensitive. Arguments that are spelled the same will be evaluated the same, regardless of case. If the %@AB@%I%@AE@% is not given, the directive is case sensitive.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%divide8 MACRO numerator,denominator%@AE@%%@NL@% %@AS@% IFDIFI <numerator>,<al> ;; If numerator isn't AL%@AE@%%@NL@% %@AS@% mov al,numerator ;; make it AL%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% xor ah,ah%@AE@%%@NL@% %@AS@% div denominator%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% In this example, a macro uses the %@AB@%IFDIFI%@AE@% directive to check one of the%@EH@% arguments and take a different action, depending on the text of the string. The sample macro could be enhanced further by checking for other values that would require adjustment (such as a denominator passed in AL or passed in AH).%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.1.6 @%%@AB@%10.1.6 ELSEIF Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.40 @%%@CR:IX10.41 @% The assembler includes an %@AB@%ELSEIF%@AE@% conditional-assembly directive%@EH@% corresponding to each of the %@AB@%IF%@AE@% directives. The %@AB@%ELSEIF%@AE@% directives provide a more compact and better structured way of writing some sequences of %@AB@%ELSE%@AE@% and %@AB@%IF%@AE@% directives. QuickAssembler supports the following directives:%@NL@% %@NL@% %@4@% %@AB@%ELSEIF ELSEIFDEF ELSEIFIDN%@AE@%%@EH@%%@NL@% %@AB@%ELSEIF1 ELSEIFDIF ELSEIFIDNI%@AE@%%@NL@% %@AB@%ELSEIF2 ELSEIFDIFI ELSEIFNB%@AE@%%@NL@% %@AB@%ELSEIFB ELSEIFE ELSEIFNDEF%@AE@%%@NL@% %@NL@% %@4@% The following macro contains nested %@AB@%IF%@AE@% and%@AB@% ELSE%@AE@% blocks:%@EH@%%@NL@% %@NL@% %@AS@%; Macro to load register for high-level-language return%@AE@%%@NL@% %@AS@%FuncRet MACRO arg,length%@AE@%%@NL@% %@AS@% LOCAL tmploc%@AE@%%@NL@% %@AS@% IF length EQ 1%@AE@%%@NL@% %@AS@% mov al,arg%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% IF length EQ 2%@AE@%%@NL@% %@AS@% mov ax,arg%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% IF length EQ 4%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% tmploc DW ?%@AE@%%@NL@% %@AS@% DW ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% mov ax,WORD PTR arg%@AE@%%@NL@% %@AS@% mov tmploc,ax%@AE@%%@NL@% %@AS@% mov ax,WORD PTR arg+2%@AE@%%@NL@% %@AS@% mov tmploc+2,ax%@AE@%%@NL@% %@AS@% mov dx,SEG tmploc%@AE@%%@NL@% %@AS@% mov ax,OFFSET tmploc%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% %OUT Error in FuncRet expansion%@AE@%%@NL@% %@AS@% .ERR%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% The macro can be rewritten as follows, using the %@AB@%ELSEIF%@AE@% directives:%@EH@%%@NL@% %@NL@% %@AS@%FuncRet MACRO arg,length%@AE@%%@NL@% %@AS@% LOCAL tmploc%@AE@%%@NL@% %@AS@% IF length EQ 1%@AE@%%@NL@% %@AS@% mov al,arg%@AE@%%@NL@% %@AS@% ELSEIF length EQ 2%@AE@%%@NL@% %@AS@% mov ax,arg%@AE@%%@NL@% %@AS@% ELSEIF length EQ 4%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@NL@% %@AS@% tmploc DW ?%@AE@%%@NL@% %@AS@% DW ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% mov ax,WORD PTR arg%@AE@%%@NL@% %@AS@% mov tmploc,ax%@AE@%%@NL@% %@AS@% mov ax,WORD PTR arg+2%@AE@%%@NL@% %@AS@% mov tmploc+2,ax%@AE@%%@NL@% %@AS@% mov dx,SEG tmploc%@AE@%%@NL@% %@AS@% mov ax,OFFSET tmploc%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% %OUT Error in FuncRet expansion%@AE@%%@NL@% %@AS@% .ERR%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC10.2 @%%@AB@%10.2 Using Conditional-Error Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.42 @%%@CR:IX10.43 @% Conditional-error directives can be used to debug programs and check for%@EH@% assembly-time errors. By inserting a conditional-error directive at a key point in your code, you can test assembly-time conditions at that point. You can also use conditional-error directives to test for boundary conditions in macros.%@NL@% %@NL@% %@4@% The conditional-error directives and the error messages they produce are%@EH@% listed in Table 10.1.%@NL@% %@NL@% %@AB@%Table 10.1 Conditional-Error Directives%@AE@%%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Number%@AE@% %@AB@%Message%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%.ERR1%@AE@% 2087 %@AS@%Forced error - pass1%@AE@%%@NL@% %@NL@% %@AB@%.ERR2%@AE@% 2088 %@AS@%Forced error - pass2%@AE@%%@NL@% %@NL@% %@AB@%.ERR%@AE@% 2089 %@AS@%Forced error%@AE@%%@NL@% %@NL@% %@AB@%.ERRE%@AE@% 2090 %@AS@%Forced error - expression equals 0%@AE@%%@NL@% %@NL@% %@AB@%.ERRNZ%@AE@% 2091 %@AS@%Forced error - expression not equal 0%@AE@%%@NL@% %@NL@% %@AB@%.ERRNDEF%@AE@% 2092 %@AS@%Forced error - symbol not defined%@AE@%%@NL@% %@NL@% %@AB@%.ERRDEF%@AE@% 2093 %@AS@%Forced error - symbol defined%@AE@%%@NL@% %@NL@% %@AB@%.ERRB%@AE@% 2094 %@AS@%Forced error - string blank%@AE@%%@NL@% %@NL@% %@AB@%.ERRNB%@AE@% 2095 %@AS@%Forced error - string not blank%@AE@%%@NL@% %@NL@% %@AB@%.ERRIDN%@AE@% [[%@AB@%I%@AE@%]] 2096 %@AS@%Forced error - strings identical%@AE@%%@NL@% %@NL@% %@AB@%.ERRDIF%@AE@% [[%@AB@%I%@AE@%]] 2097 %@AS@%Forced error - strings different%@AE@%%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX10.44 @%%@CR:IX10.45 @% Like other severe errors, those generated by conditional-error directives%@EH@% cause the assembler to return exit code 7. If a severe error is encountered during assembly, QuickAssembler will delete the object module. All conditional-error directives except %@AB@%ERR1%@AE@% generate severe errors.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.2.1 @%%@AB@%10.2.1 Generating Unconditional Errors%@AE@%%@EH@% %@AB@%with .ERR, .ERR1, and .ERR2 Directives%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX10.46 @%%@CR:IX10.47 @%%@CR:IX10.48 @%%@CR:IX10.49 @%%@CR:IX10.50 @%%@CR:IX10.51 @%%@CR:IX10.52 @%%@CR:IX10.53 @%%@CR:IX10.54 @% The %@AB@%.ERR%@AE@%, %@AB@%.ERR1%@AE@%, and %@AB@%.ERR2%@AE@% directives force an error where the directives%@EH@% occur in the source file. The error is generated unconditionally when the directive is encountered, but the directives can be placed within conditional-assembly blocks to limit the errors to certain situations.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.ERR%@AE@%%@EH@%%@NL@% %@AB@%.ERR1%@AE@%%@NL@% %@AB@%.ERR2%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%.ERR%@AE@% directive forces an error regardless of the pass. The %@AB@%.ERR1%@AE@% and%@EH@% %@AB@%.ERR2%@AE@% directives force the error only on their respective passes. The %@AB@%.ERR1%@AE@% directive appears only on the screen or in the listing file if you use the /D option to request a pass 1 listing.%@NL@% %@NL@% %@4@% You can place these directives within conditional-assembly blocks or%@EH@% macros to see which blocks are being expanded.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%IFDEF dos%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%ELSEIFDEF xenix%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% .ERR%@AE@%%@NL@% %@AS@% %OUT dos or xenix must be defined%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% This example makes sure that either the symbol %@AS@%dos%@AE@% or the symbol %@AS@%xenix%@AE@% is%@EH@% defined. If neither is defined, the nested %@AB@%ELSE%@AE@% condition is assembled and an error message is generated. Since the %@AB@%.ERR%@AE@% directive is used, an error would be generated on each pass. You could use %@AB@%.ERR1%@AE@% or %@AB@%.ERR2%@AE@% to check if you want the error to be generated only on the corresponding pass.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.2.2 @%%@AB@%10.2.2 Testing Expressions with .ERRE or .ERRNZ Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.55 @%%@CR:IX10.56 @%%@CR:IX10.57 @%%@CR:IX10.58 @%%@CR:IX10.59 @% The %@AB@%.ERRE%@AE@% and%@AB@% .ERRNZ%@AE@% directives test the value of an expression and%@EH@% conditionally generate an error based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.ERRE%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@AB@%.ERRNZ%@AE@% %@AI@%expression%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX10.60 @% The %@AB@%.ERRE%@AE@% directive generates an error if %@AI@%expression%@AE@% is false (0). The%@EH@% %@AB@%.ERRNZ%@AE@% directive generates an error if %@AI@%expression%@AE@% is true (nonzero). The expression must evaluate to a constant value and must not contain forward references.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%buffer MACRO count,bname%@AE@%%@NL@% %@AS@% .ERRE count LE 128 ;; Allocate memory, but%@AE@%%@NL@% %@AS@%bname DB count DUP(0) ;; no more than 128 bytes%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% buffer 128,buf1 ; Data allocated - no error%@AE@%%@NL@% %@AS@% buffer 129,buf2 ; Error generated%@AE@%%@NL@% %@NL@% %@4@% In this example, the %@AB@%.ERRE%@AE@% directive is used to check the boundaries of a%@EH@% parameter passed to the macro %@AS@%buffer%@AE@%. If %@AS@%count%@AE@% is less than or equal to %@AS@%128%@AE@%, the expression being tested by the error directive will be true (nonzero) and no error will be generated. If %@AS@%count%@AE@% is greater than %@AS@%128%@AE@%, the expression will be false (0) and the error will be generated.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.2.3 @%%@AB@%10.2.3 Verifying Symbol Definition with .ERRDEF and .ERRNDEF Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.61 @%%@CR:IX10.62 @%%@CR:IX10.63 @%%@CR:IX10.64 @%%@CR:IX10.65 @% The %@AB@%.ERRDEF%@AE@% and %@AB@%.ERRNDEF%@AE@% directives test whether a symbol is defined and%@EH@% conditionally generate an error based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.ERRDEF%@AE@% %@AI@%name%@AE@%%@EH@%%@NL@% %@AB@%.ERRNDEF%@AE@% %@AI@%name%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%.ERRDEF%@AE@% directive produces an error if %@AI@%name%@AE@% is defined as a label,%@EH@% variable, or symbol. The %@AB@%.ERRNDEF%@AE@% directive produces an error if %@AI@%name%@AE@% has not yet been defined. If %@AI@%name%@AE@% is a forward reference, it is considered undefined on pass 1, but defined on pass 2.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .ERRNDEF publevel%@AE@%%@NL@% %@AS@% IF publevel LE 2%@AE@%%@NL@% %@AS@% PUBLIC var1, var2%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% PUBLIC var1, var2, var3%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% In this example, the %@AB@%.ERRNDEF%@AE@% directive at the beginning of the%@EH@% conditional block makes sure that a symbol being tested in the block actually exists.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.2.4 @%%@AB@%10.2.4 Testing for Macro Parameters with .ERRB and .ERRNB Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.66 @%%@CR:IX10.67 @%%@CR:IX10.68 @%%@CR:IX10.69 @%%@CR:IX10.70 @% The %@AB@%.ERRB%@AE@% and %@AB@%.ERRNB%@AE@% directives test whether a specified argument was%@EH@% passed to a macro and conditionally generate an error based on the result. %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.ERRB%@AE@% %@AB@%<%@AE@%%@AI@%argument%@AE@%%@AB@%>%@AE@%%@EH@%%@NL@% %@AB@%.ERRNB%@AE@% %@AB@%<%@AE@%%@AI@%argument%@AE@%%@AB@%>%@AE@%%@NL@% %@NL@% %@4@% These directives are always used inside macros, and they always test%@EH@% whether a real argument was passed for a specified dummy argument. The %@AB@%.ERRB%@AE@% directive generates an error if %@AI@%argument%@AE@% is blank. The %@AB@%.ERRNB%@AE@% directive generates an error if %@AI@%argument%@AE@% is not blank. The argument can be any name, number, or expression. Angle brackets (%@AB@%<>%@AE@%) are required.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%work MACRO realarg,testarg%@AE@%%@NL@% %@AS@% .ERRB <realarg> ;; Error if no parameters%@AE@%%@NL@% %@AS@% .ERRNB <testarg> ;; Error if more than one parameter%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% In this example, error directives are used to make sure that one, and only%@EH@% one, argument is passed to the macro. The %@AB@%.ERRB%@AE@% directive generates an error if no argument is passed to the macro. The %@AB@%.ERRNB%@AE@% directive generates an error if more than one argument is passed to the macro.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC10.2.5 @%%@AB@%10.2.5 Comparing Macro Arguments with .ERRIDN and .ERRDIF Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX10.71 @%%@CR:IX10.72 @%%@CR:IX10.73 @%%@CR:IX10.74 @%%@CR:IX10.75 @%%@CR:IX10.76 @% The %@AB@%.ERRIDN%@AE@% and %@AB@%.ERRDIF%@AE@% directives compare two macro arguments and%@EH@% conditionally generate an error based on the result.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.ERRIDN%@AE@%[[%@AB@%I%@AE@%]] %@AB@%<%@AE@%%@AI@%argument1%@AE@%%@AB@%>,<%@AE@%%@AI@%argument2%@AE@%%@AB@%>%@AE@%%@EH@%%@NL@% %@AB@%.ERRDIF%@AE@%[[%@AB@%I%@AE@%]] %@AB@%<%@AE@%%@AI@%argument1%@AE@%%@AB@%>,<%@AE@%%@AI@%argument2%@AE@%%@AB@%>%@AE@%%@NL@% %@NL@% %@4@% These directives are always used inside macros, and they always compare%@EH@% the real arguments specified for two parameters. The %@AB@%.ERRIDN%@AE@% directive generates an error if the arguments are identical. The %@AB@%.ERRDIF%@AE@% directive generates an error if the arguments are different. The arguments can be names, numbers, or expressions. They must be enclosed in angle brackets and separated by a comma.%@NL@% %@NL@% %@4@% The optional %@AB@%I%@AE@% at the end of the directive name specifies that the%@EH@% directive is case insensitive. Arguments that are spelled the same will be evaluated the same regardless of case. If the %@AB@%I%@AE@% is not given, the directive is case sensitive.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%addem MACRO ad1,ad2,sum%@AE@%%@NL@% %@AS@% .ERRIDNI <ax>,<ad2> ;; Error if ad2 is "ax"%@AE@%%@NL@% %@AS@% mov ax,ad1 ;; Would overwrite if ad2 were AX%@AE@%%@NL@% %@AS@% add ax,ad2%@AE@%%@NL@% %@AS@% mov sum,ax ;; Sum must be register or memory%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% In this example, the %@AB@%.ERRIDNI%@AE@% directive is used to protect against passing%@EH@% the AX register as the second parameter, since this would cause the macro to fail.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH11 @%%@AB@%Chapter 11: Using Equates, Macros, and Repeat Blocks%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX11.1 @%%@CR:IX11.2 @%%@CR:IX11.3 @% This chapter explains how to use equates, macros, and repeat blocks.%@EH@% "Equates" are constant values assigned to symbols so that the symbol can be used in place of the value. "Macros" are a series of statements that are assigned a symbolic name (and, optionally, parameters) so that the symbol can be used in place of the statements. "Repeat blocks" are a special form of macro used to do repeated statements.%@NL@% %@NL@% %@4@% Both equates and macros are processed at assembly time. They can simplify%@EH@% writing source code by allowing the user to substitute mnemonic names for constants and repetitive code. By changing a macro or equate, a programmer can change the effect of statements throughout the source code.%@NL@% %@NL@% %@4@%%@CR:IX11.4 @% In exchange for these conveniences, the programmer loses some%@EH@% assembly-time efficiency. Assembly may be slightly slower for a program that uses macros and equates extensively than for the same program written without them. However, the program without macros and equates usually takes longer to write and is more difficult to maintain.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.1 @%%@AB@%11.1 Using Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.5 @% The equate directives enable you to use symbols that represent numeric or%@EH@% string constants. QuickAssembler recognizes three kinds of equates:%@NL@% %@NL@% 1. Redefinable numeric equates%@NL@% %@NL@% 2. Nonredefinable numeric equates%@NL@% %@NL@% 3. String equates (also called text macros)%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.1.1 @%%@AB@%11.1.1 Redefinable Numeric Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.6 @%%@CR:IX11.7 @%%@CR:IX11.8 @%%@CR:IX11.9 @%%@CR:IX11.10 @% Redefinable numeric equates are used to assign a numeric constant to a%@EH@% symbol. The value of the symbol can be redefined at any point during assembly time. Although the value of a redefinable equate may be different at different points in the source code, a constant value will be assigned for each use, and that value will not change at run time.%@NL@% %@NL@% %@4@% Redefinable equates are often used for assembly-time calculations in%@EH@% macros and repeat blocks.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@%%@AB@%=%@AE@%%@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The equal-sign (%@AB@%=%@AE@%) directive creates or redefines a constant symbol by%@EH@% assigning the numeric value of %@AI@%expression%@AE@% to %@AI@%name%@AE@%. No storage is allocated for the symbol. The symbol can be used in subsequent statements as an immediate operand having the assigned value. It can be redefined at any time.%@NL@% %@NL@% %@4@% The expression can be an integer, a constant expression, a one- or%@EH@% two-character string constant, or an expression that evaluates to an address. The name must be either a unique name or a name previously defined by using the equal-sign (%@AB@%=%@AE@%) directive.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Redefinable equates must be assigned numeric values. String constants longer than two characters cannot be used.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%counter = 0 ; Initialize counter%@AE@%%@NL@% %@AS@%array LABEL BYTE ; Label array of increasing numbers%@AE@%%@NL@% %@AS@% REPT 100 ; Repeat 100 times%@AE@%%@NL@% %@AS@% DB counter ; Initialize number%@AE@%%@NL@% %@AS@%counter = counter + 1 ; Increment counter%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This example redefines equates inside a repeat block to declare an array%@EH@% initialized to increasing values from%@AS@% 0 %@AE@%to%@AS@% 100%@AE@%. The equal-sign directive is used to increment the %@AS@%counter %@AE@%symbol for each loop. See Section 11.4%@BO: 982dc@% for more information on repeat blocks.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.1.2 @%%@AB@%11.1.2 Nonredefinable Numeric Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.11 @%%@CR:IX11.12 @%%@CR:IX11.13 @%%@CR:IX11.14 @% Nonredefinable numeric equates are used to assign a numeric constant to a%@EH@% symbol. The value of the symbol cannot be redefined.%@NL@% %@NL@% %@4@% Nonredefinable numeric equates are often used for assigning mnemonic names%@EH@% to constant values. This can make the code more readable and easier to maintain. If a constant value used in numerous places in the source code needs to be changed, the equate can be changed in one place rather than throughout the source code.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%EQU%@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%EQU%@AE@% directive creates constant symbols by assigning %@AI@%expression%@AE@% to%@EH@% %@AI@%name%@AE@%. The assembler replaces each subsequent occurrence of %@AI@%name%@AE@% with the value of %@AI@%expression%@AE@%. Once a numeric equate has been defined with the %@AB@%EQU%@AE@% directive, it cannot be redefined. Attempting to do so generates an error.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% String constants can also be defined with the %@AB@%EQU %@AE@%directive, but the syntax is different, as described in Section 11.1.3%@BO: 8f1c6@%, "String Equates."%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% No storage is allocated for the symbol. Symbols defined with numeric%@EH@% values can be used in subsequent statements as immediate operands having the assigned value.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%column EQU 80 ; Numeric constant 80%@AE@%%@NL@% %@AS@%row EQU 25 ; Numeric constant 25%@AE@%%@NL@% %@AS@%screenful EQU column * row ; Numeric constant 2000%@AE@%%@NL@% %@AS@%line EQU row ; Alias for "row"%@AE@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%buffer DW screenful%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cx,column%@AE@%%@NL@% %@AS@% mov bx,line%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.1.3 @%%@AB@%11.1.3 String Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.15 @%%@CR:IX11.16 @%%@CR:IX11.17 @%%@CR:IX11.18 @%%@CR:IX11.19 @%%@CR:IX11.20 @% String equates (or text macros) are used to assign a string constant to a%@EH@% symbol. String equates can be used in a variety of contexts, including defining aliases and string constants.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%EQU%@AE@%{%@AI@%string%@AE@% | %@AB@%<%@AE@%%@AI@%string%@AE@%%@AB@%>%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%EQU%@AE@% directive creates constant symbols by assigning %@AI@%string%@AE@% to %@AI@%name%@AE@%.%@EH@% The assembler replaces each subsequent occurrence of %@AI@%name%@AE@% with %@AI@%string%@AE@%. Symbols defined to represent strings with the %@AB@%EQU%@AE@% directive can be redefined to new strings. Symbols cannot be defined to represent strings with the equal-sign (%@AB@%=%@AE@%) directive.%@NL@% %@NL@% %@4@%%@CR:IX11.21 @% An alias is a special kind of string equate. It is a symbol that is%@EH@% equated to another symbol or keyword.%@NL@% %@NL@% %@4@% If you want an equate to be a string equate, you should use angle brackets%@EH@% to force the assembler to evaluate it as a string. If you do not use angle brackets, the assembler will try to guess from context whether a numric or string equate is appropriate. This can lead to unexpected results. For example, the statement%@NL@% %@NL@% %@AS@%rt EQU run-time%@AE@%%@NL@% %@NL@% %@4@% would be evaluated as %@AS@%run %@AE@%minus %@AS@%time%@AE@%, even though the user might intend to%@EH@% define the string %@AS@%run-time%@AE@%. If %@AS@%run %@AE@%and %@AS@%time %@AE@%were not already defined as numeric equates, the statement would generate an error. Using angle brackets solves this problem. The statement%@NL@% %@NL@% %@AS@%rt EQU <run-time>%@AE@%%@NL@% %@NL@% %@4@% is evaluated as the string %@AS@%run-time%@AE@%.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%;String equate definitions%@AE@%%@NL@% %@AS@%pi EQU <3.1415> ; String constant "3.1415"%@AE@%%@NL@% %@AS@%prompt EQU <'Type Name: '> ; String constant "'Type Name: '",%@AE@%%@NL@% %@AS@%WPT EQU <WORD PTR> ; String constant for "WORD PTR"%@AE@%%@NL@% %@AS@%argl EQU <[bp+4]> ; String constant for "[bp+4]"%@AE@%%@NL@% %@NL@% %@AS@%; Use of string equates%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%message DB prompt ; Allocate string "Type Name:"%@AE@%%@NL@% %@AS@%pie DQ pi ; Allocate real number 3.1415%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% inc WPT parm1 ; Increment word value of%@AE@%%@NL@% %@AS@% ; argument passed on stack%@AE@%%@NL@% %@NL@% %@4@% Section 11.3%@BO: 95341@%, "Text-Macro String Directives," describes directives that%@EH@% enable you to manipulate strings. They are particularly powerful when you use them from within macros and repeat blocks, described later.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.1.4 @%%@AB@%11.1.4 Predefined Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The assembler includes several predefined equates. The ones related to%@EH@% segments are described in Section 5.1.5%@BO: 47722@%, "Using Predefined Segment Equates." In addition, the following equates are available: %@AB@%@WordSize%@AE@%, %@AB@%@Cpu%@AE@%, and %@AB@%@Version%@AE@%.%@NL@% %@NL@% %@4@%%@CR:IX11.22 @%%@CR:IX11.23 @% The%@AB@% @WordSize%@AE@% equate returns the size of a word for the current segment.%@EH@% With QuickAssembler, this value is always equal to 2. However, other versions of the assembler can assign a different value to %@AB@%@WordSize%@AE@% when working with 80386 extended features.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% If you set the Preserve Case assembler flag or use the /Cl option, QuickAssembler considers predefined equates to be case-sensitive. The case-sensitive names of predefined equates are %@AB@%@WordSize%@AE@%, %@AB@%@Cpu%@AE@%, %@AB@%@Version%@AE@%, %@AB@%@CurSeg%@AE@%, %@AB@%@FileName%@AE@%, %@AB@%@CodeSize%@AE@%, %@AB@%@DataSize%@AE@%, %@AB@%@Model%@AE@%, %@AB@%@data%@AE@%, %@AB@%@data?%@AE@%, %@AB@%@fardata%@AE@%, %@AB@%@fardata?%@AE@%, and %@AB@%@code%@AE@%.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX11.24 @%%@CR:IX11.25 @% The%@AB@% @Cpu%@AE@% equate returns a 16-bit value containing information about the%@EH@% selected processor. You select a processor by using one of the processor directives, such as the %@AB@%.286%@AE@% directive. You can use the %@AB@%@Cpu%@AE@% text macro to control assembly of processor-specific code. Individual bits in the value returned by %@AB@%@Cpu%@AE@% indicate information about the selected processor.%@NL@% %@NL@% %@AB@%Bit%@AE@% %@AB@%If Bit = 1%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 0 8086 processor%@NL@% %@NL@% 1 80186 processor%@NL@% %@NL@% 2 80286 processor%@NL@% %@NL@% 8 8087 coprocessor instructions enabled%@NL@% %@NL@% 10 80287 coprocessor instructions enabled%@NL@% %@NL@% %@NL@% %@4@% Because the processors are upwardly compatible, selecting a%@EH@% higher-numbered processor automatically sets the bits indicating lower-numbered processors. For example, selecting an 80286 processor automatically sets the 80186 and 8086 bits.%@NL@% %@NL@% %@4@% Bits 4 through 6, 9, and 12 through 15 are reserved for future use and%@EH@% should be masked off when testing. Bits 3, 7, and 11 have special meaning to Versions 5.1 and later of the Microsoft Macro Assembler: bit 3 indicates an 80386 processor, bit 7 indicates privilege mode enabled, and bit 11 indicates that 80387 coprocessor instructions are enabled.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The %@AB@%@Cpu%@AE@% equate only provides information about the processor selected during assembly by one of the processor directives. It does not provide information about the processor actually used when a program is run.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% The following example uses the %@AB@%@Cpu %@AE@%text macro to select more efficient%@EH@% instructions available only on the 80186 processor and above:%@NL@% %@NL@% %@AS@%; Use the 186/286/386 pusha instruction if possible%@AE@%%@NL@% %@AS@%P186 EQU (@Cpu AND 0002h) ; Only test 186 bit--286 and%@AE@%%@NL@% %@AS@% ; 386 set 186 bit as well%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@AS@% IF P186 ; Non-zero if 186 processor%@AE@%%@NL@% %@AS@% pusha ; or above%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% push ax ; Do what the single%@AE@%%@NL@% %@AS@% push cx ; pusha instruction does%@AE@%%@NL@% %@AS@% push dx%@AE@%%@NL@% %@AS@% push bx%@AE@%%@NL@% %@AS@% push sp%@AE@%%@NL@% %@AS@% push bp%@AE@%%@NL@% %@AS@% push si%@AE@%%@NL@% %@AS@% push di%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX11.26 @%%@CR:IX11.27 @% The %@AB@%@Version%@AE@% equate returns the version of the assembler in use. With the%@EH@% %@AB@%@Version%@AE@% equate, you can write macros that take appropriate actions for different versions of the assembler. Currently, the %@AB@%@Version%@AE@% equate returns 520 as a string of three characters.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Although the version number of QuickAssembler is 2.01, the %@AB@%@Version%@AE@% equate returns 520 rather than 201. The number 520 was selected because QuickAssembler is an enhancement of Version 5.1 of the Microsoft Macro Assembler.%@AB@% %@AE@%The %@AB@%@Version %@AE@%equate was first assembled for Version 5.1.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% You can use the %@AB@%IF%@AE@% and%@AB@% IFE%@AE@% conditional assembly directives to test for%@EH@% different versions of the assembler and to assemble different code depending on the version.%@NL@% %@NL@% %@AS@%IFNDEF @Version%@AE@%%@NL@% %@AS@% %OUT MASM 5.0 or earlier has no extended PROC or .STARTUP%@AE@%%@NL@% %@AS@%ELSEIF @Version EQ 510%@AE@%%@NL@% %@AS@% %OUT MASM 5.1 has extended PROC, but not .STARTUP%@AE@%%@NL@% %@AS@%ELSEIF @Version EQ 520%@AE@%%@NL@% %@AS@% %OUT QuickAssembler 2.01 has extended PROC and .STARTUP%@AE@%%@NL@% %@AS@%ELSE%@AE@%%@NL@% %@AS@% %OUT Future assembler%@AE@%%@NL@% %@AS@%ENDIF%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.2 @%%@AB@%11.2 Using Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.28 @% Macros enable you to assign a symbolic name to a block of source%@EH@% statements and then to use that name in your source file to represent the statements. Parameters can also be defined to represent arguments passed to the macro.%@NL@% %@NL@% %@4@% Macro expansion is a text-processing function that occurs at assembly%@EH@% time. Each time QuickAssembler encounters the text associated with a macro name, it replaces that text with the text of the statements in the macro definition. Similarly, the text of parameter names is replaced with the text of the corresponding actual arguments.%@NL@% %@NL@% %@4@%%@CR:IX11.29 @%%@CR:IX11.30 @% A macro can be defined any place in the source file as long as the%@EH@% definition precedes the first source line that calls the macro. Macros and equates are often kept in a separate file and made available to the program through an %@AB@%INCLUDE%@AE@% directive (see Section 11.7.1%@BO: a0d21@%, "Using Include Files") at the start of the source code.%@NL@% %@NL@% %@4@%%@CR:IX11.31 @%%@CR:IX11.32 @% Often a task can be done by using either a macro or procedure. For%@EH@% example, the %@AS@%addup %@AE@%procedure shown in Section 15.3.3%@BO: c60b0@%, "Passing Arguments on the Stack," does the same thing as the %@AS@%addup%@AE@% macro in Section 11.2.1%@BO: 91f51@%, "Defining Macros." Macros are expanded on every occurrence of the macro name, so they can increase the length of the executable file if called repeatedly. Procedures are coded only once in the executable file, but the increased overhead of saving and restoring addresses and parameters can make them slower.%@NL@% %@NL@% %@4@% The section below tells how to define and call macros. Repeat blocks, a%@EH@% special form of macro for doing repeated operations, are discussed separately in Section 11.4%@BO: 982dc@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.2.1 @%%@AB@%11.2.1 Defining Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.33 @%%@CR:IX11.34 @%%@CR:IX11.35 @%%@CR:IX11.36 @% The %@AB@%MACRO%@AE@% and %@AB@%ENDM%@AE@% directives are used to define macros. %@AB@%MACRO%@AE@% designates%@EH@% the beginning of the macro block, and %@AB@%ENDM%@AE@% designates the end of the macro block.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% %@AB@%MACRO%@AE@% [[%@AI@%parameter%@AE@% [[%@AB@%,%@AE@%%@AI@%parameter%@AE@%]]...]]%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%ENDM%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% must be unique and a valid symbol name. It can be used later in%@EH@% the source file to invoke the macro.%@NL@% %@NL@% %@4@%%@CR:IX11.37 @%%@CR:IX11.38 @%%@CR:IX11.39 @%%@CR:IX11.40 @%%@CR:IX11.41 @% The %@AI@%parameters%@AE@% (sometimes called dummy parameters) are names that act as%@EH@% placeholders for values to be passed as arguments to the macro when it is called. Any number of parameters can be specified, but they must all fit on one line. If you give more than one parameter, you must separate them with commas, spaces, or tabs. Commas can always be used as separators; spaces and tabs may cause ambiguity if the arguments are expressions.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% This manual uses the term "parameter" to refer to a placeholder for a value that will be passed to a macro or procedure. Parameters appear in macro or procedure definitions. The term "argument" is used to refer to an actual value passed to the macro or procedure when it is called.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% Any valid assembler statements may be placed within a macro, including%@EH@% statements that call or define other macros. Any number of statements can be used. The parameters can be used any number of times in the statements. Macros can be nested, redefined, or used recursively, as explained in Section 11.6%@BO: 9e5c9@%, "Using Recursive, Nested, and Redefined Macros."%@NL@% %@NL@% %@4@% QuickAssembler assembles the statements in a macro only if the macro is%@EH@% called and only at the point in the source file from which it is called. The macro definition itself is never assembled.%@NL@% %@NL@% %@4@% A macro definition can include the %@AB@%LOCAL%@AE@% directive, which lets you define%@EH@% labels used only within a macro, or the %@AB@%EXITM%@AE@% directive, which allows you to exit from a macro before all the statements in the block are expanded. These directives are discussed in Sections 11.2.3%@BO: 93a20@%, "Using Local Symbols," and 11.2.4%@BO: 93a20@%, "Exiting from a Macro." Macro operators can also be used in macro definitions, as described in Section 11.5%@BO: 9a1b8@%, "Using Macro Operators."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%addup MACRO ad1,ad2,ad3%@AE@%%@NL@% %@AS@% mov ax,ad1 ;; First parameter in AX%@AE@%%@NL@% %@AS@% add ax,ad2 ;; Add next two parameters%@AE@%%@NL@% %@AS@% add ax,ad3 ;; and leave sum in AX%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This example defines a macro named %@AS@%addup%@AE@%, which uses three parameters to%@EH@% add three values and leave their sum in the AX register. The three parameters will be replaced with arguments when the macro is called.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.2.2 @%%@AB@%11.2.2 Calling Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.42 @% A macro call directs QuickAssembler to copy the statements of the macro to%@EH@% the point of the call and to replace any parameters in the macro statements with the corresponding actual arguments.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name%@AE@% [[%@AI@%argument%@AE@% [[%@AB@%,%@AE@%%@AI@%argument%@AE@%]]...]]%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%name%@AE@% must be the name of a macro defined earlier in the source file.%@EH@% The %@AI@%arguments%@AE@% can be any text. For example, symbols, constants, and registers are often given as arguments. Any number of arguments can be given, but they must all fit on one logical line. You can use the continuation character (\) to continue long macro calls on multiple physical lines. Multiple arguments must be separated by commas, spaces, or tabs.%@NL@% %@NL@% %@4@%%@CR:IX11.43 @%%@CR:IX11.44 @% QuickAssembler replaces the first parameter with the first argument, the%@EH@% second parameter with the second argument, and so on. If a macro call has more arguments than the macro has parameters, the extra arguments are ignored. If a call has fewer arguments than the macro has parameters, any remaining parameters are replaced with a null (empty) string.%@NL@% %@NL@% %@4@%%@CR:IX11.45 @%%@CR:IX11.46 @% You can use conditional statements to enable macros to check for null%@EH@% strings or other types of arguments. The macro can then take appropriate action to adjust to different kinds of arguments. See Chapter 10%@BO: 867d7@%, "Assembling Conditionally," for more information on using conditional-assembly and conditional-error directives to test macro arguments.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%addup MACRO ad1,ad2,ad3 ; Macro definition%@AE@%%@NL@% %@AS@% mov ax,ad1 ;; First parameter in AX%@AE@%%@NL@% %@AS@% add ax,ad2 ;; Add next two parameters%@AE@%%@NL@% %@AS@% add ax,ad3 ;; and leave sum in AX%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% addup bx,2,count ; Macro call%@AE@%%@NL@% %@NL@% %@4@% When the %@AS@%addup%@AE@% macro is called, QuickAssembler replaces the parameters%@EH@% with the actual parameters given in the macro call. In the example above, the assembler would expand the macro call to the following code:%@NL@% %@NL@% %@AS@% mov ax,bx%@AE@%%@NL@% %@AS@% add ax,2%@AE@%%@NL@% %@AS@% add ax,count%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX11.47 @%%@CR:IX11.48 @%%@CR:IX11.49 @%%@CR:IX11.50 @%%@CR:IX11.51 @%%@CR:IX11.52 @% This code could be shown in an assembler listing, depending on whether the%@EH@% %@AB@%.LALL%@AE@%, %@AB@%.XALL%@AE@%, or %@AB@%.SALL%@AE@% directive was in effect (see Section 12.3%@BO: a47ae@%, "Controlling the Contents of Listings").%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.2.3 @%%@AB@%11.2.3 Using Local Symbols%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.53 @%%@CR:IX11.54 @%%@CR:IX11.55 @%%@CR:IX11.56 @% The %@AB@%LOCAL%@AE@% directive can be used within a macro to define symbols that are%@EH@% available only within the defined macro.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% In this context, the term "local" is not related to the public availability of a symbol, as described in Chapter 8%@BO: 70f6e@%, "Creating Programs from Multiple Modules," or to variables that are defined to be local to a procedure, as described in Section 15.3.5%@BO: c9deb@%, "Using Local Variables." Local simply means that the symbol is not known outside the macro where it is defined.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LOCAL%@AE@% %@AI@%localname%@AE@% [[%@AB@%,%@AE@%%@AI@%localname%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%localname%@AE@% is a temporary symbol name that is to be replaced by a%@EH@% unique symbol name when the macro is expanded. At least one local name is required for each %@AB@%LOCAL%@AE@% directive. If more than one local symbol is given, the names must be separated with commas. Once declared, local name can be used in any statement within the macro definition.%@NL@% %@NL@% %@4@% QuickAssembler creates a new actual name for %@AI@%localname%@AE@% each time the macro%@EH@% is expanded. The actual name has the following form:%@NL@% %@NL@% %@4@% %@AB@%??%@AE@%%@AI@%number%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%number%@AE@% is a hexadecimal number in the range 0000 to 0FFFF. You should%@EH@% not give other symbols names in this format, since doing so may produce a symbol with multiple definitions. In listings, the local name is shown in the macro definition, but the actual name is shown in expansions of macro calls.%@NL@% %@NL@% %@4@%%@CR:IX11.57 @% Nonlocal labels may be used in a macro; but if the macro is used more than%@EH@% once, the same label will appear in both expansions, and QuickAssembler will display an error message, indicating that the file contains a symbol with multiple definitions. To avoid this problem, use only local labels (or redefinable equates) in macros.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The %@AB@%LOCAL %@AE@%directive in macro definitions must precede all other statements in the definition. If you try another statement (such as a comment directive) before the %@AB@%LOCAL %@AE@%directive, an error will be generated.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%power MACRO factor,exponent ;; Use for unsigned only%@AE@%%@NL@% %@AS@% LOCAL again,gotzero ;; Declare symbols for macro%@AE@%%@NL@% %@AS@% xor dx,dx ;; Clear DX%@AE@%%@NL@% %@AS@% mov cx,exponent ;; Exponent is count for loop%@AE@%%@NL@% %@AS@% mov ax,1 ;; Multiply by 1 first time%@AE@%%@NL@% %@AS@% jcxz gotzero ;; Get out if exponent is zero%@AE@%%@NL@% %@AS@% mov bx,factor%@AE@%%@NL@% %@AS@%again: mul bx ;; Multiply until done%@AE@%%@NL@% %@AS@% loop again%@AE@%%@NL@% %@AS@%gotzero:%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% In this example, the %@AB@%LOCAL%@AE@% directive defines the local names %@AS@%again%@AE@% and%@EH@% %@AS@%gotzero%@AE@% as labels to be used within the %@AS@%power%@AE@% macro. These local names will be replaced with unique names each time the macro is expanded. For example, the first time the macro is called, %@AS@%again%@AE@% will be assigned the name %@AS@%??0000%@AE@% and %@AS@%gotzero%@AE@% will be assigned %@AS@%??0001%@AE@%. The second time through, %@AS@%again%@AE@% will be assigned %@AS@%??0002%@AE@% and %@AS@%gotzero %@AE@%will be assigned %@AS@%??0003%@AE@%, and so on.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.2.4 @%%@AB@%11.2.4 Exiting from a Macro%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.58 @%%@CR:IX11.59 @%%@CR:IX11.60 @% Normally, QuickAssembler processes all the statements in a macro%@EH@% definition and then continues with the next statement after the macro call. However, you can use the %@AB@%EXITM%@AE@% directive to tell the assembler to terminate macro expansion before all the statements in the macro have been assembled.%@NL@% %@NL@% %@4@% When the %@AB@%EXITM%@AE@% directive is encountered, the assembler exits the macro or%@EH@% repeat block immediately. Any remaining statements in the macro or repeat block are not processed. If %@AB@%EXITM%@AE@% is encountered in a nested macro or repeat block, QuickAssembler returns to expanding the outer block.%@NL@% %@NL@% %@4@% The %@AB@%EXITM%@AE@% directive is typically used with conditional directives to skip%@EH@% the last statements in a macro under specified conditions. Often macros using the %@AB@%EXITM%@AE@% directive contain repeat blocks or are called recursively.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%allocate MACRO times ; Macro definition%@AE@%%@NL@% %@AS@%x = 0%@AE@%%@NL@% %@AS@% REPT times ;; Repeat up to 256 times%@AE@%%@NL@% %@AS@% IF x GT 0FFh ;; Is x > 255 yet?%@AE@%%@NL@% %@AS@% EXITM ;; If so, quit%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% DB x ;; Else allocate x%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@%x = x + 1 ;; Increment x%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This example defines a macro that allocates a variable amount of data, but%@EH@% no more than 255 bytes. The macro contains an %@AB@%IF%@AE@% directive that checks the expression %@AS@%x - 0FFh%@AE@%. When the value of this expression is true (x-255 = 0), the %@AB@%EXITM%@AE@% directive is processed and expansion of the macro stops.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.3 @%%@AB@%11.3 Text-Macro String Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.61 @% The assembler includes four text-macro string directives that let you%@EH@% manipulate literal strings or text-macro values. You use the four directives in much the same way you use the equal-sign (%@AB@%=%@AE@%) directive. For example, the following line assigns the first three characters (%@AS@%abc%@AE@%) of the literal string to the label %@AS@%three%@AE@% by using the %@AB@%SUBSTR%@AE@% directive:%@NL@% %@NL@% %@AS@%three SUBSTR <abcdefghijklmnopqrstuvwxyz>,1,3%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX11.62 @%%@CR:IX11.63 @% Each of the directives assigns its value──depending on the directive──to a%@EH@% numeric label or a text macro. The following list summarizes the four directives and the type of label that the directives should be used with:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%SUBSTR%@AE@% Returns a substring of its text macro or literal string argument.%@AB@% SUBSTR %@AE@%requires a text-macro label.%@NL@% %@NL@% %@AB@%CATSTR%@AE@% Concatenates a variable number of strings (text macros or literal strings) to form a single string. %@AB@%CATSTR%@AE@% requires a text-macro label.%@NL@% %@NL@% %@AB@%SIZESTR%@AE@% Returns the length, in characters, of its argument string. %@AB@%SIZESTR%@AE@% requires a numeric label.%@NL@% %@NL@% %@AB@%INSTR%@AE@% Returns an index indicating the starting position of a substring within another string. %@AB@%INSTR%@AE@% requires a numeric label.%@NL@% %@NL@% %@NL@% %@4@% Strings used as arguments in the directives must be text enclosed in angle%@EH@% brackets (%@AB@%<%@AE@% %@AB@%>%@AE@%), previously defined text macros, or expressions starting with a percent sign (%@AB@%%%@AE@%). Numeric arguments can be numeric constants or expressions that evaluate to constants during assembly.%@NL@% %@NL@% %@4@% The next four sections describe the directives in more detail.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.3.1 @%%@AB@%11.3.1 The SUBSTR Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.64 @%%@CR:IX11.65 @%%@CR:IX11.66 @% The %@AB@%SUBSTR%@AE@% directive returns a substring from a given string.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%textlabel%@AE@%%@AB@% SUBSTR%@AE@% %@AI@%string%@AE@%%@AB@%,%@AE@%%@AI@%start%@AE@%[[%@AB@%, %@AE@%%@AI@%length%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%SUBSTR%@AE@% directive takes the following arguments:%@EH@%%@NL@% %@NL@% %@AB@%Argument%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AI@%textlabel%@AE@% The text label the result is assigned to.%@NL@% %@NL@% %@AI@%string%@AE@% The string the substring is extracted from.%@NL@% %@NL@% %@AI@%start%@AE@% The starting position of the substring. The first character in the string has a position of one.%@NL@% %@NL@% %@AI@%length%@AE@% The number of characters to extract. If omitted, %@AB@%SUBSTR%@AE@% returns all characters to the right of position %@AI@%start%@AE@%, including the character at position %@AI@%start%@AE@%.%@NL@% %@NL@% %@NL@% %@4@% In the following lines, the text macro %@AS@%freg %@AE@%is assigned the first two%@EH@% characters of the text macro %@AS@%reglist%@AE@%:%@NL@% %@NL@% %@AS@%reglist EQU <ax,bx,cx,dx>%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%freg SUBSTR reglist,1,2 ; freg = ax%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.3.2 @%%@AB@%11.3.2 The CATSTR %@AE@%Directive%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.67 @%%@CR:IX11.68 @%%@CR:IX11.69 @% The %@AB@%CATSTR%@AE@% directive concatenates a series of strings.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%textlabel%@AE@%%@AB@% CATSTR %@AE@%%@AI@%string%@AE@%[[%@AB@%,%@AE@% %@AI@%string%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%CATSTR%@AE@% directive takes the following arguments:%@EH@%%@NL@% %@NL@% %@AB@%Argument%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AI@%textlabel%@AE@% The text label the result is assigned to%@NL@% %@NL@% %@AI@%string%@AE@% The string or strings concatenated and assigned to %@AI@%textlabel%@AE@%%@NL@% %@NL@% %@NL@% %@4@% The following lines concatenate the two literal strings and assign the%@EH@% result to the text macro %@AS@%lstring%@AE@%:%@NL@% %@NL@% %@AS@%lstring CATSTR <a b c>, <d e f>, ; lstring = a b c d e f%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.3.3 @%%@AB@%11.3.3 The SIZESTR%@AE@% Directive%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.70 @%%@CR:IX11.71 @% The %@AB@%SIZESTR%@AE@% directive assigns the length of its argument string to a%@EH@% numeric label.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%numericlabel%@AE@%%@AB@% SIZESTR %@AE@%%@AI@%string%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%SIZESTR%@AE@% directive takes the following arguments:%@EH@%%@NL@% %@NL@% %@AB@%Argument%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AI@%numericlabel%@AE@% The numeric label that the assembler assigns the string length to%@NL@% %@NL@% %@AI@%string%@AE@% The string whose length is returned%@NL@% %@NL@% %@NL@% %@4@% The following lines set %@AS@%slength %@AE@%to%@AS@% 8%@AE@%──the length of the text macro%@EH@% %@AS@%tstring%@AE@%:%@NL@% %@NL@% %@AS@%tstring EQU <ax bx cx>%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%slength SIZESTR tstring ; slength = 8%@AE@%%@NL@% %@NL@% %@4@% A null string has a length of zero.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.3.4 @%%@AB@%11.3.4 The INSTR Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.72 @%%@CR:IX11.73 @% The %@AB@%INSTR%@AE@% directive returns the position of a string within another%@EH@% string. The directive returns 0 if the string is not found. The first character in a string has a position of one.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%numericlabel%@AE@%%@AB@% INSTR%@AE@% [[%@AI@%start%@AE@%%@AB@%,%@AE@%]]%@AI@%string1%@AE@%%@AB@%,%@AE@% %@AI@%string2%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The%@AB@% INSTR%@AE@% directive takes the following arguments:%@EH@%%@NL@% %@NL@% %@AB@%Argument%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AI@%numbericlabel%@AE@% The numeric label the substring's position is assigned to.%@NL@% %@NL@% %@AI@%start%@AE@% The starting position for the search. When omitted, the %@AB@%INSTR%@AE@% directive starts searching at the first character. The first character in the string has a position of one.%@NL@% %@NL@% %@AI@%string1%@AE@% The string being searched.%@NL@% %@NL@% %@AI@%string2%@AE@% The string to look for.%@NL@% %@NL@% %@NL@% %@4@% The following lines set %@AS@%colpos %@AE@%to the character position of the first%@EH@% colon in %@AS@%segarg%@AE@%:%@NL@% %@NL@% %@AS@%segarg EQU <ES:AX>%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%colpos INSTR segarg,<:> ; colpos = 3%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.3.5 @%%@AB@%11.3.5 Using String Directives Inside Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following example uses the text-macro string directives %@AB@%CATSTR%@AE@%, %@AB@%INSTR%@AE@%,%@EH@% %@AB@%SIZESTR%@AE@%, and %@AB@%SUBSTR%@AE@%. It defines two macros, %@AS@%SaveRegs %@AE@%and %@AS@%RestRegs%@AE@%, that save and restore registers on the stack. The macros are written so that %@AS@%RestRegs %@AE@%restores only the most recently saved group of registers.%@NL@% %@NL@% %@4@% The %@AS@%SaveRegs %@AE@%macro uses a text macro, %@AS@%regpushed%@AE@%, to keep track of the%@EH@% registers pushed onto the stack. The %@AS@%RestRegs %@AE@%macro uses this string to restore the proper registers. Each time the %@AS@%SaveRegs %@AE@%macro is invoked, it adds a pound sign (#) to the string to mark the start of a new group of registers. The %@AS@%RestRegs %@AE@%macro restores the most recently saved group by finding the first pound sign in the string, creating a substring containing the saved register names, and then looping and generating %@AB@%PUSH%@AE@% instructions.%@NL@% %@NL@% %@AS@%; Initialize regpushed to the null string%@AE@%%@NL@% %@AS@%regpushed EQU <>%@AE@%%@NL@% %@NL@% %@AS@%; SaveRegs%@AE@%%@NL@% %@AS@%; Loops and generates a push for each argument register.%@AE@%%@NL@% %@AS@%; Saves each register name in regpushed.%@AE@%%@NL@% %@NL@% %@AS@%SaveRegs MACRO r1,r2,r3,r4,r5,r6,r7,r8,r9%@AE@%%@NL@% %@AS@% regpushed CATSTR <#>,regpushed ;; Mark a new group of regs%@AE@%%@NL@% %@AS@% IRP reg,<r1,r2,r3,r4,r5,r6,r7,r8,r9>%@AE@%%@NL@% %@AS@% IFNB <reg>%@AE@%%@NL@% %@AS@% push reg ;; Push and record a register%@AE@%%@NL@% %@AS@% regpushed CATSTR <reg>,<,>,regpushed%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% EXITM ;; Quit on blank argument%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@%; RestRegs%@AE@%%@NL@% %@AS@%; Generates a pop for each register in the most recently saved groups%@AE@%%@NL@% %@AS@%%@AE@%%@NL@% %@AS@%RestRegs MACRO%@AE@%%@NL@% %@AS@% numloc INSTR regpushed,<#> ;; Find location of #%@AE@%%@NL@% %@AS@% reglist SUBSTR regpushed,1,numloc-1 ;; Get list of registers to pop%@AE@%%@NL@% %@AS@% reglen SIZESTR regpushed ;; Adjust numloc if # is notlast%@AE@%%@NL@% %@AS@% IF reglen GT numloc ;; item in the string%@AE@%%@NL@% %@AS@% numloc = numloc + 1%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% regpushed SUBSTR regpushed,numloc ;; Remove list from regpushed%@AE@%%@NL@% %@AS@% % IRP reg,<reglist> ;; Generate pop for each register%@AE@%%@NL@% %@AS@% IFNB <reg>%@AE@%%@NL@% %@AS@% pop reg%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% The following lines from a listing file show the sample code that the%@EH@% macros would generate (a %@AS@%2 %@AE@%marks lines generated by the macros):%@NL@% %@NL@% %@AS@%SaveRegs ax,bx%@AE@%%@NL@% %@AS@%2 push ax ;%@AE@%%@NL@% %@AS@%2 push bx ;%@AE@%%@NL@% %@AS@% SaveRegs cx%@AE@%%@NL@% %@AS@%2 push cx ;%@AE@%%@NL@% %@AS@% SaveRegs dx%@AE@%%@NL@% %@AS@%2 push dx ;%@AE@%%@NL@% %@AS@% RestRegs%@AE@%%@NL@% %@AS@%2 pop dx%@AE@%%@NL@% %@AS@% RestRegs%@AE@%%@NL@% %@AS@%2 pop cx%@AE@%%@NL@% %@AS@% RestRegs%@AE@%%@NL@% %@AS@%2 pop bx%@AE@%%@NL@% %@AS@%2 pop ax%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.4 @%%@AB@%11.4 Defining Repeat Blocks%@AE@%%@EH@%%@NL@% %@CR:IX11.74 @%%@CR:IX11.75 @%%@CR:IX11.76 @%%@CR:IX11.77 @%%@CR:IX11.78 @%%@CR:IX11.79 @%%@CR:IX11.80 @%%@NL@% %@4@%%@CR:IX11.81 @%%@CR:IX11.82 @%%@CR:IX11.83 @%%@CR:IX11.84 @%%@CR:IX11.85 @% Repeat blocks are a special form of macro that allows you to create blocks%@EH@% of repeated statements. They differ from macros in that they are not named, and thus cannot be called. However, like macros, they can have parameters that are replaced by actual arguments during assembly. Macro operators, symbols declared with the %@AB@%LOCAL%@AE@% directive, and the %@AB@%EXITM%@AE@% directive can be used in repeat blocks. Like macros, repeat blocks are always terminated by an %@AB@%ENDM %@AE@%directive.%@NL@% %@NL@% %@4@% Repeat blocks are frequently placed in macros in order to repeat some of%@EH@% the statements in the macro. They can also be used independently, usually for declaring arrays with repeated data elements.%@NL@% %@NL@% %@4@% Repeat blocks are processed at assembly time and should not be confused%@EH@% with the %@AB@%REP%@AE@% instruction, which causes string instructions to be repeated at run time, as explained in Chapter 16%@BO: d1833@%, "Processing Strings."%@NL@% %@NL@% %@4@% Three different kinds of repeat blocks can be defined by using the %@AB@%REPT%@AE@%,%@EH@% %@AB@%IRP%@AE@%, and %@AB@%IRPC%@AE@% directives. The difference between them is in how the number of repetitions is specified.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.4.1 @%%@AB@%11.4.1 The REPT Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.86 @%%@CR:IX11.87 @%%@CR:IX11.88 @%%@CR:IX11.89 @%%@CR:IX11.90 @% The %@AB@%REPT%@AE@% directive is used to create repeat blocks in which the number of%@EH@% repetitions is specified with a numeric argument.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%REPT %@AE@% %@AI@%expression%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%ENDM%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%expression%@AE@% must evaluate to a numeric constant (a 16-bit unsigned%@EH@% number). It specifies the number of repetitions. Any valid assembler %@AI@%statements%@AE@% may be placed within the repeat block.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%alphabet LABEL BYTE%@AE@%%@NL@% %@AS@%x = 0 ;; Initialize%@AE@%%@NL@% %@AS@% REPT 26 ;; Specify 26 repetitions%@AE@%%@NL@% %@AS@% DB 'A' + x ;; Allocate ASCII code for letter%@AE@%%@NL@% %@AS@%x = x + 1 ;; Increment%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This example repeats the equal-sign (%@AB@%=%@AE@%) and %@AB@%DB%@AE@% directives to initialize%@EH@% ASCII values for each uppercase letter of the alphabet.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.4.2 @%%@AB@%11.4.2 The IRP Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.91 @%%@CR:IX11.92 @%%@CR:IX11.93 @%%@CR:IX11.94 @%%@CR:IX11.95 @% The %@AB@%IRP%@AE@% directive is used to create repeat blocks in which the number of%@EH@% repetitions, as well as parameters for each repetition, is specified in a list of arguments.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IRP%@AE@% %@AI@%parameter%@AE@%%@AB@%,<%@AE@%%@AI@%argument%@AE@%[[%@AB@%,%@AE@%%@AI@%argument%@AE@%]]...%@AB@%>%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%ENDM%@AE@%%@NL@% %@NL@% %@4@% The assembler %@AI@%statements%@AE@% inside the block are repeated once for each%@EH@% %@AI@%argument%@AE@% in the list enclosed by angle brackets (%@AB@%< >%@AE@%). The %@AI@%parameter%@AE@% is a name for a placeholder to be replaced by the current argument. Each argument can be text, such as a symbol, string, or numeric constant. Any number of arguments can be given. If multiple arguments are given, they must be separated by commas. The angle brackets %@AB@%(< >)%@AE@% around the argument list are required. The parameter can be used any number of times in the statements.%@NL@% %@NL@% %@4@% When QuickAssembler encounters an %@AB@%IRP%@AE@% directive, it makes one copy of the%@EH@% statements for each argument in the enclosed list. While copying the statements, it substitutes the current argument for all occurrences of %@AI@%parameter%@AE@% in these statements. If a null argument (%@AB@%< >%@AE@%) is found in the list, the dummy name is replaced with a blank value. If the argument list is empty, the %@AB@%IRP%@AE@% directive is ignored and no statements are copied.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%numbers LABEL BYTE%@AE@%%@NL@% %@AS@% IRP x,<0,1,2,3,4,5,6,7,8,9>%@AE@%%@NL@% %@AS@% DB 10 DUP(x)%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This example repeats the %@AB@%DB%@AE@% directive 10 times, allocating 10 bytes for%@EH@% each number in the list. The resulting statements create 100 bytes of data, starting with 10 zeros, followed by 10 ones, and so on.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.4.3 @%%@AB@%11.4.3 The IRPC Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.96 @%%@CR:IX11.97 @%%@CR:IX11.98 @%%@CR:IX11.99 @%%@CR:IX11.100 @% The %@AB@%IRPC%@AE@% directive is used to create repeat blocks in which the number of%@EH@% repetitions, as well as arguments for each repetition, is specified in a string.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IRPC%@AE@% %@AI@%parameter%@AE@%%@AB@%,%@AE@%%@AI@%string%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%ENDM%@AE@%%@NL@% %@NL@% %@4@% The assembler %@AI@%statements%@AE@% inside the block are repeated as many times as%@EH@% there are characters in %@AI@%string%@AE@%. The %@AI@%parameter%@AE@% is a name for a placeholder to be replaced by the current character in the string. The string can be any combination of letters, digits, and other characters. It should be enclosed with angle brackets (%@AB@%< >%@AE@%) if it contains spaces, commas, or other separating characters. The parameter can be used any number of times in these statements.%@NL@% %@NL@% %@4@% When QuickAssembler encounters an %@AB@%IRPC%@AE@% directive, it makes one copy of the%@EH@% statements for each character in the string. While copying the statements, it substitutes the current character for all occurrences of %@AI@%parameter%@AE@% in these statements.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%ten LABEL BYTE%@AE@%%@NL@% %@AS@% IRPC x,0123456789%@AE@%%@NL@% %@AS@% DB x%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% Example 1 repeats the %@AB@%DB%@AE@% directive 10 times, once for each character in%@EH@% the string %@AS@%0123456789%@AE@%. The resulting statements create 10 bytes of data having the values 0-9.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% IRPC letter,ABCDEFGHIJKLMNOPQRSTUVWXYZ%@AE@%%@NL@% %@AS@% DB '&letter' ; Allocate uppercase letter%@AE@%%@NL@% %@AS@% DB '&letter'+20h ; Allocate lowercase letter%@AE@%%@NL@% %@AS@% DB '&letter'-40h ; Allocate number of letter%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% Example 2 allocates the ASCII codes for uppercase, lowercase, and numeric%@EH@% versions of each letter in the string. Notice that the substitute operator (%@AB@%&%@AE@%) is required so that %@AS@%letter %@AE@%will be treated as an argument rather than a string. See Section 11.5.1%@BO: 9a58b@%, "Substitute Operator," for more information.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.5 @%%@AB@%11.5 Using Macro Operators%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.101 @%%@CR:IX11.102 @%%@CR:IX11.103 @% Macro and conditional directives use the following special set of macro%@EH@% operators:%@NL@% %@NL@% %@AB@%Operator%@AE@% %@AB@%Definition%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%&%@AE@% Substitute operator%@NL@% %@NL@% %@AB@%<>%@AE@% Literal-text operator%@NL@% %@NL@% %@AB@%!%@AE@% Literal-character operator%@NL@% %@NL@% %@AB@%%%@AE@% Expression operator%@NL@% %@NL@% %@AB@%;;%@AE@% Macro comment%@NL@% %@NL@% %@NL@% %@4@% When used in a macro definition, a macro call, a repeat block, or as the%@EH@% argument of a conditional-assembly directive, these operators carry out special control operations, such as text substitution.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.5.1 @%%@AB@%11.5.1 Substitute Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.104 @%%@CR:IX11.105 @%%@CR:IX11.106 @%%@CR:IX11.107 @% The substitute operator (%@AB@%&%@AE@%) enables substitution of macro parameters to%@EH@% take place, even when the parameter occurs within a larger word or within a quoted string.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% You can use the substitute operator in any one of three different ways:%@EH@%%@NL@% %@NL@% %@4@% %@AI@%name1%@AE@%%@AB@%&%@AE@%%@AI@%name2%@AE@%%@EH@%%@NL@% %@AI@%name%@AE@%%@AB@%&%@AE@%%@NL@% %@AB@%&%@AE@%%@AI@%name%@AE@%%@NL@% %@NL@% %@4@% The assembler responds by analyzing %@AI@%name1%@AE@% and %@AI@%name2%@AE@% separately, then%@EH@% joining them together. If either %@AI@%name1%@AE@% or %@AI@%name2%@AE@% is a parameter, the assembler replaces each parameter by an actual argument before joining the names. You can join any number of names with the substitute operator, so that items such as %@AS@%a&b&c %@AE@%are valid.%@NL@% %@NL@% %@4@% The last two forms are useful when a parameter name appears within a%@EH@% quoted string. The assembler responds by substituting the actual argument for the parameter; when not next to an ampersand (%@AB@%&%@AE@%), the assembler considers the parameter name just part of the string data.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%declare MACRO x,y%@AE@%%@NL@% %@AS@%xy DW 0%@AE@%%@NL@% %@AS@%x&y DW 0%@AE@%%@NL@% %@AS@%x&str DB 'x and y params are &x and &y'%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% The example above demonstrates how the presence or absence of the%@EH@% substitute operator affects macro substitution. Given the macro definition above, the statement%@NL@% %@NL@% %@AS@% declare foot,ball%@AE@%%@NL@% %@NL@% %@4@% is expanded to%@EH@%%@NL@% %@NL@% %@AS@%xy DW%@AE@%%@NL@% %@AS@%football DW 0%@AE@%%@NL@% %@AS@%footstr DB 'x and y params are foot and ball'%@AE@%%@NL@% %@NL@% %@4@% In the first statement of the macro, %@AS@%xy %@AE@%is not identified with either of%@EH@% the parameters %@AS@%x %@AE@%or %@AS@%y%@AE@%; instead, %@AS@%xy %@AE@%forms a distinct name. No substitution takes place. In the second statement, the assembler interprets %@AS@%x&y %@AE@%by analyzing %@AS@%x %@AE@%and %@AS@%y %@AE@%as separate names, performing the appropriate parameter substitution in each case, and then joining the resulting names together.%@NL@% %@NL@% %@4@% When you use the substitute operator with nested macros and repeat blocks,%@EH@% the assembler applies the following rules to expressions outside of quotes:%@NL@% %@NL@% 1. Perform parameter substitution as described above.%@NL@% %@NL@% 2. Remove exactly one of the substitute operators (%@AB@%&%@AE@%) from the group. The assembler strips off an operator whether or not a parameter was substituted.%@NL@% %@NL@% %@4@% The number of ampersands (%@AB@%&%@AE@%) you use should never be greater than the%@EH@% number of levels of nesting. If you use too few ampersands, proper substitution will not take place. If you use too many ampersands, text such as%@AS@% x&y %@AE@%will remain after macro expansion is complete. Such expressions are invalid.%@NL@% %@NL@% %@4@% When an ampersand appears inside quotes, the assembler removes ampersands%@EH@% on either side of a macro parameter when substitution is possible. When substitution is not possible (because the parameter name is not defined at the current level of nesting), the assembler leaves the ampersand as it is. With this method, parameter substitution automatically takes place at the appropriate level of nesting.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.5.2 @%%@AB@%11.5.2 Literal-Text Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.108 @% The literal-text operator (%@AB@%< >%@AE@%) directs QuickAssembler to treat a list as%@EH@% a single string rather than as separate arguments.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%<%@AE@%%@AI@%text%@AE@% %@AB@%>%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%text%@AE@% is considered a single literal element even if it contains%@EH@% commas, spaces, or tabs. The literal-text operator is most often used in macro calls and with the %@AB@%IRP%@AE@% directive to ensure that values in a parameter list are treated as a single parameter.%@NL@% %@NL@% %@4@% The literal-text operator can also be used to force QuickAssembler to%@EH@% treat special characters, such as the semicolon or the ampersand, literally. For example, the semicolon inside angle brackets %@AB@%<;>%@AE@% becomes a semicolon, not a comment indicator.%@NL@% %@NL@% %@4@% QuickAssembler removes one set of angle brackets each time the parameter%@EH@% is used in a macro. When using nested macros, you will need to supply as many sets of angle brackets as there are levels of nesting.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% work 1,2,3,4,5 ; Passes five parameters to "work"%@AE@%%@NL@% %@NL@% %@AS@% work <1,2,3,4,5> ; Passes one five-element%@AE@%%@NL@% %@AS@% ; parameter to "work"%@AE@%%@NL@% %@NL@% %@4@% When the%@AB@% IRP%@AE@% directive is used inside a macro definition and when the%@EH@% argument list of the %@AB@%IRP%@AE@% directive is also a parameter of the macro, you must use the literal-text operator (%@AB@%< >%@AE@%) to enclose the macro parameter.%@NL@% %@NL@% %@4@% For example, in the following macro definition, the parameter %@AS@%x %@AE@%is used as%@EH@% the argument list for the%@AB@% IRP%@AE@% directive:%@NL@% %@NL@% %@AS@%init MACRO x%@AE@%%@NL@% %@AS@% IRP y,<x>%@AE@%%@NL@% %@AS@% DB y%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% If this macro is called with%@EH@%%@NL@% %@NL@% %@AS@% init <0,1,2,3,4,5,6,7,8,9>%@AE@%%@NL@% %@NL@% %@4@% the macro removes the angle brackets from the parameter so that it is%@EH@% expanded as %@AS@%0,1,2,3,4,5,6,7,8,9%@AE@%. The brackets inside the repeat block are necessary to put the angle brackets back on. The repeat block is then expanded as shown below:%@NL@% %@NL@% %@AS@% IRP y,<0,1,2,3,4,5,6,7,8,9>%@AE@%%@NL@% %@AS@% DB y%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.5.3 @%%@AB@%11.5.3 Literal-Character Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.109 @%%@CR:IX11.110 @%%@CR:IX11.111 @%%@CR:IX11.112 @% The literal-character operator (%@AB@%!%@AE@%) forces the assembler to treat a%@EH@% specified character literally rather than as a symbol.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%!%@AE@%%@AI@%character%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The literal-character operator is used with special characters, such as%@EH@% the semicolon or ampersand, when meaning of the special character must be suppressed. Using the literal-character operator is the same as enclosing a single character in brackets. For example, %@AS@%!!%@AE@% is the same as %@AS@%<!>.%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%errgen MACRO y,x%@AE@%%@NL@% %@AS@% PUBLIC err&y%@AE@%%@NL@% %@AS@%err&y DB 'Error &y: &x'%@AE@%%@NL@% %@AS@% ENDMW%@AE@%%@NL@% %@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% errgen 103,<Expression !> 255>%@AE@%%@NL@% %@NL@% %@4@% The example macro call is expanded to allocate the string %@AS@%Error 103:%@AE@%%@EH@% %@AS@%Expression > 255%@AE@%. Without the literal-character operator, the greater-than symbol would be interpreted as the end of the argument and an error would result.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.5.4 @%%@AB@%11.5.4 Expression Operator%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.113 @%%@CR:IX11.114 @%%@CR:IX11.115 @%%@CR:IX11.116 @% The expression operator (%@AB@%%%@AE@%) causes the assembler to treat the argument%@EH@% following the operator as an expression.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%%%@AE@%%@AI@%text%@AE@%%@EH@%%@NL@% %@NL@% %@4@% QuickAssembler computes the expression's value and replaces %@AI@%text%@AE@% with the%@EH@% result. The expression can be either a numeric expression or a text equate. Additional arguments after an argument that uses the expression operator must be preceded by a comma, not a space or tab.%@NL@% %@NL@% %@4@% The expression operator is typically used in macro calls when the%@EH@% programmer needs to pass the result of an expression rather than the actual expression to a macro.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%printe MACRO exp,val%@AE@%%@NL@% %@AS@% IF1 ;; On pass 1 only%@AE@%%@NL@% %@AS@% %OUT exp = val ;; Display expression and result%@AE@%%@NL@% %@AS@% ENDIF ;; to screen%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@%sym1 EQU 100%@AE@%%@NL@% %@AS@%sym2 EQU 200%@AE@%%@NL@% %@AS@%msg EQU <"Hello, World.">%@AE@%%@NL@% %@NL@% %@AS@% printe <sym1 + sym2>,%(sym1 + sym2)%@AE@%%@NL@% %@AS@% printe msg,%msg%@AE@%%@NL@% %@NL@% %@4@% In the first macro call, the text literal %@AS@%sym1 + sym2 = %@AE@%is passed to the%@EH@% parameter %@AS@%exp%@AE@%, and the result of the expression is passed to the parameter %@AS@%val%@AE@%. In the second macro call, the equate name %@AS@%msg%@AE@% is passed to the parameter %@AS@%exp%@AE@%, and the text of the equate is passed to the parameter %@AS@%val%@AE@%. As a result, Quick-Assembler displays the following messages:%@NL@% %@NL@% %@AS@%sym1 + sym2 = 300%@AE@%%@NL@% %@AS@%msg = "Hello, World."%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%%OUT%@AE@% directive, which sends a message to the screen, is described in%@EH@% Section 12.1%@BO: a2455@%, "Sending Messages to the Standard Output Device"; the %@AB@%IF1%@AE@% directive is described in Section 10.1.2%@BO: 87b1f@%, "Testing the Pass with IF1 and IF2 Directives."%@NL@% %@NL@% %@4@% You can also use the expression operator (%@AB@%%%@AE@%) to substitute the values of%@EH@% text macros for the macro names anywhere a text-macro name appears. When the expression operator is the first item on a line and is followed by one or more blanks or tabs, the line is scanned for text macros and the values of the macros are substituted. Using the expression operator, you can force substitution of text macros wherever they appear in a line. The assembler re-scans the line until all substitutions have been made.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Text macros are always evaluated when they appear in the name or operation fields. The expression operator is required to evaluate a text macro only when the macro appears in the operand field.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% This use of the expression operator eliminates the need to do a macro call%@EH@% in order to evaluate a text macro. For example, the following macro uses a separate macro, %@AS@%popregs%@AE@%, to evaluate the text macro %@AS@%regpushed%@AE@%:%@NL@% %@NL@% %@AS@%regpushed EQU <ax,bx,cx>%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%RestRegs MACRO%@AE@%%@NL@% %@AS@% popregs %regpushed%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@%popregs MACRO reglist%@AE@%%@NL@% %@AS@% IRP reg,<reglist>%@AE@%%@NL@% %@AS@% pop reg%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% The use of the expression operator to evaluate text macros in a line makes%@EH@% the %@AS@%popregs %@AE@%macro unnecessary:%@NL@% %@NL@% %@AS@%regpushed EQU <ax,bx,cx>%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%RestRegs MACRO%@AE@%%@NL@% %@AS@% % IRP reg,<regpushed> ;; % operator makes%@AE@%%@NL@% %@AS@% ;; separate macro unnecessary%@AE@%%@NL@% %@AS@% pop reg%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% You cannot use the %@AB@%EQU%@AE@% directive to assign a value to a text macro in a%@EH@% line evaluated with the expression operator. For example, the following lines generate an error:%@NL@% %@NL@% %@AS@%strpos EQU <[si]+12>%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%% wpstrpos EQU <WORD PTR strpos>%@AE@%%@NL@% %@NL@% %@4@% On pass 1, %@AS@%wpstrpos %@AE@%is defined as a text macro that is expanded on pass 2.%@EH@% Thus, on pass 2 the second %@AB@%EQU%@AE@% directive becomes%@NL@% %@NL@% %@AS@%WORD PTR [si]+12 EQU <WORD PTR [si]+12>%@AE@%%@NL@% %@NL@% %@4@% and generates an error.%@EH@%%@NL@% %@NL@% %@4@% Instead, use the %@AB@%CATSTR%@AE@% directive to assign values to text macros (see%@EH@% Section 11.3%@BO: 95341@%, "Text-Macro String Directives," for more information about %@AB@%CATSTR%@AE@% and other text-macro string directives). The previous example should be rewritten as follows:%@NL@% %@NL@% %@AS@%strpos EQU <[si]+12>%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%wpstrpos CATSTR <WORD PTR >, strpos%@AE@%%@NL@% %@NL@% %@4@% If the text macro evaluates to a valid name, there is no error when you%@EH@% use %@AB@%EQU%@AE@%. The following lines do not generate an error, but define two names, one (%@AS@%numlabel%@AE@%) with the value %@AS@%5%@AE@%, the other (%@AS@%tmacro%@AE@%) with the value %@AS@%<numlabel>%@AE@%:%@NL@% %@NL@% %@AS@%tmacro EQU <numlabel>%@AE@%%@NL@% %@AS@%% tmacro EQU 5%@AE@%%@NL@% %@NL@% %@4@% You can also use the substitution operator (%@AB@%&%@AE@%) with text macros just as%@EH@% you would inside a macro:%@NL@% %@NL@% %@AS@%SegName EQU <MySeg>%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%.%@AE@%%@NL@% %@AS@%% SegName&_text SEGMENT PUBLIC 'CODE'%@AE@%%@NL@% %@NL@% %@4@% The final line, after expanding the text macro, becomes:%@EH@%%@NL@% %@NL@% %@AS@%MySeg_text SEGMENT PUBLIC 'CODE'%@AE@%%@NL@% %@NL@% %@4@% The substitution operator separates the text-macro name from the text that%@EH@% immediately follows it. The name appears to the assembler as %@AS@%segName_text%@AE@% without the substitution operator, and the assembler fails to recognize the text macro.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.5.5 @%%@AB@%11.5.5 Macro Comments%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.117 @%%@CR:IX11.118 @%%@CR:IX11.119 @%%@CR:IX11.120 @% A macro comment is any text in a macro definition that does not need to be%@EH@% copied in the macro expansion. A double semicolon (%@AB@%;;%@AE@%) is used to start a macro comment.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%;;%@AE@%%@AI@%text%@AE@%%@EH@%%@NL@% %@NL@% %@4@% All %@AI@%text%@AE@% following the double semicolon (%@AB@%;;%@AE@%) is ignored by the assembler%@EH@% and will appear only in the macro definition when the source listing is created.%@NL@% %@NL@% %@4@% The regular comment operator (%@AB@%;%@AE@%) can also be used in macros. However,%@EH@% regular comments may appear in listings when the macro is expanded. Macro comments will appear in the macro definition, but not in macro expansions. Whether or not regular comments are listed in macro expansions depends on the use of the %@AB@%.LALL%@AE@%, %@AB@%.XALL%@AE@%, and %@AB@%.SALL%@AE@% directives, as described in Section 12.2.3%@BO: a39b9@%, "Controlling Page Breaks."%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.6 @%%@AB@%11.6 Using Recursive, Nested, and Redefined Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The concept of replacing macro names with predefined macro text is simple,%@EH@% but in practice it has many implications and potentially unexpected side effects. The following sections discuss advanced macro features (such as nesting, recursion, and redefinition) and point out some side effects of macros.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.6.1 @%%@AB@%11.6.1 Using Recursion%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.121 @%%@CR:IX11.122 @% Macro definitions can be recursive: that is, they can call themselves.%@EH@% Using recursive macros is one way of doing repeated operations. The macro does a task, and then calls itself to do the task again. The recursion is repeated until a specified condition is met.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%pushall MACRO reg1,reg2,reg3,reg4,reg5,reg6%@AE@%%@NL@% %@AS@% IFNB <reg1> ;; If parameter not blank%@AE@%%@NL@% %@AS@% push reg1 ;; push one register and repeat%@AE@%%@NL@% %@AS@% pushall reg2,reg3,reg4,reg5,reg6%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%pushall ax,bx,si,ds%@AE@%%@NL@% %@AS@%pushall cs,es%@AE@%%@NL@% %@NL@% %@4@% In this example, the %@AS@%pushall%@AE@% macro repeatedly calls itself to push a%@EH@% register given in a parameter until no parameters are left to push. A variable number of parameters (up to six) can be given.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.6.2 @%%@AB@%11.6.2 Nesting Macro Definitions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.123 @%%@CR:IX11.124 @% One macro can define another. QuickAssembler does not process nested%@EH@% definitions until the outer macro has been called. Therefore, nested macros cannot be called until the outer macro has been called at least once. Macro definitions can be nested to any depth. Nesting is limited only by the amount of memory available when the source file is assembled. Using a macro to create similar macros can make maintenance easier. If you want to change all the macros, change the outer macro and it automatically changes the others.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%shifts MACRO opname ; Define macro that defines macros%@AE@%%@NL@% %@AS@%opname&s MACRO operand,rotates%@AE@%%@NL@% %@AS@% IF rotates LE 4%@AE@%%@NL@% %@AS@% REPT rotates%@AE@%%@NL@% %@AS@% opname operand,1 ;; One at a time is faster%@AE@%%@NL@% %@AS@% ENDM ;; for 4 or less on 8088/8086%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% mov cl,rotates ;; Using CL is faster%@AE@%%@NL@% %@AS@% opname operand,cl ;; for more than 4 on 8088/8086%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@% shifts ror ; Call macro to new macros%@AE@%%@NL@% %@AS@% shifts rol%@AE@%%@NL@% %@AS@% shifts shr%@AE@%%@NL@% %@AS@% shifts shl%@AE@%%@NL@% %@AS@% shifts rcl%@AE@%%@NL@% %@AS@% shifts rcr%@AE@%%@NL@% %@AS@% shifts sal%@AE@%%@NL@% %@AS@% shifts sar%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% shrs ax,5 ; Call defined macros%@AE@%%@NL@% %@AS@% rols bx,3%@AE@%%@NL@% %@NL@% %@4@% This macro, when called as shown, creates macros for multiple shifts with%@EH@% each of the shift and rotate instructions. All the macro names are identical except for the instruction. For example, the macro for the %@AB@%SHR%@AE@% instruction is called %@AS@%shrs%@AE@%; the macro for the %@AB@%ROL%@AE@% instruction is called %@AS@%rols%@AE@%. If you want to enhance the macros by doing more parameter checking, you can modify the original macro. Doing so will change the created macros automatically. This macro uses the substitute operator, as described in Section 11.5.1%@BO: 9a58b@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.6.3 @%%@AB@%11.6.3 Nesting Macro Calls%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Macro definitions can contain calls to other macros. Nested macro calls%@EH@% are expanded like any other macro call, but only when the outer macro is called.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%ex MACRO text,val ; Inner macro definition%@AE@%%@NL@% %@AS@% IF2%@AE@%%@NL@% %@AS@% %OUT The expression (text) has the value: &val%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@AS@%express MACRO expression ; Outer macro definition%@AE@%%@NL@% %@AS@% ex <expression>,%(expression)%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% express <4 + 2 * 7 - 3 MOD 4>%@AE@%%@NL@% %@NL@% %@4@% The two sample macros enable you to print the result of a complex%@EH@% expression to the screen by using the %@AB@%%OUT%@AE@% directive, even though that directive expects text rather than an expression (see Section 12.1%@BO: a2455@%, "Sending Messages to the Standard Output Device"). Being able to see the value of an expression is convenient during debugging.%@NL@% %@NL@% %@4@% Both macros are necessary. The %@AS@%express%@AE@% macro calls the %@AS@%ex%@AE@% macro, using%@EH@% operators to pass the expression both as text and as the value of the expression. With the call in the example, the assembler sends the following line to the standard output:%@NL@% %@NL@% %@AS@%The expression (4 + 2 * 7 - 3 MOD 4) has the value: 15%@AE@%%@NL@% %@NL@% %@4@% You could get the same output by using only the %@AS@%ex%@AE@% macro, but you would%@EH@% have to type the expression twice and supply the macro operators in the correct places yourself. The %@AS@%express%@AE@% macro does this for you automatically. Notice that expressions containing spaces must still be enclosed in angle brackets. Section 11.5.2%@BO: 9b48f@%, "Literal-Text Operator," explains why.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.6.4 @%%@AB@%11.6.4 Redefining Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.125 @%%@CR:IX11.126 @% Macros can be redefined. You do not need to purge the macro before%@EH@% redefining it. The new definition automatically replaces the old definition. If you redefine a macro from within the macro itself, make sure there are no statements or comments between the %@AB@%ENDM%@AE@% directive of the nested redefinition and the %@AB@%ENDM%@AE@% directive of the original macro.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%getasciiz MACRO%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%max DB 80%@AE@%%@NL@% %@AS@%actual DB ?%@AE@%%@NL@% %@AS@%tmpstr DB 80 DUP (?)%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% mov ah,0Ah%@AE@%%@NL@% %@AS@% mov dx,OFFSET max%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% mov bl,actual%@AE@%%@NL@% %@AS@% xor bh,bh%@AE@%%@NL@% %@AS@% mov tmpstr[bx],0%@AE@%%@NL@% %@AS@%getasciiz MACRO%@AE@%%@NL@% %@AS@% mov ah,0Ah%@AE@%%@NL@% %@AS@% mov dx,OFFSET max%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% mov bl,actual%@AE@%%@NL@% %@AS@% xor bh,bh%@AE@%%@NL@% %@AS@% mov tmpstr[bx],0%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@NL@% %@4@% This macro allocates data space the first time it is called, and then%@EH@% redefines itself so that it doesn't try to reallocate the data on subsequent calls.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.6.5 @%%@AB@%11.6.5 Avoiding Inadvertent Substitutions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.127 @%%@CR:IX11.128 @%%@CR:IX11.129 @%%@CR:IX11.130 @%%@CR:IX11.131 @% QuickAssembler replaces all parameters when they occur with the%@EH@% corresponding argument, even if the substitution is inappropriate. For example, if you use a register name, such as AX or BH, as a parameter, QuickAssembler replaces all occurrences of that name when it expands the macro. If the macro definition contains statements that use the register, not the parameter, the macro will be incorrectly expanded. QuickAssembler will not warn you about using reserved names as macro parameters.%@NL@% %@NL@% %@4@%%@CR:IX11.132 @%%@CR:IX11.133 @% QuickAssembler does give a warning if you use a reserved name as a macro%@EH@% name. You can ignore the warning, but be aware that the reserved name will no longer have its original meaning. For example, if you define a macro called %@AS@%ADD%@AE@%, the %@AB@%ADD%@AE@% instruction will no longer be available. Your %@AS@%ADD%@AE@% macro takes its place.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC11.7 @%%@AB@%11.7 Managing Macros and Equates%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Macros and equates are often kept in a separate file and read into the%@EH@% assembler source file at assembly time. In this way, libraries of related macros and equates can be used by many different source files.%@NL@% %@NL@% %@4@% The%@AB@% INCLUDE %@AE@%directive is used to read an include file into a source file.%@EH@% Memory can be saved by using the %@AB@%PURGE%@AE@% directive to delete the unneeded macros from memory.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.7.1 @%%@AB@%11.7.1 Using Include Files%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.134 @%%@CR:IX11.135 @%%@CR:IX11.136 @%%@CR:IX11.137 @%%@CR:IX11.138 @% The INCLUDE directive inserts source code from a specified file into the%@EH@% source file from which the directive is given.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%INCLUDE %@AE@%%@AI@%filespec%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.139 @% The %@AI@%filespec%@AE@% must specify an existing file containing valid assembler%@EH@% statements. When the assembler encounters an %@AB@%INCLUDE%@AE@% directive, it opens the specified source file and begins processing its statements. When all statements have been read, QuickAssembler continues with the statement immediately following the %@AB@%INCLUDE%@AE@% directive.%@NL@% %@NL@% %@4@% The %@AI@%filespec%@AE@% can be given either as a file name, or as a complete or%@EH@% relative file specification, including drive or directory name.%@NL@% %@NL@% %@4@%%@CR:IX11.140 @% If a complete or relative file specification is given, QuickAssembler%@EH@% looks for the include file only in the specified directory. If a file name is given without a directory or drive name, QuickAssembler looks for the file in the following order:%@NL@% %@NL@% %@CR:IX11.141 @%%@CR:IX11.142 @%%@CR:IX11.143 @%%@CR:IX11.144 @% 1. If paths are specified with the /I option, QuickAssembler looks for the include file in the specified directory or directories.%@NL@% %@NL@% 2. QuickAssembler looks for the include file in the current directory.%@NL@% %@NL@% 3. If an %@AB@%INCLUDE%@AE@% environment variable is defined, QuickAssembler looks for the include file in the directory or directories specified in the environment variable.%@NL@% %@NL@% %@4@%%@CR:IX11.145 @% Nested %@AB@%INCLUDE%@AE@% directives are allowed. QuickAssembler marks included%@EH@% statements with the letter "C" in assembly listings.%@NL@% %@NL@% %@4@%%@CR:IX11.146 @% Directories can be specified in %@AB@%INCLUDE%@AE@% path names with either the%@EH@% backslash (%@AB@%\%@AE@%) or the forward slash (%@AB@%/%@AE@%). This is for XENIX(R) compatibility.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Any standard code can be placed in an include file. However, include files are usually used only for macros, equates, and standard segment definitions. Standard procedures are usually assembled into separate object files and linked with the main source modules.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%INCLUDE fileio.mac ; File name only; use with%@AE@%%@NL@% %@AS@% ; /I or environment%@AE@%%@NL@% %@AS@%INCLUDE b:\include\keybd.inc ; Complete file specification%@AE@%%@NL@% %@NL@% %@AS@%INCLUDE /usr/jons/include/stdio.mac ; Path name in XENIX format%@AE@%%@NL@% %@NL@% %@AS@%INCLUDE masm_inc\define.inc ; Partial path name in DOS format%@AE@%%@NL@% %@AS@% ; (relative to current directory)%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC11.7.2 @%%@AB@%11.7.2 Purging Macros from Memory%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX11.147 @%%@CR:IX11.148 @%%@CR:IX11.149 @% The %@AB@%PURGE%@AE@% directive can be used to delete a currently defined macro from%@EH@% memory.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%PURGE%@AE@% %@AI@%macroname%@AE@%[[%@AB@%,%@AE@%%@AI@%macroname%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% Each %@AI@%macroname%@AE@% is deleted from memory when the directive is encountered at%@EH@% assembly time. Any subsequent call to that macro causes the assembler to generate an error.%@NL@% %@NL@% %@4@%%@CR:IX11.150 @%%@CR:IX11.151 @% The %@AB@%PURGE%@AE@% directive is intended to clear memory space no longer needed by%@EH@% a macro. If a macro has been used to redefine a reserved name, the reserved name is restored to its previous meaning.%@NL@% %@NL@% %@4@%%@CR:IX11.152 @%%@CR:IX11.153 @% The %@AB@%PURGE%@AE@% directive can be used to clear memory if a macro or group of%@EH@% macros is needed only for part of a source file.%@NL@% %@NL@% %@4@%%@CR:IX11.154 @% It is not necessary to purge a macro before redefining it. Any%@EH@% redefinition of a macro automatically purges the previous definition. Also, a macro can purge itself as long as the %@AB@%PURGE%@AE@% directive is on the last line of the macro.%@NL@% %@NL@% %@4@% The %@AB@%PURGE%@AE@% directive works by redefining the macro to a null string.%@EH@% Therefore, calling a purged macro does not cause an error. The macro name is simply ignored.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% GetStuff%@AE@%%@NL@% %@AS@% PURGE GetStuff%@AE@%%@NL@% %@NL@% %@4@% These examples call a macro and then purge it. You might need to purge%@EH@% macros in this way if your system does not have enough memory to keep all of the macros needed for a source file in memory at the same time.%@NL@% %@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH12 @%%@AB@%Chapter 12: Controlling Assembly Output%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% QuickAssembler has two ways of communicating results. It can write%@EH@% information to a listing or object file, or it can display messages to the standard output device (normally the screen).%@NL@% %@NL@% %@4@% Both kinds of output can be controlled by menu commands and by source-file%@EH@% statements. This chapter explains the directives that directly control output through source-file statements.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC12.1 @%%@AB@%12.1 Sending Messages to the Standard Output Device%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.1 @%%@CR:IX12.2 @%%@CR:IX12.3 @%%@CR:IX12.4 @% The %@AB@%%OUT%@AE@% directive instructs the assembler to display text to the program%@EH@% output window.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%%OUT%@AE@% %@AI@%text%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%text%@AE@% can be any line of ASCII characters. If you want to display%@EH@% multiple lines, you must use a separate %@AB@%%OUT%@AE@% directive for each line.%@NL@% %@NL@% %@4@% The directive is useful for displaying messages at specific points of a%@EH@% long assembly. It can be used inside conditional-assembly blocks to display messages when certain conditions are met.%@NL@% %@NL@% %@4@%%@CR:IX12.5 @%%@CR:IX12.6 @%%@CR:IX12.7 @%%@CR:IX12.8 @% The %@AB@%%OUT%@AE@% directive generates output for both assembly passes. The %@AB@%IF1%@AE@% and%@EH@% %@AB@%IF2%@AE@% directives can be used for control when the directive is processed. Macros that enable you to output the value of expressions are shown in Section 11.6.3%@BO: 9f74e@%, "Nesting Macro Calls."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% IF1%@AE@%%@NL@% %@AS@% %OUT First Pass - OK%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% This sample block could be placed at the end of a source file so that the%@EH@% message %@AS@%First Pass - OK %@AE@%would be displayed at the end of the first pass, but ignored on the second pass.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC12.2 @%%@AB@%12.2 Controlling Page Format in Listings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.9 @%%@CR:IX12.10 @%%@CR:IX12.11 @% QuickAssembler provides several directives for controlling the page format%@EH@% of listings. These directives include the following:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Action%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%TITLE%@AE@% Sets title for listings%@NL@% %@NL@% %@AB@%SUBTTL%@AE@% Sets title for sections in listings%@NL@% %@NL@% %@AB@%PAGE%@AE@% Sets page length and width, and controls page and section breaks%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.2.1 @%%@AB@%12.2.1 Setting the Listing Title%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.12 @%%@CR:IX12.13 @%%@CR:IX12.14 @% The %@AB@%TITLE%@AE@% directive specifies a title to be used on each page of assembly%@EH@% listings. In editor-based listing files (the default inside the QC environment), the title is only printed once, at the top of the file.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%TITLE %@AE@%%@AI@%text%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%text%@AE@% can be any combination of characters up to 60 in length. The%@EH@% title is printed flush left on the second line of each page of the listing.%@NL@% %@NL@% %@4@% If no %@AB@%TITLE%@AE@% directive is given, the title will be blank. No more than one%@EH@% %@AB@%TITLE%@AE@% directive per module is allowed.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% TITLE Graphics Routines%@AE@%%@NL@% %@NL@% %@4@% This example sets the listing title. A page heading that reflects this%@EH@% title is shown below:%@NL@% %@NL@% %@AS@%Microsoft (R) QuickC with QuickAssembler Version 2.01 9/25/89 12:00:00%@AE@%%@NL@% %@AS@%Graphics Routines%@AE@%%@NL@% %@AS@%Page 1-2%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.2.2 @%%@AB@%12.2.2 Setting the Listing Subtitle%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.15 @%%@CR:IX12.16 @%%@CR:IX12.17 @%%@CR:IX12.18 @%%@CR:IX12.19 @%%@CR:IX12.20 @% The %@AB@%SUBTTL%@AE@% directive specifies the subtitle used on each page of assembly%@EH@% listings. In editor-based listing files (the default inside the QC environment), the subtitle is ignored.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%SUBTTL%@AE@% %@AI@%text%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%text%@AE@% can be any combination of characters up to 60 in length. The%@EH@% subtitle is printed flush left on the third line of the listing pages.%@NL@% %@NL@% %@4@% If no %@AB@%SUBTTL%@AE@% directive is used, or if no text is given for a %@AB@%SUBTTL%@AE@%%@EH@% directive, the subtitle line is left blank.%@NL@% %@NL@% %@4@% Any number of %@AB@%SUBTTL%@AE@% directives can be given in a program. Each new%@EH@% directive replaces the current subtitle with the new text. %@AB@%SUBTTL%@AE@% directives are often used just before a %@AB@%PAGE%@AE@% + statement, which creates a new section (see Section 12.2.3%@BO: a39b9@%, "Controlling Page Breaks").%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% SUBTTL Point Plotting Procedure%@AE@%%@NL@% %@AS@% PAGE +%@AE@%%@NL@% %@NL@% %@4@% The example above creates a section title and then creates a page break%@EH@% and a new section. A page heading that reflects this title is shown below:%@NL@% %@NL@% %@AS@%Microsoft (R) QuickC with QuickAssembler Version 2.01 9/25/89 12:00:00%@AE@%%@NL@% %@AS@%Graphics Routines%@AE@%%@NL@% %@AS@%Page 3-1%@AE@%%@NL@% %@AS@%Point Plotting Procedure%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.2.3 @%%@AB@%12.2.3 Controlling Page Breaks%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.21 @%%@CR:IX12.22 @%%@CR:IX12.23 @%%@CR:IX12.24 @%%@CR:IX12.25 @%%@CR:IX12.26 @% The %@AB@%PAGE%@AE@% directive can be used to designate the line length and width for%@EH@% the program listing, to increment the section and adjust the section number accordingly, or to generate a page break in the listing. In editor-based listing files (the default inside the QC environment), page-break directives are ignored, except for the page width specifier.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%PAGE%@AE@% [[[[%@AI@%length%@AE@%]]%@AB@%,%@AE@%%@AI@%width%@AE@%]]%@EH@%%@NL@% %@AB@%PAGE%@AE@% %@AB@%+%@AE@%%@NL@% %@NL@% %@4@% If %@AI@%length%@AE@% and %@AI@%width%@AE@% are specified, the %@AB@%PAGE%@AE@% directive sets the maximum%@EH@% number of lines per page to %@AI@%length%@AE@% and the maximum number of characters per line to %@AI@%width%@AE@%. The length must be in the range of 10-255 lines. The default page length is 50 lines. The width must be in the range of 60-132 characters. The default page width is 80 characters. With editor-based listing files, the default page width is 255. To specify the width without changing the default length, use a comma before %@AI@%width%@AE@%.%@NL@% %@NL@% %@4@% If no argument is given, %@AB@%PAGE%@AE@% starts a new page in the program listing by%@EH@% copying a form-feed character to the file and generating new title and subtitle lines.%@NL@% %@NL@% %@4@%%@CR:IX12.27 @% If a plus sign follows %@AB@%PAGE%@AE@%, a page break occurs, the section number is%@EH@% incremented, and the page number is reset to 1. Program-listing page numbers have the following format:%@NL@% %@NL@% %@4@% %@AI@%section%@AE@%-%@AI@%page%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%section%@AE@% is the section number within the module, and %@AI@%page%@AE@% is the page%@EH@% number within the section. By default, section and page numbers begin with 1-1. The %@AB@%SUBTTL%@AE@% directive and the %@AB@%PAGE%@AE@% directive can be used together to start a new section with a new subtitle. See Section 12.2.2%@BO: a32b6@%, "Setting the Listing Subtitle," for an example.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% PAGE%@AE@%%@NL@% %@NL@% %@4@% Example 1 creates a page break.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% PAGE 58,90%@AE@%%@NL@% %@NL@% %@4@% Example 2 sets the maximum page length to 58 lines and the maximum width%@EH@% to 90 characters.%@NL@% %@NL@% %@4@% %@AB@%Example 3%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% PAGE ,132%@AE@%%@NL@% %@NL@% %@4@% Example 3 sets the maximum width to 132 characters. The current page%@EH@% length (either the default of 50 or a previously set value) remains unchanged.%@NL@% %@NL@% %@4@% %@AB@%Example 4%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% PAGE +%@AE@%%@NL@% %@NL@% %@4@% Example 4 creates a page break, increments the current section number, and%@EH@% sets the page number to 1. For example, if the preceding page was 3-6, the new page would be 4-1.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.2.4 @%%@AB@%12.2.4 Naming the Module%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The assembler automatically uses the base file name of the source file as%@EH@% the name of the module. This name is used to identify error messages when you run the assembler from the command line.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC12.3 @%%@AB@%12.3 Controlling the Contents of Listings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.28 @% QuickAssembler provides several directives for controlling what text will%@EH@% be shown in listings. The directives that control the contents of listings are shown below:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Action%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%.LIST%@AE@% Lists statements in program listing%@NL@% %@NL@% %@AB@%.XLIST%@AE@% Suppresses listing of statements%@NL@% %@NL@% %@AB@%.LFCOND%@AE@% Lists false-conditional blocks in program listing%@NL@% %@NL@% %@AB@%.SFCOND%@AE@% Suppresses false-conditional listing%@NL@% %@NL@% %@AB@%.TFCOND%@AE@% Toggles false-conditional listing%@NL@% %@NL@% %@AB@%.LALL%@AE@% Includes macro expansions in program listing%@NL@% %@NL@% %@AB@%.SALL%@AE@% Suppresses listing of macro expansions%@NL@% %@NL@% %@AB@%.XALL%@AE@% Excludes comments from macro listing%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.3.1 @%%@AB@%12.3.1 Suppressing and Restoring Listing Output%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.29 @%%@CR:IX12.30 @%%@CR:IX12.31 @%%@CR:IX12.32 @%%@CR:IX12.33 @%%@CR:IX12.34 @%%@CR:IX12.35 @% The %@AB@%.LIST%@AE@% and %@AB@%.XLIST%@AE@% directives specify which source lines are included in%@EH@% the program listing.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.LIST%@AE@%%@EH@%%@NL@% %@AB@%.XLIST%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%.XLIST%@AE@% directive suppresses copying of subsequent source lines to the%@EH@% program listing. The %@AB@%.LIST%@AE@% directive restores copying. The directives are typically used in pairs to prevent a particular section of a source file from being copied to the program listing.%@NL@% %@NL@% %@4@% The %@AB@%.XLIST%@AE@% directive overrides other listing directives, such as %@AB@%.SFCOND%@AE@%%@EH@% or %@AB@%.LALL%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .XLIST ; Listing suspended here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .LIST ; Listing resumes here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.3.2 @%%@AB@%12.3.2 Controlling Listing of Conditional Blocks%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.36 @%%@CR:IX12.37 @%%@CR:IX12.38 @%%@CR:IX12.39 @%%@CR:IX12.40 @%%@CR:IX12.41 @%%@CR:IX12.42 @%%@CR:IX12.43 @%%@CR:IX12.44 @% The %@AB@%.SFCOND%@AE@%, %@AB@%.LFCOND%@AE@%, and %@AB@%.TFCOND%@AE@% directives control whether%@EH@% false-conditional blocks should be included in assembly listings.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.SFCOND%@AE@%%@EH@%%@NL@% %@AB@%.LFCOND%@AE@%%@NL@% %@AB@%.TFCOND%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%.SFCOND%@AE@% directive suppresses the listing of any subsequent conditional%@EH@% blocks whose condition is false. The %@AB@%.LFCOND%@AE@% directive restores the listing of these blocks. Like %@AB@%.LIST%@AE@% and %@AB@%.XLIST%@AE@%, conditional-listing directives can be used to suppress listing of conditional blocks in sections of a program.%@NL@% %@NL@% %@4@%%@CR:IX12.45 @%%@CR:IX12.46 @% The %@AB@%.TFCOND%@AE@% directive toggles the current status of listing of conditional%@EH@% blocks. This directive can be used in conjunction with the /Sx option of the assembler. By default, conditional blocks are not listed on start-up. However, they will be listed on start-up if the /Sx option is given. This means that using /Sx reverses the meaning of the first %@AB@%.TFCOND%@AE@% directive in the source file. The /Sx option is discussed in Appendix B%@BO: f653b@%, Section B.14%@BO: fbf3d@%, "Listing False Conditionals."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%test1 EQU 0 ; Defined to make all conditionals false%@AE@%%@NL@% %@NL@% %@AS@% ; /Sx not used /Sx used%@AE@%%@NL@% %@AS@% .TFCOND%@AE@%%@NL@% %@AS@% IFNDEF test1 ; Listed Not listed%@AE@%%@NL@% %@AS@%test2 DB 128%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% .TFCOND%@AE@%%@NL@% %@AS@% IFNDEF test1 ; Not listed Listed%@AE@%%@NL@% %@AS@%test3 DB 128%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% .SFCOND%@AE@%%@NL@% %@AS@% IFNDEF test1 ; Not listed Not listed%@AE@%%@NL@% %@NL@% %@AS@%test4 DB 128%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% .LFCOND%@AE@%%@NL@% %@AS@% IFNDEF test1 ; Listed Listed%@AE@%%@NL@% %@AS@%test5 DB 128%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% In the example above, the listing status for the first two conditional%@EH@% blocks would be different, depending on whether the /X option was used. The blocks with %@AB@%.SFCOND%@AE@% and %@AB@%.LFCOND%@AE@% would not be affected by the /X option.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC12.3.3 @%%@AB@%12.3.3 Controlling Listing of Macros%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX12.47 @%%@CR:IX12.48 @%%@CR:IX12.49 @%%@CR:IX12.50 @%%@CR:IX12.51 @%%@CR:IX12.52 @%%@CR:IX12.53 @%%@CR:IX12.54 @%%@CR:IX12.55 @% The %@AB@%.LALL%@AE@%, %@AB@%.XALL%@AE@%, and %@AB@%.SALL%@AE@% directives control the listing of the expanded%@EH@% macro calls. The assembler always lists the full macro definition. The directives only affect expansion of macro calls.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%.LALL%@AE@%%@EH@%%@NL@% %@AB@%.XALL%@AE@%%@NL@% %@AB@%.SALL%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%.LALL%@AE@% directive causes QuickAssembler to list all the source%@EH@% statements in a macro expansion, including normal comments (preceded by a single semicolon) but not macro comments (preceded by a double semicolon).%@NL@% %@NL@% %@4@% The %@AB@%.XALL%@AE@% directive causes QuickAssembler to list only those source%@EH@% statements in a macro expansion that generate code or data. For instance, comments, equates, and segment definitions are ignored.%@NL@% %@NL@% %@4@% The %@AB@%.SALL%@AE@% directive causes QuickAssembler to suppress listing of all macro%@EH@% expansions. The listing shows the macro call, but not the source lines generated by the call.%@NL@% %@NL@% %@4@% The %@AB@%.XALL%@AE@% directive is in effect when QuickAssembler first begins%@EH@% execution.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%tryout MACRO param%@AE@%%@NL@% %@AS@% ;; Macro comment%@AE@%%@NL@% %@AS@% ; Normal comment%@AE@%%@NL@% %@AS@%it EQU 3 ; No code or data%@AE@%%@NL@% %@AS@% ASSUME es:_DATA ; No code or data%@AE@%%@NL@% %@AS@% DW param ; Generates data%@AE@%%@NL@% %@AS@% mov ax,it ; Generates code%@AE@%%@NL@% %@AS@% ENDM%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .LALL%@AE@%%@NL@% %@AS@% tryout 6 ; Call with .LALL%@AE@%%@NL@% %@NL@% %@AS@% .XALL%@AE@%%@NL@% %@AS@% tryout 6 ; Call with .XALL%@AE@%%@NL@% %@NL@% %@AS@% .SALL%@AE@%%@NL@% %@AS@% tryout 6 ; Call with .SALL%@AE@%%@NL@% %@NL@% %@4@% The macro calls in the example generate the following listing lines:%@EH@%%@NL@% %@NL@% %@AS@% .LALL%@AE@%%@NL@% %@AS@% tryout 6 ; Call with .LALL%@AE@%%@NL@% %@AS@% 1 ; Normal comment%@AE@%%@NL@% %@AS@%= 0003 1 it EQU 3 ; No code or data%@AE@%%@NL@% %@AS@% 1 ASSUME es:_TEXT ; No code or data%@AE@%%@NL@% %@AS@%0015 0006 1 DW 6 ; Generates data%@AE@%%@NL@% %@AS@%0017 B8 0003 1 mov ax,it ; Generates code%@AE@%%@NL@% %@NL@% %@AS@%%@AE@%%@NL@% %@AS@% .XALL%@AE@%%@NL@% %@AS@% tryout 6 ; Call with .XALL%@AE@%%@NL@% %@AS@%001A 0006 1 DW 6 ; Generates data%@AE@%%@NL@% %@AS@%001C B8 0003 1 mov ax,it ; Generates code%@AE@%%@NL@% %@NL@% %@AS@%%@AE@%%@NL@% %@AS@% .SALL%@AE@%%@NL@% %@AS@% tryout 6 ; Call with .SALL%@AE@%%@NL@% %@NL@% %@4@% Notice that the macro comment is never listed in macro expansions. Normal%@EH@% comments are listed only with the %@AB@%.LALL%@AE@% directive.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:PT3 @%%@AB@%PART 3: Using Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% Part 3%@BO: a6c61@% of the Programmer's Guide (comprising Chapters 13%@BO: a71ac@%-18) explains%@EH@% how to use instructions in assembly-language source code. Instructions constitute the actual steps of your program and are translated into machine-code statements that the processor executes at run time.%@NL@% %@NL@% %@4@% Part 3%@BO: a6c61@% is organized topically, with related instructions discussed%@EH@% together. Chapter 13%@BO: a71ac@% explains the instructions for moving data from one location to another. The instructions for performing calculations on numbers and bits are covered in Chapter 14%@BO: ae658@%.%@NL@% %@NL@% %@4@% The 8086-family processors provide four major types of instructions for%@EH@% controlling program flow, as described in Chapter 15%@BO: bcdd3@%. Chapter 16%@BO: d1833@% explains the instructions and techniques for processing strings. The 8087-family coprocessors and their instructions are explained in Chapter 17%@BO: d965a@%. Finally, Chapter 18%@BO: eab36@% summarizes the instructions available for processor control.%@NL@% %@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH13 @%%@AB@%Chapter 13: Loading, Storing, and Moving Data%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% The 8086-family processors provide several instructions for loading,%@EH@% storing, or moving various kinds of data. Among the types of transferable data are variables, pointers, and flags. Data can be moved to and from registers, memory, ports, and the stack. This chapter explains the instructions for moving data from one location to another.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC13.1 @%%@AB@%13.1 Transferring Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Moving data is one of the most common tasks in assembly-language%@EH@% programming. Data can be moved between registers or between memory and registers. Immediate data can be loaded into registers or into memory.%@NL@% %@NL@% %@4@% Furthermore, all memory-to-memory operations are illegal. To move data%@EH@% from one memory location to another, you must first move the data to an intermediate register.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.1.1 @%%@AB@%13.1.1 Copying Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.1 @%%@CR:IX13.2 @%%@CR:IX13.3 @% The %@AB@%MOV%@AE@% instruction is the most common method of moving data. This%@EH@% instruction can be thought of as a copy instruction, since it always copies the source operand to the destination operand. Immediately after a %@AB@%MOV%@AE@% instruction, the source and destination operands both contain the same value. The old value in the destination operand is destroyed.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%MOV%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,7 ; Immediate to register%@AE@%%@NL@% %@AS@% mov mem,7 ; Immediate to memory direct%@AE@%%@NL@% %@AS@% mov mem[bx],7 ; Immediate to memory indirect%@AE@%%@NL@% %@NL@% %@AS@% mov mem,ds ; Segment register to memory%@AE@%%@NL@% %@AS@% mov mem,ax ; Register to memory direct%@AE@%%@NL@% %@AS@% mov mem[bx],ax ; Register to memory indirect%@AE@%%@NL@% %@NL@% %@AS@% mov ax,mem ; Memory direct to register%@AE@%%@NL@% %@AS@% mov ax,mem[bx] ; Memory indirect to register%@AE@%%@NL@% %@AS@% mov ds,mem ; Memory to segment register%@AE@%%@NL@% %@NL@% %@AS@% mov ax,bx ; Register to register%@AE@%%@NL@% %@AS@% mov ds,ax ; General register to segment register%@AE@%%@NL@% %@AS@% mov ax,ds ; Segment register to general register%@AE@%%@NL@% %@NL@% %@4@% The statements in Example 1 illustrate each type of memory move that can%@EH@% be done with a single instruction. Example 2 illustrates several common types of moves that require two instructions.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Move immediate to segment register%@AE@%%@NL@% %@AS@% mov ax,DGROUP ; Load immediate to general register%@AE@%%@NL@% %@AS@% mov ds,ax ; Store general register to segment register%@AE@%%@NL@% %@NL@% %@AS@%; Move memory to memory%@AE@%%@NL@% %@AS@% mov ax,mem1 ; Load memory to general register%@AE@%%@NL@% %@AS@% mov mem2,ax ; Store general register to memory%@AE@%%@NL@% %@NL@% %@AS@%; Move segment register to segment register%@AE@%%@NL@% %@AS@% mov ax,ds ; Load segment register to general register%@AE@%%@NL@% %@AS@% mov es,ax ; Store general register to segment register%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.1.2 @%%@AB@%13.1.2 Exchanging Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.4 @%%@CR:IX13.5 @% The %@AB@%XCHG%@AE@% (Exchange) instruction exchanges the data in the source and%@EH@% destination operands. Data can be exchanged between registers or between registers and memory.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%XCHG%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% xchg ax,bx ; Put AX in BX and BX in AX%@AE@%%@NL@% %@AS@% xchg memory,ax ; Put "memory" in AX and AX in "memory"%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.1.3 @%%@AB@%13.1.3 Looking Up Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.6 @%%@CR:IX13.7 @% The %@AB@%XLAT%@AE@% (Translate) instruction is used to load data from a table in%@EH@% memory. The instruction is useful for translating bytes from one coding system to another.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%XLAT%@AE@%[[%@AB@%B%@AE@%]] [[[[%@AI@%segment%@AE@%%@AB@%:%@AE@%]]%@AI@%memory%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.8 @%%@CR:IX13.9 @% The BX register must contain the address of the start of the table. By%@EH@% default, the DS register contains the segment of the table, but a segment override can be used to specify a different segment. The operand need not be given except when specifying a segment override.%@NL@% %@NL@% %@4@% Before the %@AB@%XLAT%@AE@% instruction is called, the AL register should contain a%@EH@% value that points into the table (the start of the table is considered 0). After the instruction is called, AL will contain the table value pointed to. For example, if AL contains 7, the 8th byte of the table will be placed in the AL register.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% For compatibility with Intel 80386 mnemonics, QuickAssembler recognizes %@AB@%XLATB %@AE@%as a synonym for %@AB@%XLAT%@AE@%. In the Intel syntax,%@AB@% XLAT %@AE@%requires an operand; %@AB@%XLATB %@AE@%does not allow one. Quick-Assembler never requires an operand, but always allows one.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% ; Table of Hexadecimal digits%@AE@%%@NL@% %@AS@%hex DB "0123456789ABCDEF"%@AE@%%@NL@% %@AS@%convert DB "You pressed the key with ASCII code "%@AE@%%@NL@% %@AS@%key DB ?,?,"h",13,10,"$"%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ah,8 ; Get a key in AL%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@AS@% mov bx,OFFSET hex ; Load table address%@AE@%%@NL@% %@AS@% mov ah,al ; Save a copy in high byte%@AE@%%@NL@% %@AS@% and al,00001111b ; Mask out top character%@AE@%%@NL@% %@AS@% xlat ; Translate%@AE@%%@NL@% %@AS@% mov key[1],al ; Store the character%@AE@%%@NL@% %@AS@% mov cl,12 ; Load shift count%@AE@%%@NL@% %@AS@% shr ax,cl ; Shift high character into position%@AE@%%@NL@% %@AS@% xlat ; Translate%@AE@%%@NL@% %@AS@% mov key,al ; Store the character%@AE@%%@NL@% %@AS@% mov dx,OFFSET convert ; Load message%@AE@%%@NL@% %@AS@% mov ah,9 ; Display it%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@NL@% %@4@% This example looks up hexadecimal characters in a table in order to%@EH@% convert an eight-bit binary number to a string representing a hexadecimal number.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.1.4 @%%@AB@%13.1.4 Transferring Flags%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.10 @%%@CR:IX13.11 @%%@CR:IX13.12 @%%@CR:IX13.13 @% The 8086-family processors provide instructions for loading and storing%@EH@% flags in the AH%@AB@% %@AE@%register.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LAHF%@AE@%%@EH@%%@NL@% %@AB@%SAHF%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX13.14 @% The status of the lower byte of the flags register can be saved to the AH%@EH@% register with %@AB@%LAHF%@AE@% and then later restored with %@AB@%SAHF%@AE@%. If you need to save and restore the entire flags register, use %@AB@%PUSHF%@AE@% and %@AB@%POPF%@AE@%, as described in Section 13.4.3%@BO: acdc0@%, "Saving Flags on the Stack."%@NL@% %@NL@% %@4@% %@AB@%SAHF%@AE@% is often used with a coprocessor to transfer coprocessor control%@EH@% flags to processor control flags. Section 17.7%@BO: e64cc@%, "Controlling Program Flow," explains and illustrates this technique.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC13.2 @%%@AB@%13.2 Converting between Data Sizes%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.15 @%%@CR:IX13.16 @%%@CR:IX13.17 @%%@CR:IX13.18 @%%@CR:IX13.19 @%%@CR:IX13.20 @% Since moving data between registers of different sizes is illegal, you%@EH@% must take special steps if you need to extend a register value to a larger register or register pair.%@NL@% %@NL@% %@4@% The procedure is different for signed and unsigned values. The processor%@EH@% cannot tell the difference between signed and unsigned numbers; the programmer has to understand this difference and program accordingly.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.2.1 @%%@AB@%13.2.1 Extending Signed Values%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.21 @%%@CR:IX13.22 @%%@CR:IX13.23 @%%@CR:IX13.24 @% The %@AB@%CBW%@AE@% (Convert Byte to Word) and %@AB@%CWD%@AE@% (Convert Word to Doubleword)%@EH@% instructions are provided to sign-extend values. Sign-extending means copying the sign bit of the unextended operand to all bits of the extended operand.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%CBW%@AE@%%@EH@%%@NL@% %@AB@%CWD%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX13.25 @%%@CR:IX13.26 @% The %@AB@%CBW%@AE@% instruction converts an 8-bit signed value in AL to a 16-bit%@EH@% signed value in AX. The %@AB@%CWD%@AE@% instruction is similar except that it sign-extends a 16-bit value in AX to a 32-bit value in the DX:AX register pair. Both instructions work only on values in the accumulator register.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem8 DB -5%@AE@%%@NL@% %@AS@%mem16 DW -5%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov al,mem8 ; Load 8-bit -5 (FBh)%@AE@%%@NL@% %@AS@% cbw ; Convert to 16-bit -5 (FFFBh) in AX%@AE@%%@NL@% %@NL@% %@AS@% mov ax,mem16 ; Load 16-bit -5 (FFFBh)%@AE@%%@NL@% %@AS@% cwd ; Convert to 32-bit -5 (FFFF:FFFBh)%@AE@%%@NL@% %@AS@% ; in DX:AX%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.2.2 @%%@AB@%13.2.2 Extending Unsigned Values%@AE@%%@EH@%%@NL@% %@NL@% %@4@% To extend unsigned numbers, set the value of the upper register to 0.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem8 DB 251%@AE@%%@NL@% %@AS@%mem16 DW 251%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov al,mem8 ; Load 251 (FBh) from 8-bit memory%@AE@%%@NL@% %@AS@% xor ah,ah ; Zero upper half (AH)%@AE@%%@NL@% %@NL@% %@AS@% mov ax,mem16 ; Load 251 (FBh) from 16-bit memory%@AE@%%@NL@% %@AS@% xor dx,dx ; Zero upper half (DX)%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC13.3 @%%@AB@%13.3 Loading Pointers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.27 @% The 8086-family processors provide several instructions for loading%@EH@% pointer values into registers or register pairs. They can be used to load either near or far pointers.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.3.1 @%%@AB@%13.3.1 Loading Near Pointers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.28 @% The %@AB@%LEA%@AE@% instruction loads a near pointer into a specified register.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LEA%@AE@% %@AI@%register%@AE@%%@AB@%,%@AE@%%@AI@%memory%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.29 @%%@CR:IX13.30 @% %@AI@%The%@AE@% %@AI@%destination%@AE@% %@AI@%register%@AE@% may be any general-purpose register. The source%@EH@% operand may be any %@AI@%memory%@AE@% operand. The effective address of the source operand is placed in the destination register.%@NL@% %@NL@% %@4@% The %@AB@%LEA%@AE@% instruction can be used to calculate the effective address of a%@EH@% direct memory operand, but this is usually not efficient, since the address of a direct memory operand is a constant known at assembly time. For example, the following statements have the same effect, but the second version is faster:%@NL@% %@NL@% %@AS@% lea dx,string ; Load effective address - slow%@AE@%%@NL@% %@AS@% mov dx,OFFSET string ; Load offset - fast%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%LEA%@AE@% instruction is more useful for calculating the address of indirect%@EH@% memory operands:%@NL@% %@NL@% %@AS@% lea dx,string[si] ; Load effective address%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.3.2 @%%@AB@%13.3.2 Loading Far Pointers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.31 @%%@CR:IX13.32 @%%@CR:IX13.33 @%%@CR:IX13.34 @%%@CR:IX13.35 @% The %@AB@%LDS%@AE@% and %@AB@%LES%@AE@% instructions load far pointers.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LDS%@AE@% %@AI@%register%@AE@%%@AB@%,%@AE@%%@AI@%memory%@AE@%%@EH@%%@NL@% %@AB@%LES%@AE@% %@AI@%register%@AE@%%@AB@%,%@AE@%%@AI@%memory%@AE@%%@NL@% %@NL@% %@4@% The %@AI@%memory%@AE@% address being pointed to is specified in the source operand,%@EH@% and the %@AI@%register%@AE@% where the offset will be stored is specified in the destination operand.%@NL@% %@NL@% %@4@% The address must be stored in memory with the segment in the upper word%@EH@% and the offset in the lower word. The segment register where the segment will be stored is specified in the instruction name. For example, %@AB@%LDS%@AE@% puts the segment in DS, and %@AB@%LES%@AE@% puts the segment in ES. The instructions are often used with string instructions, as explained in Chapter 16%@BO: d1833@%, "Processing Strings."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%string DB "This is a string."%@AE@%%@NL@% %@AS@%fpstring DD string ; Far pointer to string%@AE@%%@NL@% %@AS@%pointers DD 100 DUP (?)%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% les di,fpstring ; Put address in ES:DI pair%@AE@%%@NL@% %@AS@% lds si,pointers[bx] ; Put address in DS:SI pair%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC13.4 @%%@AB@%13.4 Transferring Data to and from the Stack%@AE@%%@EH@%%@NL@% %@CR:IX13.36 @%%@CR:IX13.37 @%%@CR:IX13.38 @%%@CR:IX13.39 @%%@CR:IX13.40 @%%@CR:IX13.41 @%%@CR:IX13.42 @%%@CR:IX13.43 @%%@CR:IX13.44 @%%@CR:IX13.45 @%%@NL@% %@4@%%@CR:IX13.46 @%%@CR:IX13.47 @%%@CR:IX13.48 @%%@CR:IX13.49 @% A "stack" is an area of memory for storing temporary data. Unlike other%@EH@% segments in which data is stored starting from low memory, data on the stack is stored in reverse order starting from high memory.%@NL@% %@NL@% %@4@% Initially, the stack is an uninitialized segment of a finite size. As data%@EH@% is added to the stack at run time, the stack grows downward from high memory to low memory. When items are removed from the stack, it shrinks upward from low memory to high memory.%@NL@% %@NL@% %@4@% The stack has several purposes in the 8086-family processors. The %@AB@%CALL%@AE@%,%@EH@% %@AB@%INT%@AE@%, %@AB@%RET%@AE@%, and %@AB@%IRET%@AE@% instructions automatically use the stack to store the calling addresses of procedures and interrupts (see Sections 15.3%@BO: c3a3e@%, "Using Procedures," and 15.4%@BO: c3a3e@%, "Using Interrupts"). You can also use the %@AB@%PUSH%@AE@% and %@AB@%POP%@AE@% instructions and their variations to store values on the stack.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.4.1 @%%@AB@%13.4.1 Pushing and Popping%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.50 @%%@CR:IX13.51 @%%@CR:IX13.52 @%%@CR:IX13.53 @% In 8086-family processors, the SP (Stack Pointer) register always points%@EH@% to the current location in the stack. The %@AB@%PUSH%@AE@% and %@AB@%POP%@AE@% instructions use the SP register to keep track of the current position in the stack.%@NL@% %@NL@% %@4@% The values pointed to by the BP and SP registers are relative to the SS%@EH@% (Stack Segment) register. The BP register is often used to point to the base of a frame of reference (a stack frame) within the stack.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%PUSH %@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@AB@%POP%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@NL@% %@AB@%PUSH%@AE@% %@AI@%immediate%@AE@% (80186-80386 only)%@NL@% %@NL@% %@4@% The %@AB@%PUSH%@AE@% instruction is used to store a two-byte operand on the stack. The%@EH@% %@AB@%POP%@AE@% instruction is used to retrieve a previously pushed value. When a value is pushed onto the stack, the SP register is decreased by 2. When a value is popped off the stack, the SP register is increased by 2. Although the stack always contains word values, the SP register points to bytes. Thus, SP changes in multiples of 2.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% The 8088 and 8086 processors differ from later Intel processors in how they push and pop the SP register. If you give the statement %@AS@%push sp%@AE@% with the 8088 or 8086, the word %@AS@%pushed%@AE@% will be the word in SP after the push operation.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX13.54 @% Figure 13.1 illustrates how pushes and pops change the SP register.%@EH@%%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 13.4.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The %@AB@%PUSH%@AE@% and %@AB@%POP%@AE@% instructions are almost always used in pairs. Words are%@EH@% popped off the stack in reverse order from the order in which they are pushed onto the stack. You should normally do the same number of pops as pushes to return the stack to its original status. However, it is possible to return the stack to its original status by subtracting the correct number of words from the SP register.%@NL@% %@NL@% %@4@% Values on the stack can be accessed by using indirect memory operands with%@EH@% BP as the base register.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov bp,sp ; Set stack frame%@AE@%%@NL@% %@AS@% push ax ; Push first; SP = BP + 2%@AE@%%@NL@% %@AS@% push bx ; Push second; SP = BP + 4%@AE@%%@NL@% %@AS@% push cx ; Push third; SP = BP + 6%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,[bp+6] ; Put third in AX%@AE@%%@NL@% %@AS@% mov bx,[bp+4] ; Put second in BX%@AE@%%@NL@% %@AS@% mov cx,[bp+2] ; Put first in CX%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% sub sp,6 ; Restore stack pointer%@AE@%%@NL@% %@AS@% ; two bytes per push%@AE@%%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Starting with the 80186 processor, the %@AB@%PUSH%@AE@% instruction can be given with%@EH@% an immediate operand. For example, the following statement is legal on the 80186, 80286, and 80386 processors:%@NL@% %@NL@% %@AS@% push 7 ; 3 clocks on 80286%@AE@%%@NL@% %@NL@% %@4@% This statement is faster than the following equivalent statements, which%@EH@% are required on the 8088 or 8086:%@NL@% %@NL@% %@AS@% mov ax,7 ; 2 clocks on 80286%@AE@%%@NL@% %@AS@% push ax ; 3 clocks on 80286%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.4.2 @%%@AB@%13.4.2 Using the Stack%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.55 @% The stack can be used to store temporary data. For example, in the%@EH@% Microsoft calling convention, the stack is used to pass arguments to a procedure. The arguments are pushed onto the stack before the call. The procedure retrieves and uses them. Then the stack is restored to its original position at the end of the procedure. The stack can also be used to store variables that are local to a procedure. Both of these techniques are discussed in Section 15.3%@BO: c3a3e@%, "Using Procedures."%@NL@% %@NL@% %@4@% Another common use of the stack is to store temporary data when there are%@EH@% no free registers available or when a particular register must hold more than one value. For example, the CX register usually holds the count for loops. If two loops are nested, the outer count is loaded into CX at the start. When the inner loop starts, the outer count is pushed onto the stack and the inner count loaded into CX. When the inner loop finishes, the original count is popped back into CX.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov cx,10 ; Load outer loop counter%@AE@%%@NL@% %@AS@%outer: .%@AE@%%@NL@% %@AS@% . ; Start outer loop task%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% push cx ; Save outer loop value%@AE@%%@NL@% %@AS@% mov cx,20 ; Load inner loop counter%@AE@%%@NL@% %@AS@%inner: .%@AE@%%@NL@% %@AS@% . ; Do inner loop task%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop inner%@AE@%%@NL@% %@AS@% pop cx ; Restore outer loop counter%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Continue outer loop task%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop outer%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.4.3 @%%@AB@%13.4.3 Saving Flags on the Stack%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.56 @%%@CR:IX13.57 @%%@CR:IX13.58 @%%@CR:IX13.59 @% Flags can be pushed and popped onto the stack using the %@AB@%PUSHF%@AE@% and %@AB@%POPF%@AE@%%@EH@% instructions.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%PUSHF%@AE@%%@EH@%%@NL@% %@AB@%POPF%@AE@%%@NL@% %@NL@% %@4@% These instructions are sometimes used to save the status of flags before a%@EH@% procedure call and then to restore the same status after the procedure. They can also be used within a procedure to save and restore the flag status of the caller.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% pushf%@AE@%%@NL@% %@AS@% call systask%@AE@%%@NL@% %@AS@% popf%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC13.4.4 @%%@AB@%13.4.4 Saving All Registers on the Stack%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.60 @%%@CR:IX13.61 @%%@CR:IX13.62 @%%@CR:IX13.63 @% Starting with the 80186 processor, the %@AB@%PUSHA%@AE@% and %@AB@%POPA%@AE@% instructions were%@EH@% implemented to push or pop all the general-purpose registers with one instruction.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%PUSHA%@AE@%%@EH@%%@NL@% %@AB@%POPA%@AE@%%@NL@% %@NL@% %@4@% %@AB@%These instructions can be used to save the status of all registers before%@AE@%%@EH@% %@AB@%a procedure call and then to restore them after the return. Using%@AE@% %@AB@%PUSHA%@AE@% and %@AB@%POPA%@AE@% instructions is significantly faster and takes fewer bytes of code than pushing and popping each register individually.%@NL@% %@NL@% %@4@% The registers are pushed in the following order: AX,%@AB@% %@AE@%CX,%@AB@% %@AE@%DX,%@AB@% %@AE@%BX,%@AB@% %@AE@%SP, BP,%@EH@% SI, and DI. The SP word pushed is the value before the first register is pushed. The registers are popped in the opposite order.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% pusha%@AE@%%@NL@% %@AS@% call systask%@AE@%%@NL@% %@AS@% popa%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC13.5 @%%@AB@%13.5 Transferring Data to and from Ports%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX13.64 @%%@CR:IX13.65 @%%@CR:IX13.66 @%%@CR:IX13.67 @%%@CR:IX13.68 @% "Ports" are the gateways between hardware devices and the processor. Each%@EH@% port has a unique number through which it can be accessed. Ports can be used for low-level communication with devices, such as disks, the video display, or the keyboard. The %@AB@%OUT%@AE@% instruction is used to send data to a port; the %@AB@%IN%@AE@% instruction receives data from a port.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IN%@AE@% %@AI@%accumulator%@AE@%%@AB@%,%@AE@%{%@AI@%portnumber%@AE@% | %@AB@%DX%@AE@%}%@EH@%%@NL@% %@AB@%OUT%@AE@% {%@AI@%portnumber%@AE@% | %@AB@%DX%@AE@%}%@AB@%,%@AE@%%@AI@%accumulator%@AE@%%@NL@% %@NL@% %@4@% When using the %@AB@%IN%@AE@% and %@AB@%OUT%@AE@% instructions, the number of the port can either%@EH@% be an eight-bit immediate value or the DX register. You must use DX for ports with a number higher than 256. The value to be received from the port must be in the accumulator register (AX for word values or AL for byte values).%@NL@% %@NL@% %@4@% When using the %@AB@%IN%@AE@% instruction, the number of the port is given as the%@EH@% source operand and the value to be sent to the port is the destination operand. When using the %@AB@%OUT%@AE@% instruction, the number of the port is given as the destination operand and the value to be sent to the port is the source operand.%@NL@% %@NL@% %@4@% In applications programming, most communication with hardware is done with%@EH@% DOS or ROM-BIOS calls. Ports are more often used in systems programming. Since systems programming is beyond the scope of this manual and since ports differ depending on hardware, the %@AB@%IN%@AE@% and %@AB@%OUT%@AE@% instructions are not explained in detail here.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Actual values are hardware dependent%@AE@%%@NL@% %@AS@%sound EQU 61h ; Port to chip that controls speaker%@AE@%%@NL@% %@AS@%timer EQU 42h ; Port to chip that pulses speaker%@AE@%%@NL@% %@AS@%on EQU 00000011b ; Bits 0 and 1 turn on speaker%@AE@%%@NL@% %@NL@% %@AS@% in al,sound ; Get current port setting%@AE@%%@NL@% %@AS@% or al,on ; Turn on speaker and connect timer%@AE@%%@NL@% %@AS@% out sound,al ; Put value back in port%@AE@%%@NL@% %@NL@% %@AS@% mov al,50 ; Start at 50%@AE@%%@NL@% %@AS@%sounder: out timer,al ; Send byte to timer port...%@AE@%%@NL@% %@NL@% %@AS@% mov cx,2000 ; Loop 2000 times to delay%@AE@%%@NL@% %@AS@%hold: loop hold%@AE@%%@NL@% %@NL@% %@AS@% dec al ; Go down one step%@AE@%%@NL@% %@AS@% jnz sounder ; Repeat for each step%@AE@%%@NL@% %@NL@% %@AS@% in al,sound ; Get port value%@AE@%%@NL@% %@AS@% and al,NOT on ; Turn it back off%@AE@%%@NL@% %@AS@% out sound,al ; Put it back in port%@AE@%%@NL@% %@NL@% %@4@% This example creates a sound of ascending frequency on the IBM PC and%@EH@% IBM-compatible computers. The technique of making sound or the port values used may be different on other hardware.%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Starting with the 80186 processor, instructions were implemented to send%@EH@% strings of data to and from ports. The instructions are %@AB@%INS%@AE@%,%@AB@% INSB%@AE@%,%@AB@% INSW%@AE@%, %@AB@%OUTS%@AE@%, %@AB@%OUTSB%@AE@%, and %@AB@%OUTSW%@AE@%. The operation of these instructions is much like the operation of other string instructions. They are discussed in Section 16.7%@BO: d8b5c@%, "Transferring Strings to and from Ports."%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH14 @%%@AB@%Chapter 14: Doing Arithmetic and Bit Manipulations%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% The 8086-family processors provide instructions for doing calculations on%@EH@% byte, word, and doubleword values. Operations include addition, subtraction, multiplication, and division. You can also do calculations at the bit level. This includes the AND, OR, XOR, and NOT logical operations. Bits can also be shifted or rotated to the right or left.%@NL@% %@NL@% %@4@% This chapter tells you how to use the instructions that do calculations on%@EH@% numbers and bits.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.1 @%%@AB@%14.1 Adding%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.1 @%%@CR:IX14.2 @%%@CR:IX14.3 @%%@CR:IX14.4 @%%@CR:IX14.5 @%%@CR:IX14.6 @%%@CR:IX14.7 @%%@CR:IX14.8 @% The %@AB@%ADD%@AE@%, %@AB@%ADC%@AE@%, and %@AB@%INC%@AE@% instructions are used for adding and incrementing%@EH@% values.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%ADD %@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@AB@%ADC%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@NL@% %@AB@%INC%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@NL@% %@NL@% %@4@% These instructions can work directly on 8-bit or 16-bit values. They can%@EH@% also be used in combination to do calculations on values that are too large to be held in a single register (such as 32-bit values). When used with %@AB@%AAA%@AE@% and %@AB@%DAA%@AE@%, they can be used to do calculations on binary coded decimal (BCD) numbers, as described in Section 14.5%@BO: b4ede@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.1.1 @%%@AB@%14.1.1 Adding Values Directly%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.9 @%%@CR:IX14.10 @%%@CR:IX14.11 @%%@CR:IX14.12 @% The %@AB@%ADD%@AE@% and %@AB@%INC%@AE@% instructions are used for adding to values in registers or%@EH@% memory.%@NL@% %@NL@% %@4@% The %@AB@%INC%@AE@% instruction takes a single %@AI@%register%@AE@% or %@AI@%memory%@AE@% operand. The value%@EH@% of the operand is incremented. The value is treated as an unsigned integer, so the carry flag is not updated for signed carries.%@NL@% %@NL@% %@4@% The %@AB@%ADD%@AE@% instruction adds values given in source and destination operands.%@EH@% The destination can be either a %@AI@%register%@AE@% or a %@AI@%memory%@AE@% operand. Its contents will be destroyed by the operation. The source operand can be an %@AI@%immediate%@AE@%, %@AI@%memory%@AE@%, or %@AI@%register%@AE@% operand. Since memory-to-memory operations are never allowed, the source and destination operands can never both be memory operands.%@NL@% %@NL@% %@4@% The result of the operation is stored in the source operand. The operands%@EH@% can be either 8-bit or 16-bit, but both must be the same size.%@NL@% %@NL@% %@4@%%@CR:IX14.13 @%%@CR:IX14.14 @% An addition operation can be interpreted as addition of either signed%@EH@% numbers or unsigned numbers. It is the programmer's responsibility to decide how the addition should be interpreted and to take appropriate action if the sum is too large for the destination operand. When an addition overflows the possible range for signed numbers, the overflow flag is set. When an addition overflows the range for unsigned numbers, the carry flag is set.%@NL@% %@NL@% %@4@% There are two ways to take action on an overflow: you can use the %@AB@%JO%@AE@% or%@EH@% %@AB@%JNO%@AE@% instruction to direct program flow to or around instructions that handle the overflow (see Section 15.1.2.3%@BO: c19f4@%, "Testing Bits and Jumping"). You can also use the %@AB@%INTO%@AE@% instruction to trigger the overflow interrupt (interrupt 4) if the overflow flag is set. This requires writing an interrupt handler for interrupt 4, since the DOS overflow routine simply returns without taking any action. Section 15.4.2%@BO: cf793@%, "Defining and Redefining Interrupt Routines," gives a sample of an overflow interrupt handler.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem8 DB 39%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; unsigned signed%@AE@%%@NL@% %@AS@% mov al,26 ; Start with register 26 26%@AE@%%@NL@% %@AS@% inc al ; Increment 1 1%@AE@%%@NL@% %@AS@% add al,76 ; Add immediate + 76 76%@AE@%%@NL@% %@AS@% ; ---- ----%@AE@%%@NL@% %@AS@% ; 103 103%@AE@%%@NL@% %@AS@% add al,mem8 ; Add memory + 39 39%@AE@%%@NL@% %@AS@% ; ---- ----%@AE@%%@NL@% %@AS@% mov ah,al ; Copy to AH 142 -114+overflow%@AE@%%@NL@% %@AS@% add al,ah ; Add register 142%@AE@%%@NL@% %@AS@% ; ----%@AE@%%@NL@% %@AS@% ; 28+carry%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.15 @%%@CR:IX14.16 @%%@CR:IX14.17 @%%@CR:IX14.18 @%%@CR:IX14.19 @% This example shows 8-bit addition. When the sum exceeds 127, the overflow%@EH@% flag is set. A %@AB@%JO%@AE@% (Jump On Overflow) or %@AB@%INTO%@AE@% (Interrupt On Overflow) instruction at this point could transfer control to error-recovery statements. When the sum exceeds 255, the carry flag is set. A %@AB@%JC%@AE@% (Jump On Carry) instruction at this point could transfer control to error-recovery statements.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.1.2 @%%@AB@%14.1.2 Adding Values in Multiple Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.20 @%%@CR:IX14.21 @%%@CR:IX14.22 @%%@CR:IX14.23 @% The %@AB@%ADC%@AE@% (Add with Carry) instruction makes it possible to add numbers%@EH@% larger than can be held in a single register.%@NL@% %@NL@% %@4@%%@CR:IX14.24 @% The %@AB@%ADC%@AE@% instruction adds two numbers in the same fashion as the %@AB@%ADD%@AE@%%@EH@% instruction, except that the value of the carry flag is included in the addition. If a previous calculation has set the carry flag, then 1 will be added to the sum of the numbers. If the carry flag is not set, the %@AB@%ADC%@AE@% instruction has the same effect as the %@AB@%ADD%@AE@% instruction.%@NL@% %@NL@% %@4@%%@CR:IX14.25 @%%@CR:IX14.26 @% When adding numbers in multiple registers, the carry flag should be%@EH@% ignored for the least-significant portion, but taken into account for the most-significant portion. This can be done by using the %@AB@%ADD%@AE@% instruction for the least-significant portion and the %@AB@%ADC%@AE@% instruction for most-significant portion.%@NL@% %@NL@% %@4@% You can add and carry repeatedly inside a loop for calculations that%@EH@% require more than two registers. Use the %@AB@%ADC%@AE@% instruction in each iteration, but turn off the carry flag with the %@AB@%CLC%@AE@% (Clear Carry Flag) instruction before entering the loop so that it will not be used for the first iteration. You could also do the first add outside the loop.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem32 DD 316423%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,43981 ; Load immediate 43981%@AE@%%@NL@% %@AS@% sub dx,dx ; into DX:AX%@AE@%%@NL@% %@AS@% add ax,WORD PTR mem32[0] ; Add to both + 316423%@AE@%%@NL@% %@AS@% adc dx,WORD PTR mem32[2] ; memory words ------%@AE@%%@NL@% %@AS@% ; Result in DX:AX 360404%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.2 @%%@AB@%14.2 Subtracting%@AE@%%@EH@%%@NL@% %@CR:IX14.27 @%%@CR:IX14.28 @%%@CR:IX14.29 @%%@CR:IX14.30 @%%@CR:IX14.31 @%%@CR:IX14.32 @%%@CR:IX14.33 @%%@NL@% %@4@%%@CR:IX14.34 @%%@CR:IX14.35 @%%@CR:IX14.36 @% The %@AB@%SUB%@AE@%, %@AB@%SBB%@AE@%, %@AB@%DEC%@AE@%, and %@AB@%NEG%@AE@% instructions are used for subtracting and%@EH@% decrementing values.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%SUB%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@AB@%SBB%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@NL@% %@AB@%DEC%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@NL@% %@AB@%NEG %@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@NL@% %@NL@% %@4@% These instructions can work directly on 8-bit or 16-bit values. They can%@EH@% also be used in combination to do calculations on values too large to be held in a single register. When used with %@AB@%AAA%@AE@% and %@AB@%DAA%@AE@%, they can be used to do calculations on BCD numbers, as described in Section 14.5%@BO: b4ede@%, "Calculating with Binary Coded Decimals."%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.2.1 @%%@AB@%14.2.1 Subtracting Values Directly%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.37 @%%@CR:IX14.38 @%%@CR:IX14.39 @%%@CR:IX14.40 @%%@CR:IX14.41 @% The %@AB@%SUB%@AE@% and %@AB@%DEC%@AE@% instructions are used for subtracting from values in%@EH@% registers or memory. A related instruction, %@AB@%NEG%@AE@% (Negate), reverses the sign of a number.%@NL@% %@NL@% %@4@% The %@AB@%DEC%@AE@% instruction takes a single %@AI@%register%@AE@% or %@AI@%memory%@AE@% operand. The value%@EH@% of the operand is decremented. The value is treated as an unsigned integer, so the carry flag is not updated for signed borrows.%@NL@% %@NL@% %@4@%%@CR:IX14.42 @%%@CR:IX14.43 @% The %@AB@%NEG%@AE@% instruction takes a single %@AI@%register%@AE@% or %@AI@%memory%@AE@% operand. The sign of%@EH@% the value of the operand is reversed. The %@AB@%NEG%@AE@% instruction should only be used on signed numbers.%@NL@% %@NL@% %@4@% The %@AB@%SUB%@AE@% instruction subtracts the values given in the source operand from%@EH@% the value of the destination operand. The destination can be either a %@AI@%register%@AE@% or a %@AI@%memory%@AE@% operand. It will be destroyed by the operation. The source operand can be an %@AI@%immediate%@AE@%, %@AI@%memory%@AE@%, or %@AI@%register%@AE@% operand. It will not be destroyed by the operation. Since memory-to-memory operations are never allowed, the source and destination operands cannot both be memory operands.%@NL@% %@NL@% %@4@% The result of the operation is stored in the source operand. The operands%@EH@% can be either 8-bit or 16-bit, but both must be the same size.%@NL@% %@NL@% %@4@%%@CR:IX14.44 @%%@CR:IX14.45 @% A subtraction operation can be interpreted as subtraction of either signed%@EH@% numbers or unsigned numbers. It is the programmer's responsibility to decide how the subtraction should be interpreted and to take appropriate action if the result is too small for the destination operand. When a subtraction overflows the possible range for signed numbers, the carry flag is set. When a subtraction underflows the range for unsigned numbers (becomes negative), the sign flag is set.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem8 DB 122%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; signed unsigned%@AE@%%@NL@% %@AS@% mov al,95 ; Load register 95 95%@AE@%%@NL@% %@AS@% dec al ; Decrement - 1 - 1%@AE@%%@NL@% %@AS@% sub al,23 ; Subtract immediate - 23 - 23%@AE@%%@NL@% %@AS@% ; ---- ----%@AE@%%@NL@% %@AS@% ; 71 71%@AE@%%@NL@% %@AS@% sub al,mem8 ; Subtract memory - 122 - 122%@AE@%%@NL@% %@AS@% ; ---- ----%@AE@%%@NL@% %@AS@% ; - 51 205+sign%@AE@%%@NL@% %@NL@% %@AS@% mov ah,119 ; Load register 119%@AE@%%@NL@% %@AS@% sub al,ah ; and subtract - 51%@AE@%%@NL@% %@AS@% ; ----%@AE@%%@NL@% %@AS@% ; 86+overflow%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.46 @%%@CR:IX14.47 @%%@CR:IX14.48 @%%@CR:IX14.49 @% This example shows 8-bit subtraction. When the result goes below 0, the%@EH@% sign flag is set. A %@AB@%JS%@AE@% (Jump On Sign) instruction at this point could transfer control to error-recovery statements. When the result goes below -128, the carry flag is set. A %@AB@%JC%@AE@% (Jump On Carry) instruction at this point could transfer control to error-recovery statements.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.2.2 @%%@AB@%14.2.2 Subtracting with Values in Multiple Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.50 @%%@CR:IX14.51 @%%@CR:IX14.52 @% The %@AB@%SBB%@AE@% (Subtract with Borrow) instruction makes it possible to subtract%@EH@% from numbers larger than can be held in a single register.%@NL@% %@NL@% %@4@% The %@AB@%SBB%@AE@% instruction subtracts two numbers in the same fashion as the %@AB@%SUB%@AE@%%@EH@% instruction except that the value of the carry flag is included in the subtraction. If a previous calculation has set the carry flag, then 1 will be subtracted from the result. If the carry flag is not set, the %@AB@%SBB%@AE@% instruction has the same effect as the %@AB@%SUB%@AE@% instruction.%@NL@% %@NL@% %@4@%%@CR:IX14.53 @%%@CR:IX14.54 @%%@CR:IX14.55 @% When subtracting numbers in multiple registers, the carry flag should be%@EH@% ignored for the least-significant portion, but taken into account for the most-significant portion. This can be done by using the %@AB@%SUB%@AE@% instruction for the least-significant portion and the %@AB@%SBB%@AE@% instruction for most-significant portions.%@NL@% %@NL@% %@4@% You can subtract and borrow repeatedly inside a loop for calculations that%@EH@% require more than two registers. Use the %@AB@%SBB%@AE@% instruction in each iteration, but turn off the carry flag with the %@AB@%CLC%@AE@% (Clear Carry Flag) instruction before entering the loop so that it will not be used for the first iteration. You could also do the first subtraction outside the loop.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@% mem32a DD 316423%@AE@%%@NL@% %@AS@% mem32b DD 156739%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,WORD PTR mem32a[0] ; Load mem32 316423%@AE@%%@NL@% %@AS@% mov dx,WORD PTR mem32a[2] ; into DX:AX%@AE@%%@NL@% %@AS@% sub ax,WORD PTR mem32b[0] ; Subtract low 156739%@AE@%%@NL@% %@AS@% sbb dx,WORD PTR mem32b[2] ; then high ------%@AE@%%@NL@% %@AS@% ; Result in DX:AX 159684%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.3 @%%@AB@%14.3 Multiplying%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.56 @%%@CR:IX14.57 @%%@CR:IX14.58 @%%@CR:IX14.59 @%%@CR:IX14.60 @% The %@AB@%MUL%@AE@% and %@AB@%IMUL%@AE@% instructions are used to multiply numbers. The %@AB@%MUL%@AE@%%@EH@% instruction should be used for unsigned numbers; the %@AB@%IMUL%@AE@% instruction should be used for signed numbers. This is the only difference between the two.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%MUL%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@AB@%IMUL%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@NL@% %@NL@% %@4@% The multiply instructions require that one of the factors be in the%@EH@% accumulator register (AL for 8-bit numbers or AX for 16-bit numbers). This register is implied; it should not be specified in the source code. Its contents will be destroyed by the operation.%@NL@% %@NL@% %@4@% The other factor to be multiplied must be specified in a single %@AI@%register%@AE@%%@EH@% or %@AI@%memory%@AE@% operand. The operand will not be destroyed by the operation, unless it is DX, AX, AH, or AL. A number may be squared by loading it into the accumulator, and then executing a multiplication instruction with the accumulator as the operand.%@NL@% %@NL@% %@4@% Note that multiplying two 8-bit numbers will produce a 16-bit number. If%@EH@% the product is a 16-bit number, it will be placed in AX and the overflow and carry flags will be set.%@NL@% %@NL@% %@4@% Similarly, multiplying two 16-bit numbers will produce a 32-bit number in%@EH@% the DX:AX%@AB@% %@AE@%register pair. If the product is a 32-bit number, the least-significant bits will be in AX, the most-significant bits will be in DX, and the overflow and carry flags will be set.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Multiplication is one of the slower operations on 8086-family processors (especially the 8086 and 8088). Multiplying by certain common constants is often faster when done by shifting bits (see Section 14.7.1%@BO: bb18b@%, "Multiplying and Dividing by Constants").%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem16 DW -30000%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; 8-bit unsigned multiply%@AE@%%@NL@% %@AS@% mov al,23 ; Load AL 23%@AE@%%@NL@% %@AS@% mov bl,24 ; Load BL * 24%@AE@%%@NL@% %@AS@% mul bl ; Multiply BL -----%@AE@%%@NL@% %@AS@% ; 16-bit signed multiply%@AE@%%@NL@% %@AS@% mov ax,50 ; Load AX 50%@AE@%%@NL@% %@AS@% ; -30000%@AE@%%@NL@% %@AS@% imul mem16 ; Multiply memory -----%@AE@%%@NL@% %@AS@% ; Product in DX:AX -1500000%@AE@%%@NL@% %@AS@% ; overflow and carry set%@AE@%%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.61 @%%@CR:IX14.62 @% Starting with the 80186 processor, the %@AB@%IMUL%@AE@% instruction has two additional%@EH@% syntaxes that allow for 16-bit multiples that produce a 16-bit product.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%IMUL%@AE@% %@AI@%register16%@AE@%%@AB@%,%@AE@% %@AI@%immediate%@AE@%%@EH@%%@NL@% %@AB@%IMUL%@AE@% %@AI@%register16%@AE@%%@AB@%,%@AE@% %@AI@%memory16%@AE@%%@AB@%,%@AE@% %@AI@%immediate%@AE@%%@NL@% %@NL@% %@4@% You can specify a 16-bit %@AI@%immediate%@AE@% value as the source instruction and a%@EH@% word %@AI@%register%@AE@% as the destination operand. The product appears in the destination operand. The 16-bit result will be placed in the destination operand. If the product is too large to fit in 16 bits, the carry and overflow flags will be set. In this context, %@AB@%IMUL%@AE@% can be used for either signed or unsigned multiplication, since the 16-bit product is the same.%@NL@% %@NL@% %@4@% You can also specify three operands for %@AB@%IMUL%@AE@%. The first operand must be a%@EH@% 16-bit %@AI@%register%@AE@% operand, the second a 16-bit %@AI@%memory%@AE@% operand, and the third a 16-bit %@AI@%immediate%@AE@% operand. The second and third operands are multiplied and the product stored in the first operand.%@NL@% %@NL@% %@4@% With both of these syntaxes, the carry and overflow flags will be set if%@EH@% the product is too large to fit in 16 bits. The %@AB@%IMUL%@AE@% instruction with multiple operands can be used for either signed or unsigned multiplication, since the 16-bit product is the same in either case. If you need to get a 32-bit result, you must use the single-operand version of %@AB@%MUL%@AE@% or %@AB@%IMUL%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% imul dx,456 ; Multiply DX times 456%@AE@%%@NL@% %@AS@% imul ax,[bx],6 ; Multiply the value pointed to by BX%@AE@%%@NL@% %@AS@% ; times 6 and put the result in AX%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.4 @%%@AB@%14.4 Dividing%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.63 @%%@CR:IX14.64 @%%@CR:IX14.65 @%%@CR:IX14.66 @%%@CR:IX14.67 @% The %@AB@%DIV%@AE@% and %@AB@%IDIV%@AE@% instructions are used to divide integers. Both a quotient%@EH@% and a remainder are returned. The %@AB@%DIV%@AE@% instruction should be used for unsigned integers; the %@AB@%IDIV%@AE@% instruction should be used for signed integers. This is the only difference between the two.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%DIV%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@AB@%IDIV%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@NL@% %@NL@% %@4@% To divide a 16-bit number by an 8-bit number, put the number to be divided%@EH@% (the dividend) in the AX register. The contents of this register will be destroyed by the operation. Specify the dividing number (the divisor) in any 8-bit memory or register operand (except AL or AH). This operand will not be changed by the operation. After the multiplication, the result (quotient) will be in AL and the remainder will be in AH.%@NL@% %@NL@% %@4@% To divide a 32-bit number by a 16-bit number, put the dividend in the%@EH@% DX:AX register pair. The least-significant bits go in AX. The contents of these registers will be destroyed by the operation. Specify the divisor in any 16-bit memory or register operand (except AX or DX). This operand will not be changed by the operation. After the division, the quotient will be in AX and the remainder will be in DX.%@NL@% %@NL@% %@4@% To divide a 16-bit number by a 16-bit number, you must first sign-extend%@EH@% or zero-extend (see Section 13.2%@BO: a94e2@%, "Converting between Data Sizes") the dividend to 32 bits; then divide as described above. You cannot divide a 32-bit number by another 32-bit number.%@NL@% %@NL@% %@4@% If division by zero is specified, or if the quotient exceeds the capacity%@EH@% of its register (AL or AX), the processor automatically generates an interrupt 0. By default, the program terminates and returns to DOS. This problem can be handled in two ways: check the divisor before division and go to an error routine if you can determine it to be invalid, or write your own interrupt routine to replace the processor's interrupt 0 routine. See Section 15.4%@BO: cdbc9@% for more information on interrupts.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Division is one of the slower operations on 8086-family processors (especially the 8086 and 8088). Dividing by common constants that are powers of two is often faster when done by shifting bits, as described in Section 14.7.1%@BO: bb18b@%, "Multiplying and Dividing by Constants."%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@NL@% %@AS@%mem16 DW -2000%@AE@%%@NL@% %@AS@%mem32 DD 500000%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Divide 16-bit unsigned by 8-bit%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,700 ; Load dividend 700%@AE@%%@NL@% %@AS@% mov bl,36 ; Load divisor DIV 36%@AE@%%@NL@% %@AS@% div bl ; Divide BL -----%@AE@%%@NL@% %@AS@% ; Quotient in AL 19%@AE@%%@NL@% %@AS@% ; Remainder in AH 16%@AE@%%@NL@% %@NL@% %@AS@% ; Divide 32-bit signed by 16-bit%@AE@%%@NL@% %@AS@%%@AE@%%@NL@% %@AS@% mov ax,WORD PTR mem32[0] ; Load into DX:AX%@AE@%%@NL@% %@AS@% mov dx,WORD PTR mem32[2] ; 500000%@AE@%%@NL@% %@AS@% idiv mem16 ; DIV -2000%@AE@%%@NL@% %@AS@% ; Divide memory ------%@AE@%%@NL@% %@AS@% ; Quotient in AX -250%@AE@%%@NL@% %@AS@% ; Remainder in DX 0%@AE@%%@NL@% %@NL@% %@AS@% ; Divide 16-bit signed by 16-bit%@AE@%%@NL@% %@AS@%%@AE@%%@NL@% %@AS@% mov ax,WORD PTR mem16 ; Load into AX -2000%@AE@%%@NL@% %@AS@% cwd ; Extend to DX:AX%@AE@%%@NL@% %@AS@% mov bx,-421 ; DIV -421%@AE@%%@NL@% %@AS@% idiv bx ; Divide by BX -----%@AE@%%@NL@% %@AS@% ; Quotient in AX 4%@AE@%%@NL@% %@AS@% ; Remainder in DX -316%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.5 @%%@AB@%14.5 Calculating with Binary Coded Decimals%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.68 @% The 8086-family processors provide several instructions for adjusting BCD%@EH@% numbers. The BCD format is seldom used for applications programming in assembly language. Programmers who wish to use BCD numbers usually use a high-level language. However, BCD instructions are used to develop compilers, function libraries, and other systems tools.%@NL@% %@NL@% %@4@% Since systems programming is beyond the scope of this manual, this section%@EH@% provides only a brief overview of calculations on the two kinds of BCD numbers, unpacked and packed.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX14.69 @%%@CR:IX14.70 @%%@CR:IX14.71 @%%@CR:IX14.72 @% %@AB@%NOTE%@AE@% Intel mnemonics use the term "ASCII" to refer to unpacked BCD numbers and "decimal" to refer to packed BCD numbers. Thus %@AB@%AAA%@AE@% (ASCII Adjust After Addition) adjusts unpacked numbers, while %@AB@%DAA (%@AE@%Decimal Adjust After Addition) adjusts packed numbers.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.5.1 @%%@AB@%14.5.1 Unpacked BCD Numbers%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Unpacked BCD numbers are made up of bytes containing a single decimal%@EH@% digit in the lower four bits of each byte. The 8086-family processors provide instructions for adjusting unpacked values with the four arithmetic operations──addition, subtraction, multiplication, and division.%@NL@% %@NL@% %@4@% To do arithmetic on unpacked BCD numbers, you must do the eight-bit%@EH@% arithmetic calculations on each digit separately. The result should always be in the AL register. After each operation, use the corresponding BCD instruction to adjust the result. The ASCII-adjust instructions do not take an operand. They always work on the value in the AL register.%@NL@% %@NL@% %@4@% When a calculation using two one-digit values produces a two-digit result,%@EH@% the ASCII-adjust instructions put the first digit in AL and the second in AH. If the digit in AL needs to carry to or borrow from the digit in AH, the carry and auxiliary carry flags are set.%@NL@% %@NL@% %@4@% The four ASCII-adjust instructions are described below:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX14.73 @%%@CR:IX14.74 @% %@AB@%AAA%@AE@% Adjusts after an addition operation. For example, to add 9 and 3, use the following lines:%@NL@% %@NL@% %@NL@% %@AS@%mov ax,9 ; Load 9%@AE@%%@NL@% %@AS@%mov bx,3 ; and 3 as unpacked BCD%@AE@%%@NL@% %@AS@%add al,bl ; Add 09h and 03h to get 0Ch%@AE@%%@NL@% %@AS@%aaa ; Adjust 0Ch in AL to 02h,%@AE@%%@NL@% %@AS@% ; increment AH to 01h, set carry%@AE@%%@NL@% %@AS@% ; Result 12 unpacked BCD in AX%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.75 @%%@CR:IX14.76 @% %@AB@%AAS%@AE@% Adjusts after a subtraction operation. For example, to%@EH@% subtract 4 from 13, use the following lines:%@NL@% %@NL@% %@NL@% %@AS@%mov ax,103h ; Load 13%@AE@%%@NL@% %@AS@%mov bx,4 ; and 4 as unpacked BCD%@AE@%%@NL@% %@AS@%sub al,bl ; Subtract 4 from 3 to get FFh (-1)%@AE@%%@NL@% %@AS@%aas ; Adjust 0FFh in AL to 9,%@AE@%%@NL@% %@AS@% ; decrement AH to 0, set carry%@AE@%%@NL@% %@AS@% ; Result 9 unpacked BCD in AX%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.77 @%%@CR:IX14.78 @% %@AB@%AAM%@AE@% Adjusts after a multiplication operation. Always use%@EH@% %@AB@%MUL%@AE@%, not %@AB@%IMUL%@AE@%. For example, to multiply 9 times 3, use the following lines:%@NL@% %@NL@% %@NL@% %@AS@%mov ax,903h ; Load 9 and 3 as unpacked BCD%@AE@%%@NL@% %@AS@%mul ah ; Multiply 9 and 3 to get 1Bh%@AE@%%@NL@% %@AS@%aam ; Adjust 1Bh in AL%@AE@%%@NL@% %@AS@% ; to get 27 unpacked BCD in AX%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.79 @%%@CR:IX14.80 @% %@AB@%AAD%@AE@% Adjusts before a division operation. Unlike other BCD%@EH@% instructions, this one converts a BCD value to a binary value before the operation. After the operation, the quotient must still be adjusted by using %@AB@%AAM%@AE@%. For example, to divide 25 by 2, use the following lines:%@NL@% %@NL@% %@NL@% %@AS@%mov ax,205h ; Load 25%@AE@%%@NL@% %@AS@%mov bl,2 ; and 2 as unpacked BCD%@AE@%%@NL@% %@AS@%aad ; Adjust 0205h in AX%@AE@%%@NL@% %@AS@% ; to get 19h in AX%@AE@%%@NL@% %@AS@%div bl ; Divide by 2 to get%@AE@%%@NL@% %@AS@% ; quotient 0Ch in AL%@AE@%%@NL@% %@AS@% ; remainder 1 in AH%@AE@%%@NL@% %@AS@%aam ; Adjust 0Ch in AL%@AE@%%@NL@% %@AS@% ; to 12 unpacked BCD in AX%@AE@%%@NL@% %@AS@% ; (remainder destroyed)%@AE@%%@NL@% %@NL@% %@4@% Notice that the remainder is lost. If you need the remainder, save it in%@EH@% another register before adjusting the quotient. Then move it back to AL and adjust if necessary.%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX14.81 @%%@CR:IX14.82 @% Multidigit BCD numbers are usually processed in loops. Each digit is%@EH@% processed and adjusted in turn. In addition to their use for processing unpacked BCD numbers, the ASCII-adjust instructions can be used in routines that convert between different number bases.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov al,79 ; Load 79 (04Fh)%@AE@%%@NL@% %@AS@% aam ; Adjust to BCD (0709h)%@AE@%%@NL@% %@AS@% add ah,'0' ; Adjust to ASCII characters%@AE@%%@NL@% %@AS@% add al,'0' ; (3739h)%@AE@%%@NL@% %@AS@% mov dx,ax ; Copy to DX%@AE@%%@NL@% %@AS@% xchg dl,dh ; Trade for most significant digit%@AE@%%@NL@% %@AS@% mov ah,2 ; DOS display character function%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@AS@% mov dl,dh ; Load least significant digit%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@NL@% %@4@% The example converts an eight-bit binary number to hexadecimal and%@EH@% displays it on the screen. The routine could be enhanced to handle large numbers.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.5.2 @%%@AB@%14.5.2 Packed BCD Numbers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.83 @% Packed BCD numbers are made up of bytes containing two decimal digits: one%@EH@% in the upper four bits and one in the lower four bits. The 8086-family processors provide instructions for adjusting packed BCD numbers after addition and subtraction. You must write your own routines to adjust for multiplication and division.%@NL@% %@NL@% %@4@% To do arithmetic on packed BCD numbers, you must do the eight-bit%@EH@% arithmetic calculations on each byte separately. The result should always be in the AL register. After each operation, use the corresponding BCD instruction to adjust the result. The decimal-adjust instructions do not take an operand. They always work on the value in the AL register.%@NL@% %@NL@% %@4@% Unlike the ASCII-adjust instructions, the decimal-adjust instructions%@EH@% never affect AH. The auxiliary carry flag is set if the digit in the lower four bits carries to or borrows from the digit in the upper four bits. The carry flag is set if the digit in the upper four bits needs to carry to or borrow from another byte.%@NL@% %@NL@% %@4@% The decimal-adjust instructions are described below:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX14.84 @%%@CR:IX14.85 @% %@AB@%DAA%@AE@% Adjusts after an addition operation. For example, to add 88 and 33, use the following lines:%@NL@% %@NL@% %@NL@% %@AS@% mov ax,8833h ; Load 88 and 33 as packed BCD%@AE@%%@NL@% %@AS@% add al,ah ; Add 88 and 33 to get 0BBh%@AE@%%@NL@% %@AS@% daa ; Adjust 0BBh to 121 packed BCD:%@AE@%%@NL@% %@AS@% ; 1 in carry and 21 in AL%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.86 @%%@CR:IX14.87 @%%@CR:IX14.88 @% %@AB@%DAS%@AE@% Adjusts after a subtraction operation. For example, to%@EH@% subtract 38 from 83, put 83 in AL and 38 in AH in packed BCD format. Then use the following lines to subtract them:%@NL@% %@NL@% %@NL@% %@AS@% mov ax,3883h ; Load 83 and 38 as packed BCD%@AE@%%@NL@% %@AS@% sub al,ah ; Subtract 38 from 83 to get 04Bh%@AE@%%@NL@% %@AS@% das ; Adjust 04Bh to 45 packed BCD:%@AE@%%@NL@% %@AS@% ; 0 in carry and 45 in AL%@AE@%%@NL@% %@NL@% %@NL@% %@4@% Multidigit BCD numbers are usually processed in loops. Each byte is%@EH@% processed and adjusted in turn.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.6 @%%@AB@%14.6 Doing Logical Bit Manipulations%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.89 @% The logical instructions do Boolean operations on individual bits. The%@EH@% AND, OR, XOR, and NOT operations are supported by the 8086-family instructions.%@NL@% %@NL@% %@4@% AND compares two bits and sets the result if both bits are set. OR%@EH@% compares two bits and sets the result if either bit is set. XOR compares two bits and sets the result if the bits are different. NOT reverses a single bit. Table 14.1 shows a truth table for the logical operations.%@NL@% %@NL@% %@AB@%Table 14.1 Values Returned by Logical Operations%@AE@%%@NL@% %@NL@% %@AB@%X%@AE@% %@AB@%Y%@AE@% %@AB@%NOT X%@AE@% %@AB@%X AND Y%@AE@% %@AB@%X OR Y%@AE@% %@AB@%X XOR Y%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 1 1 0 1 1 0%@NL@% %@NL@% 1 0 0 0 1 1%@NL@% %@NL@% 0 1 1 0 1 1%@NL@% %@NL@% 0 0 1 0 0 0%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX14.90 @%%@CR:IX14.91 @%%@CR:IX14.92 @%%@CR:IX14.93 @%%@CR:IX14.94 @%%@CR:IX14.95 @%%@CR:IX14.96 @%%@CR:IX14.97 @% The syntax of the %@AB@%AND%@AE@%, %@AB@%OR%@AE@%, and %@AB@%XOR%@AE@% instructions is the same. The only%@EH@% difference is the operation performed. For all instructions, the target value to be changed by the operation is placed in one operand. A mask showing the positions of bits to be changed is placed in the other operand. The format of the mask differs for each logical instruction. The destination operand can be register or memory. The source operand can be register, memory, or immediate. However, the source and destination operands cannot both be memory operands.%@NL@% %@NL@% %@4@% Either of the values can be in either operand. However, the source operand%@EH@% will be unchanged by the operation, while the destination operand will be destroyed by it. Your choice of operands depends on whether you want to save a copy of the mask or of the target value.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX14.98 @%%@CR:IX14.99 @%%@CR:IX14.100 @% %@AB@%NOTE%@AE@% The logical instructions should not be confused with the logical operators. They specify completely different behavior. The instructions control run-time bit calculations. The operators control assembly-time bit calculations. Although the instructions and operators have the same name, the assembler can distinguish them from context.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.6.1 @%%@AB@%14.6.1 AND Operations%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.101 @%%@CR:IX14.102 @% The %@AB@%AND%@AE@% instruction does an AND operation on the bits of the source and%@EH@% destination operands. The original destination operand is replaced by the resulting bits.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%AND%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%AND%@AE@% instruction can be used to clear the value of specific bits%@EH@% regardless of their current settings. To do this, put the target value in one operand and a mask of the bits you want to clear in the other. The bits of the mask should be 0 for any bit positions you want to clear and 1 for any bit positions you want to remain unchanged.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,035h ; Load value 00110101%@AE@%%@NL@% %@AS@% and ax,0FBh ; Mask off bit 2 AND 11111011%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Value is now 31h 00110001%@AE@%%@NL@% %@AS@% and ax,0F8h ; Mask off bits 2,1,0 AND 11111000%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Value is now 30h 00110000%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ah,7 ; Get character without echo%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% and al,11011111b ; Convert to uppercase by clearing bit 5%@AE@%%@NL@% %@AS@% cmp al,'Y' ; Is it Y?%@AE@%%@NL@% %@AS@% je yes ; If so, do Yes stuff%@AE@%%@NL@% %@AS@% . ; else do No stuff%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%yes: .%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates how to use the %@AB@%AND%@AE@% instruction to convert a%@EH@% character to uppercase. If the character is already uppercase, the %@AB@%AND%@AE@% instruction has no effect, since bit 5 is always clear in uppercase letters. If the character is lowercase, clearing bit 5 converts it to uppercase.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.6.2 @%%@AB@%14.6.2 OR Operations%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.103 @%%@CR:IX14.104 @% The %@AB@%OR%@AE@% instruction does an OR operation on the bits of the source and%@EH@% destination operands. The original destination operand is replaced by the resulting bits.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%OR%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%OR%@AE@% instruction can be used to set the value of specific bits%@EH@% regardless of their current settings. To do this, put the target value in one operand and a mask of the bits you want to clear in the other. The bits of the mask should be 1 for any bit positions you want to set and 0 for any bit positions you want to remain unchanged.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,035h ; Move value to register 00110101%@AE@%%@NL@% %@AS@% or ax,08h ; Mask on bit 3 OR 00001000%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Value is now 3Dh 00111101%@AE@%%@NL@% %@AS@% or ax,07h ; Mask on bits 2,1,0 OR 00000111%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Value is now 3Fh 00111111%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.105 @% Another common use for %@AB@%OR%@AE@% is to compare an operand to 0:%@EH@%%@NL@% %@NL@% %@AS@% or bx,bx ; Compare to 0%@AE@%%@NL@% %@AS@% ; 2 bytes, 2 clocks on 8088%@AE@%%@NL@% %@AS@% jg positive ; BX is positive%@AE@%%@NL@% %@AS@% jl negative ; BX is negative%@AE@%%@NL@% %@AS@% ; BX is zero%@AE@%%@NL@% %@NL@% %@4@% The first statement has the same effect as the following statement, but is%@EH@% faster and smaller:%@NL@% %@NL@% %@AS@% cmp bx,0 ; 3 bytes, 3 clocks on 8088%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.6.3 @%%@AB@%14.6.3 XOR Operations%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.106 @%%@CR:IX14.107 @% The %@AB@%XOR%@AE@% (Exclusive OR) instruction does an XOR operation on the bits of%@EH@% the source and destination operands. The original destination operand is replaced by the resulting bits.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%XOR%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%XOR%@AE@% instruction can be used to toggle the value of specific bits%@EH@% (reverse them from their current settings). To do this, put the target value in one operand and a mask of the bits you want to toggle in the other. The bits of the mask should be 1 for any bit positions you want to toggle and 0 for any bit positions you want to remain unchanged.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov ax,035h ; Move value to register 00110101%@AE@%%@NL@% %@AS@% xor ax,08h ; Mask on bit 3 XOR 00001000%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Value is now 3Dh 00111101%@AE@%%@NL@% %@AS@% xor ax,07h ; Mask on bits 2,1,0 XOR 00000111%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Value is now 3Ah 00111010%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX14.108 @% Another common use for the %@AB@%XOR%@AE@% instruction is to set a register to 0:%@EH@%%@NL@% %@NL@% %@AS@% xor cx,cx ; 2 bytes, 3 clocks on 8088%@AE@%%@NL@% %@NL@% %@4@% This sets the CX register to 0. When the %@AB@%XOR%@AE@% instruction takes identical%@EH@% operands, each bit cancels itself, producing 0. The statement%@NL@% %@NL@% %@AS@% mov cx,0 ; 3 bytes, 4 clocks on 8088%@AE@%%@NL@% %@NL@% %@4@% is the obvious way of doing this, but it is larger and slower. The%@EH@% statement%@NL@% %@NL@% %@AS@% sub cx,cx ; 2 bytes, 3 clocks on 8088%@AE@%%@NL@% %@NL@% %@4@% is also smaller than the %@AB@%MOV%@AE@% version. The only advantage of using %@AB@%MOV%@AE@% is%@EH@% that it does not affect any flags.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.6.4 @%%@AB@%14.6.4 NOT Operations%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.109 @%%@CR:IX14.110 @% The %@AB@%NOT%@AE@% instruction does a NOT operation on the bits of a single operand.%@EH@% It is used to toggle the value of all bits at once.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% NOT {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.111 @% The %@AB@%NOT%@AE@% instruction is often used to reverse the sense of a bit mask from%@EH@% masking certain bits on to masking them off. Use the %@AB@%NOT%@AE@% instruction if the value of the mask is not known until run time; use the%@AB@% NOT%@AE@% operator (see Section 9.2.1.5%@BO: 7c89d@%, "Bitwise Logical Operators") if the mask is a constant.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%masker DB 00010000b ; Value may change at run time%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov ax,0D743h ; Load 0D7h to AH, 43h to AL 01000011%@AE@%%@NL@% %@AS@% or al,masker ; Turn on bit 4 in AL OR 00010000%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Result is 53h 01010011%@AE@%%@NL@% %@NL@% %@AS@% not masker ; Reverse sense of mask 11101111%@AE@%%@NL@% %@AS@% and ah,masker ; Turn off bit 4 in AH AND 11010111%@AE@%%@NL@% %@AS@% ; --------%@AE@%%@NL@% %@AS@% ; Result is 0C7h 11000111%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC14.7 @%%@AB@%14.7 Shifting and Rotating Bits%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.112 @% The 8086-family processors provide a complete set of instructions for%@EH@% shifting and rotating bits. Bits can be moved right (toward the most-significant bits) or left (toward the 0 bit). Values shifted off the end of the operand go into the carry flag.%@NL@% %@NL@% %@4@% Shift instructions move bits a specified number of places to the right or%@EH@% left. The last bit in the direction of the shift goes into the carry flag, and the first bit is filled with 0 or with the previous value of the first bit.%@NL@% %@NL@% %@4@%%@CR:IX14.113 @%%@CR:IX14.114 @% Rotate instructions move bits a specified number of places to the right or%@EH@% left. For each bit rotated, the last bit in the direction of the rotate operation is moved into the first bit position at the other end of the operand. With some variations, the carry bit is used as an additional bit of the operand. Figure 14.1 illustrates the eight variations of shift and rotate instructions for eight-bit operands. Notice that %@AB@%SHL%@AE@% and %@AB@%SAL%@AE@% are identical.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 14.7 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%SHL%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%SHR%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%SAL%@AE@%%@EH@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%SAR%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%ROL%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%ROR%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%RCL%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%} %@AB@%RCR%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AB@%CL%@AE@% | %@AB@%1%@AE@%}%@NL@% %@NL@% %@4@% The format of all the shift instructions is the same. The destination%@EH@% operand should contain the value to be shifted. It will contain the shifted operand after the instruction. The source operand should contain the number of bits to shift or rotate. It can be the immediate value 1 or the CL register. No other value or register is accepted on the 8088 and 8086 processors.%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Starting with the 80186 processor, eight-bit immediate values larger than%@EH@% 1 can be given as the source operand for shift or rotate instructions, as shown below:%@NL@% %@NL@% %@AS@% shr bx,4 ; 9 clocks, 3 bytes on 80286%@AE@%%@NL@% %@NL@% %@4@% The following statements are equivalent if the program must run the 8088%@EH@% or 8086:%@NL@% %@NL@% %@AS@% mov cl,4 ; 2 clocks, 3 bytes on 80286%@AE@%%@NL@% %@AS@% shr bx,cl ; 9 clocks, 2 bytes on 80286%@AE@%%@NL@% %@AS@% ; 11 clocks, 5 bytes%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.7.1 @%%@AB@%14.7.1 Multiplying and Dividing by Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.115 @%%@CR:IX14.116 @%%@CR:IX14.117 @% Shifting right by one has the effect of dividing by two; shifting left by%@EH@% one has the effect of multiplying by two. You can take advantage of this to do fast multiplication and division by common constants. The easiest constants are the powers of two. Shifting left twice multiplies by four, shifting left three times multiplies by eight, and so on.%@NL@% %@NL@% %@4@% %@AB@%SHR%@AE@% is used to divide unsigned numbers. %@AB@%SAR%@AE@% can be used to divide signed%@EH@% numbers, but %@AB@%SAR%@AE@% rounds negative numbers down──%@AB@%IDIV%@AE@% always rounds up. Code that divides by using %@AB@%SAR%@AE@% must adjust for this difference. Multiplication by shifting is the same for signed and unsigned numbers, so either %@AB@%SAL%@AE@% or %@AB@%SHL%@AE@% can be used. Both instructions do the same operation.%@NL@% %@NL@% %@4@% Since the multiply and divide instructions are the slowest on the 8088 and%@EH@% 8086 processors, using shifts instead can often speed operations by a factor of 10 or more. For example, on the 8088 or 8086 processor, the following statements take four clocks:%@NL@% %@NL@% %@AS@% xor ah,ah ; Clear AH%@AE@%%@NL@% %@AS@% shl ax,1 ; Multiply byte in AL by 2%@AE@%%@NL@% %@NL@% %@4@% The following statements have the same effect, but take between 74 and 81%@EH@% clocks on the 8088 or 8086:%@NL@% %@NL@% %@AS@% mov bl,2 ; Multiply byte in AL by 2%@AE@%%@NL@% %@AS@% mul bl%@AE@%%@NL@% %@NL@% %@4@% The same statements take 15 clocks on the 80286. See the on-line Help%@EH@% system for complete information on timing of instructions.%@NL@% %@NL@% %@4@% Shift instructions can be combined with add or subtract instructions to do%@EH@% multiplication by common constants. These operations are best put in macros so that they can be changed if the constants in a program change.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%mul_10 MACRO factor ; Factor must be unsigned%@AE@%%@NL@% %@AS@% mov ax,factor ; Load into AX%@AE@%%@NL@% %@AS@% shl ax,1 ; AX = factor * 2%@AE@%%@NL@% %@AS@% mov bx,ax ; Save copy in BX%@AE@%%@NL@% %@AS@% shl ax,1 ; AX = factor * 4%@AE@%%@NL@% %@AS@% shl ax,1 ; AX = factor * 8%@AE@%%@NL@% %@AS@% add ax,bx ; AX = (factor * 8) + (factor * 2)%@AE@%%@NL@% %@AS@% ENDM ; AX = factor * 10%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%div_u512 MACRO dividend ; Dividend must be unsigned%@AE@%%@NL@% %@AS@% mov ax,dividend ; Load into AX%@AE@%%@NL@% %@AS@% shr ax,1 ; AX = dividend / 2 (unsigned)%@AE@%%@NL@% %@AS@% xchg al,ah ; xchg is like rotate right 8%@AE@%%@NL@% %@AS@% ; AL = (dividend / 2) / 256%@AE@%%@NL@% %@AS@% cbw ; Clear upper byte%@AE@%%@NL@% %@AS@% ENDM ; AX = (dividend / 512%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.7.2 @%%@AB@%14.7.2 Moving Bits to the Least-Significant Position%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Sometimes a group of bits within an operand needs to be treated as a%@EH@% single unit──for example, to do an arithmetic operation on those bits without affecting other bits. This can be done by masking off the bits and then shifting them into the least-significant positions. After the arithmetic operation is done, the bits are shifted back to the original position and merged with the original bits by using %@AB@%OR%@AE@%. See Section 7.2.5%@BO: 7047a@%, "Using Record-Field Operands," for an example of this operation.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.7.3 @%%@AB@%14.7.3 Adjusting Masks%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.118 @% Masks for logical instructions can be shifted to new bit positions. For%@EH@% example, an operand that masks off a bit or group of bits can be shifted to move the mask to a different position.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%masker DB 00000010b ; Mask that may change at run time%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cl,2 ; Rotate two at a time%@AE@%%@NL@% %@AS@% mov bl,57h ; Load value to be changed 01010111b%@AE@%%@NL@% %@AS@% rol masker,cl ; Rotate two to left 00001000b%@AE@%%@NL@% %@AS@% or bl,masker ; Turn on masked values ---------%@AE@%%@NL@% %@AS@% ; New value is 05Fh 01011111b%@AE@%%@NL@% %@AS@% rol masker,cl ; Rotate two more 00100000b%@AE@%%@NL@% %@AS@% or bl,masker ; Turn on masked values ---------%@AE@%%@NL@% %@AS@% ; New value is 07Fh 01111111b%@AE@%%@NL@% %@NL@% %@4@% This technique is useful only if the mask value is unknown until run time.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC14.7.4 @%%@AB@%14.7.4 Shifting Multiword Values%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX14.119 @%%@CR:IX14.120 @% Sometimes it is necessary to shift a value that is too large to fit in a%@EH@% register. In this case, you can shift each part separately, passing the shifted bits through the carry flag. The %@AB@%RCR%@AE@% or %@AB@%RCL%@AE@% instructions must be used to move the carry value from the first register to the second.%@NL@% %@NL@% %@4@% %@AB@%RCR%@AE@% and %@AB@%RCL%@AE@% can also be used to initialize the high or low bit of an%@EH@% operand. Since the carry flag is treated as part of the operand (like using a nine-bit operand), the flag value before the operation is crucial. The carry flag may be set by a previous instruction, or you can set it directly using the %@AB@%CLC%@AE@% (Clear Carry Flag), %@AB@%CMC%@AE@% (Complement Carry Flag), and %@AB@%STC%@AE@% (Set Carry Flag) instructions.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%mem32 DD 500000%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Divide 32-bit unsigned by 16%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cx,4 ; Shift right 4 500000%@AE@%%@NL@% %@AS@%again: shr WORD PTR mem32[2],1 ; Shift into carry DIV 16%@AE@%%@NL@% %@AS@% rcr WORD PTR mem32[0],1 ; Rotate carry in ------%@AE@%%@NL@% %@AS@% loop again ; 31250%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH15 @%%@AB@%Chapter 15: Controlling Program Flow%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX15.1 @%%@CR:IX15.2 @% The 8086-family processors provide a variety of instructions for%@EH@% controlling the flow of a program. The four major types of program-flow instructions are jumps, loops, procedure calls, and interrupts.%@NL@% %@NL@% %@4@% This chapter tells you how to use these instructions and how to test%@EH@% conditions for the instructions that change program flow conditionally.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC15.1 @%%@AB@%15.1 Jumping%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.3 @%%@CR:IX15.4 @% Jumps are the most direct method of changing program control from one%@EH@% location to another. At the internal level, jumps work by changing the value of the IP (Instruction Pointer) register from the address of the current instruction to a target address.%@NL@% %@NL@% %@4@% Jumps can be short, near, or far. QuickAssembler automatically handles%@EH@% near and short jumps, although it may not always generate the most efficient code if the label being jumped to is a forward reference. The size and control of jumps are discussed in Section 9.4.1%@BO: 845be@%, "Forward References to Labels."%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.1.1 @%%@AB@%15.1.1 Jumping Unconditionally%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.5 @%%@CR:IX15.6 @%%@CR:IX15.7 @% The %@AB@%JMP%@AE@% instruction is used to jump unconditionally to a specified%@EH@% address.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%JMP%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The operand should contain the address to be jumped to. Unlike conditional%@EH@% jumps, whose target address must be short (within 128 bytes), the target address for unconditional jumps can be short, near, or far. See Section 9.4.1%@BO: 845be@% for more information on specifying the distance for conditional jumps.%@NL@% %@NL@% %@4@% If a conditional jump must be greater than 128 bytes, the construction%@EH@% must be reorganized. This can be done by reversing the sense of the conditional jump and adding an unconditional jump, as shown in Example 1.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% cmp ax,7 ; If AX is 7 and jump is short%@AE@%%@NL@% %@AS@% je close ; then jump close%@AE@%%@NL@% %@NL@% %@AS@% cmp ax,6 ; If AX is 6 and jump is near%@AE@%%@NL@% %@AS@% jne close ; then test opposite and skip over%@AE@%%@NL@% %@AS@% jmp distant ; Now jump%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%close: ; Less than 128 bytes from jump%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%distant: ; More than 128 bytes from jump%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX15.8 @%%@CR:IX15.9 @%%@CR:IX15.10 @%%@CR:IX15.11 @%%@CR:IX15.12 @% An unconditional jump can be used as a form of conditional jump by%@EH@% specifying the address in a register or indirect memory operand. The value of the operand can be calculated at run time, based on user interaction or other factors. You can use indirect memory operands to construct jump tables that work like C %@AB@%switch%@AE@% statements, BASIC %@AB@%ON GOTO%@AE@% statements, or Pascal %@AB@%case%@AE@% statements.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% jmp process ; Jump over data%@AE@%%@NL@% %@AS@%ctl_tbl LABEL WORD ; (required in overlay procedures)%@AE@%%@NL@% %@AS@% DW extended ; Null key (extended code)%@AE@%%@NL@% %@AS@% DW ctrla ; Address of CONTROL-A key routine%@AE@%%@NL@% %@AS@% DW ctrlb ; Address of CONTROL-B key routine%@AE@%%@NL@% %@AS@%process: mov ah,8h ; Get a key%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% cbw ; Convert AL to AX%@AE@%%@NL@% %@AS@% mov bx,ax ; Copy%@AE@%%@NL@% %@AS@% shl bx,1 ; Convert to address%@AE@%%@NL@% %@NL@% %@AS@% jmp ctl_tbl[bx] ; Jump to key routine%@AE@%%@NL@% %@NL@% %@AS@%extended: mov ah,8h ; Get second key of extended%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@AS@% . ; Use another jump table%@AE@%%@NL@% %@AS@% . ; for extended keys%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%ctrla: . ; CONTROL-A routine here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% jmp next%@AE@%%@NL@% %@NL@% %@AS@%ctrlb: . ; CONTROL-B routine here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% jmp next%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%next: . ; Continue%@AE@%%@NL@% %@NL@% %@4@% In Example 2, an indirect memory operand points to addresses of routines%@EH@% for handling different keystrokes. Notice that the jump table is placed in the code segment. This technique is optional in stand-alone assembler programs, but it may be required for procedures called from some languages.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.1.2 @%%@AB@%15.1.2 Jumping Conditionally%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.13 @%%@CR:IX15.14 @% The most common way of transferring control in assembly language is with%@EH@% conditional jumps. This is a two-step process: first test the condition, and then jump if the condition is true or continue if it is false.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%J%@AE@%%@AI@%condition%@AE@% %@AI@%label%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Conditional-jump instructions take a single operand containing the address%@EH@% to be jumped to. The distance from the jump instruction to the specified address must be short (less than 128 bytes). If a longer distance is specified, an error will be generated telling the distance of the jump in bytes. See Section 15.1.1%@BO: bd2e4@%, "Jumping Unconditionally," for information on arranging longer conditional jumps.%@NL@% %@NL@% %@4@%%@CR:IX15.15 @%%@CR:IX15.16 @%%@CR:IX15.17 @%%@CR:IX15.18 @%%@CR:IX15.19 @%%@CR:IX15.20 @% Conditional-jump instructions (except %@AB@%JCXZ%@AE@%) use the status of one or more%@EH@% flags as their condition. Thus, any statement that sets a flag under specified conditions can be the test statement. The most common test statements use the %@AB@%CMP%@AE@% or %@AB@%TEST%@AE@% instructions. The jump statement can be any one of 31 conditional-jump instructions.%@NL@% %@NL@% %@4@% Because conditional jumps cannot refer to labels more than 128 bytes away,%@EH@% they are often used in combination with unconditional jumps, which have no such limitation. For example, the following statement is valid as long as %@AS@%target %@AE@%is not far away:%@NL@% %@NL@% %@AS@% jz target ; If previous operation resulted in%@AE@%%@NL@% %@AS@% ; zero, jump to target%@AE@%%@NL@% %@NL@% %@4@% Once %@AS@%target %@AE@%becomes too distant, the following sequence must be used to%@EH@% enable a longer jump. Note that this sequence is logically equivalent to the example above:%@NL@% %@NL@% %@AS@% jnz skip ; If previous operation resulted in NOT zero,%@AE@%%@NL@% %@AS@% ; jump to "skip"%@AE@%%@NL@% %@AS@% jmp target ; Otherwise, jump to target%@AE@%%@NL@% %@AS@%skip:%@AE@%%@NL@% %@NL@% %@4@% The instructions above first test for the logical %@AI@%inverse%@AE@% of the desired%@EH@% condition. If the test condition (in this case, equality to zero) is %@AI@%not%@AE@% true, the jump to %@AS@%target %@AE@%is avoided. Yet if a zero condition is true, the program falls through to the instruction %@AS@%jmp target%@AE@%, which can jump any distance. The effect, of course, is to jump to %@AS@%target %@AE@%if the previous operation resulted in zero.%@NL@% %@NL@% %@4@% The problem with this technique is that if used often, you may have to%@EH@% think up a label name just to jump around one instruction. Anonymous labels, described in Section 6.4.2%@BO: 5e370@%, let you avoid having to invent so many label names. For example, you could use an anonymous label to rewrite the example above:%@NL@% %@NL@% %@AS@% jnz @F ; If previous operation resulted in NOT zero,%@AE@%%@NL@% %@AS@% ; jump forward to next @ label%@AE@%%@NL@% %@AS@% jmp target ; Otherwise, jump to target%@AE@%%@NL@% %@AS@%@:%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.1.2.1 @%%@AB@%15.1.2.1 Comparing and Jumping%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%CMP%@AE@% instruction is specifically designed to test for conditional%@EH@% jumps. It does not change the destination operand, so it can be used to compare two values without changing either of them. Instructions that change operands (such as %@AB@%SUB%@AE@% or %@AB@%AND%@AE@%) can also be used to test conditions.%@NL@% %@NL@% %@4@%%@CR:IX15.21 @%%@CR:IX15.22 @% The %@AB@%CMP%@AE@% instruction compares two operands and sets flags based on the%@EH@% result. It is used to test the following relationships: equal; not equal; greater than; less than; greater than or equal; or less than or equal.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%CMP %@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The destination operand can be %@AI@%memory%@AE@% or %@AI@%register%@AE@%. The source operand can%@EH@% be %@AI@%immediate%@AE@%, %@AI@%memory%@AE@%, or %@AI@%register%@AE@%. However, they cannot both be memory operands.%@NL@% %@NL@% %@4@% The jump instructions that can be used with %@AB@%CMP%@AE@% are made up of mnemonic%@EH@% letters combined to indicate the type of jump. The letters are shown below:%@NL@% %@NL@% %@AB@%Letter%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% J Jump%@NL@% %@NL@% G Greater than (for unsigned comparisons)%@NL@% %@NL@% L Less than (for unsigned comparisons)%@NL@% %@NL@% A Above (for signed comparisons)%@NL@% %@NL@% B Below (for signed comparisons)%@NL@% %@NL@% E Equal%@NL@% %@NL@% N Not%@NL@% %@NL@% %@NL@% %@4@% The mnemonic names always refer to the relationship that the first operand%@EH@% of the %@AB@%CMP%@AE@% instruction has to the second operand of the %@AB@%CMP%@AE@% instruction. For instance, %@AB@%JG%@AE@% tests whether the first operand is greater than the second. Several conditional instructions have two names. You can use whichever name seems more mnemonic in context.%@NL@% %@NL@% %@4@% Comparisons and conditional jumps can be thought of as statements in the%@EH@% following format:%@NL@% %@NL@% %@4@% %@AB@%IF%@AE@% (%@AI@%value1%@AE@% %@AI@%relationship%@AE@% %@AI@%value2%@AE@%) %@AB@%THEN GOTO%@AE@% %@AI@%truelabel:%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Statements of this type can be coded in assembly language by using the%@EH@% following syntax:%@NL@% %@NL@% %@4@% %@AB@%CMP %@AE@%%@AI@%value1%@AE@%,%@AI@%value2%@AE@%%@EH@%%@NL@% %@AB@%J%@AE@%%@AI@%relationship%@AE@% %@AI@%truelabel%@AE@%%@NL@% .%@NL@% .%@NL@% .%@NL@% %@AI@%truelabel%@AE@%:%@NL@% %@NL@% %@4@%%@CR:IX15.23 @%%@CR:IX15.24 @%%@CR:IX15.25 @%%@CR:IX15.26 @%%@CR:IX15.27 @% Table 15.1 lists conditional-jump instructions for each %@AI@%relationship%@AE@% and%@EH@% shows the flags that are tested in order to see if %@AI@%relationship%@AE@% is true.%@NL@% %@NL@% %@AB@%Table 15.1 Conditional-Jump Instructions Used after Compare%@AE@%%@NL@% %@NL@% %@AB@%Jump Condition%@AE@% %@AB@%Signed%@AE@% %@AB@%Jump if:%@AE@% %@AB@%Unsigned%@AE@% %@AB@%Jump if:%@AE@% %@AB@%Compare%@AE@% %@AB@%Compare%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% = Equal %@AB@%JE%@AE@% ZF = 1 %@AB@%JE%@AE@% ZF = 1%@NL@% %@NL@% ╪ Not equal %@AB@%JNE%@AE@% ZF = 1 %@AB@%JNE%@AE@% ZF = 1%@NL@% %@NL@% > Greater than %@AB@%JG%@AE@% or %@AB@%JNLE%@AE@% ZF = 0 and %@AB@%JA%@AE@% or %@AB@%JNBE%@AE@% CF = 0 and ZF = 0 SF = OF%@NL@% <= Less than or %@AB@%JLE%@AE@% or %@AB@%JNG%@AE@% ZF = 1 and %@AB@%JBE%@AE@% or %@AB@%JNA%@AE@% CF = 1 or ZF = 1 equal SF ╪ OF%@NL@% < Less than %@AB@%JL%@AE@% or %@AB@%JNGE%@AE@% SF ╪ OF %@AB@%JB%@AE@% or %@AB@%JNAE%@AE@% CF = 1%@NL@% %@NL@% >= Greater than %@AB@%JGE%@AE@% or %@AB@%JNL%@AE@% SF = OF %@AB@%JAE%@AE@% or %@AB@%JNB%@AE@% CF = 0 or equal%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX15.28 @%%@CR:IX15.29 @% Internally, the %@AB@%CMP%@AE@% instruction is exactly the same as the %@AB@%SUB%@AE@%%@EH@% instruction, except that the destination operand is not changed. The flags are set according to the result that would have been generated by a subtraction.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; If CX is less than -20, then make DX 30, else make DX 20%@AE@%%@NL@% %@NL@% %@AS@% cmp cx,-20 ; If signed CX is smaller than -20%@AE@%%@NL@% %@AS@% jl less ; then do stuff at "less"%@AE@%%@NL@% %@AS@% mov dx,20 ; Else set DX to 20%@AE@%%@NL@% %@AS@% jmp skip ; Finished%@AE@%%@NL@% %@AS@%less: mov dx,30 ; Then set DX to 30%@AE@%%@NL@% %@AS@%skip:%@AE@%%@NL@% %@NL@% %@4@% Example 1 shows the basic form of conditional jumps. Notice that in%@EH@% assembly language, if-then-else constructions are usually written in the form if-else-then.%@NL@% %@NL@% %@4@% This theme has many variations. For example, you may find it more mnemonic%@EH@% to code in the if-then-else format. However, you must then use the opposite jump condition, as shown in Example 2.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; If CX is greater than or equal to -20, then make DX 20, else make DX 30%@AE@%%@NL@% %@NL@% %@AS@% cmp cx,-20 ; If signed CX is smaller than -20%@AE@%%@NL@% %@AS@% jnl notless ; else do stuff at "notless"%@AE@%%@NL@% %@AS@% mov dx,30 ; Then set DX to 30%@AE@%%@NL@% %@AS@% jmp continue ; Finished%@AE@%%@NL@% %@AS@%notless: mov dx,20 ; Else set DX to 20%@AE@%%@NL@% %@AS@%continue:%@AE@%%@NL@% %@NL@% %@4@% The then-if-else format shown in Example 3 is often more efficient. Do the%@EH@% work for the most likely case, and then compare for the opposite condition. If the condition is true, you are finished.%@NL@% %@NL@% %@4@% %@AB@%Example 3%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; DX is 20, unless CX is less than -20, then make DX 30%@AE@%%@NL@% %@NL@% %@AS@% mov dx,20 ; DX is 20%@AE@%%@NL@% %@AS@% cmp cx,-20 ; If signed CX is greater than -20%@AE@%%@NL@% %@AS@% jge greatequ ; then done%@AE@%%@NL@% %@AS@% mov dx,30 ; Else set DX to 30%@AE@%%@NL@% %@AS@%greatequ:%@AE@%%@NL@% %@NL@% %@4@% This example avoids the unconditional jump used in Examples 1 and 2 and%@EH@% thus is faster even if the less likely condition is true.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.1.2.2 @%%@AB@%15.1.2.2 Jumping Based on Flag Status%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.30 @%%@CR:IX15.31 @% The %@AB@%CMP%@AE@% instruction is the most mnemonic way to set the flags for%@EH@% conditional jumps, but any instruction that changes flags can be used as the test condition. The conditional-jump instructions listed below enable you to jump based on the condition of flags rather than on relationships of operands. Some of these instructions have the same effect as instructions listed in Table 15.1.%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Action%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX15.32 @% %@AB@%JO%@AE@% Jumps if the overflow flag is set%@NL@% %@NL@% %@AB@%JNO%@AE@% Jumps if the overflow flag is clear%@NL@% %@NL@% %@AB@%JC%@AE@% Jumps if the carry flag is set (same as %@AB@%JB%@AE@%)%@NL@% %@NL@% %@AB@%JNC%@AE@% Jumps if the carry flag is clear (same as %@AB@%JAE%@AE@%)%@NL@% %@NL@% %@AB@%JZ%@AE@% Jumps if the zero flag is set (same as %@AB@%JE%@AE@%)%@NL@% %@NL@% %@AB@%JNZ%@AE@% Jumps if the zero flag is clear (same as %@AB@%JNE%@AE@%)%@NL@% %@NL@% %@AB@%JS%@AE@% Jumps if the sign flag is set%@NL@% %@NL@% %@AB@%JNS%@AE@% Jumps if the sign flag is clear%@NL@% %@NL@% %@AB@%JP%@AE@% Jumps if the parity flag is set%@NL@% %@NL@% %@AB@%JNP%@AE@% Jumps if the parity flag is clear%@NL@% %@NL@% %@AB@%JPE%@AE@% Jumps if parity is even (parity flag set)%@NL@% %@NL@% %@AB@%JPO%@AE@% Jumps if parity is odd (parity flag clear)%@NL@% %@NL@% %@AB@%JCXZ%@AE@% Jumps if %@AB@%CX%@AE@% is 0%@NL@% %@NL@% %@NL@% %@4@% Notice that %@AB@%JCXZ%@AE@% is the only conditional jump based on the condition of a%@EH@% register (CX) rather than flags. Since %@AB@%JCXZ%@AE@% is usually used with loop instructions, it is discussed in more detail in Section 15.2%@BO: c265d@%, "Looping."%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% add ax,bx ; Add two values%@AE@%%@NL@% %@AS@% jo overflow ; If value too large, adjust%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%overflow: ; Adjustment routine here%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% sub ax,dx ; Subtract%@AE@%%@NL@% %@AS@% jnz skip ; If the result is not zero, continue%@AE@%%@NL@% %@AS@% call zhandler ; else do special case%@AE@%%@NL@% %@AS@%skip:%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.1.2.3 @%%@AB@%15.1.2.3 Testing Bits and Jumping%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.33 @%%@CR:IX15.34 @% Like the %@AB@%CMP%@AE@% instruction, the %@AB@%TEST%@AE@% instruction is designed to test for%@EH@% conditional jumps. However, specific bits are compared rather than entire operands.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%TEST%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@AB@%,%@AE@%{%@AI@%register%@AE@% | %@AI@%memory%@AE@% | %@AI@%immediate%@AE@%}%@EH@%%@NL@% %@NL@% %@4@% The destination operand can be %@AI@%memory%@AE@% or %@AI@%register%@AE@%. The source operand can%@EH@% be %@AI@%immediate%@AE@%, %@AI@%memory%@AE@%, or %@AI@%register%@AE@%. However, they cannot both be memory operands.%@NL@% %@NL@% %@4@%%@CR:IX15.35 @%%@CR:IX15.36 @% Normally, one of the operands is a mask in which the bits to be tested are%@EH@% the only bits set. The other operand contains the value to be tested. If all the bits set in the mask are clear in the operand being tested, the zero flag will be set. If any of the flags set in the mask are also set in the operand, the zero flag will be cleared.%@NL@% %@NL@% %@4@%%@CR:IX15.37 @%%@CR:IX15.38 @% The %@AB@%TEST%@AE@% instruction is actually the same as the %@AB@%AND%@AE@% instruction, except%@EH@% that neither operand is changed. If the result of the operation is 0, the zero flag is set, but the 0 is not actually written to the destination operand.%@NL@% %@NL@% %@4@%%@CR:IX15.39 @%%@CR:IX15.40 @% You can use the %@AB@%JZ%@AE@% and %@AB@%JNZ%@AE@% instructions to jump after the test. %@AB@%JE%@AE@% and %@AB@%JNE%@AE@%%@EH@% are the same and can be used if you find them more mnemonic.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%bits DB ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%; If bit 2 or bit 4 is set, then call taska%@AE@%%@NL@% %@NL@% %@AS@% ; Assume "bits" is 0D3h 11010011%@AE@%%@NL@% %@AS@% test bits,10100b ; If 2 or 4 is set AND 00010100%@AE@%%@NL@% %@AS@% jz skip1 ; Else continue --------%@AE@%%@NL@% %@AS@% call taska ; Then call taska 00010000%@AE@%%@NL@% %@AS@%skip1: ; Jump not taken%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%; If bits 2 and 4 are clear, then call taskb%@AE@%%@NL@% %@NL@% %@AS@% ; Assume "bits" is 0E9h 11101001%@AE@%%@NL@% %@AS@% test bits,10100b ; If 2 and 4 are clear AND 00010100%@AE@%%@NL@% %@AS@% jnz skip2 ; Else continue --------%@AE@%%@NL@% %@AS@% call taskb ; Then call taskb 00000000%@AE@%%@NL@% %@AS@%skip2: ; Jump not taken%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC15.2 @%%@AB@%15.2 Looping%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The 8086-family processors have several instructions specifically designed%@EH@% for creating loops of repeated instructions. In addition, you can create loops using conditional jumps.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LOOP%@AE@% %@AI@%label%@AE@%%@EH@%%@NL@% %@AB@%LOOPE%@AE@% %@AI@%label%@AE@%%@NL@% %@AB@%LOOPZ %@AE@%%@AI@%label%@AE@%%@NL@% %@AB@%LOOPNE%@AE@% %@AI@%label%@AE@%%@NL@% %@AB@%LOOPNZ%@AE@% %@AI@%label%@AE@%%@NL@% %@AB@%JCXZ%@AE@% %@AI@%label%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX15.41 @%%@CR:IX15.42 @%%@CR:IX15.43 @%%@CR:IX15.44 @%%@CR:IX15.45 @%%@CR:IX15.46 @% The %@AB@%LOOP%@AE@% instruction is used for loops with a set number of iterations.%@EH@% For example, it can be used in constructions similar to the "for" loops of BASIC, C, and Pascal, and the "do" loops of FORTRAN.%@NL@% %@NL@% %@4@% A single operand specifies the address to jump to each time through the%@EH@% loop. The CX register is used as a counter for the number of times to loop. On each iteration, CX is decremented. When CX reaches 0, control passes to the instruction after the loop.%@NL@% %@NL@% %@4@%%@CR:IX15.47 @%%@CR:IX15.48 @%%@CR:IX15.49 @% The%@AB@% LOOPE%@AE@%, %@AB@%LOOPZ%@AE@%, %@AB@%LOOPNE%@AE@%, and %@AB@%LOOPNZ%@AE@% instructions are used in loops that%@EH@% check for a condition. For example, they can be used in constructions similar to the "while" loops of BASIC, C, and Pascal; the "repeat" loops of Pascal; and the "do" loops of C.%@NL@% %@NL@% %@4@%%@CR:IX15.50 @%%@CR:IX15.51 @%%@CR:IX15.52 @%%@CR:IX15.53 @%%@CR:IX15.54 @%%@CR:IX15.55 @% The %@AB@%LOOPE%@AE@% (also called %@AB@%LOOPZ%@AE@%) instruction can be thought of as meaning%@EH@% "loop while equal." Similarly, the %@AB@%LOOPNE%@AE@% (also called %@AB@%LOOPNZ%@AE@%) instruction can be thought of as meaning "loop while not equal." A single short memory operand specifies the address to loop to each time through. The CX register can specify a maximum number of times to go through the loop. The CX register can be set to a number that is out of range if you do not want a maximum count.%@NL@% %@NL@% %@4@%%@CR:IX15.56 @%%@CR:IX15.57 @% The %@AB@%JCXZ%@AE@% instruction is often used in loop structures. For example, it may%@EH@% be used in loops that check a condition at the start of the loop rather than at the end. Unlike the loop instruction, %@AB@%JCXZ%@AE@% does not decrement CX, so the programmer must use another statement to decrement the count. You can also use %@AB@%JCX2%@AE@% with string instructions, as described in Chapter 16%@BO: d1833@%, "Processing Strings."%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; For 0 to 200 do task%@AE@%%@NL@% %@NL@% %@AS@% mov cx,200 ; Set counter%@AE@%%@NL@% %@AS@%next: . ; Do the task here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop next ; Do again%@AE@%%@NL@% %@AS@% ; Continue after loop%@AE@%%@NL@% %@NL@% %@4@% This loop has the same effect as the following statements:%@EH@%%@NL@% %@NL@% %@AS@%; For 0 to 200, do task%@AE@%%@NL@% %@NL@% %@AS@% mov cx,200 ; Set counter%@AE@%%@NL@% %@AS@%next: .%@AE@%%@NL@% %@AS@% . ; Do the task here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% dec cx%@AE@%%@NL@% %@AS@% cmp cx,0%@AE@%%@NL@% %@AS@% jne next ; Do again%@AE@%%@NL@% %@AS@% ; Continue after loop%@AE@%%@NL@% %@NL@% %@4@% The first version is more efficient as well as easier to understand.%@EH@% However, there are situations in which you must use conditional-jump instructions rather than loop instructions. For example, conditional jumps are often required for loops that test several conditions.%@NL@% %@NL@% %@4@% If the counter in CX is variable because of previous instructions, you%@EH@% should use the %@AB@%JCXZ%@AE@% instruction to check for 0, as shown in Example 2. Otherwise, if CX is 0, it will be decremented to -1 in the first iteration and will continue through 65,535 iterations before it reaches 0 again.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; For 0 to CX do task%@AE@%%@NL@% %@NL@% %@AS@% ; CX counter set previously%@AE@%%@NL@% %@AS@% jcxz done ; Check for 0%@AE@%%@NL@% %@AS@%next: . ; Do the task here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% loop next ; Do again%@AE@%%@NL@% %@AS@%done: ; Continue after loop%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC15.3 @%%@AB@%15.3 Using Procedures%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.58 @%%@CR:IX15.59 @% A "procedure" is a program subdivision that typically executes a specific%@EH@% task. Once you write a procedure, you can execute it from anywhere in the program. This technique lets you avoid writing the same block of code over and over, thus saving space.%@NL@% %@NL@% %@4@% Even if you execute it only once, writing a procedure can be a useful way%@EH@% of dividing a large program into manageable units. You can place a procedure in its own source module and test it separately. Assembly-language procedures are comparable to functions in C; subprograms, functions, and subroutines in BASIC; procedures and functions in Pascal; or routines and functions in FORTRAN.%@NL@% %@NL@% %@4@%%@CR:IX15.60 @% Two instructions control the use of assembly-language procedures. The %@AB@%CALL%@AE@%%@EH@% instruction can appear anywhere in a program. It temporarily transfers program control to a specified procedure. The %@AB@%RET%@AE@% instruction appears at the end of a procedure. It returns control back to the location that issued the call.%@NL@% %@NL@% %@4@% These instructions use the stack to properly return from each call. The%@EH@% instruction immediately following the %@AB@%CALL%@AE@% instruction is called the "return address," and the procedure should return to this location when done. %@AB@%CALL%@AE@% pushes the return address onto the stack; %@AB@%RET%@AE@% pops this address off the stack and transfers program control there.%@NL@% %@NL@% %@4@% Along with the%@AB@% RET%@AE@% instruction (which terminates a procedure), two%@EH@% directives help define a procedure. The %@AB@%PROC%@AE@% and %@AB@%ENDP%@AE@% directives normally mark the beginning and end of a procedure definition, as described in Section 15.3.2%@BO: c4c26@%, "Defining Procedures."%@NL@% %@NL@% %@4@% In addition, the %@AB@%PROC%@AE@% directive can save you time and effort by automating%@EH@% the following tasks:%@NL@% %@NL@% ■ Preserving register values that should not change, but that the procedure might otherwise alter%@NL@% %@NL@% ■ Setting up a framepointer, so that you can access parameters placed on the stack%@NL@% %@NL@% ■ Creating text macros, so that your source code can refer to each parameter by a meaningful name%@NL@% %@NL@% %@4@% Section 15.3.4%@BO: c7284@%, "Declaring Parameters with the PROC Directive," describes%@EH@% how to use these features. Section 15.3.3%@BO: c60b0@%, "Passing Arguments on the Stack," gives background information on the technique for accessing parameters.%@NL@% %@NL@% %@4@% When you write procedures, you can create local variables, which exist%@EH@% only during execution of the procedure. The advantage of these variables is that they use memory dynamically, taking up space only in the procedure that uses them. Section 15.3.5%@BO: c9deb@%, "Using Local Variables," describes the basic technique for allocating and accessing local variables. Section 15.3.6%@BO: cb138@%, "Creating Locals Automatically," describes how to make the assembler generate the necessary code for you.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.1 @%%@AB@%15.3.1 Calling Procedures%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.61 @%%@CR:IX15.62 @% The %@AB@%CALL%@AE@% instruction saves the address following the instruction on the%@EH@% stack and passes control to a specified address.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%CALL%@AE@% {%@AI@%register%@AE@% | %@AI@%memory%@AE@%}%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.63 @% The address is usually specified as a direct memory operand. However, the%@EH@% operand can also be a register or indirect memory operand containing a value calculated at run time. This enables you to write call tables similar to the jump table illustrated in Section 15.1.2.1%@BO: bf2c9@%, "Comparing and Jumping."%@NL@% %@NL@% %@4@% Calls can be near or far. Near calls push only the offset portion of the%@EH@% calling address. Far calls push both the segment and offset. You must give the type of far calls to forward-referenced labels using the %@AB@%FAR%@AE@% type specifier and the %@AB@%PTR%@AE@% operator. For example, use the following statement to make a far call to a label that has not been earlier defined or declared external in the source code:%@NL@% %@NL@% %@AS@% call FAR PTR task%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.2 @%%@AB@%15.3.2 Defining Procedures%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Procedures are defined by labeling the start of the procedure and placing%@EH@% an %@AB@%ENDP%@AE@% directive at the end. The code should not fall through past the end of the procedure. Exit the procedure with a%@AB@% RET%@AE@%,%@AB@% RETF%@AE@%, %@AB@%RETN%@AE@%, or %@AB@%IRET%@AE@% instruction. There are several variations of this syntax.%@NL@% %@NL@% %@4@% %@AB@%Syntax 1%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@% %@AB@%PROC%@AE@% [[%@AB@%NEAR%@AE@%|%@AB@%FAR%@AE@%]]%@AB@% RET%@AE@% [[%@AI@%constant%@AE@%]] %@AI@%label%@AE@% %@AB@%ENDP%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.64 @%%@CR:IX15.65 @%%@CR:IX15.66 @% Procedures are normally defined by using the %@AB@%PROC%@AE@% directive at the start%@EH@% of the procedure and the %@AB@%ENDP%@AE@% directive at the end. The %@AB@%RET%@AE@% instruction is normally placed immediately before the %@AB@%ENDP%@AE@% directive. The size of the %@AB@%RET%@AE@% instruction automatically matches the size defined by the %@AB@%PROC%@AE@% directive.%@NL@% %@NL@% %@4@% The syntax shown is always available. In addition, there is an extended%@EH@% %@AB@%PROC%@AE@% syntax available if you use %@AB@%.MODEL%@AE@% and specify a language. The extended %@AB@%PROC%@AE@% syntax is explained in Section 15.3.4%@BO: c7284@%, "Declaring Parameters with the PROC Directive." These language features automate many of the details of accessing parameters and saving registers.%@NL@% %@NL@% %@4@% %@AB@%Syntax 2%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@%%@AB@%:%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%RETN%@AE@% [[%@AI@%constant%@AE@%]]%@NL@% %@NL@% %@4@% %@AB@%Syntax 3%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@% %@AB@%LABEL FAR%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%RETF%@AE@% [[%@AI@%constant%@AE@%]]%@NL@% %@NL@% %@4@%%@CR:IX15.67 @%%@CR:IX15.68 @%%@CR:IX15.69 @%%@CR:IX15.70 @% The %@AB@%RET%@AE@% instruction can be extended to %@AB@%RETN%@AE@% (Return Near) or %@AB@%RETF%@AE@% (Return%@EH@% Far) to override the default size. This enables you to define and use procedures without the %@AB@%PROC%@AE@% and %@AB@%ENDP%@AE@% directives, as shown in Syntax 2 and Syntax 3, above. However, with this method, the programmer is responsible for making sure the size of the %@AB@%CALL%@AE@% matches the size of the %@AB@%RET%@AE@%.%@NL@% %@NL@% %@4@% The %@AB@%RET%@AE@% instruction (and its %@AB@%RETF%@AE@% and %@AB@%RETN%@AE@% variations) allows a constant%@EH@% operand that specifies a number of bytes to be added to the value of the SP register after the return. This operand can be used to adjust for arguments passed to the procedure before the call, as shown in the example in Section 15.3.5%@BO: c9deb@%, "Using Local Variables."%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% call task ; Call is near because procedure is near%@AE@%%@NL@% %@AS@% . ; Return comes to here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%task PROC NEAR ; Define "task" to be near%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Instructions of "task" go here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret ; Return to instruction after call%@AE@%%@NL@% %@AS@%task ENDP ; End "task" definition%@AE@%%@NL@% %@NL@% %@4@% Example 1 shows the recommended way of making calls with QuickAssembler.%@EH@% Example 2 shows another method that programmers who are used to other assemblers may find more familiar.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% call NEAR PTR task ; Call is declared near%@AE@%%@NL@% %@AS@% . ; Return comes to here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%task: ; Procedure begins with near label%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Instructions go here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% retn ; Return declared near%@AE@%%@NL@% %@NL@% %@4@% This method gives more direct control over procedures, but the programmer%@EH@% must make sure that calls have the same size as corresponding returns.%@NL@% %@NL@% %@4@% For example, if a call is made with the statement%@EH@%%@NL@% %@NL@% %@AS@% call NEAR PTR task%@AE@%%@NL@% %@NL@% %@4@% the assembler does a near call. This means that one word (the offset%@EH@% following the calling address) is pushed onto the stack. If the return is made with the statement%@NL@% %@NL@% %@AS@% retf%@AE@%%@NL@% %@NL@% %@4@% two words are popped off the stack. The first will be the offset, but the%@EH@% second will be whatever happened to be on the stack before the call. Not only will the popped value be meaningless, but the stack status will be incorrect, causing the program to fail.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.3 @%%@AB@%15.3.3 Passing Arguments on the Stack%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.71 @%%@CR:IX15.72 @% Procedure arguments can be passed in various ways. For example, values can%@EH@% be passed to a procedure in registers or in variables. However, the most common method of passing arguments is to use the stack. Microsoft languages have a specific convention for doing this.%@NL@% %@NL@% %@4@% This section describes how a procedure accesses the parameters passed to%@EH@% it on the stack. Each parameter is accessed as an offset from BP, and you must calculate this offset. However, if you use the %@AB@%PROC%@AE@% directive to declare parameters, the assembler calculates these offsets for you and lets you refer to parameters by name. The next section explains how to use %@AB@%PROC%@AE@% this way.%@NL@% %@NL@% %@4@% The arguments are pushed onto the stack before the call. After the call,%@EH@% the procedure retrieves and processes them. At the end of the procedure, the stack is adjusted to account for the arguments.%@NL@% %@NL@% %@4@%%@CR:IX15.73 @% Although the same basic method is used for all Microsoft high-level%@EH@% languages, the details vary. For instance, in some languages, pointers to the arguments are passed to the procedure; in others, the arguments themselves are passed. The order in which arguments are passed (whether the first argument is pushed first or last) also varies according to the language. Finally, in some languages, the stack is adjusted by the %@AB@%RET%@AE@% instruction in the called procedure; in others, the code immediately following the %@AB@%CALL%@AE@% instruction adjusts the stack. See Appendix A%@BO: ed697@%, "Mixed-Language Mechanics," for details on calling conventions.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; C-style procedure call and definition%@AE@%%@NL@% %@NL@% %@AS@% mov ax,10 ; Load and%@AE@%%@NL@% %@AS@% push ax ; push constant as third argument%@AE@%%@NL@% %@AS@% push arg2 ; Push memory as second argument%@AE@%%@NL@% %@AS@% push cx ; Push register as first argument%@AE@%%@NL@% %@AS@% call addup ; Call the procedure%@AE@%%@NL@% %@AS@% add sp,6 ; Destroy the pushed arguments%@AE@%%@NL@% %@AS@% . ; (equivalent to three pops)%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%addup PROC NEAR ; Return address for near call%@AE@%%@NL@% %@AS@% ; takes two bytes%@AE@%%@NL@% %@AS@% push bp ; Save base pointer - takes two bytes%@AE@%%@NL@% %@AS@% ; so arguments start at 4th byte%@AE@%%@NL@% %@AS@% mov bp,sp ; Load stack into base pointer%@AE@%%@NL@% %@AS@% mov ax,[bp+4] ; Get first argument from%@AE@%%@NL@% %@AS@% ; 4th byte above pointer%@AE@%%@NL@% %@AS@% add ax,[bp+6] ; Add second argument from%@AE@%%@NL@% %@AS@% ; 6th byte above pointer%@AE@%%@NL@% %@AS@% add ax,[bp+8] ; Add third argument from%@AE@%%@NL@% %@AS@% ; 8th byte above pointer%@AE@%%@NL@% %@AS@% pop bp ; Restore BP%@AE@%%@NL@% %@AS@% ret ; Return result in AX%@AE@%%@NL@% %@AS@%addup ENDP%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX15.74 @%%@CR:IX15.75 @% The example shows one method of passing arguments to a procedure. This%@EH@% method is similar to the way procedures are called in the C language. Figure 15.1 shows the stack condition at key points in the process.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 15.3.3 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Arguments passed on the stack in assembler routines cannot be accessed by name in debugging commands, unless you declare parameters with the %@AB@%PROC%@AE@% directive, as explained in the next section.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.4 @%%@AB@%15.3.4 Declaring Parameters with the PROC Directive%@AE@%%@EH@%%@NL@% %@NL@% %@4@% This section describes how to use the%@AB@% PROC%@AE@% directive in order to automate%@EH@% the parameter-accessing techniques described in the last section.%@NL@% %@NL@% %@4@% The %@AB@%PROC%@AE@% directive lets you specify registers to be saved, define%@EH@% arguments to the procedure, and set up text macros so that you can refer to parameters by name (rather than as an offset to BP). For example, the following %@AB@%PROC%@AE@% directive could be placed at the beginning of a procedure called from BASIC that takes a single argument passed by value and that uses (and must save) the DI and SI registers:%@NL@% %@NL@% %@AS@%myproc PROC FAR BASIC USES DI SI, arg1:WORD%@AE@%%@NL@% %@NL@% %@4@% Note that you must use the %@AB@%.MODEL%@AE@% directive and specify a language in%@EH@% order to use the extended features of %@AB@%PROC%@AE@%, including the %@AI@%lang%@AE@% type, %@AI@%reglist%@AE@%, and %@AI@%arguments%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@% %@AB@%PROC%@AE@% [[%@AB@%NEAR%@AE@%|%@AB@%FAR%@AE@%]] [[%@AI@%lang%@AE@%]] [[%@AB@%USES %@AE@%%@AI@%reglist%@AE@%]] [[%@AI@%arguments%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@% The%@AB@% NEAR %@AE@%and %@AB@%FAR%@AE@% keywords indicate whether you invoke the procedure with a%@EH@% near call or a far call, as described in Section 15.3.2%@BO: c4c26@%, "Defining Procedures."%@NL@% %@NL@% %@4@% The following list describes the other parts of the %@AB@%PROC%@AE@% directive:%@EH@%%@NL@% %@NL@% %@AB@%Argument%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AI@%label%@AE@% The name of the procedure. The assembler automatically adds an underscore to the beginning of the name if you specify %@AB@%C%@AE@% as the language in the %@AB@%.MODEL%@AE@% directive or if you specify%@AB@% C %@AE@%as the %@AI@%lang%@AE@%.%@NL@% %@NL@% %@CR:IX15.76 @%%@CR:IX15.77 @%%@CR:IX15.78 @% %@AI@%lang%@AE@% An optional language specifier that overrides language conventions specified by the %@AB@%.MODEL%@AE@% directive. The language type may be %@AB@%C%@AE@%, %@AB@%Pascal%@AE@%, %@AB@%FORTRAN%@AE@%, or %@AB@%BASIC%@AE@%.%@NL@% %@NL@% The language type determines the calling convention used to access parameters and restore the stack. It also determines whether an underscore is prefixed to the procedure name, as required by the C naming convention. Note that use of the %@AB@%C%@AE@% specifier does not preserve lowercase letters in the procedure name. To guarantee compatibility with C naming conventions, choose Preserve Case or Preserve Extrn from the Assembler Flags dialog box, or assemble with /Cl or /Cx from the QCL command line.%@NL@% %@NL@% %@CR:IX15.79 @% %@AI@%reglist%@AE@% A list of registers that the procedure uses and that should be saved on entry. Registers in the list must be separated by blanks or tabs. The assembler generates code to push these registers on the stack. When you exit, the assembler generates code to pop the saved register values off the stack.%@NL@% %@NL@% %@AI@%arguments%@AE@% The list of arguments passed to the procedure on the stack. See the discussion below for the syntax of the %@AI@%argument%@AE@%.%@NL@% %@NL@% %@NL@% %@4@% The %@AI@%arguments%@AE@% indicate each of the procedure's arguments and are separated%@EH@% from the %@AI@%reglist%@AE@% argument by a comma if there is a list of registers. Each %@AI@%argument%@AE@% has the following syntax:%@NL@% %@NL@% %@4@% %@AI@%argname%@AE@% [[%@AB@% :%@AE@%[[[[%@AB@%NEAR%@AE@%|%@AB@%FAR%@AE@%]]%@AB@%PTR%@AE@%]]%@AI@%type%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.80 @% If you have more than one argument, separate each by a comma.%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%argname%@AE@% is the name of the argument. The %@AI@%type%@AE@% is the type of the%@EH@% argument and may be %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%,%@AB@% QWORD%@AE@%,%@AB@% TBYTE%@AE@%, or the name of a structure defined by a %@AB@%STRUC%@AE@% structure declaration (see Chapter 6%@BO: 5749b@%, "Defining Labels, Constants, and Variables" for more information about types). If you omit %@AI@%type%@AE@%, the default is the%@AB@% WORD %@AE@%type.%@NL@% %@NL@% %@4@% The%@AB@% FAR%@AE@%,%@AB@% NEAR%@AE@%, %@AB@%PTR%@AE@%, and %@AI@%type%@AE@% arguments are all optional. If you omit all%@EH@% of them, the assembler assumes the variable is a %@AB@%WORD%@AE@% type. If you use only the %@AI@%type%@AE@% argument, the assembler assumes the variable has the indicated type.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%Note%@AE@% If you are writing a routine to be called from BASIC, FORTRAN, or Pascal, and the routine returns a function value, you must declare an additional parameter if you return anything other than a two- or four-byte integer. See Appendix A%@BO: ed697@%, "Mixed-Language Mechanics," for more information.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@%%@CR:IX15.81 @%%@CR:IX15.82 @% The %@AB@%PTR%@AE@% type generates debugging information so that the variable is%@EH@% treated as a pointer during debugging. The assembler assumes specific sizes for the variable, depending on the combination of %@AB@%NEAR%@AE@%, %@AB@%FAR%@AE@%, and %@AB@%PTR%@AE@% arguments you specify. The lines below show some example combinations of %@AB@%NEAR%@AE@%,%@AB@% FAR%@AE@%, %@AB@%PTR%@AE@%, and %@AI@%type%@AE@%:%@NL@% %@NL@% %@AS@%myproc PROC var1:PTR WORD, var2:PTR DWORD%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%myproc ENDP%@AE@%%@NL@% %@NL@% %@AS@%proc2 PROC var3:FAR PTR WORD, var4:NEAR PTR BYTE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%proc2 ENDP%@AE@%%@NL@% %@NL@% %@4@% If you omit %@AB@%NEAR%@AE@% or %@AB@%FAR%@AE@%, the default data size established by .%@AB@%MODEL%@AE@% is%@EH@% used. All %@AB@%PTR%@AE@% declarations are translated into a word-size variable if the data size is near or a doubleword variable if the data size is far.%@NL@% %@NL@% %@4@% For example, the following declarations of %@AS@%procvar %@AE@%produce the same code%@EH@% for the variable name, although they generate different debugging information:%@NL@% %@NL@% %@AS@%aproc PROC procvar:PTR WORD%@AE@%%@NL@% %@NL@% %@AS@%aproc PROC procvar:PTR DWORD%@AE@%%@NL@% %@NL@% %@AS@%aproc PROC procvar:PTR BYTE%@AE@%%@NL@% %@NL@% %@4@% Specifying a particular type changes only the debugging information, not%@EH@% the code produced for accessing the argument.%@NL@% %@NL@% %@4@% If you specify a%@AB@% NEAR PTR%@AE@% or %@AB@%FAR PTR%@AE@% argument, as in the declarations of%@EH@% %@AS@%var3 %@AE@%and %@AS@%var4%@AE@%, the assembler ignores the memory model you selected and assigns a %@AB@%WORD%@AE@% type for a%@AB@% NEAR PTR%@AE@% argument and a %@AB@%DWORD%@AE@% type for a %@AB@%FAR PTR%@AE@% argument.%@NL@% %@NL@% %@4@% The assembler does not generate any code to get the value or values the%@EH@% pointer references; your program must still explicitly treat the argument as a pointer. For example, the procedure in Section 5.1%@BO: 406ed@% can be rewritten for use with BASIC so that it gets its argument by near reference (the BASIC default):%@NL@% %@NL@% %@AS@%; Call from BASIC as a FUNCTION returning an integer%@AE@%%@NL@% %@NL@% %@AS@% .MODEL medium, basic%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%myadd PROC arg1:NEAR PTR WORD, arg2:NEAR PTR WORD%@AE@%%@NL@% %@NL@% %@AS@% mov bx,arg1 ; Load first argument%@AE@%%@NL@% %@AS@% mov ax,[bx]%@AE@%%@NL@% %@AS@% mov bx,arg2 ; Add second argument%@AE@%%@NL@% %@AS@% add ax,[bx]%@AE@%%@NL@% %@NL@% %@AS@% ret%@AE@%%@NL@% %@NL@% %@AS@%myadd ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% In the example above, even though the arguments are declared as near%@EH@% pointers, you still must code two move instructions in order to get the values of the arguments──the first move gets the address of the argument; the second move gets the argument.%@NL@% %@NL@% %@4@% You can use conditional-assembly directives to make sure that your pointer%@EH@% arguments are loaded correctly for the memory model. For example, the following version of %@AS@%myadd %@AE@%treats the arguments as far arguments if necessary:%@NL@% %@NL@% %@AS@% .MODEL medium,c ;Could be any model%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%myadd PROC arg1:PTR WORD, arf2:PTR WORD%@AE@%%@NL@% %@NL@% %@AS@% IF @DataSize%@AE@%%@NL@% %@AS@% les bx,arg1 ;Far arguments%@AE@%%@NL@% %@AS@% mov ax,es:[bx]%@AE@%%@NL@% %@AS@% les bx,arg2%@AE@%%@NL@% %@AS@% add ax,es:[bx]%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% mov bx,arg1 ;Near arguments%@AE@%%@NL@% %@AS@% mov ax,[bx]%@AE@%%@NL@% %@AS@% mov bx,arg2%@AE@%%@NL@% %@AS@% add ax,[bx]%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%myadd ENDP%@AE@%%@NL@% %@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%Note%@AE@% When you use the high-level-language features and the assembler encounters a %@AB@%RET %@AE@%instruction, it automatically generates instructions to pop saved registers, remove local variables from the stack, and, if necessary, remove arguments. The assembler does not generate this code if you use a %@AB@%RETF%@AE@% or %@AB@%RETN%@AE@% instruction. It generates this code for each %@AB@%RET%@AE@% instruction it encounters. You can save code by having only one exit and jumping to it from various points.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.5 @%%@AB@%15.3.5 Using Local Variables%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.83 @%%@CR:IX15.84 @% In high-level languages, local variables are known only within a%@EH@% procedure. In Microsoft languages, these variables are usually stored on the stack. Assembly-language programs can use the same technique. These variables should not be confused with labels or variable names that are local to a module, as described in Chapter 8%@BO: 70f6e@%, "Creating Programs from Multiple Modules."%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% If your procedure has relatively few variables, you can usually write the most efficient code by placing these values in registers. Local (stack) data is efficient when you have a large amount of local data for the procedure.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% This section outlines the standard methods for creating local variables.%@EH@% The next section shows how to use the %@AB@%LOCAL%@AE@% directive to make the assembler generate local variables for you automatically. When you use this directive, the assembler generates the same instructions as those used in this section, but hides some of the details from you.%@NL@% %@NL@% %@4@% If you want to use%@AB@% LOCAL%@AE@% right away, you may want to skip directly to the%@EH@% next section. However, this section gives useful background.%@NL@% %@NL@% %@4@% Local variables are created by saving stack space for the variable at the%@EH@% start of the procedure. The variable can then be accessed by its position in the stack. At the end of the procedure, the stack pointer is restored to restore the memory used by local variables.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% push ax ; Push one argument%@AE@%%@NL@% %@AS@% call task ; Call%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%arg EQU <[bp+4]> ; Name for argument%@AE@%%@NL@% %@AS@%loc EQU <[bp-2]> ; Name for local variable%@AE@%%@NL@% %@NL@% %@AS@%task PROC NEAR%@AE@%%@NL@% %@AS@% push bp ; Save base pointer%@AE@%%@NL@% %@AS@% mov bp,sp ; Load stack into base pointer%@AE@%%@NL@% %@AS@% sub sp,2 ; Save two bytes for local variable%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov loc,3 ; Initialize local variable%@AE@%%@NL@% %@AS@% add ax,loc ; Add local variable to AX%@AE@%%@NL@% %@AS@% sub arg,ax ; Subtract local from argument%@AE@%%@NL@% %@AS@% . ; Use "loc" and "arg" in other operations%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov sp,bp ; Adjust for stack variable%@AE@%%@NL@% %@AS@% pop bp ; Restore base%@AE@%%@NL@% %@AS@% ret 2 ; Return result in AX and pop%@AE@%%@NL@% %@AS@%task ENDP ; two bytes to adjust stack%@AE@%%@NL@% %@CR:IX15.85 @%%@CR:IX15.86 @%%@NL@% %@4@% In this example, two bytes are subtracted from the SP register to make%@EH@% room for a local word variable. This variable can then be accessed as %@AS@%[bp-2]%@AE@%. In the example, this value is given the name %@AS@%loc%@AE@% with a text equate. Notice that the instruction %@AS@%mov sp,bp%@AE@% is given at the end to restore the original value of SP. The statement is only required if the value of SP is changed inside the procedure (usually by allocating local variables). The argument passed to the procedure is returned with the %@AB@%RET%@AE@% instruction. Contrast this to the example in Section 15.3.3%@BO: c60b0@%, "Passing Arguments on the Stack," in which the calling code adjusts for the argument. Figure 15.2 shows the state of the stack at key points in the process.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 15.3.5 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX15.87 @% %@AB@%NOTE%@AE@% Local variables created in assembler routines cannot be accessed by name with debugging commands, unless you declare local variables with the %@AB@%LOCAL%@AE@% directive, as explained in the next section.%@NL@% %@AB@%──────────────────────────────────────────────────────────────────────────%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.6 @%%@AB@%15.3.6 Creating Locals Automatically%@AE@%%@EH@%%@NL@% %@NL@% %@4@% This section describes how to automate the techniques for local-variable%@EH@% creation described in the last section.%@NL@% %@NL@% %@4@%%@CR:IX15.88 @%%@CR:IX15.89 @% You can use the %@AB@%LOCAL%@AE@% directive to save time and effort when working with%@EH@% local variables. When you use this directive, simply list the variables you want to create, giving a type for each one. The assembler calculates how much space is required on the stack. It also generates instructions to properly decrement SP (as described in the previous section) and to later reset SP when you return from the procedure.%@NL@% %@NL@% %@4@% The %@AB@%LOCAL%@AE@% directive can only be used inside procedures created with the%@EH@% extended %@AB@%PROC%@AE@% directive. This means that you must first use %@AB@%.MODEL%@AE@% and specify a language.%@NL@% %@NL@% %@4@% When you create local variables this way, your source code can then refer%@EH@% to each local variable by name rather than as an offset. Moreover, the assembler generates debugging information for each local variable, so that you can enter the name of the local variable as part of a Watch expression.%@NL@% %@NL@% %@4@% The procedure in Section 15.3.5%@BO: c9deb@% can be generated more simply with the%@EH@% following code:%@NL@% %@NL@% %@AS@%task PROC NEAR arg:WORD%@AE@%%@NL@% %@AS@% LOCAL loc:WORD%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov loc,3 ; Initialize local variable%@AE@%%@NL@% %@AS@% add ax,loc ; Add local variable to AX%@AE@%%@NL@% %@AS@% sub arg,ax ; Subtract local from argument%@AE@%%@NL@% %@AS@% . ; Use "Loc" and "arg" in other operations%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%task ENDP%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%LOCAL%@AE@% directive has the following syntax:%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LOCAL %@AE@%%@AI@%vardef%@AE@%%@AB@% %@AE@%[[%@AB@%,%@AE@%%@AI@%vardef%@AE@%]]...%@EH@%%@NL@% %@NL@% %@4@% Each %@AI@%vardef%@AE@% has the form:%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@%[[[%@AI@%count%@AE@%]]][[%@AB@%:%@AE@%[[[[%@AB@%NEAR %@AE@%| %@AB@%FAR]]PTR%@AE@%]]%@AI@%type%@AE@%]]]]...%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%LOCAL%@AE@% directive arguments are as follows:%@EH@%%@NL@% %@NL@% %@AB@%Argument%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AI@%label%@AE@% The name given to the local variable. The assembler automatically defines a text macro you may use to access the variable.%@NL@% %@NL@% %@AI@%count%@AE@% The number of elements of this name and type to allocate on the stack. Using %@AI@%count%@AE@% allows you to allocate a simple array on the stack. The brackets around %@AI@%count%@AE@% are required. If this field is omitted, one data object is assumed.%@NL@% %@NL@% %@AI@%type%@AE@% The type of variable to allocate. The %@AI@%type%@AE@% argument may be one of the following: %@AB@%WORD%@AE@%, %@AB@%DWORD%@AE@%, %@AB@%QWORD%@AE@%, %@AB@%TBYTE%@AE@%, or the name of a structure defined by a %@AB@%STRUC%@AE@% structure declaration.%@NL@% %@NL@% %@NL@% %@4@% The assembler sets aside space on the stack, following the same rules as%@EH@% for procedure arguments.%@NL@% %@NL@% %@4@% The assembler does not initialize local variables. Your program must%@EH@% include code to perform any necessary initializations. For example, the following code fragment sets up a local array and initializes it to zero:%@NL@% %@NL@% %@AS@%arraysz EQU 20%@AE@%%@NL@% %@NL@% %@AS@%aproc PROC%@AE@%%@NL@% %@AS@% LOCAL var1[arraysz]:WORD, var2:WORD%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%; Initialize local array to zero%@AE@%%@NL@% %@AS@% mov cx,arraysz%@AE@%%@NL@% %@AS@% xor ax,ax%@AE@%%@NL@% %@AS@% xor di,di ; Use di as array index%@AE@%%@NL@% %@AS@%repeat: mov var1[di],ax%@AE@%%@NL@% %@AS@% inc di%@AE@%%@NL@% %@AS@% inc di%@AE@%%@NL@% %@AS@% loop repeat%@AE@%%@NL@% %@AS@%; Use the array...%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@AS@%aproc%@AE@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.7 @%%@AB@%15.3.7 Variable Scope%@AE@%%@EH@%%@NL@% %@NL@% %@4@% When you use the extended form of the %@AB@%.MODEL%@AE@% directive, the assembler%@EH@% makes all identifiers inside a procedure local to the procedure. Labels ending with a colon (:), procedure arguments, and local variables declared in a %@AB@%LOCAL%@AE@% directive are undefined outside of the procedure. Variables defined outside of any procedure are available inside a procedure. For example, in the following fragment, %@AS@%var1 %@AE@%can be used in %@AS@%proc1 %@AE@%and %@AS@%proc2%@AE@%, while %@AS@%var2%@AE@%──because it is defined in %@AS@%proc2%@AE@%──is not available to %@AS@%proc1%@AE@%:%@NL@% %@NL@% %@AS@% .MODEL medium,c%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%var1 DW 256 ; Available to proc1 and proc2%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%proc1 PROC%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%exit: ret%@AE@%%@NL@% %@AS@%proc1 ENDP%@AE@%%@NL@% %@NL@% %@AS@%proc2 PROC%@AE@%%@NL@% %@AS@% LOCAL var2:WORD ; This var2 only available in proc2%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%exit: ret%@AE@%%@NL@% %@AS@%proc2 ENDP%@AE@%%@NL@% %@NL@% %@4@% If %@AS@%proc1 %@AE@%contained a%@AB@% LOCAL %@AE@%directive defining %@AS@%var2%@AE@%, that %@AS@%var2 %@AE@%would be a%@EH@% completely different variable than the %@AS@%var2 %@AE@%in %@AS@%proc2%@AE@%.%@NL@% %@NL@% %@4@% Notice that both procedures contain the label %@AS@%exit%@AE@%. Because labels are%@EH@% local when you use the language option on the %@AB@%.MODEL%@AE@% directive, you may use the same labels in different procedures. You can make a label in a procedure global (make it available outside the procedure) by ending it with two colons:%@NL@% %@NL@% %@AS@%proc3 PROC%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%label1::%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%proc3 ENDP%@AE@%%@NL@% %@NL@% %@4@% In the preceding example, %@AS@%label1 %@AE@%is available throughout the file%@EH@% containing %@AS@%proc3%@AE@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.3.8 @%%@AB@%15.3.8 Setting Up Stack Frames%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.90 @%%@CR:IX15.91 @%%@CR:IX15.92 @%%@CR:IX15.93 @%%@CR:IX15.94 @% Starting with the 80186 processor, the %@AB@%ENTER%@AE@% and %@AB@%LEAVE%@AE@% instructions are%@EH@% provided for setting up a stack frame. These instructions do the same thing as the multiple instructions at the start and end of procedures in the Microsoft calling conventions (see the examples in Section 15.3.3%@BO: c60b0@%, "Passing Arguments on the Stack").%@NL@% %@NL@% %@4@% The%@AB@% PROC%@AE@% statement takes advantage of these instructions if you enable the%@EH@% extended instruction set with the%@AB@% .186%@AE@% or%@AB@% .286 %@AE@%directive.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%ENTER%@AE@% %@AI@%framesize%@AE@%%@AB@%,%@AE@% %@AI@%nestinglevel%@AE@%%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%LEAVE%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX15.95 @%%@CR:IX15.96 @%%@CR:IX15.97 @%%@CR:IX15.98 @%%@CR:IX15.99 @% The %@AB@%ENTER%@AE@% instruction takes two constant operands. The %@AI@%framesize%@AE@% (a 16-bit%@EH@% constant) specifies the number of bytes to reserve for local variables. The %@AI@%nestinglevel%@AE@% (an 8-bit constant) specifies the level at which the procedure is nested. This operand should always be 0 when writing procedures for BASIC, C, and FORTRAN. The %@AI@%nestinglevel%@AE@% can be greater than 0 with Pascal and other languages that enable procedures to access the local variables of calling procedures.%@NL@% %@NL@% %@4@% The %@AB@%LEAVE%@AE@% instruction reverses the effect of the last %@AB@%ENTER%@AE@% instruction by%@EH@% restoring BP and SP to their values before the procedure call.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%task PROC NEAR%@AE@%%@NL@% %@AS@% enter 6,0 ; Set stack frame and reserve 6%@AE@%%@NL@% %@AS@% . ; bytes for local variables%@AE@%%@NL@% %@AS@% . ; Do task here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% leave ; Restore stack frame%@AE@%%@NL@% %@AS@% ret ; Return%@AE@%%@NL@% %@AS@%task ENDP%@AE@%%@NL@% %@NL@% %@4@% Example 1 has the same effect as the code in Example 2.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%task PROC NEAR%@AE@%%@NL@% %@AS@% push bp ; Save base pointer%@AE@%%@NL@% %@AS@% mov bp,sp ; Load stack into base pointer%@AE@%%@NL@% %@AS@% sub sp,6 ; Reserve 6 bytes for local variables%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Do task here%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@AS@% mov sp,bp ; Restore stack pointer%@AE@%%@NL@% %@AS@% pop bp ; Restore base%@AE@%%@NL@% %@AS@% ret ; Return%@AE@%%@NL@% %@AS@%task ENDP%@AE@%%@NL@% %@NL@% %@4@% The code in Example 1 takes fewer bytes, but is slightly slower. See%@EH@% on-line Help on instructions for exact comparisons of size and timing.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC15.4 @%%@AB@%15.4 Using Interrupts%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.100 @% "Interrupts" are a special form of routines that are called by number%@EH@% instead of by address. They can be initiated by hardware devices as well as by software. Hardware interrupts are called automatically whenever certain events occur in the hardware.%@NL@% %@NL@% %@4@% Interrupts can have any number from 0 to 255. Most of the interrupts with%@EH@% lower numbers are reserved for use by the processor, DOS, or the ROM BIOS.%@NL@% %@NL@% %@4@% The programmer can call existing interrupts with the %@AB@%INT%@AE@% instruction.%@EH@% Interrupt routines can also be defined or redefined to be called later. For example, an interrupt routine that is called automatically by a hardware device can be redefined so that its action is different.%@NL@% %@NL@% %@4@% DOS defines several interrupt handlers. Two that are sometimes used by%@EH@% applications programmers are listed below:%@NL@% %@NL@% %@AB@%Interrupt%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX15.101 @% 0 Divide overflow. Called automatically when the quotient of a divide operation is too large for the source operand or when a divide by zero is attempted.%@NL@% %@NL@% %@CR:IX15.102 @% 4 Overflow. Called by the %@AB@%INTO%@AE@% instruction if the overflow flag is set.%@NL@% %@NL@% %@NL@% %@4@% Interrupt 21H is the normal method of using DOS functions. To call a%@EH@% function, place the function number in AH, put arguments in registers as appropriate, then call the interrupt. For complete documentation of DOS functions, see the %@AI@%Microsoft%@AE@% %@AI@%MS-DOS%@AE@% %@AI@%Programmer's%@AE@% %@AI@%Reference%@AE@%, one of the many other books on DOS functions, or the on-line Help system.%@NL@% %@NL@% %@4@% DOS has several other interrupts, but they should not normally be called.%@EH@% Some (such as 20H and 27H) have been replaced by DOS functions. Others are used internally by DOS.%@NL@% %@NL@% %@4@%%@CR:IX15.103 @% You can also access ROM-BIOS services through interrupt calls. See the%@EH@% on-line Help system for a description of all these services.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.4.1 @%%@AB@%15.4.1 Calling Interrupts%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.104 @%%@CR:IX15.105 @%%@CR:IX15.106 @%%@CR:IX15.107 @% Interrupts are called with the %@AB@%INT%@AE@% instruction.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%INT%@AE@% %@AI@%interruptnumber%@AE@%%@EH@%%@NL@% %@AB@%INTO%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%INT%@AE@% instruction takes an immediate operand with a value between 0 and%@EH@% 255.%@NL@% %@NL@% %@4@%%@CR:IX15.108 @%%@CR:IX15.109 @% When calling DOS and ROM-BIOS interrupts, a function number is usually%@EH@% placed in the AH register. Other registers may be used to pass arguments to functions. Some interrupts and functions return values in certain registers. Register use varies for each interrupt.%@NL@% %@NL@% %@4@% When the instruction is called, the processor takes the following six%@EH@% steps:%@NL@% %@NL@% 1. Looks up the address of the interrupt routine in the interrupt descriptor table. In real mode, this table starts at the lowest point in memory (segment 0, offset 0) and consists of four bytes (two segment and two offset) for each interrupt. Thus, the address of an interrupt routine can be found by multiplying the number of the interrupt by 4.%@NL@% %@NL@% 2. Pushes the flags register, the current code segment (CS), and the current instruction pointer (IP).%@NL@% %@NL@% %@CR:IX15.110 @%%@CR:IX15.111 @% 3. Clears the trap (TF) and interrupt enable (IF) flags.%@NL@% %@NL@% 4. Jumps to the address of the interrupt routine, as specified in the interrupt description table.%@NL@% %@NL@% %@CR:IX15.112 @%%@CR:IX15.113 @% 5. Executes the code of the interrupt routine until it encounters an %@AB@%IRET%@AE@% instruction.%@NL@% %@NL@% 6. Pops the instruction pointer, code segment, and flags.%@NL@% %@NL@% %@4@%%@CR:IX15.114 @% Figure 15.3 illustrates how interrupts work.%@EH@%%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 15.4.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX15.115 @%%@CR:IX15.116 @%%@CR:IX15.117 @%%@CR:IX15.118 @%%@CR:IX15.119 @%%@CR:IX15.120 @%%@CR:IX15.121 @% The %@AB@%INTO%@AE@% (Interrupt On Overflow) instruction is a variation of the %@AB@%INT%@AE@%%@EH@% instruction. It calls interrupt 04H if called when the overflow flag is set. By default, the routine for interrupt 4 simply consists of an %@AB@%IRET%@AE@% so that it returns without doing anything. However, you can write your own overflow interrupt routine. Using %@AB@%INTO%@AE@% is an alternative to using %@AB@%JO%@AE@% (Jump On Overflow) to jump to an overflow routine. Section 15.4.2%@BO: cf793@%, "Defining and Redefining Interrupt Routines," gives an example of this.%@NL@% %@NL@% %@4@%%@CR:IX15.122 @%%@CR:IX15.123 @%%@CR:IX15.124 @%%@CR:IX15.125 @%%@CR:IX15.126 @% The %@AB@%CLI%@AE@% (Clear Interrupt Flag) and %@AB@%STI%@AE@% (Set Interrupt Flag) instructions%@EH@% can be used to turn interrupts on or off. You can use %@AB@%CLI%@AE@% to turn interrupt processing off so that an important routine cannot be stopped by a hardware interrupt. After the routine has finished, use %@AB@%STI %@AE@%to turn interrupt processing back on. Interrupts received while interrupt processing was turned off by %@AB@%CLI%@AE@% are saved and executed when %@AB@%STI%@AE@% turns interrupts back on.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; DOS call (Display String)%@AE@%%@NL@% %@NL@% %@AS@% mov ah,09h ; Load function number%@AE@%%@NL@% %@AS@% mov dx,OFFSET string ; Load argument%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; BIOS call (Read Character from Keyboard)%@AE@%%@NL@% %@NL@% %@AS@% xor ah,ah ; Load function number 0 in AH%@AE@%%@NL@% %@AS@% int 16h ; Call BIOS%@AE@%%@NL@% %@AS@% ; Return scan code in AH%@AE@%%@NL@% %@AS@% ; Return ascii code in AL%@AE@%%@NL@% %@NL@% %@4@% Example 1 is a call to a DOS function.%@EH@%%@NL@% %@NL@% %@4@% Example 2 is a ROM-BIOS call that works on IBM Personal Computers and%@EH@% IBM-compatible computers. See the on-line Help system for complete information on DOS and BIOS calls.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC15.4.2 @%%@AB@%15.4.2 Defining and Redefining Interrupt Routines%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.127 @% You can write your own interrupt routines, either to replace an existing%@EH@% routine or to use an undefined interrupt number.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AI@%label%@AE@% PROC FAR[[%@AB@%USES %@AE@%%@AI@%reglist%@AE@%]]%@EH@%%@NL@% %@AI@%statements%@AE@%%@NL@% %@AB@%IRET%@AE@%%@NL@% label%@AB@% ENDP%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX15.128 @%%@CR:IX15.129 @%%@CR:IX15.130 @%%@CR:IX15.131 @%%@CR:IX15.132 @%%@CR:IX15.133 @% An interrupt routine can be written like a procedure by using the %@AB@%PROC%@AE@% and%@EH@% %@AB@%ENDP%@AE@% directives. The only differences are that the routine should always be defined as far and the routine should be terminated by an %@AB@%IRET%@AE@% instruction instead of a %@AB@%RET%@AE@% instruction.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Since the assembler doesn't know whether you are going to terminate with a %@AB@%RET %@AE@%or an %@AB@%IRET%@AE@%, it is possible to use the full extended %@AB@%PROC %@AE@%syntax (described in Section 15.3.4%@BO: c7284@%) for interrupt procedures. However, making interrupt procedures %@AB@%NEAR %@AE@%or specifying arguments for them makes no sense. The %@AB@%USES%@AE@% keyword does correctly generate code to save and restore a register list in interrupt procedures.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% Your program should replace the address in the interrupt descriptor table%@EH@% with the address of your routine. DOS calls are provided for this task. Another common technique is to jump to the old interrupt routine and let it do the %@AB@%IRET%@AE@% instruction.%@NL@% %@NL@% %@4@% It is usually a good idea to save the old address and restore it before%@EH@% your program ends.%@NL@% %@NL@% %@4@% Interrupt routines you may want to replace include the processor's%@EH@% divide-overflow (0H) and overflow (04H) interrupts. You can also replace DOS interrupts, such as the critical-error (24H) and CONTROL+C (23H) handlers. Interrupt routines can be part of device drivers. Writing interrupt routines is usually a systems task. The example below illustrates a simple routine. For complete information, see the %@AI@%Microsoft%@AE@% %@AI@%MS-DOS%@AE@% %@AI@%Programmer's%@AE@% %@AI@%Reference%@AE@% or one of the other reference books on DOS.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%message DB "Overflow - result set to 0",13,10,"$"%@AE@%%@NL@% %@AS@%vector DD ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .STARTUP%@AE@%%@NL@% %@NL@% %@AS@% mov ax,3504h ; Load interrupt 4 and call DOS%@AE@%%@NL@% %@AS@% int 21h ; get interrupt vector function%@AE@%%@NL@% %@AS@% mov WORD PTR vector[2],es ; Save segment%@AE@%%@NL@% %@AS@% mov WORD PTR vector[0],bx ; and offset%@AE@%%@NL@% %@NL@% %@AS@% push ds ; Save DS%@AE@%%@NL@% %@AS@% mov ax,cs ; Load segment of new routine%@AE@%%@NL@% %@AS@% mov ds,ax%@AE@%%@NL@% %@AS@% mov dx,OFFSET overflow ; Load offset of new routine%@AE@%%@NL@% %@AS@% mov ax,2504h ; Load interrupt 4 and call DOS%@AE@%%@NL@% %@AS@% int 21h ; set interrupt vector function%@AE@%%@NL@% %@AS@% pop ds ; Restore%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% add ax,bx ; Do addition (or multiplication)%@AE@%%@NL@% %@AS@% into ; Call interrupt 4 if overflow%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% lds dx,vector ; Load original interrupt address%@AE@%%@NL@% %@AS@% mov ax,2504h ; Restore interrupt number 4%@AE@%%@NL@% %@AS@% int 21h ; with DOS set vector function%@AE@%%@NL@% %@AS@% mov ax,4C00h ; Terminate function%@AE@%%@NL@% %@AS@% int 21h%@AE@%%@NL@% %@NL@% %@AS@%overflow PROC FAR%@AE@%%@NL@% %@AS@% sti ; Enable interrupts%@AE@%%@NL@% %@AS@% ; (turned off by INT)%@AE@%%@NL@% %@AS@% mov ah,09h ; Display string function%@AE@%%@NL@% %@AS@% mov dx,OFFSET message ; Load address%@AE@%%@NL@% %@AS@% int 21h ; Call DOS%@AE@%%@NL@% %@AS@% xor ax,ax ; Set AX to 0%@AE@%%@NL@% %@AS@% xor dx,dx ; Set DX to 0%@AE@%%@NL@% %@AS@% iret ; Return%@AE@%%@NL@% %@AS@%overflow ENDP%@AE@%%@NL@% %@AS@% END start%@AE@%%@NL@% %@NL@% %@4@% In this example, DOS functions are used to save the address of the initial%@EH@% interrupt routine in a variable and to put the address of the new interrupt routine in the interrupt table. Once the new address has been set, the new routine is called any time the interrupt is called. The sample interrupt handler sets the result of a calculation that causes an overflow (either in AX or AX:DX) to 0. It is good practice to restore the original interrupt address before terminating the program.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC15.5 @%%@AB@%15.5 Checking Memory Ranges%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX15.134 @%%@CR:IX15.135 @%%@CR:IX15.136 @%%@CR:IX15.137 @%%@CR:IX15.138 @% Starting with the 80186 processor, the %@AB@%BOUND%@AE@% instruction can check to see%@EH@% if a value is within a specified range. This instruction is usually used to check a signed index value to see if it is within the range of an array. %@AB@%BOUND%@AE@% is a conditional interrupt instruction like %@AB@%INTO%@AE@%. If the condition is not met (the index is out of range), an interrupt 5 is executed.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%BOUND%@AE@% %@AI@%register16%@AE@%%@AB@%,%@AE@% %@AI@%memory32%@AE@%%@EH@%%@NL@% %@NL@% %@4@% To use it for this purpose, the starting and ending values of the array mu%@EH@% st be stored as 16-bit values in the low and high words of a doubleword me mory operand. This operand is given as the source operand. The index value to be checked is given as the destination operand. If the index value is out of range, the instruction issues interrupt 5. This means that the oper ating system or the program must provide an interrupt routine for interrup t 5. DOS does not provide such a routine, so you must write your own. See Section 15.4%@BO: cdbc9@%, "Using Interrupts," for more information.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%bottom EQU 0%@AE@%%@NL@% %@AS@%top EQU 19%@AE@%%@NL@% %@AS@%dbounds LABEL DWORD ; Allocate boundaries%@AE@%%@NL@% %@AS@%wbounds DW bottom,top ; initialized to bounds%@AE@%%@NL@% %@AS@%array DB top+1 DUP (?) ; Allocate array%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% . ; Assume index in DI%@AE@%%@NL@% %@AS@% bound di,dbounds ; Check to see if it is in range%@AE@%%@NL@% %@AS@% ; if out of range, interrupt 5%@AE@%%@NL@% %@AS@% mov dx,array[di] ; If in range, use it%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH16 @%%@AB@%Chapter 16: Processing Strings%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IX16.1 @%%@CR:IX16.2 @% The 8086-family processors have a full set of instructions for%@EH@% manipulating strings. In the discussion of these instructions, the term "string" refers not only to the common definition of a string──a sequence of bytes containing characters──but to any sequence of bytes or words%@NL@% %@NL@% %@4@% The following instructions are provided for 8086-family string functions:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%MOVS%@AE@% Moves string from one location to another%@NL@% %@NL@% %@AB@%SCAS%@AE@% Scans string for specified values%@NL@% %@NL@% %@AB@%CMPS%@AE@% Compares values in one string with values in another%@NL@% %@NL@% %@AB@%LODS%@AE@% Loads values from a string to accumulator register%@NL@% %@NL@% %@AB@%STOS%@AE@% Stores values from accumulator register to a string%@NL@% %@NL@% %@AB@%INS%@AE@% Transfers values from a port to memory%@NL@% %@NL@% %@AB@%OUTS%@AE@% Transfers values from memory to a port%@NL@% %@NL@% %@NL@% %@4@% All these instructions use registers in the same way and have a similar%@EH@% syntax. Most are used with the repeat instruction prefixes: %@AB@%REP%@AE@%, %@AB@%REPE%@AE@%, %@AB@%REPNE%@AE@%, %@AB@%REPZ%@AE@%, and %@AB@%REPNZ%@AE@%.%@NL@% %@NL@% %@4@% This chapter first explains the general format for string instructions and%@EH@% then tells you how to use each instruction.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.1 @%%@AB@%16.1 Setting Up String Operations%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The string instructions all work in a similar way. Once you understand the%@EH@% gen-eral procedure, it is easy to adapt the format for a particular string operation. The five steps are listed below:%@NL@% %@NL@% %@CR:IX16.3 @%%@CR:IX16.4 @%%@CR:IX16.5 @%%@CR:IX16.6 @%%@CR:IX16.7 @% 1. Make sure the direction flag indicates the direction in which you want the string to be processed. If the direction flag is clear, the string will be pro-cessed up (from low addresses to high addresses). If the direction flag is set, the string will be processed down (from high addresses to low addresses). The %@AB@%CLD%@AE@% instruction clears the flag, while %@AB@%STD%@AE@% sets it. Under DOS, the direction flag will normally be cleared if your program has not changed it.%@NL@% %@NL@% 2. Load the number of iterations for the string instruction into the CX register. For instance, if you want to process a 100-byte string, load 100. If a string instruction will be terminated conditionally, load the maximum number of iterations that can be done without an error.%@NL@% %@NL@% %@CR:IX16.8 @%%@CR:IX16.9 @%%@CR:IX16.10 @%%@CR:IX16.11 @%%@CR:IX16.12 @% 3. Load the starting offset address of the source string into DS:SI and the starting address of the destination string into ES:DI. Some string instructions take only a destination or source (shown in Table 16.1 below). Normally, the segment address of the source string should be DS, but you can use a segment override with the string instruction to specify a different segment. You cannot override the segment address for the destination string. Therefore, you may need to change the value of ES.%@NL@% %@NL@% 4. Choose the appropriate repeat-prefix instruction. Table 16.1 shows the repeat prefixes that can be used with each instruction.%@NL@% %@NL@% 5. Put the appropriate string instruction immediately after the repeat prefix (on the same line).%@NL@% %@NL@% %@4@% String instructions have two basic forms, as shown below:%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax 1%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%repeatprefix%@AE@%]] %@AI@%stringinstruction%@AE@%[[%@AB@%ES:%@AE@%[[%@AI@%destination%@AE@%%@AB@%,%@AE@%]]]]%@EH@%%@NL@% [[[[%@AI@%segmentregister%@AE@%%@AB@%:%@AE@%]]%@AI@%source%@AE@%]]%@NL@% %@NL@% %@4@%%@CR:IX16.13 @% The string instruction can be given with the source and/or destination as%@EH@% operands. The size of the operand or operands indicates the size of the objects to be processed by the string. Note that the operands only specify the size. The actual values to be worked on are the ones pointed to by DS:SI and/or ES:DI. No error is generated if the operand is not the same as the actual source or destination. One important advantage of this syntax is that the source operand can have a segment override. The destination operand is always relative to ES and cannot be overridden.%@NL@% %@NL@% %@4@% %@AB@%Syntax 2%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AI@%repeatprefix%@AE@%]] %@AI@%stringinstruction%@AE@%%@AB@%B%@AE@%%@EH@%%@NL@% [[%@AI@%repeatprefix%@AE@%]] %@AI@%stringinstruction%@AE@%%@AB@%W%@AE@%%@NL@% %@NL@% %@4@% The letter %@AB@%B%@AE@% or %@AB@%W%@AE@% appended to %@AI@%stringinstruction%@AE@% indicates bytes or words.%@EH@% With a letter appended to a string instruction, no operand is allowed.%@NL@% %@NL@% %@4@% For instance, %@AB@%MOVS%@AE@% can be given with byte operands to move bytes or with%@EH@% word operands to move words. As an alternative, %@AB@%MOVSB%@AE@% can be given with no operands to move bytes, or %@AB@%MOVSW%@AE@% can be given with no operands to move words.%@NL@% %@NL@% %@4@% Note that instructions that specify the size in the name never accept%@EH@% operands. Therefore, the following statement is illegal:%@NL@% %@NL@% %@AS@% lodsb es:0 ; Illegal - no operand allowed%@AE@%%@NL@% %@NL@% %@4@% Instead, the statement must be coded as shown below:%@EH@%%@NL@% %@NL@% %@AS@% lods BYTE PTR es:0 ; Legal - use type specifier%@AE@%%@NL@% %@NL@% %@4@% If a repeat prefix is used, it can be one of the following instructions:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX16.14 @%%@CR:IX16.15 @% %@AB@%REP%@AE@% Repeats for a specified number of iterations. The number is given in CX.%@NL@% %@NL@% %@CR:IX16.16 @%%@CR:IX16.17 @%%@CR:IX16.18 @%%@CR:IX16.19 @% %@AB@%REPE%@AE@% or %@AB@%REPZ%@AE@% Repeats while equal. The maximum number of iterations should be specified in CX.%@NL@% %@NL@% %@CR:IX16.20 @%%@CR:IX16.21 @%%@CR:IX16.22 @%%@CR:IX16.23 @% %@AB@%REPNE%@AE@% or %@AB@%REPNZ%@AE@% Repeats while not equal. The maximum number of iterations should be specified in CX.%@NL@% %@NL@% %@NL@% %@4@% %@AB@%REPE%@AE@% is the same as %@AB@%REPZ%@AE@%, and %@AB@%REPNE%@AE@% is the same as %@AB@%REPNZ%@AE@%. You can use%@EH@% whichever name you find more mnemonic. The prefixes ending with %@AB@%E%@AE@% are used in syntax listings and tables in the rest of this chapter.%@NL@% %@NL@% %@4@%%@CR:IX16.24 @% Table 16.1 lists each string instruction with the type of repeat prefix it%@EH@% uses and whether the instruction works on a source, a destination, or both.%@NL@% %@NL@% %@AB@%Table 16.1 Requirements for String Instructions%@AE@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Repeat Prefix%@AE@% %@AB@%Source/Destination%@AE@% %@AB@%Register Pair%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%MOVS%@AE@% %@AB@%REP%@AE@% Both DS:SI, ES:DI%@NL@% %@NL@% %@AB@%SCAS%@AE@% %@AB@%REPE/REPNE%@AE@% Destination ES:DI%@NL@% %@NL@% %@AB@%CMPS%@AE@% %@AB@%REPE/REPNE%@AE@% Both ES:DI, DS:SI%@NL@% %@NL@% %@AB@%LODS%@AE@% None Source DS:SI%@NL@% %@NL@% %@AB@%STOS%@AE@% %@AB@%REP%@AE@% Destination ES:DI%@NL@% %@NL@% %@AB@%INS%@AE@% %@AB@%REP%@AE@% Destination ES:DI%@NL@% %@NL@% %@AB@%OUTS%@AE@% %@AB@%REP%@AE@% Source DS:SI%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% At run time, a string instruction preceded by a repeat sequence causes the%@EH@% processor to take the following steps:%@NL@% %@NL@% 1. Checks the CX registers and exits from the string instruction if CX is 0.%@NL@% %@NL@% 2. Performs the string operation once.%@NL@% %@NL@% 3. Increases SI and/or DI if the direction flag is cleared. Decreases SI and/or DI if the direction flag is set. The amount of increase or decrease is 1 for byte operations, 2 for word operations.%@NL@% %@NL@% 4. Decrements CX (no flags are modified).%@NL@% %@NL@% 5. If the string instruction is %@AB@%SCAS%@AE@% or %@AB@%CMPS%@AE@%, checks the zero flag and exits if the repeat condition is false──that is, if the flag is set with %@AB@%REPE%@AE@% or %@AB@%REPZ%@AE@% or if it is clear with %@AB@%REPNE%@AE@% or %@AB@%REPNZ%@AE@%.%@NL@% %@NL@% 6. Goes to the next iteration (step 1).%@NL@% %@NL@% %@4@% Although string instructions (except %@AB@%LODS%@AE@%) are most often used with repeat%@EH@% prefixes, they can also be used by themselves. In this case, the SI and/or DI registers are adjusted as specified by the direction flag and the size of operands. However, you must decrement the CX register and set up a loop for the repeated action.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Although you can use a segment override on the source operand, a segment override combined with a repeat prefix can cause problems in certain situations. If an interrupt occurs during the string operation, the segment override is lost and the rest of the string operation processes incorrectly. Segment overrides can be used safely when interrupts are turned off.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.2 @%%@AB@%16.2 Moving Strings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX16.25 @%%@CR:IX16.26 @%%@CR:IX16.27 @% The %@AB@%MOVS%@AE@% instruction is used to move data from one area of memory to%@EH@% another.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AB@%REP%@AE@%]] %@AB@%MOVS%@AE@% [[%@AB@%ES:%@AE@%]]%@AI@%destination%@AE@%%@AB@%,%@AE@%[[%@AI@%segmentregister%@AE@%%@AB@%:%@AE@%]]%@AI@%source%@AE@%%@EH@%%@NL@% [[%@AB@%REP%@AE@%]] %@AB@%MOVSB%@AE@%%@NL@% [[%@AB@%REP%@AE@%]] %@AB@%MOVSW%@AE@%%@NL@% %@NL@% %@4@% To move the data, load the count and the source and destination addresses%@EH@% into the appropriate registers. Then use %@AB@%REP%@AE@% with the %@AB@%MOVS%@AE@% instruction.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%source DB 10 DUP ('0123456789')%@AE@%%@NL@% %@AS@%destin DB 100 DUP (?)%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% mov ax,@data ; Load same segment%@AE@%%@NL@% %@AS@% mov ds,ax ; to both DS%@AE@%%@NL@% %@AS@% mov es,ax ; and ES%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov cx,100 ; Set iteration count to 100%@AE@%%@NL@% %@AS@% mov si,OFFSET source ; Load address of source%@AE@%%@NL@% %@AS@% mov di,OFFSET destin ; Load address of destination%@AE@%%@NL@% %@AS@% rep movsb ; Move 100 bytes%@AE@%%@NL@% %@NL@% %@4@% Example 1 shows how to move a string by using string instructions. For%@EH@% comparison, Example 2 shows a much less efficient way of doing the same operation without string instructions.%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%source DB 10 DUP ('0123456789')%@AE@%%@NL@% %@AS@%destin DB 100 DUP (?)%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% . ; Assume ES = DS%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cx,100 ; Set iteration count to 100%@AE@%%@NL@% %@AS@% mov si,OFFSET source ; Load offset of source%@AE@%%@NL@% %@AS@% mov di,OFFSET destin ; Load offset of destination%@AE@%%@NL@% %@AS@%repeat: mov al,[si] ; Get a byte from source%@AE@%%@NL@% %@AS@% mov [di],al ; Put it in destination%@AE@%%@NL@% %@AS@% inc si ; Increment source pointer%@AE@%%@NL@% %@AS@% inc di ; Increment destination pointer%@AE@%%@NL@% %@AS@% loop repeat ; Do it again%@AE@%%@NL@% %@NL@% %@4@% Both examples illustrate how to move byte strings in a small-model program%@EH@% in which DS already points to the segment containing the variables. In such programs, ES can be set to the same value as DS.%@NL@% %@NL@% %@4@% There are several variations on this. If the source string was not in the%@EH@% current data segment, you could load the starting address of its segment into ES. Another option would be to use the %@AB@%MOVS%@AE@% instruction with operands and give a segment override on the source operand. For example, you could use the following statement if ES pointed to both the source and the destination strings:%@NL@% %@NL@% %@AS@% rep movs destin,es:source%@AE@%%@NL@% %@NL@% %@4@% It is sometimes faster to move a string of bytes as words. You must adjust%@EH@% for any odd bytes, as shown in Example 3. Assume the source and destination are already loaded.%@NL@% %@NL@% %@4@% %@AB@%Example 3%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% mov cx,count ; Load count%@AE@%%@NL@% %@AS@% shr cx,1 ; Divide by 2 (carry will be set%@AE@%%@NL@% %@AS@% ; if count is odd)%@AE@%%@NL@% %@AS@% rep movsw ; Move words%@AE@%%@NL@% %@AS@% rcl cx,1 ; If odd, make CX 1%@AE@%%@NL@% %@AS@% rep movsb ; Move odd byte if there is one%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.3 @%%@AB@%16.3 Searching Strings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX16.28 @%%@CR:IX16.29 @%%@CR:IX16.30 @%%@CR:IX16.31 @% The %@AB@%SCAS %@AE@%instruction is used to scan a string for a specified value.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AB@%REPE%@AE@% | %@AB@%REPNE%@AE@%]] %@AB@%SCAS%@AE@% [[%@AB@%ES:%@AE@%]]%@AI@%destination%@AE@%%@EH@%%@NL@% [[%@AB@%REPE%@AE@% | %@AB@%REPNE%@AE@%]] %@AB@%SCASB%@AE@%%@NL@% [[%@AB@%REPE%@AE@% | %@AB@%REPNE%@AE@%]] %@AB@%SCASW%@AE@%%@NL@% %@NL@% %@4@% %@AB@%SCAS%@AE@% and its variations work only on a destination string, which must be%@EH@% pointed to by ES:DI. The value to scan for must be in the accumulator register──AL for bytes, AX for words.%@NL@% %@NL@% %@4@% The %@AB@%SCAS%@AE@% instruction works by comparing the value pointed to by DI with%@EH@% the value in the accumulator. If the values are the same, the zero flag is set. Thus, the instruction only makes sense when used with one of the repeat prefixes that checks the zero flag.%@NL@% %@NL@% %@4@%%@CR:IX16.32 @%%@CR:IX16.33 @%%@CR:IX16.34 @%%@CR:IX16.35 @% If you want to search for the first occurrence of a specified value, use%@EH@% the %@AB@%REPNE%@AE@% or %@AB@%REPNZ%@AE@% instruction. If the value is found, ES:DI will point to the value immediately after the first occurrence. You can decrement DI to make it point to the first matching value.%@NL@% %@NL@% %@4@%%@CR:IX16.36 @%%@CR:IX16.37 @%%@CR:IX16.38 @%%@CR:IX16.39 @% If you want to search for the first value that does not have a specified%@EH@% value, use %@AB@%REPE%@AE@% or %@AB@%REPZ%@AE@%. If the value is found, ES:DI will point to the position after the first nonmatching value. You can decrement DI to make it point to the first non-matching value.%@NL@% %@NL@% %@4@%%@CR:IX16.40 @% After a %@AB@%REPNE SCAS%@AE@%, the zero flag will be cleared if no match was found.%@EH@% After a %@AB@%REPE SCAS%@AE@%, the zero flag will be set if no nonmatch was found.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%string DB "The quick brown fox jumps over the lazy dog"%@AE@%%@NL@% %@AS@%lstring EQU $-string ; Length of string%@AE@%%@NL@% %@AS@%pstring DD string ; Far pointer to string%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov cx,lstring ; Load length of string%@AE@%%@NL@% %@AS@% les di,pstring ; Load address of string%@AE@%%@NL@% %@AS@% mov al,'z' ; Load character to find%@AE@%%@NL@% %@AS@% repne scasb ; Search%@AE@%%@NL@% %@AS@% jnz notfound ; CX is 0 if not found%@AE@%%@NL@% %@AS@% . ; ES:DI points to character%@AE@%%@NL@% %@AS@% . ; after first 'z'%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%notfound: ; Special case for not found%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX16.41 @%%@CR:IX16.42 @% This example assumes that ES is not the same as DS, but that the address%@EH@% of the string is stored in a pointer variable. The %@AB@%LES%@AE@% instruction is used to load the far address of the string into %@AB@%ES:DI%@AE@%.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.4 @%%@AB@%16.4 Comparing Strings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX16.43 @%%@CR:IX16.44 @%%@CR:IX16.45 @%%@CR:IX16.46 @% The %@AB@%CMPS%@AE@% instruction is used to compare two strings and point to the%@EH@% address where a match or nonmatch occurs.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AB@%REPE%@AE@% | %@AB@%REPNE%@AE@%]] %@AB@%CMPS%@AE@% [[%@AI@%segmentregister%@AE@%%@AB@%:%@AE@%]]%@AI@%source%@AE@%%@AB@%,%@AE@%[[%@AB@%ES:%@AE@%]]%@AB@%,%@AE@%%@AI@%destination%@AE@%%@EH@%%@NL@% [[%@AB@%REPE%@AE@% | %@AB@%REPNE%@AE@%]] %@AB@%CMPSB%@AE@%%@NL@% [[%@AB@%REPE%@AE@% | %@AB@%REPNE%@AE@%]] %@AB@%CMPSW%@AE@%%@NL@% %@NL@% %@4@% The count and the addresses of the strings are loaded into registers, as%@EH@% described in Section 16.1%@BO: d1f06@%, "Setting Up String Operations." Either string can be considered the destination or source string unless a segment override is used. Notice that unlike other instructions, %@AB@%CMPS%@AE@% requires that the source be on the left.%@NL@% %@NL@% %@4@% The %@AB@%CMPS%@AE@% instruction works by comparing, in turn, each value pointed to by%@EH@% DI with the value pointed to by SI. If the values are the same, the zero flag is set. Thus, the instruction makes sense only when used with one of the repeat prefixes that checks the zero flag.%@NL@% %@NL@% %@4@%%@CR:IX16.47 @%%@CR:IX16.48 @%%@CR:IX16.49 @%%@CR:IX16.50 @% If you want to search for the first match between the strings, use the%@EH@% %@AB@%REPNE%@AE@% or %@AB@%REPNZ%@AE@% instruction. If a match is found, ES:DI and DS:SI will point to the position after the first match in the respective strings. You can decrement DI or SI to point to the match. (Conversely, you would increment DI or SI if the direction flag was set.)%@NL@% %@NL@% %@4@%%@CR:IX16.51 @%%@CR:IX16.52 @%%@CR:IX16.53 @%%@CR:IX16.54 @% If you want to search for a nonmatch, use %@AB@%REPE%@AE@% or %@AB@%REPZ%@AE@%. If a nonmatch is%@EH@% found, ES:DI and DS:SI will point to the position after the first nonmatch in the respective strings. You can decrement DI or SI to point to the nonmatch.%@NL@% %@NL@% %@4@%%@CR:IX16.55 @% After a %@AB@%REPNE CMPS%@AE@%, the zero flag will be cleared if no match was found.%@EH@% After a %@AB@%REPE CMPS%@AE@%, the zero flag will be set if no nonmatch was found.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL large%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%string1 DB "The quick brown fox jumps over the lazy dog"%@AE@%%@NL@% %@AS@% .FARDATA%@AE@%%@NL@% %@AS@%string2 DB "The quick brown dog jumps over the lazy fox"%@AE@%%@NL@% %@AS@%lstring EQU $-string2%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% mov ax,@data ; Load data segment%@AE@%%@NL@% %@AS@% mov ds,ax ; into DS%@AE@%%@NL@% %@AS@% mov ax,@fardata ; Load far data segment%@AE@%%@NL@% %@AS@% mov es,ax ; into ES%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov cx,lstring ; Load length of string%@AE@%%@NL@% %@AS@% mov si,OFFSET string1 ; Load offset of string1%@AE@%%@NL@% %@AS@% mov di,OFFSET string2 ; Load offset of string2%@AE@%%@NL@% %@AS@% repe cmpsb ; Compare%@AE@%%@NL@% %@AS@% jnz allmatch ; CX is 0 if no nonmatch%@AE@%%@NL@% %@AS@% dec si ; Adjust to point to nonmatch%@AE@%%@NL@% %@AS@% dec di ; in each string%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%allmatch: . ; Special case for all match%@AE@%%@NL@% %@NL@% %@4@% This example assumes that the strings are in different segments. Both%@EH@% segments must be initialized to the appropriate segment register.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.5 @%%@AB@%16.5 Filling Strings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX16.56 @%%@CR:IX16.57 @%%@CR:IX16.58 @% The %@AB@%STOS%@AE@% instruction is used to store a specified value in each position%@EH@% of a string.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% [[%@AB@%REP%@AE@%]]%@AB@% STOS %@AE@%[[%@AB@%ES:%@AE@%]]%@AI@%destination%@AE@%%@EH@%%@NL@% [[%@AB@%REP%@AE@%]]%@AB@% STOSB%@AE@%%@NL@% [[%@AB@%REP%@AE@%]]%@AB@% STOSW%@AE@%%@NL@% %@NL@% %@4@% The string is considered the destination, so it must be pointed to by%@EH@% ES:DI. The length and address of the string must be loaded into registers, as described in Section 16.1%@BO: d1f06@%, "Setting Up String Operations." The value to store must be in the accumulator register──AL for bytes, AX for words.%@NL@% %@NL@% %@4@%%@CR:IX16.59 @%%@CR:IX16.60 @% For each iteration specified by the %@AB@%REP%@AE@% instruction prefix, the value in%@EH@% the accumulator is loaded into the string.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%destin DB 100 DUP ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% . ; Assume ES = DS%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov ax,'aa' ; Load character to fill%@AE@%%@NL@% %@AS@% mov cx,50 ; Load length of string%@AE@%%@NL@% %@AS@% mov di,OFFSET destin ; Load address of destination%@AE@%%@NL@% %@AS@% rep stosw ; Store 'a' into array%@AE@%%@NL@% %@NL@% %@4@% This example loads 100 bytes containing the character %@AS@%'a'%@AE@%. Notice that%@EH@% this is done by storing 50 words rather than 100 bytes. This makes the code faster by reducing the number of iterations. You would have to adjust for the last byte if you wanted to fill an odd number of bytes.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.6 @%%@AB@%16.6 Loading Values from Strings%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX16.61 @%%@CR:IX16.62 @%%@CR:IX16.63 @%%@CR:IX16.64 @% The %@AB@%LODS%@AE@% instruction is used to load a value from a string into a%@EH@% register.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%LODS%@AE@% [[%@AI@%segmentregister%@AE@%%@AB@%:%@AE@%]]%@AI@%source%@AE@%%@EH@%%@NL@% %@AB@%LODSB%@AE@%%@NL@% %@AB@%LODSW%@AE@%%@NL@% %@NL@% %@4@% The string is considered the source, so it must be pointed to by DS:SI.%@EH@% The value is always loaded from the string into the accumulator register──AL for bytes, AX for words.%@NL@% %@NL@% %@4@% Unlike other string instructions, %@AB@%LODS%@AE@% is not normally used with a repeat%@EH@% prefix since there is no reason to move a value repeatedly to a register. However, %@AB@%LODS%@AE@% does adjust the SI register as specified by the direction flag and the size of operands. The programmer must code the instructions to use the value after it is loaded.%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%stuff DB 0,1,2,3,4,5,6,7,8,9%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov cx,10 ; Load length%@AE@%%@NL@% %@AS@% mov si,OFFSET stuff ; Load offset of source%@AE@%%@NL@% %@AS@% mov ah,2 ; Display character function%@AE@%%@NL@% %@AS@%get: lodsb ; Get a character%@AE@%%@NL@% %@AS@% add al,'0' ; Convert to ASCII%@AE@%%@NL@% %@AS@% mov dl,al ; Move to DL%@AE@%%@NL@% %@AS@% int 21h ; Call DOS to display character%@AE@%%@NL@% %@AS@% loop get ; Repeat%@AE@%%@NL@% %@NL@% %@4@% Example 1 loads, processes, and displays each byte in a string of bytes.%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%buffer DB 80 DUP(?) ; Create buffer for argument string%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@%start: mov ax,@data ; Initialize DS%@AE@%%@NL@% %@AS@% mov ds,ax%@AE@%%@NL@% %@AS@% ; On start-up ES points to PSP%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov cl,BYTE PTR es:[80h] ; Load length of arguments%@AE@%%@NL@% %@AS@% xor ch,ch%@AE@%%@NL@% %@AS@% mov di,OFFSET buffer ; Load offset of buffer%@AE@%%@NL@% %@AS@% mov si,82h ; Load position of argument string%@AE@%%@NL@% %@AS@% mov dx,es ; Exchange ES and DS%@AE@%%@NL@% %@AS@% mov ax,ds%@AE@%%@NL@% %@AS@% mov es,ax%@AE@%%@NL@% %@AS@% mov ds,dx%@AE@%%@NL@% %@AS@%another: lodsb ; Get a character%@AE@%%@NL@% %@AS@% cmp al,'a' ; Is it high enough to be upper?%@AE@%%@NL@% %@AS@% jb noway ; No? Check%@AE@%%@NL@% %@AS@% cmp al,'z' ; Is it low enough to be letter?%@AE@%%@NL@% %@AS@% ja noway%@AE@%%@NL@% %@AS@% sub al,32 ; Yes? Convert to uppercase%@AE@%%@NL@% %@NL@% %@AS@%noway: stosb%@AE@%%@NL@% %@AS@% loop another ; Repeat%@AE@%%@NL@% %@AS@% mov dx,es ; Restore ES and DS%@AE@%%@NL@% %@AS@% mov ax,ds%@AE@%%@NL@% %@AS@% mov es,ax%@AE@%%@NL@% %@AS@% mov ds,dx%@AE@%%@NL@% %@NL@% %@4@% Example 2 copies the command arguments from position 82H in the DOS%@EH@% Program Segment Prefix (PSP) while converting them to uppercase. See the %@AI@%Microsoft%@AE@% %@AI@%MS-DOS%@AE@% %@AI@%Programmer's%@AE@% %@AI@%Reference%@AE@% or one of the many other books on DOS for information about the PSP. Notice that both %@AB@%LODSB%@AE@% and %@AB@%STOSB%@AE@% are used without repeat prefixes.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC16.7 @%%@AB@%16.7 Transferring Strings to and from Ports%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%80186/286/386 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX16.65 @%%@CR:IX16.66 @%%@CR:IX16.67 @%%@CR:IX16.68 @%%@CR:IX16.69 @%%@CR:IX16.70 @%%@CR:IX16.71 @% The %@AB@%INS%@AE@% instruction reads a string from a port to memory, and the %@AB@%OUTS%@AE@%%@EH@% instruction writes a string from memory to a port.%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%OUTS DX%@AE@%%@AB@%,%@AE@%[[%@AI@%segmentregister%@AE@%%@AB@%:%@AE@%]]%@AI@%source%@AE@%%@EH@%%@NL@% %@AB@%OUTSB%@AE@%%@NL@% %@AB@%OUTSW%@AE@%%@NL@% %@NL@% %@AB@%INS%@AE@% [[%@AB@%ES:%@AE@%]]%@AI@%destination%@AE@%%@AB@%,%@AE@%%@AB@%DX%@AE@%%@NL@% %@AB@%INSB%@AE@%%@NL@% %@AB@%INSW%@AE@%%@NL@% %@NL@% %@4@% The %@AB@%INS%@AE@% and %@AB@%OUTS%@AE@% instructions require that the number of the port be in%@EH@% DX. The port cannot be specified as an immediate value, as it can be with %@AB@%IN%@AE@% and %@AB@%OUT%@AE@%.%@NL@% %@NL@% %@4@% To move the data, load the count into CX. The string to be transferred by%@EH@% %@AB@%INS%@AE@% is considered the destination string, so it must be pointed to by ES:DI. The string to be transferred by %@AB@%OUTS%@AE@% is considered the source string, so it must be pointed to by DS:SI.%@NL@% %@NL@% %@4@% If you specify the source or destination as an operand, DX must be%@EH@% specified. Otherwise, DX is assumed and should be omitted.%@NL@% %@NL@% %@4@%%@CR:IX16.72 @%%@CR:IX16.73 @% If you need to process the string as it is transferred (for instance, to%@EH@% check for the end of a null-terminated string), you must set up the loop yourself instead of using the %@AB@%REP%@AE@% instruction prefix.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%count EQU 100%@AE@%%@NL@% %@AS@%buffer DB count DUP (?)%@AE@%%@NL@% %@AS@%inport DW ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% . ; Assume ES = DS%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% cld ; Work upward%@AE@%%@NL@% %@AS@% mov cx,count ; Load length to transfer%@AE@%%@NL@% %@AS@% mov di,OFFSET buffer ; Load address of destination%@AE@%%@NL@% %@AS@% mov dx,inport ; Load port number%@AE@%%@NL@% %@AS@% rep insb ; Transfer the string%@AE@%%@NL@% %@AS@% ; from port to buffer%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH17 @%%@AB@%Chapter 17: Calculating with a Math Coprocessor%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% The 8087-family coprocessors are used to do fast mathematical%@EH@% calculations. When used with real numbers, packed binary coded decimal (BCD) numbers, or long integers, they do calculations many times faster than the same operations done with 8086-family processors.%@NL@% %@NL@% %@4@% This chapter explains how to use the 8087-family processors to transfer%@EH@% and process data. The approach taken is from an applications standpoint. Features that would be used by systems programmers (such the flags used when writing exception handlers) are not explained. This chapter is intended as a reference, not a tutorial.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% This manual does not attempt to explain the mathematical concepts involved in using certain coprocessor features. It assumes that you will not need to use a feature unless you understand the mathematics involved. For example, you need to understand logarithms to use the %@AB@%FYL2X %@AE@%and %@AB@%FYL2XP1 %@AE@%instructions.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.1 @%%@AB@%17.1 Coprocessor Architecture%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.1 @%%@CR:IX17.2 @% The math coprocessor works simultaneously with the main processor.%@EH@% However, since the coprocessor cannot handle device input or output, most data originates in the main processor.%@NL@% %@NL@% %@4@% The main processor and the coprocessor have their own registers, which are%@EH@% completely separate and inaccessible to the other. They exchange data through memory, since memory is available to both.%@NL@% %@NL@% %@4@% Ordinarily you follow these three steps when using the coprocessor:%@EH@%%@NL@% %@NL@% 1. Load data from memory to coprocessor registers.%@NL@% %@NL@% 2. Process the data.%@NL@% %@NL@% 3. Store the data from coprocessor registers back to memory.%@NL@% %@NL@% %@4@% Step 2, processing the data, can occur while the main processor is%@EH@% handling other tasks. Steps 1 and 3 must be coordinated with the main processor so that the processor and coprocessor do not try to access the same memory at the same time, as explained in Section 17.5%@BO: dedf8@%, "Transferring Data."%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.1.1 @%%@AB@%17.1.1 Coprocessor Data Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.3 @% The 8087-family coprocessors have eight 80-bit data registers. Unlike%@EH@% 8086-family registers, the coprocessor data registers are organized as a stack. As data is pushed into the top register, previous data items move into higher-numbered registers. Register 0 is the top of the stack; register 7 is the bottom. The syntax for specifying registers is shown below:%@NL@% %@NL@% %@4@% %@AB@%ST%@AE@%[[(%@AI@%number%@AE@%)]]%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%number%@AE@% must be a digit between 0 and 7, or a constant expression that%@EH@% evaluates to a number from 0 to 7. If %@AI@%number%@AE@% is omitted, register 0 (top of stack) is assumed.%@NL@% %@NL@% %@4@%%@CR:IX17.4 @%%@CR:IX17.5 @% All coprocessor data is stored in registers in the temporary-real format.%@EH@% This is the 10-byte IEEE format described in Section 6.5.1.4%@BO: 62baf@%, "Real-Number Variables." The registers and the register format are shown in Figure 17.1.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.1.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% Internally, all calculations are done on numbers of the same type. Since%@EH@% temporary-real numbers have the greatest precision, lower-precision numbers are guaranteed not to lose precision as a result of calculations. The instructions that transfer values between the main processor and the coprocessor automatically convert numbers to and from the temporary-real format.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.1.2 @%%@AB@%17.1.2 Coprocessor Control Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.6 @% The 8087-family coprocessors have seven 16-bit control registers. The most%@EH@% useful control registers are made up of bit fields or flags. Some flags control coprocessor operations, while others maintain the current status of the coprocessor. In this sense, they are much like the 8086-family flags registers.%@NL@% %@NL@% %@4@% You do not need to understand these registers to do most coprocessor%@EH@% operations. Control flags are set by default to the values appropriate for most programs. Errors and exceptions are reported in the status-word register. However, the coprocessor already has a default system for handling exceptions. Applications programmers can usually accept the defaults. Systems programmers may want to use the status-word and control-word registers when writing exception handlers, but such problems are beyond the scope of this manual.%@NL@% %@NL@% %@4@%%@CR:IX17.7 @%%@CR:IX17.8 @% Figure 17.2 shows the overall layout of the control registers, including%@EH@% the control word, status word, tag word, instruction pointer, and operand pointer. The format of each of the registers is not shown, since these registers are generally of use only to systems programmers. The exception is the condition-code bits of the status-word register. These bits are explained in Section 17.7%@BO: e64cc@%, "Controlling Program Flow."%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.1.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The control registers are explained in more detail in the on-line Help%@EH@% system.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.2 @%%@AB@%17.2 Emulation%@AE@%%@EH@%%@NL@% %@NL@% %@4@% You can write assembly-language procedures that use the emulator library%@EH@% when called from QuickC. First write the procedure by using coprocessor instructions, then assemble it using the /E option, and finally link it with your high-level-language modules. When compiling modules, use the compiler options that specify emulation.%@NL@% %@NL@% %@4@% Some coprocessor instructions are not emulated by Microsoft emulation%@EH@% libraries. Which instructions are emulated varies depending on the language and version. If you use a coprocessor instruction that is not emulated, the program will generate a run-time error when it tries to execute the unemulated instruction. You cannot use a Microsoft emulation library with stand-alone assembler programs, since the library depends on the compiler start-up code.%@NL@% %@NL@% %@4@% See Appendix B%@BO: f653b@%, Section B.6%@BO: f9a75@%, "Creating Code for a Floating-Point%@EH@% Emulator," for information on the /E option. See Appendix A%@BO: ed697@%, "Mixed-Language Mechanics," for information on writing assembly-language procedures for high-level languages.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.3 @%%@AB@%17.3 Using Coprocessor Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Coprocessor instructions are readily recognizable because, unlike all%@EH@% 8086-family instruction mnemonics, they start with the letter %@AB@%F%@AE@%.%@NL@% %@NL@% %@4@%%@CR:IX17.9 @% Most coprocessor instructions have two operands, but in many cases, one or%@EH@% both operands are implied. Often, one operand can be a memory operand; in this case, the other operand is always implied as the stack-top register. Coprocessor instructions can never have immediate operands, and with the exception of the %@AB@%FSTSW%@AE@% instruction (see Section 17.5.2%@BO: e11e1@%, "Loading Constants"), they cannot have processor registers as operands. As with 8086-family instructions, memory-to-memory operations are never allowed. One operand must be a coprocessor register.%@NL@% %@NL@% %@4@% Instructions usually have a source and a destination operand. The source%@EH@% specifies one of the values to be processed. It is never changed by the operation. The destination specifies the value to be operated on and replaced with the result of the operation. If two operands are specified, the first is the destination and the second is the source.%@NL@% %@NL@% %@4@%%@CR:IX17.10 @%%@CR:IX17.11 @% The stack organization of registers gives the programmer flexibility to%@EH@% think of registers either as elements on a stack or as registers much like 8086-family registers. Table 17.1 lists the variations of coprocessor instructions along with the syntax for each.%@NL@% %@NL@% %@AB@%Table 17.1 Coprocessor Operand Forms%@AE@%%@NL@% %@NL@% %@AB@%Instruction Form%@AE@% %@AB@%Implied Syntax%@AE@% %@AB@%Operands%@AE@% %@AB@%Example%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% Classical-stack %@AB@%F%@AE@%%@AI@%action%@AE@% ST(1), ST %@AS@%fadd%@AE@%%@NL@% %@NL@% Memory %@AB@%F%@AE@%%@AI@%action%@AE@% %@AI@%memory%@AE@% ST %@AS@%fadd memloc%@AE@%%@NL@% %@NL@% Register %@AB@%F%@AE@%%@AI@%action%@AE@%%@AB@% ST(%@AE@%%@AI@%num%@AE@%%@AB@%),%@AE@% %@AS@%fadd st(5),st%@AE@% F%@AI@%action%@AE@%%@AB@% ST, ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% %@AB@%ST%@AE@% %@AS@%fadd st,st(3)%@AE@%%@NL@% %@NL@% Register pop %@AB@%F%@AE@%%@AI@%action%@AE@%%@AB@%P ST(%@AE@%%@AI@%num%@AE@%%@AB@%),%@AE@% %@AS@%faddp st(4),st%@AE@% %@AB@%ST%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% Not all instructions accept all operand variations. For example, load and%@EH@% store instructions always require the memory form. Load-constant instructions always take the classical-stack form. Arithmetic instructions can usually take any form.%@NL@% %@NL@% %@4@% Some instructions that accept the memory form can have the letter %@AB@%I%@AE@%%@EH@% (integer) or %@AB@%B%@AE@% (BCD) following the initial %@AB@%F%@AE@% to specify how a memory operand is to be interpreted. For example, %@AB@%FILD%@AE@% interprets its operand as an integer and %@AB@%FBLD%@AE@% interprets its operand as a BCD number. If no type letter is included in the instruction name, the instruction works on real numbers.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.3.1 @%%@AB@%17.3.1 Using Implied Operands in the Classical-Stack Form%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.12 @%%@CR:IX17.13 @%%@CR:IX17.14 @%%@CR:IX17.15 @%%@CR:IX17.16 @%%@CR:IX17.17 @% The classical-stack form treats coprocessor registers like items on a%@EH@% stack. Items are pushed onto or popped off the top elements of the stack. Since only the top item can be accessed on a traditional stack, there is no need to specify operands. The first register (and the second if there are two operands) is always assumed.%@NL@% %@NL@% %@4@% In arithmetic operations (see Section 17.6%@BO: e24a3@%), the top of the stack (ST) is%@EH@% the source operand, and the second register (ST(1)) is the destination. The result of the operation goes into the destination operand, and the source is popped off the stack. The effect is that both of the values used in the operation are destroyed and the result is left at the top of the stack.%@NL@% %@NL@% %@4@% Instructions that load constants always use the stack form (see Section%@EH@% 17.5.1%@BO: defb7@%, "Transferring Data to and from Registers"). In this case, the constant created by the instruction is the implied source, and the top of the stack (ST) is the destination. The source is pushed into the destination.%@NL@% %@NL@% %@4@% Note that the classical-stack form with its implied operands is similar to%@EH@% the register-pop form, not to the register form. For example, %@AS@%fadd%@AE@%, with the implied operands ST(1),ST, is equivalent to %@AS@%faddp st(1),st%@AE@%, rather than to %@AS@%fadd st(1),st%@AE@%.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% fld1 ; Push 1 into first position%@AE@%%@NL@% %@AS@% fldpi ; Push pi into first position%@AE@%%@NL@% %@AS@% fadd ; Add pi and 1 and pop%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX17.18 @% The status of the register stack after each instruction is shown below:%@EH@%%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.3.1 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.3.2 @%%@AB@%17.3.2 Using Memory Operands%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.19 @% The memory form treats coprocessor registers like items on a stack. Items%@EH@% are pushed from memory onto the top element of the stack, or popped from the top element to memory. Since only the top item can be accessed on a traditional stack, there is no need to specify the stack operand. The top register (ST) is always assumed. However, the memory operand must be specified.%@NL@% %@NL@% %@4@% Memory operands can be used in load and store instructions (see Section%@EH@% 17.5.1%@BO: defb7@%, "Transferring Data to and from Registers"). Load instructions push source values from memory to an implied destination register (ST). Store instructions pop source values from an implied source register (ST) to the destination in memory. Some versions of store instructions pop the register stack so that the source is destroyed. Others simply copy the source without changing the stack.%@NL@% %@NL@% %@4@% Memory operands can also be used in calculation instructions that operate%@EH@% on two values (see Section 17.6%@BO: e24a3@%, "Doing Arithmetic Calculations"). The memory operand is always the source. The stack top (ST) is always the implied destination. The result of the operation replaces the destination without changing its stack position.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%m1 DD 1.0%@AE@%%@NL@% %@AS@%m2 DD 2.0%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% fld m1 ; Push m1 into first position%@AE@%%@NL@% %@AS@% fld m2 ; Push m2 into first position%@AE@%%@NL@% %@AS@% fadd m1 ; Add m2 to first position%@AE@%%@NL@% %@AS@% fstp m1 ; Pop first position into m1%@AE@%%@NL@% %@AS@% fst m2 ; Copy first position to m2%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX17.20 @% The status of the register stack and the memory locations used in the%@EH@% instructions is shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.3.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.3.3 @%%@AB@%17.3.3 Specifying Operands in the Register Form%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.21 @% The register form treats coprocessor registers as traditional registers.%@EH@% Registers are specified the same as 8086-family instructions with two register operands. The only limitation is that one of the two registers must be the stack top (ST).%@NL@% %@NL@% %@4@% In the register form, operands are specified by name. The second operand%@EH@% is the source; it is not affected by the operation. The first operand is the destination; its value is replaced with the result of the operation. The stack position of the operands does not change.%@NL@% %@NL@% %@4@% The register form can only be used with the %@AB@%FXCH%@AE@% instruction and with%@EH@% arithmetic instructions that do calculations on two values. With the %@AB@%FXCH%@AE@% instruction, the stack top is implied and need not be specified.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% fadd st(1),st ; Add second position to first -%@AE@%%@NL@% %@AS@% ; result goes in second position%@AE@%%@NL@% %@AS@% fadd st,st(2) ; Add first position to second -%@AE@%%@NL@% %@AS@% ; result goes in first position%@AE@%%@NL@% %@AS@% fxch st(1) ; Exchange first and second positions%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX17.22 @% The status of the register stack if the registers were previously%@EH@% initialized to 1.0, 2.0, and 3.0 is shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.3.3 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.3.4 @%%@AB@%17.3.4 Specifying Operands in the Register-Pop Form%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.23 @% The register-pop form treats coprocessor registers as a modified stack.%@EH@% This form has some of the aspects of both a stack and registers. The destination register can be specified by name, but the source register must always be the stack top.%@NL@% %@NL@% %@4@% The result of the operation will be placed in the destination operand, and%@EH@% the stack top will be popped off the stack. The effect is that both values being operated on will be destroyed and the result of the operation will be saved in the specified destination register. The register-pop form is only used for instructions that do calculations on two values.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% faddp st(2),st ; Add first and third positions and pop -%@AE@%%@NL@% %@AS@% ; first position destroyed%@AE@%%@NL@% %@AS@% ; third moves to second and holds result%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX17.24 @% The status of the register stack if the registers were already initialized%@EH@% to 1.0, 2.0, and 3.0 is shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.3.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.4 @%%@AB@%17.4 Coordinating Memory Access%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.25 @% Problems of coordinating memory access can occur when the coprocessor and%@EH@% the main processor both try to access a memory location at the same time. Since the processor and coprocessor work independently, they may not finish working on memory in the order in which you give instructions. There are two separate cases, and they are handled in different ways.%@NL@% %@NL@% %@4@% In the first case, if a processor instruction is given and then followed%@EH@% by a coprocessor instruction, the coprocessor must wait until the processor is finished before it can start the next instruction. This is handled automatically by Quick-Assembler for the 8088 and 8086 or by the processor for the 80186 and 80286.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.26 @%%@CR:IX17.27 @%%@CR:IX17.28 @%%@CR:IX17.29 @%%@CR:IX17.30 @% %@AB@%Coprocessor Differences%@AE@% To synchronize operations between the 8088 or 8086 processor and the 8087 coprocessor, each 8087 instruction must be preceded by a %@AB@%WAIT i%@AE@%nstruction. This is not necessary for the 80287. If you use the %@AB@%.8087 %@AE@%directive, QuickAssembler inserts %@AB@%WAIT %@AE@%instructions automatically. However, if you use the %@AB@%.286 %@AE@%directive, QuickAssembler assumes the instructions are for the 80287 or 80387 and does not insert the %@AB@%WAIT %@AE@%instructions. If your code will never need to run on an 8086 or 8088 processor, you can make your programs shorter and more efficient by using the %@AB@%.286 %@AE@%directive.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% In the second case, if a coprocessor instruction that accesses memory is%@EH@% followed by a processor instruction attempting to access the same memory location, memory access is not automatically synchronized. For instance, if you store a coprocessor register to a variable and then try to load that variable into a processor register, the coprocessor may not be finished. Thus, the processor gets the value that was in memory before the coprocessor finished, rather than the value stored by the coprocessor. Use the %@AB@%WAIT%@AE@% or %@AB@%FWAIT%@AE@% instruction (they are mnemonics for the same instruction) to ensure that the coprocessor finishes before the processor begins.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; Coprocessor instruction first - Wait needed%@AE@%%@NL@% %@NL@% %@AS@% fist mem32 ; Store to memory%@AE@%%@NL@% %@AS@% fwait ; Wait until coprocessor is done%@AE@%%@NL@% %@AS@% mov ax,WORD PTR mem32 ; Move to register%@AE@%%@NL@% %@AS@% mov dx,WORD PTR mem32[2]%@AE@%%@NL@% %@NL@% %@AS@%; Processor instruction first - No wait needed%@AE@%%@NL@% %@NL@% %@AS@% mov WORD PTR mem32,ax ; Load memory%@AE@%%@NL@% %@AS@% mov WORD PTR mem32[2],dx%@AE@%%@NL@% %@AS@% fild mem32 ; Load to register%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.5 @%%@AB@%17.5 Transferring Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The 8087-family coprocessors have separate instructions for each of the%@EH@% following types of transfers:%@NL@% %@NL@% 1. Transferring data between memory and registers, or between different registers%@NL@% %@NL@% 2. Loading certain common constants into registers%@NL@% %@NL@% 3. Transferring control data to and from memory%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.5.1 @%%@AB@%17.5.1 Transferring Data to and from Registers%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Data-transfer instructions transfer data between main memory and the%@EH@% coprocessor registers, or between different coprocessor registers. Two basic principles govern data transfers:%@NL@% %@NL@% ■ The instruction determines whether a value in memory will be considered an integer, a BCD number, or a real number. The value is always considered a temporary-real number once it is transferred to the coprocessor.%@NL@% %@NL@% ■ The size of the operand determines the size of a value in memory. Values in the coprocessor always take up 10 bytes.%@NL@% %@NL@% %@4@%%@CR:IX17.31 @%%@CR:IX17.32 @% The adjustments between formats are made automatically. Notice that%@EH@% floating-point numbers must be stored in the IEEE format, not in the Microsoft Binary format. Data is automatically stored correctly by default. It is stored incorrectly and the coprocessor instructions disabled if you use the %@AB@%.MSFLOAT%@AE@% directive. Data formats for real numbers are explained in Section 6.5.1.4%@BO: 62baf@%, "Real-Number Variables."%@NL@% %@NL@% %@4@%%@CR:IX17.33 @%%@CR:IX17.34 @%%@CR:IX17.35 @% Data is transferred to stack registers by using load commands. These%@EH@% commands push data onto the stack from memory or coprocessor registers. Data is removed by using store commands. Some store commands pop data off the register stack into memory or coprocessor registers, whereas others simply copy the data without changing it on the stack.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.5.1.1 @%%@AB@%17.5.1.1 Real Transfers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.36 @%%@CR:IX17.37 @% The following instructions are available for transferring real numbers:%@EH@%%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.38 @%%@CR:IX17.39 @% %@AB@%FLD %@AE@%%@AI@%mem%@AE@% Pushes a copy of %@AI@%mem%@AE@% into ST. The source must be a 4-, 8-, or 10-byte memory operand. It is automatically converted to the temporary-real format.%@NL@% %@NL@% %@AB@%FLD ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Pushes a copy of the specified register into ST.%@NL@% %@NL@% %@CR:IX17.40 @%%@CR:IX17.41 @% %@AB@%FST %@AE@%%@AI@%mem%@AE@% Copies ST to %@AI@%mem%@AE@% without affecting the register stack. The destination can be a 4- or 8-byte memory operand. It is automatically converted from temporary-real format to short real or long real format, depending on the size of the operand. It cannot be stored in the 10-byte-real format.%@NL@% %@NL@% %@AB@%FST ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Copies ST to the specified register. The current value of the specified register is replaced.%@NL@% %@NL@% %@CR:IX17.42 @%%@CR:IX17.43 @% %@AB@%FSTP %@AE@%%@AI@%mem%@AE@% Pops a copy of ST into %@AI@%mem%@AE@%. The destination can be a 4-, 8-, or 10-byte memory operand. It is automatically converted from temporary-real format to the appropriate real-number format, depending on the size of the operand.%@NL@% %@NL@% %@AB@%FSTP ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Pops ST into the specified register. The current value of the specified register is replaced.%@NL@% %@NL@% %@CR:IX17.44 @%%@CR:IX17.45 @% %@AB@%FXCH %@AE@%[[%@AB@%ST%@AE@%(%@AI@%num%@AE@%%@AB@%)%@AE@%]] Exchanges the value in ST with the value in ST(%@AI@%num%@AE@%). If no operand is specified, ST(0) and ST(1) are exchanged.%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.5.1.2 @%%@AB@%17.5.1.2 Integer Transfers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.46 @%%@CR:IX17.47 @% The following instructions are available for transferring binary integers:%@EH@%%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.48 @% %@AB@%FILD %@AE@%%@AI@%mem%@AE@% The source must be a 2-, 4-, or 8-byte integer memory operand. It is interpreted as an integer and converted to temporary-real format.%@NL@% %@NL@% %@CR:IX17.49 @%%@CR:IX17.50 @% %@AB@%FIST %@AE@%%@AI@%mem%@AE@% Copies ST to %@AI@%mem%@AE@%. The destination must be a 2- or 4-byte memory operand. It is automatically converted from temporary-real format to a word or a doubleword, depending on the size of the operand. It cannot be converted to a quadword integer.%@NL@% %@NL@% %@CR:IX17.51 @%%@CR:IX17.52 @% %@AB@%FISTP %@AE@%%@AI@%mem%@AE@% Pops ST into %@AI@%mem%@AE@%. The destination must be a 2-, 4-, or 8-byte memory operand. It is automatically converted from temporary-real format to a word, doubleword, or quadword integer, depending on the size of the operand.%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.5.1.3 @%%@AB@%17.5.1.3 Packed BCD Transfers%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.53 @%%@CR:IX17.54 @% The following instructions are available for transferring BCD integers:%@EH@%%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.55 @%%@CR:IX17.56 @% %@AB@%FBLD %@AE@%%@AI@%mem%@AE@% Pushes a copy of %@AI@%mem%@AE@% into ST. The source must be a 10-byte memory operand. It should contain a packed BCD value, although no check is made to see that the data is valid.%@NL@% %@NL@% %@CR:IX17.57 @%%@CR:IX17.58 @% %@AB@%FBSTP %@AE@%%@AI@%mem%@AE@% Pops ST into %@AI@%mem%@AE@%. The destination must be a 10-byte memory operand. The value is rounded to an integer if necessary and converted to a packed BCD value.%@NL@% %@NL@% %@NL@% %@4@% The following examples illustrate instructions described throughout this%@EH@% section:%@NL@% %@NL@% %@4@% %@AB@%Example 1%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% fld m1 ; Push m1 into first item%@AE@%%@NL@% %@AS@% fld st(2) ; Push third item into first%@AE@%%@NL@% %@AS@% fst m2 ; Copy first item to m2%@AE@%%@NL@% %@AS@% fxch st(2) ; Exchange first and third items%@AE@%%@NL@% %@AS@% fstp m1 ; Pop first item into m1%@AE@%%@NL@% %@NL@% %@4@%%@CR:IX17.59 @% Assuming that registers ST and ST(1) were previously initialized to 3.0%@EH@% and 4.0, the status of the register stack is shown below:%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.5.1.3 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% %@AB@%Example 2%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%shortreal DD 100 DUP (?)%@AE@%%@NL@% %@AS@%longreal DQ 100 DUP (?)%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% . ; Assume array shortreal has been%@AE@%%@NL@% %@AS@% . ; filled by previous code%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov cx,100 ; Initialize loop%@AE@%%@NL@% %@AS@% xor si,si ; Clear pointer into shortreal%@AE@%%@NL@% %@AS@% xor di,di ; Clear pointer into longreal%@AE@%%@NL@% %@AS@%again: fld shortreal[si] ; Push shortreal%@AE@%%@NL@% %@AS@% fstp longreal[di] ; Pop longreal%@AE@%%@NL@% %@AS@% add si,4 ; Increment source pointer%@AE@%%@NL@% %@AS@% add di,8 ; Increment destination pointer%@AE@%%@NL@% %@AS@% loop again ; Do it again%@AE@%%@NL@% %@NL@% %@4@% Example 2 illustrates one way of doing run-time type conversions.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.5.2 @%%@AB@%17.5.2 Loading Constants%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.60 @% Constants cannot be given as operands and loaded directly into coprocessor%@EH@% registers. You must allocate memory and initialize the variable to a constant value. The variable can then be loaded by using one of the load instructions described in Section 17.5.1%@BO: defb7@%, "Transferring Data to and from Registers."%@NL@% %@NL@% %@4@%%@CR:IX17.61 @%%@CR:IX17.62 @% However, special instructions are provided for loading certain constants.%@EH@% You can load 0, 1, pi, and several common logarithmic values directly. Using these instructions is faster and often more precise than loading the values from initialized variables.%@NL@% %@NL@% %@4@%%@CR:IX17.63 @% The instructions that load constants all have the stack top as the implied%@EH@% destination operand. The constant to be loaded is the implied source operand. The instructions are listed below:%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.64 @%%@CR:IX17.65 @% %@AB@%FLDZ%@AE@% Pushes 0 into ST%@NL@% %@NL@% %@CR:IX17.66 @%%@CR:IX17.67 @% %@AB@%FLD1%@AE@% Pushes 1 into ST%@NL@% %@NL@% %@CR:IX17.68 @%%@CR:IX17.69 @% %@AB@%FLDPI%@AE@% Pushes the value of pi into ST%@NL@% %@NL@% %@CR:IX17.70 @%%@CR:IX17.71 @% %@AB@%FLDL2E%@AE@% Pushes the value of log2^e into ST%@NL@% %@NL@% %@CR:IX17.72 @%%@CR:IX17.73 @% %@AB@%FLDL2T%@AE@% Pushes log2^10 into ST%@NL@% %@NL@% %@CR:IX17.74 @%%@CR:IX17.75 @% %@AB@%FLDLG2%@AE@% Pushes log10^2 into ST%@NL@% %@NL@% %@CR:IX17.76 @%%@CR:IX17.77 @% %@AB@%FLDLN2%@AE@% Pushes loge^2 ST%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.5.3 @%%@AB@%17.5.3 Transferring Control Data%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.78 @%%@CR:IX17.79 @% The coprocessor data area, or parts of it, can be stored to memory and%@EH@% later loaded back. One reason for doing this is to save a snapshot of the coprocessor state before going into a procedure, and restore the same status after the procedure. Another reason is to modify coprocessor behavior by storing certain data to main memory, operating on the data with 8086-family instructions, and then loading it back to the coprocessor data area.%@NL@% %@NL@% %@4@% You can choose to transfer the entire coprocessor data area, the control%@EH@% registers, or just the status or control word. Applications programmers seldom need to load anything other than the status word.%@NL@% %@NL@% %@4@% All the control-transfer instructions take a single memory operand. Load%@EH@% instructions use the memory operand as the destination; store instructions use it as the source. The coprocessor data area is the implied source for load instructions and the implied destination for store instructions.%@NL@% %@NL@% %@4@% Each store instruction has two forms. The "wait form" checks for unmasked%@EH@% numeric-error exceptions and waits until they have been handled. The "no-wait" form (which always begins with %@AB@%FN%@AE@%) ignores unmasked exceptions. The instructions are listed below:%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.80 @%%@CR:IX17.81 @% %@AB@%FLDCW %@AE@%%@AI@%mem2byte%@AE@% Loads control word%@NL@% %@NL@% %@CR:IX17.82 @%%@CR:IX17.83 @% %@AB@%F%@AE@%[[%@AB@%N%@AE@%]]%@AB@%STCW%@AE@% %@AI@%mem2byte%@AE@% Stores control word%@NL@% %@NL@% %@CR:IX17.84 @%%@CR:IX17.85 @% %@AB@%F%@AE@%[[%@AB@%N%@AE@%]]%@AB@%STSW%@AE@% %@AI@%mem2byte%@AE@% Stores status word%@NL@% %@NL@% %@AB@%FLENV %@AE@%%@AI@%mem14byte%@AE@% Loads environment%@NL@% %@NL@% %@AB@%F%@AE@%[[%@AB@%N%@AE@%]]%@AB@%STENV%@AE@% %@AI@%mem14byte%@AE@% Stores environment%@NL@% %@NL@% %@AB@%FRSTOR %@AE@%%@AI@%mem94byte%@AE@% Restores state%@NL@% %@NL@% %@AB@%F%@AE@%[[%@AB@%N%@AE@%]]%@AB@%SAVE%@AE@% %@AI@%mem94byte%@AE@% Saves state%@NL@% %@NL@% %@NL@% %@4@% %@AB@%80287/80387 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.86 @%%@CR:IX17.87 @% Starting with the 80287 coprocessor, the %@AB@%FSTSW%@AE@% and %@AB@%FNSTSW%@AE@% instructions can%@EH@% store data directly to the AX register. This is the only case in which data can be transferred directly between processor and coprocessor registers, as shown below:%@NL@% %@NL@% %@AS@% fstsw ax%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.6 @%%@AB@%17.6 Doing Arithmetic Calculations%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The math coprocessors offer a rich set of instructions for doing%@EH@% arithmetic. Most arithmetic instructions accept operands in any of the formats discussed in Section 17.3%@BO: db35c@%, "Using Coprocessor Instructions."%@NL@% %@NL@% %@4@%%@CR:IX17.88 @%%@CR:IX17.89 @%%@CR:IX17.90 @% When using memory operands with an arithmetic instruction, make sure you%@EH@% indicate in the name whether you want the memory operand to be treated as a real number or an integer. For example, use %@AB@%FADD%@AE@% to add a real number to the stack top or %@AB@%FIADD%@AE@% to add an integer to the stack top. You do not need to specify the operand type in the instruction if both operands are stack registers, since register values are always real numbers. You cannot do arithmetic on BCD numbers in memory. You must use %@AB@%FBLD%@AE@% to load the numbers into stack registers.%@NL@% %@NL@% %@4@%%@CR:IX17.91 @% The arithmetic instructions are listed below.%@EH@%%@NL@% %@NL@% %@3@% %@AB@%Addition%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following instructions add the source and destination and put the%@EH@% result in the destination:%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.92 @%%@CR:IX17.93 @% %@AB@%FADD%@AE@% Classical-stack form. Adds ST and ST(1) and pops the result into ST. Both operands are destroyed.%@NL@% %@NL@% %@AB@%FADD ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register form with stack top as source. Adds the two register values and replaces ST(%@AI@%num%@AE@%) with the result.%@NL@% %@NL@% %@AB@%FADD ST,ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Register form with stack top as destination. Adds the two register values and replaces ST with the result.%@NL@% %@NL@% %@AB@%FADD %@AE@%%@AI@%mem%@AE@% Real-memory form. Adds a real number in %@AI@%mem%@AE@% to ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.94 @%%@CR:IX17.95 @% %@AB@%FIADD %@AE@%%@AI@%mem%@AE@% Integer-memory form. Adds an integer in %@AI@%mem%@AE@% to ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.96 @%%@CR:IX17.97 @% %@AB@%FADDP ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register-pop form. Adds the two register values and pops the result into ST(%@AI@%num%@AE@%).%@NL@% %@NL@% %@NL@% %@3@% %@AB@%Normal Subtraction%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.98 @% The following instructions subtract the source from the destination and%@EH@% put the difference in the destination. Thus, the number being subtracted from is replaced by the result.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.99 @%%@CR:IX17.100 @% %@AB@%FSUB%@AE@% Classical-stack form. Subtracts ST from ST(1) and pops the result into ST. Both operands are destroyed.%@NL@% %@NL@% %@AB@%FSUB ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register form with stack top as source. Subtracts ST from ST(%@AI@%num%@AE@%) and replaces ST(%@AI@%num%@AE@%) with the result.%@NL@% %@NL@% %@AB@%FSUB ST,ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Register form with stack top as destination. Subtracts ST(%@AI@%num%@AE@%) from ST and replaces ST with the result.%@NL@% %@NL@% %@AB@%FSUB %@AE@%%@AI@%mem%@AE@% Real-memory form. Subtracts the real number in %@AI@%mem%@AE@% from ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.101 @%%@CR:IX17.102 @% %@AB@%FISUB %@AE@%%@AI@%mem%@AE@% Integer-memory form. Subtracts the integer in %@AI@%mem%@AE@% from ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.103 @%%@CR:IX17.104 @% %@AB@%FSUBP ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register-pop form. Subtracts ST from ST(%@AI@%num%@AE@%) and pops the result into ST(%@AI@%num%@AE@%). Both operands are destroyed.%@NL@% %@NL@% %@NL@% %@3@% %@AB@%Reversed Subtraction%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.105 @% The following instructions subtract the destination from the source and%@EH@% put the difference in the destination. Thus, the number subtracted is replaced by the result.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.106 @%%@CR:IX17.107 @% %@AB@%FSUBR%@AE@% Classical-stack form. Subtracts ST(1) from ST and pops the result into ST. Both operands are destroyed.%@NL@% %@NL@% %@AB@%FSUBR ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register form with stack top as source. Subtracts ST(%@AI@%num%@AE@%) from ST and replaces ST(%@AI@%num%@AE@%) with the result.%@NL@% %@NL@% %@AB@%FSUBR ST,ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Register form with stack top as destination. Subtracts ST from ST(%@AI@%num%@AE@%) and replaces ST%@AB@% %@AE@%with the result.%@NL@% %@NL@% %@AB@%FSUBR %@AE@%%@AI@%mem%@AE@% Real-memory form. Subtracts ST from the real number in %@AI@%mem%@AE@%. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.108 @%%@CR:IX17.109 @% %@AB@%FISUBR %@AE@%%@AI@%mem%@AE@% Integer-memory form. Subtracts ST from the integer in %@AI@%mem%@AE@%. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.110 @%%@CR:IX17.111 @% %@AB@%FSUBRP ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register-pop form. Subtracts ST(%@AI@%num%@AE@%) from ST and pops the result into ST(%@AI@%num%@AE@%). Both operands are destroyed.%@NL@% %@NL@% %@NL@% %@3@% %@AB@%Multiplication%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.112 @% The following instructions multiply the source and destination and put the%@EH@% product in the destination:%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.113 @%%@CR:IX17.114 @% %@AB@%FMUL%@AE@% Classical-stack form. Multiplies ST by ST(1) and pops the result into ST. Both operands are destroyed.%@NL@% %@NL@% %@AB@%FMUL ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register form with stack top as source. Multiplies the two register values and replaces ST%@AB@%(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% with the result.%@NL@% %@NL@% %@AB@%FMUL ST,ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Register form with stack top as destination. Multiplies the two register values and replaces ST with the result.%@NL@% %@NL@% %@AB@%FMUL %@AE@%%@AI@%mem%@AE@% Real-memory form. Multiplies a real number in %@AI@%mem%@AE@% by ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.115 @%%@CR:IX17.116 @% %@AB@%FIMUL %@AE@%%@AI@%mem%@AE@% Integer-memory form. Multiplies an integer in %@AI@%mem%@AE@% by ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.117 @%%@CR:IX17.118 @% %@AB@%FMULP ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register-pop form. Multiplies the two register values and pops the result into ST(%@AI@%num%@AE@%%@AB@%)%@AE@%. Both operands are destroyed.%@NL@% %@NL@% %@NL@% %@3@% %@AB@%Normal Division%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.119 @% The following instructions divide the destination by the source and put%@EH@% the quotient in the destination. Thus, the dividend is replaced by the quotient.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.120 @%%@CR:IX17.121 @% %@AB@%FDIV%@AE@% Classical-stack form. Divides ST(1) by ST and pops the result into ST. Both operands are destroyed.%@NL@% %@NL@% %@AB@%FDIV ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register form with stack top as source. Divides ST(%@AI@%num%@AE@%) by ST and replaces ST(%@AI@%num%@AE@%) with the result.%@NL@% %@NL@% %@AB@%FDIV ST,ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Register form with stack top as destination. Divides ST by ST(%@AI@%num%@AE@%) and replaces ST with the result.%@NL@% %@NL@% %@AB@%FDIV %@AE@%%@AI@%mem%@AE@% Real-memory form. Divides ST by the real number in %@AI@%mem%@AE@%. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.122 @%%@CR:IX17.123 @% %@AB@%FIDIV %@AE@%%@AI@%mem%@AE@% Integer-memory form. Divides ST by the integer in %@AI@%mem%@AE@%. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.124 @%%@CR:IX17.125 @% %@AB@%FDIVP ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register-pop form. Divides ST(%@AI@%num%@AE@%) by ST and pops the result into ST(%@AI@%num%@AE@%). Both operands are destroyed.%@NL@% %@NL@% %@NL@% %@3@% %@AB@%Reversed Division%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.126 @% The following instructions divide the source by the destination and put%@EH@% the quotient in the destination. Thus, the divisor is replaced by the quotient.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.127 @%%@CR:IX17.128 @% %@AB@%FDIVR%@AE@% Classical-stack form. Divides ST by ST(1) and pops the result into ST. Both operands are destroyed.%@NL@% %@NL@% %@AB@%FDIVR ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register form with stack top as source. Divides ST by ST(%@AI@%num%@AE@%) and replaces ST(%@AI@%num%@AE@%) with the result.%@NL@% %@NL@% %@AB@%FDIVR ST,ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Register form with stack top as destination. Divides ST(%@AI@%num%@AE@%) by ST and replaces ST with the result.%@NL@% %@NL@% %@AB@%FDIVR %@AE@%%@AI@%mem%@AE@% Real-memory form. Divides the real number in %@AI@%mem%@AE@% by ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.129 @%%@CR:IX17.130 @% %@AB@%FIDIVR %@AE@%%@AI@%mem%@AE@% Integer-memory form. Divides the integer in %@AI@%mem%@AE@% by ST. The result replaces ST.%@NL@% %@NL@% %@CR:IX17.131 @%%@CR:IX17.132 @% %@AB@%FDIVRP ST(%@AE@%%@AI@%num%@AE@%%@AB@%),ST%@AE@% Register-pop form. Divides ST by ST(%@AI@%num%@AE@%) and pops the result into ST(%@AI@%num%@AE@%). Both operands are destroyed.%@NL@% %@NL@% %@NL@% %@3@% %@AB@%Other Operations%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The following instructions all use the stack top (ST) as an implied%@EH@% destination operand. The result of the operation replaces the value in the stack top. No operand should be given.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.133 @%%@CR:IX17.134 @% %@AB@%FABS%@AE@% Sets the sign of ST to positive.%@NL@% %@NL@% %@CR:IX17.135 @%%@CR:IX17.136 @% %@AB@%FCHS%@AE@% Reverses the sign of ST.%@NL@% %@NL@% %@CR:IX17.137 @%%@CR:IX17.138 @% %@AB@%FRNDINT%@AE@% Rounds ST to an integer.%@NL@% %@NL@% %@CR:IX17.139 @%%@CR:IX17.140 @%%@CR:IX17.141 @% %@AB@%FSQRT%@AE@% Replaces the contents of ST%@AB@% %@AE@%with its square root.%@NL@% %@NL@% %@CR:IX17.142 @% %@AB@%FSCALE%@AE@% Scales by powers of 2 by adding the value of ST(1) to the exponent of the value in ST. This effectively multiplies the stack-top value by 2 to the power contained in ST(1). Since the exponent field is an integer, the value in ST(1) should normally be an integer.%@NL@% %@NL@% %@CR:IX17.143 @%%@CR:IX17.144 @%%@CR:IX17.145 @%%@CR:IX17.146 @%%@CR:IX17.147 @%%@CR:IX17.148 @% %@AB@%FPREM%@AE@% Calculates the partial remainder by performing modulo division on the top two stack registers. The value in ST is divided by the value in ST(1). The remainder replaces the value in ST. The value in ST(1) is unchanged. Since this instruction works by repeated subtractions, it can take a lot of execution time if the operands are very different in magnitude. %@AB@%FRPEM%@AE@% is sometimes used with trigonometric functions.%@NL@% %@NL@% %@CR:IX17.149 @%%@CR:IX17.150 @% %@AB@%FXTRACT%@AE@% Breaks a number down into its exponent and mantissa and pushes the mantissa onto the register stack. Following the operation, ST contains the value of the original mantissa and ST(1) contains the value of the unbiased exponent.%@NL@% %@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%a DD 3.0%@AE@%%@NL@% %@AS@%b DD 7.0%@AE@%%@NL@% %@AS@%c DD 2.0%@AE@%%@NL@% %@AS@%posx DD 0.0%@AE@%%@NL@% %@AS@%negx DD 0.0%@AE@%%@NL@% %@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%; Solve quadratic equation - no error checking%@AE@%%@NL@% %@AS@% fld1 ; Get constants 2 and 4%@AE@%%@NL@% %@AS@% fadd st,st ; 2 at bottom%@AE@%%@NL@% %@AS@% fld st ; Copy it%@AE@%%@NL@% %@AS@% fmul a ; = 2a%@AE@%%@NL@% %@NL@% %@AS@% fmul st(1),st ; = 4a%@AE@%%@NL@% %@AS@% fxch ; Exchange%@AE@%%@NL@% %@AS@% fmul c ; = 4ac%@AE@%%@NL@% %@NL@% %@AS@% fld b ; Load b%@AE@%%@NL@% %@AS@% fmul st,st ; = b^2%@AE@%%@NL@% %@AS@% fsubr ; = b^2 - 4ac%@AE@%%@NL@% %@AS@% ; Negative value here produces error%@AE@%%@NL@% %@AS@% fsqrt ; = square root(b^2 - 4ac)%@AE@%%@NL@% %@AS@% fld b ; Load b%@AE@%%@NL@% %@AS@% fchs ; Make it negative%@AE@%%@NL@% %@AS@% fxch ; Exchange%@AE@%%@NL@% %@NL@% %@AS@% fld st ; Copy square root%@AE@%%@NL@% %@AS@% fadd st,st(2) ; Plus version = -b + root((b^2 - 4ac)%@AE@%%@NL@% %@AS@% fxch ; Exchange%@AE@%%@NL@% %@AS@% fsubp st(2),st ; Minus version = -b - root((b^2 - 4ac)%@AE@%%@NL@% %@NL@% %@AS@% fdiv st,st(2) ; Divide plus version%@AE@%%@NL@% %@AS@% fstp posx ; Store it%@AE@%%@NL@% %@AS@% fdivr ; Divide minus version%@AE@%%@NL@% %@AS@% fstp negx ; Store it%@AE@%%@NL@% %@NL@% %@4@% This example solves quadratic equations. It does no error checking and%@EH@% fails for some values because it attempts to find the square root of a negative number. You could enhance the code by using the %@AB@%FTST%@AE@% instruction (see Section 17.7.1%@BO: e6d88@%, "Comparing Operands to Control Program Flow") to check for a negative number or 0 just before the square root is calculated. If %@AS@%b^2 - 4ac %@AE@%is negative or 0, the code can jump to routines that handle special cases for no solution or one solution, respectively.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.7 @%%@AB@%17.7 Controlling Program Flow%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.151 @% The math coprocessors have several instructions that set control flags in%@EH@% the status word. The 8087-family control flags can be used with conditional jumps to direct program flow in the same way that 8086-family flags are used. Since the coprocessor does not have jump instructions, you must transfer the status word to memory so that the flags can be used by 8086-family instructions.%@NL@% %@NL@% %@4@% An easy way to use the status word with conditional jumps is to move its%@EH@% upper byte into the lower byte of the processor flags. For example, use the following statements:%@NL@% %@NL@% %@AS@% fstsw mem16 ; Store status word in memory%@AE@%%@NL@% %@AS@% fwait ; Make sure coprocessor is done%@AE@%%@NL@% %@AS@% mov ax,mem16 ; Move to AX%@AE@%%@NL@% %@AS@% sahf ; Store upper word in flags%@AE@%%@NL@% %@NL@% %@4@% As noted in Section 17.5.3%@BO: e1971@%, "Transferring Control Data," you can save%@EH@% several steps by loading the status word directly to AX on the 80287.%@NL@% %@NL@% %@4@%%@CR:IX17.152 @%%@CR:IX17.153 @% Figure 17.3 shows how the coprocessor control flags line up with the%@EH@% processor flags. C3 overwrites the zero flag, C2 overwrites the parity flag, and C0 overwrites the carry flag. C1 overwrites an undefined bit, so it cannot be used directly with conditional jumps, although you can use the %@AB@%TEST%@AE@% instruction to check C1 in memory or in a register. The sign and auxiliary-carry flags are also overwritten, so you cannot count on them being unchanged after the operation.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section 17.7 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IX17.154 @%%@CR:IX17.155 @% See Section 15.1.2%@BO: be4fa@% for more information on using conditional-jump%@EH@% instructions based on flag status.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.7.1 @%%@AB@%17.7.1 Comparing Operands to Control Program Flow%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The 8087-family coprocessors provide several instructions for comparing%@EH@% operands. All these instructions compare the stack top (ST) to a source operand, which may either be specified or implied as ST(1).%@NL@% %@NL@% %@4@%%@CR:IX17.156 @% The compare instructions affect the C3, C2, and C0 control flags. The C1%@EH@% flag is not affected. Table 17.2 shows the flags set for each possible result of a comparison or test.%@NL@% %@NL@% %@4@% Variations on the compare instructions allow you to pop the stack once or%@EH@% twice, and to compare integers and zero. For each instruction, the stack top is always the implied destination operand. If you do not give an operand, ST(1) is the implied source. Some compare instructions allow you to specify the source as a memory or register operand.%@NL@% %@NL@% %@CR:IX17.157 @%%@CR:IX17.158 @%%@CR:IX17.159 @%%@CR:IX17.160 @%%@CR:IX17.161 @%%@AB@% Table 17.2 Control-Flag Settings after Compare or Test%@AE@%%@NL@% %@NL@% %@AB@%After FCOM%@AE@% %@AB@%After FTEST%@AE@% %@AB@%C3%@AE@% %@AB@%C2%@AE@% %@AB@%C0%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% ST > %@AI@%source%@AE@% ST is positive 0 0 0%@NL@% %@NL@% ST < %@AI@%source%@AE@% ST is negative 0 0 1%@NL@% %@NL@% ST = %@AI@%source%@AE@% ST is 0 1 0 0%@NL@% %@NL@% Not comparable ST is NAN or 1 1 1 projective infinity%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% The compare instructions are listed below.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.7.1.1 @%%@AB@%17.7.1.1 Compare%@AE@%%@EH@%%@NL@% %@NL@% %@4@% These instructions compare the stack top to the source. The source and%@EH@% destination are unaffected by the comparison.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.162 @%%@CR:IX17.163 @% %@AB@%FCOM%@AE@% Compares ST to ST(1).%@NL@% %@NL@% %@AB@%FCOM ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Compares ST to ST(%@AI@%num%@AE@%).%@NL@% %@NL@% %@AB@%FCOM %@AE@%%@AI@%mem%@AE@% Compares ST to %@AI@%mem%@AE@%. The memory operand can be a four- or eight-byte real number.%@NL@% %@NL@% %@CR:IX17.164 @%%@CR:IX17.165 @% %@AB@%FICOM %@AE@%%@AI@%mem%@AE@% Compares ST to %@AI@%mem%@AE@%. The memory operand can be a two- or four-byte integer.%@NL@% %@NL@% %@CR:IX17.166 @%%@CR:IX17.167 @% %@AB@%FTST%@AE@% Compares ST to 0. The control registers will be affected as if ST had been compared to 0 in ST(1). Table 17.2 above shows the possible results.%@NL@% %@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.7.1.2 @%%@AB@%17.7.1.2 Compare and Pop%@AE@%%@EH@%%@NL@% %@NL@% %@4@% These instructions compare the stack top to the source and then pop the%@EH@% stack. Thus, the destination is destroyed by the comparison.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.168 @%%@CR:IX17.169 @% %@AB@%FCOMP%@AE@% Compares ST to ST(1) and pops ST off the register stack.%@NL@% %@NL@% %@AB@%FCOMP ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Compares ST to ST(%@AI@%num%@AE@%) and pops ST off the register stack.%@NL@% %@NL@% %@AB@%FCOMP %@AE@%%@AI@%mem%@AE@% Compares ST to %@AI@%mem%@AE@% and pops ST off the register stack. The operand can be a four- or eight-byte real number.%@NL@% %@NL@% %@CR:IX17.170 @%%@CR:IX17.171 @% %@AB@%FICOMP %@AE@%%@AI@%mem%@AE@% Compares ST to %@AI@%mem%@AE@% and pops ST off the register stack. The operand can be a two- or four-byte integer.%@NL@% %@NL@% %@CR:IX17.172 @%%@CR:IX17.173 @% %@AB@%FCOMPP%@AE@% Compares ST to ST(1) and then pops the stack twice. Both the source and destination are destroyed by the comparison.%@NL@% %@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% IFDEF c287%@AE@%%@NL@% %@AS@% .287%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% .DATA%@AE@%%@NL@% %@AS@%down DD 10.35 ; Sides of a rectangle%@AE@%%@NL@% %@AS@%across DD 13.07%@AE@%%@NL@% %@AS@%diameter DD 12.93 ; Diameter of a circle%@AE@%%@NL@% %@AS@%status DW ?%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%; Get area of rectangle%@AE@%%@NL@% %@AS@% fld across ; Load one side%@AE@%%@NL@% %@AS@% fmul down ; Multiply by the other%@AE@%%@NL@% %@NL@% %@AS@%; Get area of circle%@AE@%%@NL@% %@AS@% fld1 ; Load one and%@AE@%%@NL@% %@AS@% fadd st,st ; double it to get constant 2%@AE@%%@NL@% %@AS@% fdivr diameter ; Divide diameter to get radius%@AE@%%@NL@% %@AS@% fmul st,st ; Square radius%@AE@%%@NL@% %@AS@% fldpi ; Load pi%@AE@%%@NL@% %@AS@% fmul ; Multiply it%@AE@%%@NL@% %@NL@% %@AS@%; Compare area of circle and rectangle%@AE@%%@NL@% %@AS@% fcompp ; Compare and throw both away%@AE@%%@NL@% %@AS@% IFNDEF c287%@AE@%%@NL@% %@AS@% fstsw status ; Load from coprocessor to memory%@AE@%%@NL@% %@AS@% fwait ; Wait for coprocessor%@AE@%%@NL@% %@AS@% mov ax,status ; Memory to register%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@% fstsw ax ; (for 287+, skip memory)%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@AS@% sahf ; to flags%@AE@%%@NL@% %@AS@% jp nocomp ; If parity set, can't compare%@AE@%%@NL@% %@AS@% jz same ; If zero set, they're the same%@AE@%%@NL@% %@AS@% jc rectangle ; If carry set, rectangle is bigger%@AE@%%@NL@% %@AS@% jmp circle ; else circle is bigger%@AE@%%@NL@% %@NL@% %@AS@%nocomp: . ; Error handler%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%same: . ; Both equal%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%rectangle: . ; Rectangle bigger%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@%circle: . ; Circle bigger%@AE@%%@NL@% %@NL@% %@4@% Notice how conditional blocks are used to enhance 80287 code. If you%@EH@% define the symbol %@AS@%c287 %@AE@%from the command line by using the /D%@AB@% %@AE@%%@AI@%symbol%@AE@% option (see Section B.4%@BO: f8ea4@%, "Defining Assembler Symbols"), the code is smaller and faster, but does not run on an 8087.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SC17.7.2 @%%@AB@%17.7.2 Testing Control Flags after Other Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.174 @%%@CR:IX17.175 @% In addition to the compare instructions, the %@AB@%FXAM%@AE@% and %@AB@%FPREM%@AE@% instructions%@EH@% affect coprocessor control flags.%@NL@% %@NL@% %@4@% The %@AB@%FXAM%@AE@% instruction sets the value of the control flags based on the type%@EH@% of the number in the stack top (ST). This instruction is used to identify and handle special values, such as infinity, zero, unnormal numbers, denormal numbers, and NANs (not a number). Certain math operations are capable of producing these special-format numbers. A description of them is beyond the scope of this manual. The possible settings of the flags are shown in the on-line Help system.%@NL@% %@NL@% %@4@%%@CR:IX17.176 @%%@CR:IX17.177 @% %@AB@%FPREM%@AE@% also sets control flags. Since this instruction must sometimes be%@EH@% repeated to get a correct remainder for large operands, it uses the C2 flag to indicate whether the remainder returned is partial (C2 is set) or complete (C2 is clear). If the bit is set, the operation should be repeated.%@NL@% %@NL@% %@4@% %@AB@%FPREM%@AE@% also returns the least-significant three bits of the quotient in C0,%@EH@% C3, and C1. These bits are useful for reducing operands of periodic transcendental functions, such as sine and cosine, to an acceptable range. The technique is not explained here. The possible settings for each flag are shown in the on-line Help system.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.8 @%%@AB@%17.8 Using Transcendental Instructions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.178 @%%@CR:IX17.179 @%%@CR:IX17.180 @%%@CR:IX17.181 @% The 8087-family coprocessors provide a variety of instructions for doing%@EH@% transcendental calculations, including exponentiation, logarithmic calculations, and some trigonometric functions.%@NL@% %@NL@% %@4@% Use of these advanced instructions is beyond the scope of this manual.%@EH@% However, the instructions are listed below for reference. All transcendental instructions have implied operands──either ST as a single-destination operand, or ST as the destination and ST(1) as the source.%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.182 @%%@CR:IX17.183 @% %@AB@%F2XM1%@AE@% Calculates 2^x-1, where %@AI@%x%@AE@% is the value of the stack top. The value %@AI@%x%@AE@% must be between 0 and .5, inclusive. Returning 2^x-1 instead of 2^x allows the instruction to return the value with greater accuracy. The programmer can adjust the result to get 2^x.%@NL@% %@NL@% %@CR:IX17.184 @%%@CR:IX17.185 @% %@AB@%FYL2X%@AE@% Calculates %@AI@%Y%@AE@% times log2 %@AI@%X%@AE@%, where %@AI@%X%@AE@% is in ST and %@AI@%Y%@AE@% is in ST(1). The stack is popped, so both %@AI@%X%@AE@% and %@AI@%Y%@AE@% are destroyed, leaving the result in ST. The value of %@AI@%X%@AE@% must be positive.%@NL@% %@NL@% %@CR:IX17.186 @%%@CR:IX17.187 @% %@AB@%FYL2XP1%@AE@% Calculates %@AI@%Y%@AE@% times log2 (%@AI@%X%@AE@%+1), where %@AI@%X%@AE@% is in ST and %@AI@%Y%@AE@% is in ST(1). The stack is popped, so both %@AI@%X%@AE@% and %@AI@%Y%@AE@% are destroyed, leaving the result in ST. The absolute value of %@AI@%X%@AE@% must be between 0 and the square root of 2 divided by 2. This instruction is more accurate than %@AB@%FYL2X%@AE@% when computing the log of a number close to 1.%@NL@% %@NL@% %@CR:IX17.188 @%%@CR:IX17.189 @% %@AB@%FPTAN%@AE@% Calculates the tangent of the value in ST. The result is a ratio %@AI@%Y%@AE@%/%@AI@%X%@AE@%, with %@AI@%Y%@AE@% replacing the value in ST and %@AI@%X%@AE@% pushed onto the stack so that after the instruction, ST contains %@AI@%Y%@AE@% and ST(1) contains %@AI@%X%@AE@%. The value being calculated must be a positive number less than pi/4. The result of the %@AB@%FPTAN%@AE@% instruction can be used to calculate other trigonometric functions, including sine and cosine.%@NL@% %@NL@% %@CR:IX17.190 @%%@CR:IX17.191 @% %@AB@%FPATAN%@AE@% Calculates the arctangent of the ratio %@AI@%Y%@AE@%/%@AI@%X%@AE@%, where %@AI@%X%@AE@% is in ST and %@AI@%Y%@AE@% is in ST(1). The stack is popped, so both %@AI@%X%@AE@% and %@AI@%Y%@AE@% are destroyed, leaving the result in ST. Both %@AI@%X%@AE@% and %@AI@%Y%@AE@% must be positive numbers less than infinity, and %@AI@%Y%@AE@% must be less than %@AI@%X%@AE@%. The result of the %@AB@%FPATAN%@AE@% instruction can be used to calculate other inverse trigonometric functions, including arcsine and arccosine.%@NL@% %@NL@% %@NL@% %@NL@% %@2@%%@CR:SC17.9 @%%@AB@%17.9 Controlling the Coprocessor%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX17.192 @%%@CR:IX17.193 @% Additional instructions are available for controlling various aspects of%@EH@% the coprocessor. With the exception of %@AB@%FINIT%@AE@%, these instructions are generally used only by systems programmers. They are summarized below, but not fully explained or illustrated. Some instructions have a wait version and a no-wait version. The no-wait versions have %@AB@%N%@AE@% as the second letter.%@NL@% %@NL@% %@AB@%Syntax%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX17.194 @%%@CR:IX17.195 @%%@CR:IX17.196 @% %@AB@%F%@AE@%[[%@AB@%N%@AE@%]]%@AB@%INIT%@AE@% Resets the coprocessor and restores all the default conditions in the control and status words. It is a good idea to use this instruction at the start and end of your program. Placing it at the start ensures that no register values from previous programs affect your program. Placing it at the end ensures that register values from your program will not affect later programs.%@NL@% %@NL@% %@AB@%F%@AE@%[[%@AB@%N%@AE@%]]%@AB@%CLEX%@AE@% Clears all exception flags and the busy flag of the status word. It also clears the error-status flag on the 80287, or the interrupt-request flag on the 8087.%@NL@% %@NL@% %@AB@%FINCSTP%@AE@% Adds 1 to the stack pointer in the status word. Do not use to pop the register stack. No tags or registers are altered.%@NL@% %@NL@% %@AB@%FDECSTP%@AE@% Subtracts 1 from the stack pointer in the status word. No tags or registers are altered.%@NL@% %@NL@% %@AB@%FREE ST(%@AE@%%@AI@%num%@AE@%%@AB@%)%@AE@% Marks the specified register as empty.%@NL@% %@NL@% %@AB@%FNOP%@AE@% Copies the stack top to itself, thus padding the executable file and taking up processing time without having any effect on registers or memory.%@NL@% %@NL@% %@NL@% %@4@% %@AB@%8087 Only%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The 8087 has the instructions %@AB@%FDISI%@AE@%, %@AB@%FNDISI%@AE@%, %@AB@%FENI%@AE@%, and %@AB@%FNENI%@AE@%. These%@EH@% instructions can be used to enable or disable interrupts. The 80287 coprocessor permits these instructions, but ignores them. Applications programmers will not normally need these instructions. Systems programmers should avoid using them so that their programs are portable to all coprocessors.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CH18 @%%@AB@%Chapter 18: Controlling the Processor%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@% The 8086-family processors provide instructions for processor control.%@EH@% These instructions are available on all 8086-family processors.%@NL@% %@NL@% %@4@% System-control instructions have limited use in applications programming.%@EH@% They are primarily used by systems programmers who write operating systems and other control software. Since systems programming is beyond the scope of this manual, the systems-control instructions are summarized, but not explained in detail, in the sections below.%@NL@% %@NL@% %@4@% This chapter ends with a description of all the directives that enable the%@EH@% instruction sets for the various processors in the 8086 family.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC18.1 @%%@AB@%18.1 Controlling Timing and Alignment%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX18.1 @%%@CR:IX18.2 @% The %@AB@%NOP%@AE@% instruction takes up one byte of memory but does not have any%@EH@% effect when executed. The purpose of this instruction is generally to fill up space in the code segment; primarily, it is used to pad executable code for alignment.%@NL@% %@NL@% %@4@% Although%@AB@% NOP%@AE@% has no effect, it does take a few clock cycles to execute. In%@EH@% a sense,%@AB@% NOP%@AE@% does do something──it is exactly equivalent to the following instruction:%@NL@% %@NL@% %@AS@% xchg ax,ax ; Exchange AX with itself%@AE@%%@NL@% %@NL@% %@4@% Because %@AB@%NOP%@AE@% does use up some clock time, you can use it in timing loops by%@EH@% executing it many times. However, when writing a program for use on different machines, avoid using this technique. Timing loops that use %@AB@%NOP%@AE@% take different lengths of time on different machines. A better way to control timing is to use the DOS Get Time function, since it is based on the computer's internal clock rather than on the speed of the processor.%@NL@% %@NL@% %@4@% QuickAssembler automatically inserts %@AB@%NOP%@AE@% instructions for padding when you%@EH@% use the %@AB@%ALIGN%@AE@% or %@AB@%EVEN%@AE@% directive (see Section 6.7%@BO: 67efe@%, "Aligning Data") to align data or code on a given boundary.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SC18.2 @%%@AB@%18.2 Controlling the Processor%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AB@%LOCK%@AE@%, %@AB@%WAIT%@AE@%, %@AB@%ESC%@AE@%, and %@AB@%HLT%@AE@% instructions control different aspects of the%@EH@% processor.%@NL@% %@NL@% %@4@% These instructions can be used to control processes handled by external%@EH@% coprocessors. The 8087-family coprocessors are the coprocessors most commonly used with 8086-family processors, but 8086-based machines can work with other coprocessors if they have the proper hardware and control software.%@NL@% %@NL@% %@4@%%@CR:IX18.3 @% These instructions are summarized below:%@EH@%%@NL@% %@NL@% %@AB@%Instruction%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX18.4 @%%@CR:IX18.5 @% %@AB@%LOCK%@AE@% Locks out other processors until a specified instruction is finished. This is a prefix that precedes the instruction. It can be used to make sure that a coprocessor does not change data being worked on by the processor.%@NL@% %@NL@% %@CR:IX18.6 @%%@CR:IX18.7 @% %@AB@%WAIT%@AE@% Instructs the processor to do nothing until it receives a signal that a coprocessor has finished with a task being performed at the same time. See Section 17.4%@BO: de11e@%, "Coordinating Memory Access," for information on using %@AB@%WAIT%@AE@% or its coprocessor equivalent, %@AB@%FWAIT%@AE@%, with the 8087-family coprocessors.%@NL@% %@NL@% %@CR:IX18.8 @%%@CR:IX18.9 @% %@AB@%ESC%@AE@% Provides an instruction and possibly a memory operand for use by a coprocessor. QuickAssembler automatically inserts %@AB@%ESC%@AE@% instructions when required for use with 8087-family coprocessors.%@NL@% %@NL@% %@CR:IX18.10 @%%@CR:IX18.11 @% %@AB@%HLT%@AE@% Stops the processor until an interrupt is received. It can be used in place of an endless loop if a program needs to wait for an interrupt.%@NL@% %@NL@% %@NL@% %@NL@% %@2@%%@CR:SC18.3 @%%@AB@%18.3 Processor Directives%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IX18.12 @%%@CR:IX18.13 @%%@CR:IX18.14 @% Processor and coprocessor directives define the instruction set that is%@EH@% recognized by QuickAssembler. They are listed and explained below:%@NL@% %@NL@% %@AB@%Directive%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IX18.15 @%%@CR:IX18.16 @%%@CR:IX18.17 @% %@AB@%.8086%@AE@% The %@AB@%.8086%@AE@% directive enables assembly of instructions for the 8086 and 8088 processors and the 8087 coprocessor. It disables assembly of the instructions unique to the 80186, 80286, and 80386 processors.%@NL@% %@NL@% This is the default mode and is used if no instruction set directive is specified. Using the default instruction set ensures that your program can be used on all 8086-family processors. However, if you choose this directive, your program will not take advantage of the more powerful instructions available on more advanced processors.%@NL@% %@NL@% %@CR:IX18.18 @%%@CR:IX18.19 @% %@AB@%.186%@AE@% The %@AB@%.186%@AE@% directive enables assembly of the 8086 processor instructions, 8087 coprocessor instructions, and the additional instructions for the 80186 processor.%@NL@% %@NL@% %@CR:IX18.20 @%%@CR:IX18.21 @% %@AB@%.286%@AE@% The %@AB@%.286%@AE@% directive enables assembly of the 8086 instructions plus the additional nonprivileged instructions of the 80286 processor. It also enables 80287 coprocessor instructions. If privileged instructions were previously enabled, the %@AB@%.286%@AE@% directive disables them.%@NL@% %@NL@% This directive should be used for programs that will be executed only by an 80186, 80286, or 80386 processor. For compatibility with early versions of the Macro Assembler, the %@AB@%.286C%@AE@% directive is also available. It is equivalent to the %@AB@%.286%@AE@% directive.%@NL@% %@NL@% %@CR:IX18.22 @%%@CR:IX18.23 @% %@AB@%.8087%@AE@% The %@AB@%.8087%@AE@% directive enables assembly of instructions for the 8087 math coprocessor and disables assem-bly of instructions unique to the 80287 coprocessor. It also specifies the IEEE format for encoding floating-point variables.%@NL@% %@NL@% This is the default mode and is used if no coprocessor directive is specified. This directive should be used for programs that must run with either the 8087, 80287, or 80387 coprocessors.%@NL@% %@NL@% %@CR:IX18.24 @%%@CR:IX18.25 @% %@AB@%.287%@AE@% The %@AB@%.287%@AE@% directive enables assembly of instructions for the 8087 floating-point coprocessor and the additional instructions for the 80287. It also specifies the IEEE format for encoding floating-point variables.%@NL@% %@NL@% Coprocessor instructions are optimized if you use this directive rather than the %@AB@%.8087%@AE@% directive. Therefore, you should use it if you know your program will never need to run under an 8087 coprocessor. See Section 17.4%@BO: de11e@%, "Coordinating Memory Access," for an explanation.%@NL@% %@NL@% %@NL@% %@4@% If you do not specify any processor directives, QuickAssembler uses the%@EH@% following defaults:%@NL@% %@NL@% 1. 8086/8088 processor instruction set%@NL@% %@NL@% 2. 8087 coprocessor instruction set%@NL@% %@NL@% 3. IEEE format for floating-point variables%@NL@% %@NL@% %@4@% Normally, the processor and coprocessor directives can be used at the%@EH@% start of the source file to define the instruction sets for the entire assembly. However, it is possible to use different processor directives at different points in the source file to change assumptions for a section of code. For instance, you might have processor-specific code in different parts of the same source file. You can also turn privileged instructions on and off or allow unusual combinations of the processor and coprocessor.%@NL@% %@NL@% %@4@% There are two limitations on changing the processor or coprocessor:%@EH@%%@NL@% %@NL@% 1. The directives must be given outside segments. You must end the current segment, give the processor directive, and then open another segment. See Section 5.1.5%@BO: 47722@%, "Using Predefined Segment Equates," for an example of changing the processor directives with simplified segment directives.%@NL@% %@NL@% 2. You can specify a lower-level coprocessor with a higher-level coprocessor, but an error message will be generated if you try to specify a lower-level processor with a higher-level coprocessor.%@NL@% %@NL@% %@4@% The coprocessor directives have the opposite effect of the %@AB@%.MSFLOAT%@AE@%%@EH@% directive. %@AB@%.MSFLOAT%@AE@% turns off coprocessor instruction sets and enables the Microsoft Binary format for floating-point variables. Any coprocessor instruction turns on the specified coprocessor instruction set and enables IEEE format for floating-point variables.%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%; .MSFLOAT affects the source file until turned off%@AE@%%@NL@% %@AS@% .MSFLOAT%@AE@%%@NL@% %@AS@% .8087 ; Ignored%@AE@%%@NL@% %@NL@% %@AS@%; Illegal - can't use 8086 with 80287%@AE@%%@NL@% %@AS@% .8086%@AE@%%@NL@% %@AS@% .287%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CHA @%%@AB@%Appendix A: Mixed-Language Mechanics%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IXA.1 @%%@CR:IXA.2 @%%@CR:IXA.3 @%%@CR:IXA.4 @% The QuickAssembler %@AB@%PROC%@AE@% statement automates most details of interfacing to%@EH@% QuickC, as well as to other Microsoft high-level languages. When you use %@AB@%PROC%@AE@% with a parameter list or %@AB@%USES%@AE@% clause as described in Section 15.3.4%@BO: c7284@%, or when you use the %@AB@%LOCAL%@AE@% directive, the assembler generates code that properly enters and exits the procedure. The assembler also determines the location of each parameter on the stack for you. You refer to each parameter by a meaningful name, and the assembler translates each parameter name into the actual memory reference.%@NL@% %@NL@% %@4@% The main purpose of this appendix is to show you what code the assembler%@EH@% generates when you use the %@AB@%LOCAL%@AE@% directive or expanded features of the %@AB@%PROC%@AE@% directive. However, you can write this code yourself rather than letting the assembler generate it. Doing so requires significant extra work, but it does give you complete control over your procedure.%@NL@% %@NL@% %@4@% Using simplified segment directives is the easiest way to interface with a%@EH@% Microsoft high-level language (including QuickC). The simplified segment directives generate segment definitions similar to the ones generated by high-level languages and guarantee compatibility in the use of segment names and conventions. If you want to use full segment definitions, see Chapter 5%@BO: 3ffad@%, "Defining Segment Structure," for a description of the segments used in Microsoft languages.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCA.1 @%%@AB@%A.1 Writing the Assembly Procedure%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The Microsoft BASIC, C, FORTRAN, and Pascal compilers use roughly the same%@EH@% interface for procedure calls. This section describes the interface, so that you can call assembly procedures using essentially the same methods as Microsoft compiler-generated code. Procedures written with these methods can be called recursively.%@NL@% %@NL@% %@4@% The standard assembly-interface method consists of these steps:%@EH@%%@NL@% %@NL@% 1. Setting up the procedure%@NL@% %@NL@% 2. Entering the procedure%@NL@% %@NL@% 3. Allocating local data (optional)%@NL@% %@NL@% 4. Preserving register values%@NL@% %@NL@% 5. Accessing parameters%@NL@% %@NL@% 6. Returning a value (optional)%@NL@% %@NL@% 7. Exiting the procedure%@NL@% %@NL@% %@4@% The %@AB@%PROC%@AE@% statement, when used with a parameter list or %@AB@%USES%@AE@% clause,%@EH@% automates steps 1 and 2, and simplifies step 5 for you. The %@AB@%LOCAL%@AE@% directive automates step 3, and the %@AB@%USES%@AE@% clause automates step 4. Finally, if you use any of these features, the assembler automatically generates all the proper code to exit (step 7) wherever it encounters a %@AB@%RET%@AE@% directive. (However, the %@AB@%RETF%@AE@% and %@AB@%RETN%@AE@% statements never generate automatic code.)%@NL@% %@NL@% %@4@% Sections A.1.1%@BO: ee356@%-A.1.7 describe these steps.%@EH@%%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.1 @%%@AB@%A.1.1 Setting Up the Procedure%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The linker cannot combine the assembly procedure with the calling program%@EH@% unless compatible segments are used and unless the procedure itself is declared properly. The following points may be helpful:%@NL@% %@NL@% %@CR:IXA.5 @%%@CR:IXA.6 @%%@CR:IXA.7 @%%@CR:IXA.8 @%%@CR:IXA.9 @%%@CR:IXA.10 @% 1. Use the %@AB@%.MODEL%@AE@% directive at the beginning of the source file; this directive automatically causes the appropriate kind of returns to be generated (%@AB@%NEAR%@AE@% for small or compact model, %@AB@%FAR%@AE@% otherwise). Modules called from Pascal should be declared as %@AB@%.MODEL LARGE%@AE@%; modules called from BASIC should be %@AB@%.MODEL MEDIUM%@AE@%.%@NL@% %@NL@% %@CR:IXA.11 @%%@CR:IXA.12 @% 2. Use the simplified segment directives: %@AB@%.CODE%@AE@% to declare the code segment and %@AB@%.DATA%@AE@% to declare the data segment. (Having a code segment is sufficient if you do not have data declarations.)%@NL@% %@NL@% 3. Tie procedure label must be public. This makes the procedure available to be called by other modules. If you specify a language type with the %@AB@%.MODEL%@AE@% directive, the assembler automatically makes all procedure names public, but you must use the %@AB@%PUBLIC%@AE@% directive if you don't specify the language. Also, any data you want to make public to other modules must be declared as %@AB@%PUBLIC%@AE@%.%@NL@% %@NL@% 4. Global data or procedures accessed by the routine (but defined in other modules) must be declared %@AB@%EXTRN%@AE@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.2 @%%@AB@%A.1.2 Entering the Procedure%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.13 @% Two instructions begin the procedure:%@EH@%%@NL@% %@NL@% %@AS@% push bp%@AE@%%@NL@% %@AS@% mov bp,sp%@AE@%%@NL@% %@NL@% %@4@%%@CR:IXA.14 @% This sequence establishes BP as the framepointer. The framepointer is used%@EH@% to access parameters and local data, which are located on the stack. SP cannot be used for this purpose because it is not an index or base register. Also, the value of SP may change as more data is pushed onto the stack. However, the value of the base register (BP) will remain constant throughout the procedure, so that each parameter can be addressed as a fixed displacement from the location pointed to by BP.%@NL@% %@NL@% %@4@% The instruction sequence above first saves the value of BP, since it will%@EH@% be needed by the calling procedure as soon as the current procedure terminates. Then BP is loaded with the value of SP in order to capture the value of the stack pointer at the time of entry to the procedure.%@NL@% %@NL@% %@4@% The %@AB@%PROC%@AE@% statement generates these two lines of code automatically if you%@EH@% use a parameter list, %@AB@%LOCAL%@AE@% directive, or %@AB@%USES%@AE@% clause.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% If you alter the direction flag with the %@AB@%STD%@AE@% instruction, make sure you reset this flag with the %@AB@%CLD%@AE@% instruction before you exit.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.3 @%%@AB@%A.1.3 Allocating Local Data (Optional)%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.15 @%%@CR:IXA.16 @%%@CR:IXA.17 @%%@CR:IXA.18 @%%@CR:IXA.19 @% Local variables are also called dynamic, stack, or automatic variables.%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.20 @% An assembly procedure can use the same technique for implementing local%@EH@% data used by high-level languages. To set up local data space, decrease the contents of SP in the third instruction of the procedure. (To ensure correct execution, you should always increase or decrease SP by an even amount.) Decreasing SP reserves space on the stack for the local data. The space must be restored at the end of the procedure.%@NL@% %@NL@% %@AS@% push bp%@AE@%%@NL@% %@AS@% mov bp,sp%@AE@%%@NL@% %@AS@% sub sp,%@AE@%%@AI@%space%@AE@%%@NL@% %@NL@% %@4@% In the code above, %@AI@%space%@AE@% is the total size in bytes of the local data.%@EH@% Local variables are then accessed as fixed, negative displacements from the location pointed to by BP.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% push bp%@AE@%%@NL@% %@AS@% mov bp,sp%@AE@%%@NL@% %@AS@% sub sp,4%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% mov WORD PTR [bp-2],0%@AE@%%@NL@% %@AS@% mov WORD PTR [bp-4],0%@AE@%%@NL@% %@NL@% %@4@% The example above uses two local variables, each of which is two bytes in%@EH@% size. SP is decreased by 4, since there are four bytes total of local data. Later, each of the variables is initialized to 0. These variables are never formally declared with any assembler directive; the programmer must keep track of them manually.%@NL@% %@NL@% %@4@% The %@AB@%LOCAL%@AE@% directive uses this same method for creating local variables.%@EH@% However, when you use %@AB@%LOCAL%@AE@%, you can refer to a local variable by a symbolic name rather than by a reference, such as %@AS@%WORD PTR [bp-2]%@AE@%.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.4 @%%@AB@%A.1.4 Preserving Register Values%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.21 @% A procedure called from any of the Microsoft high-level languages should%@EH@% preserve the values of SI, DI, SS, and DS (in addition to BP, which is already saved). Therefore, push any of these register values that the procedure alters. If the procedure does not change the value of any of these registers, the registers do not need to be pushed.%@NL@% %@NL@% %@4@%%@CR:IXA.22 @% The recommended method (used by high-level languages) is to save registers%@EH@% after the framepointer is set and local data (if any) is allocated.%@NL@% %@NL@% %@AS@% push bp ; Save old framepointer%@AE@%%@NL@% %@AS@% mov bp,sp ; Establish current framepointer%@AE@%%@NL@% %@AS@% sub sp,4 ; Allocate local data space%@AE@%%@NL@% %@AS@% push si ; Save SI and DI%@AE@%%@NL@% %@AS@% push di%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@AS@% .%@AE@%%@NL@% %@NL@% %@4@% In the example above, DI and SI (in that order) must be popped from the%@EH@% stack before the end of the procedure.%@NL@% %@NL@% %@4@% The %@AB@%USES%@AE@% clause in a %@AB@%PROC%@AE@% statement causes the assembler to generate this%@EH@% same code.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.5 @%%@AB@%A.1.5 Accessing Parameters%@AE@%%@EH@%%@NL@% %@NL@% %@4@% When you use %@AB@%PROC%@AE@% with a parameter list, the assembler calculates the%@EH@% location of each parameter on the stack. This section shows how the assembler determines these locations. If you do not use a parameter list, you must calculate parameter locations yourself and refer to them explicitly by their offsets from BP. Otherwise, you can refer to each parameter by the name you gave it in the parameter list.%@NL@% %@NL@% %@4@%%@CR:IXA.23 @%%@CR:IXA.24 @% To write instructions that can access parameters, consider the general%@EH@% picture of the stack frame after a procedure call, as illustrated in Figure A.1.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section A.1.5 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% When determining the order of parameters on the stack, note that the C%@EH@% calling convention (the default for QuickC) specifies that parameters are passed in the reverse order they appear in source code. The non-C calling convention (which you can specify in QuickC with the %@AB@%pascal%@AE@% or %@AB@%fortran%@AE@% keyword) specifies that parameters are passed in the same order they appear in source code.%@NL@% %@NL@% %@4@%%@CR:IXA.25 @% The stack frame for the procedure is established by the following%@EH@% sequence:%@NL@% %@NL@% 1. The calling program pushes each of the parameters on the stack, after which SP points to the last parameter pushed.%@NL@% %@NL@% 2. The calling program issues a %@AB@%CALL%@AE@% instruction, which causes the return address (the place in the calling program to which control will ultimately return) to be placed on the stack. This address may be either two bytes long (for near calls) or four bytes long (for far calls). SP now points to this address.%@NL@% %@NL@% %@CR:IXA.26 @% 3. The first instruction of the called procedure saves the old value of BP, with the instruction %@AS@%push bp%@AE@%. SP now points to the saved copy of BP.%@NL@% %@NL@% 4. BP is used to capture the current value of SP, with the instruction %@AS@%mov%@AE@% %@AS@%bp,sp%@AE@%. BP therefore now points to the old value of BP.%@NL@% %@NL@% %@CR:IXA.27 @% 5. Whereas BP remains constant throughout the procedure, SP may be decreased to provide room on the stack for local data or saved registers.%@NL@% %@NL@% %@4@% In general, the displacement (from the location pointed to by BP) for a%@EH@% parameter %@AS@%X%@AE@% is equal to:%@NL@% %@NL@% %@AS@%2 + size of return address + total size of parameters between X and BP%@AE@%%@NL@% %@NL@% %@4@% For example, consider a %@AB@%FAR%@AE@% procedure that has received one parameter, a%@EH@% two-byte address. The displacement of the parameter would be:%@NL@% %@NL@% %@AS@%Argument's displacement = 2 + size of return address%@AE@%%@NL@% %@AS@% = 2 + 4%@AE@%%@NL@% %@AS@% = 6%@AE@%%@NL@% %@NL@% %@4@% The argument can thus be loaded into BX with the following instruction:%@EH@%%@NL@% %@NL@% %@AS@% mov bx,[bp+6]%@AE@%%@NL@% %@NL@% %@4@%%@CR:IXA.28 @% Once you determine the displacement of each parameter, you may want to use%@EH@% string equates or structures so that the parameter can be referenced with a single identifier name in your assembly source code. For example, the parameter above at BP+6 can be conveniently accessed if you put the following statement at the beginning of the assembly source file:%@NL@% %@NL@% %@AS@%Arg1 EQU <[bp+6]>%@AE@%%@NL@% %@NL@% %@4@% You could then refer to this parameter as %@AS@%Arg1 %@AE@%in any instruction. Use of%@EH@% this feature is optional.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Microsoft high-level languages always push segment addresses before pushing offset addresses. Furthermore, when pushing arguments larger than two bytes, high-order words are always pushed before low-order words.%@NL@% %@CR:IXA.29 @%%@NL@% This standard for pushing segment addresses before pushing offset addresses facilitates the use of the %@AB@%LES%@AE@% and %@AB@%LDS%@AE@% instructions, as described in Chapter 3%@BO: 2aca0@%, "Writing Assembly Modules for C Programs."%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.6 @%%@AB@%A.1.6 Returning a Value (Optional)%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The assembler does not generate code to return a value. If you want your%@EH@% procedure to return a value, you must take care of the details yourself.%@NL@% %@NL@% %@4@%%@CR:IXA.30 @% Microsoft BASIC, C, FORTRAN, and Pascal share similar conventions for%@EH@% receiving return values. The conventions are the same when the data type to be returned is simple (that is, not an array or structured type) and is no more than four bytes long. This includes all %@AB@%NEAR%@AE@% and %@AB@%FAR%@AE@% address types (in other words, all pointers and all parameters passed by reference).%@NL@% %@NL@% %@AB@%Data Size%@AE@% %@AB@%Returned in Register%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 1 byte AL%@NL@% %@NL@% 2 bytes AX%@NL@% %@NL@% 4 bytes High-order portion (or segment address) in DX; low-order portion (or offset address) in AX%@NL@% %@NL@% %@NL@% %@4@%%@CR:IXA.31 @% When the return value is larger than four bytes, a procedure called by C%@EH@% must allocate space for the return value and then place its address in DX:AX. You can create space for the return value by simply declaring it in a data segment.%@NL@% %@NL@% %@4@% If your assembly procedure uses the non-C calling convention, it must use%@EH@% a special convention in order to return floating-point values, records, user-defined types and arrays, and values larger than four bytes. This convention is presented below.%@NL@% %@NL@% %@4@% %@AB@%BASIC/FORTRAN/Pascal Long Return Values%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.32 @%%@CR:IXA.33 @% To create an interface for long return values, modules using the non-C%@EH@% calling convention take the following actions before they call your procedure:%@NL@% %@NL@% 1. The calling modules create space, somewhere in the stack segment, to hold the actual return value.%@NL@% %@NL@% %@CR:IXA.34 @% 2. When the call to your procedure is made, an extra parameter is passed containing the offset address of the actual return value. This parameter is placed immediately above the return address. (In other words, this parameter is the last one pushed.)%@NL@% %@NL@% 3. The segment address of the return value is contained in both SS and DS.%@NL@% %@NL@% %@4@%%@CR:IXA.35 @% The extra parameter (containing the offset address of the return value) is%@EH@% always located at BP+6. Furthermore, its presence automatically increases the displacement of all other parameters by 2, as shown in Figure A.2.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section A.1.6 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% Your assembly procedure will successfully return a long value if you%@EH@% follow these steps:%@NL@% %@NL@% 1. Put the data for the return value at the location pointed to by the return value offset.%@NL@% %@NL@% 2. Copy the return-value offset (located at BP+6) to AX, and copy SS to DX. This is necessary because the calling module expects DX:AX to point to the return value.%@NL@% %@NL@% 3. Exit the procedure as described in the next section.%@NL@% %@NL@% %@NL@% %@3@%%@CR:SCA.1.7 @%%@AB@%A.1.7 Exiting the Procedure%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.36 @% Several steps may be involved in terminating the procedure:%@EH@%%@NL@% %@NL@% 1. If any of the registers SS, DS, SI, or DI have been saved, these must be popped off the stack in the reverse order that they were saved.%@NL@% %@NL@% 2. If local data space was allocated at the beginning of the procedure, SP must be restored with the instruction %@AS@%mov sp,bp%@AE@%.%@NL@% %@NL@% 3. Restore BP with %@AS@%pop bp%@AE@%. This step is always necessary.%@NL@% %@NL@% 4. Finally, return to the calling program with %@AS@%ret%@AE@%. If the BASIC, FORTRAN, or Pascal calling convention is in use, you can use the %@AS@%ret%@AE@% %@AI@%n%@AE@% form of the instruction to adjust the stack with respect to the parameters that were pushed by the caller. (If the procedure is called by a C module, the calling module will perform this adjustment.)%@NL@% %@NL@% %@4@% %@AB@%Examples%@AE@%%@EH@%%@NL@% %@NL@% %@AS@% pop bp%@AE@%%@NL@% %@AS@% ret%@AE@%%@NL@% %@NL@% %@4@% The example above shows the simplest possible exit sequence. No registers%@EH@% were saved, no local data space was allocated, and the C calling convention is in use.%@NL@% %@NL@% %@AS@% pop di ; Pop saved regs%@AE@%%@NL@% %@AS@% pop si%@AE@%%@NL@% %@AS@% mov sp,bp ; Remove local data space%@AE@%%@NL@% %@AS@% pop bp ; Restore old framepointer%@AE@%%@NL@% %@AS@% ret 6 ; Exit, and restore 6 byte of args%@AE@%%@NL@% %@NL@% %@4@% The example above shows an exit sequence for a procedure that has%@EH@% previously saved SI and DI, allocated local data space, and uses a non-C calling convention. The procedure must therefore use %@AS@%ret 6 %@AE@%(where %@AI@%n%@AE@% is 6) to restore the six bytes of parameters on the stack.%@NL@% %@NL@% %@4@% Assuming you use one of the automated features described above (such as a%@EH@% parameter list or %@AB@%LOCAL%@AE@% directive), the assembler generates all the code to properly exit from a procedure whenever it encounters a %@AB@%RET%@AE@% instruction. However, the assembler does not generate any exit code when you use the directives %@AB@%RETN%@AE@% or %@AB@%RETF%@AE@%.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCA.2 @%%@AB@%A.2 Calls from Modules Using C Conventions%@AE@%%@EH@%%@NL@% %@NL@% %@4@% Most of the details below are automated when you use simplified segment%@EH@% directives and the expanded features of the %@AB@%PROC%@AE@% directive. Make sure to declare both a language type and a memory model with the %@AB@%.MODEL%@AE@% directive.%@NL@% %@NL@% %@4@% This section reviews all the steps taken when you use the %@AB@%C%@AE@% language type.%@EH@% In addition to the steps outlined in Section A.1%@BO: edd84@%, the assembler observes the following rules to set up an interface to C.%@NL@% %@NL@% %@4@%%@CR:IXA.37 @%%@CR:IXA.38 @% Follow these rules if you want to manually establish this interface:%@EH@%%@NL@% %@NL@% 1. Declare procedures called from C as %@AB@%FAR%@AE@% if the C module is compiled in large, huge, or medium model, and %@AB@%NEAR%@AE@% if the C module is compiled in small or compact model (although the%@AB@% near%@AE@% and %@AB@%far%@AE@% keywords can override these defaults). The correct declaration for the procedure is made implicitly when you use the %@AB@%.MODEL%@AE@% directive. Note that tiny memory model is not supported by QuickC 2.0.%@NL@% %@NL@% %@CR:IXA.39 @% 2. Observe the C calling convention.%@NL@% %@NL@% a. Return with a simple %@AS@%ret%@AE@% instruction. Do not restore the stack with %@AS@%ret%@AE@% %@AI@%size%@AE@%, since the calling C routine will restore the stack itself as soon as it resumes control.%@NL@% %@NL@% b. Parameters are placed on the stack in the reverse order that they appear in the C source code. The first parameter will be lowest in memory (because it is placed on the stack last and the stack grows downward).%@NL@% %@NL@% %@CR:IXA.40 @%%@CR:IXA.41 @%%@CR:IXA.42 @% c. By default, C parameters are passed by value, except for arrays, which are passed by reference. As a rule, do not expect an address to be placed on the stack, unless the C code specifically refers to a pointer or array in the function call or prototype.%@NL@% %@NL@% 3. Observe the C naming convention.%@NL@% %@NL@% Include an underscore (_) in front of any name that will be shared publicly with C. C recognizes only the first eight characters of any name, so do not make names shared with C longer than eight characters. Also, if you plan to link with the /NOIGNORECASE option, remember that C is case sensitive and does not convert names to uppercase. To preserve lowercase names in public symbols, choose Preserve Case or Preserve Extrn from the Assembler Flags dialog box, or assemble with the /Cl or /Cx option on the QCL command line.%@NL@% %@NL@% In the example program below, C calls an assembly procedure that calculates "A x 2^B," where A and B are the first and second parameters, respectively. The calculation is performed by shifting the bits in A to the left, B times.%@NL@% %@NL@% %@AS@%extern int power2( int, int ),%@AE@%%@NL@% %@NL@% %@AS@%main ()%@AE@%%@NL@% %@AS@%{%@AE@%%@NL@% %@AS@% printf( "3 times 2 to the power of 5 is %d\n",%@AE@%%@NL@% %@AS@% power2( 3, 5 ) );%@AE@%%@NL@% %@AS@%}%@AE@%%@NL@% %@NL@% %@4@% The C program uses an %@AB@%extern%@AE@% declaration to create an interface with the%@EH@% assembly procedure. No special keywords are required because the assembly procedure will use the C calling convention.%@NL@% %@NL@% %@4@%%@CR:IXA.43 @% To understand how to write the assembly procedure, consider how the%@EH@% parameters are placed on the stack, as illustrated in Figure A.3.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section A.2 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% The return address is two bytes long, assuming that the C module is%@EH@% compiled in small or compact model. If the C module is compiled in large, huge, or medium model, the addresses of Arg 1 and Arg 2 are each increased by 2, to BP+6 and BP+8, respectively, because the return address will be four bytes long.%@NL@% %@NL@% %@4@%%@CR:IXA.44 @%%@CR:IXA.45 @% Arg 1 (parameter 1) is lower in memory than Arg 2, because C pushes%@EH@% arguments in the reverse order that they appear. Each argument is passed by value.%@NL@% %@NL@% %@4@% The assembly procedure can be written as follows:%@EH@%%@NL@% %@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% PUBLIC _power2%@AE@%%@NL@% %@AS@%_power2 PROC%@AE@%%@NL@% %@AS@% push bp ; Entry sequence - save old BP%@AE@%%@NL@% %@AS@% mov bp,sp ; Set stack framepointer%@AE@%%@NL@% %@NL@% %@AS@% mov ax,[bp+4] ; Load Arg1 into AX%@AE@%%@NL@% %@AS@% mov cx,[bp+6] ; Load Arg2 into CX%@AE@%%@NL@% %@AS@% shl ax,cl ; AX = AX * (2 to power of CX)%@AE@%%@NL@% %@AS@% ; Leave return value in AX%@AE@%%@NL@% %@NL@% %@AS@% pop bp ; Exit sequence - restore old BP%@AE@%%@NL@% %@AS@% ret ; Return%@AE@%%@NL@% %@AS@%_power2 ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@%%@CR:IXA.46 @% The example above assumes that the C module is compiled in small model.%@EH@% The parameter offsets and the %@AB@%.MODEL%@AE@% directive will change for different models.%@NL@% %@NL@% %@4@% Note that %@AS@%ret %@AE@%without a size variable is used, since the caller will%@EH@% adjust the stack upon return from the call.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCA.3 @%%@AB@%A.3 Calls from Non-C Modules%@AE@%%@EH@%%@NL@% %@NL@% %@4@% In your C programs you can specify the %@AB@%pascal%@AE@% or %@AB@%fortran%@AE@% function type.%@EH@% These keywords are equivalent: they both specify use of non-C calling and naming conventions. Furthermore, you may want to interface to languages other than C (which you can do by linking .OBJ files together outside the environment). In all these cases, make sure you specify %@AB@%BASIC%@AE@%, %@AB@%FORTRAN%@AE@%, or %@AB@%Pascal%@AE@% as the language type with the %@AB@%.MODEL%@AE@% directive. Alternately, you can specify the language as part of the procedure if you are using the extended %@AB@%PROC%@AE@% directive.%@NL@% %@NL@% %@4@% This section reviews all the steps taken when you use a non-C language%@EH@% type. In addition to the steps outlined in Section A.1%@BO: edd84@%, the assembler observes the following rules to set up an interface to a language using a non-C calling convention.%@NL@% %@NL@% %@4@%%@CR:IXA.47 @%%@CR:IXA.48 @% Follow these rules if you want to manually establish an interface to a%@EH@% high-level language:%@NL@% %@NL@% 1. If the procedure is called from Microsoft BASIC, Pascal, or FORTRAN, make sure to declare the procedure as %@AB@%FAR%@AE@%, or use the %@AB@%.MODEL%@AE@% directive to specify medium or large memory model. BASIC always uses medium memory model; Pascal uses large memory model.%@NL@% %@NL@% %@CR:IXA.49 @% 2. Observe the non-C calling convention.%@NL@% %@NL@% a. Upon exit, the procedure must reset SP to the value it had before the parameters were placed on the stack. This is accomplished with the instruction %@AS@%ret%@AE@% %@AI@%size%@AE@%, where %@AI@%size%@AE@% is the total size in bytes of all the parameters.%@NL@% %@NL@% b. Parameters are placed on the stack in the same order in which they appear in the high-level language source code. The first parameter will be highest in memory (because it is placed on the stack first and the stack grows downward).%@NL@% %@NL@% c. Each language has different defaults for passing parameters by value or reference. When a language passes by reference, it places a data pointer on the stack. When it passes by value, it places a complete copy of the parameter on the stack. Consult your language documentation for the details of when the language passes by value or reference. (In C, the default is by value except for arrays.)%@NL@% %@NL@% 3. Observe the language naming convention.%@NL@% %@NL@% Microsoft BASIC, FORTRAN, and Pascal output symbolic names in uppercase characters, which is also the default behavior of the assembler. Each language recognizes a different number of characters in a name. For example, BASIC recognizes up to 40 characters of a name, whereas the assembler recognizes only the first 31.%@NL@% %@NL@% In the following example program, QuickBASIC 4.0 calls an assembly procedure that calculates "A x 2B," where A and B are the first and second parameters, respectively. The calculation is performed by shifting the bits in A to the left, B times. (Note: with earlier versions of BASIC, you need to rewrite the example so that it calls a subprogram, not a function.)%@NL@% %@NL@% %@AS@%' BASIC program%@AE@%%@NL@% %@AS@%DEFINT A-Z%@AE@%%@NL@% %@AS@%PRINT "3 times 2 to the power of 5 is ";%@AE@%%@NL@% %@AS@%PRINT Power2(3,5)%@AE@%%@NL@% %@AS@%END%@AE@%%@NL@% %@NL@% %@4@% To understand how to write the assembly procedure, consider how the%@EH@% parameters are placed on the stack, as illustrated in Figure A.4.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section A.3 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@%%@CR:IXA.50 @%%@CR:IXA.51 @%%@CR:IXA.52 @% The return address is four bytes long because procedures called from BASIC%@EH@% must be %@AB@%FAR%@AE@%. Arg 1 (parameter 1) is higher in memory than Arg 2 because BASIC pushes arguments (parameters) in the same order in which they appear. Also, each argument is passed as a two-byte offset address, the BASIC default.%@NL@% %@NL@% %@4@%%@CR:IXA.53 @% The assembly procedure can be written as follows:%@EH@%%@NL@% %@NL@% %@AS@% .MODEL medium%@AE@%%@NL@% %@AS@% .CODE%@AE@%%@NL@% %@AS@% PUBLIC Power2%@AE@%%@NL@% %@AS@%Power2 PROC%@AE@%%@NL@% %@AS@% push bp ; Entry sequence - save old BP%@AE@%%@NL@% %@AS@% mov bpsp ; Set stack framepointer%@AE@%%@NL@% %@NL@% %@AS@% mov bx,[bp+8] ; Load Arg1 into%@AE@%%@NL@% %@AS@% mov ax,[bx] ; AX%@AE@%%@NL@% %@AS@% mov bx,[bp] ; Load Arg2 into%@AE@%%@NL@% %@AS@% mov cx,[bx] ; CX%@AE@%%@NL@% %@AS@% shl ax,cl ; AX = AX * (2 to power of CX)%@AE@%%@NL@% %@AS@% ; Leave return value in AX%@AE@%%@NL@% %@NL@% %@AS@% pop bp ; Exit sequence - restore old BP%@AE@%%@NL@% %@AS@% ret 4 ; Return, and restore 4 bytes%@AE@%%@NL@% %@AS@%Power2 ENDP%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@4@% Note that each parameter must be loaded in a two-step process because the%@EH@% %@AI@%address%@AE@% of each is passed rather than the value. Also, note that the stack is restored with the instruction %@AS@%ret 4%@AE@%, since the total size of the parameters is four bytes.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCA.4 @%%@AB@%A.4 Calling High-Level Languages from Assembly Language%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.54 @% Many high-level-language routines assume that certain initialization code%@EH@% has previously been executed; you can ensure that the proper initialization is performed by starting in a high-level-language module, and then calling an assembly procedure. The assembly procedure can then call high-level-language routines as needed, as shown in Figure A.5.%@NL@% %@NL@% ┌────────────────────────────────────────────────────────────────────────┐%@NL@% │ This figure can be found in Section A.4 of the manual │%@NL@% └────────────────────────────────────────────────────────────────────────┘%@NL@% %@NL@% %@4@% To execute an assembly call to a high-level language, you need to observe%@EH@% the following guidelines:%@NL@% %@NL@% 1. Push each parameter onto the stack, observing the calling convention of the high-level language. Constants, such as offset addresses, must first be loaded into a register before being pushed.%@NL@% %@NL@% 2. With long parameters, always push the segment or high-order portion of the parameter first, regardless of the calling convention.%@NL@% %@NL@% 3. If you are using the BASIC/FORTRAN/Pascal calling convention with a function that returns a noninteger value, allocate an additional two-byte parameter. This additional parameter should contain the offset of the location where you want the value returned and must be pushed on the stack last.%@NL@% %@NL@% 4. Execute a call. The call must be far unless the high-level-language routine is small model.%@NL@% %@NL@% 5. If the routine used the C calling convention, after the call you must immediately clear the stack of parameters with the instruction %@AS@%add sp,%@AE@% %@AI@%size%@AE@%, where %@AI@%size%@AE@% is the total size in bytes of all parameters that were pushed.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCA.5 @%%@AB@%A.5 Using Full Segment Definitions%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXA.55 @%%@CR:IXA.56 @%%@CR:IXA.57 @% If you use the simplified segment directives by themselves, you do not%@EH@% need to know the names assigned for each segment. However, if you choose to use full segment definitions, you should use the %@AB@%SEGMENT%@AE@%, %@AB@%GROUP%@AE@%, %@AB@%ASSUME%@AE@%, and %@AB@%ENDS%@AE@% directives equivalent to the simplified segment directives.%@NL@% %@NL@% %@4@% The following example shows the C-assembly program from Section A.3%@BO: f3f49@%,%@EH@% without the simplified segment directives:%@NL@% %@NL@% %@AS@%_TEXT SEGMENT WORD PUBLIC 'CODE'%@AE@%%@NL@% %@AS@% ASSUME cs:_TEXT%@AE@%%@NL@% %@AS@% PUBLIC _Power2%@AE@%%@NL@% %@AS@%_Power2 PROC%@AE@%%@NL@% %@AS@% push bp ; Entry sequence - save BP%@AE@%%@NL@% %@AS@% mov bp,sp ; Set stack frame%@AE@%%@NL@% %@NL@% %@AS@% mov ax,[bp+4] ; Load Arg1 into AX%@AE@%%@NL@% %@AS@% mov cx,[bp+6] ; Load Arg2 into CX%@AE@%%@NL@% %@AS@% shl ax,cl ; AX = AX * (2 to power of CX)%@AE@%%@NL@% %@AS@% ; Leave return value in AX%@AE@%%@NL@% %@NL@% %@AS@% pop bp ; Exit sequence - restore BP%@AE@%%@NL@% %@AS@% ret ; Return%@AE@%%@NL@% %@AS@%_Power2 ENDP%@AE@%%@NL@% %@AS@%_TEXT ENDS%@AE@%%@NL@% %@AS@% END%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CHB @%%@AB@%Appendix B: Using Assembler Options with QCL%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IXB.1 @%%@CR:IXB.2 @% You can use the QCL driver for both compiling and assembling. The driver%@EH@% compiles .C files and assembles .ASM files. Unless the /c option is given, the QCL driver then links together all resulting .OBJ files, as well as any .OBJ files specified on the command line. The default file extension is .OBJ.%@NL@% %@NL@% %@4@% If you acquired QuickAssembler as an upgrade, make sure you use the%@EH@% version of QCL that came with the QuickAssembler package. This driver program is an updated and expanded version, and it supports assembly options in addition to all the compile options listed in the QuickC %@AI@%Tool%@AE@% %@AI@%Kit%@AE@%.%@NL@% %@NL@% %@4@% The following options may affect work with .ASM files, but are not%@EH@% described here because they work precisely the same way as described in the QuickC %@AI@%Tool%@AE@% %@AI@%Kit%@AE@%:%@NL@% %@NL@% %@AB@%Option%@AE@% %@AB@%Action%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% /help Print help listing for QCL%@NL@% %@NL@% /link %@AI@%flags%@AE@% Specify linker flags%@NL@% %@NL@% /Fe%@AI@%file%@AE@% Specify output file%@NL@% %@NL@% /Fo%@AI@%file%@AE@% Name object file%@NL@% %@NL@% /Z{d|i} Generate debugging information.%@NL@% %@NL@% %@NL@% %@4@% The /c, /D, and /W options are documented in the QuickC %@AI@%Tool%@AE@% %@AI@%Kit%@AE@%, but are%@EH@% also documented here because their meaning and usage change somewhat for assembly-language files.%@NL@% %@NL@% %@4@%%@CR:IXB.3 @%%@CR:IXB.4 @%%@CR:IXB.5 @% In addition to the linker options documented in the QuickC %@AI@%Tool%@AE@% %@AI@%Kit%@AE@%, QCL%@EH@% supports one other option, /TINY. This option causes the linker to output a .COM file, if possible. The linker can only create a .COM file if the program is entirely written in assembly language, and all the modules observe the rules for the .COM format. (The easiest way to do this is to use tiny memory model as described in Chapter 5%@BO: 3ffad@%.) The following example generates a .COM file:%@NL@% %@NL@% %@AS@%QCL /AT TINYPROG.ASM /link /TINY%@AE@%%@NL@% %@NL@% %@4@% The /AT option causes the assembler to check the assembly code for%@EH@% adherence to the .COM format. The /TINY linker option causes the linker to generate a .COM file.%@NL@% %@NL@% %@4@%%@CR:IXB.6 @% The QuickAssembler version of QCL supports the following options in%@EH@% addition to the ones supported for use with C-language modules:%@NL@% %@NL@% %@AB@%Option%@AE@% %@AB@%Action%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@CR:IXB.7 @% /a Writes segments in alphabetical order%@NL@% %@NL@% /AT Requires program to use tiny memory model; gives error messages for code that violates requirements of .COM format%@NL@% %@NL@% /C{l|u|x} Determines case sensitivity (l=preserve case, u=convert to upper, x=preserve case of external and public symbols)%@NL@% %@NL@% /D Defines symbols%@NL@% %@NL@% /Ez Displays error lines on screen%@NL@% %@NL@% /Fl%@AI@%file%@AE@% Generates an assembly-listing file with given file name%@NL@% %@NL@% /FPi Creates code for emulated floating-point instructions%@NL@% %@NL@% /l Generates an assembly-listing file%@NL@% %@NL@% /P1 Enables one-pass assembly%@NL@% %@NL@% %@CR:IXB.8 @% /s Writes segments in source-code order (reverses effect of /a)%@NL@% %@NL@% /Sa Lists all lines of macro expansions (assumes /Fl%@AI@%file%@AE@% or /l is given)%@NL@% %@NL@% /Sd Adds pass 1 information to listing (assumes /Fl%@AI@%file%@AE@% or /l is given)%@NL@% %@NL@% /Se Creates editor-oriented listing file; the resulting listing has no page breaks or page headings (assumes /Fl%@AI@%file%@AE@% or /l is given)%@NL@% %@NL@% /Sn Suppresses symbol-table in listing (assumes /Fl%@AI@%file%@AE@% or /l is given)%@NL@% %@NL@% /Sq Generates an editor-based listing file with a source-line index at the end (assumes Fl%@AI@%file%@AE@% or /l is given)%@NL@% %@NL@% /Sx Suppresses listing of false conditionals (assumes Fl%@AI@%file%@AE@% or /l is given)%@NL@% %@NL@% /t Suppresses messages if assembly is successful%@NL@% %@NL@% /v Displays extra statistics during assembly%@NL@% %@NL@% %@CR:IXB.9 @%%@CR:IXB.10 @% /w Equivalent to /W0%@NL@% %@NL@% /W{0|1|2} Sets warning-message level%@NL@% %@NL@% %@NL@% %@CR:IXB.11 @%%@CR:IXB.12 @%%@CR:IXB.13 @%%@CR:IXB.14 @%%@CR:IXB.15 @%%@NL@% %@2@%%@CR:SCB.1 @%%@AB@%B.1 Specifying the Segment-Order Method%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /s Default%@EH@%%@NL@% /a%@NL@% %@NL@% %@4@% The /a option directs QuickAssembler to place the assembled segments in%@EH@% alphabetical order before copying them to the object file. The /s option directs the assembler to write segments in the order in which they appear in the source code.%@NL@% %@NL@% %@4@%%@CR:IXB.16 @% Source-code order is the default. If no option is given, QuickAssembler%@EH@% copies the segments in the order encountered in the source file. The /s option is provided for compatibility with the XENIX(R) operating system and for overriding a default option in the QuickAssembler environment variable.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% Some previous versions of the IBM Macro Assembler ordered segments alphabetically by default. Listings in some books and magazines have been written with these early versions in mind. If you have trouble assembling and linking a listing taken from a book or magazine, try using the /a option.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% The order in which segments are written to the object file is only one%@EH@% factor in determining the order in which they will appear in the executable file. The significance of segment order and ways to control it are discussed in Sections 5.2.1%@BO: 4bb75@%, "Setting the Segment-Order Method" and 5.2.2.2%@BO: 4bb75@%, "Defining Segment Combinations with Combine Type."%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /a file.asm%@AE@%%@NL@% %@NL@% %@4@% The example above creates an object file, FILE.OBJ, whose segments are%@EH@% arranged in alphabetical order. If the /s option were used instead, or if no option were specified, the segments would be arranged in sequential order.%@NL@% %@NL@% %@CR:IXB.17 @%%@CR:IXB.18 @%%@CR:IXB.19 @%%@NL@% %@2@%%@CR:SCB.2 @%%@AB@%B.2 Checking Code for Tiny Model%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /AT%@EH@%%@NL@% %@NL@% %@4@% The /AT option causes the assembler to enforce the requirements of .COM%@EH@% format. If the %@AB@%.MODEL%@AE@% directive is used, /AT generates an error unless the directive specifies tiny memory model. If the %@AB@%.MODEL%@AE@% directive is not used, the /AT option generates an error if any program-defined segments are referenced (since these references violate conditions of .COM format).%@NL@% %@NL@% %@4@% The use of /AT alone does not generate a .COM file. You must also use the%@EH@% /TINY linker option, as in the following example:%@NL@% %@NL@% %@AS@%QCL /AT TINYPROG.ASM /link /TINY%@AE@%%@NL@% %@NL@% %@CR:IXB.20 @%%@CR:IXB.21 @%%@CR:IXB.22 @%%@CR:IXB.23 @%%@CR:IXB.24 @%%@CR:IXB.25 @%%@CR:IXB.26 @%%@NL@% %@2@%%@CR:SCB.3 @%%@AB@%B.3 Selecting Case Sensitivity%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Cu Default%@EH@%%@NL@% /Cl%@NL@% /Cx%@NL@% %@NL@% %@4@% The /Cl option directs the assembler to make all names case sensitive. The%@EH@% /Cx option directs the assembler to make public and external names case sensitive. The /Cu option directs the assembler to convert all names to uppercase.%@NL@% %@NL@% %@4@% By default, QuickAssembler converts all names to uppercase (/Cu).%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXB.27 @%%@CR:IXB.28 @%%@CR:IXB.29 @%%@CR:IXB.30 @% If case sensitivity is turned on, all names that have the same spelling%@EH@% but use letters of different cases are considered distinct. For example, with the /Cl option, %@AS@%DATA %@AE@%and %@AS@%data %@AE@%are different. They would also be different with the /Cx option if they were declared external or public. Public and external names include any label, variable, or symbol names defined by using the %@AB@%EXTRN%@AE@%, %@AB@%PUBLIC%@AE@%, or %@AB@%COMM%@AE@% directives (see Chapter 8%@BO: 70f6e@%, "Creating Programs from Multiple Modules").%@NL@% %@NL@% %@4@% If you use the /Zi or /Zd option (these cause QCL to include debugging%@EH@% information), the /Cx, /Cl, and /Cu options affect the case of the symbolic data that will be available to a symbolic debugger.%@NL@% %@NL@% %@4@%%@CR:IXB.31 @%%@CR:IXB.32 @%%@CR:IXB.33 @% The /Cl and /Cx options are typically used when object modules created%@EH@% with QuickAssembler are to be linked with object modules created by a case-sensitive compiler such as the Microsoft C compiler. If case sensitivity is important, you should also use the linker /NOI option.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /Cx module.asm%@AE@%%@NL@% %@NL@% %@4@% This example shows how to use the /Cx option with QuickAssembler to%@EH@% assemble a file with case-sensitive public symbols.%@NL@% %@NL@% %@CR:IXB.34 @%%@CR:IXB.35 @%%@CR:IXB.36 @%%@CR:IXB.37 @%%@NL@% %@2@%%@CR:SCB.4 @%%@AB@%B.4 Defining Assembler Symbols%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /D%@AI@%symbol%@AE@%[[=%@AI@%value%@AE@%]]%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXB.38 @%%@CR:IXB.39 @%%@CR:IXB.40 @% The /D option, when given with a %@AI@%symbol%@AE@% argument, directs QuickAssembler%@EH@% to define a symbol that can be used during the assembly as if it were defined as a text equate in the source file. Multiple symbols can be defined in a single command line.%@NL@% %@NL@% %@4@% The %@AI@%value%@AE@% can be any text string that does not include a space, comma, or%@EH@% semicolon. If %@AI@%value%@AE@% is not given, the symbol is assigned a null string.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /Dwide /Dmode=3 file,,;%@AE@%%@NL@% %@NL@% %@4@% This example defines the symbol %@AS@%wide %@AE@%and gives it a null value. The symbol%@EH@% could then be used in the following conditional-assembly block:%@NL@% %@NL@% %@AS@% IFDEF wide%@AE@%%@NL@% %@AS@% PAGE 50,132%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@4@% When the symbol is defined in the command line, the listing file is%@EH@% formatted for a 132-column printer. When the symbol is not defined in the command line, the listing file is given the default width of 80 (see the description of the %@AB@%PAGE%@AE@% directive in Section 12.2%@BO: a2ad0@%, "Controlling Page Format in Listings").%@NL@% %@NL@% %@4@% The example also defines the symbol %@AS@%mode %@AE@%and gives it the value 3. The%@EH@% symbol could then be used in a variety of contexts, as shown below:%@NL@% %@NL@% %@AS@% IF mode LT 15 ; Use in expression%@AE@%%@NL@% %@AS@%scrmode DB mode ; Initialize to mode%@AE@%%@NL@% %@AS@% ELSE%@AE@%%@NL@% %@AS@%scrmode DB 15 ; Initialize to 15%@AE@%%@NL@% %@AS@% ENDIF%@AE@%%@NL@% %@NL@% %@CR:IXB.41 @%%@CR:IXB.42 @%%@CR:IXB.43 @%%@NL@% %@2@%%@CR:SCB.5 @%%@AB@%B.5 Displaying Error Lines on the Screen%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Ez%@EH@%%@NL@% %@NL@% %@4@% The /Ez option directs QuickAssembler to display lines containing errors%@EH@% on the screen. Normally, when the assembler encounters an error, it displays only an error message describing the problem. When you use the /Ez option in the command line, the assembler displays the source line that produced the error in addition to the error message. QuickAssembler assembles faster without the /Ez option, but you may find the convenience of seeing the incorrect source lines worth the slight cost in processing speed.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /Ez file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.44 @%%@CR:IXB.45 @%%@CR:IXB.46 @%%@CR:IXB.47 @%%@CR:IXB.48 @%%@CR:IXB.49 @%%@NL@% %@2@%%@CR:SCB.6 @%%@AB@%B.6 Creating Code for a Floating-Point Emulator%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /FPi 87 /FPi%@EH@%%@NL@% %@NL@% %@4@% The /FPi and /FPi87 options control how instructions for a math%@EH@% coprocessor (such as the 8087, 80287, or 80387) are assembled. The /FPi option tells the assembler to generate code for a coprocessor emulator library. The /FPi87 option tells the assembler to generate code for a coprocessor. These options are different than most other QuickAssembler options in that the default for C files is /FPi, but the default for assembler files is /FPi87. They are also different in that the options must be specified separately for each file.%@NL@% %@NL@% %@4@% An emulator library uses the instructions of a coprocessor if one is%@EH@% present; otherwise, the library executes interrupts that emulate coprocessor instructions. Emulator libraries are available for QuickC and other high-level language compilers, including Microsoft Pascal, BASIC, and FORTRAN compilers.%@NL@% %@NL@% %@4@% With QuickAssembler, you should specify /FPi only for assembly modules%@EH@% that will be linked with a main C module, since the emulator code requires the start-up code generated by the C compiler. A stand-alone assembler program generated with /FPi will execute emulator interrupts, but the program will not work because the interrupts will not be initialized. If you are programming in the QC environment and you want the emulator library to be used with an assembler module, you must specify /FPi in the Global Custom Flags field of the Assembler Flags dialog box (reached from the Options menu). This will affect all assembly modules in the program list.%@NL@% %@NL@% %@4@% To the applications programmer, writing code for the emulator is like%@EH@% writing code for a coprocessor. The instruction sets are the same (except as noted in Chapter 17%@BO: d965a@%, "Calculating with a Math Coprocessor"). However, at run time the coprocessor instructions are used only if there is a coprocessor available on the machine. If there is no coprocessor, the slower code from the emulator library is used instead.%@NL@% %@NL@% %@4@% The /FPi87 option specifies that coprocessor instructions should be%@EH@% generated directly. It does not need to be given directly for assembly modules, since it is the default, but it must be specified for C modules. Programs that use this option can be run only on a system that has a coprocessor. If the program contains a main C module, it will fail with a warning if the system has no coprocessor. If the program is a stand-alone assembler program, you should write the code to check for a coprocessor and terminate with an error message if no coprocessor exists.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL calc.c /FPi /Cx math.asm%@AE@%%@NL@% %@NL@% %@4@% The example above assembles MATH.ASM with the /FPi option and compiles the%@EH@% C source file CALC.C. The resulting object files are then linked together to produce the file CALC.EXE. The C compiler generates emulator code for floating-point instructions. The FORTRAN, BASIC, and Pascal compilers generate similar code.%@NL@% %@NL@% %@CR:IXB.50 @%%@CR:IXB.51 @%%@CR:IXB.52 @%%@CR:IXB.53 @%%@CR:IXB.54 @%%@CR:IXB.55 @%%@NL@% %@2@%%@CR:SCB.7 @%%@AB@%B.7 Creating Listing Files%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /l%@EH@%%@NL@% /Fl%@AI@%file%@AE@%%@NL@% %@NL@% %@4@% The /l option directs QuickAssembler to create a listing file. Files%@EH@% specified with this option always have the base name of the source file plus a .LST extension. You cannot specify any other file name. The /Fl option has the same purpose as /l, but lets you specify any file name as the listing file. The default file name is the base file name plus a .LST extension.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /l prog.asm%@AE@%%@NL@% %@NL@% %@4@% This example causes the assembler to generate the file PROG.LST during%@EH@% assembly.%@NL@% %@NL@% %@CR:IXB.56 @%%@CR:IXB.57 @%%@CR:IXB.58 @%%@NL@% %@2@%%@CR:SCB.8 @%%@AB@%B.8 Enabling One-Pass Assembly%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /P1%@EH@%%@NL@% %@NL@% %@4@% The /P1 option causes the assembler to attempt translation of source code%@EH@% in one pass. If successful, the translation is significantly faster than the default two-pass assembly. Assembly modules cannot be successfully assembled with this option if they contain conditional-assembly directives that make references to pass 1 or pass 2.%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@AB@%NOTE%@AE@% One-pass assembly is not compatible with the generation of listing files or the /a option for specifying alphabetical segment order.%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@4@% If the assembler generates a message reporting that one-pass assembly is%@EH@% not possible, simply assemble the file again without using this option.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /P1 file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.59 @%%@CR:IXB.60 @%%@CR:IXB.61 @%%@NL@% %@2@%%@CR:SCB.9 @%%@AB@%B.9 Listing All Lines of Macro Expansions%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Sa%@EH@%%@NL@% %@NL@% %@4@% The /Sa option causes the listing file to contain all statements generated%@EH@% by the assembler. It overrides directives that limit listings such as %@AB@%.XLIST%@AE@%,%@AB@% .XALL%@AE@%, and %@AB@%.SFCOND%@AE@%. It forces display of all statements generated automatically by simplified segment directives and the extended %@AB@%PROC%@AE@% syntax. The /Sa option has no effect unless /l or /Fl is also specified.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /l /Sa file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.62 @%%@CR:IXB.63 @%%@CR:IXB.64 @%%@CR:IXB.65 @%%@NL@% %@2@%%@CR:SCB.10 @%%@AB@%B.10 Creating a Pass 1 Listing%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Sd%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXB.66 @% The /Sd option causes the listing file to contain the results of both%@EH@% assembler passes. A pass 1 listing is typically used to locate phase errors. Phase errors occur when the assembler makes assumptions about the program in pass 1 that are not valid in pass 2. The /Sd option has no effect unless /l or /Fl is also specified.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /l /Sd file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.67 @%%@CR:IXB.68 @%%@CR:IXB.69 @%%@NL@% %@2@%%@CR:SCB.11 @%%@AB@%B.11 Specifying an Editor-Oriented Listing%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Se%@EH@%%@NL@% %@NL@% %@4@% The /Se option causes the assembler to generate the listing file in a%@EH@% format suited to text editors. This format does not contain page breaks or page headings. The default behavior, which is designed for files output to a printer, assumes a page break and heading at periodic intervals. The /Se option has no effect unless /l or /Fl is also specified.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /l /Se file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.70 @%%@CR:IXB.71 @%%@CR:IXB.72 @%%@NL@% %@2@%%@CR:SCB.12 @%%@AB@%B.12 Suppressing Tables in the Listing File%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Sn%@EH@%%@NL@% %@NL@% %@4@% The /Sn option tells the assembler to omit all tables from the end of the%@EH@% listing file. If this option is not chosen, QuickAssembler includes tables of macros, structures, records, segments and groups, and symbols. The code portion of the listing file is not changed by the /Sn option. The /Sn option has no effect unless /l or /Fl is also specified.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /l /Sn file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.73 @%%@CR:IXB.74 @%%@CR:IXB.75 @%%@NL@% %@2@%%@CR:SCB.13 @%%@AB@%B.13 Adding a Line-Number Index to the Listing%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Sq%@EH@%%@NL@% %@NL@% %@4@% The /Sq option generates an editor-based listing file just as the /Se%@EH@% option does, but it also adds a source-line index to the end of the listing file. This index contains pairs of corresponding line numbers for the listing file and appropriate source files. The QuickC/QuickAssembler environment uses this information to let you move from a source file to the corresponding position in a listing file.%@NL@% %@NL@% %@4@% When you create a listing file from within the QuickC/QuickAssembler%@EH@% environment, QC.EXE automatically passes this option to the assembler. The /Sq option has no effect unless /l or /Fl is also specified.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /l /Sq file.asm%@AE@%%@NL@% %@NL@% %@CR:IXB.76 @%%@CR:IXB.77 @%%@CR:IXB.78 @%%@NL@% %@2@%%@CR:SCB.14 @%%@AB@%B.14 Listing False Conditionals%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /Sx%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXB.79 @% The /Sx option directs QuickAssembler to copy to the assembly listing all%@EH@% statements forming the body of conditional-assembly blocks whose condition is false. If you do not give the /Sx option in the command line, QuickAssembler suppresses all such statements. The /Sx option lets you display conditionals that do not generate code. Conditional-assembly directives are explained in Chapter 12%@BO: a2236@%, "Controlling Assembly Output."%@NL@% %@NL@% %@4@%%@CR:IXB.80 @%%@CR:IXB.81 @%%@CR:IXB.82 @%%@CR:IXB.83 @%%@CR:IXB.84 @%%@CR:IXB.85 @% The %@AB@%.LFCOND%@AE@%, %@AB@%.SFCOND%@AE@%, and %@AB@%.TFCOND%@AE@% directives can override the effect of%@EH@% the /Sx option, as described in Section 12.3.2%@BO: a518f@%, "Controlling Listing of Conditional Blocks." The /Sx option does not affect the assembly listing unless you direct the assembler to create an assembly-listing file.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%QCL /Sx file,,;%@AE@%%@NL@% %@NL@% %@4@% Listing of false conditionals is turned on when FILE.ASM is assembled.%@EH@% Directives in the source file can override the /Sx option to change the status of false-conditional listing.%@NL@% %@NL@% %@CR:IXB.86 @%%@CR:IXB.87 @%%@CR:IXB.88 @%%@CR:IXB.89 @%%@CR:IXB.90 @%%@CR:IXB.91 @%%@NL@% %@2@%%@CR:SCB.15 @%%@AB@%B.15 Controlling Display of Assembly Statistics%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /v%@EH@%%@NL@% /t%@NL@% %@NL@% %@4@% The /v and /t options specify the level of information displayed to the%@EH@% screen at the end of assembly (/v is a mnemonic for verbose; /t is a mnemonic for terse).%@NL@% %@NL@% %@4@% If neither option is given, QuickAssembler outputs a line telling the%@EH@% amount of symbol space free and the number of warnings and errors.%@NL@% %@NL@% %@4@% If the /v option is given, QuickAssembler also reports the number of lines%@EH@% and symbols processed.%@NL@% %@NL@% %@4@% If the /t option is given, QuickAssembler does not output anything to the%@EH@% screen unless errors are encountered. This option may be useful in batch or make files if you do not want the output cluttered with unnecessary messages.%@NL@% %@NL@% %@4@% If errors are encountered, they will be displayed whether these options%@EH@% are given or not.%@NL@% %@NL@% %@CR:IXB.92 @%%@CR:IXB.93 @%%@CR:IXB.94 @%%@CR:IXB.95 @%%@NL@% %@2@%%@CR:SCB.16 @%%@AB@%B.16 Setting the Warning Level%@AE@%%@EH@%%@NL@% %@NL@% %@4@% %@AB@%Syntax%@AE@%%@EH@%%@NL@% %@NL@% %@4@% /W{0 | 1 | 2}%@EH@%%@NL@% /w%@NL@% %@NL@% %@4@% The /W option sets the assembler warning level. QuickAssembler gives%@EH@% warning messages for assembly statements that are ambiguous or questionable but not necessarily illegal. Some programmers purposely use practices that generate warnings. By setting the appropriate warning level, they can turn off warnings if they are aware of the problem and do not wish to take action to remedy it. The /w option is equivalent to /W0.%@NL@% %@NL@% %@4@%%@CR:IXB.96 @%%@CR:IXB.97 @%%@CR:IXB.98 @% QuickAssembler has three levels of errors, as shown in Table B.1.%@EH@%%@NL@% %@NL@% %@AB@%Table B.1 Warning Levels%@AE@%%@NL@% %@NL@% %@AB@%Level%@AE@% %@AB@%Type%@AE@% %@AB@%Description%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% 0 Severe errors Illegal statements%@NL@% %@NL@% 1 Serious Ambiguous statements or questionable warnings programming practices%@NL@% %@NL@% 2 Advisory Statements that may produce inefficient warnings code%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% The default warning level is 1. A higher warning level includes all of the%@EH@% messages reported by a lower level. Level 2 includes severe errors, serious warnings, and advisory warnings. If severe errors are encountered, no object file is produced.%@NL@% %@NL@% %@4@% Warning level 0 reports error messages in the range 1000-2999. Warning%@EH@% level 1 reports warning and error messages in the ranges 1000-2999 and 4000-4999. Warning level 2 reports all warning and error messages, including those numbered 5000 and above.%@NL@% %@NL@% %@NL@% %@NL@% ────────────────────────────────────────────────────────────────────────────%@NL@% %@1@%%@CR:CHC @%%@AB@%Appendix C: Reading Assembly Listings%@AE@%%@EH@%%@NL@% %@NL@% %@NL@% %@4@%%@CR:IXC.1 @%%@CR:IXC.2 @% QuickAssembler creates an assembly listing of your source file whenever%@EH@% you give an assembly-listing option on the QCL command line or select a listing file option in the Assembler Flags dialog box. The assembly listing contains both the statements in the source file and the object code (if any) generated for each statement. The listing also shows the names and values of all labels, variables, and symbols in your source file.%@NL@% %@NL@% %@4@%%@CR:IXC.3 @% The assembler creates tables for macros, structures, records, segments,%@EH@% groups, and other symbols. These tables are placed at the end of the assembly listing (unless you suppress them with the QCL /Sn option). QuickAssembler lists only the types of symbols encountered in the program. All symbol names will be shown in uppercase letters unless you choose Preserve Case or Preserve Extrn from the Assembler Flags dialog box or use a QCL option (/Cx or /Cl) that supports case sensitivity.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCC.1 @%%@AB@%C.1 Reading Code in a Listing%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The assembler lists the code generated from the statements of a source%@EH@% file. Each line has the syntax shown below:%@NL@% %@NL@% %@4@% %@AI@%offset%@AE@% [[%@AI@%code%@AE@%]] %@AI@%statement%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The %@AI@%offset%@AE@% is the offset from the beginning of the current segment to the%@EH@% code. If the statement generates code or data, %@AI@%code%@AE@% shows the numeric value in hexadecimal if the value is known at assembly time. If the value is calculated at run time, the assembler indicates what action is necessary to compute the value. The %@AI@%statement%@AE@% is the source statement shown exactly as it appears in the source file, or as expanded by a macro.%@NL@% %@NL@% %@4@%%@CR:IXC.4 @% If any errors occur during assembly, each error message and error number%@EH@% will appear directly below the statement where the error occurred. An example of an error line and message is shown below:%@NL@% %@NL@% %@AS@%0012 E8 001C R call doit%@AE@%%@NL@% %@AS@%test.ASM(46): error A2071: Forward needs override or FAR%@AE@%%@NL@% %@CR:IXC.5 @%%@CR:IXC.6 @%%@CR:IXC.7 @%%@CR:IXC.8 @%%@CR:IXC.9 @%%@CR:IXC.10 @%%@CR:IXC.11 @%%@CR:IXC.12 @%%@CR:IXC.13 @%%@CR:IXC.14 @%%@NL@% %@4@%%@CR:IXC.15 @%%@CR:IXC.16 @%%@CR:IXC.17 @%%@CR:IXC.18 @%%@CR:IXC.19 @%%@CR:IXC.20 @%%@CR:IXC.21 @% The assembler uses the symbols and abbreviations in Table C.1 to indicate%@EH@% addresses that need to be resolved by the linker or values that were generated in a special way.%@NL@% %@NL@% %@AB@%Table C.1 Symbols and Abbreviations in Listings%@AE@%%@NL@% %@NL@% %@AB@%Character%@AE@% %@AB@%Meaning%@AE@%%@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% R Relocatable address (linker must resolve)%@NL@% %@NL@% E External address (linker must resolve)%@NL@% %@NL@% ---- Segment/group address (linker must resolve)%@NL@% %@NL@% = %@AB@%EQU%@AE@% or equal-sign (%@AB@%=%@AE@%) directive%@NL@% %@NL@% %@AI@%nn%@AE@%: Segment override in statement%@NL@% %@NL@% %@AI@%nn%@AE@%/ %@AB@%REP%@AE@% or %@AB@%LOCK%@AE@% prefix instruction%@NL@% %@NL@% %@AI@%nn%@AE@%[%@AI@%xx%@AE@%] %@AB@%DUP%@AE@% expression: %@AI@%nn%@AE@% copies of the value %@AI@%xx%@AE@%%@NL@% %@NL@% %@AI@%n%@AE@% Macro-expansion nesting level (+ if more than nine)%@NL@% %@NL@% C Line from include file%@NL@% %@NL@% ──────────────────────────────────────────────────────────────────────────%@NL@% %@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@4@% The sample listing shown in this section is produced using the /Se option,%@EH@% which produces an editor-oriented listing. The QuickC/QuickAssembler environment always produces this kind of listing. The editor-oriented environment produces no page headings and is thus ideal for viewing within the environment or another editor. If you are using QCL to generate a listing file that you intend to print, you may want to generate a printer-oriented listing file by giving the /Sp option:%@NL@% %@NL@% %@AS@%QCL /l /Sp listdemo.asm%@AE@%%@NL@% %@NL@% %@4@% The code portion of the resulting listing is shown below. The tables%@EH@% normally seen at the end of the listing are explained later, in Sections C.2%@BO: ff16e@%-C.7.%@NL@% %@NL@% %@AS@%Microsoft(R) QuickC with QuickAssembler Version 2.01 Listing features demo%@AE@%%@NL@% %@NL@% %@AS@% PAGE 65,132%@AE@%%@NL@% %@AS@% TITLE Listing features demo%@AE@%%@NL@% %@AS@% C INCLUDE dos.mac%@AE@%%@NL@% %@AS@% C StrAlloc MACRO name,text%@AE@%%@NL@% %@AS@% C name DB &text%@AE@%%@NL@% %@AS@% C DB 13d,10d%@AE@%%@NL@% %@AS@% C l&name EQU $-name%@AE@%%@NL@% %@AS@% C ENDM%@AE@%%@NL@% %@NL@% %@AS@%= 0080 larg EQU 80h%@AE@%%@NL@% %@NL@% %@AS@% DOSSEG%@AE@%%@NL@% %@AS@% .MODEL small%@AE@%%@NL@% %@NL@% %@AS@%0100 .STACK 256%@AE@%%@NL@% %@NL@% %@AS@% color RECORD b:1,r:3=1,i:1=1,f:3=7%@AE@%%@NL@% %@NL@% %@AS@% date STRUC%@AE@%%@NL@% %@AS@%0000 05 month DB 5%@AE@%%@NL@% %@AS@%0001 07 day DB 7%@AE@%%@NL@% %@AS@%0002 07C3 year DW 1987%@AE@%%@NL@% %@AS@%0004 date ENDS%@AE@%%@NL@% %@NL@% %@AS@%0000 .DATA%@AE@%%@NL@% %@AS@%0000 1F text color <>%@AE@%%@NL@% %@AS@%0001 09 today date <9,22,1987>%@AE@%%@NL@% %@AS@%0002 16%@AE@%%@NL@% %@AS@%0003 07C3%@AE@%%@NL@% %@NL@% %@AS@%0005 0064[ buffer DW 100 DUP(?)%@AE@%%@NL@% %@AS@% ????%@AE@%%@NL@% %@NL@% %@NL@% %@NL@% %@AS@% StrAlloc ending,"Finished."%@AE@%%@NL@% %@AS@%00CD 46 69 6E 69 73 68 65 1 ending DB "Finished."%@AE@%%@NL@% %@AS@%00D6 0D 0A 1 DB 13d,10d%@AE@%%@NL@% %@NL@% %@AS@%0000 .CODE%@AE@%%@NL@% %@NL@% %@AS@%0000 B8 ---- R start: mov ax,@DATA%@AE@%%@NL@% %@AS@%0003 8E D8 mov ds,ax%@AE@%%@NL@% %@NL@% %@AS@%0005 B8 0063 mov ax,'c'%@AE@%%@NL@% %@AS@%0008 26: 8B 0E 0080 mov cx,es:larg%@AE@%%@NL@% %@AS@%000D BF 0052 mov di,82%@AE@%%@NL@% %@AS@%0010 F2/ AE repne scasb%@AE@%%@NL@% %@AS@%0012 57 push di%@AE@%%@NL@% %@NL@% %@AS@% EXTRN work:NEAR%@AE@%%@NL@% %@AS@%0013 E8 0000 E call work%@AE@%%@NL@% %@NL@% %@AS@%0016 B8 170C mov ax,4C00%@AE@%%@NL@% %@AS@%listdemo.ASM(40): error A2107: Non-digit in number%@AE@%%@NL@% %@AS@%0019 CD 21 int 21h%@AE@%%@NL@% %@NL@% %@AS@%001B END start%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCC.2 @%%@AB@%C.2 Reading a Macro Table%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXC.22 @%%@CR:IXC.23 @% A macro table at the end of a listing file gives the names and sizes (in%@EH@% lines) of all macros called or defined in the source file. The macros appear in alphabetical order.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%Macros:%@AE@%%@NL@% %@NL@% %@AS@% N a m e Lines%@AE@%%@NL@% %@NL@% %@AS@%STRALLOC . . . . . . . . . . . . 3%@AE@%%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCC.3 @%%@AB@%C.3 Reading a Structure and Record Table%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXC.24 @%%@CR:IXC.25 @%%@CR:IXC.26 @%%@CR:IXC.27 @% All structures and records declared in the source file are given at the%@EH@% end of the listing file. The names are listed in alphabetical order. Each name is followed by the fields in the order in which they are declared.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%Structures and Records:%@AE@%%@NL@% %@NL@% %@AS@% N a m e Width # fields%@AE@%%@NL@% %@AS@% Shift Width Mask Initial%@AE@%%@NL@% %@NL@% %@AS@%COLOR . . . . . . . . . . . . . 0008 0004%@AE@%%@NL@% %@AS@% B . . . . . . . . . . . . . . 0007 0001 0080 0000%@AE@%%@NL@% %@AS@% R . . . . . . . . . . . . . . 0004 0003 0070 0010%@AE@%%@NL@% %@AS@% I . . . . . . . . . . . . . . 0003 0001 0008 0008%@AE@%%@NL@% %@AS@% F . . . . . . . . . . . . . . 0000 0003 0007 0007%@AE@%%@NL@% %@AS@%DATE . . . . . . . . . . . . . . 0004 0003%@AE@%%@NL@% %@AS@% MONTH . . . . . . . . . . . . 0000%@AE@%%@NL@% %@AS@% DAY . . . . . . . . . . . . . 0001%@AE@%%@NL@% %@AS@% YEAR . . . . . . . . . . . . . 0002%@AE@%%@NL@% %@NL@% %@4@% The first row of headings only applies to the record or structure itself.%@EH@% For a record, the "Width" column shows the width in bits while the "# fields" column tells the total number of fields.%@NL@% %@NL@% %@4@% The second row of headings applies only to fields of the record or%@EH@% structure. For records, the "Shift" column lists the offset (in bits) from the low-order bit of the record to the low-order bit in the field. The "Width" column lists the number of bits in the field. The "Mask" column lists the maximum value of the field, expressed in hexadecimal. The "Initial" column lists the initial value of the field, if any. For each field, the table shows the mask and initial values as if they were placed in the record and all other fields were set to 0.%@NL@% %@NL@% %@4@% For a structure, the "Width" column lists the size of the structure in%@EH@% bytes. The "# fields" column lists the number of fields in the structure. Both values are in hexadecimal.%@NL@% %@NL@% %@4@% For structure fields, the "Shift" column lists the offset in bytes from%@EH@% the beginning of the structure to the field. This value is in hexadecimal. The other columns are not used.%@NL@% %@NL@% %@NL@% %@2@%%@CR:SCC.4 @%%@AB@%C.4 Reading a Segment and Group Table%@AE@%%@EH@%%@NL@% %@NL@% %@4@%%@CR:IXC.28 @%%@CR:IXC.29 @%%@CR:IXC.30 @%%@CR:IXC.31 @% Segments and groups used in the source file are listed at the end of the%@EH@% program with their size, align type, combine type, and class. If you used simplified segment directives in the source file, the actual segment names generated by QuickAssembler will be listed in the table.%@NL@% %@NL@% %@4@% %@AB@%Example%@AE@%%@EH@%%@NL@% %@NL@% %@AS@%Segments and Groups:%@AE@%%@