SlideShare a Scribd company logo
User's Guide
Borland"
TUrbo Assembler"
I.
User's Guide
Borland®
Turbo Assemble~
Borland International, Inc., 100 Borland Way
P.O. Box 660001, Scotts Valley, CA 95067-0001
Borland may have patents and/or pending patent applications covering subject matter in this document. The
furnishing of this document does not give you any license to these patents.
COPYRIGHT ©1988, 1996 Borland International. All rights resewed. All Borland product names are trademarks or
registered trademarks of Borland International, Inc. Other brand and product names are trademarks or registered
-trademarks of theirrespective holders.
Printed in the U.S.A.
LSM1350WW21774 1EOR0196
9697989900-9 8 7 6 5 4
HI
The LENGTH unary operator . . . . . . . . . 64
The SIZE unary operator . . . . . . . . . . . . 65
The WIDTH unary operator . . . . . . . . . . 65
MASK unary operator. . . . . . . . . . . . . . 65
General arithmetic operators . . . . . . . . . . . 66
Simple arithmetic operators . . . . . . . . . . 66
Logical arithmetic operators .......... 66
Bit shift operators . . . . . . . . . . . . . . . . 67
Comparison operators. . . . . . . . . . . . . . 67
Setting the address subtype of an
expression. . . . . . . . . . . . . . . . . . . . 67
Obtaining the type of an expression. . . . . . 68
Overriding the segment part of an
address expression. . . . . . . . . . . . . . . 69
Obtaining the segment and offset of an
address expression. . . . . . . . . . . . . . . 69
Creating an address expression using the
location counter ................ 70
Determining the characteristics of an
expression. . . . . . . . . . . . . . . . . . . . 70
Referencing structure, union, and table
member offsets. . . . . . . . . . . . . . . . . 71
Describing the contents of an address. . . . . 71
Implied addition. . . . . . . . . . . . . . . . . 72
Obtaining the high or low byte values
of an expression . . . . . . . . .. . . . . . . 72
Specifying a 16- or 32-bit expression 72
Chapter 6
Choosing processor directives
and symbols 75
iAPx86 processor directives. . . . . . . . . . . . 76
Predefined symbols . . . . . . . . . . . . . . . . 77
8087 coprocessor directives . . . . . . . . . . . . 78
Coprocessor emulation directives . . . . . . . . 79
Chapter 7
Using program models and
'segmentation 81
The MODEL directive . . . . . . . . . . . 82
Symbols created by the MODEL directive . . . 84
The @Model symbol. . . . . . . . . . . . . . . 84
The @32Bit symbol. . . . . . . . . . . . . . . . 85
The @CodeSize symbol . . . . . . . . . . . . . 85
The @DataSize symbol . . . . . . . . . . . . . 85
The @Interface symbol . . . . . . . . . . . .. 85
Simplified segment directives . . . . . . . . . . 86
Symbols created by the simplified segment
directives .................... 87
The STARTUPCODE directive .......... 87
The @Startup symbol . . . . . . . . . . . . . . 87
The EXITCODE directive. . . . . . . . . . . . 87
Defining generic segments and groups. . . . . 88
ii
The SEGMENT directive. . . . . . . . . . . . . 88
Segment combination attribute. . . . . . . . . 88
Segment class attribute . . . . . . . . . . . . . 89
Segment alignment attribute . . . . . . . . . . 89
Segment size attribute. . . . . . . . . . . . . . 90
Segment access attribute............. 90
The ENDS directive. . . . . . . . . . . . . . . . 90
The GROUP directive. . . . . . . . . . . . . . . 91
The ASSUME directive. . . . . . . . . . . . . . . 91
Segment ordering . . . . . . . . . . . . . . . . . 92
Changing a module's segment ordering ... 92
The .ALPHA directive . . . . . . . . . . . . 93
The SEQ directive. . . . . . . . . . . . . . . 93
DOS ordering of segments: the DOSSEG
directive . . . . . . . . . . . . . . . . . . . . . 93
Changing the size of the stack. . . . . . . . . . 93
ChapterS
Defining data types 95
Defining enumerated data types. . . . 95
Defining bit-field records ............. 96
Defining structures and unions. . . . . . . . . . 98
Opening a structure or union definition . . . . 98
Specifying structure and union members . . . 98
I Df~E~~u.c~~~ ~~~~e.r ~a~~l~ ~~~. . . . 99
Aligning structure members . . . . . . . . . . 99
Closing a structure or union definition. . . . . 99
Nesting structures and unions. . . . . . . . . .100
Including one named structure within
another.......................101
Using structure names in expressions . . . . .102
Defining tables................... 102
Overriding table members. . . . . . . . . . . .104
Defining a named type. . . . . . . . . . . . . . 104
Defining a procedure type. . . .. . . . . . . . 105
Defining an object. . . . . . . . . . . . . . . . . 105
The TBLPTR directive. . . . . . : . . . . . . . .106
Symbols defined by the extended STRUC
directive. . . . . . . . . . . . . . . . . . . . . .107
Chapter 9
Setting and using the
location counter 109
The $ location counter symbol . . . . . . . . . 109
Location counter directives ........... 110
The ORG directive. . . . . . . . . . . . . . . . .110
The EVEN and EVENDATA directives ....112
The ALIGN directive . . . . . . . . . . . . . . .112
Defining labels. . . . . . . . . . . . . . . . . . . 113
The : operator. . . . . . . . . . . . . . . . . . . .113
Contents
Introduction 1
New features . . . . . . . . . . . .'. . . . " . . .2
Hardware and software requirements . . . . . .2
About the manuals. . . . . . . . . . . . . . . . . .2
Typographic conventions. . . . . . . . . . . . . .3
Software registration and technical support ... 4
Chapter 1
Getting started with Turbo Assembler 5
Installing Turbo Assembler. . . . . . . . . . . . . 5
The Turbo Assemblers. . . . . . . . . . . . . . . .6
Utility and example programs . . . . . . . . . . .6
Online Help . . . . . . . . . . . . . . . . . . . . . .7
Writing your first Turbo Assembler
program ...........' ............ 7
Assembling your first program. . ~ . . . . . . . .8
Linking your first program . . . . . . . . . . . . .9
Recommended reading . . . ... . . . . . . . . . . 9
Chapter 2 .
Using directives and switches 11
Starting Turbo Assembler ............. 11
Command-line options . . . . . . . . . . . . . . 13
Indirect command files. . . . . . . . . . . . . . . 26
The configuration file . . . . . . . . . . . . . '.' 27
Chapter 3
General programming concepts 29
Turbo Assembler Ideal mode. . . . . . .. . . . 29
Why use Ideal mode? . . . . . . . . . . . . . . . 30
Entering and leaving Ideal mode. . . . . . . . . 30
MASM and Ideal mode differences . . . . ... 31
Expressions and operands . . . . . . . . . . . 31
Operators . . . . . . . . '.' . . . . . . . . . . . 32
Suppressed fixups . : . . . . . . . . . . . . . . 32
Operand for BOUND instruction . . . . . . . 32
Segments and groups . . . . . . . . . . . . . . 33
Accessing data in asegment belonging
to.a group . . . . . . . . . . . . . . . . . . . . 33
Commenting the program. . . . . . . . . . . . . 35
Comments at the end of the line . . . . . . . . . 35
The COMMENT directive. . . . .. . . . . . . . 35
Extending the line. . . . . . . . . . . . . . . . . . 36
Using INCLUDE files " . . . .'. . . . . . . . . 37
Predefined symbols . . . . . . . . . . . . . . . . 37
Assigning values to symbols . . . .. . . . . . . 38
General module structure . . . . . . . . . . . . . 38
The VERSION directive.............. 39
The NAME directive. . . . . . . . . . . . . . . 40
The END directive . . . . . . . . . . . . . . . . 40
Displaying a message during assembly. . . . . 40
Displaying warning messages . . . . . . . . . . 41
Multiple error-message reporting . . . . . . . . 42
Chapfer 4 .
Creating object-oriented programs 43
Terminology. . . . . . . . . . . . . . . . . . . . . 43
Why use objects in Turbo Assembler? . . . . . 44
Whatis an object? . . . . . . . . . . . .. . . . . 44
A sample object . . . . . . . . . . . . . . . . . . 45
Declaring objects. .. . . . . . . . . .. . . . . . 45
Declaring a base object.......... '.... 45
Declaring a derived object. . . . . . . . . . . . 47
Declaring a method procedure . . . .. . . . . . 48
The virtual method table; . . . . . . . : . . . . . 49
Initializing the virtual method table . . . . . . 50
Calling an object method. . . . . . . . . . . . . . 50
Calling a static method . . . . . . . . . . . . . . 50
Calling a virtual method . . . . . . . . . . . . . 51
Calling ancestor virtual methods . . . . . . . . 53
More on calling methods. . . . . . . . . . . . . 54
Creating an instance of an object . . . . . . . . . 55
Programming form for objects . . . . . . . . . . 55
ChapterS
Using expressions and
symbol values 57
Constants. . . . . . . . . . . . . . . . . . . . . . . 57
Numeric constants. . . . . . . . . . . . . . . . . 57
Changing the default radix . . . . . . . . . . . 58
String constants ; . . . . . . . . . . . . . . . . . 58
Symbols. . . . ...'. .... . . .'. . . . . . .. . . . . 59
Symbol names . . . . . . . . . . . . . . . . . . . 59
Symbol types. . . . . . . . . . . . . . . . . . . . 59
Simple address subtypes . . . . . . . . . . . . . 60
Describing a complex address subtype..... 61
Expressions.................... , .61
Expression precision. . . . . . . . . . . . . . . . 62
Constants in expressions . . . . . . . . . . . . . 62
Symbols in expressions. . . . . . . . . . . . . . 62
Registers. . . . . . . .. . . . . . . . . . . . . . 62
Standard symbol values. . . . . . . . . . . . . 63
Simple symbol values . . . . . . . . . . . . . . 63
The LABEL directive. . . . . . . . . . . . . . . 113
The:: directive................... 114
Chapter 10
Declaring procedures 115
Procedure definition syntax. . . . . . . . . . . 115
Declaring NEAR or FAR procedures . . . . . 116
Declaring a procedure language. . . . . . . . 118
Specifying a languagemodifier......... 119
Defining arguments and local variables. . . . 120
ARC and LOCAL syntax . . . . . . . . . . . . 121
The scope of ARC and LOCAL variable
names....................... 122
Preserving registers . . . . . . . . . . . . . . . 123
Defining procedures using procedure
types .......................123
Nested procedures and scope rules . . . . . . 124
Declaring method procedures for objects. . . 125
Using procedure prototypes . . . . . . . . . . 126
Chapter 11
ContrOlling the scope of symbols 129
Redefinable symbols. . . . . . . . . . . .. . . 129
Block scoping . . . . . . . . . . . . . . . . . . . 130
The LOCALS and NOLOCALS
directives. . . . . . . . . . . . . . . . . . . . . 130
MASM block scoping . . . . . . . . . . . . . . 131
MASM-style locallabels ............. 131
Chapter 12
Allocating data 133
Simple data directives . . . . . . . . . . . . . . 134
Creating an instance of a structure or
union . . . . '. . . . . . . . . . . . . . . . . . . 137
Initializing union or structure instances. . . . 137
Creating an instance of a record . . . . : . . . 140
Initializing record instances .......... 140
Creating an instance of an enumerated
data type . . . . . . . . . . . . . . . . . . . . . 141
Initializing enumerated data type
instances. . . . . . . . . . . . . . . . . . . . .141
Creating an instance of a table . . . . . . . . . 141
Initializing table instances ............ 142
Creating and initializing a named-type
instance. . . . . . . . . . . . . . . . . . . . . . 142
Creating an instance of an object. . . . . . . . 143
Creating an instance of an object's virtual
methoa table ................... 143
iii
Chapter 13
Advanced coding instructions 145
Intelligent code generation: SMART and
NOSMART .................... 145
Extended jumps. . . . . . . . . . . . . . . . . . 146
Additional 80386 LOOP instructions . . . . . 147
Additional 80386 ENTER and LEAVB
instructions. . . . . . . . . . . . . . . . . . . . 147
Additional return instructions . . . . . . . . . 147
Additional IRET instructions . . . . . . . . . . 148
Extended PUSH and POP instructions . . . . 148
Multiple PUSH and POPs . . . . . . . . . . . .148
Pointer PUSH and POPs .............148
PUSHing constants on the 8086 processor. . .149
Additional PUSHA, paPA, PUSHF and
POPF instructions. . . . . . . . . . . . . . . . 149
The PUSHSTATE and POPSTATE
instructions. . . . . . . . . . . . . . . . . . . . 149
Extended shifts . . . . . . . . . . . . . . . . . . 151
Forced segment overrides: SEGxx '
instructions. . . . . . . . . . . . . . . . . . . . 151
Additipnal smart flag instructions . . . . . . . 151
Additional field value manipulation
instructions. . . . . . . . . . . . . . . . . . . . 152
The SETFIELD instruction . . . . . . . . . . . .152
The CETFIELD instruction. . . . . . . . . . . .153
Additional fast immediate multiply
instruction . . . . . . . . . . . . . . . . . . . . 154
Extensions to necessary instructions for the
80386 processor . . . . . . . . . . . . . . . . . 154
Calling procedures with stack frames. . . . . 155
Calling procedures that contain
RETURNS. . . . . . . . . . . . . . . . . . . . .156
Calling procedures that have been
prototyped . . . . . . . . . . . . . . . . . . . .156
Calling metho<i procedures for objects:
CALL..METHOD ................157
Tail recursion for object methods:
JMP..METHOD..................158
Additional instruction for. object-oriented
programming . . . . ; . . . . . . . . . . . . . 158
Chapter 14
Using macros 159
Textmacros .................... 159
Defining text macros with the EQU
directive. . . . . . . . .. . . . . . . . . . . . .159
String macro manipulation directives. . . . . 160
The CATSTR directive.............. 160
The SUBSTR directive. . . . . . . . . . . . . .160
The INSTR directive. . . . . . . . . . . . . . .161
The SIZESTR directive . . . . . . . . . . . . .161
Text macro manipulation examples. . . ... .161
Multiline macros . . . . . . . . . . . . . . . . .' 161
The multiline macro body. . . . . . . . . . . . 162
Using & in macros. . . ............. 162
Including comments in macro bodies..... 163
Local dummy arguments. . . . . . . . . . . .163
The EXITM directive. . . . . . . . . . . . . . .164
Tags and the GOTO directive . . . . . . .'. .164
General multiline macros . . . . . . . . . . . . 165
Invoking a general multiline macro. . . . . .166
The < > literal s~ing brackets . . . . . . . 166
The ! character . . . . . . . . . .. . . .. . 167
The % expression evaluation character. . 167
Redefining a general multiline macro..... 168
Deleting a general multiline macrQ:
The PURGE directive . . . . . . . . . . . . .168
Defining nested and recursive macros . . . .168
The count repeat macro . . . . . . . . . . . . . 169
The WHILE directive. . ., . . . . . . . . . . . 170
String repeat macros. . . . . . . . . . . . . . . 170
The %immediate macro directive. . . . . . . 171
Including multiline macro expansions
in the list file ................... 172
Saving the current operating state... . . . . . 172
Chapter 15
Using conditional directives 175
General conditional directives syntax ..... 175
IFxxx conditional assembly directives... ~ . 175
ELSEIFxxx conditional assembly
directives. . . . . . . . . . . . . . . . . . . . . 177
ERRxxx error-generation directives ...... 177
Specific directive descriptions . . . . . . . . . 178
Unconditional error-generation directives .. 178
Expression-conditional directives ....... 178
Symbol-definition conditional directives ... 179
Text-string conditional directives ....... 180
Assembler-pass conditionals . . . . . . . . . . 182
Including conditionals in the list file. ... . . . 182
Chapter 16
Interfacing with the linker 183
Publishing symbols externally . . . . . . . . . 183
Conventions for a particular language .. . . 183
Declaring public symbols . . . . . . . . . . . . 184
Declaring librarysymbols. '.' . . . . . . . . . 184
iv
Defining external symbols . . . . . . . . . . . .185
Defining global symbols . . . . . . . . . . . . .185
Publishing a procedure prototype. . . . . . . .185
Defining communal variables . . . .. . . . . .186
Including a library . . . . . . . . . . ..' . . . . 187
The ALIAS directive . . . . . . . . . . . . . . . 187
Chapter 17
Generating alisting 189
Listing format . . . . . . . . . . . . . . 189 .
General list directives............... 190
Include file list directives.. . . . . . . . . . . . 191
Conditional list directives ............. 191
Macro list directives. . . . . . . . . . . . . . . . 192
Cross-reference list directives .......... 193
Changing list format parameters. . . . . . . . 194
Chapter 18
Interfacing Turbo Assembler with
Borland C++ 197
Calling Turbo Assembler functions from
Borland C++ . . . . . . . . . . . . . . . . . . . 197
The framework. . . . . . . . . . . .. . . . . . .198
Linking assembly language modules
with C++ . ............. , ......198
Using Extern "c" to simplify linkage ...200
Memory models and segments. . . . . . . . .200
Simplified segment directives and
Borland C++ .................200
Old-style segment directives and
Borland C++ . . . . . . . . . . . . . . . . .202
Segment defaults: When is it necessary
to load segments? ..............203
Publics and externals. . '.' . . . . . . . . . . .205
Underscores and the C language ......205
The significance of uppercase and
lowercase. . . . . . . . . . . . . . . . . . .206
Label types. . . . . . . . . . . . . . . . . . .207
Far extemaJs ..................208
Linker command line . . . . . . . . . . . . . .209
Parameter passing. . . . . . . . . . . . . . . . .209
Preserving registers . . . . . . . . . ". . . . .213
Returningvalues ............ , ....214
Calling an assembler function from C++. . . .215
Writing C++ member functions in
assembly language............ : ...218
Pascal calling conventions . . . . . . . . . . . .220
Calling Borland C++ from
Turbo Assembler . . . . . . . . . . . . . . . . 221
Link in the Ct+ startup code...........221
The segment setup. . . . . . . . . . . . . . . . .221
Performing the call. . ; . . . . . . . . . . . . . 222
Calling a Borland C++ function from
Turbo Assembler . . . . . . . . . . . . . . . . 223
Appendix A
Program blueprints 227
Simplified segmentation segment ,
description .................... 227
DOS programs . . . . . . . . . . . . . . . . . . 229
DOS EXE program blueprint. . . . . . . . . . 229
COM program blueprint . . . . . . . . . . . . 230
Windows programs . . . . . . . . . . . . . . . 231
Windows DLL blueprint . . . . . . . . . . . . 231
Windows 16-bit application blueprint. . . . . 232
Windows 32-bit application blueprint. . . . . 232
OS/2 programs . . . . . . . . . . . . . . . . . . 233
OS/2 flat-model program blueprint. ..... 233
AppendixB
Turbo Assembler syntax summary 235
Lexical grammar . . . . . . . . . . . . . . . . . 235
MASM mode expression grammar . . . . . . 237
Ideal mode expression grammar. . . . . . . . 239
Keyword precedence............... 241
Ideal mode precedence . . . . . . . . . . . . . 241
MASM mode precedence. . . . . . . . . . . . 242
Keywords and predefined symbols. . . . . . 242
Directive keywords. . . . . . . . . . . . . . . . 242
AppendixC
MASM 6.1 compatibility 249
Basic data types. . . . . . . . . . . . . . . . . . 249
Signed types . . . . . . . . . . . . . . . . . . .250
Floating-point types. . . . . . . . . . . . : . .250
New decision and looping directives . . . . . 250
.IF .ELSE .ELSEIF .ENDIF. . . . . . . . . . . . 250
Example. . . . . . . . . . . . . . . . . . . . 251
.WHILE .ENDW .. " ............. 251
Example .................... 251
v
.REPEAT .UNTIL .UNTILCXZ .........252
Example ....................252
.BREAK .CONTINUE ...............252
Example ....................252
Logical operators . . . . . . . . . . . . . . . . . 253
Using flags in conditions............. 253
Text Macros . . . . . . . . . . . . . . . . . . . . 253
Macro repeat blocks with loop directives. . . 254
REPEAT loops . . . . . . . . . . . . . . . . . ..254
Example ....................254
FOR loops. . . . . . . . . . . . . . . . . . . . . .254
Example ....................254
FORC loops. . . . . . . . . . . . . . . . . . . . .255
Example ....................255
New Directives . . . . . . . . . . . . . . . . . . 255
ECHO directive . . . . . . . . . . . . . . . . . .255
EXTERNDEF directive ..............255
OPTION directive. . . . . . . . . . . . . . . . .256
CASEMAP: NONE/NOTPUBLIC/ALL ...256
DOTNAME/NODOTNAME . . . . . . . . .256
EMULATOR/NOEMULATOR ........256
EXPR16/EXPR32. . . . . . . . . . . . . . . . .256
LJMP/NOLJMP . . . . . . . . . . . . . . . . .256
NOKEYWORD: <keywordList>. . . . . ...256
PROC: PRIVATE/PUBLIC/EXPORT. . . . .257
SCOPED/NOSCOPED . . . _ . . . . . . . . .257
SEGMENT: USE16/USE32/FLAT.......257
Visibility in procedure declarations . . . . . . 257
Distance in procedure declarations. . . . . . . 257
SIZE operator in MASM mode . . . . . . . . . 257
Compatibility issues . . . . . . . . . . . . . . . 258
One-pass versus two-pass assembly ...... 258
Environment variables. . . . . . . . . . . . . . 259
Microsoft binary floating-point format . . . . 259
AppendixD
Error messages 261
Information messages . . . . . . . . . . . . . . 261
Warning and error messages . . . . . . . . . . 262
Index 281
Tables
1.1 Turbo Assemblers.................. 6
4.1 Object-oriented programming terrrUnology . 43
4.2 Symbols defined for objects. . . . . . . . . . . 44
5.1 Radixes....................... 57
5.2 Characters determining radixes . . . . . . . . 58
5.3 Numeric constants . . . . .. . . . . . . . . . . 58
5.4 Symbol types ................... 59
5.5 Address subtypes. . . . . . . . . . . . . . . . . 60
5.6 Complex address subtypes . . . . . . . . . . . 61
5.7 Distance syntax . . . . . . . . . . . . . . . . . . 61
5.8 Simple expressions. . . . . . . . . . . . . . . . 62
5.9 Standard symbols. . . . . . . . . . . . . . . . . 63
5.10 Values of symbols used by themselves .... 63
5.11 LENGTH operator return values........ 64
5.12 SIZE values . . . . . . . . . . . . . . . . . . . . 65
5.13 WIDTHvalues .................. 65
5.14 MASK return values ............... 66
5.15 Simple arithmetic operators........... 66
5.16 Logical arithmetic operators .......... 66
5.17 Bit shift operators ................. 67
5.18 Comparison operators. . . . . . . ....... 67
5.19 Type override operators ............. 67
5.20 TYPE values. . . . . . . . . . . . . . . . . . . . 68
5.21 Bit fields from SYMTYPE and .TYPE ..... 71
6.1 Processor directives................ 76
6.2 8087 coprocessor directives . . . . . . . . . . . 78
7.1 Standard memory models............ 83
7.2 Model modifiers ................. 84
7.3 Model modifiers . . . . . . . . . . . . . . . . . 85
7.4 Simplified segment directives . . . . . . . . . 86
7.5 Symbols from simplified segment
directives ...................... 87
7.6 Segment combination attribute......... 88
7.7 Segment alignment attribute . . . . . . . . . . 89
7.8 Segment size attribute values. . . . . . . . . . 90
7.9 Segment access attribute. . . . . . . . . . . . . 90
7.10 Stack size modification directives ....... 94
8.1 STRUC, UNION, and ENDS directives. . . 100
8.2 Block members . . . . . . . . . . . . . . . . . 101
8.3 Available modifiers. .. . . . . . . . . . . . . . 106
8.4 Symbols used or defined by STRUC . . . . 107
12.1 Data size directives ............... 134
13.1 Intelligent code generation directives .... 145
13.2 Return instructions. . . . . . . . . . . . . . . 147
13.3 Segment override instructions.. . . . . . . . 151
13.4 Smart flag instructions. . . . . . . . . . . . . 152
13.5 Instructions for setting and retrieving
values ....................... 152
vi
13.6 Instructions affected by SMALL and
LARGE ...................... 154
14.1 Dummy argument types ............ 166
14.2 Uses for the ! character . . . . . . . . . . . . . 167
15.1 Conditional assembly directives using
expressions .................... 178
15.2 Error-generation directives using
expressions . . . . . . ... . . . . . . . . . . . . 179
15.3 Evaluation of defined and undefined
symbol. . . . . . . . . . . . . . . .'. . . . . . . 179
15.4 Symbol-expression directives using
symbol_expr ................... 179
15.5 Error-generation directives...... ; .... 180
15.6 Conditional assembly directives using
text_strings ........ , ........... 180
15.7 Error-generation directive~ using
text_strings . . . . . . . . . . . ......... 181
18.1 Register settings when Borland C++
enters assembler . . . . . . . . . .. . . . . . . 203
A.l Default segments and types for TINY
memory model. . . . . . . . . ......... 227
A.2 Default segments and types for SMALL
memory model. . . . . . . . . ......... 227
A.3 Default segments and types for MEDIUM
memory model. . . . .. . . . ......... 228
A.4 Default segments and types for COMPACT
memory model..... , . . . ......... 228
A.5 Default segments and types for LARGE
or HUGE memory model . . . ........ 228
A.6 Default segments and types for Borland C++
HUGE (TCHUGE) memory model. ..... 229
B.l Turbo Assembler v1.0 keywords ....... 243
B.2 Turbo Assembler v2.0 new keywords .... 245
B.3 Turbo Assemblerv2.5 new keywords .... 246
B.4 /Turbo Assembler v3.0 new keywords . . . . 246
B.5 Turbo Assembler v3.1 new keywords .... 246
B.6 Turbo Assembl~r v3.2 new keywords . . . . 246
B.7 Turbo Assembler v4.0 new keywords . . . . 246
B.8 Turbo Assembler v5.0 new keywords . . . . 247
B.9 Options supported by OPTION. . . . . . . . 247
C.l Turbo Assembler types and their
equivalent directives .............. 249
C.2 Signed integer data types. . . . . . . . . . . . 250
C.3 Floating-point data types. . . . . . . . . . . . 250
C.4 New Turbo Assembler logical operators... 253
C.5 Return value of SIZE in MASM mode .... 257
Introduction
Welcome to Borland's Turbo Assembler,® a multi-pass assembler with forward-
reference resolution, assembly speeds of up to 48,000 lines per minute (on an IBM PS/2
model 60), Microsoft Macro Assembler (MASM) compatibility, and an optional Ideal
mode extended syntax. Whether you're a novice or an experienced programmer, you'll
appreciate these features and others we've provided to make programming in assembly
language easier. Here are the highlights-we'll describe them in detail later:
• Object-oriented programming capabilities
• 32-bit model and stack frame support
• Full 386, i486, and Pentium support
• Simplified segmentation directives
• Table support
• Enumerations
• Smart flag instructions
• Fast immediate multiply operation
• Multiline definition support
• VERSION specification directive
• Nested directives
• Quirks mode to emulate MASM
• Full source debugging output
• Cross-reference utility (TCREF)
• Configuration and command files
• File converter utility (converts C .h files to TASM .ash files)
• Procedure prototyping and argument checking capabilities
• Alias support
• Windows 95 flat thunking support
Turho Assembler is a powerful command-line assembler that takes your source (.ASM)
files and produces object (.OBD modules. You then use TLINK.EXE, Borland's high-
speed linker program, to link your object modules and create executable (.EXE) files.
Introduction 1
New features
Turbo Assembler version 5.0 incorporatesthe following new feature enhancements:
• Enhanced MASM compatibility, as described in "MASM 6.1 compatibility" on
page 249.
• Windows 95 flat thunking support with the -utthk command-line option. For more
information on thunking, refer to the sample program and documentation contained'
in the subdirectory  EXAMPLES THUNK95 off your main TASM directory.
Hardware.and software requirements
Turbo Assembler generates instructions for the 8086,80186,80286,80386, i486, Pentium,
and Pentium Pro, and compatible processors. Essentially Turbo Assembler runs on all
Intel-processor based computers, including all true compatibles. Turbo Assembler
also generates floating-point instructions for the 8087,80287, and 80387 numeric
coprocessors. (For more information about the instruction sets of the 80x86/80x87
families, consult the Intel data books.)
About the manuals
Turbo Assembler comes with the Turbo Assembler User's Guide (this book) and the Turbo
Assembler Quick Reference Guide. The User's Guide provides basic instructions for using
Turbo Assembler, explores how to interface Turbo Assembler with other languages, and
describes in detail the operators, predefined symbols, and directives Turbo Assembler
uses. The Quick Reference Guide is a handy guide to directives and processor and
coprocessor instructions.
Here's a more detailed look at what the User's Guide contains.
Chapter 1, "Getting started with Turbo Assembler," tells you how to install Turbo
Assembler on your system
Chapter 2, "Using directives and switches," describes how you can control the way the
assembler runs when you use directives and switches.
Chapter 3, "General programming concepts," discusses the differences between Ideal
and MASM modes, how to use predefined symbols, using comment characters, and so
forth.
~ Chapter 4, "Creating object-oriented programs," describes how you can use object-
~QJ oriented programming techniques in assembly language.
Chapter 5, "Using expressions and symbol values," talks about evaluating and
defining expressions and operators.
Chapter 6,'IIChoosing processor directives and symbols," tells you how to generate
code for particular processors.
2 Turbo Assembler User's Guide
Chapter 7, IIUsing program models and segmentation," talks about program models,
creating symbols, simplified segments, and ordering of segments.
Chapter 8, IIDefining data types," explains how to define structures, unions, tables,
bit-field records, and objects.
Chapter 9, IISetting and using the location counter," describes how and why you'd
want to use the location counter, as well as how to define labels.
Chapter 10, IIDeclaring procedures,"examines how to use various types of procedures,
and how to define and use arguments and local variables. .
Chapter 11, II Controlling the scope of symbols," discusses how you can limit or
expand the area in which a symbol has a particular value.
Chapter 12, IIAllocating data,"describes simple data directives, and how to create
instances of structures, unions, records, enumerated data types, tables, and objects.
Chapter 13, IIAdvanced coding instructions," covers Turbo Assembler's extended
instructions, including prototyping and calling language procedures.
Chapter 14, IIUsing macros," tells you how to use macros in your code.
Chapter 15, "Using conditional directives," talks about the directives that let you
execute your code conditionally.
Chapter 16, "Interfacing with the linker," describes how you can include libraries and
publish symbols as you link your code.
Chapter 17, IIGenerating a listing," talks about Turbo Assembler listing files and how
to use them.
Chapter 18, "Interfacing Turbo Assembler with Borland C++," explains how to use
Borland's line of C++ compilers with assembly language.
Appendix A, IIProgram blueprints," contains example program structures for
Windows and DOS programs.
Appendix B, "Turbo Assembler syntax summary," illustrates Turbo Assembler
expressions (be>th MASM and Ideal modes) in modified Backus-Naur form (BNF).
Appendix C, "MASM 6.1 compatibility," covers the differences between MASM and
Turbo Assembler MASM mode.
Appendix D, IIError messages," describes all the error messages1hat can be generated
when using Turbo Assembler: information messages, fatal error messages, warning
messages, and error messages.
Typographic conventions
When we talk about IBM PCs or compatibles, we're referring to any computer that uses
the 8088, 8086, 80186, 80286, 80386, i486, Pentium, and Pentium Pro processors (all of
these chips are commonly referred to as 80x86).
Introduction 3
The following typefaces are used in this book:
Italics
Boldface
CAPITALS
Monospace
Keycaps
In text, italics represent labels, placeholders, variables, and arrays. In
syntax expressions, placeholders are set in italics to indicate they are
user-defined.
Boldface is used ~ text for directives, instructions, symbols, and
operators, as well as for command-line options.
In text, capital letters are used to represent instructions, directives,
registers, and operators.
Monospace type is used to display any sample code or text that
appears on your screen, and any text that you must actually type to
assemble, link, and run a program
In text, keycaps indicate a key on your keyboard. It is often used when
describing a key you must press to perform a particular function; for
example, "Press Enter after typing yourprogram name at the prompt."
Software registration and technical support
The Borland® Assist program offers a range of technical support plans to fit the different
needs of individuals, consultants, large corporations, and developers. To receive help
with this product, send in the registration card and select the Borland Assist plan that
best suits your needs. North American customers can register by phone 24 hours a day
by calling 1-800-845-0147..
For additionaldetails on these and other Borland services, see the Borland Assist and
Services Guide included with thls product.
4 Turbo Assembler User's Guide
Getting started with Turbo Assembler
You might have heard that programming in assembly language is a black art suited only
to hackers and wizards. However, assembly language is nothing more than the human
form ofthe language of the computer. And, as you'd expect, the computer's language is
highly logical. As you might also expect, assembly language is very powerful-in fact,
assembly language is the only way to tap the full power of the Intel80x86 family, the
processors at the heart of the IBM PC family and compatibles.
You can write whole programs using nothing but assembly language or you can
mix assembly language with programs written in high-level languages such as
Borland® C++ and Borland® Pascal. Either way, assembly language lets you write small
and blindingly fast programs. In addition to the advantage of speed, assembly language
gives you the ability to control every aspect of your computer's operation, all the way
down to the last tick of the computer's system clock.
Installing Turbo Assembler·
The Turbo Assembler package consists of a set of executable programs, utilities, and
example programs. In addition, the package includes a Quick Reference Guide and this
User's Guide.
For instructions on installing Turbo Assembler, refer to the TSM_INST .TXT file on your
installation disk:
1 Insert the TASM Install disk in drive A of your computer.
2 User your text editor to open TSM_INST.TXT, or issue the following command at the
cominand line:
TYPE A:TSM_INST.TXT I MORE
Chapter 1, Getting started with Turbo Assembler 5
The Turbo Assemblers
The Turbo Assembler package comes complete with 3 different assemblers, as outlined
in Table 1.1:
Table 1.1
TASM.EXE
TASMX.EXE
TASM32.EXE
Turbo Assemblers
Real-mode assembler. Assembles 16- and 32-bit .oBJs using the 640K memory space
addressable by DOS. Produces only 16-bit debug information.
Protected-mode assembler. Assembles 16- and 32-bit .OBJs using memory above
640K. Produces only 16-bitdebug information.
Pr9tected-mode assembler. Assembles 16- and 32-bit .oBJs using memory above
640K. Produces only 32-bit debug information.
All three assemblers are capable of producingboth 16- and 32-bit object files, depending
on the directives contained in your assembler source files. If you produce a 16-bit object
file, then you must use the 16-bit linker (TLINK.EXE) to link your application. If you
produce a 32-bit object file, then you must use the 32-bit linker (TLINK32.EXE) to link
your application.
TASM.EXE is a real-mode assembler, meaning that it is capable ofusing only the lower
640K of memory addressable by DOS. If you're assembling larger applications, use
either TASMX.EXE or TASM32.EXE. Both of these assemblers use the DPMI server to
take advantage of extended memory.
The biggest difference between the three assemblers is the type of debug information
they produce when you assemble your source files with the /zi command-line option.
Both TASM.EXEand TASMX.EXE produce only 16-bit debug information.
TASM32.EXE produces only 32-bit debug information. If youplanto use Turbo
Debugger to debug your assembler application, then youmust assemble 16-bit files
with either TASM.EXE or TASMX.EXE. To produce 32-bit debug information, then you
must assemble your files with TASM32.EXE.
Utility and example programs
The Turbo Assembler package includes several utility programs to help you build
assembly programs. The utilities include the Turbo Linkers, the MAKE utility, the GREP
file search utility, and the resource compilers and linkers. These utility programs are
described in the online text files located in the DOC subdirectory located off your main
TASM directory.
To get you started writing assembler programs, the Turbo Assembler package includes
various example programs that demonstrate different assembler programming
techniques. The example programs, located in the. EXAMPLES directory under the
main TASM directory, even include complete 16- and 32-bit Windows assembly
programs.
6 Turbo Assembler User's Guide
Online Help
You can get online Help for Turbo Assembler using the Windows Help facility. To
access the online Help, do one of the following:
• From Windows, click the TASM Reference icon in the TASM program group
• From Windows, run the TASM.HLP file located in the  TASMBIN subdirectory
You can run TASM.HLP from a DOS box in Windows. On the DOS command line,
enter the following command from the  TASMBIN directory:
winhelp tasm.hlp
Writing your first Turbo Assembler program
If you have not yet written an assembly program, the DOS-based "Greetings, World!"
program is a good place to start. To begin writing this program, open your favorite
program editor and enter the following lines of code to create the HELLO.ASM
program:
.MODEL SMALL
.STACK lOOh
.DATA
TimePrompt
GoodMorningMessage
GoodAfternoonMessage
DefaultMessage
.CODE
start:
mov aX,@data
mov ds,ax
DB 'Is it after 12 noon (Y/N)?$'
DB 13,10, 'Good morning, world!' ,13,10, '$'
DB 13,10, 'Good afternoon, world!' ,13,10,'$'
DB 13,10,'Good day, world!' ,10,13, '$'
mov dx,OFFSET TimePrompt
iset DS to point to the data segment
ipoint to the time prompt
mov
int
mov
int
or
cmp
je
cmp
je
ah,9
21h
ah,l
21h
al,20h
al,'y'
IsAfternoon
al, 'n'
IsMorning
iDOS: print string
idisplay the time prompt
;DOS: get character
iget a single-character response
iforce character to lower case
;typed Y for afternoon?
ityped N for morning?
mov dx,OFFSET DefaultMessage ;default greeting
jmp DisplayGreeting
IsAfternoon:
mov dx,OFFSET GoodAfternoonMessage iafternoon greeting
.jmp DisplayGreeting
Chapter 1, Getting started with Turbo Assembler 7
IsMorning:
mov dX,OFFSET GOQdMorningMessage ibefore noon greeting
DisplayGreeting:
mov ah,9
int 21h
mov ah,4ch
mov aLO
int 21h
END start
iDOS: print string
idisplay the appropriate greeting
iDOS: terminate program
ireturn code will be 0
iterminate the program
After you've entered the preceding program, save it to disk as HELLO.ASM. (For
convenience, HELLO.ASM is supplied in the  EXAMPLES USRGUIDE directory
located under your main TASM directory.)
If you're familiar with high-level languages (such as C, C++, or Pascal), you might think
that HELLO.ASM is a bit long for a uGreetings, World!" program. Indeed, assembler
programs tend to be much longer than high-level language programs because each
high-level language statement actually breaks down to form many assembler
instructions. However, assembly language gives you complete freedom over the actual
instructions that are given to the computer's CPU. With assembly language, you can
write programs that tell the computer to do anything that it's.capable of doing.
,Assembling your first program
Now that you've saved HELLO.ASM, you'll want to run it. However, before you can
run it, you'll have to assemble it into an.OBJ file, and then'link the file to form an
executable program.
The assembly step turns your source code into an intermediate form called an object
module, and the linking step combines one or more object modules into an executable
program. You can do your assembling and linking from the command line.
To assemble HELLO.ASM, type the following line at the command line:
TASM hello
Unless you specify another file name, HELLO.ASM will be assembled to form the object
file HELLO.OBJ. (Note that you don't need to type in the file extension name; Turbo
Assembler assumes all source files end with .ASM.) If you entered the HELLO.ASM
program correctly, you'll see a listing similar to the following one displayed onscreen:
Turbo Assembler Version 5.0 Copyright (c) 1988, 1996 by Borland International, Inc.
Assembling file: HELLO.ASM
Error messages: None
Warning messages: None
Passes: 1
Remaining memory: 439K
If you get warnings or errors, they are displayed with the program line numbers to
indicate where they occurred. If you do get errors, edit HELLO.ASM make sure it's
8 Turbo Assembler User's Guide
precisely th~ same as the program shown above. After editing the program, reassemble
it with the TASM hello command.
Linking your first program
After you've successfully assembled HELLO.ASM, you'll need to link the program
using TLINK. At the command line, type:
TLINK hello
If no errors or warnings are reported, an executable file is created, named HELLO.EXE.
To run this program, enter the command HELLO from the command line.
Errors can occur during the linking process, although it's unlikely with this example
program. If you do receive linker errors, modify your code to exactly match the code
shown here, then assemble and link again.
Recommended reading
Although HELLO.ASM is a good program for testing TASM.EXE and TLINK.EXE, the
example is of little use if you're trying to learn assembly language. However, many
books are available that teach both the fundamentals and the advanced features of
assembly language. To help you get started with assembly language, refer to one or
more of the following book titles:
• Hummel, Robert 1. Programmers Technical Reference: Processor and coprocessor.
Emeryville, CA: Ziff Davis Press, 1992. .
• Mischel, Jim. Macro Magic with Turbo Assembler. New York, NY: John Wiley & Sons,
1993.
• Swan, Tom. Mastering Turbo Assembler, Second Edition. Indianapolis, IN: Sams
Publishing, 1995.
• Yao, Paul. Borland C++ 4.0 ProgrammingforWindows. New York, NT: Random House,
Inc., 1994. In particular, Part 6 of this book offers useful insights into programming
Windows prologue and epilogue code, along with code showing the Windows
callback mechanism.
In addition to these books, Intel Corporation offers fact sheets and reference manuals on
the workings of their processor products. Contact Intel at the following address:
Intel Literature Sales
P.O. Box 7641
Mount Prospect, IL 60056-7641
1 (800) 548-4725
Chapter 1, Getting started with Turbo Assembler 9
10 Turbo Assembler User's Guide
Using directives and switches
This chapter is dedicated to familiarizing you with Turbo Assembler's command-line
options. We'll describe each of the command-line options you can use to alter the
assembler's behavior, and then show how and when to use command files. We'll also
describe the configuration file, and how you can control the display of warning and
error messages.
Starting Turbo Assembler
If you start Turbo Assembler from your operating system command line without giving
it any arguments, like this,
TASM
you'll get a screenful of help describing many of the command-line options, and the
syntax for specifying the files you want to assemble. Figure 2.1 shows you how this
looks.
Figure 2.1 Turbo Assembler command line
Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International
Syntax: TASM [options] source [,object] [,listing] [,xref]
la,/s Alphabetic or Source-code segment ordering
Ic Generate cross-reference in listing
IdSYM[=VAL] Define symbol SYM = 0, or = value VAL
le,/r Emulated or Real floating-point instructions
Ih,l? Display this help screen
lipATH Search PATH for include files
IjCMD Jam in an assembler directive CMD (e.g. IjIDEAL)
Ikh# Hash table capacity # symbols
Il,/la Generate listing: l=normal listing, la=expanded listing
Iml,/mx,/mu Case sensitivity on symbols: ml=all, mx=globals, mu=none
lmv# Set maximum valid length for symbols
Im# Allow # mUltiple passes to resolve forward references
Chapter 2, Using directives and switches 11
In Suppress symbol tables in listing,
los,/o,/op,/oi Object code: standard, standard w/overlays, Phar Lap, or IBM
Ip Check for code segment overrides in protected mode
Iq Suppress OBJ records not needed for linking
It Suppress messages if successful assembly
luxxxx
IwO, Iwl, Iw2
Iw-xxx,/w+xxx
Ix
Iz
Izi, Izd,/zn
Set version emulation, version xxxx
Set warning level: wO=none, wl=w2=warnings on
Disable (-) or enable (+) warning xxx
Include false conditionals in listing
Display source line with error message
Debug info: zi=full,zd=line numbers only, zn=none
With the command-line options, you can specify the name of one or more files that you
want to assemble, as well as any options that control how the files get assembled.
The generalform of the command line looks like this:
TASM fileset [i fileset] .. ,
,The semicolon (;) after the left bracket (D lets you assemble multiple groups of files on
one command line by separating the file groups. If you prefer, you can set different
options for each set of files; for example,
TASM Ie FILEli la FILE2
assembles FILEl.ASM with the fe command-line option and assembles file FILE2.ASM
with the fa command-line option.
In the generalform of the command line,fileset can be
[option] ... sourcefile [[+] sourcefile] ...
, [, [objfile] [, [listfile] [, [xreffile]JJ]
This syntax shows that a group of files can start off with any options you want to apply
,to those files, followed by the files you want to assemble. A file name can be a single file
name, or it can use the normal wildcard characters * and? to specify multiple files to
assemble. Ifyour file name does not have an extension, Turbo Assembler adds the .ASM
extensIon. For example, to assemble all the .ASM files in the current directory, you
would type
TASM *
If you want to assemble multiple files, you can separate their names with the plus sign
(+):
TASM MYFILEl + MYFILE2
You can follow the file name youwant to assemble by an optional object file name,
listing file name, and a cross-reference file name. If you do not specify an object file or
listing file, Turbo Assembler creates an object file with the same name as the source file
and an extension of .OBJ.
A listing file is not generated unless you explicitly request one. To request one, place a
comma after the object file name, followed by a listing file name. If you don't explicitly
, provide a listing file name, Turbo Assembler creates a listing file with the same name as
the source file and the extension .LST. If you supply a listing file name without an
extension, .LST is appended to it.
12 T urboA sse mbIerUse r's Gui de
A cross-reference file is not generated unless you explicitly request one. To request one,
place a comma after the listing file name, followed by a cross-reference file name. If you
don't explicitly provide a cross-reference file name, Turbo Assembler creates a cross-
reference file with the same name as the source file and the extension .XRF. If you
supply a cross-reference file namewithout an extension, .XRF is appended to it.
(TCREF, a cross-reference utility, is described on disk.)
If you want to accept the default object file name and also request a listing file, you must
supply the comma that separates the object file name from the listing file name:
TASM FILE1, ,TEST
This assembles FILE1.ASM to FILE1.0BJ and creates a listing file named TEST.LST.
If you want to accept the default object and listing file names and also request a cross-
reference file, you must supply the commas that separate the file names:
TASM MYFILE, "MYXREF
This assembles file MYFILE.ASM to MYFILE.OBJ, with a listing in file MYFILE.LST and
a cross-reference in MYXREF.XRF.
If you use wildcards to specify the source files to assemble, you can also use wildcards
to indicate the object and listing file names. For example, if your current directory
contains XXl.ASM and XX2.ASM, the command line
TASM XX*,YY*
assembles all the files that start with XX, generates object files that start with YY, and
derives the remainder of the name from the source file name. The resulting object files
are therefore called YY1.0BJ and YY2.0BJ.
If you don't want an object file but you do want a listing file, or if you want a cross-
reference file but don't want a listing file or object file, you can specify the null device
(NUL) as the file name. For example,
TASM FILE1, ,NUL,
assembles file FILE1.ASM to object file FILE1.0BJ, doesn't produce a listing file, and
creates a cross-reference file FILEl.XRF.
Command-line options
The command-line options let you control the behavior of the assembler, and how it
outputs information to the screen, listing, and object file. Turbo Assembler provides you
with some options that produce no action, but are accepted for compatibilitY with the
current and previous versions of MASM:
Ib Sets buffer size
Iv Displays extra statistics
You can enter options using any combination of uppercase and l~wercase letters. You
can also enter your options in any order except where you have multiple Ii or Ij options;
these are processed in sequence. When using the Id option, you must also be careful to
define symbols before using them in subsequent Id options.
Chap ter 2, Usin 9 dire ctivesan d swit c hes 13
fa
la
Note You can override command-line options by using conflicting directives in your source
code. . .
Figure 2.1 on page 11 summarizes the Turbo Assembler command-line options; here's a
detailed de~cription of each option.
Function Specifies alphabetical segment-ordering
Syntax la
Remarks The fa option tells Turbo Assembler to place segments in the object file in alphabetical
order. This is the same as using the .ALPHA directive in your source file.
You usually only have to use this option if you want to assemble a source file that was
written for very early ve!sions of the IBM or Microsoft assemblers.
The Is option reverses the effect of this option by returning to the defaultsequential
segment-ordering.
If you specify sequential segment-ordering with the .SEQ directive in your source file, it
will override any fa you provide on the command line.
Example TASM la TESTl
Ib
This command line creates an object file, TESTl.OBJ, that has its segments in
alphabetical order.
Syntax Ib
Remarks The Ib option is included for compatibility. It performs no action and has no effect on
the assembly.
Ie
Function Enables cross-reference in listing file
Syntax Ie
Remarks The Ie option enables cross-reference information in the listing file. Turbo Assembler
adds the cross-reference information to the symbol table at the end of the listing file.
This means that, in order to see the cross-reference information, you must either
explicitly specify a listing file on the command line or use the II option to enable the
listing file. :
For each symbol, the cross-reference shows the line on which-it is defined and all lines
that refer to it. !
Example TASM /l IcTESTl
14 Turbo Assembler User's Guide
Id
This code creates a listing file that also has cross-reference information in the symbol
table.
/d
Function Defines a symbol
Syntax Idsymbol [=value or expression]
Remarks The Id option defines a symbol for your source file, exactly as if it were defined on the
first line of your file with the =directive. You can use this option as many times as you
wanf on the command line.
You can only define a symbol as being equal to another symbol or a constant value. You
can't use an expression with operators to the right of the equal sign (=). For example,
IdX=9 and IdX=Yare allowed, but IdX=Y-4 is not.
Example TASM IdMAX=lO IdMIN=2 TESTl
Ie
This command line defines two symbols, MAX and MIN, that other statements in the
source file TESTl.ASM can refer to.
Function Generates floating-point emulator instructions
Syntax Ie
Remarks The Ie option tells Turbo Assembler to generate floating-point instructions that will be
executed by a software floatinS-point emulator. Use this option if your program
contains a floating-point emulation library that mimics the functions of the 80x87
numeric coprocessor.
Normally, you would only use this option if your assembler module is part of a
program written in a high-level language that uses a floating-point emulation library.
(Borland's line of C++ compilers, Borland Pascal, Turbo Basic, and Turbo Prolog all
support floating-point emulation.) You can't just link an assembler program with the
emulation library, since the library expects to have been initialized by the compiler's
startup code. '
The Ir option reverses the effect of this option by enabling the assembly of real floating-
point instructions that can only be executed by a numeric coprocessor.
If you use the NOEMUL directive in your source file, it will override the Ie option on the
command line.
The Ie command-line option has the same effect as using the EMUL directive at the start
of your source file, and is also the same as using the IjEMUL command-line option.
Example TASM I e SECANT
TCC -f TRIG.C SECANT.OBJ
Chap t er 2, Usin 9 dire cti vesan d swit ches 15
· Ih or I?
Ih or 11
The first command line assembles a module with emulated floating-point instructions.
The second command line compiles a C source module with floating-point emulation
and then links it with the object file from the assembler. .
Function  Displays a help screen
Syntax Ih or I?
Remarks The /h option tells Turbo Assembler to display a help screen that describes the
command-line syntax. This includes a list of the options, as well as the various file
names you can supply. The I? option does the same thing.
Example TASM Ih
Ii
Function Sets an include file path
Syntax lipATH
Remarks The Ii option lets you tell Turbo Assembler where to look for files that are included in
your source file by using the INCLUDE directive. You can place more than one Ii option
on the command line (the number is only limited by RAM).
When Turbo Assembler encounters an INCLUDE directive, the location where it
searches for the include file is determined by whether the file name in the INCLUDE
directive has a directory path or is just a simple file name.
If you supply a directory path as part of the file name, that path is tried first, then Turbo
Assembler searches the directories specified by Ii command-line options in the order
they appear on the command line. It then looks in any directories specified by Ii options
in a configuration file.
If you don't supply a directory path as part of the file name, Turbo Assembler searches
first in the directories specified by Ii command-line options, then it looks in any
directories specified by Ii options in a configuration file, and finally it looks in the
current directory.
Example TASM Ii INCLUDE liD:  INCLUDETESTl
If the source file contains the statement
INCLUDE MYMACS.INC
Turbo Assembler will first look for INCLUDEMYMACS.INC, then it will look for
D:INCLUDEMYMACS.INC. If it still hasn't found the file, it will look for
MYMACS.INC in the current directory. If the statement in your source file had been
INCLUDE INCSMYMACS.INC
16 Turbo Assembler User's Guide
Ij
Turbo Assembler would first look for INCSMYMACS.INC and then it would look
for INCLUDEMYMACSJNC, and finally for D: INCLUDE MYMACSJNC.
Function Defines an assembler startup directive
Syntax Ijdirective
Remarks The Ij option lets you specify a directive that will be assembled before the first line of
the source file. directive can be any Turbo Assembler directive that does not take any
arguments, such as .286, IDEAL, %MACS, NOJUMPS, and so on.
/ j
You can put more than one Ij option on the command line; they are processed from left
to right across the command line.
Example TASM I j .286 I j IDEAL TESTl
This code assembles the file TESTl.ASM with 80286 instructions enabled and Ideal
mode expression-parsing enabled.
Ikh
Function Sets the maximum number of symbols allowed
Syntax Ikhnsymbols
Remarks The Ikh option sets the maximum number of symbols that your program can contain.
If you don't use this option, your program can only have a maximum of 8,192 symbols;
using this option increases the number of symbols to nsymbols, up to a maximum of
32,768.
Use this option if you get the Out of hash space message when assembling your
, program.
You can also use this option to reduce the total number of symbols below the default
8,192. This releases some memory that can be used when you are trying to assemble a
program but don't have enough available memory.
Example TASM IkhlOOOOO BIGFILE
This command tells Turbo Assembler to reserve space for 10,000 symbols when
assembling the file BIGFILE.
II
Function Generates a listing file
Syntax II
Chap t er 2, Us in9 dire ct ivesan d swit ches 17
/ I a.
Remarks The II option indicates that you want a listing file, even ifyou did not explicitly specify it
on the command line. The listing file will have the same name as the source file, with an
extension of .LST.
Example TASM 11 TESTl
This command line requests a listing file that will be named TESTl.LST.
Iia
Function Shows high-level interface code in listing file
Syntax Ila
Remarks The IIa option tells Turbo Assembler to show all generated code in the listing file,
including the code that gets generated as a result of the high-level language interface
.MODEL directive.
Example TASM Ila FILEl
1m
Function Sets the maximum number of assembly passes
Syntax 1m [npasses 1
Remarks Normally, Turbo Assembler functions as a single-pass assembler. The 1m option lets
you specify the maximum number of passes the assembler should make during the
assembly process. TASM automatically decides whether it can perform less than the
number of passes specified. Ifyou select the 1m option, but don't specify npasses, a
default of five is used.
You might want to specify multiple passes either if you want TurboAssembler to
remove Nap instructions added because of forward references or if you are assembling
a module containing instructions that require two passes. If multiple passes are not
enabled, such a module will produce atleast one ilPass-dependent construction
encountered" warning. If the1m option is enabled, Turbo Assembler assembles this
module correctly but will not optimize the code by removing Naps, no matter how
many passes are allowed. The warning ilModule is pass dependent-compatibility pass
was done" is displayed if this occurs;
Example TASM 1M2 TESTl
This tells Turbo Assembler to use up to two passes when assembling TE~Tl.
Iml
Function Treats symbols as case-sensitive
Syntax Iml
18 TurboA sse mbIer Use r's Guide
1m u
Remarks The Iml option tells Turbo Assembler to treat all symbol names as case-sensitive.
Normally, uppercase and lowercase letters are considered equivalent so that the names
ABCxyz, abcxyz, and ABCXYZ would all refer to the same symbol. If you specify the Iml
option, these three symbols will be treated as distinct. Even when you specify Iml, you
can still enter any assembler keyword in uppercase or lowercase. Keywords are the
symbols built into the assembler that have special meanings, such as instruction
mnemonics, directives, and operators.
Example TASM /ml TESTl
Imu
where TEST1.ASM contains the following statements:
abc DW 0
ABC DW 1
Mov Ax, [Bp]
inot a duplicate symbol
imixed case OK in keywords
The Iml switchused together with Imx has a special meaning for Pascal symbols. See the
Imx section for further details.
Function Converts symbols to uppercase
Syntax /mu
Remarks The Imu option tells Turbo Assembler to ignore the case of all symbols. By default,
Turbo Assembler specifies that any lowercase letters in symbols will be converted to
uppercase unless you change it by using the Iml directive.
Example TASM /mu TESTl
Imv#
makes sure that all symbols are converted to uppercase (which is the default):
EXTRN myfunc:NEAR
call myfunc idon't know if declared as
i MYFUNC, Myfunc, ...
Function Sets the maximum length of symbols.
Syntax /mv#
Remarks The Imv# option sets the maximum length of symbols that TASM will distinguish
between. For example, if you set Imv12, TASM will see ABCDEFGHIJKLM and
ABCDEFGHIJIKLL as the same symbol, but not ABCDEFGHIJKL. Note that the
minimum number you ~an have here is 12.
Imx
Function Makes public and external symbols case-sensitive
Chapter 2, Using directives and switches 19
In
Syntax Imx
Remarks The Imx option tells Turbo Assembler to treat only external and public symbols as case-
sensitive. All other symbols used (within: thesource file) are treated as uppercase.
You should use this directive when you call routines in other modules that were
compiled or assembled so that case-sensitivity is preserved; for example, modules
compiled by one of Borland's line of C++ compilers.
Example TASM Imx TEST1;
In
where TESTl.ASM contains the following source lines:
EXTRN Cfunc:NEAR
myproc PROC NEAR
call Cfunc
Note Using the Imx and Iml options together has a special meaning for symbols declared as
Pascal; if you use these symbols together, the symbols will be published as all uppercase
to the linker.
Function Suppresses symbol table in listing file
Syntax In
Remarks The In op#on indicates that you don't want the usual symbol table at the end of the
listing file. Normally, a complete symbol table listing appears at the end of the file,
showing all symbols, their types, and their values.
You must specify a listing file, either explicitly on the command line or by using the 11
option; otherwise, In has no effect.
Example TASM 11 In TESTl
10
This code generates a listing file showing the generated code only, and not the value of
your symbols.
Function Generates overlay code for TLINK
Syntax. 10
Remarks Specifying the 10 switch on the command line causes overlay-compatible fixups to be
generated. When this switch is used, 386 references to USE32 segments should not be
made since they won't link properly.
20 .'TurboA sse mbIer Use r's Guide
loi
loi
Function Generates overlay code for the IBM linker
Syntax 10
Remarks Specifying the loi switch on the command will generate overlay-compatible fixups for
the IBM linker. The resulting object file will not be compatible with TLINK, Borland's
linker.
lop
Function Generates overlay code for the Phar Lap linker
Syntax lop
Remarks Specifying the lop switch on the command will generate overlay-compatible fixups for
the Phar Lap linker. The resulting object file will not be compatible with TLINK,
Borland's linker.
los
Function Outputs TLINK-compatible objects without overlay support. This is the default
selection.
Syntax los
Remarks Specifying the los switch on the command will generate objects without overlay support
for use with TLINK.
Ip
Function Checks for impure code in protected mode
Syntax Ip
Remarks The Ip option specifies that you want to be warned about any instructions that generate
"impure" code in protected mode. Instructions that move data into memory by using a
CS: override in protected mode are considered impure because they might not work
correctly unless you take special measures.
You only need to use this option if you are writing a program that runs in protected
mode on the 80286, 386, or i486.
Example TASM Ip TESTl
where TESTl.ASM contains the following statements:
Chap t er 2, Us i n9 dire ct ivesan d swit ches 21
/q
Iq
.286P
, CODE SEGMENT
temp DW ?
mov CS:temp,O ;impure in protected mode
Function Suppresses .OBI re'cords not needed f()r linking
Syntax /q
Remarks The Iq option removes the copyright and file dependency records from the resulting
.OBI files, making it smaller. Don't use this option if you are using MAKE or a similar
program that relies on the dependency records. '
Ir
Function Generates real fl0Cl.ting~point instructions
Syntax /r
Remarks The Ir option tells Turbo Assembler to generate real floating-point instructions (instead
of generating emulated floating-point instructions). Use this option if your program is
going to run on machines equipped with an 80x87 numeric coprocessor.
The Ie option reVerses the effect of this option in generating emulated floating~point
instructions.
If you use the EMUL directive in your source file, it will override the Ir option on the
command line.
The Ir command-line option has the same effect as using the NOEMUL directive at the
start of your source file, and is also the same as using the IjNOEMUL command-line
option. .
Example TASM /r SECANT
Is
TPC /$N+ /$E TRIG.PAS
The first command line assembles a module with real floating-point instructions. The
second compiles a Pascal source module with real floating-point instructions that links
in the object file from the assembler.
Function Specifies sequential segment-ordering
Syntax /s
Remarks The Is option tells Turbo Assembler to place segments in the object file in the order in
which they were encountered in the source file. By default, Turbo Assembler uses
segment-ordering, unless you change itby placing an la option in the configuration file.
22 Tur boA 55 embIer U5 er '5 GU ide
If you specify alphabetical segment-ordering in your source file with the .ALPHA
directive, it will override Is on the command line.
Example TASM Is TESTl
It
This code creates an object file (TESTl.OBJ) that has its segments ordered exactly as they
were specified in the source file.
It
Function Suppresses messages on successful assembly
Syntax It
Remarks The It option stops any display by Turbo Assembler unless warning or error messages
result from the assembly.
You can use this option when you are assembling many modules, and you only want
warning or error messages to be displayed onscreen.
Example TASM It TESTl
lu
Function Sets version ID in command line
Syntax lu version
Remarks The lu option lets you specify which version of Turbo Assembler or MASM you want to
use to run your modules. This is the command-line version of the VERSION directive.
lutthk
Function Enables support for Windows 95 flat thunking.
Syntax lutthk
Remarks The lutthk option tells Turbo Assemblerto assemble code generated by the Microsoft
thunk compiler. For more information, see the thunking example and documentation
provided in the  EXAMPLES THUNK95 directory off your main TASM directory.
Iv
Syntax Iv
Remarks The Iv option is included for compatibility. It performs no action and has no effecton the
assembly.
Chap ter 2, Us i ng dire ct ivesan d swit ches 23
/w
/w
Function Contro~s the generation of warning messages
Syntax /w
w- [warnclassl
w+ [warnclass1
Remarks The Iw option controls which warning messages are emitted by Turbo Assembler.
If you specify Iw by itself, "mild" warnings are enabled. Mild warnings merely indicate
that you can improve some aspect of your code's efficiency.
Ifyou specify Iw- without warnclass, all warnings are disabled. If you follow Iw- with
warnclass,only that warning is disabled. Each warning message has a three-letter
identifier:
, ALN
ASS
BRI<
GTP
ICG
!NT
LCO
MCP
OPI
OPP
OPS
OVF
PDC
PQK
PRO
RES
TPI
UNI
Segment alignment
Assuming segment is 16-bit
, Brackets needed
Global type doesn't match symbol type
Inefficient code'generation
INT 3 generation
Location coun,ter overflow
MASM compatibility pass
Open IF conditional
Open procedure
Open segment
Arithmetic overflow
Pass-dependertt construction
Assuming constant for [const] warning
Write-to memory in protected mode needs CS override
Reserved word warning
illegal warning
For turning off uninitialized segment warning
If you specify Iw+ without warnclass, all warnings are enabled. If you specify Iw+ with'
warnclass from the preceding list, only that warning will be enabled.
By default, Turbo Assembler first starts assembling your file with all warnings enabled
except the inefficient code-generation (lCG) and the write-to-memory in protected
mode (PRO) warnings.
You can use the WARN and NOWARN directives within your source file to control
whether a particular warning is allowed for a certain range of source lines. These
directives are described later in this chapter.
Example TASM /w TESTl
24 TurboA sse mbIer Use r' s Guide
Ix
Ix
The following statement in TESTl.ASM issues a warning message that would not have
appeared without the Iw option:
mov bx,ABC
ABC =1
iinefficient code generation warning
With the command line
TASM Iw-OVF TEST2
no warnings are generated if TEST2.ASM contains
dw lOOOh * 20h
Function Includes false conditionals in listing
Syntax Ix
Remarks If a conditional IF, IFNDEF, IFDEF, and so forth evaluates to False, the Ix option causes
the statements inside the conditional block to appear in the listing file. This option also
causes the conditional directives themselves to be listed; normally they are not.
You must specify a listing file on the command line or use the 11 option, otherwise Ix has
no effect.
You can use the .LFCOND, .SFCOND, and .TFCOND directives to override the effects
of the Ix option.
Example TASM /x TESTl
/z
Function Displays source lines along with error messages
Syntax /z
Remarks The Iz option tells Turbo Assembler to display the corresponding line from the source
file when an error message is generated. The line that caused the error is displayed
before the error message. With this option disabled, Turbo Assembler justdisplays a
message that describes the error..
Example TASM /z TESTl
/zd
Function Enables line-number information in object files
Syntax· /zd
Remarks The Izd option causes Turbo Assembler to place line-number information in the object
file. Thislets the debugger display the current location in your source code, but does not
Chapt er 2, Us i n9 dire ct ivesan d swit ches 25
Izi
put the information in the object file that would allow the debugger to access your data
items. 
Ifyou run out of memory when trying to debug your program, you can use Izd for some
modules and Izi for others.
Example TASM /zd TESTl
/zi
Function Enables debug information in object file
Syntax /zi
.Remarks· The Izi option tells Turbo Assembler to output complete debugging information to the
object file. This includes line-number records to synchronize source code display and
data type information to let you examine an~ modify your program's data.
The Izi option lets you use all the features of the debugger to step through your program
and examine or change your data items. You can use Izi on all your program's modules,
or just on those you're interested in debugging. Since the Izi switch adds information to
the object and executable programs, you might not want to use it on all your modules if
you run out of memory when running a program under the debugger.
Example TASM. zi TESTl
/zn
Function Disables debug information in object file
Syntax /zn
Remarks The Izn option tells Turbo Assembler to disable the output of debuggmg information to
the object file. It's useful for overriding any prevailing Izi switch in a configurationfile.
Indirect command files
At any point when entering a command line, Turbo Assembler lets you specify an
indirect command file by preceding its name with an "at" sign (@). For example,
TASM /dTESTMODE @MYPROJ.TA
causes the contents of the file MYPROJ.TA to become part of the command line, exactly
as if you had typed in its contents. directly.
This useful feature lets you put your most frequently used command lines and file lists
in a separate file. And you don't have to place your entire command linein one indirect
file, since you can use more than one indirect file on the command line and can also mix
indired command files with normal arguments. For example,
TASM @MYFILES @IOLIBS /dBUF=1024
26 Turbo Assembler User's Guide
/ z n
This way you can keep long lists of standard files and options in files, so that you can
quickly and easily alter the behavior of an individual assembly run.
You can either put all your file names and options on a single line in the command file,
or you can split them across as many lines as you want.
The configuration file
Turbo Assembler also lets you put your most frequently used options into a
configuration file in the current directory. This way, when you run Turbo Assembler, it
looks for a file called TASM.CFG in your current directory. If Turbo Assembler finds the
file, it treats it as an indirect file and processes it before anything else on the command
line.
This is helpful when you have all the source files for a project in a single directory, and
you know that, for example, you always want to assemble with emulated floating-point
instructions (the Ie option). You can place that option in the TASM.CFG file, so you
don't have to specify th~t option each time you start Turbo Assembler.
The contents of the configuration file have exactly the same format as an indirect file.
The file can contain any valid command-line options, on as many lines as you want. The
options are treated as if they all appeared on one line.
The contents of the configuration file are processed before any arguments on the
command line. This lets you override any options set in the configuration file by simply
placing an option with the opposite effect on the command line. For example, if your •
configuration file contains
la Ie
and you invoke Turbo Assembler with
TASM Is Ir MYFILE
MYFILE is your program file, and your file will be assembled with sequential segment-
ordering (Is) and real floating-point instructions (/r), even though the configuration file
contained the la and Ie options that specified alphabetical segment-ordering and
emulated floating-point instructions.
Chap t er 2, Usin 9 dire cti vesan d swit c hes 27
28 Turbo Assembler User's Guide
General programming concepts
This chapter introduces you to the basic concepts of Turbo Assembler. We'll look at
Ideal mode versus MASM mode, commenting your programs and extending lines of
code, includingfiles, using predefined symbols, and using several important directives
that produce module information. Although this is a lot of ground to cover, it will give
you a good idea of what assembly language is all about.
Turbo Assembler Ideal mode
For those of you struggling to make MASM do your bidding, this may be the most
important chapter in the manual. In addition to near-perfect compatibility with MASM
syntax, Turbo Assembler smooths the rough areas of assembly language programming
with a MASM derivative we call Ideal mode.
Among other things, Ideal mode lets you know solely by looking at the source text
exactly how an expression or instruction operand will behave. There's no need to
memorize all of MASM's many quirks and tricks. Instead, with Ideal mode, you write
clear, concise expressions that do exactly what you want.
Ideal mode uses nearly all MASM's same keywords, operators, and statement
constructions. This means you can explore Ideal mode's features one at a time without
having to learn a large number of new rules or keywords.
Ideal mode adds strict type checking to expressions. Strict type checking helps reduce
errors caused by assigning values of wrong types to registers and variables, and by
using constructions that appear correct in the source text, but are assembled differently
than you expect. Instead of playing guessing games with values and expressions, you
can use Ideal mode to write code that makes logical and aesthetic sense.
With strict type checking, Ideal mode expressions are both easier to understand and less
prone to producing unexpected results. And, as a result, many of the MASM
idiosyncrasies we warn you about in other chapters disappear.
Chap ter 3, Genera I pro 9ram min 9 con cepts 29
Ideal mode also has a number offeatures that make programming easier for novices aI}d
experts alike. These features include the following:
• duplicate member names among multiple structures
• complex HIGH and LOW expressions
• predictable EQU processing
• correct handling of grouped data segments
• improved consistency among directives
• sensible bracketed expressions
Why use Ideal mode?
There are many good reasons why you should use Turbo Assembler's Ideal mode.
.If you are justlearning assembly language, you can easily construct Ideal mode
expressions and statements that have the effects you desire. You don't have to
experiment trying different things until you get an instruction that does what you want.
If you are an experienced assembly language programm~r, you can use Ideal mode
features to·write complex programs using language extensions such as nestable
structures and unions.
As a direct benefit of cleaner syntax, Ideal mode assembles files 30% faster than MASM
mode. The larger your projects and files, the more savings in assembly time you'll gain
by switching to Ideal mode.
Strong type-checking rules, enforced by Ideal mode, let Turbo Assembler catch errors
that you would otherwise have to find at run time or by debugging your code. This is.
similar to the way high-level language compilers point out questionable constructions
and mismatched data sizes.
Although Ideal mode uses a different syntax for some expressions, you can still write
programs that assemble equally well in both MASM and Ideal modes. You can also
switch between MASM and Ideal modes as often as necessary within the same source
file. This is especially helpful when you're experimenting with Ideal mode features, or
when you're converting existing programs written in the MASM syntax. You can switch
to Ideal mode for new code that you add to your source files and maintain full MASM
compatibility for other portions of your program.
Entering and leaving Ideal mode
Use the IDEAL and MASM directives to switch between Ideal and MASM modes.
Turbo Assembler always starts assembling a source file in MASM mode. To switch to
. Ideal mode, include the IDEAL directive in your source file before using any Ideal mode
capabilities. From then on, or until the next MASM directive, all statements behave as
described in this chapter. You can switch back and forth between MASM and Ideal
modes in a source file as many times as you wish and at any place. Here's a sample:
DATA SEGMENT
abc LABEL·BYTE
xyz DW 0
DATA ENDS
istart in MASM mode
iabc addresses xyz as a byte
idefine a word at label xyz
iend of data segment
30 Turbo Assembler User's Guide
IDEAL
SEGMENT CODE
PROC MyProc
ENDP MyProc
ENDS
MASM
CODE SEGMENT
Func2 PROC
IDEAL
MASM
Func2 ENDP
CODE ENDS
iswitch to Ideal mode
isegment keyword now comes first
iproc keyword comes first, too
iIdeal mode programming goes here
irepeating MyProc label is optional
irepeating segment name not required
iswitch back to MASM mode
iname now required before segment keyword
iname now comes before proc keyword, too
iMASM-mode programming goes here
iswitch to Ideal mode again!
ido some programming in Ideal mode
;back to MASM mode. Getting dizzy?
iname again required before keyword
iname again required here
In Ideal mode, directive keywords such as PROC and SEGMENT appear before the
identifying symbol names, which is the reverse of MASM's order. You also have the
option of repeating a segment or procedure name after the ENDP and ENDS directives.
Adding the name can help clarify the program by identifying the segment or procedure
that is ending. This is a good idea, especially in programs that nest multiple segments
and procedures. Youdon't have to include the symbol name after ENDP and ENDS,
however.
MASM and Ideal mode differences
This section describes the main differences between Ideal and MASM modes; If you
know MASM, you might want to experiment with individual features by converting
small sections of your existing programs to Ideal mode. Further details of these
differences are in Chapter 5, "Using expressions and symbol values."
Expressions and operands
The biggest difference between Ideal and MASM mode expressions is'the way square .
brackets function. In Ideal mode, square brackets always refer to the contents of the
enclosed quantity. Brackets never cause implied additions to occur. Many standard
MASM constructions, therefore, are not permitted by Ideal mode.
In Ideal mode, square brackets must be used in order to get the contents of an item. For
example,
mov ax,wordptr
displays a warning message. You're trying to load a pointer (wordptr) into a register
(AX). The correct form is
mov ax, [wordptr]
Using Ideal mode, it's clear you are loading the contents of the location addressed by
wordptr (in the current data segment at DS) into AX
Chap t er3 , General programming concepts 31
If you wish to refer to the offset of a symbol within a segment, you must explicitly use
the OFFSET operator, as in this example:
mov ax/OFFSET wordptr
Operators
The changes made to the expression operators in Ideal mode increase the power and
flexibility of some operators while leaving unchanged the overall behavior of
expressions. The precedence levels of some operators have been changed to facilitate
common operator combinations.
The period (.) structure member operator is far more strict in Ideal mode when
accurately specifying the structure members you're referring to. The expression to the
left of a period must be a structure pointer. The expression.to the right must be a member
name in that structure. Here's an example of loading registers with the values of specific
structure members:
iDeclare variables using the structure types
S_Stuff SomeStuff <>
O_Stuff OtherStuff <>
mov aX1 [S_Stuff.AmountJ
mov bl l [O_Stuff.AmountJ
Suppressed fixups
iload word value
ilaad byte value
Turbo Assembler in Ideal mode does not generate segment-relative fixups for private
segments that are page- or pgragraph-aligned. Because the linker does not require such
fixups, assembling programs in Ideal mode can result in smaller objectfiles that also link
more quickly than object files generated by MASM mode. The following demonstrates
how superfluous fixups occur in MASM but not in Ideal mode:
SEGMENT DATA PRIVATE PARA
VARl DB 0
VAR2 DW
ENDS
SEGMENT CODE
ASSUME ds:DATA
mav ax/VAR2
ENDS
ina fixup needed
Note .This difference has no effect on code that you write. The documentation here is simply
for your information.
Operand for BOUND instruction
The BOUND instruction expects a WORD operand, not a DWORD. This lets you
define the lower and upper bounds as two constant words, eliminating the need to
convert the operand to a DWORD with an explicit DWORD PTR. In MASM mode,
you must write
BOUNDS DW 1/4
BOUND AXI DWORD PTR BOUNDS
ilawer and upper bounds
irequired far MASMmade
but in Idealmode, you need only write
32 Tu rboA sse mbIer Use r' s Guide
BOUNDS DW 1,4
BOUND AX, [BOUNDS]
Segments and groups
;lower and upper bounds
;legal in Ideal mode
The way Turbo Assembler handles segments and groups in Ideal mode can make a
difference in getting a program up and running. If you're like most people, you
probably shudder at the thought of dealing with a bug that has anything to do with the
interaction of segments and groups.
Much of the difficulty in this process stems from the arbitrary way that MASM and,
therefore, Turbo Assembler's MASM mode, makes assumptions about references to
data or code within a group. Fortunately, Ideal mode alleviates some of the more
nagging problems caused by MASM segment and group directives, as you'll see in the
information that follows.
Accessing data in asegment belonging to agroup
In Ideal mode, any data item in a segment that is part of a group is considered to be
principally a member of the group, not of the segment. An explicit segment override
must be used for Turbo Assembler to recognize ,the data item as a member of the
segment.
MASM mode handles this differently; sometimes a symbol is considered to be part of
the segment instead of the group. In particular, MASM mode treats a symbol as part of a
segment when the symbol is used with the OFFSET operator, but as part of a group
when the symbol is used as a pointer in a data allocation. This canbe confusing because
When you directly access the data without OFFSET, MASM incorrectly generates the
reference relative to the segment instead of the group.
Here's an example of how easily you can get into trouble with MASM's addressing
quirks. Consider the following incomplete MASM program, which declares three data
segments:
dseg1 SEGMENT PARA PUBLIC 'data'
v1 DB 0
dseg1 ENDS
dseg2 SEGMENT PARA PUBLIC 'data'
v2 DB 0
dseg2 ENDS
dseg3 SEGMENT PARA PUBLIC 'data'
v3 DB 0
dseg3 ENDS
DGROUP GROUP dseg1,dseg2,dseg3
cseg SEGMENT PARA PUBLIC 'code'
ASSUME cs:cseg,ds:DGROUP
start:
Chap t er 3,G enera I. pro 9ra mmin 9 con cept s 33
mov ax/OFFSET vI
mov bx/OFFSET v2
mov ex/OFFSET v3
eseg ENDS
END start
The threesegments, dsegi, dseg2, and dseg3, are grouped under one name, DGROUP. As
a result, all the variables in the individual segments are stored together in memory. In
the program source text, each of the individual segments declares a BYTE variable,
labeled vi, v2, and v3.
In the codeportion of this MASM program, the offset addresses of the three variables
are loaded into registers AX, BX, and ex. Because of theearlier ASSUME directive and
because the data segments weregrouped together, you might think that MASM would
calculate the offsets to the variables relative to the entire group in which the variables
are eventually stored in memory.
But this is not what happens. Despite your intentions, MASM calculates the offsets of
the variables relative to the individual segments, dsegi, dseg2, and dseg3. It does this even
though the three segments are combined into one data segment in memory, addressed
here by register DS. It makes no sense to take the offsets of variables relative to
individual segments in the program text when those segments are combined into a
single segment in memory. The only wayto address such variables is to refer to their
offsets relative to the entire group.
To fix the problem in MASM, you must specify the group name along with the OFFSET
keyword:
mov ax/OFFSET DGROUP:vl
mav bx/OFFSET DGROUP:v2
mav ex/OFFSET DGROUP:v3
Although this now assembles correctly and loads the offsets of vi, v2, and v3 relative to
DGROUP (which collects the individual segments), you might easily forget to specify
the DGROUP qualifier. If you make this mistake, the offset values will not correctly
locate the variables in memory and you'll receive no indication from MASM that
anything is amiss. In Ideal mode, there's no need to go to ,all this trouble:
IDEAL
SEGME_NT dsegl PARA PUBLIC 'data'
vI DB 0
ENDS
SEGMENT dseg2 PARA PUBLIC 'data'
v2 DB 0
ENDS
SEGMENT dseg3 PARA PUBLIC 'data'
v3 DB 0
ENDS
GROUP DGROUP dsegl/dseg2/dseg3
SEGMENT eseg PARA PUBLIC 'code'
ASSUME es:eseg, ds:DGROUP
34 Turbo Assembler User's Guide
start:
mov ax, OFFSET vl
mov ax, OFFSET v2
mov ax, OFFSET v3
ENDS
END start
The offsets to vI, v2, and v3 are correctly calculated relative to the group that collects the
individual segments to which the variables belong. Ideal mode does not require the
DGROUP qualifier to refer to variables in grouped segments. MASM mode does
require the qualifier and, even worse, gives no warning of a serious problem should
you forget to specify the group name in every single reference.
Commenting the program
Commenting your code is a great way to help you (or anyone who has to maintain your
code in the future) quickly understand how it functions. Using comments is good
programming practice in any language. They can describe the semantic as opposed to
syntactic function of your code. We recommend that you use comments liberally in your
Turbo Assembler code, and this section describes how you can do so.
Comments at the end of the line
There are several ways to·comment assembler code. One approach is to add a comment
at the end of a line using the semicolon (;), such as
mov [bxl, al istore the modified character
Another way to comment assembler code is to use the line continuation character () as
a comment char~cter. See the section called "Extending the line" for an example of how
this is done.
The COMMENT directive
The COMMENT directive lets you comment blocks of code. COMMENT ignores all
text from the first delimiter character and the line containing the next occurrence of the
delimiter. The following example uses * as a delimiter character:
COMMENT *
Work long and late to get free pizza
Note COMMENT only works in MASM mode.
Chapter 3, General programming concepts 35
Extending the line
For lines of code that are longer than 80 characters, Turbo Assembler provides the  line
continuation character. Use this character at the end of your line, because Turbo
Assembler ignores any characters that follow it on the same line.
The maximum line length is·1024 when you use ; however, tables, records, and enums
might have definitions that are longer than 1024 characters. An alternative that does not
have the 1024 character limitation is the multiline definition syntax. Here's an example
of the syntax (for an enum definition):
faa enum {
£1
;Multiline version
f2
f3
f4
f5
f6
f7
f8
}
A more compact version of the same definition:
faa enum £1, f2 , {
f3, f4
f5,f6
f7 ,f8}
; compact multiline version
When using multiline definitions, remember these rules:
• The left brace that starts the definition must be the last token on the starting line. It
does not, hpwever, have to precede the first element in the list.
• You c~ot include any directives such as IF or INCLUDE inside the multiline
definition.
MASM-mode line continuationis available if you select VERSION M510, M520. Strings
and other tokens can be extended across multiple lines if the 1/" character is the last
character on the line. For example,
VERSION M510
DB 'Hello out there
you guys'
You can place standard Turbo Assembler mode line continuation anywhere in a line,
and it is always available. It functions as a comment as well. For example,
ARG al :word,
a2:word,
a3:word
36 Turbo Assembler User's Guide
first argument
second argument
;final argument
Using INCLUDE files
Include files let you use the same block of code in several places in your program, insert
the block in several source modules, or reduce the size of your source program without
having to create several linkable modules. Using the INCLUDE directive tells Turbo
Assembler to find the specified files on disk and assemble them as if they were a part of
the source program.
The Ideal mode syntax:
INCLUDE "filename"
The MASM mode syntax:
INCLUDE filename
Note You can nest INCLUDE directives as deep as you want.
filename can specify any drive, directory, or extension. Iffilename does not include a
directory or drive name, Turbo Assembler first searches for the file in any directories
you specify with the II command-line option, and then in the current directory.
Predefined symbols
Turbo Assembler provides a number of predefined symbols that you can use in your
programs. These symbols can have different values at different places in your source
file, and are similar to equated symbols you define using the EQU directive. When
Turbo Assembler encounters one of these symbols in your source file, it replaces it with
the current value of that predefined symbol.
Some of these symbols are text (string) equates, some are numeric equates, and others
are aliases. The string values can be used anywhere that you would use a character
string, for example, to initialize a series of data bytes using the DB directive:
NOW DB ??time
Numeric predefined values can be used anywhere that you would use a number:
·IF ??version GT 100h
Alias values make the predefined symbol into a synonym for the value it represents,
allowing you to use the predefined symbol name anywhere you would use an ordinary
symbol name:
ASSUME cs:@code
All the predefined symbols can be used in both MASM and Ideal mode.
If you use the 1m! command-line option when assembling, you must use the predefined
symbol names exactly as they are described on the following pages.
Note The following rule applies to predefined symbols starting with an at-sign (@): Thefirst
letter ofeach word that makes up part ofthe symbol name is an uppercase letter (exceptfor
segment names); the rest ofthe word is lowercase. As an example,
@FileName
Chap t er 3, Genera I pro 9ram min 9 con cept 5 37
Notice that@FileNameperforms an alias-equate for the current assembly line.
The exception is redefined symbols/which refer to segments. Segment names begin
with an ahsign (@)and are all lowercase. Ear example,
, '@curseg
@fardata
For symbols that start with two question marks (??), the letters are all lowercase. For
example,
??date
??version
Note that the ??date symbol defines a text equate that represents today's date. The exact
format of the date string is determined by the country code. The ??version symbol lets
you write source files that can take advantage of features in particular versions of Turbo
Assembler. This equate also lets your source files know whether they are being
assembled by MASM or Turbo Assembler, since ??version is not defined by MASM.
,Similarly, ??filename defines an eight-character string that represents the file name
being assembled. The file name is padded with spaces if it contains fewer than eight
characters. The ??time symbol defines a text equate that represents the current time. The
exact format of the time string is determined by the country code.
ASSigning values to symbols .
Turbo Assembler provides two directives that let you assign values to symbols: EQU
and =. The EQU directive defines a string, alias, or numeric equate. To use it, specify the
following syntax,
name EQU expression
where name is assigned the result of evaluating expression. name must be anew symbol
name that you haven't previously defined in a different manner. ill MASM mode, you
can only redefine a symbol that youdefined using the EQU directive if you first define it
as a string equate. In MASM mode, EQU can generate anyone of three kinds of equates:
alias, expression,or string.
The = directive defines only a numeric equate:.To use it, specify
name = expression
where nameis assigned the result of evaluating expression, which must evaluate to either
,a constant or an address within a segment. name can either be a new symbol name, or a
symbol that you previously defined with =. Since the = directive has far more
predictable behavior than the EQU directiv~ in MASM mode, use =instead of EQU
wherever you can.
General module structure
Turbo Assembler provides several directives to help you work with modules of code.
The remainder of this chapter describes these directives.
38 Turbo Assembler User's Guide
The VERSION directive
Using the VERSION directive lets you specify which version of Turbo Assembler or
MASM you've written particular modules for. This is helpful for upward and
downward compatibility of various versions of TASM and MASM. The VERSION
directive also puts you into the operating mode for the specified version.
You can specify the VERSION directive as either a command-line switch or within
program source code.
Within code, the syntax is
VERSION <versioD_ID>
You can specify the following legal version IDs:
M400 MASM4.0
MSOO MASMS.O
MS10 MASMS.1
MS20 MASM S.2 (Quick ASM)
100 Turbo Assembler 1.0
T101 Turbo Assembler 1.01
T200 Turbo Assembler 2.0
T2S0 Turbo Assembler 2.5
T300 Turbo Assembler 3.0
T310 Turbo Assembler 3.1
T320 Turbo Assembler 3.2
T400 Turbo Assembler 4.0
T410 Turbo Assembler 4.1
TSOO Turbo Assembler S.O
The command-line syntax is:
IV<version_ID>
As an example, if you wanted to assemble a program written for MASM S.O, you could
leave the source for the program intact and use the switch /uM510.
Here are the general rules:
Note 1 The VERSION directi~e always selects MASM mode by default, because that is the
starting mode of operation for both MASM and Turbo Assembler.
2 The VERSION directive limits the high-priority keywords available to those in the
specified compiler and version. As a result, some features that were added to later
versions are unavailable to you. .
3 From Ideal mode, the VERSION directive is unavailable if you select a version prior
to T300. To use the VERSION directive in this case, you must switch to MASM mode
first.
4 No attempt is made to limit access to low priority keywords, since these will not
affect compatibility.
Chap t er 3, Genera I pr 0 9ram min 9 con cept s 39
Previous versions of Turbo Assembler controlled MASMcompatibility with directives
such as MASM51, NOMASM51, QUIRKS, SMART, and NOSMART. The VERSION
directive supersedes these older directives. See Appendix Bfor a complete list of
keywords available with eadl prior version of Turbo Assembler.
The NAME directive
Use the NAME directive to set the object file's module name. Here is the syntax for it:
NAME modulename
Turbo Assembler usually uses the source file name with any drive, directory, or -
extension as the module name. Use NAME if you wish to change this default name;
modulename will be the new name of the module. For example,
NAME loader
Note' The NAME directive only works in Ideal mode.
The END directive
Use the END directive to mark the end of your source file. The syntax looks like this:
END [ startaddress 1
startaddress is an optional symbol or expression that specifies the address in your
program where you want execution to begin. If your program is linked from multiple
source files, only one file can specify a startaddress. startaddress can be an address within
the module; it can also be an external symbol defined in another module, declared with
the EXTRN directive.
Turbo Assembler ignores any text after the END directive in the source file.
Example .MODEL small
.CODE
START:
iBody of program goes here
END START iprogram entry point is "START"
THIS LINE IS IGNORED
SO IS THIS ONE
Displaying amessage during assembly
Turbo Assembler provides two directives that let you display a string on the console
during assembly: DISPLAY and %OUT. You can use these directives to report on the
progress of an assembly, either to let you know how far the assembly has progressed, or
to let you know that a certain part of the code has been reached.
The two directives are essentially the same except that DISPLAY displays a quoted
string onscreen, and %OUT displays a nonquoted string onscreen.
In both Ideal anq MASM modes,the syntax for DISPLAY is
DISPLAY "text"
40 Turbo Assembler User's Guide
where text is any message you want to display.
The syntax for %OUT in both Ideal and MASM modes is
%OUT text
where, again, text is the message that you want displayed.
Displaying warning messages
Turbo Assembler lets you choose what (if any) warning messages you'll receive when
you assemble different parts of your code. Each warning message contains a three-letter
identifier, which you can specify ahead of time to let the assembler know whether or not
you want to see warnings of that kind. You can use the WARN directive to enable
warning messages, and the NOWARN directive to disable them.
The syntax of the WARN directive is
WARN [warnclassl
where warnclass is the three-letter identifier that represents a particular type of warning
message. The available warnclasses are:
ALN
BRK
GTP
ICG
INT
LCO
MCP
OPI
OPP
OPS
OVF
PDC
PRO
PQK
RES
TPI
Segment alignment
Brackets needed
Global type doesn't match symbol type
Inefficient code generation
!NT 3 generation
Location counter overflow
MASM compatibility pass
Open IF conditional
Open procedure
Open segment
Arithmetic overflow
Pass-dependent construction
Write-to-memory in protected mode using CS
Assuming constant for [const] warning
Reserved word warning
illegal warning
Note WARN without a wamc1ass enables all warnings. WARN followed by an identifier
only enables that particular warning.
Notice that the identifiers used by WARN are the same as those used by the IW
command-line option.
Here's an example using WARN:
WARN OVF
DW 1000h * I234h
;enables arithmetic overflow warning
;overflow warning will occur
Chap t er 3, Genera I pro 9ram min 9 con cept s 41
Use the NOWARN directive to disable specific (or all) warning messages. NOWARN
uses the same identifiers described earlier under WARN. Here's an example that uses
NOWARN:
NOWARN OVF-
DW 1000h * 1234h
idisable arithmetic overflow warnings
idoesn't warn how
Note NOWARN without a wamclass disables all warnings. NOWARN with an identifier
disables only that particular warning.
Multiple error-message reporting
By default, Turbo Assembler only allows one error message to be reported for each line
of source code. If a source line contains multiple errors, Turbo Assembler reports the
most-significant error first. You can control the number of error messages you get for
each source line by using the MULTERRS and NOMULTERRS directives.
The MULTERRS directive allows the assembler to report more than one error message
for each source line. This is sometimes helpful in locating the cause of a subtle error or
when the source line contains more than one error.
Note that sometimes additional error messages can be a "chain reaction" caused by the
first error condition; these IFchain" error messages may disappear once you correct the
first error.
Here's an example of the MULTERRS directive:
MULTERRS
mov ax, [bp+abc iproduces two errors:
i1) Undefined symbol: abc
i2) Need right square bracket
Note The NOMULTERRS directive only lets one error or warning message (the most
significant message) appear for each source line. When you correct this error, the other
error messages may disappear as well. To avoid this problem, use the MULTERRS
directive to see all of the error messages.
Here is an example of using the NOMULTERRS directive:
NOMULTERRS
mov ax, [bp+abc ione error:
i1) Undefined symbol: abc
42 Turbo Assembler User's Guide
Creating object-oriented programs
Object-oriented programming is an approach to software design that is based on objects
rather than procedures. This approach maximizes modularity and information hiding.
The underlying premise behind object-oriented programming is the binding or
encapsulation of a data structure with procedures for manipulating the data in the
structure into a unit.
Object-oriell.ted design provides many advantages. For example, every object
encapsulates its data structure with the procedures used to manipulate instances of the
data structure. This removes interdependencies in code that can quickly make
maintenance difficult. Objects can also inherit a data structure and other characteristics
from a parent object, which saves work and lets you transp~rent1y use a single chunk of
code for many purposes.
If you're not an experienced Turbo Assembler user, you might want to skim through
this chapternow, but come back to it later after reading the other chapters of this
manual. We've put it here to make you aware of these features, but object-oriented
programming in Turbo Assembler is really an advanced topic. It will make more sense
after going through the rest of the manual.
Terminology
Assembler, C++, and Pascal use different terms for various entities in object-oriented
programming. The following table outlines the differences among these languages.
Table 4.1 Object-oriented programming terminology
method
method procedure
object
base object
member function
class
base class
method
object
base object
Chapter 4, Creating object-oriented programs 43
Table 4.1 Object-oriented programming terminology (continued)
parent object
derived object
field
parent class
derived class
data member
parent object
derived object
field
Why use objects in Turbo Assembler?
Most people think of assembly language as a low-level language. Turbo Assembler,
however, provides many of the features of a high-level language (such as abstract data
types, and easy interfacing to other languages). The addition of object-oriented data
structures gives Turbo Assembler the power to create object-oriented programs as easily
as high-level languages while retaining- the speedand flexibility of assembly language.
What is an object?
An object consists of a data structure and associated procedures (called methods) that
manage data stored in instances of the data structure.
An object can inherit characteristics from a parent object. This means that the new
object's data structure includes the parent object's data structure, as well as any new
data. Also, the new object can call all the method procedures of the parent object, as well
as any new method procedures it declares.
Note We strongly recommend that you use Ideal mode for object-oriented programming in
Turbo Assembler because symbol scoping is global in MASM, which means you can't
distinguish the different positions of shown methods.
An object having no inheritance is called a base object; an object that inherits another is a
derived object.
Turbo Assembler defines several symbols you can use when declaring objects. The
following table lists these symbols.
Table 4.2 Symbols defined for objects
@Object
<objedname>
@Table_<objedname>
@TableAddr_<objectname>
44 Turbo Assembler User's Guide
A text macro containing the name of the current object (the object last
declared).
A STRUC data type that describes the object's data structure.
A TABLE data type containing the object's method table, which is not
the same as an instance of the virtual method table.
A label describing the address of the instance of the object's virtual
method table, if there is one.
Asample object
As an example of where you can use objects, consider any program that useslinked lists.
Think of a linked list as an object consisting of the linked list data and the operations
(methods) that you can perform on it.
The linked list data consists of pointers to the head and tail of the linked list (this
example contains a doubly linked list because of its flexibility). Each element of the
linked list is a separate object instance.
The following operations provide the power needed to use a linked list:
• Creating the linked list (allocating memory for it).
• Destroying the linked list (deallocating memory for it).
• Initializing the linked list.
• Deinitializing the linked list.
• Inserting an item into the middle of the linked list before an existing item.
• Appending an item to the end of the linked list.
• Deleting an item from the linked list.
• Returning the first item in the linked list.
• Returning the last item in the linked list.
Keep in mind that create and initialize, as well as destroy and deinitialize methods are not
synonymous. create and destroy methods allocate and deallocate memory for the linked
list object, while the initialize and deinitialize methods only initialize and deinitialize
previously allocated instances of the object. If you don't combine initialization with
creation, it's possible to statically allocate linked list objects.
You'can see how the linked list object canbe inherited by a queue or stack object, since a
queue or a stack can be implemented as a linked list with limited operations. For
example, you can implement a queue as a linked list where items can be added to the
start and taken off the end. If you implement a queue in this way, you must disable the
inherited linked list methods that are illegal on a queue (such as inserting into the
middle of the list).
Declaring objects
Declaring an object consists of declaring the data structure for the object, and declaring
the method procedures that you can call for the object. Declaring an object does not
involve creating an instance of the object. You'llieam how to do this later.
Declaring abase object
When you declare an object, Turbo Assembler creates a STRUC that declares the data
for the object, and a TABLE that declares the methods for the object. The object's data
declaration is a structure with the same name as the object. The object's method
declarations are stored in a TABLE data type, named@Table_<objectname>.
Chapter 4, Creating object-oriented programs 45
For example, for the list object, two data types are declared:
list A STRUC declaring the following members:
list_head dword pointer to head of list
lisCtail dword pointer to tail of list
A TABLE declaring the following methods:
construct dword pointer to the procedure list_construct
destroy dword pointer to the procedure list_destroy
and so on...
STRUC declares the data for the object that is created whenever you create an instance
of the object. TABLE declares the table of default method procedures for the declaration.
Turbo Assembler maintains this data type; it does not create an instance of the table
anywhere in your program memory. However, you'll see later that you must include an
instance of the table for any object that uses virtual methods.
Here's an example of anobject declaration for a linked list (for more on STRUC as it
applies to declaring objects, see Chapter 8):
list STRUC GLOBAL METHOD {
construct:dword = list_construct
destroy:dword = list_destroy
init:dword = list_init
deinit:dword =list_deinit
virtual insert:word = list_insert
virtual append:word = list_append
virtual remove:word = list_delete
virtual 'first:word = lisLfirst
virtual last:word = list_last
}
list_head dd?
list_tail dd?
ENDS
ilist constructor procedure
ilist destructor procedure
ilist initializer procedure
ilist deinitializer procedure
ilist node insert procedure
ilist node append procedure
ilist node remove procedure
ilist first node procedure
ilist last node procedure
ilist h~adpointer
ilist tail pointer
In this example, the METHOD keyword shows that you're using an extended form of
STRUC, and are defining an object called list.
Each entry consists of a method name, a colon, and the size of a pointer to the method
procedure (WORD for near procedures, DWORD for far procedures). This is followed
by an equal sign, and the name of the procedure to call for that method.
Let's look at this example to see what's happening.
METHOD indicates an object method call and is followed by a list of the method
procedure declarations for the object. These declarations are enclosed in braces ({ })
because the list of methods requires more than one line.
Each method declaration tells Turbo Assembler which procedure it should use to
manipulate the object when invoking that method name. For example, the first method
procedure declaration
construct:dword = list_construct
46 Turbo Assembler User's Guide
declares a method named construct that is a far procedure (because a DWORD stores the
pointer to it). The actualprocedure name of the method is list_construct, which should
be defined elsewhere in the source code.
Turbo Assembler considers a method to be virtual if it's preceded by the keyword
VIRTUAL. When you call such a method, Turbo Assembler will locate the method's
procedure address by looking it up from a table present in memory at run time.
Otherwise, the method is a static method, meaning that Turbo Assembler can determine
its address at compile time. For example, the method construct is a static method, while
the method insert is declared as a virtual method. Later in this chapter, we'll explain
why you might want to choose virtual or static methods. '
The data structure for the method immediately follows the method procedure
declaration section. This definition uses the syntax for the standard STRUC directive.
This example contains declarations for the linked list's head and tail pointers.
The method declaration portion of the object declaration doesn't place any data in the
object's datastructure unless you've used virtual methods. Instead, these declarations
cause Turbo Assembler to build a separate table data structure that contains the
specified method procedure addresses as default values. You should have an instanceof
this table for every object, and you must explicitly place the table. We'll explain how to
do this later in this chapter.
Since the object declaration must exist in the module containing the method procedures
for the object (as well as included in any source code that uses the object), you should
declare the object itself in a separate file that can be INCLUDEd into the source code.
We recommend using a file name in the form objectname.ASO (ASsembly Object). This
file should consist of only the object declaration. The object methods should be in .
another source file so that you can include the object declaration wherever you need it.
For example, the linked list object declaration in the previous example wouldbe placed
in the file LIST.ASO. The file LIST.ASM could be used to define the object's method
procedures. Any program making use of the objects would include LIST.ASO, but not
LIST.ASM.
The keyword GLOBAL in the object declaration causes Turbo Assembler to publish
information that lets you use the object in a module other than the one it's defined in.
The object declaration must also be included in all modules that use the object.
Declaring aderived object
An object that inherits another object's methods and data is called a derived object. You
can't override the members of the parent data structure, but you can override the
individual methods by respecifying them in the new object method list.
An object can inherit any other single object, whether that other object is a base or
derived object itself. The inherited object is called the parent object. The derived object
inherits the data and methods of the parent object, so you should only use inheritance
when these methods and data are useful to the new object.
For example, you can define a queue object that inherits the linked list object because
you can implement a queue as a linked list. Here's an example of sucha derived object:
Chap t er 4, ere at i ngob j ect -0 ri ented pro 9ram s 47
queue STRUC GLOBAL list METHOD {
init:DWORD=queue_init
virtual insert:word = queue_insert
virtual rernove:word = queue_delete
virtual first:word = queue_first
virtual last:word = queue_last
virtualenqueue:word = list_append
virtual dequeue:word = queue_dequeue
}
ENDS
; (queue node insert
; procedure)
; (queue node delete
; procedure)
; (queue first node procedure)
; (queue end node procedure)
;queue enqueue procedure
. ;queue dequeue procedure
Placing the object name list before the METHOD keywords tells Turbo Assembler that
the new object queue inherits the methods and data of the object, list. Any object name
placed in this location will be inherited.by the object being declared. You Can use only
one name (only single inheritance is supported).
The new queue object inherits all the data and methods from the list object, unless you
override it. Note that queue needs its own init to install the pointer to the virtual method
table for queues.
The.inherited insert, remove,first, and last method declarations for the queue are
respecified in the declaratio~, so these,methods are replaced with the indicated
procedures. .
Two new methods have been declared for the queue: enqueue and dequeue. Notice that
the method procedure for enqueue is the same as for appending to a linked list.
However, we need a new procedure to dequeue from the queue, and this we call
queue_dequeue.
Th~ queue object has no additional data declared other than what it inherits from list.
It inherits the linked list's head and tail pointers, which are still needed for the queue
because of the linked list methods used to manage the queue.
Declaring amethod procedure
Method procedures manipulate instances of the object. They are much like library
routines in that they should have a well-defined call and a return value/interface, but
knowledge of how the method procedures work internally is not necessary.
The method procedures for an object should provide comprehensive management of
the objects; thatis, they should be the only procedures allowed direct access to the
objects. Furthermore, you should use the concepts of data abstraction when you design
the methods: You should be able to call the method procedures without having any
knowledge of the inner workings of the method procedures.
In all other respects, you can write method procedures for any language or interface you
want, although usually C++ or Pascal calling conventions are used. Any arguments to
the procedures are up to you as well. One argument that is usually required is a pointer
to an object instance. Some method procedures might require additional parameters.
For example, the initialization method for the list object requires justthe pointer to the
48 Turbo Assembler User's Guide
list object, while the list insert method requires a pointer to the list, a pointer to the new
node to insert, and a pointer to the node it's inserted after.
Note There are advantages and disadvantages to using both static and virtual methods. Static
methods are resolved at compile time, and result in direct calls to the method procedure.
This makes the call faster, and does not require you to use intermediate registers (as in
virtual method calls). However, since these calls are resolved at compile time, static
method calls don't have the flexibility of virtual method calls.
Virtual method calls are made indirectly through an instance of the virtual method table
for the object. The fact that the call is indirect gives virtual methods the disadvantage of
requiring you to use intermediate registers when you make the call (which could
complicate your code). A big advantage, however, is that virtual method calls are
resolved at run time. Thus, you can make virtual method calls for a derived object by
calling a common ancestor object's method without having to know exactly what sort of
descendant object you're dealing with.
Note Declare static and virtual method procedures exactly the same way as any other
procedure, with the following exception: if you omit the procedure name for virtual
methods, you'll cause an empty uninitialized location in the virtual method table and
Turbo Assembler won't warn you if you do this. Omitting the procedure name is an
error if the method is not virtual, since virtual methods don't go into the table.
Here's an example of a method procedure:
iConstruct a Linked-List object.
iThis is the method "construct".
iThis must be a static method.
iReturns DX:AX pointing to linked-list object, null if none.
iObject is allocated but not yet initialized.
list_construct PROC PASCAL FAR
USES ds
i-- Allocate the Linked-List object --
ii«do the allocation here»
ret
ENDP
The virtual method table
The virtual method table (VMT) is a table of addresses of the procedures that perform
virtual methods. Usually this table is placed in the program's data segment. Any object
having virtual methods requires an instance of the VMT somewhere in the program.
Use the TBLINST directive to create the instance of the VMT for an object. Since this
directive creates a table for the most recently declared object, you should place this
directive immediately after the object declaration, as in the following:
INCLUDE list.aso
DATASEG
TBLINST
Chap t er 4, Crea tin gob j ect -0 ri ented pro 9ra: ms 49
Initializing the virtual method table
Simply creatirig the instance of the VMT is not enough to let you make calls to virtual
methods. Every object with virtual methods includes a pointer to the VMT in its data
structure. You must initialize this pointer whenever you create an instance of an object,
and can use TBLINIT to' do so.
Initialize the VMT pointer in the init method for the object as follows:
iInitialize a Linked List object.
iThis is the method "init".
iThis must be a static method!
list_init PROC PASCAL FAR
ARG @@list:dword
USES ds,bx
ENDP
Ids bX,@@list
i-- Initialize any virtual method table for the object at ds:bx
TBLINIT ds:bx
i-- Initialize the object's data --
ii«initialize any data for the object here ...»
ret
Notice that the init method must be static because you can't call a virtual method for an
object instance until after you initialize the virtual table pointer.
Calling an object method
Use the CALL instruction to invoke object methods.Turbo Assembler provides an
extension to the standard CALL instruction, CALL..METHOD, for calling method
procedures.
,Notice that the syntax for CALL is similar for calling both static or virtual ~ethods.
Calling astatic method
When making a call to a method procedure, you should write the CALL.'.METHOD
instruction as if you were making a call to a virtual method, even if you know that
you're calling a static method. Doing so will have no ill effects on static method calls,
and gives you the flexibility of changingmethods from static to virtual or back again
without having to change all the calls to the method. For the same reasons, you should
specify a reasonable selection for the mtermediate calling registers, even if you know
that the method you're calling is static.
Calls to static methods are resolved at compile time to direct calls to the desired method
procedure for the object. However, when making the call, you should not make a direct
call to the method procedure; instead, use the extended CALL..METHODinstruction.
The following example shows a sample call to the static init method for the linked list
object. '
50 Turbo Assembler User's Guide
CALL foolist METHOD list: init pasc,al, ds offset foolist
CALL es:di METHOD list:init pascal,es di
The call address itself is the address of aninstance of the object. This address is used for
syntactic reasons only; the actual call generated is a direct call to the method procedure.
In this example, the first call is to the init method for the object list. Since this is a static
method, you make a direct call to the method procedure list_init. Turbo Assembler
ignores the object instance,foolist (except that it's passed as an argument to the method
procedure).
The method name is followed by the usual extended call language and parameter list.
The language and parameters depend on the method you're calling, and one of the
parameters is generally a pointer to the instance of the object. In this example, the
method accepts a single parameter, which is a pointer to the instance of the object.
Calling avirtual method
Any call to a virtual method requires an indirect call to the method procedure. You can
use the extended CALL..METHOD instruction to let this happen. Turbo Assembler
generates the following instructions to perform the call:
1 Loa,d intermediate registers from the object instance with a pointer to the VMT.
2 Make an indirect call to the appropriate table member.
Therefore, when you specify
CALL <instance> METHOD <object>:<method> USES <seg>:<reg> <calling_stuff>
the generated instructions are as follows:
MOV <reg>, [<instance>. <virtual_method_table_pointer>]
CALL [«seg>:<reg» .<method>] <calling_stuff>
The first instruction loads the selected register <reg> with the address of the table from
the VMT pointer field of the object structure. The second instruction makes an indirect
call to the appropriate method in the table.
For example, a call of the form
CA~L es:di method list:insert uses ds:bx pascal,es di,es dX,es cx
generates a sequence like
mov bx, [es:di.@Mptr_list]
CALL [ds:bx.insert] pascal, 
es di,es dX,es cx
Note that for objects declared with NEAR tables, only the offset register will be loaded
by the CALL..METHOD instruction. The segment register should already contain the
correct value. The following example shows how to make sure that the segment register
is properly set up. '
;Append a node at the end of a Linked-List object.
;This is the virtual method "listiappend".
list_append PROC PASCAL NEAR
Chap t er 4, ere atin gob j ect -0 riented pro 9ram s 51
ARG @@list:dword,
@@new:dword
USES ds,bx,es,di
movax,@Data
mov ds,ax
les di,@@list
sub ax,ax
CALL es:di method list:insert uses ds:bx pascal, 
es di,@@new,ax ax
ret
ENDP
Note You can't call any virtual methods until after you initializethe VMT pointer in the
object's data. This is because the pointer loads tJ::te address of the VMT (from which
the address of the desired virtual method procedure is retrieved). Thus, if you haven't
initialized the pointer to the VMT, any virtual method call will result in a call to some
random address.
As another example, consider the base object node, which you can include in any object
placed in a linked list or a queue.
node STRUC GLOBAL METHOD {
construct:dword =node_construct
destroy:dword = node_destroy
init:dword =node_init
deinit:dword = node_deinit
virtual next:word =node_adv
virtual prev:word = node_back
virtual print:word = node_print
}
node_next
node_prey
ends
dd ?
dd ?
inode constructor routine
inode destructor routine
inode initialization routine
inode deinitializ·ation routine
inext node routine
iprevious node routine
iprint contents of node
inext node pointer
iprev node pointer
You can define any number of other objects inheriting the node object, to let it use a
linked list or queue. Here are two examples:
mlabel STRUC GLOBAL node METHOD {
virtual print:word = label_print
}
label_name
label_addr
label_city
,label_state
label..:.zip
ENDS
db 80 dup (?)
db 80*2 dup (?)
db 80 dup (?)
db 2 dup (?)
db 10 dup (?)
book STRUC GLOBAL node METHOD
virtual print:word = book_print
}
book_title db 80 dup (?)
book_author db 80 dup (?)
ENDS
52 TurboA sse mbIer Use r's Guide
In the next example, we're making calls to methods by calling printit for both label and
book objects. It doesn't matter what object gets passed to printit, as long as node is an
ancestor. Because the print method is a virtual method" the call is made indirectly
through the VMT for the object. For the first call to printit, the method procedure
labelyrint is called, because we're passing an instance of a label object. For the second
call to printit, the method procedure bookyrint is called, because we're passing an
instance of a book object. Note that if the method print were static, then the call in printit
would always call the nodeyrint procedure (which is not desirable).
call printit pascal,«instance address of label object»
call printit pascal, «instance address of book object»
printit proc pascal near
arg @@obj:dword
uses ds,si,es,bx
mov aX,@data
mov eS,ax
Ids si,@@obj
call ds:si method node:print uses es:bx pascal,ds si
ret
endp
Calling ancestor virtual methods
Using ancestor virtual methods can help you write methods for derived classes since
you can reuse some of the code. For example, queues can use the same listing method as
a list, as long as you specify whether the item is a queue or a list. Within the list class,
you can have
virtual show:word = list_show
and within the queue class,
virtual show:word = queue_show
The list_show routine might print LIST SHOW:, followed by a listing of the individual
items in the list. However, if the derived class queue_show uses the listing routine, it
should print its own title, QUEUE SHOW: and use list_show only for the mechanics of
sequentially going through the list and printing individual elements. list_show can
determine the kind of structure passed to it, and whether it should print the list title. If
the routine for list_show looks at the pointer to the virtual method table (VMT) of the
structure passed to it, it can determine whether the pointer matches the one installed for
lists in the list3nit routine (or if it differs). If the VMT pointer in the structure does not
point to the VMT for lists, the structure is probably a derived type. list_show can do this
checking with the following statements:
cmp [([es:di]) .@mptr_list];offset @TableAddr_LIST
jne @@not a list '; Skip over printing the list title
Chapter 4, Creating object-oriented programs 53
If we come here, it is a list, and the list title
should be printed.
@@not a list:
;- Now show the individual list elements.
So how do'we call the list class show method from within a queue_show routine? Ifyou
were to directly call list_show, you could have a problem if the name of the routine used
for the show method of the list class ever changes. (You might not remember to change
what queue_show calls.) If you put the following statement in queue_show,
call (es:di) method list:show
you'd have an infinite loop because even though list is specified as the class for which
show should be called, the VMTwill be used because show is a virtual method. Since
the VMT for the structure would have been pointing to queue_show, you'd end up back
in the same routine.
"
The best way to call the list class show method would be
call t@table_list I show
Turbo Assembler automatically translates this statement to a direct call to list_show, ,
since lisCshow was specified as the value for the show element of the @table_list when'
the list class was declared. Note that even though list declares show to be virtual,
specifying the call causes Turbo Assembler to make a direct call without the VMT
lookup.
Note Virtual routines are usually called through an indirect lookup to a VMT.
In the event that you need to use the VMT for the list class (for example, some
initialization routine might'change the show element of the table to point to different
routines depending on whatoutput device to use for the show command of all list class
elements), the following statements use the list class VMT:
mov bx,offset @TABLEADDR_LIST
call [(@table_list ptr es:bx) .SHOW]
This is very similar to the sequence of instructions that Turbo Assembler uses to make
the indirect call using the VMT.
More on calling methods
Often, you might find it necessary to calla parent object's method from inside a derived
method procedure. You can also use the CALL..METHOD statement to do this.
You can use the IMP instruction with the METHOD extension in the same way you use
the CALL..METHOD instruction. This instruction provides optimal tail recursion. See
Chapter 12 for more information about the CALL..METHOD and IMP..METHOD
instructions.
54 Turbo Assembler User's Guide
Creating an instance of an object
To create an instance of an object, you can call an object's constructor method (which
allocates memory for an object instance) or allocate an instance of the object in a
predefined (static) data segment.
You can create an instance of the object exactly the same way you create an instance of a
structure. For example, examine the following instances of objects:
foolist list {} iinstance of a list
fooqueue label queue
queue {} iinstance of a queue
queue {list_head=mynode,list_tail=mynode}
iinstance of a queue
When you create an instance of an object, you can override any of the object's default
data values as defined in the object declaration by specifying the overriding values
inside the braces. You can't, however, override the methods for an object when you
create an instance of an object.
Programming form for objects
It's a good idea to keep method procedures in a separate file from the method
declaration, and from the code that uses the object. We recommend placing method
procedures in a file with the name of the object and an extension of .ASM. For example,
the method procedures for the linked-list object would go into the file LIST.ASM. The
method procedure file must INCLUDE the method declaration from the .ASO file.
An example of the method procedures for the list object is described at the end of this
chapter. This excerpt from the LIST.ASM file (on the example disks) shows the general
structure of this file.
._-------------------------------
,
i-- Define Linked-List objects --
._-------------------------------
,
MODEL SMALL
LOCALS
i** Define Linked-List object **
INCLUDE node.aso
i** Create instance of Linked-List virtual method table **
DATASEG
TBLINST
i** Linked-List methods **
CODESEG
ii«include all method procedures here»
Chapter 4, Creating object-oriented programs 55
In general, you should use the following form for object-oriented programming in
Turbo Assembler:
<object>.ASO
<object>.ASM
INCLUDEs <parenCobject>.ASO, if any; contains GLOBAL object declaration <md a
GLOBAL directive for each method procedure.
INCLUDEs <object>.ASO; contains TBLINST directive and method procedure
.declarations; has an init method with a TBLINIT somewhere inside.
Note that you can use the TBLINST and TBLINIT directives even when there are
~currentlyno virtual methods in the object; in that case, no action is taken.
We therefore recommend using the TBLINST and TBLINIT directives regardless of
whether virtual methods are currently pte~ent in an object: Place the TBLINST directive
in an appropriate data segment and the TBLINIT directive in the object's initialization
method (which must be a static method). You must call this method before using any
other methods for the object.
56 Turbo Assembler User's Guide
Using expressions and
symbol values
Expressions and symbols are fundamental components of an assembly language
program. Use expressions to calculate values and memory addresses. Symbols
represent different kinds of values. This chapter describes the different types of these
language components, and how you can use them.
Constants
Constants are numbers or strings that Turbo Assembler interprets as a fixed numeric
value. You can use a variety of different numeric formats, including decimal,
hexadecimal, binary, and octal.
Numeric constants
A numeric constant in Turbo Assembler always starts with a digit (0-9), and consists of
an arbitrary number of alphanumeric characters. The actual value of the constant
depends on the radix you select to interpret it. Radixes available inTurbo Assembler are
binary, octal, decimal, and hexadecimal, as shown in Table 5.1:
Table 5.1 Radixes
Binary 01
Octal 01234567
Decimal 0123456789
Hexadecimal 0 1 2345 6 789 ABC D E F
Note that for hexadecimal constants, you can use both upper- and lowercase letters.
Chap ter 5, Usin 9 expre ssion san d symb0 I val ues 57
Turbo Assembler determmes the radix of a numeric constantby first checking the LAST
character of the constant. The characters in the following table determille the radix used
to interpret the numeric constant.
Table 5.2 Characters determining radixes
B
o
Q
D
H
Binary
Octal
Octal
Decimal
Hexadecimal
You can use both uppercase and lowercase characters to specify the radix of a number.
If the last character of the numeric constant is not one of these values, Turbo Assembler
will use the current default radix to interpret the constant. The following table lists the
available numeric constants and their values.
Table 5.3 Numeric constants
77d
77h
ffffh
Offffh
88
77 decimal
77 hexadecimal
illegal; doesn't start with a digit
FFFF hexadecimal
Interpretation depends on current default radix
Changing the default radix
You can use the RADIX or .RADIX directivesto change the current default radix. Use
the following syntax for Ideal mode:
RADIX expr~ssion
Here's the MASM mode syntax:
.RADIX expression
expression must have a value of either 2 (binary), 8 (octal), 10 (decimal), or 16
(hexadecimal). Turbo Assembler assumes that the current default radix is decimal while
it processes the RADIX directive.
String constants
String constants always begin with a single or double quote, and end with a matching
single or double quote. Turbo Assembler converts the characters between the quotes to
ASCII values.
Sometimes, you might want to include a quote within a string constant.' To do this, use
a pair of matching quotes as a single matching quote character within the string. For
example,
58 Turbo Assembler User's Guide
1 It lIS 1 represents It 1 S
Symbols
A symbol represents a value, which canbe a variable, address label, or an operand to an
assembly instruction and directive.
Symbol names
Symbol names are combinations of letters (both uppercase and lowercase), digits, and
special characters. Symbol names cadt start with a digit. Turbo Assembler treats
symbols as either case sensitive or case insensitive. The command line switches !ML,
!MU, and IMX control the case sensitivity of symbols. For more information about these
command-line switches, see Chapter 2.
Symbols names can be up to 255 characters in length. By default, symbol names are
significant up to 32 characters. You can use the !MV command-line switch to change the
number of charac~ers of significance in symbols.
The underscore C), question mark (?), dollar sign ($), and at-sign (@) can all be used as
part of a symbol name. In MASM mode only, you can use a dot (.) as the first character
of a symbol name. However, since it's easy to confuse a dot at the start of a symbol with
the dot operator (which performs a structure member operation), it's better not to use it
in symbol names.
Symbol types
Each symbol has a type that describes the characteristics and information associated
with it. The way you define a symbol determines its type. For example, you can declare
a symbol to represent a numeric expression, a text string, a procedure name, or a data
variable. Table 5.4 lists the types of symbols that Turbo Assembler supports.
Table 5.4 Symbol types
text_macro
alias
numericatexpr
multiline_macro
struc/union
table
struc/table_member
record
record-field
enum
An address. subtypes are PWORD
or FWORD, QWORD, TBYTE, and an address of a named structure or table.
Code subtypes are SHORT, NEAR, and FAR
A text string
An equivalent symbol
The value of a numerical expression
Multiple text lines with dummy arguments
A structure or union data type
A table data type
A structure or table member
A record data type
A record field
An enumerated data type
Chap ter 5, Usin 9 expre ssion san d symb0 I val ues 59
Table 5.4 Symbol types (continued)
segment
group
type
proctype
A segment
A group
A named type
A procedure description type
Simple address subtypes
Symbols subtypes describe whether the symbol represents the address of a byte, a word,
and so forth. Table 5.5 shows the simple address subtypes that Turbo Assembler
provides.
,
Table 5.5 Address subtypes
UNKNOWN Unknown or undetermined address subtype.
BYTE Address describes a byte.
WORD Address describes a word.
DWORD Address describes a 4-byte quantity.
PWORD or FWORD
QWORD
TBYTE
SHORT
NEAR
FAR
PROC
DATAPTR
CODEPTR
struclunion_name
table_name
record_name
type_name
TYPE expression
Address describes a6-byte quantity.
Address describes an 8-byte quantity.
Address describes a lO-byte quantity.
Address describes a short label/procedure address.
Address describes a near label/procedure address.
Address describes a far label/procedureaddress.
Address describes either a near or far label/procedure address, depending on
the currently selected programming model.
Address describes either a word, dword, or pword quantity, depending on the
currently selected programming model.
Address describes either a word, dword, or pword quantity, depending on the
currently selected programming model.
Address describes an instance of the named structure or union.
Address describes an instance of the named table.
Address describes an instance of the named record; either a byte, word, or
dword quantity. .
Address describes an instance of the named enumerated data type; either a
byte, word,or dword quantity.
Address describes an instance of the named type;
Address describes an item whose subtype is the address subtype of the
expression; Ideal mode only.
Address describes procedure of proctype.
60 Turbo Assembler User'& Guide
Describing acomplex address subtype
Severaldirectives let you declare and use complex address subtypes. These type
expressions are similar to C in that they can represent multiple levels of pointer
indirection, for example, the complex type expression
PTR WORD
represents a pointer to a word. (The size of the pointer depends on the segmentation
model you selected with MODEL.)
Table 5.6 shows a syntax summary of complex address subtypes:
Table 5.6 Complex address subtypes
···synt~"···'
simple_address_subtype
[dist]PTR[complex_address_subtype]
the specified address subtype
a pointer to the specified complex address subtype, the size of
which is determined by the current MODEL or by the specified
distance, if present
You can describe the optional distance parameter in the following ways:
Table 5.7 Distance syntax
NEAR
FAR
SMALL NEAR
LARGE NEAR
SMALL FAR
LARGE FAR
use a near pointer;.can be either 16 or 32 bits, depending on the current model
use a far pointer; can be either 32 or 48 bits, depending on current model
use a 16-bit pointer; 80386 and 80486 only
use a 32-bit near pointer; 80386 and 80486 only
use a 32-bit far pointer; 80386 and 80486 only
use a 48-bit far pointer; 80386 and 80486 only
The type of the object being pointed to is not strictly required in complex pointer types;
Turbo Assembler only needs to know the size of the type. Therefore, forward references
are permitted in complex pointer types (but not in simple types).
Expressions
Using expressions lets you produce modular code, because you can represent program
values symbolically. Turbo Assembler performs any recalculations required because of
changes (rather than requiring you to do them).
Turbo Assembler uses standard infix notation for equations. Expressions can contain
operands and unary or binary operators. Unary operators are placed before a single
Chapter 5, Using expressions and symbol values 61
operand; binary operators are placed between two operands. Table 5.8 shows examples
of simple expressions. ..
TableS.S Simple expressions
5 constant 5
-5 constant-5
4+3 constant 7
4*3 constant 12
4*3+2*1 constant 14
4*(3+2)*1 constant 21
Appendix Bcontains tp.e full Backus-Naur form (BNF) grammar that Turbo Assembler
uses for expression parsing in both MASM and Ideal modes. This grammar inherently
describes the valid syntax of Turbo Assembler expressions, as well as operator
precedence.
Expression precision
Turbo Assembler always uses 32-bit arithmetic in Ideal mode. In MASM mode, Turbo
Assembler uses either 16- or 32-bit arithmetic, depending on whether you select the
80386 processor. Therefore, some expressions might produce different results
depending on which processor you've selected. For example,
(1000h 1000h) /1000h
evaluates to 1000h if you select the 80386 processor, or to 0 if you select the 8086, 80186,
or 80286 processors.
Constants in expressions
You can use constants as operands in any expression. For example,
mov ax,S ;"5" is a constant operand
Symbols in expressions
When you use a symbol in an expression; the returned value depends on the type of
symbol. You can use a symbol by itself or in conjunction with certain unary operators
that are designed to extract other information from the entity represented by the
symbol.
Registers
Register names represent 8086-family processor registers, and are set aside as part of the
expression value. For example,
5+ax+7
62 TurboA sse mbIer Use r's Guide
This expression has a final value ofax+12, because AX is a register symbol that Turbo
Assembler sets aside. The following list contains register symbols:
8086
80186,80286
80386
AX,BX,CX,DX,SI,D1,BP,CS,DS,ES,SS
Same as 8086
8086 registers, plus EAX, EBX, ECX, EDX, ES1, ED1, EBP, FS, GS, CRO,
CR2, CR3, DRO, DR!, DR2, DR3, DR6, DR7
80486 80386 registers, plus: TR3, TR4, TR5
Standard symbol values
Some symbols always represent specific values and don't have to be defined for you to
use them. The following table lists these symbols and their values.
Table 5.9 Standard symbols
$ Current program counter
NOTHING 0
? 0
UNKNOWN 0
BYTE 1
WORD 2
DWORD 4
PWORD 6
FWORD 6
QWORD 8
TBYTE 10
NEAR Offffh
FAR Offfeh
PROC Either Offffh or Offfeh, depending on current model
CODEPTR Either 2 or 4, depending on current model
DATAPTR Either 2 or 4, depending on current model
Simple symbol values
Turbo Assembler returns the following values for symbols used by themselves:
Table 5.10 Values of symbols used by themselves
address_name Returns the address.
numericaCexpr--,-name Returns the value of the numerical expression.
table_name I table_member_name Returns the default value for the table memberspecified in the definition
of the table.
Returns the offset of the member within the table or structure (MASM
mode only). "
Chapter 5, Using expressions and symbol values 63
Table 5.10 Values of symbols used by themselves (continued)
record_name <...>
record_name {...}
recordJield_name
segment_name
group_name
struc/union_name
Returns a mask where the bits reserved to represent bit fields in the
record definition are1, the rest are O.
Returns the initial value a record instance would have if it were declared
with the Salne text enclosed in angle brackets (see Chapter 12 for details).
Similar to record_name <...>.
Returns the number ofbits the field is displaced fromthe low orderbit of
the record (also known as the shift value).
Returns a mask where the bits required to represent the maximum value
present in the enum definition are 1, the rest are O.
Returns the segment value.
Returns the group value.
Returns the size in bytes of the structure or union, but only ifit is 1, 2, or
4; all other sizes return a value of O.
If the type is defined as a synonym for a structure or union, the value
returned is the same as for a structure or union. Otherwise, the size of the
type is returned (with Offffh for short and near labels, and Offfeh for far
labels).
Returns OFFFFh if the proctype describes a near procedure, or OFFFEh
for a far procedure.
All other symbols types return the value O.
Note that when you use a text macro name in an expression, Turbo Assembler
substitutes the string value of the text macro for the text macro symbol. Similarly, when
you use an alias name, Turbo Assembler substitutes the symbol value that the alias
represents for the alias symboL
The LENGTH unary operator
The LENGTH operator returns information about the count or number of entities
represented by a symbol. The actual value returned depends on the type of the symbol,
as shown in the following table.
Table 5.11 LENGTH operator return values
LENGTH address_name Returns the count of items allocated when the address name was
defined.
LENGTH strucltable_member_name Returns the count of items allocated when the member was defined
(MASM mode only).
The length operator (when applied to all other symbol types) returns the value 1. Here
are some examples using the LENGTH operator:
MSG DB "Hello"
array DW 10 DUP (4 DUP (1),0)
nurnbrs DD 1,2,3,4
lrnsg =LENGTH rnsg
larray =LENGTH array
Inurnbrs = LENGTH nurnbrs
64 Turbo Assembler User's Guide
; =1, no DUP
;=10, DUP repeat count
; =1, rio DUP
The SIZE unary operator
The SIZE operator returns size information about the allocated data item. The value
returned depends on the type of the symbol you've specified. The following table lists
the available values for SIZE.
Table 5.12 . SIZE values
Expression
SIZE address_name
SIZE struc/union_name
SIZE table_name
SIZE struc/table_member_name
SIZE record_name
SIZE segment_name
SIZE type_name
.Value
In Ideal mode, returns the actual number of bytes allocated to the data
variable. In MASM mode, returns the size of the subtype of
address_name (UNKNOWN=O, BYfE=l, WORD=2, DWORD=4,
PWORD=FWORD=6, QWORD=8, TBYTE=10, SHORT=NEAR=Offffh,
FAR=Offfeh, structure address=size of structure) multiplied by the value
of LENGTH address_name.
Returns the number of bytes required to represent the structure or union.
Returns the number of bytes required to represent the table.
Returns the quantity TYPE struc/table_member_name * LENGTH struc/
table_member_name (MASM mode only).
Returns the number of bytes required to represent the total number ofbits
reserved in the record definition; either 1, 2, or 4.
Returns the number of bytes required to represent the maximum value
present in the enum definition; either 1, 2, or 4.
Returns the size of the segment in bytes.
Returns the number of bytes required to represent the named type, with
short and near labels returning Offffh, and far labels returning Offfeh.
The SIZE operator returns the value 0 when used on all other symbol types.
The WIDTH unary operator
The WIDTH operator returns the width in bits of a field in a record. The value depends
on the type of symbol. The following table shows these types of symbols. You can't use
WIDTH for any other symbol types.
Table 5.13 WIDTH values
WIDTH record_name Returns the total number of bits reserved in the record definition.
WIDTH recordJield_name Returns the number of bits reserved for the field in the record definition.
WIDTH enum_name Returns the number of bits required to represent the maximum value in the
enum definition.
MASK unary operator
The MASK operator creates a mask from a bit field, where bits are set to 1 in the
returned value and correspond to bits in afield that a symbol represents. The value
Chapter 5, Using expressions and symbol values 65
returned depends on the type of symbol, as shown in the following table. Note that you
can't use MASK on any other symbols.
Table 5.14 MASK return values
MASK record_name Returns a mask where the bits reserved to represent bit fields in the record
definition are I, the rest O.
MASK recordJield_name ' Returns a mask where the bits reserved for the field in the record definition are
I, the rest O.
Returns a mask where the bits required to represent up to the maxiinum value
present in the enum definition are I, the rest O.
General arithmetic operators
General arithmetic operators manipulate constants, symbol values, and the values of
other general arithmetic operations. Common operators are addition, subtraction,
multiplication, and division. Others operators are more specifically tailored for
assembly language programming. We'll;discuss a little about all of these in the next few
sections.
Simple arithmetic operators
Turbo Assembler supports the simple arithmetic operators shown in the following
table.
Table 5.15 Simpl~ arithmetic operators
+ expression Expression.
- expression
exprl + expr2
exprl - expr2
exprl *expr2
exprl / expr2
exprl MOD expr2
Negative of expression.
exprl plus expr2.
exprl minus expr2.
exprl multiplied by expr2.
exprl divided by expr2 using signed integer division; note that expr2 cannot be 0 or
greater than 16 bits in extent.
Remainder of exprl divided by expr2; same rules apply as for division.
Logical arithmetic operators
Logical operators let you perform Boolean algebra. Each of these operators performs in
a bitwise manner; that is, the logical operation is performed one bit at a time. The
following table shows the logical operators.
Table 5.16 Logical arithmetic operators
NOT expression
exprl ANDexpr2
exprl OR expr2
exprl XOR expr2
. expression bitwise complemented
exprl bitwise ANDed with expr2
exprl bitwise ORed with expr2
exprlbitwise XORed with expr2
66 Turbo Assembler User's Guide
Bit shift operators.
Shift operators move values left or right by a fixed number of bits. You can use them to
do quick multiplication or division, or to access the value of a bitfield within a value.
The following table lists the bit shift operators.
Table 5.17 Bit shift operators
···~~~~~S~Q~··";·
exprl SHL expr2
exprl SHR expr2
exprl shifted left by expr2 bits (shifted right if expr2 is negative).
exprl shifted right by expr2 bits (shifted left if expr2 is negative).
Note that the SHL and SHR operators shift in Os from the right or left to fill the vacated
bits.
Comparison operators
Comparison operators compare two expressions to see if they're equal or unequal, or if
one is greater than or less than the other. The operators return a value of -1 if the
condition is true, or a value of 0 if the condition is not true. The following table shows
how you can use these operators.
Table 5.18 Comparison operators
exprl EQ expr2
exprl NE expr2
exprl GT expr2
exprl GE expr2
exprl LT expr2
exprl LE expr2
-1 if exprl is equal to expr2; otherwise, O.
-1 if exprl is not equal to expr2; otherwise, O.
-1 if exprl is greater than expr2; otherwise, O.
-1 if exprl is greater than or equal expr2; otherwise, O.
-1 if exprl is less than expr2; otherwise, O.
-1 if exprl is less than or equal expr2; o~erwise, O.
EQ and NE treat expressions as unsigned numbers. For example; -1 EQ offffh has a
value of -1 (unless you've selected the 80386 processor or used Ideal mode; then, -1 EQ
offffffffh has a value of -1).
GT, GE, LT, and LE treat expressions as signed numbers. For example, 1 GE -1 has a
value of -1, but 1 GE Offffh has a value of O.
Setting the address subtype of an expression
Turbo Assembler provides operators that let you override or change the type of an
expression. The following table lists these operators.
Table 5.19 Type override operators
~ '.'h
exprl PTR expr2
type PTR expression
or type expression
Converts expr2 to the type determined by exprl, where O=UNKNOWN, l=BYTE,
2=WORD,4=DWORD, 6=PWORD, 8=QWORD, 10=TBYTE, Offffh=NEAR,
Offfeh=FAR, all others=UNKNOWN; MASM mode only.
! Converts expression to the specified address subtype; Ideal mode only.
Chap ter 5, Usin 9 ex pre ssion san d symb0 I val ues 67
Table 5.1~ Type override operators (continued)
type LOW expression Converts expression to the specified address subtype. Type described mustbe
smaller in size than the type of the expression; Ideal mode only.
type HIGH expression Converts expression to the specified address subtype. Type described must be
smaller in size than the type of the expression; the resulting address is adjusted to
point to the high part of the object described by the address expression; Ideal
mode only.
Here are some examples:
IDEAL
big DD 12345678h
MOV ax, [WORD big]
MOV aI, [BYTE PTR big]
MOV ax, [WORD HIGH big]
MOV ax, [WORD LOW big]
MOV aI, [BYTE LOW WORD HIGH big]
MASM
MOV aX,2 PTR big
MOV aX,WORDPTR big
iax=5678h
ial=78h
iax=1234h
iax=5678h
ial = 3rd byte of big = 34h
iax=5678h
iax=5678h (WORD has value 2)
Obtaining the type of an expression
In MASM mode, you can obtain the numeric value of the type of an expression by using
the TYPE operator. (You can't do this in Ideal mode, because types can never be
described numerically). The syntax of the TYPE operator is
TYPE expression
The TYPE operator returns the size of the object described by the address expression,as
follows:
Table 5.20
byte
word
dword
pword
qword
tbyte
short
near
far
struct/union
table
proctype
TYPE values
1
2
4
6
8
10
Offffh
Offffh
Offfeh
Size of a structure or union instance
Size of a table instance
Returns OFFFFh if the proctype describes a near procedure, or OFFFEh for a far procedure
Here's an example:
68 TurboA 55 embIer U5 er's Guide
avar = 5
bvar db 1
darray dd 10 dup (1)
x struc
dw ?
dt ?
ends
fp label far
tavar = TYPE avar
tbvar = TYPE bvar
tdarray =TYPE darray
tx = TYPE x
tfp = TYPE fp
i=O
i= 1
i= 4
i= 12 .
i= OFFFEh
Overriding the segment part of an address expression
Address expressions have values consisting of a segment and an offset. You can specify
the segment explicitly as a segment register, or as a segment or group value. (If you
specify it as a group value, Turbo Assembler determines which segment register to use
based on the values that the segment registers are ASSUMEd to be.) Use the following
syntax to change the segment part of an address expression:
exprl : expr2
This operation returns an address expression using the offset of expr2, and exprl as a
segment or group value. For example,
VarPtr dd dgroup:memvar
mov el, es: [si+4J
idgroup is a group
isegment override ES
Obtaining the segment and offset of an address expression
You can use the SEG and OFFSET operators to get the segment and offset of an
expression. The SEG operator returns the segment value of the address expression.
Here's its syntax:
SEG expression
Here is a code example:
DATASEG
temp DW 0
CODESEG
mov aX,SEG temp
mov ds,ax
ASSUME ds:SEG temp
The OFFSET operator returns the offset of the address expression. Its syntax follows:
OFFSET expression
Note that when you use the offset operator, be sure that the expression refers to the
correct segment. For example, if you are using MASM mode and not using the
simplified segmentation.directives, the expression
Chap ter 5, U5 in9 expre 5 5 ion 5 and symb0 I val ue5 69
OFFSET BUFFER ;buffer is a memory address
is not the same as
OFFSET DGROUP:BUFFER ;Dgroup is the group containing the segment that contains BUFFER
unless the segment that contains BUFFER happens to the first segment in DGROUP.
In Ideal mode, addresses are automatically calculated relative to any group that a
segment belongs to unless you override them with the: operator. In MASM mode, the
same is true ifyou use the simplified segment directives. Otherwise, addresses are
calculated relative to the segment an object is in, rather than any group.
Creating an address expression using the location count~r
You can use the THIS operator to create an address expression that points to the current
segment and location counter, and has a specific address subtype. You can use the
following syntax in Ideal mode:
THIS type
The Ideal mode syntax lets you build an address expression from the current segment
and location counter ofthe specified type.
You can use the next syntax in MASM mode:
THIS expression
The MASM mode syntax functions like the syntax in Ideal mode, but uses the numerical
value of the expression to determine the type. These values are: O=UNKNOWN,
l=BYTE, 2=WORD, 4=DWORD, 6=PWORD, 8=QWORD, lO=TBYTE, Offffh=NEAR,
Offfeh=FAR. For example,
ptrl LABEL WORD
ptr2 EQU THIS WORD ;similar to ptrl
Determining the characteristics of an expression
Sometimes, it's useful to determine (within a macro) whether an expression has specific
characteristics. The SYMTYPE and .TYPE operators let this happen.
The Ideal mode syntax:
SYMTYPE expression
The MASM mode syntax:
.TYPE expression
Note The SYMTYPE and .TYPE operators are exactly equivalent; however, .TYPE is available
only in MASM mode, and you can use SYMTYPE·only in Ideal mode.
70 TurboA sse mbIer Use r 's Guide
SYMTYPE and .TYPE both return a constant value that describes the expression. This
value is broken down into the bit fields shown in the following table.
Table 5.21 Bit fields from SYMTYPE and .TYPE
o Expression is a program relative memory pointer.
1 Expression is a data relative memory pointer.
2 Expression is Ii constant value.
3 Expression uses direct addressing mode.
4 Expression contains a register.
5 .. Symbol is defin~d.
7 Expression contains an externally defined symbol.
The expression uses register indirection ([BX]) if bits 2 and 3 are both zero.
If Turbo Assembler can't evaluate the expression, SYMTYPE returns appropriate errors.
.TYPE, however, will return a value in these situations (usually 0).
Referenc-ing structure, union, and table member offsets
Structure, union, and table members are global variables whose values are the offset of
the member within the structure, union, or table in MASM mode. In Ideal mode,
however, members of these data types are considered local to the data type. The dot (.)
operator lets you obtain the offsets of members. Here's the Ideal mode syntax:
expression . symbol
expression must represent an address of a structure, union, or table instance. symbol must
be a member of the structure, union, or table. The dot operator returns the offset of the
member within the structure.
MASM mode also contains a version of the dot operator. However, its function is
similar to the + operator, and has the following syntax:
exprl . expr2
Describing the contents of an address
Many instructions reqtt:iie you to distinguish between an address and the contents of an
address. You can do this by using square brackets ([]). For example,
MOV M,BX
MOV AX, [BX]
imove EX into M.
imove contents of address BX into AX
Here's the general syntax for using square brackets:
[ expression ]
In MASM mode, the brackets are optional for expressions that are addresses. Complete
addresses can't be used as an operand for any 80x86 instruction; rather, only the
segment (obtained with the SEC operator) or the offset (obtained with the OFFSET
operator) is used.
Chapter 5, Using expressions and symbol values 71
In Ideal mode, a warning is given when an expression is clearly an address, but no
brackets are present. You can disable this warning (see Chapter 12 for further
information). However, it's good programming practice to include_these brackets.
Implied addition
In MASM mode, you can add expressions in several ways: usingthe addition operator
(+), using the dot operator (.), or by implied addition (when expressions are separated
by brackets or parentheses). For example,
MOV AX, 5[BX]
MOV AX,5 (XYZ)
icontents of address BX+5
icontents of addressXYZ+5
Here's the general syntax for implicit addition:
exprl [ expr2 ]
or
exprl ( expr2 )
Obtaining the high or low byte values of an exp'ression
You can use the HIGH and LOW operators on an expression to return its high and low.
byte values. This information can be useful in circumstances where, for example, only
the high 8 bits of an address offset is required.
Here's the syntax of the HIGH and LOW operators:
HIGH expression
LOW expression
For example,
magic equ 1234h
mov cl,HIGH magic
mov cl,LOW magic
icl=12h
icl=34h
Specifying a16· or 32·bit expression
When the currently selected processor is the 80386 or higher, Turbo Assembler provides
two operators that let you control whether an expression is interpreted as a 16-bit value
or as a 32-bit value: the SMALL and LARGE operators. Here are their syntaxes:
SMALL expression
LARGE expression
The SMALL operator flags the expression as representing a 16-bit value. LARGE flags it
as representing a 32-bit value. These operators are particularly important when you
program for an environment in which some segments are 32-bit and others are 16-bit.
For example, the instruction
JMP [DWORD PTR ABC]
represents an indirect jump to the contents of the memory variable ABC. If you have
enabled the 80386 processor, this instruction could be interpreted as either a far jump
with a segment and 16-bitoffset, or a near jump to a 32-bit offset. You can use SMALL or
LARGE to remove the ambiguity, as follows:
72 Turbo Assembler User's Guide
JMP SMALL [DWORD PTR ABC]
This instruction causes Turbo Assembler to assemble the jump instruction so that the
value read from ABC is interpreted as a 16-bit segment and 16-bit offset. Turbo
Assembler then performs an indirect FAR jump.
When you use SMALL or LARGE within the address portion of an expression, the
operators indicate that the address is a 32-bit address. For example,
JMP SMALL [LARGE DWORD PTR ABC]
indicates that a large 32-bit address describes the memory variable ABC, but its contents
are interpreted as a 16-bit segment and 16-bit offset.
Cha pte r 5, Usin 9 expre ssion san d symb0 I val ues 73
74 Turbo Assembler User's Guide
Choosing processor directives
and symbols
The 8086 processor is actually only one of a family of processors known as the iAPx86
family. Members of this family include
• The 8088 (which contains an 8-bit data bus), the 8086 (containing a 16-bit data bus)
• The 80186 and 80188 (like the 8086 and 8088 but contain additional instructions and
run faster than their predecessors)
• The 80286 (which contains instructions for protected mode)
• The 80386 (which can process 16- and 32-bit data)
• The 80486 (an enhanced version of 80386 that runs even faster).
• The Pentium (an even faster version of the 80486).
Math coprocessors such as the 8087, 80287, and 80387 work with the iAPx86 family so
that you can perform floating-point operations.
Turbo Assembler provides directives and predefined symbols that let you use the
instructions included for particular processors. This chapter describes these directives
and symbols.
Chapter 6, Choosing processor directives and symbols 75
iAPx86 processor directives
The iAPx86 family provides a variety of directives for you to use. In the following
directives, note that those beginning with. are only available in MASM mode.
Table 6.1
P8086
.8086
P186
.186
P286
·P286N
P286P
.286
.286C
.286P
P386
P386N
P386P
.386
.386C
.386P
P486
P486N
.486
.486C
.486P
.487
P487
P586
P586N
.586
Processor directives
Enables assembly of 8086 instructions only.
Enables assembly of the 8086 instructions and disables all instructions available only on the
80186,80286, and 386 processors. It also enables the 8087 coprocessor instructions exactly as
if the .8087 or 8087 had been issued.
Enables assembly of 80186 instructions.
Enables assembly of 80186 instructions.
Enables assembly of all 80286 instructions.
Enables assembly of nonprivileged 80286 instructions.
Enables assembly of privileged 80286 instructions.
Enables assembly of nonprivileged 80286 instructions. It also enables the 80287 numeric
processor instructions exactly as if the .286 or P287 directive had been issued.
.Enables assembly of nonprivileged 80286 instructions.
Enables assembly of all the additional instructions supported by the 80286 processor,
including the privileged mode instructions. It also enables the 80287 numeric processor
instructions exactly as if the .287 or P287 directive had been issued.
Enables assembly of all 386 instructions.
Enables assembly of all nonprivileged 386 instructions.
Enables assembly of privileged 386 instructions.
Enables assembly of the additional instructions supported by the 386 processor in
nonprivileged mode. It also enables the 80387 numeric processor instructions exactly as if the
.387 or P387 directive had been issued.
Enables assembly of 386 instructions.
Enables assembly of all the additional instructions supported by the 386 processor, includirig
the privileged mode instructions. It also enables the 80387 numeric processor instructions
exactly as if the .387 or P387 directive had been issued.
Enables assembly of all i486 instructions.
Enables assembly of nonprivileged i486 instructions.
Enables assembly of the additional instructions supported by the i486 processor in
nonprivileged mode. It also enables the 387 numeric processor instructions exactly as if the
.387 or P387 directive had been issued.
Enables assembly of all i486 instructions.
Enables assembly of all the additional instructions supported by the i486 processor,
including the privileged mode instructions. It also enables the 80387 numeric processor
instructions exactly as if the .387 or P387 directive had been issued. .
Enables assembly of 487 numeric processor instructions. This instruction works only in
MASMmode.
Enables assembly of 487 numeric processor instructions. This instruction works inboth
MASM and Ideal modes.
Enables assembly of all Pentium instructions.
Enables assembly of nonprivileged Pentium instructions.
Enables assembly of the additional instructions supported by the Pentium processor in
nonprivileged mode.
76 TurboA S S 9 mbI9 r US9 r' S Gui d9
Table 6.1
l.1it¢ctiy~
.586C
.586P
.587
P587
@Cpu
Processor directives (continued)
Enables all Pentium instructions.
Enables assembly of all the additional instructions supported by the PentiUm processor,
including the privileged mode instructions.
Enables assembly of Pentium numeric processor instructions. This instruction works only in
MASMmode.
Enables assembly of Pentium numeric processor instructions. This instruction ~orks in both
MASM and Ideal modes.
Note The Quick Reference Guide contains details on the assembly instructions supported by
each processor. For additional information, refer to the books listed in Chapter 1.
Predefined symbols
@Cpu
Two predefined symbols, @Cpu and @WordSize, can give you information about the
type of processor you're using, or the size of the current segment. Here are descriptions
of these symbols:
Function Numeric equate that returns information about current processor
Remarks The value returned by @Cpu encodes the processor type in a number of single-bit fields:
o 8086 instructions enabled
1 80186 instructions enabled
2 80286 instructions enabled
3 386 instructions enabled
4 486 instructions enabled
5 586 instructions enabled
7 Privileged instructions enabled (80286, 386, 486)
8 8087 numeric processor instructions
10 80287 numeric processor instructions
11 387 numeric processor instructions
The bits not defined here are reserved for future use. Mask them off when using @Cpu
so that your programs will remain compatible with future versions of Turbo Assembler.
Since the 8086 processor family is upward compatible, when you enable a processor
type with a directive like .286, the lower processor types (8086, 80186) are automatically
enabled as well.
Chapter 6, Choosing processor directives and symbols 77
@WordSize
This equate only provides information about the processor you've selected at assembly
time using the .286 and related directives. The processor type and the CPU your
program is executing on at run time are not indicated.
Example IPUSH = @Cpu AND 2 ;allow immediate push on 186 and above
IF IPUSH
PUSH 1234
ELSE
, mov ax,1234
push ax
ENDIF
@WordSize
Function Numeric equate that indicates 16- or 32-bit segments
Remarks @WordSize returns 2 if the current segment is a 16-bit segment, or 4 if the segment is a
32-bit segment.
Example IF @WordSize EQ 4
mov esp,0100h
ELSE
mov sp,0100h
ENDIF
8087 coprocessor directives
The following table contains the a.vailable math coprocessor directives. Again, directives
beginning with a dot (.) work only in MASM mode.Turbo Assembler User's Guide
Table 6.2
.287
.387
.487
.587
.8087
P287
P387
P487
8087 coprocessor directives
Enables assembly of all the 80287 numeric coprocessor instructions. Use this directive ifyou
know you'll never run programs using an 8087 coprocessor. This directive causes floating-
point instructions to be optimized in a manner incompatible with the 8087, so don't use it if
you want your programs to run using an 8087.
Enables assembly of all the 80387 numeric coprocessor instructions. Use this directive if you
knowyou'll never run programs using an 8087 coprocessor. This directive causes floating-
point instructions to be optimized in a manner incompatible with the 8087, so don't use it if
you want yout programs to run using an 8087.
Enables assembly of all 80486 numeric instructions.
Enables assembly of all Pentium numeric instructions.
Enables all the 8087 coprocessor instructions, and disables all those coprocessor instructions
available only on the 80287 and 80387 (the default).
Enables assembly of 80287 coprocessor instructions.
Enables assembly of 80387 coprocessor instructions.
Enables assembly of all 80486 numeric instructions.
78 TurboA sse mbIer Use r's Guide
@WordSize
Table 6.2 8087 coprocessor directives (continued)
Di~~~Y'~;' ,',M~¥1ng;
P587 Enables assembly of all Pentium numeric instructions.
P8087 Enables assembly of 8087 coprocessor instructions.
Coprocessor emulation directives
If you need to use real floating-point instructions, you must use an 80x87 coprocessor. If
your program has installed a software floating-point emulation package, you can use
the EMUL directive to use it. (EMUL functions like Ie.)
For example,
Finit
EMUL
Fsave BUF
;real 80x87 coprocessor instruction
;emulated instruction]
Note Both EMUL and NOEMUL work in MASM and Ideal modes.
If you're using an 80x87 coprocessor, you can either emulate floating-point instructions
using EMUL, or force the generation of real floating-point instructions with the
NOEMUL directive. Note that you can use EMUL and NOEMUL when you want to
generate real floating-point instructions in one portion of a file, and emulated
instructions in another.
Here's an example using NOEMUL:
NOEMUL
finit
EMUL
;assemble real FP instructions
iback to emulation
Chapter 6, Choosing processor directives and symbols 79
80 TurboA 5 5 em bIer U5 er'5 Guide
Using program models and
segmentation
Each processor in the 80x86 family has at least four segment registers: CS, DS, ES, and
SS. These registers contain a segment value that describes a physical block of memory
up to 64K in length (or up to 4 gigabytes on the 80386 and above). All addresses are
calculated using one of these segmentregisters as a base value.
The meaning of the value stored in a segment register differs depending on whether the
processor is using real mode (the ONLY mode available for the 8086 and 80186), where
the segment value is actually a paragraph number, or protected mode, where a segment
register contains a selector (which has no numerical significance).
The operating system or platform for a program determines whether the program
operates in real mode or protected mode. If you use protected mode on the 80386 or
80486, the operating system also determines whether large (4 gigabyte) segments are
permitted. Turbo Assembler supports all of these environments equally well.
In the general80x86 model, programs are composed of one or more segments, where
each segment is a physically distinct piece of code or data (or both) designed to be
accessed by usinga segment register. From this general scheme, many arbitrary
organizations are possible. To apply some order to the chaos, some standard memory
models have been devised. Since many high-level languages adhere to these
conventions, your assembly language programs should also.
One obvious way to break up a program is to separate the program instructions from
program data. You can classify each piece of program data as initialized (containing an
initial value, such as text messages), or uninitialized (having no starting value). Turbo
Assembler usually assigns uninitialized data to a separate segment so that it can be
placed at the end of the program, reducing the size of the executable program file.
The stack is usually a fairly large portion of the uninitialized data. It's also special
because the SS and SP registers are usually initialized automatically to the stack area
Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 81
when you execute a program. Thus, the standard memory models treat the stack as a
separate segm~nt.
You can also combine segments into groups. The advantage of using groups is that you
can use thesame segment value for all the segments in the group. For example,
initialized data, uninitialized data, and stack segments are often combined into a group
so that the same segment value can be used.for all of the program data.
. This chapter describes how to use models and segments in your code and the directives
that make this possible.
The MODEL directive
The MODEL directive lets you specify one of several standard segmentation models for
your program. You can also use it to specify a language for the procedures in your
program.
Here's the syntax for the MODEL directive:
MODEL [model_modifier] memory_model [code_segment_name]
[, [language_modifier] language]
[, modeLmodifier]
In MASM mode, you can use the same syntax, but with the .MODEL directive.
memory_model and modeCmodifier specify the segmentation memory model to use for the
program.
The standard memory models available in Turbo Assembler have specific segments
available for:
• code
• initialized data
• uninitialized data
• far initialized data
• far uninitialized data
• constants
• stack
The code segment usually contains a module's code (but it can also contain data if
nece~sary). Initialized data and constants are treated separately for compatibility with
some high level languages. They contain data suchas messageswhere the initial value is
important. Uninitialized data and stack contain data whose initial value is unimportant.
Far initialized data is initialized data that is not part of the standard data segment, and
can be reached only by changing the value of a segment register. A module can have
more than one far initialized data segment. Far uninitialized data is similar, except that
it contains uninitialized data instead of initialized data.
The specific memory model determines how these segments are referenced with
segment registers, and how they are combined into groups (if at all). When writing a
program, you should keep these segments separate, regardless of the program's size.
82 TurboA sse mbIer Use r 's Guide
Table 7.1
TINY
SMALL
MEDIUM
COMPACT
LARGE
HUGE
TCHUGE
TPASCAL
FLAT
Then, you can select the proper model to group the segments together. If you keep these
segments separate and your program grows, you can choose a larger model.
The memory model is the only required parameter of the MODEL directive. Table 7.1
describes each of the standard memory models. •
The modeCmodifier field lets you change certain aspects of the model. You can specify
more than one model modifier, if you wish. Table 7.2 shows the available model
modifiers.
Note that you can specify the model modifier in two places, for compatibility with
MASM 5.2. If you don't use a model specifier, Turbo Assembler as.sumes the
NEARSTACK modifier, and USE32 (if the 80386 or 80486 processor is selected). Unless
otherwise specified, DOS is the platform.
Use the optional code_segment_name field in the large code models to override the
default name of the code segment. Normally, this is the module name with _TEXT
appended to it.
Standard memory models
near near cs=dgroup All code and data combined into a single group called
ds=ss=dgroup DGROUP. This model is used for .COM assembly programs.
Some languages don't support this model.
near near cs= text Code is in a single segment. All data is combined into a
ds=ss=dgroup group called DGROUP. This is the most common model for
stand-alone assembly programs.
far near cs=<module> text Code uses multiple segments, one per module. Data is in a
ds=ss=dgroup group called DGROUP.
near far cs= text Code is in a single segment. All near data is in a group called
ds=ss=dgroup DGROUP. Far pointers are used to reference data.
far far cs=<module> text Code uses multiple segments, one per module. All near data
ds=ss=dgroup is in a group called DGROUP. Far pointers are used to
reference data.
far far cs=<module> text Same as LARGE model, as far as Turbo Assembler is
ds=ss=dgroup concerned.
far far cs=<module>_text This is the same as the LARGE model, but with different
ds=nothing segment register assumptions.
ss=nothing
near far cs=code This is a model to support early versions of Borland Pascal.
ds=data It's not required for later versions.
ss=nothing
near near cs= text This is.the same as the SMALL model, but tailored for use
ds=ss=flat under 32-bit flat memory models (Win32 and OS/2).
Chapter 7, Using program models and segmentation 83
Table 7.2 Model modifiers
NEARSTACK Indicates that the stack segment should be included in DGROUP (if DGROUP is
• present), and SS should point to DGROUP.
FARSTACK Specifies that the stack segment should never be included in DGROUP, and SS
should point to nothing.
USE16 Specifies (when the 80386 or 80486 processor is selected) that 16-bit segments should
be used for all segments in the selected model.
USE32 Indicates (when the 80386 or 80486 processor is selected) that 32-bit segments should
be used for all segments in the selected model. .
DOS, OS_DOS Specifies that the applica~onplatform is DOS.
NT, OS_NT Specifies that the application platform is Win32 (Windows NT or Windows 95).
OS2, OS_OS2 Specifies that the application platform is OS/2~
language and language_modifier together specify the defaultprocedure calling
conventions/and the default style of the prolog and epilog code present in each
procedure. They also control how to publish symbols externally for the linker t use.
Turbo Assembler will automatically generate the procedure entry and exit code that is
proper for procedures using any of the following interfacing conventions: PASCAL, C,
CPP (C++), SYSCALL, STDCALL, BASIC, FORTRAN, PROLOG, and NOLANGUAGE.
If you don't specify a language, Turbo Assembler assumes the default language to be
NOLANGUAGE.
Use language_modifier to specify additional prolog and epilog code when you write
procedures for Windows, or for the Borland Overlay loader. These options are:
NORMAL, WINDOWS, ODDNEAR and ODDFAR. If you don't specify an option,
Turbo Assembler assumes the default to be NORMAL.
Also note that you can override the default language and language modifier when you
define a procedure. See Chapter 10 for further details.
You can additionally override the default language when you publish a symbol.
Symbols created by the MODEL directive
When you use the MODEL directive, Turbo Assembler creates and initializes certain
variables to reflect the details of the selected model. These variables can help you write
code that's model independent, through the use of conditional assembly statements. See
Chapter 15 for information about how you can use variables to alter the assembly
process.
The @Model symbol
The @Model symbol contains a representation of the model currently in effect. It is
defined as a text macro with any of the following values:
1 = tiny model is in effect
2 = small or flat
3 = compact
4 = medium
5 =·large
84 Turbo Assembler User's Guide
6 =huge
7 = tchuge
o = tpascal
The @32Bit symbol
The @32Bit symbol contains an indication of whether segments in the currently
specified model are declared as 16 bit or 32 bit. The symbol has a value of 0 ifyou
specified 16-bit segments in the MODEL directive, or 1if you indicated 32-bit segments.
The @CodeSize symbol
The @CodeSize text macro symbol indicates the default size of a code pointer in the
current memory model. It's set to 0 for the memory models that use NEAR code
pointers (TINY, SMALL, FLAT, COMPACT, TPASCAL), and 1 for memory models
that use FAR code pointers (all others).
The @DataSize symbol
The @DataSize text macro symbol indicates the default size of a data pointer in the
current memory model. It's set to 0 for the memory models using NEAR data pointers
(TINY, SMALL, FLAT, MEDIUM), 1 for memory models that use FAR data pointers
(COMPACT, LARGE, TPASCAL), and 2 for models using huge data pointers (HUGE
and TCHUGE).
The @Interface symbol
The @Interface symbol provides information about the language and operating system
selected by the MODEL statement. This text macro contains a number whose bits
represent the following values:
Table 7.3 Model modifiers
0 NOLANGUAGE
1 C
2 SYSCALL
3 STDCALL
4 PASCAL
5 FORTRAN
6 BASIC
7 PROLOG
8 CPP
Bit 7 can have a value of 0 for DOS/Windows, or 1 for 32-bit flat models (Windows 95,
Windows NT, or OS/2).
For example, the value 01h for @Interface shows that you selected the DOS operating
system and the C language.
Chapter 7, Using program models and segmentation 85
Simplified segment directives
Once you select a memory model, you can use simplified segment directives to begin
the individual segments. You can only lise these segmentation directives after a
MODEL directive specifies the memory model for the module. Place as many
segmentation directives as you want in a module; Turbo Assembler combines all the
pieces with the same name to produce one segment (exactly as if you had entered all the
pieces at once after a single segmentation directive). Table 7.4 contains alist of these
directives.
Table 7.4 Simplified segment directives
CODESEG [name]
.CODE [name]
DATASEG
.DATA
CONST
.CONST
UDATASEG
.DATA?
STACK [size]
.STACK [size]
FARDATA {name]
.FARDATA [name]
UFARDATA [name]
.FARDATA? [name]
Begins or continues the module's code segment. For models whose code is FAR,
you can specify a name thatis the actual name of the segment. Note that you can
generatemore than one code segment per module in this way.
Same as CODESEG. MASM mode only.
Begins or continu~s the module's NEAR or default initialized data segment.
Same as DATASEG. MASM mode only.
Begins or continues a module's constant data segment. Constant data is always
NEAR and is equivalent to initialized data.
Same as CONST. MASM mode only.
Begins or continues a module's NEAR or default uninitialized data segment. Be
careful to include only uninitialized data in this segment or the resulting
executable program will be larger than necessary. See Chapter 12 for a description
of how to allocate uninitialized data.
Same as UDATASEG. MASM mode only.
Begins or continues a module's stack segment. The optional size parameter
specifies the amount of stack to reserve, in words. Ifyou don't specify a size, Turbo
Assembler assumes 200h words (lKbytes).
In MASM mode, any labels, code, or data following the STACK statementwill not
be considered part of the stac~ segment. Ideal mode, however, reserves the
specified space, and leaves the stack segment open so that you can add labels or
other uninitialized data.
You usually only need to use the stack directive if you are writing a stand-alone
assembly language program; most high-level languages will create a stack for you.
Same as STACK. MASM mode only.
Begins or continuesa FAR initialized data segment of the specified name. If you
don't specify a name, Turbo Assembler uses the segment name FAR_DATA. You
can have more than one FAR initialized data segment per module.
Same as FARDATA. MASM mode only.
Begins or continues a FAR uninitialized data segment of the specified name. Ifyou
don't specify a name, Turbo Assembler uses segment name FAR_BSS. You can
have more than one FAR uninitialized data segmentper module.
Same as UFARDATA. MASM mode only.
Note See Appendix A if you need to know the actual names, class names, and alignments of
the segments created with the simplified segmentdirectives.
86 TurboA sse mbIer Use r 's Gu ide
Symbols created by the simplified segment directives
When you use the simplified segment directives, they create variables that reflect the
details of the selected segment, just as the MODEL directive does. See Chapter 15 for
further information. The following table lists these symbols.
Table 7.5 Symbols from simplified segment directives
@code
@data
@fardata
@fardata?
@curseg
@stack
the segment or group that C5 is assumed to be
the segment or group that D5 is assumed to be
the current FARDATA segment name
the current UFARDATA segment name
the current segment name
the segment or group that 55 is assumed to be
The STARTUPCODE directive
The STARTUPCODE directive provides initialization code appropriate for the current
model and operating system. It also marks the beginning of the program. Here's its
syntax:
STARTUPCODE
or
.STARTUP; (MASM mode only)
STARTUPCODE initializes the DS, SS, and SP registers. For the SMALL, MEDIUM,
COMPACT, LARGE, HUGE, and TPASCAL models, Turbo Assemblersets DS and SS
to @data, and SP to the end of the stack. For TINY and TCHUGE models, the
STARTUPCODE directive doesn't change the segment registers.
The @Startup symbol
The @Startup sytpbol is placed at the beginning of the startup code that the
STARTUPCODE directive generates. It is a near label marking the start of the program.
The EXITCODE directive
You can use the EXITCODE directive to produce termination code appropriate for the
current operating system. You can use it more than once in a module, for each desired
exit point. Here's its syntax:
EXITCODE [return_value_exprJ
You can use the following syntax only in MASM mode:
.EXIT [return_value_exprJ
Th; optional return_value_expr describes the number to be returned to the operating
system. If you don't specify a return value, T~rbo Assembler assumes the value in the
AX register.
. Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 87
Defining generic segments and groups
Most applications can use segments created using the standard models. These standard
models, however, are limited in their flexibility. Some applications require full control
over all aspects of segment generation; generic segment directives provide this
flexibility.
The SEGMENT directive
The SEGMENT directive opens a segment. All code or data following it will be included
in the segment, until a corresponding ENDS directive closes the segment.
The Ideal mode syntax for the SEGMENT directive is:
SEGMENT name [attributes]
You can use the following syntax for MASM mode:
name SEGMENT [attributes]
name is the name of the segment. You should name segments according to their usages.
See Appendix Bfor examples of segment names.
You can open and close a segment of the same name many times in a single module. In
this case, Turbo Assembler concatenates together the sections of the segment in the
order it finds them. You only need to specify the attributes for the segment the first time
you open the segment.
attributes includes any and all desired segment attribute values, for each of the
following:
• segment combination attribute
• segment class attribute
• segment alignment attribute
• segment size attribute
• segment access attribute
Note Turbo Assembler processes attribute values from left to right.
Segment combination attribute
The segment combination attribute tells the linker how to combine segments from
different modules thathave the same name. The following table lists the legal values of
the segment combination attribute. Note tp.at if you don't specify the combine type,
Turbo Assembler assumes PRIVATE.
Table 7.6 Segment combination attribute
PRIVATE Segment will not be combined with any other segments of the same name outsideof
this module.
PUBLIC Segment will be concatenated with other segments of the same name outside of this
module to form a single contiguous segment.
88 Turbo Assembler User's Guide
Table 7.6 Segment combination attribute (continued)
MEMORY Same as PUBLIC. Segment will be concatenated with other segments of the same
name outside this module to form a single contiguous segment, used as the default
stack. The linker initializes values for the initial SS and SP registers so that they point
to the end of these segments.
COMMON Locates this segment and all other segments with the same name at the same address.
All segments of this name overlap shared memory. The length of the resulting
common segment is the length of the longest segment from a single module.
VIRTUAL Defines a special kind of segment that must be declared inside an enclosing segment.
The linker treats it as a common area and attaches it to the enclosing segment. The
virtual segment inherits its attributes from the enclosing segment. The assume
directive considers a virtual segment to be a part of its parent segment; in all other
ways, a virtual segment is a common area that is combined across modules. This
permits the sharing of static data that comes into many modules from included files.
AT xxx Locates the segment at the absolute paragraph address that the expression xxx
specifies. The linker doesn't emit any data or code for AT segments. Use AT to allow
symbolic access to fixed memory locations, such as the display screen or ROM areas.
UNINIT Produces a warning message to let you know that you have inadvertently written
initialized data to uninitialized data segments. For example, you can specify the
following to produce a warning message: BSS SEGMENT PUBLIC WORD UNINIT I BSS I •
To disable this warning message, use the NOWARN UNI directive. You can reenable
the message by using the WARN UNI directive.
Segment class attribute
The segment class attribute is a quoted string that helps the linker determine the proper
ordering of segments when it puts together a program from modules. The linker groups
together all segments with the same class name in memory. A typical use of the class
name is to group all the code segments of a program together (usually the class CODE is
used for this). Data and uninitialized data are also grouped using the class mechanism.
Segment alignment attribute
The segment alignment attribute tells the linker to ensure that a segment begins on a
specified boundary. This is important because data can be loaded faster on the 80x86
processors if it's properly aligned. The following table lists legal values for this attribute.
Table 7.7
BYTE
WORD
DWORD
PARA
PAGE
MEMPAGE
Segment alignment attribute
No special alignment; start segment on the next available byte.
Start segment on the next word-aligned address.
Start segment on the next doubleword-aligned address.
Start segment on the next paragraph (16-byte aligned) address.
Start segment on the next page (256-byte aligned) address.
Start segment on the next memory page (4Kb aligned) address.
Turbo Assembler assumes the PARA alignment if you don't specify the alignment type.
Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 89
Segment size attribute
If the currently selected processor is the 80386, segments can be either 16 bit or 32 bit.
The segnlent size attribute tells the linker which of these you want for a specific
segment. The following table contains the legal attribute values.
.Table 7.8 Segment size attribute values
USE16
USE32
Segment is 16 bit. A 16-bit segment can contain up to 64K of code and/or data.
Segment is 32 bit. A 32-bit segment can contain up to 4 gigabytes ofcode and/or data.
Turbo Assembler assumes the USE32 value if you selected the 80386 processor in
MASM mode. In Ideal mode, Turbo Assembler assumes USE16 by default.
Segment access attribute
For any segment in protected mode, you can control access so that certain kinds of
memory operations are not permitted. (Note that this feature is currently supported
only by the Phar Lap linker. You must generate object code compatible with it using the
lop switch if you want to be able to use the segment access attribute.) The segment
access attribute tells the linker to apply specific access restrictions to a segment.
The following table lists the legal values for this attribute.
Table 7.9 Segment access attribute
EXECONLY the segment is executable only
EXECREAD the segment is readable and executable
REAOONLY the segment is readable only
READWRITE the segment is readable and writable
The Phar Lap linker assumes that the segment is meant to run in protected mode if you
select any of these attributes, or if you select the USE32 attribute.·Turbo Assembler
assumes the READONLY attribute if you selected the USE32 attribute but did not
specify any of these four attributes.
The ENDS directive
You can use the ENDS directive to close a segment so that no further data is emitted
into it. You should use the ENDS directive to close any segments opened with the
SEGMENT directive. Segments openedusing the simplified segment directives don't
require the ENDS directive.
Here's the syntax of the ENDS directive:
ENDS [name]
For MASM mode only, you can use the following syntax:
name ENDS
90 TurboA sse mbIer Use r's Guide .
name specifies the name of the segment to be closed. Turbo Assembler will report an
error message if name doesn't agree with the segment currently open. If you don't
specify a name, Turbo Assembler assumes the currently-open segment.
The GROUP directive
You can use the GROUP directive to assign segments to groups. A group lets you
specify a single segment value to access data in all segments in the group.
Here's the.Jdeal mode syntax for the GROUP directive:
GROUP name segment_name [I segment_name... J
You can use the following syntax for MASM mode:
name GROUP segment_name [I segment_name. .. J
name is the name of the group. segment_name is the name of a segment you want to
assign to that group.
The ASSUME directive
A segment register must be loaded with the correct segment value for you to access data
in a segment. Often, you must do this yourself. For example, you could use the
following code to load the address of the current far data segment into DS:
MOV AX/@fardata
MOV DS/AX
When a program loads a segment value into a segment register, you use that segment
register to access data in the segment. It rapidly becomes tiring (and is also poor
programming practice) to specify a specific segment register every time you process
data in memory.
Note Use the ASSUME directiveto tell Turbo Assembler to associate a segment register with
a segment or-group name. This allows Turbo Assembler to be "smartenough" to use the
correct segment registers when data is accessed.
In fact, Turbo Assembler uses the information about the association between the
segment registers and group or segment names for another purpose as well: in MASM
mode, the value tllat the CS register is ASSUMEd to be is used to determine the
segment or group a label belongs to. Thus, the CS register must be correctly specified in
an ASSUME directive, or Turbo Assembler will report errors every time you define a
label or procedure.
Here's the syntax of the ASSUME directive:
ASSUME segreg : expression [/segreg : expression J
or
ASSUME NOTHING
segreg is one ofCS, DS, ES or SS registers. Ifyou specify the 80386 or 80486 processor,
you can also use the FS and GS segment registers. expression can be any expression that
Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 91
evaluates to a group or segment name. Alternatively, it can be the keyword NOTHING.
The NOTHING keyword cancels the association between the designated segment
register and anysegment or group name.
ASSUME NOTHING removes associations between all segment registers and segment
or group names.
You can use the ASSUME directive whenever you modify asegment register, or at the
start of a procedure to specify the assumptions at that point. In practice, ASSUMEs are
usually used at the beginning of a module and occasionally within it. If you use the
MODEL statement, Turbo Assembler automatically sets up the initial ASSUMEs for.
you.
If you don't specify asegment in an ASSUME directive, its ASSUMEd value isnot
changed.
For example, the following code shows how you can load the current initialized far data
segment into the DS register, access memory in that segment, and restore the DS register
to the data segment value.
MOV AX,@fardata
MOV DS,AX
ASSUME DS:@fardata
MOV BX,<far_data_variable>
MOV AX,@data
MOV DS,AX
ASSUME DS:@data
Segment ordering
The linker arranges and locates all segments defined in a program's modules. Generally,
the linker starts with the order in which it encounters the segments in a program's
modules. You can alter this order using mechanisms such as segment combination and
segment classing.
There are other ways to affect the way the linker arranges segments in the final
program. For example, the order in which seglnents appear in a module's source can be
changed. There are also directives that affect segment ordering. Descriptions of these
follow.
Changing amodule's segment ordering
The order of segments in each module determines the startingpoint for the linker's
location of segments in the program. In MASM 1.0,2.0, and 3.0, segments were passed
to the linker in alphabetical order. Turbo Assembler provides directives (in MASM
mode only) that let you reproduce this behavior.
Note that these directives have the same function as thelA and /S command line
switches. See Chapter 2 for further details.
92 TurboA sse mbIer Use r's Guide
The .ALPHA directive
The .ALPHA directive specifies alphabetic segment ordering. This directive tells Turbo
Assembler to place segments in the object file in alphabetical order (according to the
segment name). Its syntaxis
.ALPHA
The .SEQ directive
The .SEQ directive specifies sequential segment ordering, and tells Turbo Assembler to
place segments in the object file in the order in which they were encountered in the
source file. Since this is the default behavior of the assembler, you should usually use
the .SEQ directive only to override a previous .ALPHA or a command line switch.
Here's the syntax of .SEQ:
.SEQ
DOS ordering of segments: the DOSSEG directive
Normally, the linker arranges segments in the sequential order it encounters them
during the generation of the program. When you include a DOSSEG directive in any
module in a program, it instructs the linker to use standard DOS segment ordering
instead. The linker defines this convention to mean the following arrangement of
segments in the final program:
• segments having the class name CODE (typically code segments)
• segments that do not have class name CODE and are not part of DGROUP
• segments that are part of DGROUP in the following order:
1 segments not of class BSS or STACK (typically initialized data)
2 segments of class BSS (typically uninitialized data)
3 segments of class STACK (stack space)
Thesegments within DGROUP are located in the order in which they were defined in
the source modules.
Note DOSSEG is included in TASM for backward compatibility only. It is recommended that
you do not use the DOSSEG directive in new assembly programs. In addition, do not
use the DOSSEG directive if you're interfacing assembly programs with C programs.
Changing the size of the stack
A procedure's prolog and epilog code manipulates registers that point into the stack. On
the 80386 or 80486 processor, the stack segment canbe either 16 bits or 32 bits. Turbo
Assembler therefore must know the correct size of the stack before it can generate
correct prolog and epilog code for a procedure.
The stacksize is automatically selected if you selected a standard model using the
MODEL statement.
Chap t er 7, Usin 9 pro 9ram mod eIsan d se9in entat ion 93
Turbo Assembler provides directives that can set or override the default stack size for
procedure prolog and epilog generation. The following table lists these directives.
Table 7.10 Stack size modification directives
SMALLSTACK
LARGESTACK
Indicates that the stack is 16 bit
Indicates that the stack is 32 bit
94 Tur boA sse mbIe r Use r' s Guide
Defining data types
Defining data types symbolically helps you write modular code. You can easily change
or extend data structures without having to rewrite code by separating the definition of
a data type from the code that uses it, and allowing symbolic access to the data type and
its components.
Turbo Assembler supports as many or more symbolic data types than most high-level
languages. This chapter describes how to define various kinds of data types.
Defining enumerated data types
An enumerated data type represents a collection of values that canbe stored in a certain
number of bits. The maximum value stored determines the actual number of bits
required.
Here is the Ideal mode syntax for declaring an enumerated data type:
ENUM name [enum_var [,enum_var... ]]
You can use the following syntax in MASM mode:
name ENUM [enum_var [, enum_var. .. ]]
The syntax of each enum_var is:
var_name [=valuel
Turbo Assembler will assign a value equal to that of the last variable in the list plus one
if value isn't present when you assign the specified value to the variable var_name.
Values can't be relative or forward referenced. Variables that ENUM created are
redefinable numeric variables of global scope.
Warning! If you use the same variable name in two enumerated data types, the first value of the
variable will be lost, and errors could result.
Chap ter 8, Defin i n9 data, types 95
name is the name of the ENUM data type. You can use it later in the module to obtain a
variety of information about the values assigned to the variables detailed. See Chapter 5
for information about using enumeration data type names in Turbo Assembler
expressions.
Note You can also use enumerated data type names to create variables and allocate memory:
See Chapter 12 for details.
Enumerated data types are redefinable. You can define the same name as an
enumerated data type more than once in a module.
Turbo Assembler provides a multiline syntax for enumerated data type definitions
requiring a large number of variables. The symbol {starts the multiline definition, and
the symbol} stops it.
The Ideal mode syntax follows:
ENUM name [enum_var [,enum_var . .. ]]
[enum_var [, enum_var] ... ]
[enum_var [, enum_var] ... ]
You can use the following syntax in MASM mode:
name ENUM [enum_var [,enum_var . .. ]]
[enum_var [, enum_var] ... ]
[enum_var [, enum_var] ... ]
For example, all of the following enumerated data type definitions are equivalent:
foo ENUM fl l f2 l f3 ,f4
foo ENUM {
fl
f2
f3
f4
}
foo ENUM fl, f2, {
f3 I f4}
;Original version
;Multiline version
;More compact multiline version
Note Turbo Assembler doesn't recognize any pseudo ops inside the multiline enumerated
. data type definition.
Defining bit·field records
A record data type represents a collection ofbit fields. Each bit field has a specific width
(in bits) and an initial value. The record data type width is the sum of the widths of all
the fields. .
You can use record data types to compress data into a form that's as compact as
possible. For example, you can represent a group of 16 flags (which can be either ON or
96 Turbo Ass embIer Use r's Guide
OFF) as 16 individual bytes, 16individualwords, or as a record containing 16 1-bit fields
(the efficient method).
Here's the Ideal mode syntax for declaring a record data type:
RECORD name [recjield [, recjield. .. JJ
The MASM mode syntax is:
name RECORD [rec_field [,rec_field .. .JJ
Each reeJield has the following syntax:
field_name: width_expression [~valueJ
field_name is the name of a record field. Turbo Assembler will allocate a bit field of the
width width_expression for it. value describes the initial value of the field (the default
value used when an instance of the record is created). Values and width expressions
can't be relative or forward referenced. Record field names are global in scope and can't
be redefined.
name is the name of the record data type. You can use it later in the module to obtain a
variety of information about the record data type. You can also use the names of
individual record fields to obtain information. See Chapter 5 for details about how to
obtain information from record data type names and record field names using Turbo
Assembler expressions.
You can redefine record data types, and define the same name as a record data type
more than once in a module.
Note You can also use record data type names to create variables and allocate memory. See
Chapter 12 for details.
Turbo Assembler provides special support for record fields that represent flags and
enumerated data types. Additional and extended instructions provide efficient access to
record fields. Chapter 13 describes this concept further.
For record data type definitions requiring a large number of fields, Turbo Assembler
provides a multiline syntax similar to that for enumerated data types.
For example, all of the following record data type definitions are equivalent:
faa RECORD f1:1,f2:2,f3:3,f4:4
faa RECORD {
f1: 1
f2:2
f3: 3
f4:4
}
faa .RECORD £1:l,f2:2, {
f3:3,f4:4}
iOriginal version
;Multiline version
;More compact multiline version
Note Turbo Assembler does not recognize any pseudo ops inside the multiline record data
type definition.
Chap t er 8, Defin i n9 datat ypes 97
Defining structures and unions

Structures and unions let you mix and match various types. A structure in Turbo
Assembler is a data type that contains one or more data elements called members.
Structures differ from records because structure members, are always an integral
number of bytes, while records describe the breakdown of bit fields within bytes.
The size 01 a structure is the combined size of all data elements within it.
Unions are similar to structures, except that all of the members in a union occupythe
same memory. The sizeof a unionis the size of its largest member. Unions are useful
when a block of memory must represent one of several distinct possibilities, each with
different data storage requirements.
Turbo Assembler lets you fully nest structures and unions within one another, but this
can become complicated. For example, you could have a structure member that is really
a union. A union could also have a full structure as each member.
Opening astructure or union definition
Use the following Ideal mode syntaxes to open a structure or union dat~ type definition:
STRUC name or UNION name
You Cqn use the following MASM mode syntaxes to do the same thing:
name STRUC or name UNION
name is the name of the structure or union data type.
Turbo Assembler considers all data or code emitted between the time a structure or
union data type definition is opened and the time a corresponding ENDS directive is
encountered to be part of that structure or union data type.
Turbo Assembler treats structure and union data type names as global but redefinable.
You can define the same name as a structure or union data type more than once in a
module.
Specifying structure and union members
Turbo Assembler includes data one line at a time in structures or unions. To allocate
data and create members in a structure or union definition, use the same directives as
those for allocating data and creating labels in an open segment. For example,
memberl DW 1
is equally valid in a segment or in a structure definition. In a segment, this statement
means "reserve a word of value 1, whose name is memberl." Irl'a structureo! union
definition, this statement means "reserve a word of initial value 1, whose member name
is memberl." ,
You can use the initial value for a structure member if an instance of the structure or
union is allocated in a segmentor a structure. If you don't intend to allocate structure
98 TurboA sse mbIer Use r's Guide
instances this way, the initial value of the structure member is not important. You can
use the data value? (the uninitialized data symbol) to indicate this.
Turbo Assembler allows all methods of allocating data with a structure definition,
including instances of other structures, unions, records, enumerated data types, tables,
and objects. For more information on how to allocate data, see Chapter 12. '
MASM and Ideal modes treat structure member names differently. In MASM mode,
structure member names are global and can't be redefined. In Ideal mode, structure
member names are considered local toa structure or union data type.
Defining structure member labels with LABEL
The LABEL directive lets you create structure members without allocating data.
Normally, the LABEL directive establishes a named label or marker at the point it's
encountered in a segment. LABEL directives found inside structure definitions define
members of the structure. Here's the syntax of the LAB,EL directive:
LABEL name complex_type
In MASM mode only, you can use the following syntax:
name LABEL complex_type
name is the name of the structure member. type is the desired type for the structure
member. It can be any legal type name. See Chapter 5 for a description of the available
type specifiers.
Aligning structure members
You can use the ALIGN directive within structure definitions to align structures
members on appropriate boundaries. For example,
iDWORD alignment
ALIGN 4
member dd? imember will be DWORD aligned
Closing astructure or union definition
You must close the structure or union definition after you define all structure or union
members. Use the ENDS directive to do this.
ENDS has the following syntax in Ideal mode:
ENDS [name]
In MASM mode, you can use the following syntax:
name ENDS
name, ifpresent, is the name of the currently open structure or union data type
definition..If name is not present, the currently open structure or union will be closed.
You can also use the ENDS directive to close segments. This isnot a conflict, because
you can't open a segment inside a structure or union definition.
Chapter 8, Defining data types 99
Nesting structures and unions
Turbo Assembler lets you nest the STRUC, UNION, and ENDS directives inside open
structure and union data type definitions to control the offsets assigned to structure
members. .
In a structure, each data element begins where the previous one ended. In a union, each
data elementbegins at the same offset as the previous data element. Allowing a single
data element to consist of an entire union or structure provides enormous flexibility and
power. The following table contains descriptions of STRUC, UNION, and ENDS.
Table 8.1
STRUC
UNION
ENDS
STRUC, UNION, and ENDS directives
Used inside an open structure or union, this directive begins a block of elements that
the enclosing structure or union considers a single member. The members in the block
are assigned offsets in ascending order. The size of the block is thesurn. of the sizes of
all of the members in it.
Used inside an open structure or union, this begins a block of members that the
enclosing structure or union considers a single unit. The members in the block are all
assigned the same offset. The size of the block is the-size of the largest member in it.
Terminates a block of members started with a previous STRUC or UNION directive.
For example, the composite has five members in the following structure/union data
definition:
CUNION STRUC
CTYPE DB ?
UNION iStart of union
i If CTYPE=O I use this ...
STRUC
CTOPARl DW 1
CTOPAR2 DB 2
ENDS
i If CTYPE=l, use this ...
STRUC
CTIPARl DB 3
CTIPAR2 DD 4
ENDS
ENDS iEnd of union
ENDS iEnd of structure data type
The following table lists these members:
100· TurboA sse mbIe r Use r' s Guide
Table 8.2 Block members
CTYPE Byte 0
CTOPARl Word 1
CTOPAR2 Byte 3
CTIPARl Byte 1
CTIPAR2 Dword 2
The length of this structure/ union is 6 bytes.
Including one named structure within another
? (uninitialized)
1
2
3
4
Turbo Assembler provides a way of incorporating an entire existing structure or union
data type, including member names, into an open structure definition to assist in the
inheritance of objects. It treats the incorporated structure or union as if it were nested
inside the open structure or union definition at that point. In this way, incorporating a
structure or union into another is intrinsically different from including an instance of a
structure or union in another; an instance includes only initialized or uninitialized data,
while incorporation includes data, structure, and member names.
Here's the Ideal mode syntax:
STRUC struc_name fill_parameters
You can use the following syntax in MASM mode:
struc_name STRUC fill_parameters
Use a statement of this form only inside a structure or union definition. struc_name is the
name of the previously defined structure or union that is to be included. fill-parameters
represents the changes you want to make to the initial (default) values of the included
structure's members. A ? keyword indicates that all of the incorporated structure's
members should be considered uninitialized. Otherwise, the syntax for the
fi1tparameters field is:
{ [member_name [=expression] [,member_name [=expression] ... ]] }
member_name is the name of any member of the included structure whose initial value
should be changed when it's included. expression is the value you want to change it to. If
you have expression, then the initial value for that member of the structure will be
unchanged when it is included. If you specify a ? keyword for the expression field, that
member's initial value will be recorded as uninitialized when it's included.
Since structure member names are global in MASM mode, they are not redefined when
you copy a structure. Thus, including a structure within another is most useful in
MASM mode when you do it at the beginn}ng of the structure or union you're defining.
Usually, when you create an instance of a union, you would have to make sure that only
one of the union's members contains initialized data. (See Chapter 12 for details.) Since
incorporating a structure in another does not involve creating an instance, this
restriction does not apply. More than one member of an included union can contain
initialized data. For example,
Chapter 8, Defining data types 101
FOO STRUC
ABC DW 1
DEF DW 2
UNION
Al DB '123'
A2 DW ?
ENDS
ENDS
F002 STRUC
FOO STRUC {Al=2} iIncorporates struc FOO into struc F002', with override
iNote that both Al and A2 are initialized by
idefault in F002!
GHI DB 3
ENDS
The definition of structure F002 in the previous example is equivalent to the following
nested structure/union:
F002 STRUC
STRUC iBeginning of nested structure ...
ABC DW 1
DEF DW 2
UNION iBeginning of doubly nested union ...
Al DB '123'
A2 DW 2
ENDS
ENDS
GHI DB 3
ENDS
iEnd of doubly nested union ...
iEnd of nested structure ...
Note that when an instance of the structure F002 is made, be sure that only one value
in the union is initialized.
Using structure names in expressions
Once you define a, structure or union, information about the structure or union is
available in many ways. You can use both the structure or union data type name and a
structure member name to obtain information using Turbo Assembler expressions. See
Chapter 5 for further information.
Defining tables
A table data type represents a collection of table members. Each member has a specific
size (in bytes) and an initial value. A table member can be either virtual or static. A virtual
member of a table is assigned an offset within the table data type; space is reserved for it
in any instance of the table. A static member does not have an offset; space isn't reserved
for it in an instance ofthe table.
The size of the table data type as a whole is the sum of the sizes of all of the virtual
members. .
102 Turbo Assembler User's Guide
Table data types represent method tables, used in object-oriented programming. An
object usually has a number of methods associated with it, which are pointers to
procedures that manipulate instances of the object. Method procedures can either be
called directly (static methods) or indirectly, using a table of method procedure pointers
(virtual methods).
You can use the following Ideal mode syntax for declaring a table data type:
TABLE name [table_member [,table_member.. .]]
The following syntax works only in MASM mode:
name TABLE [table_member [,table_member.. .]]
Here's the syntax of each table_member field:
or
[VIRTUAL] member_name [[countl_expression]]
[: complex_type [:count2_expression]] [= expression]
table_name is the name of an existing table data type whose members are incorporated
entirely in the table you define. Use this syntax wherever you want inheritance to occur.
member_name is the name of the table member. The optional VIRTUAL keyword
indicates that the member is virtual and should be assigned to a table offset.
complex_type can be any legal complex type expression. See Chapter 5 for a detailed
description of the valid type expressions.
If you don't specify a complex_type field, Turbo Assembler assumes it to be WORD
(DWORD is assumed if the currently selected model is a 32-bit model).
count2_expression specifies how many items of this type the table member defines. A
table member definition of
faa TABLE VIRTUAL tmp:DWORD:4
defines a table member called tmp, consisting of four doublewords.
The default value for count2_expression is 1 if you don't specify it. countl_expression
is an array element size multiplier. The total space reserved for the member is
count2_expression times the length specified by the memtype field, times
countl_expression. The default value for countl_expression is also 1 if you don't specify
one. countl:....expression multiplied by count2_expression specifies the total count of the
table member.
Note Table member names are local to a table in Ideal mode, but are global in scope in MASM
mode.
name is the name of the table data type. You can use it later in the module to get a variety
of information about the table data type. You can also use the names of individual table
members to obtain information. See Chapter 5 for further information.
Table data types are redefinable. You can define the same name as a table data type
more than once in a module.
Chap ter 8, De fin i n9 datat ypes 103
You can also use table data type names to create variables and allocate memory. See
Chapter 12 for details.
Alternatively, Turbo Assembler provides a multiline syntax for table data type
definitions requiringa large number of members. This syntax is similar to that of
enumerated data type definitions. Here's an example:
foo TABLE tl:WORD,t2:WORD,t3:WORD,t4:WORD
foo TABLE {
iOriginal version
iMultiline version
t1 :WORD
t2:WORD
t3 :WORD
t4:WORD
}
foo TABLE tl:WORD,t2:WORD,{
t3:WORD,t4:WORD}
iMore compact multiline version
Overriding table members
If you declare two or more members of the same name as part of the same table data
type, Turbo Assembler will check to be sure that their types and sizes agree. If they
don't, it will generate an error. Turbo Assembler will use the last initial value occurring
in the table definition for the member. In this way, you can override the initial value of a
table after it is incorporated into another. For example,
FOO TABLE VIRTUAL MEM1:WORD=MEM1PROC, VIRTUAL MEM2:WORD=MEM2PROC
F002 TABLE FOO, VIRTUAL MEM1:WORD=MEM3PROC iOverrides inherited iMEMl
Defining anamed type
Named types represent simple or complex types. You can use the TYPEDEF directive to
define named types. Here's the Ideal mode syntax:
TYPEDEF type_name complex_type
The MASM mode syntax is:
type_name TYPEDEF complex_type
complex_type describes any type or multiple levels of pointer indirection. See Chapter 5
for further information about complex types. type_name is the name of the specified
type.
. When you use a named type in an expression, it functions as if it were a simple type of
the appropriate size. For example,
MOV aX,wordptr [bx]
foo TYPEDEF near ptr byte
MOV ax,foo ptr [bx]
104 TurboA sse mbIer Use rJ s Guide
i8imple statementi
iFOO is basically a word
iSO this works too
Defining aprocedure type
For Turbo Assembler version 3.2 or higher, you can use a user-defined data type (called
a procedure type) to describe the arguments and calling conventions of a procedure.
Turbo Assembler treats procedure types like any other types; you can use it wherever
types are allowed. Note that since procedure types don't allocate data, you can't create
an instance of a procedure type.
Use the PROCTYPE directive to create a procedure type. Here is the Ideal mode syntax:
PROCTYPE name [procedure_description]
The MASM mode syntax is:
name PROCTYPE [procedure_description]
procedure_description is similar to the language and argument specification for the PROC
directive. Its syntax is:
[[language_modifier] language] [distance] [argument_list]
specify language_modifier, language, and distance exactly the same way you would for the
corresponding fields in the PROC directive. For more information about the PROC
directive, see Chapter 10.
Use the following form for argument_list:
argument [,argument] ...
An individual argument has the following syntax:'
[argname] [[countl_expression]] :complex_type [:count2_expression]
complex_type is the data type of the argument. It can be either a simple type or a pointer
expression.
Note See Chapter 5 for a discussion of the syntax of complex types.
count2_expression specifies how many items of this type the argument defines. Its default
value is 1, except for BYTE arguments. Those arguments have a default count of 2, since
you can't PUSH a byte value onto the 80x86 stack.
In procedure types whose calling convention permits variable-length arguments (like
C), count2_expression (for the last argument) can be the special keyword ?, which
indicates that the procedure caller will determine the size of the array. The type
UNKNOWN also indicates a variable-length parameter.
The name of each argument is optional, but complex_type is required because procedure
types are used mainly for type checking purposes. The names of the arguments don't
have to agree, but the types must.
Defining an object
~~ An object consists of both a data structure and a list of methods that correspond to the
CQ)~ object. Turbo Assembler uses a structure data type to represent the data structure
Chap t er 8, 0 el i ni n9 datat ypes 105
associated with an object, and a table data type to represent the list of methods
associated with an object. .
An extension to the STRUC directive lets you define objects. The Ideal mode syntax
follows:
STRUC name [modi fi ersl [parent_namel [METHOD
[table_membeF [,·table_member. .·.lll
structure_members.
ENDS [namel
You can use the following syntax in MASM mode:
name STRUC [modifiersl [parent_name] [METHOD
[table_member 1 [,table_member.. .ll]
structure_members
[name] ENDS'
name is the name of the object. parent_name is the optional name of the parent object.
(Turbo Assembler explicitly supports only single inheritance.) The parent object's
structure data will automatically be included in the new object's structure data, and the
parentobject's table of methods will be included in the new object's table of methods as
well.
Each table_member field describes a method name and method procedure associated
with the object. The syntax of a table_member field is exactly the same as in a table
definition.
. structure_members describe any additional structure members you want within the
object's data structure. These are formatted exactly the same as in an open structure
definition.
The optional modifiers field can be one or more of the following keywords:
TableS.3
GLOBAL
NEAR
FAR
Available modifiers
Causes the address of the virtual method table (if any) to be published globally.
Forces the virtual table pointer (if any) to be an offset quantity, either 16 or 32 bits,
depending on whether the current model is USE16 or USE32.
Forces the virtual table pointer (ifany) to be a segment and offset quantity, either 32 or
48 bits, depending on whether the current model is USE16 or USE32. .
The size of the virtual table pointer (if any) depends on whether data in the current
model is addressed as.NEAR or FAR if you don't specify a modifier.
The TBLPTR directive
Inherent in the idea of objects is the concept of the virtual method table. An instance of
this table exists once for any object having virtual methods. The data structure for any
object having virtual methods also must contain a pointer to the virtual method table for·
that object. Turbo Assembler,automatically provides a virtual method table pointer in
106 Turbo Assembler User'.s Guide
an object's data structure (if required) and if you don't specify it explicitly using the
TBLPTR directive.
You should use the TBLPTR directive within an object data structure definition.
TBLPTR lets you explicitly locate the virtual table pointer wherever you want. Here's
its syntax:
TBLPTR
The size of the pointer that TBLPTR reserves is determined by whether the current
model is USE16 or USE32, and what modifiers you used in the object definition.
Symbols defined by the extended STRUC directive
The extended STRUC directive defines or uses several symbols, which reflect the object
being defined. The following table shows these symbols.
Table 8.4 Symbols used or defined by STRUC
Syilil101,····'·.·M¢c#rln8.··
OObject
@Table_<object_name>
@Tableaddr_<object_name>
A text macro containing the name of the current object
A table data type containing the object's method table
A label describing the address of the object's virtual method table
Chap t er 8, De fin i n9 datat y pes 107
108 Turbo Assembler User's Guide
Setting and using the
location counter
The location counter keeps track of the current address as your source files assemble.
This lets you know where you are at any time during assembly of your program. Turbo
Assembler supplies directives that let you manipulate the location counter to move it to
a desired address.
Labels are the names used for referring to addresses within a program. Labels are
assigned the value of the location counter at the time they are defined. Labels let you
give names to memory variables and the locations of particular instructions.
This chapter discusses the available directives for manipulating the location counter,
and declaring labels at the current location counter.
The $location counter symbol
The predefined symbol $ represents the current location counter. The location counter
consists of two parts: a segment, and an offset. The location counter is the current offset
within the current segment during ~ssembly.
The location counter is an address that is incremented to reflect the current address as
each statement in the source file is assembled. As an example,
helpMessage
helpLength
DB 'This is help for the program.'
= $ - helpMessage
Once these two lines are assembled, the symbol helpLength will equal the length of the
help message.
Chap t er 9, Sett in9 and us in 9 the 10 cat ion co un ter 109
Location counter directives
Turbo Assembler provides several directives for setting the location counter. The next
few sections describe these directives. Note that all of these directives work in both
MASM and Ideal modes.
The ORG directive
You can use the ORG directive to set the location counter in the current segment. ORG
has the following syntax:
ORG expression
expression can't contain any forward-referenced symbol names. It can either be a
constant or an offset from a symbol inthe current segment or from the current location
counter.
You can back up the location counter before data or code that has already been emitted
into a segment. You can use this to go back and fill in table entries whose values weren't
known at the time the table was defined. Be careful when using this technique; you
mightaccidentally overwrite something you didn't intend to.
You can use the ORG directive to connect a label with a specific absolute address. The
ORG directive can also set the starting location for .COM files. Here's an example of
how to use ORG:
This program shows how to create a structure and macro for
declaring instances of the structure, that allows additional
elements to be added to the linked list without regard to
other structures already declared in the list. If the macro
is invoked in a section of code that is between two other
instances of the structure, the new structure will automatically
be inserted in the linked list at that point without your
needing to know the names of the previous or next
structure variables. Similarly, using the macro
at the end of the program easily adds new structures to the
linked list without regard for the name of the previous
element.
The ma~ro also maintains variables that point to the first
and last elements of the linked list.
ideal
p386
model OS_NT flat
codeseg
struc a·
prev dd 0
next dd 0
info db 100 dup (0)
ends a
last a name equ <>
110 TurboA sse mbIer Use r's Guide
i Maintain the offsets of the head and tail of the list.
list_a_head dd 0
list a tail dd 0
macro makea name: req, args
ifidni __last_a_name,<>
i There is no, previous item of this type.
name a <O,O,args>
Setup the head and tail pointers
org list a head
dd name
org list a tail
dd name
i Return to the offset after the structure element
org nametsize a
last a name equ name
else
name a
Declare it, with previous pointing to previous
item of structure a.
<__last_a_name,O,args>
i Make the next pointer of the previous structure
i point to this structure.
org last a name.next
dd name
i Setup the tail pointer for the new member
org list a tail
dd name
i Go back to location after the current structure
org nametsize a
i Set up an equate to remember the name of the
; structure just declared
last a name
endif
equ name
endm
makea first
; Miscellaneous other data
db 5 dup (0)
makea second
; More miscellaneous, data
db 56 dup (0)
Give a string to put in the info element of this structure
makea third,<'Hello'>
end
Chap t er 9, Set tin 9 and us i n9 the 10 cat ion co un ter 111
The EVEN and EVENDATA directives
You can use the EVEN directive to round up the location counter to the next even
address. EVEN lets you align code for efficient access by processors that use a 16-bit
data bus. It does not improve performance for processors that have an 8-bit data bus.
EVENDATA aligns evenly by advancing the location counter without emitting data,
which is useful for uninitialized segments. Both EVEN and EVENDATA will cause
Turbo Assembler to generate a warning message if the current segment's alignment
isn't strict enough.
If the location counter is odd when an EVEN directive appears, Turbo Assembler places
a single byte of a NOP instruction in the segment to make the location counter even. By
padding with a NOP, EVEN can be used in code segments without causing erroneous
instructions to be executed at run time. This directive has no effect if the location is
already even.
Note In code segments, NOPs are emitted. :rndata segments, zeros are emitted.
Similarly, if the location counter is odd when an EVENDATA directive appears, Turbo
Assembler emits an uninitialized byte.
An example of using the EVEN directive follows:
EVEN
@@A: lodsb
xor bl,al ialign for efficient access
loop @@A
Here's an example of using the EVENDATA directive:
EVENDATA
VARl DW ialign for efficient 8086 access
The ALIGN directive
You'll use the ALIGN directive to round up the location counter to a power-of-two
address. ALIGN has the following syntax:
ALIGN boundary
boundary must be a power of two.
Turbo Assembler inserts NOP instructions into the segment to bring the location
counter up to the desired address if the location counter is not already at 'an offset that is
a multiple of boundary. This directive has no effed if the location counter is already at a
multiple of boundary.
You can't reliably align to a boundary that's more strict than the segment alignment in
which ALIGN appears. The segment's alignment is specified when the segment is first
started with the SEGMENT directive.
For example, if you've defined a segment with
CODE SEGMENT PARA PUBLIC
112 T urboA sse mbIer Use r's Guide
you can then say ALIGN 16 (same as PARA) but not ALIGN 32, since that's more strict
than the alignment that PARA indicated in the SEGMENT directive. ALIGN generates
a warning if the segment alignment isn't strict enough.
The following example shows how you can use the ALIGN directive:
ALIGN 4 ia1ign to DWORD boundary for 386
BigNum DD 12345678
Defining labels
Labels let you assign values to symbols. There are three ways of defining labels:
• using the: operator
• using the LABEL directive
• using the :: operator (from MASM 5.1)
The :operator
The : operator defines a near code label, and has the syntax
name:
where name is a symbol that you haven't previously defined in the source file. You can
place a near code label on a line by itself or at the start of a line before an instruction. You
usually would use a near code label as the destination of a JMP.or CALL instruction
from within the same segment.
The code label will only be accessible from within the current source file unless you use
the PUBLIC directive to make it accessible from other source files.
This directive functions the same as using the LABEL directive to define a NEAR label;
for example, A: is the same as A LABEL NEAR. For example,'
A:
is the same as
A LABEL NEAR
Here's an example of using the: operator.
jne A iskip following function
inc si
A: ijne goes here
The LABEL directive
You'll use the LABEL directive to denne a symbol with a specified type. Note that the
syntax is different for Ideal and MASM modes. In Ideal mode, specify
LABEL name complex_type
In MASM mode, use the following:
Chap ter 9, Set tin 9 and usin 9 the I0 cat ion C 0 unter 113
name LABEL complex_type
name is a symbqlthat you haven'tpreviously defined in the source file. complex_type
describes the size of the symbol and whether it refers to code or data. See Chapter 5 for
f4rther information about complex types.
The label is only accessible from within the current source file, unless you use PUBLIC
to make it accessible from other source files.
You can use LABEL to access different-sized items than those in the data structure; the
following example illustrates this concept.
WORDS LABEL WORD
BYTES DB 64 DUP (0)
mov WORDS [2] ,1
The :: directive
iaccess "BYTES" as WORDS
iwrite WORD of 1
The:: directive lets you define labels with a scope beyond the procedure you're in. This
differs from the: directive in that labels defined with: have a scope of only within the
current procedure. Note that:: is different from :.only when you specify a language in
the .MODEL statement.
Note The :: directive only works when you're using MASM51.
114 Turbo Assembler User-'s Guide
Declaring procedures
Turbo Assembler lets you declare procedures in many ways. This chapter discusses
NEAR and FAR procedures, declaring procedure languages using arguments and
variables in procedures, preserving registers, nesting procedures, declaring method
procedures for objects, and declaring procedure prototypes. You can find more
information about how to call language procedures in Chapter 13.
Procedure definition syntax
You can use the PROC directive to declare procedures. Here's its Ideal mode syntax:
PROC name [[ laI?guage modifier] language] [distance]
[ARG argument_list] [RETURNS item_list]
[LOCAL argument_list]
[USES item_list]
ENDP [name]
Use the following syntax in MASM mode:
name PROC [[language modifier] language] [distance]
[ARG.argument_list] [RETURNS item_list]
[LOCAL argument_list]
[USES item_list]
[name] ENDP
Turbo Assembler also accepts MASM syntax for defining procedures. For more
information on MASM syntax, see Chapter 3.
If you're using Turbo Assembler version T310 or earlier, use the following Ideal mode
syntax:
Chap t er 10, 0 eel ari n9 pro cedures 115
PROC [[language modifier] language] name [distance]
[ARG argument_list] [RETURNS item_list]
[LOCAL argument_list] ,
[USES item_list]
ENDP
Note Unless you specifyversion T310 or earlier, the Ideal mode syntax is no longer allowed in
MASMmode. )
Note that the only difference between the older versions of Turbo Assembler and the
later versions is that language and language_modifier have been moved to follow the'
procedure name to facilitate consistent function prototyping.
Declaring NEAR or FAR procedures
NEAR procedures are called with a near call, and contain a near return; you must call
them only from within the same segment in which they're defined. A near call pushes
the return address onto,the stack, and sets the instruction pointer (IP) to the offset of the
procedure. Since the code segment(CS) is not changed, the procedure must be in the
same segment as the caller. When the processor encounters a near return, it pops the
return address from the stack and sets IP to it; again, the code segment is not changed.
FAR procedures are called with a far call and contain far returns. You can call FAR
procedures from outside the segment in which they're defined. A far call pushes the
return address onto the stack as a segment and offset, and then sets CS:IP to the address
of the procedure. When the processor encounters a far return, it pops the segment and
offset of the return address from the stack and sets CS:IP to it.
The currently selected model determines the default distance of a procedure. For tiny,
small, and compact models, the default procedure distance is NEAR. For all other
models, FAR is the default. If you don't use the simplified segmentation directives, the
default procedure distance is always N13AR. Note that you can specify NEAR or FAR as
an argumentto the MODEL statement. See Chapter 7 for more information.
You can override the default distance of a procedure by specifying the desired'distance
in the procedure definition. To do this, use the NEAR or FAR keywords. These ,
keywords override the default procedure distance, but only for the current procedure.
For example,
MODEL TINY ;default distance near
;testl is a far procedure
testl PROC FAR
;body of procedure
RET . ;this will be a far return
ENDP
116 Turbo Assembler User's Guide
itest2 is by default a near procedure
test2 PROC
ibody of procedure
RET ithis will be a near return
ENDP
The same RET mnemonic is used inboth NEAR and FAR procedures; Turbo Assembler
uses the distance of the procedure to determine whether a near or far return is required.
Similarly, Turbo Assembler uses the procedure distance to determine whether a near or
far call is required to reference the procedure:
CALL testlithis is a far call
CALL test2ithis is a near call
When you make a call to a forward referenced procedure, Turbo Assembler might have
to make multiple passes to determine the distance of the procedure. For example,
testl PROC NEAR
MOVax,lO
CALL test2
RET
testl ENDP
test2 PROC FAR
ADD ax,ax
RET
test2 ENDP
When Turbo Assembler reaches the "call test2" instruction during thefirst pass, it has
not yet encountered test2, and therefore doesn't know the distance. It assumes a
distance of NEAR, and presumes it can use a near call.
When it discovers that test2 is in fact a FAR procedure, Turbo Assembler determines
that it needs a second pass to correctly generate the call. If you enable multiple passes
(with the 1m command-line switch), a second pass will be made. Ifyou don't enable
multiple passes, Turbo Assembler will report a "forward reference needs override"
error.
You can specify the distance of forward referenced procedures as NEAR PTR or FAR
PTR in the call to avoid this situation (and to reduce the number of passes).
testl PROC NEAR
MOVax,lO
CALL FAR PTR test2
RET
testl ENDP
The previous example tells Turbo Assembler to use a far call, so that multiple assembler
passes aren't necessary.
Chap ter 10, Dec Iarin 9 pro cedures 117
Declaring aprocedure language
You can easily define procedures that use high-level language interfacing conventions
in Turbo Assembler. Interfacing conventions are supported for the NOLANGUAGE
(Assembler), BASIC, FORTRAN, PROLOG, C, CPP (C++), SYSCALL, STDCALL, and
PASCAL languages.
Turbo Assembler does all the work of generating the correct prolog (procedure entry)
and epilog (procedure exit) code necessary to adhere to the specified language
convention.
You can specify a default language as a parameter of the MODEL directive. See
Chapter 7 for further details. Ifa default language is present, all procedures that don't
otherwise specify a language use the conventions of the default language.
To override the default language for an individual procedure, include the language
name in the procedure definition. You can specify a procedure language by including a
language identifier keyword in its declaration. For example, a definition in MASM
modeforaPASCAL procedure would be
pascalproc PROC PASCAL FAR
iprocedure body
pascalproc ENDP
Turbo Assembler uses the language of the procedure to determine what prolog and
epilog code is automatically included in the procedure's body. The prolog code sets up
the stack frame for passed argUments and local variables in the procedure; the epilog
code restores the stack frame before returning from the procedure.
Turbo Assembler automatically inserts prolog code into the procedure before the first
instruction of the procedure, or before the first "label:" tag.
Prolog code does the following:
• Saves the current BP on the stack.
• Sets BP to the current stack pointer.
• Adjusts the stack pointer to allocate local variables.
• Saves the registers specified by USES on the stack.
Turbo Assembler automatically inserts epilog code irlto the procedure at each RET
instruction in the procedure (1£ there are multiple RETs, the epilog code will be inserted
multiple times). Turbo Assembler also inserts epilog code before any object-oriented
method jump (see Chapter 4).
Epilog code reverses the effects of prolog code in the following ways:
• Pops the registers specified by USES off the stack.
• Adjusts the stack pointer to discard local arguments.
• Pops the stored BP off the stack.
• Adjusts the stack to discard passed arguments (if the language requires it) and
returns.
118 Turbo Assembler User's G.uide
The last part of the epilog code, discarding passed arguments, is performed only for
those languages requiring the procedure to discard arguments (for example, BASIC,
FORTRAN, PASCAL). The convention for other languages (C, C++, PROLOG) is to
leave the arguments on the stack and let the caller discard them. SYSCALL behaves like
C++. For the STDCALL language specification, C++ calling conventions are used if the
procedure has variable arguments. Otherwise, PASCAL calling conventions are used.
Note Turbo Assembler always implements the prolog and epilog code using the most
, efficient instructions for the language and the current processor selected.
Turbo Assembler doesn't generate prolog or epilog code for NOLANGUAGE
procedures. If such procedures expect arguments on the stack, you must specifically
include the prolog and epilog code yourself.
In general, the language of a procedure affects the procedure in the manner shown in
the following figure.
Figure 10.1 How language affects procedures
Language: None Basic Fortran Pascal C CPP Prolog
Argument L-R L-R L-R L-R R-L R-L R-L
ordering
(Ieft-to-right,
right-to-Ieft)
Who cleans PROC PROC PROC PROC CALLER CALLER CALLER
up stack
(caller,
procedure)
You can use the /la command-line switch to include procedure prolog and epilog code
in your listing file. This lets you see the differences between the languages. See
Chapter 13 for further information.
Specifying alanguage modifier
Language modifiers tell Turbo Assembler to include special prolog and epilog code in
procedures that interface with Windows or the VROOM overlay manager. To use them,
specify one before the procedure language in the model directive, or in the procedure
header. Valid modifiers are NORMAL, WINDOWS, ODDNEAR, and ODDFAR.
Additionally, you can specify a default language modifier as a parameter of the
MODEL directive. If a default language modifier exists, all procedures that don't
otherwise specify a language modifier will use the conventions of the default. See
Chapter 7 for more information.
Include the modifier in the procedure definition to specify the language modifier for an
individual procedure. Forexample,
sample PROC WINDOWS PASCAL FAR
iprocedure body
ENDP
Chapter 10, Declaring procedures 119
Note If you don't specify a language modifier, Turbo Assembler uses the language modifier
specified in the MODEL statement. Turbo Assembler will generate the standard prolog
or epilog code for the procedure if there isn't a MODEL statement, or if NORMAL is
specified.
If you've selected the WINDOWSlanguage modifier, Turbo Assembler generates
prolog and epilog code that let~ you call the procedure from Windows. Turbo
Assembler generates special prolog and epilog code only for FAR Windows procedures.
You can't call NEAR proc~dures from Windows, so they don't need special prolog or
epilog code. Procedures called by Windows typically use PASCAL calling conventions.
For example,
winproc PROC'WINDOWS PASCAL FAR
ARG @@hwnd:WORD,@@mess:WORD,@@wparam:WORD,@@lparam:DWORD
ibody of procedure
ENDP
Note Refer to your Windows documentation for more information on Windows procedures.
The ODDNEAR and ODDFAR language modifiers are used in connection with the
VROOM overlay manager. VROOM has'two modes of operation: oddnear and oddfar.
You can use the Ila switch option on the Turbo Assembler command line to see the
prolog and epilog code that these language modifiers produce.
Defining arguments and local variables
Turbo Assembler passes arguments to higher-level language procedures in stack frames
by pushing the arguments onto the stack before the procedure is called. A language
procedure reads the arguments off the stack when it needs them. When the procedure
returns, it either removes the arguments from the stack at that point (the Pascal calling
convention),or relies on the caller to remove the arguments (the C calling convention).
The ARG directive specifies, in the procedure declaration, the stack frame arguments
passed to procedures. Arguments are defined internally as positive offsets from the BP
or EBP registers.
The procedure's language convention determines whether or not the arguments will be
assigned in reverse order on the,Stack. You should always list arguments in the ARG
statement in the same order they would appear in a high-level declaration of the
procedure."
The LOCAL directive specifies, in the procedure declaration, the stack frame variables
local to procedures. Arguments are defined internally as negative offsets from the BP or
EBP register.
Allocate space for local stackframe variables on the stack frame by including procedure
prolog code, which adjusts the stack pointer downward by the ~mount of space
required. The procedure's epilog code must discard this extra space by restoring the
stack pointer. (Turbo Assembler automatically generates this prolog code when the
procedure obeys any language convention other than NOLANGUAGE.)
120 TurboA sse mbIer Use r 's Guide
Remember that Turbo Assembler assumes that any procedure using stack frame
arguments will include proper prolog code in it to set up the BP or EBP register. (Turbo
Assembler automatically generates prolog code when the procedure obeys any
language convention other than NOLANGUAGE). Define arguments and local
variables with the ARG and LOCAL directives even if the language interfacing
convention for the procedure is NOLANGUAGE. No prolog or epilog code will
automatically be generated, however, in this case.
ARG and LOCAL syntax
Here's the syntax for defining the arguments passed to the procedure:
ARG argument [,argument] ... [=symbol]
[RETURNS argument [,argument]]
To define the local variables for the procedure, use the following:
LOCAL argument [,argument] ... [=symbol]
An individual argument has the following syntax:
argname [[countl_expression]] [: complex_type [:count2_expression]]
complex_type is the data type of the argument. It canbe either a simple type, or a complex
pointer expression. See Chapter 5 for more information about the syntax of complex
types.
Ifyou don't specify a complex_type field, Turbo Assembler assumes WORD. It assumes
DWORD if the selected model is a 32-bit model.
count2_expression specifies how many items of this type the argument defines. An
argument definition of
ARG tmp:DWORD:4
defines an argument called tmp, consisting of 4 double words.
The default value for count2_expression is 1, except for arguments of type BYTE. Since
you can't push a byte value, BYTE arguments have a default count of 2 to make them
word-sized on the stack. This corresponds with the way high-level languages treat
character variables passed as parameters. If you really want to specify an argument as a
~ingle byte on the stack, you must explicitly supply a count2_expression field of 1, such as
ARG realbyte:BYTE:l
countl_expression is an array element size multiplier. The total space reserved for the
argument on the stack is count2_expression times the length specified by the argtype field,
times countl_expression. The default value for countl_expression is 1 if it is not specified.
countl_expression times count2_expression specifies the total count of the argument.
For Turbo Assembler versions 3.2 or later, you can specify count2_expression using the?
keyword to indicate that a variable number of arguments are passed to the procedure.
For example, an argument definition of
ARG tmp:WORD:?
defines an argument called tmp, consisting of a variable number of words.
Chap ter 10, 0 eel ari n9 pro cedures 121
? must be used as the last item in the argument list. Also, you can use? only in
procedures that support variable-length arguments (such as procedures that use the C
calling conventions).
If you end the argument list with an equal sign' (=) and a symbol, Turbo Assembler will
equate that symbol to the totalsize of the ·argument block in bytes. If you are not using
Turbo Assembler's automatic handling of high level language interfacing conventions,
you can use this value at the end of the procedure as an argument to the RET
instruction. Notice that this causes a stack cleanup of any pushed arguments before
returning (this is the Pascal calling convention).
The arguments and variables are defined within the procedure as BP-relative memory
operands. Passed arguments defined with ARG are positive offset from BP; local
variables defined with LOCAL are negative offset from BP. For example,
func1 PROC NEAR
ARG a:WORD,b:DWORD:4,c:BYTE=d
LOCAL x:DWORD,y:WORD:2=z
defines aas [bp+4], b as [bp+6], cas [bp+14], and d as 20; x is [bp-2], y is [bp-6], and
zis 8.
The scope of ARG and LOCAL variable names .
All argument names specified in the procedure header, whether ARGs (passed
arguments), RETURNs (return arguments), or LOCALs (local variables), are global in
scope unless you give them names prepended with the local symbol prefix.
The LOCALS directive enables locally 'scoped symbols. For example,
LOCALS
testl PROC PASCAL FAR
ARG @@a:WORD,@@b:DWORD,@@c:BYTE
LOCAL @@x:WORD,@@y:DWORD
MOVax,@@a
MOV @@x,ax
LES di,@@b
MOV WORD ptr @@y,di
MOV'WORD ptr @@y+2,es
MOV @@c, 'a'
RET
ENDP
test2 PROC PASCAL FAR
ARG @@a:DWORD,@@b:BYTE
LOCAL @@x:WORD
LES di,@@a
MOV ax, es: [di1
MOV @@x,ax
CMP al,@@b
122 TurboA sse mbIer Use r's Guide
jz @@dn
MOV @@x,o
@@dn: MOV ax,@@x
RET
ENDP
Since this example uses locally scoped variables, the names exist only within the body
of the procedure. Thus, test2can reuse the argument names @@a, @@b, and @@x. See
Chapter 11 for more information about controlling the scope of symbols.
Preserving registers
Most higher-level languages require that called procedures preserve certain registers.
You can do this by pushing them at the start of the procedure, and popping them again
at the end of it.
Turbo Assembler can automatically generate code to save and restore these registers as
part of a procedure's prolog and epilog code. You can specify these registers with the
USES statement. Here's its syntax:
USES item [,item] '"
item can be any register.or single-token data item that can legally be pushed or popped.
There is a limit of eight items per procedure. For example,
myproc PROC PASCAL NEAR
ARG @@source:DWORD,@@dest:DWORD,@@count:WORD
USES cx,si,di,foo
MOV cX,@@count
MOV foo,@@count
LES di,@@dest
LDS si,@@source
REP MbvSB
RE
ENDP
See Chapter 18 for information about what registers are normally preserved.
USES is only available when used with procedures that have a language interfacing
convention other than NOLANGUAGE.
Defining procedures using procedure types
You can use a procedure type (defined with PROCTYPE) as a template for the
procedure declaration itself. For example,
Chap t er 10, 0 eel ari h9 pro cedures 123
footype PROCTYPE pascal near :word, :dword,:word
foo PROC footype
arg al:word,a2:dword,a3:word
ipascal near procedure
ian error would occur if
iarguments did not match
ithose of footype
When you declare a procedure using a named procedure description, the number and .
types of the.arguments declared for PROC are checked against those declared by
PROCTYPE. The procedure description supplies the language and distance of the
procedure declaration.
Nested procedures and scope rules
All procedures have global scope, even if you nest them within another procedure. For
example,
testl PROC FAR
isome code here
CALL test2
isome more code here
RET
test2 PROC NEAR
isome code here
RET inear return
test2 ENDP
testl ENDP
In this example, it's legal to call testl or test2 from outside the outer procedure.
If you want to have localized subprocedures, use a locally scoped name. For example,
LOCALS
testl PROC FAR
RET
@@test2 PROC NEAR
RET
@@test2 ENDP
testl ENDP
isome code here
isome code here
. In this case, you can only access the procedure @@test2 from within the procedure testl.
In fact, there can be multiple procedures named@@test2 as Jong as no two are within
the same procedure. For example, the following is legal:
LOCALS
testl PROC FAR
MOV si,OFFSET Buffer
CALL @@test2
124 TurboA sse mbIer Use r' s Guide
RET
@@test2 PROC NEAR
RET
@@test2 ENDP
testl ENDP
test2 PROC FAR
isorne code here
MOV si,OFFSET Buffer2
CALL @@test2
RET
@@test2 PROC NEAR
RET
@@test2 ENDP
test2 ENDP
isorne code here
The following code is not legal:
LOCALS
testl PROC FAR
MOV si,OFFSET Buffer
CALL@@test2
RET
testl ENDP
@@test2 PROC NEAR
isorne code here
RET
@@test2 ENDP
since the CALL to @@test2 specifies a symbol local to the procedure testl, and no such
symbol exists.
Note The LOCALS directive enables locally scoped symbols. See Chapter 11 for further
information.
Declaring method procedures for objects
Some special considerations apply when you create method procedures for objects.
Object method procedures must be able to access the object that they are operating on,
and thus require a pointer to that object as a parameter to the procedure.
Turbo Assembler's treatment of objects is flexible enough to allow a wide range of
conventions for p~ssing arguments to method procedures. The conventions are
, constrained only by the need to interface with objects created by a high-level language.
If you are writing a native assembly-language object method procedure, you might
want to use register argument passing conventions. In this case, you should write a
method procedure to expect a pointer to the object in a register or register pair (such as
ES:DI).
Chap t er 10, De cIari n9 pro cedures 125
If you are writing a method procedure that uses high-level language interfacing
conventions, your procedure should expect the object pointer to be one of the
arguments passed to the procedure. The object pointer passed from high-level OOP
languages like C++ is an implicit argument usually placed at the start of the list of
arguments. A method procedure written in assembly language must include the object
pointer explicitly in its list of arguments, or unexpected results will occur. Remember .
that the object pointer can be either a WORD or DWORD quantity, depending on
whether the object is NEAR or FAR.
Other complexities arise when you write constructor or destructor procedures in
assembly language. C++ uses other implicit arguments (under some circumstances) to
indicate to the constructor or destructor that certain actions must be taken.
Constructors written for an application using native assembly language do not
necessarily need a pointer to the object passed to them. If an object is never statically
allocated, the object's constructor will always allocate the object from the heap.
Note You can find information about the calling conventions of Borland C++ in Chapter 18.
Using procedure prototypes
For versions 3.2 and later, Turbo Assembler lets you declare procedure prototypes
much like procedure prototypes in C. To do so, use thePROCDESC directive.
The Ideal mode syntax of PROCDESC is:
PROCDESC name [procedure_description]
Use the following syntax in MASM mode:
(
name PROCDESC [procedure_description]
procedure_description is similar to the language and argument specification used in the
PROC directive. Its syntax is:
[[language_modifier] language] [distance] [argument_list]
language_modifier, language, and distance have the same syntax as in the PROC directive.
argument_list has the form:
argument [,argument] ...
For more information about PROC, see the beginning of this chapter.
An individual argument has the following syntax:
[argname] [[countl_expression]] :complex_type [:count2_expression]
complex_type is the data type of the argument, and can be either a simple type or a
pointer expression. count2_expression specifies how many items of this type the
argument defines. The default value of count2_expression is 1,except for arguments of
BYTE, which have a default count of2 (since you can't PUSH a byte value onto the
. 80x86 stack). See Chapter 5 for further information about the syntax of complex types.

126 TurboA sse mbIer Use r's Guide
For the last argument, in procedure types whose calling convention allows variable-
length arguments (like C), count2_expression can be?, to indicate that the procedure
caller will determine the size of the array.
Note that the name of each argument (argname) is optional, but complex_type is required
for each argument because procedure types are used mainly for type checking
purposes. The names of the arguments do not have to agree, but the types must.
Here's an example:
test PROCDESC pascal near a:word,b:dword,c:word
This example defines a prototype for the procedure test as a PASCAL procedure taking
three arguments (WORD, DWORD, WORD). Argument names are ignored, and you
can omit them in the PROCDESC directive, as follows:
test PROCDESC pascal near :word, :dword, :word
The procedure prototype is used to check calls to the procedure, and to check the PROC
declaration against the language, number of arguments, and argument types in the
prototype. For example,
test PROC pascal near
ARG al:word,a2:dword,a3:word imatches PROCDESC for test
PROCDESC also globally publishes the name of the procedure. Procedures that are not
defined in a module are published as externals, while procedures that are defined are
published as public. Be sure that PROCDESC precedes the PROC declaration, and any
use of the procedure name.
Procedure prototypes can also use procedure types (defined with PROCTYPE). For
example,
footype PROCTYPE pascal near :word, :dword, :word
foo PROCDESC footype
Chap t er 10, Dec Iari n9 pro Gedures 127
128 TurboA sse mbIer Use r's Guide
Controlling the scope of symbols
In Turbo Assembler and most otherprogramming languages, a symbol can have more
than one meaning depending on where it's located in a module. For example, some
symbols have the same meaning across a whole module, while others are defined only
within a specific procedure.
Symbol scope refers to the range of lines over which a symbol has a specific meaning.
Proper scoping of symbols is very important for modular program development. By
controlling the scope of a symbol, you can control its use. Also, properly selecting the
scope of a symbol can eliminate problems thatoccur when you try to define more than
one symbol of the same name.
Redefinable symbols
Some symbol types that Turbo Assembler supports are considered redefinable. This
means that you can redefine a symbol of this type to be another symbol of the same type
at any point in the module. For example, numeric symbols have this property:
foo· = 1
movax/foo iMoves 1 into AX.
faa = 2
movax/foo iMoves 2 into AX.
Generally, the scope of a given redefinable symbol starts at the point of its definition,
and proceeds to the point where it's redefined. The scope of the last definition of the
symbol is extended to include the beginning of the module up through the first
definition of the symbol. For example,
movax/foo ;Moves 2 into AX!
foG = 1
movax/foo ;Moves 1 into AX.
foo = 2 ;This definition is carried around to the start
;of the module ...
movax/foo ;Moves 2 into AX.
Chap ter 11 , Co nt roll i n9 the scop e of symb0 Is 129
The following list contains the redefinable symbol types.
• text_macro
• numerical_expr
• multiline_macro
• struc/union
• table
• record
• enum
See Chapter 5 for mbre information about these redefinable symbols.
Block seoping
Block scoping makes a symbol have a scope that corresponds to a procedure in a
module. Turbo Assembler supports two varieties of block scoping: MASM-style,
and native Turbo Assembler style. By default, block-scoped symbols are disabled in
Turbo Assembler.
The LOCALS and NOLOCALS directives
Turbo Asse~bler uses a two-character code prepended to symbols, which determines
whether a symbol in a procedure has block scope. This local-symbol prefix is denoted
with "@@.'~ You can use the LOCALS directive to both enable block-scoped symbols,
and to set the local symbol prefix. Its syntax looks like this:
LOCALS [prefix_symbol]
The optional prefix_symbol field contains the symbol (of two character length) that Turbo
Assembler will use as the local-symbol prefix. For example,
LOCALS i@@ is assu;ed to be the prefix by default.
foo proc
@@a: jmp @@a i This @@a symbol belongs to procedure FOO.,
foo endp
bar proc
@@a: jmp @@a iThis @@a symbol belongs to procedure BAR.
bar endp
Ifyou want to disable block-scoped symbols, you can use the NOLOCALS directive. Its
syntax follows:
NOLOCALS
Note that you can also use block-scoped symbols outside procedures. In this case, the
scope of a symbol is determined by the labels defined with the colon directive (:),which
are not block-scoped symbqls. For example,
foo:
@@a:
@@b =1
iStart of scope.
iBelongs to scope starting at FOO:
iBelongs to scope starting at FOO:
130 Tu rboA sse mbIer Use r' s Guide
;Start of scope.
bar:
@@a = 2 ;Belongs to scope starting at BAR:
MASM block seoping
In MASM versions 5.1 and 5.2, NEAR labels defined with the colon directive (:) are
considered block-scoped if they are located inside a procedure, and you've selected a
language interfacing convention with the MODEL statement. However, these symbols
are not truly block-scoped; they can't be defined as anything other than a near label
elsewhere in the program. For example,
version m510
model small, c '
codeseg
foo proc
a: jmp a ;Belongs to procedure FOO
foo endp
bar proc
a: jmp a ;Belongs to procedure BAR
bar endp
a = l;Illegal!
MASM-style local labels
MASM 5.1 and 5.2 provide special symbols that you can use to control the scope of near
'labels within a small range of lines. These symbols are: @@, @F, and @B.
When you declare @@ as a NEAR label using the colon (:) directive, you're defining a
unique symbol of the form @@xxxx (where xxxx is a hexadecimal number). @B refers to
the last symbol defined in this way. @F refers to the next symbol with this kind of
definition. For example,
version m510
@@:
jmp @B
jmp @F
@@:
jmp @B
jmp @F
;Goes to the previous @@.
;Goes to the next @@.
;Goes to the previous @@.
;Error: no next @@.
Chapter 11, Controlling the scope of symbols 131
132 TurboA sse mbIer Use r 's Guide
Allocating data
Data allocation directives are used for allocating bytes in a segment. You can also use
them for filling those bytes with initial data, and for defining data variables.
All data allocation directives have some features in common. First, they can generate
initialized data and set aside room for uninitialized data. Initialized data is defined with
some initial value; uninitialized data is defined without specifying an initial value (its
initial value is said to be indeterminate). Data allocation directives indicate an
uninitialized dataNalue with a ? Anything else should represent an initialized data
value. Chapter 7 explaiJ;ts why you should distinguish between initialized and
uninitialized data.
Another feature common to all data allocation directives is the use of the DUP keyword
to indicate a repeated block of data. Here's the general syntax of all data allocation
directives:
[name] directive dup_expr [,dup_expr ... ]
Turbo Assembler initializes name to point to the space that the directive reserves. The
variable will have a type depending on the actual directive used.
The syntax of each dup_expr can be one of the following:
• ?
• value
• count_expression DUP ( dup_expr [, dup_expr. .. J )
count_expression represents the number of times the data block will be repeated.
, count_expression cannot be relative or forward referenced.
Use the ? symbol if you want uninitialized data. The amount of space reserved for the
uninitialized data depends on the actual directive used.
value stands for the particular description of an individual data element that is
appropriate for each directive. For some directives, the value field can be very complex
and contain many components; others may only require a simple expression. The
following example uses the DW directive, which allocates WORDS:
Chap t er 12, AII 0 cat i n9 dat a 133
DW 2 DUP (3 DUP (1,3),5) i8ame as DW 1,3,1,3,1,3,5,1,3,1,3,1,3,5
Simple data directives
You can define data with the DB, OW, DO, OQ, OF, OP, or OT directives. These
directives define different sizes of simple data, as shown in the following table.
Table 12.1 Data size directives
DB Define byte-siZe data.
DW Define ward-size data:
DD Define daubleward-size data.
DQ Define quadward-size data.
DF Define 48-bit 80386 far-painter-size (6 byte) data.
DP Define 48-bit 80386 far-painter-size (6 byte) data.
DT Define tenbyte (lO-byte) size data.
Note Data is always stored in memory low value before high value.
The syntax of the value field for each of these directive differs, based on the capability of
each data size to represent certain quantities. (For example, it's never appropriate to
interpret byte data as a floating-point number.)
DB (byte) values can be
• A constant expression that has a value between -128 and 255 (signed bytes range
from -128 to +127; unsigned byte values are from 0 to 255). .
• An 8-bit relative expression using the HIGH or LOW operators.
• A character string of one or more characters, using standard quoted string format. In
this case, multiple bytes are defined, one for each character in the string.
OW (word) values can be
• A constant expression that has a value between -32,768 and 65,535 (signed words
range from -32,768 to 32,767; unsigned word values are from 0 to 65,535).
• A relative expression that requires 16 bits or fewer, (including an offset in a 16-bit
segment, or a segment or group value).
• A one or two-byte string in standard quoted string format.
DO (doubleword) values can be
• A constant expression that has a value between -2,147,483,648 and 4,294,967,295
(when the 80386 is selected), or-32,768 and 65,535 otherwise.
• A relative expression or address that requires 32 bits or fewer (when the 80386 is
selected), 16 bits or fewer for any other processor. /
• A relative address expression consisting of a 16-bit segment and a 16-bit offset.
134 TurboA sse mbIer Use rJ s Guide
• A string of up to four bytes in length, using standard quoted string format.
• A short (32-bit) floating-point number.
DQ (quadword) values can be
• A constant expression that has a value between --':2,147,483,648 and 4,294,967,295
(when the 80386 is selected), or -32,768 and 65,535 otherwise.
• A relative expression or address that requires 32 bits or fewer (when the 80386 is
selected), or 16 bits or fewer for any other processor.
• A positive or negative constant that has a value between _263 and 264-1 (signed
quadwords range in value from _263 to 263-1; unsigned quadwords have values from
oto 264-1).
• A string of up to 8 bytes in length, using standard quoted string format.
.• A long (64-bit) floating-point number.
DF, DP (80386 48-bit far pointer) values can be
• A constant expression that has a value between -2,147,483,648 and 4,294,967,295
(when the 80386 is selected), or -32,768 and 65,535 otherwise.
• A relative expression or address that requires 32 bits or fewer (when the 80386 is
selected), or 16 bits or fewer for any other processor.
• A relative address expression consisting of a 16-bit segment and a 32-bit offset.
• A positive or negative constant that has a value between -:-247 and 248-1 (signed 6-byte
values range in value from _247 to 247-1; unsigned 6-byte values have values from 0 to
248-1).
• A string of up to 6 bytes in length, in standard quoted string format.
DT values can be
• A constant expression that has a value between -2,147,483,648 and 4,294,967,295
(when the 80386 is selected), or -32,768 and 65,535 otherwise.
• A relative expression or address that requires 32 bits or fewer (when the 80386 is
selected), or 16 bits or fewer for any other processor.
• A positive or negative constant that has a value between _279 and 28°-1 (signed
tenbytes range in value from _279 to 279-1; unsigned tenbytes have values from 0 to
28°-I).
• A 10-byte temporary real formatted floating-point number.
• A string of up to 10 bytes in length, in standard quoted string format.
• A packed decimal constant'that has a value between 0 and 99,999,999,999,999,999,999.
Numerical and string constants for the simple data allocation directives differ in some
cases from those found in standard Turbo Assembler expressions. For example, the DB,
DP, DQ, and DT directives accept quoted strings that are longer than those accepted
within an expression.
Chap t er 12, AII 0 cat i n9 dat.a 135
Quoted strings are delimited either by single quotes(') or double quotes ("). Inside of a
string, two delimiters together indicate that the delimiter character should be part of the
string. For example,
'what','s up doc?'
represents the following characters:
what's up doc?
You can have floating-point numbers as the value field for the DD, DQ, and DT
directives. Here are some examples of floating-point numbers:
1.0EjO ;Stands for 1.0 x 1030
2.56E-21 ;Stands for 2.56 x 10-21
1.28E+5 ;Stands for 1.28 x 105
0.025 ;Stands for .025
Turbo Assembler recognizes these floating-point numbers because they contain a'.'
after a leading digit. These rules are relaxed somewhat in MASM mode. For example,
DD lE30 ;L~gal MASM mode floating point value!
DD .123 ;Legal in MASM mode only.
Note For clarity, we recommend using the form with the leading digit and the decimal point.
Turbo Assembler also allows encoded real numbers for the DD, DQ, and DT directives.
An encoded real number is a hexadecimal number of exactly acertain length. A suffix of
R indicates that the number will be interpreted as an encoded real number. The length
of the number must fill the required field (plus one digit if the leading digit is a zero); for
example,
DD 12345678r ;Legal number
DD 012345678r ;Legal number
DD 1234567r ;Illegal number (too short)
The other suffix values (D, H, 0, Q, and B) function similarly to those found on numbers
in normal expressions.
Some of the simple data allocation directives treat other numerical constant values
specially. For-example, if you don't specify radix for a value in the DT directive, Turbo
Assembler uses binary coded decimal (BCD) encoding. The other directives assume a
decimal value, as follows:
DD 1234
DT 1234
;Decimal
;BCD
The default radix (that the RADIX directive specifies) is not applied for the DD, DQ,
and DT directives if a value is a simple positive or negative constant. For example,
RADIX 16
DW 1234
DD 1234
;1234 hexidecimal
;1234 decimal
Chapter 5 details numerical constants and the RADIX directive.
136 TurboA sse mbIe r Use r' s Guide
Creating an instance of astructure or union
To create an instance of a structure or a union data type, use the structure or union name
as a data allocation directive. For example, assume you've defined the following:
ASTRUe STRUe
B DB "xyz"
e DW 1
D DD 2
ASTRue ENDS
BUNION UNION
X DW?
Y DD?
Z DB?
BUNION ends
Then the statements
ATEST ASTRUe
BTEST BUNION
would create instances of the structure astrue (defining the variable atest) and the union
bunion (defining the variable btest). Since the example contained the? uninitialized data
value, no initial data will be emitted to the current segment.
Initializing union or structure instances
Initialized structure instances are more complex than uninitialized instances. When you
define a structure, you have to specify an initial default value for each of the structure
members. (You can use the? keyword as the initial value, which indicates that no
speCific initial value should be saved.) When you create an instance of the structure,
you can create it using the default values or overriding values. The simplest initialized
instance of a structure contains just the initial data specified in the definition. For
example,
ASTRUe {}
is equivalent to
DB "xyz"
DW 1
DD 2
The braces ({ }) represent a null initializer value for the structure. The initializer value
determines what members (if any) have initial values that should be overridden, and by
what new values, as you allocate data for the structure instance. The syntax of the brace
initializer follows:
{ [member_name = value [,member_name =value ... ]] }
member_name is the name of a member of the structure or union. value is the value that
you want the member to have in this instance. Specify a null value to tell Turbo
Assembler to use the initial value of the member from the structure or union definition.
A ? value indicates that the member should be uninitialized. Turbo Assembler sets any
Chap t er 12, AII 0 cat i n9 dat a 137
member that doesn't appear in the initializer to the initial value of the member from the
structure or union definition. For example,
ASTRUC {C=2,D=?}
is equivalent to
DB IxyZ"
DW 2
DD ?
You can use the brace initializer to specify the value of any structure or union member,
even in a nested structure or union.
Unions differ from structures because elements in.a union overlap one another. Be
careful when youinitialize a union instance since if several union members overlap,
Turbo Assembler only lets one of those members have an initialized value in an
instance. For example,
BUNION {}
is valid because all three members of the union are uninitialized in the union definition.
This statement is equivalent to
DB 4 DUP (?)
In this example, four bytes are reserved because the size of the union is the size of its
largest member (in this case a DWORD). If the initialized member of the union is not the
largest member of the union, Turbo Assembler makes up the difference by reserving
space but not emitting data. For example,
BUNION {Z=l}
is equivalent to
DB 1
DB 3 DUP (?)
Finally, multiple initialized members in a union produce an error. For example, this is
illegal:
BUNION {X=l, Z=2}
Note that if two or more fields of the union have initial values in the union definition,
then using the simple brace initializer ({}) will also produce an error. The initializer
must set all but one value to? for a legal instance to be generated.
An alternative method of initializing structure and union instances is to use the bracket
« » initializer. The values in the initializer are unnamed but are laid out in the same
order as the corresponding members in the structure or union definition. Use this syntax
for the bracket initializer:
< [value [,value ... ]] >
value represents the desired value of the corresponding member in the structure or
union definition. A blank value indicates that you'll use the initial value of the member
from the structure or: union definition. A ? keyword indicates that the member should
be uninitialized. For example,
138 TurboA sse mbIer Use r's Guide
ASTRUC <"abc",,?>
is equivalent to
DB "abc"
DW 1
DD ?
If you specify fewer values than there are members, Turbo Assembler finishes the
instance by using the initial values from the structure or union definition for the
remaining members.
ASTRUC <"abc"> ;Same as ASTRUC <"abc",,>
When you use the bracket initializer, give special consideration to nested structures and
unions. The bracket initializer expects an additionaLmatching pair of angle brackets for
every level of nesting, so that Turbo Assembler will treat the nested structure or union
initializer as a single entity (to match the value in the instance). Alternatively, you can
skip an entire level of nesting by leaving the corresponding entry blank (for the default
value of the nested structure or union), or by specifying the ? keyword (for an
uninitialized nested structure or union). For example, examine the following nested
structure and union:
CUNION
CTYPE
UNION
STRUC
DB ?
;Start of union
;If CTYPE=O, use this ...
STRUC
ENDS
CTOPARl DW 1
CTOPAR2 DB 2
;If CTYPE=l, use this ...
STRUC
ENDS
ENDS
ENDS
CT1PARl DB 3
CT1PAR2 DD 4
;End of union
;End of structure data type
The bracket initializer for this complex structure/union has two levels of nesting.
This nesting must appear as matched angle brackets within the initializer, like
CUNION <O,«2,>,?»
This directive is equivalent to
DB 0
DW 2
DB 2
DB 2 DUP (?)
Chapter 12, Allocating data 139
Creating an instance of arecord
To create an instance of a record data type, use the name of the record data type as a
data allocation directive. For example, assume you've defined the following:
MYREC RECORD VAL:3=4,MODE:2,SZE:4=15
Then, the statement
MTEST'MYREC'?
would create an instance of the record myrec (defining the variable mtest). No initial data
is emitted to the current segment in this example because the ? uninitialized data value
was specified.
Record instances are always either a byte, a word, or a doubleword, depending on the
,number of bits allocated in the record definition.
Initializing record instances
You must specify an initial value for some or all of the record fields when you define a
record. (Turbo Assembler assumes that any unspecified initial values are 0.) The ,
simplest initialized instance of a record contains justthe initial field data specified in the
definition. For example,
MYREC {}
is equivalent to
DW (4 SHL 6) + (0 SHL 4) + (15 SHL 0)
iSHL is the shift left operator for expressions
The braces ({ }) represent a null initializer value for the record. The initializer value
determines what initial values should be overridden, and by what new values (as you
allocate data for the record instance).
Use this syntax of the brace initializer for records:
{ [field_name = expression [,field_name =expression... ]]
field_name is the name of a field in the record. expression is the value that you want the
field to have in this instance. A blank value indicates that you'll use the initial value of
the field from the record definition. A ?value is equivalent to zero. Turbo Assembler
sets any field that doesn't appear in the initializer to the initial value of the field from the
record definition. For example,
MYREC {VAL=2,SZE=?}
is equivalent to
DW (2 SHL 6) + (0 SHL 4) + (0 SHL 0)
An alternative method of initializing record instances is to use the bracket « »
initializer. Inthis case, brackets delineate the initializer. The values in the initializer are
unnamed but are laid out in the same order as the corresponding fields in therecord
definition. The syntax of the bracket initializer follows:
< [expression [,expression... ]] >
140 Turbo Assembler User's Guide
expression represents the desired value of the corresponding field in the record
definition. A blank value indicates that you'll use the initial value of the field from the
record definition. A ? keyword indicates that the field should be zero. For example,
MYREC <,2,?>
is equivalent to
DW (4 SHL 6) + (2 SHL 4) + (0 SHL 0)
If you specify fewer values than there are fields, Turbo Assembler finishes the instance
by using the initial values from the record definition for the remaining fields.
MYREC <l>isame as MYREC <1, ,>
Creating an instance of an enumerated data type
You can create an instance of an enumerated data type by using the name of the
enumerated data type as a data allocation directive. For example, assume you have
defined the following:
ETYPE ENUM FEE,FIE,FOO,FUM
Then the statement
ETEST ETYPE ?
would create an instance of the enumerated data type etype (defining the variable etest).
In this example, no initial data is emitted to the current segment because the l'
uninitialized data value is specified.
Enumerated data type instances are always either a byte, a word, or a doubleword,
depending on the maximum value present in the enumerated datatype definition.
Initializing enumerated data type instances
You can use any expression that evaluates to a number that will fit within the
enumerated data type instance; for example,
ETYPE ?
ETYPE FOO
ETYPE 255
iuninitialized instance
iinitialized instance, value FOO
ia number outside the ENUM that also fits
Creating an instance of atable
To create an instance of a table data type, use the table name as a data allocation
directive. For example, assume you have defined the following table:
TTYPE TABLE VIRTUAL DoneProc:WORD=DoneRtn,
VIRTUAL MsgProc:DWORD-MsgRtn,
VIRTUAL DoneProc:WORD-DoneRtn
Then, the statement
TTEST TTYPE ?
Chapter 12, Allocating data 141
would create an instance of the table ttype (defining the variable ttest). No initial data
will be emitted to the current segment in this example because the ? uninitialized data
value was specified.
Initializing table instances
When you define a table, you must specify an initial value for all table members. The
simplest initialized instance of a table contains just the initial data specified in the
definition. For example, ' . '
TTYPE {}
is equivalent to
DW MoveRtn
DD MsgRtn
DW DoneRtn
The braces ({}) represent a null initializer value for the structure. The initializer value
determines what members (if any) have initial values that should be overridden, andby
what new values, as you allocate data for the table instance.
Here's the syntax of the brace initializer:
{ [member_name = value [,member_name = value ... ]]
member_name is the name of a member of the table. value is the value that you want the
member to have in this instance. A blank value indicates that you'll use the initial value
of the member from the table definition. A ? value indicates that the member should be
uninitialized. Turbo Assembler sets any member that doesn't appear in the initializer to
the initial value of the member from the table definition. For example, '
TTYPE {MoveProc=MoveRtn2,DoneProc=?}
is equivalent to
DW MoveRtn2
DD MsgRtn
DW ?
,Creating'and initializing anamed-type instance
You can create an instance of a named type by using the type name as a data allocation
directive. For example; if you define the following type:
NTTYPE TYPEDEF PTR BYTE
the statement
NTTEST NTTYPE ?
creates an instance of the named type nttype (defining the variable nttest). No initial data
is emitted tothe current segment in this example because you specified the ?
uninitialized data value.
142 Turbo Assembler User's Guide
The way that you initialize a named-type instance depends on the type that the named
type represents. For example, NTTYPE in the previous example is a word, so it will be
initialized as if you had used the DW directive, as follows:
NTTYPE 1,2,3
DW 1, 2,3
iRepresents pointer values 1,2,3.
iSame as NTTYPE 1,2,3.
However, if the named type represents a structure or table, it must be initialized the
same way as structures and tables are. For example,
foo STRUC
f1 DB?
ENDS
bar TYPEDEF foo
bar {f1=1} iMust use structure initializer.
Creating an instance of an object
Creating an instance of an object in an initialized or uninitialized data segment is exactly
the same as creating an instance of a structure. In fact, objects in Turbo Assembler are
structures, with some extensions. One of these extensions is the @Mptr_<obj ecLname>
structure member.
An object data type with virtual methods is a structure having one member that points
to a table of virtual method pointers. The name of this member is @Mptr_<obj ect_name>.
Usually, you would initialize an instance of an objectusing a constructor method.
However, you could have objects designed to be static and have no constructor, but are
instead initialized with an initializer in a data segment.
If you use the @Mptr_<object_name> member's default value, Turbo Assembler will
correctly initialize the object instance.
Another difference between structures and objects is that objects can inherit members
from previous object definitions. When this inheritance occurs, Turbo Assembler
handles it as a nested structure. Because of this, we do not recommend using bracket
« » initializers for object data.
Creating an instance of an object's virtual method table
Every object that has virtual methods requires an instance of a table of virtual methods
to be available somewhere. A number of factors determine the proper placement of this
table, including what program model you're using, whether you want near or far tables,
and sq forth. Turbo Assembler requires you to place this table. You can create an
instance for the most recently defined object by using the TBLINST pseudo-op, with
this syntax:
TBLINST
TBLINST defines @TableAddr_<object_name> as the address of the virtual table for the
object. It is equivalent to
@TableAddr_<object_name> @Table_<object_name> {}
Chapter 12, Allocating data 143
144 Turbo Assembler User's Guide
Advanced coding instructions
Turbo Assembler recognizes all standard Intel instruction mnemonics applicable to the
currently selected processor(s). You can find a detailed summary of these instructions in
the quick reference guide. This chapter describes Turbo Assembler's extensions to the
instruction set, such as the extended CALL instruction for calling language procedures.
Intelligent code generation: SMART and NOSMART
Intelligent code generation means that Turbo Assembler can determine when you could
have used different instructions more efficiently than those you supplied. For example,
there are times when you could have replaced an LEA instruction by a shorter and
faster MOV instruction, as follows:
LEA AX/lval
can be replaced with
MOV AX/OFFSET lval
Turbo Assembler supplies directives that let you use intelligent code generation. The
following table lists these directives.
Table 13.1 Intelligent code generation directives
NOSMART Disables smart code generation.
By default, smart code generation is enabled. However, smart code generation is
affected not only by the SMART and NOSMART directives,but also by the VERSION
directive (see Chapter 3 for details on VERSION).
Smart code generation affects the following code generation situations:
Chapter 13, Advanced coding instructions 145
.• Replacement of LEA instructions with MOV instructions if the operand of the LEA
instruction is a simple address.
• Generation of signed Boolean instructions, where possible. For example, AND
AX,+02 vs. AND AX,0002.
• Replacement of CALL FAR xxxx with a combination of PUSH CS, CALL NEAR
xxxx, when the target xxxx shares the same CS register.
Using smart instructions make it easier to write efficient code. Somestandard Intel
instructions have also been extended to increase their power and ease of use. These are
discussed in the next few sections.
Extended jumps
!
Conditional jumps such as JC or JE on the, 80186, and 80286 processors are only
allowed to be near (within a single segment) and have a maximum extent of -128 bytes
to 127bytes, relative to the current location counter. The same is true of the loop
conditional instructions such as JCXZ or LOOP on all the Intel processors.
Turbo Assembler can generate complementary jump sequences where necessary and
remove this restriction. For example, Turbo Assembler might convert
to
JC xxx
JNC temptag
JMP xxx
Note You can enable thiscomplementary jump sequences with the JUMPS directive, and
disable it with the NOJUMPS directive. By default; Turbo Assembler doesn't generate
this feature.
When you enable JUMPS, Turbo Assembler reserves enough space for all forward-
referenced conditional jumps for a complementary jump sequence. When the actual
distance of the forward jump is determined, you might not need a complementary
sequence. When this happens, Turbo Assembler generates NOP instructions to fill the
extra space.
To avoid generating extra NOPs, you can
• You can usean override for conditional jumps that you know are in range; for
example,
JC SHORT abc
ADD ax,ax
abc:
• Specify the 1m command-line switch. See Chapter 2 for more about this switch.
146 Turbo Assembler Us.er's Guide
Additional 80386 LOOP instructions
The loop instructions for the 80386 processor can either use CX or ECX as the counting
register. The standard LOOP, LOOPE, LOOPZ, LOOPNE, and LOOPNZ mnemonics
from Intel select the counting register based on whether the current code segment is a
32-bit segment (when using ECX) or a 16-bit segment (when using CX).
Turbo Assembler has special instructions that increase the flexibility of the LOOP
feature. The LOOPW, LOOPWE, LOOPWZ, LOOPWNE, and LOOPWNZ instructions
use CX as the counting register, regardless of the size of the current segment. Similarly,
the LOOPD, LOOPDE, LOOPDZ, LOOPDNE, and LOOPDNZ instructions use ECX
as the counting register.
Additional 80386 ENTER and LEAVE instructions
Use the ENTER and LEAVE instructions for setting up and removing a procedure's
frame on the stack. Depending on whether the current code segment is a 32-bit segment
or a 16-bit segment, the standard ENTER and LEAVE instructions will modify either
the EBP and ESP 32-bit registers, or the BP and SP 16-bit registers. These instructions
might be inappropriate if the stack segment is a 32-'bit segment and the code segment is
a 16-bit segment, orthe reverse.
Turbo Assembler provides four additional instructions that always select a particular
stack frame size regardless of the code segment size. The ENTERW and LEAVEW
instructions always use BP and SP as the stack frame registers, while the ENTERD and
the LEAVED instructions always use EBP and ESP.
Additional return instructions
The standard RET instruction generates code that terminates the current procedure
appropriately. This includes generating epilog code for a procedure that uses a high-
level language interfacing convention. Even for a procedure with NOLANGUAGE as its
calling convention, the RET instruction will generate different code if you declare the
procedure NEAR or FAR. For a NEAR procedure, Turbo Assembler generates a near
return instruction. For a FAR procedure, Turbo Assembler generates a far return .
instruction. (Outside of a procedure, a near return is always generated.)
Turbo Assembler contains additional instructions to allow specific return instructions to
be generated (without epilog sequences). The following table lists them.
Table 13.2 Return instructions
RETN Always generates anear return.
RETF Always generates a far return.
RETCODE Generates a return appropriate for the currently selected mod~l. Generates a near
return for models TINY, SMALL, COMPACT, and TPASCAL. Generates afar return
for models MEDIUM, LARGE, HUGE, and TCHUGE.
Chap t er 13, Advan ced cod i ngin st rue t ion s 147
AdditionallRET instructions
For Turbo Assembler version 3.2 or later, you can use an expanded form of the IRET
instruction. IRET will pop flags from the stack DWORD-style if the current code
segment is 32-bit. Otherwise, a WORD-style POP is used. The IRETW instruction
always pops WORD-style. Note that you can use these enhancements only if you select
version T320. Otherwise, IRET will pop flags WORD-style, and IRETW is unavailable.
Extended PUSH and POP instructions
Turbo Assembler supports several extensions to the PUSH and POP instructions. These
extensions greatly reduce the quantity of typing required to specify an extensive.series
of PUSH or POPs.
Multiple PUSH and POPs
You can specify more than one basic PUSH or POP instruction per line. For example,
PUSH ax
PUSH bx
PUSH ex
POP ex
POP bx
POP ax
can be written as
PUSH ax bx ex
POP ex bx ax
For Turbo Assembler to recognize there are multiple operands present, make sure that
any operand cannot conceivably be considered part of an adjacent operand. For
example,
PUSH foo [bxl
might produce unintended results because foo I [bx], and foo [bx] are all legal
expressions. You can use brackets or parentheses to clarify the instruction, as follows:
PUSH [fool [bxl
Pointer PUSH and POPs
The standard PUSH and POP instructions can't push far pointers, which require 4 bytes
on the 8086, 80186, and 80286 processors, and up to 6 bytes on the 80386 processor.
Turbo Assembler permits PUSH and POP instructions to accept DWORD-sized pointer
operands for the 8086, 80186, and 80286 processors, and PWORD and QWORD-sized
pointer operands for the 80386 processor. When such a PUSH or POP is encountered,
Turbo Assembler will generate code to PUSH or POP the operand into two pieces.
148 Turbo Assembler User's Guide
PUSHing constants on the 8086 processor
While the 80186, 80286, and 80386'processors have basic instructions available for
directly PUSHing a constant value, the 8086 does not.
Turbo Assembler permits constants to be PUSHed on the 8086, and generates a
sequence of instructions that has the exact same result as the PUSH of a constant on the
80186 and higher processors.
Note You can only do this if you've turned smart code generation on.
The sequence of instructions Turbo Assembler uses to perform the PUSH of a constant
is about tenbytes long. There are shorter and faster ways of performing the same
function, but they all involve the destruction of the contents of a register; for example,
MOV ax/constant
PUSH ax
This sequence is only four bytes long, but the contents of the AX register is destroyed in
the process. .
Additional PUSHA, POPA, PUSHF and POPF instructions
For Turbo Assembler versions 3.2 or later, you can use an expanded form of the
PUSHA, POPA, PUSHF and POPF instructions. If the current code segment is 32-bit,
the PUSHA instruction will push registers in DWORD-style, and POPA will pop
registers in DWORD-style. Otherwise, Turbo Assembler uses WORD-style PUSH and
POP. Similarly, PUSHF and POPF will push and pop flags DWORD-style for a 32-bit
code segment, or WORD-style otherwise. !
The PUSHAW, POPAW, PUSHFW, and POPFW instructions always push and pop
WORD-style. Remember that you can use these enhancements only if you're using
version T320 or later; otherwise, the pushes and pops will be done WORD-style.
The PUSHSTATE and POPSTATE instructions
The PUSHSTATE directive saves the current operating state on an internal stack that is
16 levels deep. PUSHSTATE is particularly useful if you have code inside a macro that
functions independently of the current operating state, but does not affect the current
operating mode.
The state information that Turbo Assembler saves consists of:
• Current emulation version (for example T310)
• Mode selection (for example IDEAL, MASM, QUIRKS, MASM51)
• EMUL or NOEMUL switches
• Current processor or coprocessor selection
• MULTERRS or NOMULTERRS switches
• SMART or NOSMART switches
• The current radix
Chap t er 13, Advan ced cod i ngin st rue t ion s 149
• JUMPS or NOJUMPS switches
• LOCALS or NOLOCALS switches
• The current local symbol prefix
Use the POPSTATE directive to return to the last saved state from the stack.
; PUSHSTATE and POPSTATE example
.386
ideal
model small
dataseg
pass_string db 'passed' ,13,10,36
fail_string db 'failed' ,13,10,36
codeseg
jumps
nextl :
pass1:
fini:
end
; Show changing processor selection, number radix, and JUMPS mode
xor eax,eax
pushstate
Zero out eax. Can use EAX in 386 mode
Preserve state of processor, radix and JUMPS
nojumps
radix
p286
mov
cmp
jne
mov
popstate
cmp
je
mov
jmp
mov
mov
mov
mov
int
mov
int
2
ax,l
ax,l
next1
ax,100
eax,4
pass1
dX,OFFSET
fini
Set to binary radix
Only AX available now. EAX would give errors.
No extra NOPS after this
Assemble with /la and check in .lst file.
Now 100 means binary 100 or 4 decimal.
Restores JUMPS and 386 mode and default radix.
EAX available again. Back in decimal mode.
Extra NOPS to handle JUMPS. Check in .1st file
faiLstring ; Load the fail string
dX,OFFSET pass_string ; Load the pasp string.
aX,@data ; Print the string out
ds,ax
ah,9h
21h
ah, 4ch
21h
Return to DOS
150 TurboA sse mbIer Use r's Guide
Extended shifts
On the 8086 processor, the shift instructions RCL, RCR, ROL, ROR, SHL, SHR, SAL,
and SAR cannot accept a constant rotation count other than 1. The 80186, 80286, and
80386 processors accept constant rotation counts up to 255.
When Turbo Assembler encounters a shift instruction with a constant rotation count
greater than 1 (with the 8086 processor selected), it generates an appropriate number of
shift instructions with a rotation count of 1. For example,
.8086
SHL ax/4
generates
SHL ax,l
SHL ax,l
SHL ax,l
SHL ax,l
Forced segme'nt overrides: SEGxx instructions
Turbo Assembler provides six instructions that cause the generation of segment
overrides. The following table lists these instructions.
Table 13.3 Segment override instructions
SEGSS Generates an SS override prefix byte.
SEGDS Generates a DS override prefix byte.
SEGES Generates an ES override prefix byte.
SEGFS Generates an FS override prefix byte.
SEGGS Generates a GS override prefix byte.
You can use these instructions in conjunction with instructions such as XLATB, which
do not require arguments, but can use a segment override. For example:
SEGCS XLATB
Note that most such instructions have an alternative form, where you can provide a
dummy argument to indicate that an override is required. For example,
XLAT BYTE PTR cs: [bxl
These two examples generate exactly the same code.
Additional smart flag instructions
Often, you can simplify an instruction that manipulates bits in a flag to improve both
code size and efficiency. For example,
Chap ter 13, Advan ced cod ingin st rue tio ns 151
OR aX,lOOOh
might be simplified to
OR ah,lOh
if the only result desired was to set a specific bit in AX, and the processor flags that the
instruction affects are unimportant. Turbo Assembler provides four additional
instructions that have this functionality, as shown in the following table:
Table 13.4 Smart flag instructions
SETFLAG
MASKFLAG
TESTFLAG
FLIPFLAG
Set flag bites)
Mask off ~ag bites)
Test flag bites)
Complement flag bites)
OR
AND
TEST
XOR
Use these instructions to enhance the modularity of records; for example,
FOO RECORD RO:l,Rl:4,R2:3,R3:l
TESTFLAG AX, MASK RO
In this example, TESTFLAG will generate the most efficient instruction regardless of
where RO exists in the record.
Additional field value manipulation instructions
Turbo Assembler can generate specific code sequences for setting and retrieving values
from bit fields specified with the RECORD statement. This lets you write code that is
independent of the actual location of a field within a record. Used in conjunction with
the ENUM statement, records can thus achieve an unprecedented level of modularity in
assembly language. The following table lists these instructions:
Table 13.5 Instructions for setting and retrieving values
SETFIELD Sets a value in a record field.
GETFIELD Retrieves a value from a record field.
The SETFIELD instruction
SETFIELD generates code that sets a value in a record field. Its syntax follows:
SETFIELD field_name destination_rim , source_reg
field_name is the name of a record member field. destination~r/m for SETFIELD is a
register or memory address of type BYTE or WORD (or DWORD for the 80386). -
source_reg must be a register of the same size or smaller. If the source is smaller than the
destination, the source register must be the least significant part ofanother register that
is the same size as the destination. This full-size register is called the operating register,
152 TurboA sse mbIer Use r's Guide
Use this register to shift the value in the source register so that it's aligned with the
destination. For example,
Faa RECORD RO:l,Rl:4,R2:3,R3:1
SETFIELD Rl AX,BL
SETFIELD Rl AX,BH
ioperating register is BX
iillegal!
SETFIELD shifts the source register efficiently to align it with the field in the
destination, and ORs the result into the destination register. Otherwise, SETFIELD
modifies only the operating register and the processor flags.
Note Using SETFIELD will destroy the contents of the operating register.
To perform its function, SETFIELD generates an efficient but extended series of the
following instructions: XOR, XCHG, ROL, ROR, OR, and MOVZX.
If you're using SETFIELD when your source and target registers are the same, the
instruction does not OR the source value to itself. Instead, SETFIELD ensures that the
fields of the target register not being set will be zero.
SETFIELD does not attempt to clear the target field before ~Ring the new value. If this
is necessary, you must explicitly clear the field using the MASKFLAG instruction.
The GETFIELD instruction
GETFIELD retrieves data from a record field. It functions as the logical reverse qf the
SETFIELD instruction. Its syntax follows:
GETFIELD field_name destination_reg, source_rim
field_name and destiiwtion_reg function as they do for SETFIELD. You can use source_rim
as you would for source_reg (for SETFIELD). For example,
Faa RECORD RO:l,Rl:4,R2:3,R3:1
GETFIELD Rl BL,AX ioperating register is BX
GETFIELD Rl BH,AX iillegal!
Note Note that GETFIELD destroys the entire contents of the operating register.
GETFIELD retrieves the value of a field found in the source register or memory
address, and sets the pertinent portion of the destination register to that value. This
instruction affects no other registers than the operating register and the processor flags.
To accomplish its function, GETFIELD generates an efficient but extended series of the
following instructions: MOV, XCHG, ROL, and ROR.
If you're using the GETFIELD instructi~n when your source and target registers are the
same, the instruction will not generate the nonfunctional MOV target, source instruction.
Chap t er 13, Advan ced cod ingin st rue t ion s 153
Additional fast immediate multiply instruction
Turbo Assembler provides a special immediate multiply operation for efficient array
indexing. FASTIMUL addresses a typical problem that occurs when you create an array
of structures. There is no immediate multiply operation available for the 8086 processor.
Even for the more advanced processors, multiplication using shifts and adds is
significantly faster in some circumstances than using the standard immediate IMUL
instruction. Based on the currently specified processor, Turbo Assembler's FASTIMUL
instruction chooses between the most efficientsequence of shifts and adds available,
and the current processor's immediate IMUL operation (if any). FASTIMUL has the
following syntax:
FASTlMUL dest_reg, source_rim, value
This instruction is much like the trinary IMUL operation available on the 80186, 80286,
and 80386 processors. The desCreg destination register is a WORD register (or it can be
DWORD on the 80386). source_rim is a register or memory address that must match the
size of the destination. value is a fixed, signed constant multiplicand.
FASTIMUL uses a combination of IMUL, MOV, NEG, SHL, ADD, and SUB
instructions to perform its function. This function destroys the source register or
memory address, and leaves the processor flags in an indeterminate state.
Extensions to necessary instructions for the 80386 processor
The 80386 processor has,the ability to operate in both 16-bit and 32-bit mode. Many of
the standard instructions have different meanings in these two modes. In Turbo
Assembler, you can control the operating size of the instruction using the SMALL and
LARGE overrides in expressions. '
In general, when you use SMALL or LARGEas part of an address expression, the
operator controls the generation of the address portion of the instruction, determining
whether it should be 16- or,32-bit.
When SMALL or LARGE appears outside of the address portion of an expression, it
can control whether a 16-bit instruction or a 32-bit instruction is performed. In cases
where you can determine the size of the instruction from the type of the operand, Turbo
Assembler selects the size'of the instruction. The following table shows the instructions
,that SMALL and LARGE affect.
Table 13.6 Instructions affected by SMALL and LARGE
PUSH [SMALL/LARGE] segreg
POplSMALL/LARGE] segreg
FSAVE [SMALL/LARGE] memptr
FRSTOR [SMALL/LARGE] memptr
FSTENV [SMALL/LARGE] memptr
FLDENV [SMALL/LARGE] memptr
LGDT [SMALL/LARGE] memptr
Selects whether 16-bit or 32-bit form of segment register is PUSHed.
Selects whether 16-bit or 32-bit form of segment register is POpped.
Selectswhether small or large version of floating-point state is saved.
Selects whether small or large version of floating-point state is restored.
Selects whether small or large version of floating-point state is stored.
Selects whether small or large version of floating-point state is loaded.
Selects whether small or large version of global descriptortable is loaded.
154 TurboA sse mbIer Use r 's Guide
Table 13.6 Instructions affected by SMALL and LARGE (continued)
hlst);uction
SGOT [SMALL/LARGE] memptr
LIOT [SMALL/LARGE] memptr
SlOT [SMALL/LARGE] memptr
JMP [SMALL/LARGE] memptr
Selects whether small or large version of global descriptor table is saved.
Selects whether small or large version of interrupt descriptor table is loaded.
Selects whether small or large version of interrupt descriptor table is saved.
For DWORD-sized memory addresses, selects between FAR 16-bit JMP and
NEAR 32-bit JMP.
CALL [SMALL/LARGE] memptr For DWORD-sized memory addresses, selects between FAR 16-bit CALL
and NEAR 32-bit CALL.
Note Turbo Assembler selects the size of the instruction using SMALL and LARGE only
when no other information is available. For further information about overriding
address sizes with the SMALL and LARGE operators, see Chapter5.
Calling procedures with stack frames
Turbo Assembler supports an extended form of the CALL instruction that lets you
directly call procedures that use high-level language interfacing conventions.
Arguments to procedures that use high-level language interfacing conventions are
passed on the stack in a stackframe. The caller must push these arguments onto the stack
before calling the procedure.
The interfacing convention of the procedure determines the order arguments should be
pushed into the stack frame. For BASIC, FORTRAN, and PASCAL procedures,
arguments are pushed onto the stack in the order they are encountered; for C and CPP
(C++), the arguments are pushed in the reverse order.
The interfacing convention of a procedure also determines whether the procedure or the
caller of the procedure must remove the arguments from the stack once the procedure is
called. C and c++ require the caller to clean up the stack. In all other languages, the
procedure itself must remove the arguments from the stack before returning.
Turbo Assembler handles both the proper argument ordering and stack cleanup for you
with the extended CALL instruction. The syntax for calling a procedure with
parameters follows:
CALL expression [language] [,argument_list]
expression is the target of the CALL instruction. language specifies the interfacing
convention to use for the call. Ifyou don't specify a language, Turbo Assembler uses the
default language set by MODEL (see Chapter 7 for further information about using
MODEL).
Arguments, if any, follow the language identifier. The syntax of each argument in the
argument list is the same as for the extended PUSH and POP instructions. You can
separate these arguments with commas; for example,
CALL test PASCAL,ax,es OFFSET buffer,blen
PASCAL, the language in the example, causes Turbo Assembler to push the arguments
in the same order that it encounters them. This example call is equivalent to
Cha pte r 13, Advan ced cod i ngin strue tion s 155
PUSH ax
PUSH es OFFSET buffer
PUSH word PTR bIen
CALL test
A call to a C procedure requires that the arguments be pushed onto the stack in the
reverse order. Turbo Assembler automatically does this so that a call of the form
CALL test C,ax,es OFFSET buffer,word PTR bIen
results in the following code:
PUSH word PTR bIen
PUSH esOFFSET buffer
PUSH ax
CALL test
SUB sp,8
When ~alling a procedure with arguments, you should always list the arguments in the
same order theywere listed in the procedure header. Turbo Assembler reverses them if
,necessary.
Note Remember to separate arguments with commas and components ofarguments with
spaces. Turbo Assembler, depending on the interfacing convention, can push
arguments in reverse order on the stack, but it won't alter the ordering of argument
components.
If the interfacing ~onvention for the call is NOLANGUAGE, Turbo Assembler reports
an error if any arguments are present. Although you can define arguments to a
NOLANGUAGE procedure with the ARG directive, you must explicitly push the
arguments when you make a call to a NOLANGUAGE procedure.
Calling procedures that contain RETURNS
Procedures that define some oftheir arguments with the RETURNS keyword must be
considered specially. These arguments are used to return values to the caller; therefore,
the caller always pops them. There is no special extension to the CALL instruction in
Turbo Assembler to help pass those arguments specified in a procedure declaration
after the RETURNS directive. You must explicitly PUSH these arguments before the
CALL, and POP them afterward. '
Calling procedures that have been prototyped
If you've defined the procedure prior to the call or used PROCDESC to prototype the
procedure (see Chapter 10), Turbo Assembler will type check any language and
arguments specified in the call and generate a warning if the language, number of
parameters, or types of parameters don't match.
For example,
156 TurboA sse mbIer Use r's Guide
test PROCDESC pascal far :word, :dword, :word
call test pascal aX,ds bx,cx
call test c, aX,dx, bx,cx
call test pascal, eax, ebx, ecx
call test pascal, aX,ds bx
iworks fine
iwrong language!
iwrong parameter types!
itoo few parameters!
Since the language of the procedure has been specified, you don't have to include it in
the calL If you omit it, however, make sure to include the comma that would normally
follow it:
call test,ax,ds bx,cx iworks fine
You can also use procedure types (declared with PROCTYPE) to supply a distance and
language, and force type-checking to occur. For example,
footype proctype pascal near :word, :dword, :word
call footype ptr[bx],ax,ds bx,cs ino error!
Calling method procedures for objects: CALL..METHOD
The CALL instruction is extended to support the calling of object methods. A call to an
object method can generate either a direct call (for static methods) or an indirect call (for
.virtual methods).
Because you can use an indirect call, the instructions that pertbrm the call can destroy
the contents of some registers. Therefore, Turbo Assembler lets you select the proper
registers if you're using a virtual method call.
i!} .Here's the syntax of the CALL..METHOD extension:
~ CALL instance_ptr METHOD [object_name:]method_name [USES [segreg:]offsreg]
[language_and_args]
instance-rtr must describe an instance of an object. In MASM mode,it's often impossible
to determine the name of the object associated with an instance. Therefore, Turbo
Assembler allows the object_name field, so that you can specify the instance's object
name.
method_name contains the name of the method to be called for the specified object
instance.
See Chapter 8 for further information about how to specify a method as virtual or static.
If the method is virtual and an indirect call is required, the CALL..METHOD instruction
normally calls indirectly through ES:BX (or ES:EBX for USE32 models on the 80386
processor). If you want to use other registers, you can override them with the USES
clause. segreg is the optional segment register to use, and offsreg is the offset register to
use for the call.
For objects declared with near tables, CALL..METHOD only loads the offset register.
Turbo Assembler assumes that the segment register is already set up to the correct
value.
Chap t er 13, Adv anee d cod i ngin st rue t ion s 157
Note It's good programming practice to specify an appropriate selection for indirect calling
registers, even if you know that the method you're calling is static. As objects are
modified, met~ods can change from being static to virtual.
The language_and_args field of theCALL..METHOD instruction contains the optional
language and argument specifications, which are identical in form to that listed
previously under "Calling procedures with stack frames."
Calling method procedures for C++or Pascal usually requires that the instance of the
object be passedas an argument on the stack. SeeChapter 18 for further information.
Tail recursion for object methods: JMP..METHOD
Turbo Assembler provides a IMP..METHOD instruction that corresponds to the
CALL..METHOD mstruction. Here's its syntax:
JMP instance_ptr METHOD [object_name:]method_name [USES [segreg:]offsreg] .
~ IMP..METHOD functions exactly like CALL..METHOD except that
©) • It generates a IMP instead of a CALL instruction.
• It generates procedure epilog code to clean up the stack before the IMP instructionis
generated.
The IMP..METHOD instruction makes it possible to write efficient tail recursion code.
It's intended to replace the common situation where a CALL..METHOD instruction is
issued to the current method, followed by a RET instruction.
Additional instruction for object-oriented programming
When an object instance is constructed, you must initialize the instance's virtual table
pointer (if any) to point to the correct virtual method table. The TBLINIT instruction lets
you do this automatically. The syntax of the TBLINIT instruction is
TBLINIT object_instance_pointer
The object_instance~ointer field is the address of the object whose virtual table pointer is
to be initialiied. The TBLINIT instruction assumes that the object instance should be of
the current object type (in other words, the immediately preceding object definition
determines the object type that TBLINIT initializes). For example,
TBLINIT DS:SI
would initialize the virtual table pointer of the object at DS:SI, if it has one.
158 TurboA sse mbIer Use r's Guide
·Using macros
Macros let you give a symbolic nameto a text string or a block of code that will be used
frequently throughout your program. Macros go beyond this simple substitution,
however. Turbo Assembler has macro operators that provide great flexibility in
designing macros. Combined with the ability to use multiline macros with arguments,
this makes Turbo Assembler's macro facility a very powerful tool. This chapter
discusses how to use text and multiline macros in your program.
Text macros
A text macro is a symbol that represents a string of text characters. When Turbo
Assembler encounters the symbol in expressions (and other situations), it substitutes the
text characters for the symbol. For example, if DoneMsg is a text macro whose value is
"Returning to the OS", the following statement
GoodBye DB DoneMsg
results in
GoodBye DB 'Returning to the OS'
Defining text macros with the EQU directive
You can use the EQU directive to define simple text macros. Here's the syntax for
defining a text macro:
name EQU text_string
text_string associates with the text macro name name. You should enclose text_string in
brackets « » to qelineate the text; for example,
DoneMsg EQU <'Returning to the OS'>
Note If you omit the brackets in MASM mode, Turbo Assembler will try to evaluate
text_string to an expression, and an error may result. Only if it can't evaluate text_string
Chap ter 14, Usin 9 mac r0 s 159
will Turbo Assembler treat it as a text macro (to remain compatible with MASM). In
Ideal mode, EQU always defines a text macro. If you don't enclose text_string in
brackets and it's the name of another text macro, Turbo Assembler will use that macro's
contents. Otherwise, the macro will be defined to the text.
You should always enclose text macro strings in angle brackets to make sure they're
properly defined. Consider the following mistake that can occur when you don't:
IDEAL
Earth EQU dirt
Planet EQU Earth
Planet EQU <Earth>
;Earth = "dirt"
;Planet = "dirt" (wrong!) .
;Planet = "Earth" (correct!)
In Ideal mode, the EQU statement always defines a text macro.
Text macros are redefinable; you can redefine a text macro name in the same module to
another text string.
String macro manipulation directives
Turbo Assembler provides directives that can manipulate strmg macros. These
directives are available in Ideal mode, and for versions M510, M520, and T300 orlater
(as specified by the VERSION directive).
A string argument for any of these directives can be any of the following:
• 'a text string enclosed in brackets; for irlstance, <abc>
• the name of a previously defined text macro
• an expressionpreceded by a % character, whose value is converted to the equivalent
numerical string representation appropriate for the current radix
The CATSTRdirective
The CATSTR directive defines a new text macro by concatenating strings together.
CATSTR has the following syntax:
name CATSTR string[,string] ...
CATSTR concatenates from left to right. Turbo Assembler creates a new text macro of
the name name.
The SUBSTR directive
The SUBSTR directive defines a newtext macro to be a substring of a string. Here's its
syntax:
name SUBSTR string,position_expression[,size_expression]
The new text macro, name consists of the portion of string that starts at the
. position_expression character, and is size_expression characters in length. If you don't
supply size_expression, the new text macro consists of the rest of stringfrom the character
at position_expression. Turbo Assembler considers the first character of string to be at
po~sition 1.
160 Turbo Assembler User's Guide
The INSTR directive
The INSTR directive returns the position of one string inside another string. INSTR has
the following syntax:
name INSTR [start_expression,lstring1,string2
Turbo Assembler assigns name a numeric value that is the position of the first instance of
string2 in stringl. The first character in stringl has a position of 1. Ifstring2 does not
appear anywhere within stringl, Turbo Assembler returns a value of O. If you include
start_expression, the search begins at the start_expression character. The first character of a
string is 1.
The SIZESTR directive
The SIZESTR directive returns the length of a text macro (the number of characters in
the string). Here's its syntax:
name SIZESTR string
name is set to the numeric value of the length of the string. A null string < > has a length
of zero.
Text macro manipulation examples
The following examples show how these operators work:
VERSION T300
IDEAL
ABC
ABC2
ABC
ABC3
ABCLEN
EQU <abc>
EQU ABC
EQU <deb
CATSTR ABC2,<,>,ABC,<,>,ABC2
SIZESTR ABC
ABC3LEN SIZESTR ABC3
COMMA1 INSTR ABC3,<,>
COMMA2 INSTR
ABC4 SUBSTR
ABC5 SUBSTR
ABC6 EQU
ABC7 EQU
ABC8 EQU
Multiline macros
COMMA1+1,ABC3,<,>
ABC3,5
ABC3,5,3
3+2+1
%3+2+1
%
COMMA1
;ABC = "abc"
;ABC2 = "abc"
;ABC = "def" (redefined)
;ABC3 = "abc, def, abc" .
;ABCLEN = 3
;ABC3LEN = 11
;COMMA1 = 4
;COMMA2 = 8
;ABC4 = "def,abc"
;ABC5 = "def"
;ABC6 = 6 (numeric equate)
;ABC7 = "6" (text macro)
;ABC8 = "4"
The multiline macro facility lets you define a body of instructions, directives, or other
macros that you'll include in your source code whenever the macro is invoked. You can
supply arguments to the macro that Turbo Assembler will substitute into the macro
body when you include the macro in the module.
.There are several types of multiline macros. One version substitutes each element of a
string, one after the other, as an argument to the macro. Another version repeats the
macro body a certain number of times. Finally, you can define still another version in
Chap t er 14, Us in 9 mac r0 s 161
one place, and invoke it many times. All versions have the definition of a macro body in
common.
The multiline macro body
Regardless of its actual content, Turbo Assembler's macro processing facility treats a
multiline macro body as merely a number of lines of text. Turbo Assembler lets you
replace symbols within the macro body with text specified at the time a macro is
invoked. This feature is called argument substitution. The symbols in the macro body that
will be replaced are called dummy arguments. For example, suppose the symbolfaa is a
dummy argument in the following macro body:
PUSH foo
MOV foo,l
If you assignfaa with the text string AX when you invoke this macro, the actual text
included in the module will be .
PUSH AX
MOV AX,l
The rules Turbo Assembler uses for recognizing a dummy argument are fairly complex.
Examine the following macro body lines where the dummy argumentfoowould not be
recognized:
symfoo:
DB 'It is foo time'
In general, Turbo Assembler will not recognize a dummy argument without special
help in the following situations:
• when it is part of another symbol
• when it is inside of quotation marks (' or ")
• in Ideal mode, when it appears after a semicolon not inside of quotes
Using &in macros
The & character has aspecial meaning when used with the macro parameters. In
general, & separates a d~mmy argument name from surrounding text, so Turbo
Assembler can recognize it for substitution. For example, given the following Ideal
mode macro:
macro mac1 foo
sym&foo:
DB 'It is &foo time'
endm
if you assignfaa the text string party When this macro is invoked, the actual text included
in the module will be
symparty: .
DB 'It is party time'
Another example might be
foo&sym:
162 TurboA sse mbIer Use r' s Gui d.e
DB 'We are in O&foo&o'
Ifyou assignfaa the text string hi when this macro is invoked, the text included in the
module will be
hisym:
DB 'We are in Ohio'
Note Here are the rules for the & character:
• Outside quoted strings, the & serves only as a general separator.
• Inside quoted strings and after a semicolon that's not in a quoted string in Ideal
mode, & must precede a dummy argument for it to be recognized.
• Turbo Assembler removes one & from any group of &s during a macro expansion.
The last point makes it possible to place macro definitions requiring & characters inside
other macro definitions. Turbo Assembler will remove only one & from any group.
Including comments in macro bodies
For particularly complicated macros, you might want to include (in the macro body
text) comments that won't be included when the macro is invoked. This also reduces the
memory required for Turbo Assembler to process macros. To do this, use the double
semicolon comment at the beginning of a line. For example, the following macro body
iiWOW, this is a nasty macro!
DB 'Nasty macro'
will only include the following text when it is invoked:
DB 'Nasty macro'
Note Comments preceded by single semicolons are always included in a macro expansion.
Local dummy arguments
At the beginning of any macro body, you can include one or more LOCAL directives.
LOCAL declares special dummy arguments that, each time the macro expands, will be
assigned a unique symbol name.
The syntax for the LOCAL directive in macro bodies looks like this:
LOCAL dummy_argumentl [,dummy_argument21 ...
When using this syntax, note that the LOCAL-directive must come before any other
statements in a macro body.
If the dummy_argument name used in the LOCAL directive does not have a local symbol
prefix the uniqu~ symbol name assigned to it will be in the form ??xxxx, where xxxx
represents a hexadecimal number. Otherwise, the unique symbol name will be <local
prefix>xxxx. For details on how to enable local symbols and set the local symbol prefix,
see Chapter 11.
You can use LOCAL dummy arguments to define labels within the macro body. For
example,
Chap t er 14, Us i n9 mac r0 S 163
LOCAL @@agn,@@zero
XOR dX,dx
MOV cX,exp
MOVax,l
JCXZ @@zero
MOV bX,factor
@@agn: MUL bx
LOOP @@agn
@@zero:
Note In macros, you don't have to use @@ since local labels in macros are turned into
consecutive numbers, like ??OOOL Their names are not easily accessible outside macros.
The EXITM directive
You can use the EXITM directive within a macro body to prematurely terminate the
assembly of an included macro body. Its syntax follows:
EXITM
When Turbo Assembler encounters EXITM in a macro body that has been included m
the module source code, assembly of the expanded macro body stops immed~ately.
Instead, Turbo Assembler will continue assembling the module at the end of the macro.
You can use the EXITM statement with a conditional assembly directive to terminate a
macro expansion when certain conditions are met.
Tags and the GOTO directive
Using macro tags and the GOTO directive lets you control the sequence in which lines
within the macro body expand. You can place a macro tag at any place within the macro
body. The tag occupies an entire line in the macro, with the following syntax:
:tag_symbol
When the macro expands, all macro tags are discarded.
The GOTO directive tells the assembler to go to a specified point in your code, namely
the tag_symbol. GOTO has the followingsyntax:
GOTO tag_symbol
GOTO also terminates any conditional block that contains another GOTO. This lets you
place GOTO inside conditional assembly blocks. For example,
IF faa
GOTO tagl
ENDIF
DISPLAY "faa was false!"
:tagl
;resume macro here ...
;works the same whether faa was false or true
Note Be careful not to create infinite macro loops when you use the GOTO directive. Infinite
loops can cause Turbo Assembler to run out of memory, or even appear to stop
functioning.
164 TurboA sse mbIer Us erJ s Guide
See Chapter 15 for further information about conditional assembly directives.
General multiline macros
Turbo Assembler associates a general multiline macro's body of directives, instructions,
and other macros with a symbolic macro name. Turbo Assembler inserts the body of
statements into your program wherever you use the macro name as a directive. In this
way, you can use a general multiline macro more than once.
Note You can invoke a macro before you define it only when you use the / m command-line
switch as explained in Chapter 2. However, this is considered to be poor programming
practice.
Here's the Ideal mode syntax for defining a general multiline macro:
MACRO name parameter_list
macro_body
ENDM
Here's the MASM mode syntax for defining a general multiline macro:
name MACRO parameter_list
macro_body
ENDM
name is the name of the multiline macro you're defining. macro_body contains the
statements that make up the body of the macro expansion. You can place any valid
(and any number of) Turbo Assembler statements within a macro. The ENDM keyword I
terminates the macro body.
This example defines a macro named PUSHALL that, when invoked, includes the
macro body consisting of three PUSH instructions into your program.
PUSHALL MACRO
PUSH AX BX CX DX
PUSH DS S1
PUSH ES DI
ENDM
parameter_list is a list of dummy argument symbols for 'the macro. Here's its syntax:
[dummy_argument [,dummy_argument ... JJ
You can l,lse any number of dummy arguments with a macro, as long as they fit on one
line, or you use the line continuation character ( ) to continue them to the next line. For
example,
ADDUP MACRO dest,
sl,s2
MOV dest,sl
ADD dest,s2
ENDM
idest is 1st dummy argument
is1,s2 are 2nd and 3rd dummy arguments
Each dummy argument has the following syntax:
Chap ter 14, Usin 9 .mac r0 s 165
dummy_name is a symbolic name used as a place holder for the actual argument passed
to the macro when it's invoked. The optional dummy_type specifies something about the
form the actual argument must take when you invoke the macro. The following types
are supported:
Table 14.1 Dummy argument types
REQ
=<texCstring>
VARARG
REST
Argument cannot be null or spaces.
Bracketed text string is the default value for the dummy argument when the actual
argument is null or contains spaces. '
Actual argument consists of the rest of the macro invocation, interpreted as a list of
arguments. Commas and angle brackets are added to ensure this interpretation.
Actual argument consists of the rest of the macro invocation, interpreted as raw text.
Invoking ageneral multiline macro
To invoke a general multiline macro, use the name of the macro as a directive in your
program. Turbo Assembler inserts the macro body (after all the dummy arguments are
substituted) at that point in the module. The syntax for invoking a general multiline
macro is as follows:
macro_name [argument [ [' I Jargument J ••• J
macro_name is the symbolic name of a macro. Ifyou invoke a macro with arguments, the
arguments are listed following the, macro name. You can specify any number of
arguments, but they must all fit on one line. Separate multiple arguments with commas
or spaces. When the macro expands, Turbo Assembler replaces the first dummy
argument in the macro definition with the first argument passed, the second dummy
argument,with the second argument, and so forth.
Each argument represents a text string. You can specify this text string in the following
ways:
• as a contiguous group of characters, not containing any whitespace, commas, or
semicolons
• as a group of characters delineated by angle brackets « », which can contain spaces,
commas, and semicolons
• as a single character preceded by a !character, which is equivalent to enclosing the
character in angle brackets
• as an expression preceded by a % character,which represents the text value of the
expression appropriate for the currently selected radix
The < > literal string brackets
Use angle brackets to delineate a literal string that contains the characters between them.
You should use them like this:
<text>
166 Turbo Assembler User's Guide
text is treated as a single string parameter, even it if contains commas, spaces, or tabs
that usually separate each parameter. Use this operator when you want to pass an
argument that contains any of these separator characters.
You can also use this operator to force Turbo Assembler to treat a character literally,
without giving it any special meaning. For example, if you want to pass a semicolon (;)
as a parameter to a macro invocation, you have to enclose it in angle brackets «;» to
prevent it from being treated as the beginning of a comment. Turbo Assembler removes
only one level of angle brackets when it cpnverts a bracketed string to a text argument.
This makes it possible to invoke a macro requiring angle brackets from inside another
macro body.
The !character
The !character lets you invoke macros with arguments that contain special characters.
Using this character prior to another is similar to enclosing the second character in angle
brackets. For example, !; functions the same as <;>. Some common uses are shown in the
following table.
Table 14.2 Uses for the !character
!> >
!< <
!!
The %expression evaluation character
The % character causes Turbo Assembler to evaluate an expression. The assembler
converts the result of the expression to an ASCII number in the current radix, which is
the text that the % character produces. Use this character when you want to pass the
string representing a calculated result, rather than the expression itself, as a macro
argument. The syntax follows:
%expr
expr can be either an expression (using any lega1 operands and operators), or it can be
the name of a text macro. If it is an expression, the text that is produced is the result of
the expression, represented as a numerical string in the current radix. If expr is a text
macro name, the text that's produced is the string that the text macro represents. See
Chapter 5 for more information about Turbo Assembler expressions.
For example, this code
DEFSYM MACRO NUM
TMP_&NUM:
ENDM
TNAME EQU <JUNK>
DEFSYM %5+4
DEFSYM %TNAME
;defining a text macro
Chap t er 14, Us in 9 mac r0 s 167
results in the following code macro expansions:
TMP_9:
TMP_JUNK:
Redefining ageneral multiline macro
You can redefine general multilme macros. The new definition automatically replaces
the old definition. All preceding places where the macro had already been invoked will
not change. All invocations of the macro following the redefinition use the new
definition.
Deleting ageneral multiline macro: The PURGE directive.
You can use the PURGE directive to delete a macro. PURGE has the following syntax:
PURGE macroname [,macroname] ...
PURGE deletes the general multiline macro definition associated with macroname. After
you PURGE a macro, Turbo Assembler no longer treats the symbol macroname as if it
were a macro; for example,
ADD MACRO al,a2
SUB al, a2
ENDM
ADD aX,bx ;This invocation will produce SUB aX,bx
PURGE ADD
ADD aX,bx ;This is no longer a macro, so ADD aX,bx is produced
You can purge several macros at a time byseparating their names with commas. Note,
however, that you can't redefine a purged macro symbol as anything other than another
macro.
Defining nested and recursive macros
The statements in a macro body can include statements that invoke or define other
macros. If you take this example, .
MCREATE MACRO opname,opl,op2,op3,op4,op5,op6,op7
IFNB opname
DO&opnameMACRO op,count
IF count LE 4
REPT count
opname op,l
ENDM
ELSE
MOV CL,count
opname op,CL
ENDIF
ENDM
MCREATE opl,op2,op3,op4,op5,op6,op7
ENDIF
ENDM
168 Turbo Assembler User's Guide
;end of DOopname macro
;recurse!
;end of if
;end of MCREATE macro
..
and invoke it with
MCREATE ror,rol,rcl,rcr,shl,shr,sal,sar
it will create the additional macros DOror, DOrol, and so forth, which you can then use
like this:
DOshr ax,S
DOrcr bX,3
You can call recursive macros with a list of parameters, and set them up so that the
macro will work with anywhere from zero to a maximum number of parameters. To do
this, have the macro body use the first parameter to do its expansion, then call itselfwith
the remaining parameters. Every time it recurses, there will be one fewer parameter.
Eventually, it will recurse with no parameters.
When you call the macro recursively, it·always needs some way to test for the end of the
recursion. Usually, an IFNB conditional statement will do this for only the macro body
if the passed parameter is present. Here is a simpler example of a recursive macro:
PUSHM MACRO rl,r2,r3,r4,r5,r6,r7,r8
IFNB rl
push rl
PUSHM r2,r3,r4,r5,r6,r7,r8
ENDIF
ENDM
Note See Chapter 15 for more information about the IFNB directive.
The count repeat macro.
You can use the REPT repeating macro directive to repeat a macro body a specific
number of times, using this syntax:
REPT expression
macro_body
ENDM
expression tells Turbo Assembler how many times to repeat the macro body specified
between the REPT and END directives. expression must evaluate to a constant and can't
contain any forward-referenced symbol names. Use ENDM to mark the end of the
repeat block. For example, this code
REPT 4
SHL aX,l
ENDM
produces the following:
SHL aX,l
SHL aX,l
SHL aX,l
SHL aX,l
Another example shows how to use REPTin a macro to generate numbers that are the
various powers of two:
Chap t er 14, Usin 9 mac r0 s 169
count = 0
defname macro num
Bit&num dd (1 SRL (&num))
endin
rept 32
defname %count
count = count + 1
endin
The WHILE directive
You can use the WHILE macrodirective to repeat a macro body until a certain
expression evaluates to a(false). WHILE has the following syntax:
WHILE while_expression
macro_body
ENDM
Turbo Assembler evaluates while_expression before each iteration of the macro body. Be
careful to avoid infinite loops, which can cause Turbo Assembler to run out of memory
or appear to stop functioning. Here's an example using WHILE:
WHILE 1
IF some_condition
EXITM
ENDIF
i i 'Do nothing
ENDM
i We never make it this far unless some_condition is true
The EXITM directive can be used to break out of a WHILE loop.
String ,repeat macros
You can use the IRP and IRPC string repeat macro directives to repeat a macro body
once for each element in a list or each character in a string. Each of these directives
requires you to specify a single dummy argument. Here's the IRP syntax:
IRP dummy_argument, argument_list
macro_body
ENDM
IRPC has the following syntax:
IRPC dummy_argument, string'
macro_body
ENDM
In both'cases, dummy_argument is the dummy argument used in the macro body.
ENDM marks the end of the macro body.
For IRP, argument_list consists of a list of arguments separated by commas. The
arguments can be any text, such as symbols, strings, numbers, and so on. The form of
170 TurboA sse mbIer Use r' s Guide
each argument in the list is similar to that described for general multiline macro
invocations, described earlier in this chapter. You must always surround the argument
list with angle brackets « ».
For IRPC, the argument consists of a single string. The string can contain as many
characters as you want.
For each argument or character in a string, Turbo Assembler will include the macro
body in the module, substituting the argument or character for the dummy argument
wherever it finds it. For example,
IRP reg,<ax,bx,cx,dx>
PUSH reg
ENDM
produces the following:
PUSH ax
PUSH bx
PUSH ex
PUSH dx
and the directive IRPC
IRPC LUCKY, 1379
DB LUCKY
ENDM
produces this:
DB 1
DB 3
DB 7
DB 9
Note Be careful when using IRPC because Turbo Assembler places each character in the
string"as is" in the expanded macro, so that a string repeat macro such as
IRPC CHAR, HELLO
DB CHAR
ENDM
might not produce DB 'H', 'E', 'L', 'L', 'a', but instead would produce DB H, E, L, L, a
(where each letter is treated as a symbol name).
The % immediate macro directive
The % immediate macro directive treats a line of text as if it's a macro body. The dummy
argument names used for the macro body include all of the text macros defined at that
time. Here's its syntax:
%maero_body_line
macro_body_line represents the macro body to use for the immediate macro expansion;
for example:
Chap t er 14, Us i n9 mac r0 s 171
SEGSIZE EQU <TINY>
LANGUAGE EQU .<WINDOWS PASCAL>
%MODEL SEGSIZE,LANGUAGE ;Produces MODEL TINY,WINDOWS PASCAL
Including multiline macro expansions in the list file
Multiline macro expansions are not normally included in the listing file. However,
Turbo Assembler provides the following directives that let you list macro expansions:
• .lAll
• .SAll
• .XAll
• %MACS
• %NOMACS
Refer to Chapter 17 for more details onthese directives.
Saving the current operating state
The PUSHSTATE directive saves the current operating state on an internal stack that is
16 levels deep. PUSHSTATE is particularly useful if you have code inside a macro that
functions independently of the current operating state, but does not affect the current
operating mode.
Note that you can use PUSHSTATE outside of macros. This can be useful for include
files.
The state information that Turbo Assembler saves consists of:
• current emulation version (for example, T310)
• mode selection (for example, IDEAL, MASM, QUIRKS, MASM51)
• EMUL or NOEMUL switches
• current processor or coprocessor selection
• MULTERRS or NOMULTERRS switches
• SMART or NOSMART switches
• the current radix
• JUMP? or NOJUMPS switches
• LOCALS or NOLOCALS switches
• the current local symbol prefix
Use the POPSTATE directive to return to the last saved state from the stack.
Here's an example of how to use PUSHSTATEand POPSTATE.
; PUSHSTATE and POPSTATE examples
ideal
model small
codeseg
172 TurboA sse mbIer Use r's Guide
jumps
locals @@
Show changing processor selection, number radix, and JUMPS mode
pushstate
next1:
nojumps
radix
p386
jl
mov
popstate
next1
eax,100
Set to binary radix
No extra NOPS after this
Now 100 means binary 100 or 4 decimal.
Restores JUMPS and non 386 mode.
Back to jumps directive, no 386, and decimal radix
jl next2 Three extra NOPS to handle JUMPS
xor eax, eax
mov cx,100
pushstate
MULTERRS
mov ax, [bp+abc
popstate
mov ax, [bp+abc
Not in 386 mode anymore!
Now 100 means decimal 100
i Show disabling local scoping of symbols
locals
next2:
@@a: loop @@a
next3:
@@a: loop @@a Allowed because of scoping of NEXT2: and NEXT3:
pushstate
nolocals
next4 :
@@b: loop @@b
next5:
@@b: loop @@b This will conflict because of nolocals
popstate
i Show changing local symbol prefix and MASM/IDEAL mode
pushstate
masm
locals @$
testproc proc
jmp @$end
@$end: nop
@@end: ret
testproc endp
testproc2 proc
jmp @$end
MASM mode for procedure declaration
@$end: nop This doesn't conflict with label in
TESTPROC
@@end: ret This label does conflict
Chap ter 14, Us i n9 mac r0 s 173
testproc2 endp
popstate
; Now back to @@ as a local label prefix, arid IDEAL mode
testproc2b proc This won't work since we are back in
IDEAL mode!
ret
testproc2b endp
proc testproc3
jmp @$end2
@$end2: nop
@@end2: ret
endp testproc3
proc testproc4
jmp @$end2
@$end2: nop'
@@end2: ret
endp testproc4
end
174 Turbo Assembler User's Guide
And this will give an error also.
This label does'conflict
This label doesn't conflict with
label in TESTPROC3
Using conditional directives
There are two classes of conditional directives: conditional assembly directives and
conditional error-generation directives. With conditional assembly directives, you can
control which code gets assembled in your program under certain conditions.
Conditional error-generation directives let you generate an assembly-time error
message if certain conditions occur. Turbo Assembler displays the error message on the
screen and in the listing file, and it acts like any other error message in that it prevents
the emission of an object file. This chapter describes how you can use the available
conditional directives.
General conditional directives syntax
The three types of conditional assembly directives are IFxxx directives, ELSEIFxxx
directives, and ERRxxx directives. Use these directives as you would conditional
statements in high-level languages.
IFxxx conditional assembly directives
You can use IFxxx conditional assembly directives to define blocks of code that are
included in the object file if certain conditions are met (such as whether a symbol is
defined or set to a particular value). Here's the syntax of a conditional assembly
statement:
or
IFxxx
true_conditional_body
ENDIF
IFxxx
true_conditional_body
ELSE
Chap t er 15, Us i n9 con dit ion aI dire ct ives 175
false_conditional_body
ENDIF
Here, IFxxx represents any of the following <:onditional assembly directives:
• IF • IFNB
• IFl • IFIDN
• IF2 • IFIDNI
• IFDEF • IFDIF
• IFNDEF • IFDIFI
• IFB
Each IFxxx conditional assembly directive specifies a specific condition that evaluates
to either true or false. If the condition is true, the block of assembly code in
true_conditionaCbody is assembled into the output object file. If the condition evaluates
to false, Turbo Assembler skips over true_conditionaCbody and does not include it in the
object file. If there is an ELSE directive, thefalse_conditionaCbody is assembled into the
object file if the condition is false; it's ignored if the condition is true. All conditionals are
terminated with an ENDIF directive.
Note Except for the special cases of IFl and IF2 (which are discussed later), the two bodies of
code are mutually exclusive: Either true_conditionaCbody will be included in the object
file orfalse_conditionaCbody, but never both. Also, if you use the IFxxx...ELSE...ENDIF
form, one of the two bodies will be included in the generated object file. If only the
IFxxx...ENDIF form is used, true_conditionaCbody mayor may not be included,
depending on the condition. .
When you nest IFs and ELSEs, ELSE always pairs with the nearest preceding IF
directive.
In this example, test is a symbol that flags the inclusion of test code (if the symbol is
defined, then test code is generated). color is a symbol set to nonzero if the display is
color, or 0 for a monochrome display.
The actual code generated depends on these values:
IFDEF test
itest code 1
IF color
icolor code
ELSE
iffiono code
ENDIF
itest code 2
ELSE
inon-test code
ENDIF
176 Turbo Assembler User's Guide
iT if test defined
i if test defined
iT if color <> 0
if color <> 0
if color = 0
if test defined
if test not defined
code: test code 1
mono code
test code 2
test code 1
color code
test code 2
non-test code non-test code
Note If test is undefined, neither the color nor monochrome debug code based on the value of
color is assembled, as this lies entirely within the conditional assembly for a defined test.
ELSEIFxxx conditional assembly directives
You can use the ELSEIFxxx as a shortcut where multiple IFs are required. ELSEIFxxx is
equivalent to an ELSE followed by a nested IFxxx, but provides more compact code. For
example,
IF mode EQ 0
;mode 0 code
ELSEIF mode LT 5
imode 1-4 code
ELSE
imode 5+ code
ENDIF
compares to
IF mode EQ 0
;mode 0 cod~
ELSE
IF mode LT 5
imode 1-4 code
ELSE
imode 5+ code
ENDIF
ENDIF
You can't use the ELSEIFxxx directives outside of an IFxxx statement.
ERRxxx error-generation directives
ERRxxx directives generate user errors when certain conditions are met. These
conditions are the same as for the IFxxx conditional assembly directives.
Here's thegeneral syntax:
ERRxxx [arguments] [message]
Chapter 15, Using conditional directives 177
In this case, ERRxxx represents any of the conditional error-generating directives (such
as ERRIFB, .ERRB, and so on).
arguments represents argwnents that the directive might require to evaluate its
condition. Some directives require an expression, some require a symbol expression,
and some require one or two text expressions. Other directives require no arguments
at all.
If message is included, it represents an optional message that's displayed along with the
error. The message must be enclosed in single (') or double (") quotation marks.
The error-generating directives generate a user error that is displayed onscreen and
included in the listing file (if there is one) at the location of the directive in your code. If
the directive specifies a m,essage,it displays on the same 1.ifle immediately following the
error. For example, the directive .
ERRIFNDEF foo "foonot defined!"
generates the error
User error: "foo not defined!"
if the·symbolfaa is not defined when the directive is encountered. No error would be
generated in this case iffaa were already defined.
Specific directive descriptions
Unconditional error-generation directives
The unconditional error-generation directives are ERR and .ERR. These directives
always generate an error and require no ar~ents, although they can have an optional
message. You can,only use .ERR in MASM mode.
Expression-conditional directives
These directives provide conditional assembly or error generation based on the results
of evaluating a Turbo Assembler expression. For all of these directives, the expression
must evaluate to a constant and can't contain any forward references. If it evaluates to 0,
Turbo Assembler considers the expression to be false; otherwise, it considers the
expression to be true.
The following table shows conditional assembly directives that use expressions.
Table 15.1 Conditional assembly directives using expressions
IF expression
IFE expression
ELSEIF expression
ELSEIFE expression
178 Turbo Assembler User:s Guide
. Expression evaluates to true.
Expression evaluates to false.
Expression evaluates to true.
Expression evaluates to false.
The following table shows the error-generation directives that use expressions.
Table 15.2 Error-generation directives using expressions
~~~cijjte
ERRIF expression
.ERRNZ expression
ERRIFE expression
.ERRE expression
Expression evaluates to true.
Expression evaluates to true (MASM mode only).
Expression evaluates to false.
Expression evaluates to false (MASM mode only).
Symbol-definition conditional directives
These directives provide conditional assembly or error generation based on whether
one or more symbols are defined. These symbols are organized into a symboCexpression.
A symboCexpressionis an expression made up of symbol names, the Boolean operators
AND, OR, and NOT, and parentheses. In a symboCexpression, each symbol name is
treated as a Boolean value that evaluates to true if the symbol currently exists, or false if
the symbol does not exist (even if it's defined later in the module). Turbo Assembler
combines these values using the Boolean operators to produce a final true or false result.
In its simplest form, a symbol expression consists of a single symbol name and evaluates
to true if the symbol is defined. The parsing and syntax rules for symboCexpression are
similar to those for other Turbo Assembler expressions.
For example, if the symbolfoo is defined but the symbol bar is not, the following symbol-
expression evaluations are returned:
Table 15.3 Evaluation of defined and undefined symbol
foo
bar
notfoo
not bar
foo OR bar
foo AND bar
NOT (foo AND bar)
NOTfoo OR NOT bar
True
False
False
True
True
False
True
True (same as "(NOTfoo) OR (NOT bar)")
The directives that control assembly and use symboCexpressions are shown in the
following table.
Table 15.4 Symbol-expression directives using symbol_expr
IFDEF symboCexpr
IFNDEF symboCexpr
ELSEIFDEF symboCexpr
ELSEIFNDEF symbol;,..expr
symboCexpr evaluates to true.
symboCexpr evaluates to false.
symboCexpr evaluates to true.
symboCexpr evaluates to false.
Chap t er 15, Us i n9 con dit ion aI dire cti ves 179
The error-generation directives that use symboCexpressions are shown in the following
table.
Table 15.5 Error-generation directives
.......~.."u. ....,I ...... symboCexpr
.ERRDEF symboCexpr
ERRIFNDEF symboCexpr
.ERRNDEF symboCexpr
symboCexpr evaluates to true.
symboCexpr evaluates to true (MASM mode only).
symboCexpr evaluates to false. '
symboCexpr evaluates to false (MASM mode only).
For example, the following error-generating conditionals are equivalent, and would
generate an error only if bothfaa and bar are currently defined:
ERRIFDEF foo AND bar
ERRIFNDEF NOT ( foo AND bar )
ERRIFNDEF NOT foo OR NOT bar
Text-string conditional directives
These directives provide conditional assembly or error generationbased on the contents
of text_string. A text_string can be either a string constant delineated by brackets « » or
a text macro name preceded by a percent sign (%). For example,
<ABC>
%foo
; text string ABC
; the contents of text macro foo
Note See Chapter 14 for information about how to define and manipulate text macros.
The conditional assembly directives that use text_string are shown in the following table:
Table 15.6 Conditional assembly directives using texCstrings
IFNB txt_str txt-,-str is not blank.
IFB txt_str txt_str is blank (empty).
IFIDN txt_strl, txt_str2
IFIDNI txt_strl, txt_str2
IFDIF txt_strl, txt_str2
IFDIFI txt_strl, txt_str2
ELSEIFNB txt_str
ELSEIFB txt_str
ELSEIFIDN txt_strl, txt_str2
ELSEIFIDNI txt_strl, txt_str2
ELSEIFDIF txCstrl, txt_str2
ELSEIFDIFI txt_strl, txt_str2
180 TurboA sse mbIer Use r' s Guide
txt_strl and txt,-str2 are identical text strings.
txt_strl and txt_str2 are identical text strings, ignoring case distinctions.
txt_strland txCstr2 are different text strings.
txt_strl and txt_str2 are different text strings, ignoring case distinctions.
txt_str is not blank.
txt_str is blank (empty).
txt~strl and txt_str2 are identical text strings.
txt_strl and txt_str2 are identical text strings, ignoring case distinctions.
txt_strl and txt_str2 are different text strings.
txt_strl and txt_str2 are different text strings, ignoring case distinctions.
The error-generation directives that use text_string are shown in Table 15.7:
Table 15.7 Error-generation directives using texCstrings
.ERRNB txt_str
ERRIFB txt_str
.ERRB txt_str
ERRIFIDN txt_strl, txt_str2
.ERRIDN txt_strl, txt_str2
ERRIFIDNI txt_strl, txCstr2
.ERRIDNI txt_strl, txt_str2
ERRIFDIF txt_strl, txt_str2
.ERRDIF txt_strl, txt_str2
ERRIFDIFI txCstrl, txCstr2
.ERRDIFI txt_strl, txt_str2
txt_str is not blank.
txt_str is not blank (MASM mode only).
txCstr is blank (null).
txt_str is blank (MASM mode only).
.txt_strl and txt_str2 are identical text strings.
txt_strl and txt_str2 are identical text strings (MASM mode only).
txt_strl and txt_str2 are identical text strings, ignoring case distinctions.
txt_strl and txt_str2 are identical text strings, ignoring case distinctions
(MASM mode only).
txt_strl and txt_str2 are different text strings.
txt_strl ahd txt_str2 are different text strings (MASM mode only).
txt_strl and txt_str2 are different text strings, ignoring case distinctions.
txt_strl and txt_str2 are different text strings, ignoring case distinctions
(MASM mode only).
Use these directives to check the arguments passed to macros. (Note that they are not
restricted to use within macros.)
When used within a macro definition, IFB and IFNB can determine whether you've
supplied the proper number of arguments to the macro. When invoking a macro, Turbo
Assembler does not generate an error message if you've supplied too few arguments;
instead, the unspecified arguments are blank. In this way, you can define a macro that
may take arguments. For example,
load MACRO addr,reg
IFNB <reg>
MOV reg,addr
ELSE
MOV aX,addr
ENDIF
ENDM
You could invoke this example with load test, ex, which would generate a mov ex, test
instruction (or invoke simply load test, which will generate a mov ax, test instruction
because the second parameter is blank). Alternately, you could use ERRIFB to generate
an error for a macro invocation with a missing critical argument. Thus,
load MACRO addr
ERRIFB <addr>
MOV aX,addr
ENDM
Chap t er 15, Us in 9 con dit ion aI dire ct ives 181
generates an error when invoked with load, but would not when invoked with load
test.
Assembler-pass conditionals
These directives provide conditional assembly or error generation based on the current
assembly pass:
IFl
IF2
ERRIFl
.ERRl
ERRIF2
.ERR2
Assembler pass 1
Assembler pass 2
Assembling pass 1
Assembling pass 1 (MASM mode only)
Assembling pass 2
Assembling pass 2 (MASM mode only)
Normally, Turbo Assembler acts as a single-pass assembler. If you use Turbo
Assembler's multi-pass capability (invoked with the 1m command-line switch),
multiple passes are used if necessary.
Since there is always at least one pass through the assembler, the IF1 conditional
assembly directive will always assemble the code in its conditional block, and the .ERR1
and ERRIF1 directives will always generate an error (but only during the first assembly
pass).
If you use any of these directives and have not enabled multiple pa~ses, Turbo
Assembler will generate Pass dependent construction warnings for all of these directives
to alert you to a potentially hazardous code omission. If you enable multiple passes,
Turbo Assembler will perform exactly two passes, artdwill generate the warning
Maximum compatibility pass was done
Including conditionals in the list file
Normally, false conditional assembly code is not included in a listing file. You can
override this through the use of assembler directives and command-line switches.
Note See Chapter 2 and Chapter 17 for further information on this subject.
182 TurboA sse mbIer Use r 's Guide
Interfacing with the linker
Modular programs are typically constructed from several independent sections of code,
called modules. The compiler processes each of these modules independently, and the
linker (TLINK) puts the resulting pieces together to create an executable file. The
README file explains where you can find information about how to use TLINK, but it's
also important to know how to define and include all the files and libraries you might
want prior to linking. This chapter describes how to do these things.
Publishing symbols externally
You may find that you'll need to use some variables and procedures in all of your
program modules. Turbo Assembler provides several directives that let you define
symbols and libraries so that you can use them globally, as well as use communal
variables (which the linker allocates space for). You'll also have to be careful about how
you name your symbols, since different languages have particular requirements. The
next few sections discuss these directives and naming requirements.
Conventions for aparticular language
When you name symbols that you plan to use externally, remember to use the language
specifier'for your particular language. These requirements for variable names are:
• Pascal
• C/C++
uppercase characters
name must start with _.
Rest of name should be in lowercase characters Lname).
When you specify a language in the MODEL directive or in the PROC declaration, or
declare the language in a symbol's PUBLIC declaration, Turbo Assembler will
automatically use the proper naming conventions for that language, as follows:
Chap t er 16, In t erf acin9 wit h the lin ker 183
• C, CPP, and PROLOG use the CjC++ naming conventions.
• BASIC, PASCAL, FORTRAN, and NOLANGUAGE languages use the Pascal
naming conventions.
• SYSCALL specifies C calling conventions, but without prepending underscores to
symbol names (like Pascal naming conventions).
• .STDCALL uses C calling conventions for procedures with variable arguments, and
Pascal calling conventions for procedures with fixed arguments. It always uses the C
naming convention;
The Iml switch (described in Chapter 2) tells Turbo Assembler to treat all symbol names
as case sensitive. The/rnx switch (also described in Chapter 2) tells the assembler to treat
only external and public symbols as case sensitive, and that all other symbols within the
source file are uppercase. When you use these two switches together, they have a special
meaning for symbols declared as Pascal: These switches cause the symbols in question
to be published as all uppercase to the linker.
Declaring public symbols
When you declare a public symbol, you intend it to be accessible from other modules.
The following types of symbols can be public:
• data variable names
• program labels
• numeric constants defined with EQU
You can use the PUBLIC directive to define public symbols. Its syntax follows:
PUBLIC [language] symbol [, [language] symbol] '"
Notice that in order to use public symbols outside the module where they're defined,
you must use the EXTRN directive.
language is either C, CPP, PASCAL, BASIC, FORTRAN, PROLOG, or NOLANGUAGE,
and defines any language-specific conventions to be applied to the symbol name. Using
a language in the PUBLIC directive temporarily overrides the current language setting
(the default, NOLANGUAGE, or one that you've established with .MODEL).
Turbo Assembler publishes symbol in the object file so that other modules can access it. If
you don't make a symbol public, you can access it 0nly from the current source file; for
example: . . .
PUBLIC XYPROC
XYPROC PROC NEAR
Declaring library symbols
;make procedure public
You can also use symbols as dynamic link entry points for a dynamic link library. Use
the PUBLICDLL directive to declare symbols to be accessible this way. Here's its
syntax:
PUBLICDLL [language] symbol [, [language] symbol] ",
184 TurboA sse mbIer Use r' s Guide ,
Turbo Assembler publishes symbol in the object file as a dynamic link entry point (using
EXPDEF and IMPDEF records) so that it can be accessed by other programs. language
causes any language-specific conventions to be applied to the symbol name. Valid
language specifiers are C, PASCAL, BASIC, FORTRAN, PROLOG, and
NOLANGUAGE.
Here's an example of code using PUBLICDLL:
PUBLICDLL XYPROC
XYPROC PROC NEAR
;make procedure XYPROC
;accessible as dynamic link entry point
Defining external symbols
External symbols are symbols that are defined outside a module, that you can use
within the module. These symbols must have been declared using the PUBLIC
directive. EXTRN has the following syntax:
EXTRN definition [,definition] ...
definition describes a symbol and has the following format:
[language] name [[countl]] :complex_type [:count2]
Defining global symbols
Global symbols function like public symbols, without your having to specify a PUBLIC
or an EXTRN. If the variable is defined in the module, it functions like PUBLIC. If not, it
functions like EXTRN. You can use the GLOBAL directive to define global symbols.
GLOBAL has the same syntax as PUBLIC and EXTRN (see the previous few sections
for syntax descriptions.)
GLOBAL lets you have an INCLUDE file included by all source files; the INCLUDE file
contains all shared data defined as global symbols. When you reference these data items
in each module, the GLOBAL definition acts as an EXTRN directive, describing how
the data is defined in another module.
You must define a symbol as GLOBAL before you first use it elsewhere in your source
file. Also note that each argument of GLOBAL accepts the same syntax as an argument
ofEXTRN.
Here's an example:
GLOBAL X:WORD, Y:BYTE
X DW 0
mov al, Y
;made public for other module
;Y is defined as external
Publishing aprocedure prototype
If you're using version T320 or later and you use PROCDESC to describe a procedure
prototype, Turbo Assembler treats the procedure name as if it were a GLOBAL symbol.
If you've defined the procedure within the module, it is treated as PUBLIC. Otherwise,
Turbo Assembler assumes it to be EXTRN.
Chap t er 16, In t erfa ci n9 wit h the lin ker 185
You can place PROCDESC directives in an include file. When you reference the
procedure name in the module, PROCDESC acts as an EXTRN directive, describing
how the procedure is defined in another module. If the procedure is defined in the
module,PROCDESC acts as a PUBLIC directive to publish the procedure.
Defining communal variables
Communal variables function like external variables, with a major difference:
communal variables are allocated by the linker. Communal variables are actually like
global variables, but you can't assign them initial values. These uninitialized variables
can be referenced from multiple modules. '
One drawback to using communal variables is that there's no guarantee they'll appear
in consecutive memory locations. If this is an issue for you, use global variables instead.
You can use the COMM directive to define a communal ~ariable. Here's its syntax:
COMM definition [, definition] ...
Each definition describes a symbol and has the following format:
[distance] [language] symbolname[[countll] :complex_type [:count2]
distance is optional and can be either NEAR or FAR. Ifyou don't specify a distance, it will
default to the size of the default data memory model. If you're not using the simplified
segmentation directives, the default size is NEAR. With the tiny, small, and medium
models, the default size is also NEAR; all other models are FAR.
language is either C, PASCAL, BASIC; FORTRAN, PROLOG, or NOLANGUAGE.
Using a language in the COMM directive temporarily overrides the currentlanguage
setting (default or one established with .MODEL). Note that you don't need to have a
.MODEL directive in effect to use this feature.
symbolname is the symbol that is to be communal and have storage allocated at li1)k time.
symbolname can also specify an array element size multiplier countl to be included in the
total space computation. If distance is NEAR, the linker uses countl to calculate the total
size of the array. If distance is FAR, the linker uses count2 to indicate how many
elements there are of size countl timesthe.basic element size (determined by type).
countl defaults to a value of l.
complex_type is the data type of the argument. It can be either a simple type, or a complex
pointer expression. See Chapter 5 for more information about the syntax of complex
types.
The optional count2 specifies how many items this communal symbol defines.If you do
not specify a count2, a value of 1 is assumed. The total space allocated for the communal
variable is count2 times the length specified by the type field times countl.
In MASM mode, communal symbols declared outside of any segment are presumed to
be reachable using the DS register, which may not always be avalid assumption. Make
sure thatyou either place the correct segment value in DS or use an explicit segment
override when referring to these variables. In Ideal mode, Turbo Assembler correctly
checks for whether the commUnal variable is addressable, using any of the current
s~gment registers as described with the ASSUME directive.
186 TurboA sse mbIe r Use r's Guide
Here's an example using the COMM directive.
COMM buffer:BYTE:512
COMM abc[41] :WORD:l0
COMM FAR abc[41] :WORD:l0
Including alibrary
j512 bytes allocated at link time
j820 bytes (10 items of 41 words
jeach) allocated at link time
jl0 elements of 82 bytes (2 bytes
jtimes 41 elements) allocated at
jlink time
For the times when you know that your source file will always need to use routines in a
specified library, you can use the INCLUDELIB directive. Using INCLUDELIB also
prevents you from having to remember to specify the library name in the linker
commands; INCLUDELIB tells the linker to include a particular library. The
appropriate syntaxes for this directive are:
Ideal mode:
INCLUDELIB "filename" jnote the quotes!
MASMmode:
INCLUDELIB filename
filename is the name of the library you want the linker to include at link time. If you don't
supply an extension withfil(!name, the linker assumes .LIB.
Here's an example:
INCLUDELIB "diskio" jincludes DISKIO.LIB
The ALIAS directive
Turbo Assembler supports ALIAS to allow the association of an alias name with a
substitute name. When the linker encounters an alias name, it resolves the alias by
referring to the substitute name.
Chap t er 16, In t erfa ci n9 wit h the lin ker .187
188 Turbo Assembler User's Guide
Generating alisting
A listing file is useful if you want to see exactly what Turbo Assembler generates when
each instruction or directive is assembled. The file is basically the source file annotated
with a variety of information about the results of the assembly. Turbo Assembler lists
the actual machine code for each instruction, along with the offset in the current
segment of the machine codefor each line. What's more, Turbo Assembler provides
tables of information about the labels and segments used in the program, including the
value and type of each label, and the attributes of each segment. For additional
information on creating listings, refer to the /1 and /Ia command-line switches
documented in Chapter 2.
Turbo Assembler can also, on demand, generate a cross-reference table for all labels
used in a source file, showing you where each label was defined and where it was
referenced. See the Ie command-line option in Chapter 2 for more information on
generating cross-reference tables.
Listing format
The top of each page of the listing file displays a header consisting of the version of
Turbo Assembler that assembled the file, the date and time of assembly, and the page
number within the listing.
There are two parts to the listing file: the annotated source code listing and the symbol
tables. The original assembly code is displayed first, with a header containing the name
of the file where the source code resides. The assembler source code is annotated with
information about the machine code Turbo Assembler assembled from it. Any errors or
warnings encountered during assembly are inserted immediately following the line
they occurred on.
The code lines in the listing file follow this format:
<depth> <line number> <offset> <machine code> <source>
<depth> indicates the level of nesting of Include files and macros within your listing file.
Chap t er 17, Genera tin 9 a lis tin 9 189
<line number> is the number of the line in the listing file (not including header and title
lines). Line numbers are particularly useful when the cross-:reference feature of Turbo
Assembler, which refers to lines by line number, is used. Be aware that the line numbers
in <line number> are not the source module line numbers. For example, if a macro is
expanded or a file is included, the line-number field will continue to advance, even
though the current line in the source module stays the same. To translate a line n.umber
(for example, one that the cross-referencer produced) back to the source file, ypu must
look up the line number in the listing file, and then find that same line (by eye, not by
number) in the source file.
<offset> is the offset in the current segment of the start of the ~achine code generated by
the associated assembler source line.
<machine code> is the actual sequence of hexadecimal byte and word values that is
assembled from the associated assembler source line.
<source> is simply the original assembler line, comments and all. Some assembler lines,
suchas those that contain only comments, don't generate any machine code; these lines
have no <offset> or <machine code> fields, but do have a line number.
General list directives
There are a variety of list directives that let you control what you want in your listing
file. The general list directives follow:
• .LIST ;MASM mode only
• .XLIST ;MASM mode only
• %LIST
• %NOLIST
• %CTLS
• %NOCTLS
• %SYMS
• %NOSYMS
The %LIST directive shows all of the source lines in your listing. This is the default
condition when you create a listing file. To tum off the display of all the source lines, use
the %NOLIST directive. Here's an example:
%NOLIST
INCLUDE MORE .INC
%LIST
iturn off listing
iturn on listing
The .LIST and .XLIST directives function the same way as %LIST and %NOLIST.
Here's an example:
.LIST
jrnp xyz
.XLIST
add dx,ByteVar
ithis line always listed
inbt in listing
You can use the%CTLS and %NOCTLS directives to control the listing directives.
%CTLS causes listing control directives (such as %LIST, %INCL, and so on) to be
190 TurboA sse mbIer Use r' s Guide
placed in the listing file; normally, they are not listed. It takes effect on all subsequent
lines, so the %CTLS directive itself will not appear in the listing file. %NOCTLS
reverses the effect of a previous %CTLS directive. After issuing %NOCTLS, all
subsequent listing-control directives will not appear in the listing file. (%NOCTLS is the
default listing-control mode that Turbo Assembler useswhen it starts assembling a
source file.);for example,
%CTLS
%NOLIST
%NOCTLS
%LIST
ithis will be in listing file
ithis will not appear in listing
You can .qse the %SYMS and %NOSYMS directives to cause the symbol table to either
appear or not to appear in your' listing file (the default is for it to appear). The symbol
table will appear at the end of the listing file.
Here's the syntax for %SYMS:
%SYMS
Here's the syntax for %NOSYMS:
%NOSYMS
Include file list directives
In the event that you might want to list the include files in your listing file, you can tum
this capability on and off using the %INCL and %NOINCL directives. By default, .
INCLUDE files are normally contained in the listing file. %NOINCL stops all
subsequent INCLUDE files source lines from appearing inthe listing until a %INCL is
enabled. This is useful ifyou have a large INCLUDE file that contains things such as a
lot of EQU definitions that never change.
Here's an example:
%INCL
INCLUDE DEFS.INC
%NOINCL
icontents appear in listing
INCLUDE DEF1.INC icontents don't appear
Conditional list directives
When you have conditional blocks of code in your source files, you might not want all of
that informationto appear in the listing file. Showing conditional blocks can be very
helpful in some instances when you want to see exactly how your code is behaving.
Turbo Assembler provides the following conditional list directives:
• .LFCOND ;MASM mode only
• .SFCOND ;MASM mode only
• .TFCOND ;MASMmodeonly
• %CONDS
• %NOCONDS
Chap t er 17, Genera tin 9 a lis tin 9 191
Turbo Assembler does not usually list conditional blocks.
The %CONDS directive displays all statements in conditional blocks in the listing file.
This includes the listing offalse conditional blocks in assembly listings. The .LFCOND
directive functions the sam,e as %CONDS. %NOCONDS prevents statements in false
conditional blocks from appearing in the listing file. The .SFCONDS directive functions
exactly the same as %NOCOND. If you want to toggle conditional block-listing mode,
use the .TFCOND directive.
The first .TFCOND that Turbo Assembler encounters enables a listing of conditional
blocks. If you use the IX command-line option, conditional blocks start off being listed,
and the first .TFCOND encountered disables listing them. Each time .TFCOND appears
in_ the source file, the state of false conditional listings is reversed.
To invoke any of these directives, place it by itself on a-line in your code. They will affect
the conditional blocks that immediately follow them.
Macro list directives
Macro expansions are not normally included-in listing files. Having this information in
listing files can be very helpful when you want to see what your code is :doing. Turbo
Assembler provides several directives that let tum this feature on and off. They are:
• .LALL ;MASM mode only
• .SALL ;MASM mode only'
• .XALL ;MASM mode only
• %MACS
• %NOMACS
The %MACS directive enables the listing of macro expansions. The .LALL directive
does the same thing, but only works in MASM mode. You can use these macros to
toggle macro expansion in listings on.
%MACS has the following syntax:
%MACS
You can specify .LALL as follows:
.LALL
If you want to suppress the listing of all statements in macro expansions, use either the
%NOMACS or .sALL directives. Note that you can use these directives to toggle macro
expansion in listings off.
%NOMACS has the following syntax:
%NOMACS
You can specify .sALL as follows:
.SALL
The .XALL directive, which is only available in MASM mode, lets you list only the
macro expansions that generate code or data..XALL has the syntax .XALL •
192 Turbo Assembler User's Guide
Cross-reference list directives
The symbol table portion of the listing file normally tells you a great deal about labels,
groups, and segments, but there are two things it doesn't tell you: where labels, groups,
and segments are defined, and where they're used. Cross-referenced symbol
information makes it easier to find labels and follow program execution when
debugging a program.
There are several ways of enabling cross-referencing information in your listing file. You
can tise Ie to produce cross-referencing information for an entire file (see Chapter 2 for
details), or you can include directives in your code that let you enable and disable
cross-referencing in selected portions of your listings. These directives are:
• .CREF ;MASM mode only
• .XCREF ;MASM mode only
• %CREF
• %NOCREF
• %CREFALL
• %CREFREF
• %CREFUREF
Turbo Assembler includes cross referencing information in the listing file. In addition,
you can specify a .XRF file in your Turbo Assembler command to get a separate .XRF
file.
The %CREF and .CREF directives let you accumulate cross-reference information for all
symbols encountered from that point forward in the source file. %CREF and .CREF
reverse the effects of any %NOCREF or .XCREF directives, which inhibit the collection
of cross-reference information.
%CREF and .CREF have the following syntaxes:
%CREF
or
.CREF
%NOCREF and .XCREF have the following syntaxes:
%NOCREF [symbol, ... J
or
.XCREF [symbol, ... J
If you use %NOCREF or .XCREF alone withoutspecifying any symbols, cross-
referencing is disabled completely. If you supply one or more symbol names, cross-
referencing is disabled only for those symbols.
The %CREFALL directive lists all symbols in the cross reference. %CREFALL reverses
the effect of any previous %CREFREF (which disables listing of unreferenced symbols
in the cross reference), or %CREFUREF (which lists only the unreferenced symbols in
the cross reference). After issuing %CREFALL, all subsequent symbols in the source file
Chap t er 17, Genera tin 9 a lis tin 9 193
will appear in the cross-reference listing. This is the default mode that Turbo Assembler
uses when assembling your source file.
The syntax for %CREFALL, %CREFREF, and %CREFUREF follows:
%CREFALL
%CREFREF
%CREFUREF
Changing list format parameters
The listing format control directives alter the format of the listing file. You can use these
directives to tailor the appearance of the listing file to your tastes and needs.
The PAGE directive sets the listing page height and width, and starts new pages. PAGE
onlyworks in MASM mode. PAGE has the following syntax:
PAGE [rows] Lcols]
PAGE +
rows specifies the number of lines that will appear on each listing page. The minimum is
10 and the maximum is 255. eols specifies the number of columns wide the page will be.
The minimum width is 59; the maximum is 255. If you omit either rows or eols, the
current setting for that parameter will remain unchanged. To change only the number of
columns, precede the column width with a comma; otherwise, you'll end up changing
the number of rows instead.
If you follow the PAGE directive with a plus sign (+), a new page starts, the section
number is incremented, and the page number restarts at 1. If you use PAGE with no
arguments, the listing resumes on a new page, with no change in section number.
The %PAGESIZE directive functions exactly like the PAGE directive, except that it
doesn't start a new page and that it works in both MASM and Ideal modes.
%PAGESIZE has the following syntax:
%PAGESIZE [rows] [,cols]
%NEWPAGE functions like PAGE, with no arguments. Source lines appearing after
%NEWPAGE will begin at the start of a new page in the listingfile. %NEWPAGE has
the following syntax:
%NEWPAGE
The %BIN directive sets the width of the object code field in the listing file. %BIN has
the following syntax:
%BIN size
size is a constant. If you don't use this directive, the instruction opcode field takes up 20
columns in the listing file. For example,
%BIN 12 ;set listing width to 12 columns
194 TurboA sse mbIe r Use r' s Guide
%DEPTH sets the size of the depth field in the listing file. %DEPTH has the following
syntax:
%DEPTH width
width specifies how many columns to reserve for the nesting depth field in the listing
file. The depth field indicates the nesting level for INCLUDE files and macro
expansions. If you specify a width of 0, this field does not appear in the listing file.
Usually, you won't need to specify a width of more than 2, since that would display a
depth of up to 99 without trun,cation. The default width for this field is 1 column.
%LINUM sets the width of the line-number field in the listing file. %LINUM has the
following syntax:
%LINUM size
%LINUM lets you set how many columns the line numbers take up in the listing file.
size must be a constant. If you want to make your listing as narrow as possible, you can
reduce the width of this field. Also, if your source file contains more than 9,999 lines,
you can increase the width of this field so that the line numbers are not truncated. The
default width for this field is 4 columns.
%TRUNC truncates listing fields that are too long. %TRUNC has the following syntax:
%TRUNC
The object code field of the listing file has enough room to show the code emitted for
most instructions and data allocations. You can adjust the width ofthis field with %BIN.
If a single sofuce line emits more code than can be displayed on a single line, the rest is
normally truncated and therefore notvisible. When you want to see all the code
generated, use %NOTRUNC (which word-wraps too-long fields in the listing file).
Otherwise, use %TRUNC. You can use these directives to toggle truncation on and off.
%NOTRUNC has the following syntax:
%NOTRUNC
%PCNT sets the segment:offset field width in the listing file. %PCNT has the following
syntax:
%PCNT width
where width is the number of columns you want to reserve for the offset within the
current segment being assembled. Turbo Assembler sets the width to 4 for ordinary 16-
bit segments and sets it to 8 for 32-bit segments used by the 386 processor. %PCNT
overrides these defaults.
The TITLE directive, which you can use only in MASM mode, sets the title in the listing
file. TITLE has the following syntax:
TITLE text
The title text appears at the top of each page, after the name of the source file and before
any subtitle set with the-SUBTTL directive. You can use TITLE as many times as you
want.
Chapter 17, Generating a listing 195
%TITLE functions like TITLE, but you can use it for either MASM or Ideal mode.
%TITLE has the following syntax:
%TITLE II text"
SUBTTL, which only works in MASM mode, sets the subtitle in the listing file. SUBTTL
has the following syntax:
SUBTTL text
The subtitle appears at the top of eachpage, after the name of the source file, and after
any title set with TITLE.
You can place as many SUBTTL directives in your program as you wish. Each directive
changes the subtitle that will appear at the top of the next listing page.
%SUBTTL functions like SUBTTL, but it works in both MASM and Ideal modes.
%SUBTTL has the following syntax:
%SUBTTL "text"
%TABSIZE sets the tab column width in the listing file. %TABSIZE has the following
syntax:
%TABSIZE width
width is the number of columns between tabs in the listing file. The default tab column
width is 8 columns.
You can use the %TEXT directive to set the width of the source field in the listing file.
It has the followingsyntax:
%TEXT width
width is the number of columns to use for source lines in the listing file. If the source line
is longer than this field, it will either be truncated or wrapped to the following line,
depending on whether you've used %TRUNC or %NOTRUNC.
You can use the %PUSHLCTL directive to save the listing controls on a 16-level stack.
It only saves the listing controls that can be enabled or disabled (%INCL, %NOINCL,
and so on). The listing field widths are not saved. This directive is particularly useful in
macros, where you can invoke special listing modes that disappear once the macro
expansion terminates.
%PUSHLCTL has the following syntax:
%PUSHLCTL
Conversely, the %POPLCTL directive recalls listing controls from the stack. Here's its
syntax:
%POPLCTL
%POPLCTLresetsthe listing controls to the way they were when the last
%PUSHLCTL directive was issued. None of the listing controls that set field width are
restored (such as %DEPTH, %PCNT).
196 TurboA sse mbIer Use rJ s Guide
Interfacing Turbo Assembler with
Borland C++
While many programmers can-and do-develop entire programs in assembly
language, many others prefer to do the bulk of their programming in a high-level
language, dipping into assembly language only when low-level control or very high-
performance code is required. Still others prefer to program primarily in assembler,
taking occasional advantage of high-level language libraries and constructs.
Borland C++ lends itself particularly well to supporting nuxed C++ and assembler code
on an as-needed basis, providing not one but three mechanisms for integrating
assembler and C++ code. The inline assembly feature of Borland C++ provides a quick
and simple way to put assembler code directly into a C++ function. You can assemble
the inline code with Turbo Assembler or use Borland C++'s built-in assembler. For
further information about using in-line assembly in Borland C++ or the built-in
assembler, see the Borland C++ Programmer's Guide. For those who prefer to do their
assembler programming in separate modules written entirely in assembly language,
Turbo Assembler modules can be assembled separately and linked to Borland C++
code.
First, we'll discuss the details of linking separately assembled Turbo Assembler
modules to Borland C++, and explore the process of calling Turbo Assembler functions
from Borland C++ code. Then, we'll cover calling Borland C++ functions from Turbo
Assembler code.
Calling Turbo Assembler functions from Borland C++
C++ and assembler have traditionally been mixed by writing separate modules entirely
in C++ or assembler, compiling the C++ modules and assembling the assembler
modules, and then linking the separately compiled modules together. Borland C++
modules can readily be linked with Turbo Assembler modules in this fashion.
Chap ter 18, In t erfa cin 9 TurboA sse mbIer wit h B0 rIand C++ 197
The executable file is produced from mixed c++ and assembler source files. You start
this cycle with
bee filenaml.epp filenam2.asm
This instructs Borland C++ to first compile FILENAMl.CPP to FILENAMl.OBJ~ then
invoke Turbo Assembler to assemble FILENAM2.ASM to FILENAM2.0BJ, and finally
invoke TLINK to link FILENAMl.OBJ and FILENAM~.OBJ into FILENAMl.EXE.
Separate compilation is very useful for programs that have sizable amounts of
assembler code, since it makes the full power of Turbo Assembler available and allows
you to do.your assembly language programming in a pure assembler environment,
without the asm keywords, extra compilation time, and C++-related overhead of inline
assem~ly.
There is a price to be paid for separate compilation: The assembler programmer must
attend to all the details of interfacing C++ and assembler code. Where Borland C++
handles segment specification, parameter-passing, reference to C++ variables, register
variable preservation, and the like for inline assembly, separately compiled assembler
functions must explicitly do all that and more.
There are two major aspects to interfacing Borland C++ and Turbo Assembler. First, the
various parts of the C++ and assembler code must be linked together properly, and
functions and variables in each part of the code must be made available to the rest of the
code as needed. Second, the assembler code must properly handle C-style function calls.
This includes accessing passed parameters, returning values, and following the register
preservation rules required of C++ functions.
Let's start by examining the rules for linking together Borland C++ and Turbo
Assembler code.
The framework
In order to link Borland C++ and Turbo Assembler modules together, three things must
happen: .
• The Turbo Assembler modules must use a Borland C++-compatible segment-naming
scheme.
• The Borland C++ and Turbo Assembler modules must share appropriate function
and variable names in a form acceptable to Borland C++.
• TLINK must be used to combine the modules into an exe~utable program.
This says nothing about what the Turbo Assembler modules actually do; at this point,
we're only concerned with creating a framework within which C++-compatible Turbo
Assembler functions can be written.
Linking assembly language modules with C++
Type-safe linkage is an important concept in C++. The compiler and linker must work
together to ensure function calls between modules use the correct argument types.·
A process called name-mangling provides the necessary argument type information.
198 TurboA sse mbIer Use r' s Guide
Name-mangling modifies the name of the function to indicate what arguments the
function takes.
When you build a program entirely in C++, name-mangling occurs automatically and
transparently. However; when you write a module in assemblylanguageto be linked
into a c++ program, you must be sure the assembler module contains mangled names.
You can do this easily by writing a dummy function in C++ and compiling it to
assembler. The .ASM file that Borland C++ generates will have the proper mangled
names. You use these names when you write the real assembler module.
For example, the following coq.e fragment defines four different versions of the function
named test:
void test ()
{
}
voidtest( int )
{
}
void test( int, int )
{
}
void test( float, double)
{
}
Ifthe code is compiled using the -5 option, the compiler produces an assembly language
output file (.ASM). This is how the output looks (edited to remove extraneous details):
void test ()
@test$qv proc near
push bp
mov bp, sp
pop bp
ret
@test$qv endp
void test( int
@test$qi proc near
push bp
mov bp, sp
pop bp
ret
@test$qi endp
void test( int, int )
@test$qii proc near
push bp
mov bp,sp
pop bp
ret
@test$qii endp
void test( float, double)
Chapter 18, Interfacing Turbo Assembler with Borland C++ 199
@test$qfd proc near
push bp
mov bp, sp
pop bp
ret
@test$qfd endp
Using Extern "e" to simplify linkage
If you prefer, you can use unmangled names for your assembler functions, instead of
trying to figure out what the mangled names would be. Using unmangled names will
protect your assembler functions from possible future changes in the name-mangling
algorithm. Borland C++ allows you to define standard C function names in your C++
programs.
Look at this example:
extern "e" {
int add(int *a,int b) ;
Any functions dedared within the braces will be given C style names. Here is the
~atching assembler procedure.definition.
public _add
_add proc
Declaring an assembler function with an extern "c" block can save you the trouble of
determining what the mangled names will be. Your code will be more readable, also.
Memory models and segments
For a given assembler function to be callable from C++, that function must use the same
memory model as the C++ program and must use a C++-compatible code segment.
Likewise, in order for data defined in an assembler module to be accessed by C++ code
(or for C++ data to be accessed by assembler code), the assembler code must follow C++
data segment-naming conventions.
Memory models and segment handling can be quite complex to implement in
assembler. Fortunately, Turbo Assembler does virtually all the work of implementing
Borland C++-compatible memory models and segments for you in the form of the
simplified segment directives.
Simplified segment directives and Borland C++
The .MODEL directive tells Turbo Assembler that segments created with the simplified
segment directives should be compatible with the selected memory model (tiny, small,
compact, medium, large, huge, or tchuge), and controls the default type (near or far) of
procedures created with the PROC directive. Memory models defined with the
.MODEL directive are compatible with the equivalently named Borland C++ models
except that you should use Turbo Assembler's tchuge memory model when you want '"
to support Borland C++'s huge memory model. (The huge memory modelis more
appropriate for compatibility withother C compilers.) You should use theFARSTACK
modifier with the .MODEL directive for large model, so the stack does not become a
part of DGROUP.
200 TurboA sse mbIer Use r's Guide
Finally, the .CODE, .DATA, .DATA?, .FARDATA, and .FARDATA? simplified
segment directives generate Borland C++-compatible segments. (Don't use .DATA?
orFARDATA? in huge model as they do not exist in Borland C++.)
For example, consider the following Turbo Assembler module, named
DOTOTAL.ASM:
select Intel-convention segment ordering
.MODEL small iselect small model (near code and data)
.DATA iTC-compatibleinitialized data segment
EXTRN _Repetitions:WORD iexternally defined
PUBLIC _StartingValue iavailable to other modules
_StartingValue DW 0
.DATA? iTC-compatibleuninitialized data segment
RunningTotal
.CODE
PUBLIC
_DoTotal
mov
mov
mov
TotalLoop:
inc
loop
mov
ret
Do't'otal
END
DW
iTC-compatible code segment
DoTotal
PROC ifunction (near-callable in small model)
ex, [_Repetitions] i# of counts to do
ax, [_StartingValue]
[RunningTotal] ,ax iset initial value
[Rumi.ingTotal]
TotalLoop
ax, [RunningTotal]
ENDP
iRunningTotaltt
ireturn final total
The assembler procedure _DoTotal is readily callable from a small-model Borland C++
program with the statement
DoTotal()i
Note that _DoTotal expects some other part of the program to define the external
variable Repetitions. Similarly, the variable StartingValue is made public, so other
portions ofthe program can access it. The following Borland C++ module,
SHOWTOT.CPP, accesses public data in DOTOTAL.ASM and provides external data
to DOTOTAL.ASM:
#include <stdio.h>
extern "C" int DoTotal(void) i
extern int StartingValuei
int Repetitionsi
int main()
{
Repetitions =10i
StartingValue = 2i
printf("%dn", DoTotal()) i
return 0i
Chap t er 18, Int erf aci n9 TurboA sse mbIer wit h B0 rIand C++ 201
StartingValue doesn't have to go in the Extern nCff block because variable names are not
mangled.
To create the executable program SHOWTOT.EXE from SHOWTOT.CPP and
DOTOTAL.ASM, enter the command line
bcc showtot.cpp dototal.asm
If you wanted to link _DoTotal to a compact-model C++ program, you would simply
change the .MODEL directive to .MODEL COMPACT. If you wanted to use a far
segment in DOTOTAL.ASM, you could use the .FARDATA directive.
In short, generating the correct segment ordering, memory model, and segment names
for linking with Borland C++ is easy with the simplified segment directives.
Old-style segment directives and Borland C++
Simply put, it's a nuisance interfacing Turbo Assembler code to C++ code using the old-
style segment directives. For example, 1£ you replace the simplified segment directives
in DOTOTAL.ASM with old-style segment directives, you get
DGROUP GROUP _DATA,_BSS
DATA SEGMENT WORD PUBLIC 'DATA'
EXTRN _Repetitions:WORD
PUBLIC _StartingValue
-,-StartingValue DW 0
DATA ENDS
BSS SEGMENT WORD PUBLIC 'BSS'
RunningTotal DW?
BSS ENDS
TEXT SEGMENT BYTE PUBLIC 'CODE'
iexternally defined
iavailable to other modules
ASSUME es:_TEXT,ds:DGROUP,ss:DGROUP
PUBLIC DoTotal
_DoTotal PROC
mov ex, [_Repetitions]
. mov ax, [_StartingValue]
mov [RunningTotal],ax
TotalLoop:
inc [RunningTotal]
loop TotalLoop
mov ax, [RunningTotal]
ret
_DoTotal ENDP
TEXT ENDS
END
ifunction (near-callable.
i in small model)
i# of eo.unts to do
iset initial value
iRunningTotal++
ireturn final total
The version with old~style segment directives is not only longer, but also much harder
to read and harder to change to match a different C++ memory model. When you're
interfacing to.Borland C++, there's generally noadvantage to using the old-style
segment directives. If you still want to use the old-style segment directives when
interfacing to Borland C++j you'll have to identify the correct segments for the membry
model your C+-:1- code uses. .
202 TurboA sse mbIer User .s Guide
Note. The easy way to determine the appropriate old-style segment directives for linking with
a given Borland C++ program is to compile the main module of the Borland C++
program in the desired memory model with the -5 option. This causes Borland C++ to
generate an assembler version of the C++ code. In that C++ code, you'll find all the old-
style segment directives used by Borland C++; just copy them into your assembler code.
You can also find out what the appropriate old-style directives are by compiling as you
normally would (without the -5 option) and then using TDUMP, a utility that comes
with Turbo Assembler, to display all the segment definition records. Use this command
line:
tdump -OIsegdef module.obj
Segment defaults: When is it necessary to load segments?
Under some circumstances, your C++-callable assembler functions might have to load
DS and/or ES in order to access data. It's also useful to know the relationships between
the settings of the segment registers on a call from Borland C++, since sometimes
assembler code can take advantage of the equivalence of two segment registers. Let's
take a moment to examine the settings of the segment registers when an assembler
function is called from Borland C++, the relationships between the segment registers,
and the cases in which an assembler function might need to load one or more segment
registers.
On entry to an assembler function from Borland C++, the CS and DS registers have the
following settings, depending on the memory model in use.(SS is always used for the
stack segment, and ES is always used as a scratch segment register):
Table 18.1 Register settings when Borland C++ enters assembler
Tiny _TEXT DGROUP
Small _TEXT DGROUP
Compact _TEXT DGROUP
Medium filename_TEXT DGROUP
Large filename_TEXT DGROUP
Huge filename_TEXT callingJilename_DATA
filenameis the name of the assembler module, and callingJilename is the name. of the
module calling the assembler module.
In the tiny model, _TEXT and DGROUP are the same, so CS equals DS on entry to
functions. Also in the tiny, small, and medium models, SS equals DS on entry to
functions.
, So, when is it necessary to load a segment register in a C++-callable assembler function?
First, you should never have to (or wantto) directly load the CS or SS registers. CS is
automatically set as needed on far calls, jumps, and returns, and can't be tampered with
otherwise, SS always points to the stack segment, which should never change during
the course of a program (unless you're writing code that switches stacks, in which case
you had best know exactly what you're doing).
Chap ter 18, Interf aci n9 TurboA ssembIer wit h B0 rIand C++ 203
ES is always available for you to use as you wish. You can use ES to point at far data, or
you can load ES with the destination segment for a string instruction.
That leaves the DS register; in all Borland C++ models other than the huge model, DS
points to the static data segment (DGROUP) on entry to functions, and that's generally
where you'll want to"leave it. You can always use ES to access far data, although you
may find it desirable to instead temporarily point DS to far data that you're going to
access intensively, thereby saving many segment override instructions in your code. For
example, you could access a far segment in either of the following ways:
or
.FARDATA
Counter DW 0
.CODE
PUBLIC _AsmFunction
AsmFunction PROC
mov aX,@fardata
mov
. inc
eS,ax
es: [Counter]
AsmFunction ENDP
.FARDATA
Counter DW 0
.CODE
PUBLIC AsmFunction
-
AsmFunction PROC
-
ASSUME ds:@fardata
mov aX,@fardata
mov ds,ax
inc [Counter]
ASSUME ds:@data
mov aX,@data
mQv ds,ax
_AsmFunction ENDP
ipoint ES to far data segment
iincrement counter variable
ipoint DS to far data segment
iincrement counter variable
ipoint DS back to DGROUP
The second version has the advantage of not requiring an ES: override on each memory
access to the far data segment. If you do load DS to point to a far segment, be sure to
restore it like in the preceding example before attempting to access any variables in
DGROUP. Even if you don't access DGROUP in a given assembler function, be sure
to restore DS before exiting since Borland C++ assumes that' functions leave OS
unchanged.
204 TurboAs sembIer Use r's Guide
Handling DS in C++-callable huge model functions is a bit different. In the huge model,
Borland C++ doesn't use DGROUP at all. Instead, each module has its own data
segment, which is a far segment relative to all the other modules in the program; there is
'no commonly shared near data segment. On entry to a function in the huge model, DS
should be set to point to that module's far segment and left there for the remainder of
the function, as follows:
.FARDATA
.CODE
PUBLIC_AsmFunction
AsmFunction PROC
push ds
mov aX,@fardata
mov ds,ax
pop ds
ret
_AsmFunction ENDP
Note that the original state of DS is preserved with a PUSH on entry to AsmFunction and
restored with a POP before exiting; even in the huge model, Borland C++ requires all
functions to preserve DS.
Publics and externals
Turbo Assembler code can call C++ functions and reference external C++ variables.
Borland C++ code can likewise call public Turbo Assembler functions and reference
public Turbo Assembler variables. Once Borland C++-compatible segments are set up in
Turbo Assembler, as described in the preceding sections, only the following few simple
rules are necessary to share functions and variables between Borland C++ and
Turbo Assembler.
Underscores and the Clanguage
If you are programming in C or C++, all external labels should start with an underscore
character C). The C and C++ compilers automatically prefix an underscore to all
function and external variable names when they're used iri C/C++ code, so you only
need to attend to underscores in your assembler code. You must be sure that all
assembler references to C and C++ functions and variables begin with underscores, and
you must begin all assembler functions and variables that are made public and
referenced by C/C++ code with underscores.
For example, the following C code (link2asm.cpp),
int ToggleFlag() i
int Flagi
" main ()
{
ToggleFlag();
Chap t er 18, Int e.r f aci n9 TurboA sse mbIer wit h B0 rIan d C++ 205
links properly with the following assembler program (CASMLINK.ASM):
.MODEL small
.DATA
EXTRN _Flag:WORD
.CODE
PUBLIC _ToggleFlag
_ToggleFlag PROC
cmp []lag] ,0
jz SetTheFlag
mov []lag], 0
jmp short EndToggleFlag
SetTheFlag:
mov' [_Flag],l'
EndToggleFlag:
ret
_ToggleFlag ENDP
END
;is the flag reset?
;yes, set it
;no, reset it
;done
;set flag
Note Labels not referenced by C code, such as SetTheFlag, don't need leading underscores.
When you use the C language specifier in your EXTRN and PUBLIC directives, as in
the following program (CSPEC.ASM),
.MODEL small
.DATA
EXTRN C Flag:word
.CODE
PUBLIC C ToggleFlag
ToggleFlag PROC
cmp [Flag] ,0
jz SetTheFlag
mov [Flag] , 0
jmp short EndToggleFlag
SetTheFlag:
mov [Flag], 1
EndToggleFlag:
ret
ToggleFlag ENDP
END
Turbo Assembler c';luses the underscores to be prefixed automatically when Flag and
ToggleFlagare published in the object module.
The Significance of uppercase and lowercase
Turbo Assembler is normally insensitive to case when handling symbolic names,
making no distinction between uppercase and lowercase letters. Since C++ is case-
sensitive, it's desirable to have Turbo Assembler be case-sensitive, at least for those
symbols that are shared between assembler and C+-i::; Iml and Imx make this possible.
The Iml command-line switch causes Turbo Assembler to become case-sensitive for all
symbols. The Imx command-line switch causes Turbo Assembler to become case- .
sensitive for public (PUBLIC), external (EXTRN), global (GLOBAL), and communal
206 TurboA sse mbIer Use r 's Guide
(COMM) symbols only. When Borland C++ calls Turbo Assembler, it uses the Iml
switch. Most of the time you should use Iml also.
Label types
While assembler programs are free to access any variable as data of any size (8 bit, 16 bit,
32 bit, and so on), it is generally a good idea to access variables in their native size. For
instance, it usually causes problems if you write a word to a byte variable:
SmallCount DB
mov WORD PTR [SmallCount] ,Offffh
Consequently, it's important that your assembler EXTRN statements that declare
external C++ variables specify the right size for those variables, since Turbo Assembler
has only your declaration to go by when deciding what size access to generate to a C++
variable. Given the statement
char c
in a C++ program, the assembler code
EXTRN c:WORD
inc [c]
could lead to problems, since every 256th time the assembler codeincremented c, c
would tum over. And, since c is erroneously declared as a word variable, the byte at
OFFSET c + 1 is incorrectly incremented, and with unpredictable results.
Correspondence between C++ and assembler data types is as follows:
unsigned char byte
char byte
enum word
unsigned short word
short word
unsigned int word
int word
unsigned long dword
long dword
float dword
double qword
long double tbyte
near * word
far * dword
Chap t er 18, Int erf aci n9 TurboA sse mbIer wit h 8 0 rIand C++ 207
Far externals
If you're using the simplified segment directives, EXTRN declarations of symbols in
far segments must not be placed within any segment, since Turbo Assembler considers
symbols declared within a given segment to be associated with that segment. This has
its drawbacks: Turbo Assembler cannot check the addressability of symbols declared
EXTRN outside any segment,and so can neither generate segment overrides as needed
nor inform you when you attempt to access that variable when the correct segment is
notloaded. Turbo Assembler still assembles the correct code for references to such
external symbols, but can no longer provide the normal degree of segment
addressability checking.
You can use the old-style segment directives to explicitly declare the segment each
external symbol is in, and then place the EXTRN directive for that symbol inside the
segment declaration. This is a lot of work, however; if you make sure that the correct
segment is loaded when you access far data, it's easiest to just put EXTRN declarations
of far symbols outside all segments. For example, suppose that FILEl.ASM contains
.FARDATA
FilelVariable DB
Then if FILEl.ASM is linked to FILE2.ASM, which contains
.DATA
EXTRN FilelVariable:BYTE
.CODE
Start PROC
mav
mav
ax/SEG FilelVariable
ds/ax
SEG FilelVariable will not return the correct segment. The EXTRN directive is placed
within the scope of the DATA directive of FILE2.ASM, so Turbo Assembler considers
FilelVariable to be in the near DATA segment of FILE2.ASM rather than in the
FARDATA segment.
The following code for FILE2.ASM allows SEG FilelVariable to return the correct
segment:
.DATA
@curseg ENDS
EXTRN FilelVariable:BYTE
.CODE
Start PROC
mav ax/SEG FilelVariable
mav dsiax
Here, the @cursegENDS directive ends the .DATA segment, so no segment directive is
,in effect when FilelVariable is declared external.
208 'T urboA ssemb Ier Use r 's Guide
Linker command line
The simplest way to link Borland C++ modules with Turbo Assembler modules is to
enter a single Borland C++ command line and let Borland C++ do all the work. Given
the proper command line, Borland C++ will compile the C++ code, invoke Turbo
Assembler to do the assembling, and invoke TLINK to link the object files into an
executable file. Suppose, for example, that you have a program consisting of the C++
files MAIN.CPP andSTAT.CPP and the assembler files SUMM.ASM and
DISPLAY.ASM. The command line
bee main.cpp stat.epp summ.asm display.asm
compiles MAIN.CPP and STAT.CPP, assembles SUMM.ASM and DISPLAY.ASM, and
links all four object files, along with the C++ start-up code and any required library
functions, into MAIN.EXE. You only need remember the .ASM extensions when typing
your assembler file names.
If you use TLINK in stand-alone mode, the object files generated by Turbo Assembler
are standard object modules and are treated just like C++ object modules. See
Appendix C for more information about using TLINK in stand-alone mode.
Parameter passing
Borland C++ passes parameters to functions on the stack. Before calling a function,
Borland C++ first pushes the parameters to that function onto the stack, starting with
the right-most parameter and ending with the left-most parameter. The C++ function
call
Test(L j, 1);
compiles to
mov ax,l
push ax
push WORD PTR DGROUP:_j
push WORD PTR DGROUP:_i
call NEAR PTR _Test
add sp,6
in which you can clearly see the right-most parameter, I, being pushed first, then j, and
finally i.
Upon return from a function, the parameters that were pushed on the stack are still
there, but are no longer useful. Consequently, immediately following each function
call, Borland C++ adjusts the stack pointer back to the value it contained before the
parameters were pushed, thereby discarding the parameters. In the previous example,
the threeparameters of 2 bytes each take up 6bytes of stack space altogether, so Borland
C++ adds 6 to the stack pointer to discard the parameters after the call to Test. The
important point here is that under the default C/C++ calling conventions, the calling
code is responsible for discarding the parameters from the stack.
Chap t er 18, Int erf aci n9 TurboA sse mbIer wit h B0 rIand C++ 209
Assembler functions can access parameters passed on the stack relative to the BP
register. For example, suppose the function Test in the previous example is the following
assembler function, called·PRMSTACK.ASM:
.MODEL small
.CODE
PUBLIC _Test
Test PROC
push
mov
mov
add
sub
pop
ret
Test ENDP
END
bp
bp,sp
ax, [bp+4]
ax, [bp+6]
ax, [bp+8]
bp
iget parameter 1
iadd parameter 2 to parameter 1
isubtract parameter 3 from sum
You can see that Test is getting the parameters passed by the C++ code from the stack,
relative to BP. (Remember that BP addresses the stack segment.) Butjust how are you to
know where to find the parameters relative to BP?
i = 25i
j = 4i
Test (i, j, 1) i
The parameters to Test are at fixed locations relative to SP, starting at the stack location
2 bytes higher than the location of the·retum address that was pushed by the call. After,
loading BP with SP, you can access the parameters relative to BP. However, you must
'first preserve BP, since the calling C++ code expects you to return with BP unchanged.
Pushing BP changes all the offsets on the stack.
push bp
mov bp,sp
This is the standard C++ stack frame, the organization of a function's parameters and
automatic variables on the stack. As you can see, no matter how many parameters a
C++ program might have; the left-most parameter is always stored at the stack address
immediately above the pushed return address, the next parameter to the right is stored
just above the left-most parameter, and so on. As long as you know the order and type
of the passed parameters, you always know where to find them on the stack.
Space for automatic variables can be reserved by subtracting the required number of
bytes from SP. For example, room for a IOO-byte automatic array could be reserved by
starting Test with -
push bp
mov bp,sp
sub 'sp, 100
210 TurboA sse mbIer Use r' s Guide
Since the portion of the stack holding automatic variables is at a lower address than BP,
negative offsets from BP are used to address automatic variables. For example,
mov BYTE PTR [bp-100],O
would set the first byte of the lOO-byte array you reserved earlier to zero. Passed
parameters, on the other hand, are always addressed at positive offsets from BP.
While you can, if you wish, allocate space for automatic variables as shown previously,
Turbo Assembler provides a special version of the LOCAL directive that makes
allocation and naming of automatic variables a snap. When LOCAL is encountered
within a procedure, it is assumed to define automatic variables for that procedure. For
example,
LOCAL LocalArray:BYTE:1oo,LocalCount:WORD = AUTO_SIZE
defines the automatic variables LocalArray and LocalCount. LocalArray is actually a label
equated to [BP-100], and LocalCount is actually a label equated to [BP-102], but you can
use them as variable names without ever needing to know their values. AUTO_SIZE is
the total number of bytes of automatic storage required; you must subtract this value
from SP in order to allocate space for the automatic variables.
Here's how you might use LOCAL:
TestSub PROC
LOCAL LocalArray: BYTE: 100, LocalCount:WORD=AUTO_SIZE
push bp
mov . bp, sp
sub sp,AUTO_SIZE
mov [LocalCount] ,10
mov ex, [LocalCount]
mov aI,' A'
lea bx, [LocalArray]
FillLOOp:
mov [bx],al
inc bx
loop FillLoop
mov sp,bp
pop bp
ret
TestSub ENDP
ipreserve caller's stack frame pointer
iset up our own stack frame pointer
iallocate room for automatic variables
iset local count variable to 10
i (LocalCount is actually [BP-102])
iget count from local variable
iwe'll fill with character "A"
ipoint to local array
i (LocalArray is actually [BP-100])
ifill next byte
ipoint to following byte
ido next byte, if any
ideallocate storage for automatic
i variables (add sp,AUTO_SIZE would
i also have worked)
irestore caller's stack frame pointer
In this example, note that the first field after the definition of a given automatic variable
is the data type of the variable: BYTE, WORD, DWORD, NEAR, and so on. The second
field after the definition of a given automatic variable is the number of elements of that
variable's type to reserve for that variable. This field is optional and definesan
automatic array if used; if it is omitted, one element of the specified type is reserved.
Chap ter 18, In terf acin9 TurboA sse mbIer wit h B0 rIand C++ 211
Consequently, LocalArray consists of 100 byte,-sized elements, while LocalCount consists
of 1 word-sized element.
Also note that the LOCAL line in the preceding example ends with =AUTO_SIZE. This
field, beginning with an equal sign, is optional; ifpresent, it sets the label following the
equal signto the number of bytes of automatic storage required. You must then use that
label to allocate and deallocate storage for automatic variables, since the LOCAL
directive only generates labels, and doesn't actually generate any code or data storage.
To put this another way: LOCAL doesn't allocate automatic variables, but simply
generates labels thatyou can readily use to both allocate storage for and access
automatic variables.
As you can see, LOCAL makes it much easier to define and use automatic variables.
Note that the LOCAL directive has a completely different meaning when used in
macros.
By the way, Borland C++ handles stack frames in just the way we've described here.
You might find it instructive to compile a few Borland C++ modules with the -5 option,
and then look at the assembler code Borland C++ generates to see how Borland C++
creates and uses stack frames.
This looks good so far, but there are further complications. First of all, this business of
accessing parameters at constantoffsets from BP is a nuisance; not only is it easy to
make mistakes, but if you add another parameter, all the other stack frame offsets in the
function must be changed. For example, suppose you change Test to accept four
parameters:
Test (Flag, i, j, 1);
Suddenly i is at offset 6, not offset 4, j is at offset 8/not offset 6, and so on. You can use
equates for the parameter offsets:
Flag EQU
AddParm1 EQU
AddParm2 EQU
SubParm1 EQU 10
mav ax, [bp+AddParm1]
add ax," [bp+AddParm2]
sub ax, [bp+SubParm1]
but it's still a nuisance to calculate the offsets and maintain them. There's a more serious
problem, too: Thesize ofthe pushed return address grows by 2bytes in far code models,
as do the sizes of passed code pointers and data pointer in far code and far data models,
respectively. Writing a function that Cat} be easily assembled to access the stack frame
properly in any memory model would thus seem to be a difficult task.
Turbo Assembler, however, provides you with the ARG directive, which makes it easy
to handle passed parameters in your assembler routines.
212 TurboA sse mbIe r Use r' s Guide
The ARG directive automatically generates the correct stack offsets for the variables you
specify. For example,
arg FillArray:WORD,Count:WORD,FillValue:BYTE
specifies three parameters: FillArray, a word-sized parameter; Count, a word-sized
parameter, and FillValue, a byte-sized parameter. ARG actually sets the label FillArray to
[BP+4] (assuming the example code resides in a near procedure), the label Count to
[BP+6], and the label FillValue to [BP+S]. However, ARG is valuable precisely because
you can use ARG-defined labels without ever knowing the values they're set to.
For example, suppose you've got a function FillSub, called from c++ as follows:
extern "C" {
void FillSub(
main()
{
char *FillArray,
int Count,
char FillValue) i
const int ARRAY_LENGTH=100i
char TestArray[ARRAY_LENGTH]i
FillSub(TestArray,ARRAY_LENGTH, '*') i
You could use ARGin FillSub to handle the parameters as follows:
FillSub PROC NEAR
ARG FillArray:WORD,Count:WORD,FillValue:BYTE
push bp ipreserve caller's stack frame
mov bp,sp iset our own stack frame
mov bx, [FillArray] iget pointer to array to fill
mov cx, [Count] iget length to fill
mov al, [F{llValue] iget value to fill with
FillLoop:
mov [bx] ,al
inc bx
loop FillLaop
pop bp
ret
FillSub ENDP
ifill a character
ipoint to next character
ida next character
irestore caller's stack frame
That's really all it takes to handle passed parameters with ARG. Better yet, ARG
automatically accounts for the different sizes of near and far returns.
Preserving registers .
As far as Borland C++- is concerned, C++-callable assembler functions can do anything
as long as they preserve the following registers: BP, SP, CS, DS, and SS. While these
registers c?Jl- be altered during the course of an assembler function, when the calling
code is returned, they must be exactly as they were when the assembl~r function was
called. AX, BX, CX, DX,ES, and the flags can be changed in any way.
Chap t er 18, In t erfa cin 9 TurboA sse mbIe r wit h B0 rIan d C++ 213
S1 and D1 are special cases, since they're used by Borland C++ as register variables. If
register variables are enabled in the C++ module calling your assembler function, you
must preserve S1 and D1; but if register variables are not enabled, S1 and D1 need not be
preserved. '
It's good practice to always preserve S1 and D1 in your C++-callable assembler
functions, regardless of whether register variables are enabled. You never know when
you might link a given assembler module to a different C++ module, or recompile your
C++ code with register variables enabled, without remembering that your assembler
code needs to be changed as well.
Returning values
A C++-callable assembler function can return a value, just like a C++ function. Function
values are returned as follows:
unsigned char
char
enum
unsigned short
short
unsigned int
int
unsigned long
long
float
double
long double
near *
far * _
AX
AX
AX
AX
AX
AX
AX
DX:AX
DX:AX
8087 top-of-stack (TOS) register (ST(O»
8087 top-of-stack (TOS) register (ST(O)
8087 top-of-stack (TOS) register (ST(O»
AX
DX:AX
In general, 8- and 16-bit values are returned in AX, and 32-bit values are returned in
DX:AX, with the high 16 bits of the value in DX. Floating-point values are returned in
ST(O), which is the 8087's top-of-stack (TOS) register, or in the 8087 emulator's TOS
register if the floating-point emulator is being used. -
Structures are a bit more complex. Structures that are 1 or 2 bytes in length are returned
in AX, and structures that are 4 bytes in length are returned in DX:AX. When a function
that returns a three-byte structure or astructure larger than 4 bytes is called, the caller
must allocate space for the return value (usually on the stack), and pass the address of
this space to the function as an additional Jihidden" parameter. The function assigns
the return value through this pointer argument, and returns that pointer as its result. As
with aUpointers, near pointers to structures are returned in AX, and far pointers to
structures are returned in DX:AX.
214 Turbo Assembler User's Guide
Let's look at a small model C++-callable assembler function, FindLastChar, that returns a
near pointer to the last character of a passed string. The C++ prototype for this function
would be
extern char * FindLastChar(char * StringToScan)i
where StringToScan is the non-empty string for which a pointer to the last character is to
be returned.
Here's FindLastChar, from FINDCHAR.ASM:
.MODEL small
.CODE
PUBLIC FindLastChar
. FindLastChar PROC
ARG StringToScan:WORD
push bp
mov bp, sp,
cld iwe need string instructions to count up
mov
mov
mov
mov
mov
repnz
dec
dec
mov
pop
ret
aX,ds
eS,ax iset ES to point to the near data segment
di, [StringToScan] ipoint ES:DI to start of
ipassed string
al,O isearch for the null that ends the string
,cx, Offffh isearch up to 64K-l bytes
~casb ;look for the null
di ipoint back to the null
di ipoint back to the last character
aX,di ireturn the near pointer in AX
bp
FindLastChar
END
ENDP
The final result, the nearpointer to the last character in the passed string, is returned
in AX.
Calling an assembler function from C++
Now look at an example of Borland C++ code calling a Turbo Assembler function. The
following Turbo Assembler module, COUNT.ASM, contains the function LineCount,
which returns counts of the number of lines and characters in a passed string:
Small model C++-callable assembler function to count the number
of lines and characters in a zero-terminated'string.
Function prototype:
Input:
extern unsigned int LineCount(char * near StringToCount,
unsigned int near * CharacterCountptr) i
char near * StringToCount:pointer to the string on which
a line count is to be performed
Chap ter 18, In t erfa ci n9 TurboA sse mbIer wit h B0 rIand C++ 215
unsigned int near * CharacterCountPtr: pointer to the
int variable in which the character count is
to be stored
NEWLINE EQU
.MODEL
.CODE
PUBLIC
LineCount
push
mov
push
mov
sub
mov
LineCountLoop:
lodsb
and
jz
inc
cmp
jnz
inc
jmp
EndLineCount:
inc
mov
mov
mov
pop
Oah
small
LineCount
PROC
bp
bp, sp
si
si, [bp+4]
cx, cx
dx,cx
al,al
EndLineCount
cx
al,NEWLINE
LineCountLoop
dx
LineCountLoop
dx
bx, [bp+6]
pop bp
ret
LineCount ENDP
END
ithe linefeed character is C's
inewline character
ipreserve calling program's
i register variable, if any
ipoint SI to the string
iset character count to 0
;set line count to a
iget the next character
iis it null, to end the string?
iyes, we're done
ino, count another character
iis it a newline?
ino, check the next character
iyes, count another line
icount the line that ends with the
i null character
ipoint to the location at which to
i return the character count
iset the character count variable
ireturn line count as function value
irestore calling program's register
i variable, if any
The following C++ module, CALLCT.CPP, is a sample invocation of the LineCount
function: .
#include <stdio.h>
char ~ TestString="Line lnline 2nline3"i
extern "CO unsigned int LineCount(char * StringToCount,
int main ()
{
unsigned int LCounti
unsigned int CCount;
unsigned int * CharacterCountPtr)i
LCount = LineCount(TestString, &CCount) i
216 Turbo Assembler User's Guide
printf("Lines: %dnCharacters: %dn", LCount, CCount);
return 0;
The two modules are compiled and linked together with the command line
bcc -ms callct.cpp count.asm
As shown here, LineCount will work only when linked to small-model C++ programs
since pointer sizes and locations on the stack frame change in other models. Here's a
version of LineCount, COUNTLG.ASM, that will work with large-model C++ programs
(but not small-model ones, unless far pointers are passed, and LineCount is declared far):
Large model C++-callable assembler function to count the number
of lines and characters in a zero-terminated string.
Function prototype:
extern unsigned int LineCount(char * far StringToCount,
unsigned int * far CharacterCountPtr);
char far * StringToCount: pointer to the string on which
a line count is to be performed
unsigned int far * CharacterCountptr: pointer to the
int variable in which the character count
is to be stored
NEWLINE EQU Oah ;the linefeed character is C's newline
; character
.MODEL large
.CODE
PUBLIC
LineCount
push
mov
push
push
lds
sub
mov
LineCountLoop:
lodsb
and
jz
inc
cmp
jnz
inc
jmp
EndLineCount:
inc
les
LineCount
PROC
bp
bp,sp
si
ds
si, [bp+6]
cx, cx
dx,cx
al,al
EndLineCount
cx
al,NEWLINE
LineCountLoop
dx
LineCountLoop
dx
bx, [bp+l0]
;preserve calling program's
; register variable, if any
;preserve C's standard data seg
;point DS:SI to the string
;set character count to 0
;set line count to 0
;get the next character
;is it null, to end the string?
;yes; we're done
;no, count another character
;is it a newline?
;no, check the next character
iyes, count another line
icount line ending with null
i character
;point ES:BX to the location at
i which to return char count
Chapt er 18, I nterfa ci n9 TurboA sse mbIer wit h B0 rIand C++ 217
mov es: [bx] ,ex ;set the char count variable
mov ax,dx ireturn the line count as
; the function value
pop ds irestore C's standard data seg
pop si irestore calling program's
i register variable, if any
pop bp
ret
LineCount ENDP
END
COUNTLG.ASM can be linked to CALLCT.CPP with the following command line:
bcc -ml callct.cpp countlg.asm
Writing C++ member functions in assembly language
While you can write a member function of a C++ class completely in assembly
language, it is not easy. For example, all member functions of C++ classes are name-
mangled to provide the type-safe linkage that makes thingslike overridden functions
available, and your assembler function would have to know exactly what name C++
would be expecting for the member function. To access the member variables you must
prepare a STRUC definition in your assembler code that defines all the member
variables with exactly the same sizes and locations. If your class is a derived class, there
may be other membet variables derived from a base class. Even if your class is not a
descendant of another class, the location of meIJ;lber variables in memory changes if the
class includes any virtual functions.
If you write your function using inline assembler, Borland C++ can take care of these
issues for you. But if you must write your function in assembly language, (perhaps
because you are reusing some existing assembler code), there are some special
techniques'you can use to make things easier.
Create a dummy stub C++ function definition for the assembler function. This stub
will satisfy the linker because it will have a properly mangled name for the member
function. The dummy stub then calls your assembler function and passes to it the
member variables and other parameters. Since your assembler code has all the
parameters it needs passed as arguments, you don't have to worry about changes in the
class definition. Your assembler function can be declared in the C++ code as an extern
"C" function, just as we have shown you in otherexamples.
Note For an example of how to write assembly functions using mangled names, see the
example on page 199.
Here's an example, called COUNTER.CPP:
#include <stdio.h>
class counter {
II Private member variables:
int counti II The ongoing count
public:
counter (void) { count=Oi }
int get_count (void) {return count;}
218 TurboA sse mbIer Use r' s Guide
};
II Two functions that will actually be written
II in assembler:
void increment (void) ;
void add(int what_to_add=-l);
II Note that the default value only
II affects calls to add, it does not
II affect the code for add.
extern "C" {
II To create some unique, meaningful names for the
II assembler routines, prepend the name of the class
II to the assembler routine. Unlike some assemblers,
II Turbo Assembler has no problem with long names.
void counter_increment (int *count); II We will pass a
II pointer to the
II count variable.
II Assembler will
II do the incrementing.
void counter_add(int *count,int what_to_add);
}
void counter::increment(void)
counter_increment (&count) ;
void counter::add(int what_to_add)
counter_add(&count, what_to_add);
int main() {
counter Counter;
printf( "Before count: %dn", Counter.get_count());
Counter.increment() ;
Counter.add( 5 );
printf( "After count: %dn", Counter.get_count());
return 0;
Your assembler module that defines the count_add_increment and count_add_add
routines could look like this example, called COUNTADD.AISM:
.MODEL small
.CODE
; Select small model (near code and data)
PUBLIC counter increment
counter_increment PROC
ARG count.offset:word
push bp
mov bp,sp
mov bx, [count_offset]
inc word ptr [bxl
pop bp
ret
counter_increment ENDP
Address of the member variable
Preserve caller's stack frame
Set our own stack frame
, Load pointer
Increment member variable
Restore callers stack frame
Chap t er 18, In t erfa cin 9 TurboA sse mbIer wit h B0 r Iand C+ + 219
counter_add PROC
ARG count_offset:word,what_to_add:word
push bp
mov bp, sp
mov bx, [count_offset] Load pointer
mov ax, [what_to_add]
add [bx] ,ax .
pop bp
ret
counter_add ENDP
end
Using this method, you don't have to worry about changes in your class definition.
Even if you add or delete member variables, make this class a derived class, or add
virtual functions, you won't have to change your assembler module. You need to
reassemble your module only ifyou change the structure of the count member variable,
or if you make a large model version of this class. You need to reassemble because you
have to deal with a segment and an offset whenreferring to the count member variable.
Pascal calling conventions
So far, you've seeri how c++ normally passes parameters to functions by having the
calling code push parameters right to left, call the function, and discard the parameters
from the stack after the call. Borland C++ is also capable of following the conventions
used by Pascal programs in which parameters are passed from left to right, and the
called function discards the parameters from the stack. In Borland C++,·Pascal
conventions are enabled with the -p command-line option or the pascal keyword.
The following example, ASMPSCL.ASM, shows an assembler function that uses Pascal
conventions:
; Called as: TEST_PROC(i, j, k) ;
i equ ;leftmost parameter
j equ
k equ 4 ;rightmost parameter
.MODEL small
.CODE
PUBLIC TEST_PROC
TEST_PROC PROC
push bp
mov bp,sp
mov ax, [bp+i] ;get i
add ax, [bp+j] ;add j to i
sub ax, [bp+k] ;subtract k from the sum
pop bp
ret 6 ;return, discarding 6 parameter bytes
TEST_PROC ENDP
END
220 TurboA sse mbIer Use r'.s Guide
Note that RET 6 is used by the called function to clear the passed parameters from the
stack.
Pascal calling conventions also require all external and public symbols to be in
uppercase, with no leading underscores. Why would you want to use Pascal calling
conventions in a C++ program? Code that uses Pascal conventions tends to be
somewhat smaller and faster than normal C++ code since there's no need to execute an
ADD SP n instruction to discard the parameters after each call.
Caning Borland C++ from Turbo Assembler
Although it's most common to call assembler functions from C++ to perform
specialized tasks, you might occasionally want to call C++ functions from assembler. As
it turns out, it's actually easier to call a Borland C++ function from a Turbo Assembler
function than the reverse since no stack-frame handling on the part of the assembler
code is required. Let's take a quick look at the requirements for calling Borland C++
functions from assembler.
Link in the C++ startup code
As a general rule, you should only call Borland C++ library functions from assembler
code in programs that link in the C++ startup module as the first module linked.
Note Generally, you should not call Borland C++ library functions from programs that don't
link in the C++ startup module since some Borland C++ library functions will not
operate properly if the startup code is not linked in. If you really want to call Borland
C++ library functions from such programs, we suggest you look at the startup source
code (the file CO.ASM on the Borland C++ distribution disks) and purchase the C++
library source code from Borland. This way, you can be sure to provide the proper
initialization for thelibrary functions you need. .
Note Calling user-defined C++ functions that in tum call C++ library functions falls into the
same category as calling library functions directly; lack of the C++ startup can
potentially cause problems for any assembler program that calls C++ library functions,
directly ot indirectly.
The segment setup
As we learned earlier, you must make sure that Borland C++ and Turbo Assembler are
using the same memory model and that the segments you use in Turbo Assembler
match those used by Borland C++. Turbo Assembler has a tchuge memory model that
supports Borland C++'s huge memory model..Refer to the previous section if you need
a refresher on matching memory models and segments. Also, remember to put EXTRN
directives for far symbols either outside all segments or inside the correct segment.
Chap t er 18, I nterf aci n9 TurboA sse mbIer wit h B0 rIand C++ 221
Performing the call
All you need to do when passing parameters to a Borland C++ function is push the
right-most parameter first, then the next right-most parameter, and so on, until the left-
most parameter has been pushed. Then just call the function. For example, when
programming in Borland C++, to call the Borland C++ library function strcpy to copy
SourceString to DestString, you would type
strcPY,(DestString, SourceString) i
To perform the same call in assembler, you would use
lea
lea
ax,SourceString
bX,DestString
push ax
push bx
call _strcpy
sp,4
add
irightmost parameter
ileftmost parameter
iPush rightmost first
iPush leftmost next
iCOPY the string
idiscard the parameters
Don't forget to discard the paramEfters by adjusting SP after the call.
You can simplify your code and make it language independent at the same time by
taking advantage of Turbo Assembler's CALL instruction extension:
call destination [language [,arglJ ... J
where language is C, CPP, PASCAL, BASIC, FORTRAN, PROLOG or NOLANGUAGE,
and arg is any valid argument to the routine that can be directly pushed onto the
processor stack. .
Using this feature, the preceding code can be reduced to
lea aX,SourceString
lea bX,DestString
call strcPY c,bx,ax .
Turbo Assembler automatically inserts instructions to push the arguments in the correct
order for C++ (AX first, then BX), performs the call to _strcpy (Turbo Assembler
automatically inserts an underscore in front of the name for C++), and cleans up the
stack after the call.
If you're calling a C++ function that uses Pascal calling conventions, you have to push
the parameters left to right and not adjust SP afterward: .
lea bx,DestString
lea ax,SourceString
push bx
push ax
i call STRCPY
ileftmost parameter
irightmost parameter
iPush leftmost first
iPush rightmost next
iCOPY the string
ileave the stack alone
Again, you can use Turbo Assembler's CALL instruction extension to simplify your
code:
lea bX,DestString ileftmostparameter
lea ax,SourceString irightmost parameter
call strcPY pascal,bx,ax
222 TurboA sse mbIer. Use r' s Guide
Turbo Assembler automatically inserts instructions to push the arguments in the correct
order for Pascal (BX first, then AX) and performs the call to STRCPY (converting the
name to all uppercase, as is the Pascalconvention).
The last example assumes that you've recompiled strcpy with the -p switch, since the
standard library version of strcpy uses C++ rather than Pascal calling conventions.
Rely on C++ functions to preserve the following registers and only the following
registers: 51, DI, BP, DS, 55, SP, and CS. Registers AX, BX, CX, DX, ES, and the flags may
be changed arbitrarily.
Calling aBorland C++ function from Turbo Assembler
One case in which you may wish to call a Borland C++ function from Turbo Assembler
is when you need to perform complex calculations. This is especially true when mixed
integer and floating-point calculations are involved; while it's certainly possible to
perform such operations in assembler, it's simpler to let C++ handle the details of type
conversion and floating-point arithmetic. .
Let's look at an example of assembler code that calls a Borland C++ function in order to
get a floating-point calculation performed. In fact, let's look at an example in which a
Borland C++ function passes a series of integer numbers to a Turbo Assembler function,
which sums the numbers and in tum calls another Borland C++ function to perform the
floating-point calculation of the average value of the series.
The C++ portion of the program in CALCAVG.CPP is
#include <stdio.h>
extern "C" float Average(int far * Valueptr, int NurnberOfValues);
#define NUMBER_OF_TEST_VALUES 10
int TestValues[NUMBER_OF_TEST_VALUES] = {
I, 2, 3, 4, 5, 6, 7, 8, 9, 10
};
int main ()
{
printf ("The average value is: %fn",
Average (TestValues, NUMBER_OF_TEST_VALUES));
return 0;
extern "C"
float IntDivide(int Dividend, int Divisor)
{
return ( (float) Dividend / (float) Divisor);
and the assembler portion of the program in AVERAGE.ASM is
Borland C++-callable small-model function that returns the average
of a set of integer values. Calls the Borland C++ function
IntDivide() to perform the final division.
Chap ter ,1 8, Int erf aci n9 TurboA sse mbIer wit h B 0 rIand C++ 223
~J
Function prototype:
extern float Average(int far * Valueptr, int NumberOfValues) i
Input:
int far * Valueptr:
int NumberOfValues:
.,MODEL small
EXTRN IntDivide:PROC
.CODE
PUBLIC _Average
_Average PROC
push bp
mov bp,sp
les bx, [bp+4]
mov cx, [bp+8]
mov ax,O
AverageLoop:
add ax, es: [bx]
add bX,2
loop AverageLoop
push WORD PTR [bp+8]
push ax
call IntDivide
add sp,4
pop bp
ret
_Average ENDP
END
ithe array of values to average
ithe number of values to average
ipoint ES:BX to array of values
i# of values to average
iclear the running total
iadd the current value'
ipoint to the next value
iget back the number of values
i passed to IntDivide as the
i rightmost parameter
ipass the total as the leftmost parameter
icalculate the floating-point average
idiscard the parameters
iaverage is in 8087's TOS register
The C++ main function passes a pointer to the array of integers TestValues and the
length of the array to the assembler function Average. Average sums the integers, then
passes the sum and the number of values to the C++ function IntDivide. IntDivide casts
the sum and number of values to floating-point numbers and calculates the average
value, doing in a single line of C++ code what would have taken several assembler lines.
JntDivide returns the average to Average in the 8087 TOS register, and Averagejust leaves
the average in the TOS register and returns to main.
CALCAVG.CPP and AVERAGE.ASM could be compiled and linked into the executable
program CALCAVG.EXE with the command
bcc calcavg.cpp average.asm
Note that Average will handle both small and large data models without the need for
any code change since a far pointer is passed in all models. All that would be needed to
support large code models (huge, large, and medium) would be use of the appropriate
.MODEL directive.
Taking full advantage of Turbo Assembler's language-independent extensions, the
assembly code in the previous example could be written more concisely as shown here
in CONCISE.ASM: '
224 TurboA sse mbIer Use r' s Guide
.MODEL small,C
EXrrRN C IntDivide: PROC
.CODE
PUBLIC
Average
les
mov
C Average
PROC C ValuePtr:DWORD,NumberOfValues:WORD
bx,Valueptr
cx,NumberOfValues
mov ax,O
AverageLoop:
add ax,es: [bx]
add bx,2 ;point to the next value
loop AverageLoop
call IntDivide C,ax,NumberOfValues
ret
Average ENDP
END
Chapter 18, Interfacing Turbo Assembler with Borland C++ 225
226 TurboA sse mbIer Use rl s Guide
Program blueprints
This appendix describes basic program construction information depending on specific
memory models and executable object formats.
Simplified segmentation segment description
The following tables show the default segment attributes for each memory model.
Table A.1 Default segments and types for TINY memory model
.CODE _TEXT WORD PUBLIC 'CODE' DGROUP
.FARDATA FAR_DATA PARA private 'FAR_DATA'
.FARDATA? FAR_BSS PARA private 'FAR_BSS'
DATA _DATA WORD PUBLIC 'DATA' DGROUP
.CONST CaNST WORD PUBLIC 'CaNST' DGROUP
.DATA? _BSS WORD PUBLIC 'BSS' DGROUP
.sTACKl
STACK PARA STACK 'STACK' DGROUP
1. STACK not assumed to be in DGROUP if FARSTACK specified in the MODEL directive.
Table A.2 Default segments and types for SMALL memory model
.FARDATA
.FARDATA?
DATA
.CONST
FAR_DATA
FAR_BSS
_DATA
CaNST
PARA
PARA
WORD
WORD
private
private
PUBLIC
PUBLIC
'FAR_DATA'
'FAR_BSS'
'DATA' DGROUP
'CaNST' ·DGROUP
APpen di x A, Pro 9ram bIueprj nt s 227
Table A.2 Default segments and types for SMALL memory model (continued)
.DATA?
.STACKl
_BSS
STACK
WORD
PARA
PUBLIC
STACK
'BSS'
'STACK'
1. STACK not assumed to be in DGROUP if FARSTACK specified in the MODEL directive.
TableA.3 Default segments and types for MEDIUM memory model
.CODE name_TEXT WORD PUBLIC 'CODE'
.FARDATA FAR_DATA PARA private ' 'FAR_DATA'
.FARDATA? FAR_BSS PARA private 'FAR_BSS'
.DATA _DATA WORD PUBLIC 'DATA'
.CONST CONST WORD PUBLIC 'CONST'
.DATA? _BSS WORD PUBLIC 'BSS'
.STACKl STACK PARA STACK 'STACK'
1. STACK not assumed to be in DGROUP if FARSTACK specified in the MODEL directive.
TableA.4 Default segments and types for COMPACT memory model
.CODE _TEXT WORD PUBLIC 'CODE'
.FARDATA FAR_DATA PARA private 'FAR_DATA'
.FARDATA? FAR_BSS PARA private 'FAR_BSS'
.DATA _DATA WORD PUBLIC 'DATA'
.CONST CONST WORD PUBLIC 'CONST'
.DATA? _BSS WORD PUBLIC 'BSS'
.sTACKl
STACK PARA STACK 'STACK'
1. STACKnot assumed to be in DGROUP if FARSTACK specified in the MODEL directive.
Table A.S Default segments and types for LARGE or HUGE memory model
.CODE name_TEXT WORD PUBLIC 'CODE'
.FARDATA FAR_DATA PARA private 'FAR_DATA'
.FARDATA? FAR_BSS PARA private 'FAR_BSS'
.DATA _DATA WORD PUBLIC 'DATA'
.CONST CONST WORD PUBLIC 'CONST'
.DATA? _BSS WORD PUBLIC 'BSS'
.sTACKl STACK PARA STACK 'STACK'
1. .STACK not assumed to bein DGROUP if FARSTACK specified in the MODEL directive.
228 Turbo Assem'bler User's Guide
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
DGROUP
Table A.6 Default segments and types for Borland C++ HUGE (TCHUGE) memory model
'fQ~.·~~~,V,'1:/ii"hd:;,/i;'i'i
.CODE name_TEXT WORD PUBLIC 'CODE'
.FARDATA
.FARDATA?
DATA
.5TACK1
FAR_DATA
FAR_BSS
name_DATA
STACK
PARA
PARA
PARA
PARA
private 'FAR_DATA'
private 'FAR_BSS'
private 'DATA'
STACK 'STACK'
1. STACK is automatically FAR.
DOS programs
Programs designed to be executed under DOS are stored in two formats:
• EXE format (for EXEcutable)
• COM format (for COre iMage)
EXE format permits the most general program segmentation under DOS. A program
can have multiple segments, and can reference segment and group names symbolically.
EXE programs are thus permitted to exceed 64K in size.
COM format is essentially a throwback to a simpler era. Programs using COM format
can't contain symbolic references to group and segment names. Thus, COM programs
are written using the TINY model, and are limited to 64K of code and data.
To build DOS programs, you need to use a DOS.linker (like TLINK) and a program
construction utility (like MAKE).
DOS EXE program blueprint
When you load an EXE program, the operating system sets up the registers as follows:
DS,ES
CS:IP
SS:sp
Contains the paragraph address of the program segment prefix (PSP) for the program. The
PSP contains arguments passed to the program from the command line, and a pointer to the
environment string for the program.
Contains the starting address specified byEND in one of the program's modules, or the
address of the STARTUPCODE directive.
Contains the address of the last word that the stack segment specified in the program.
You can define EXE programs with any memory model. You should use the simplest
memory model possible because it makes programming simpler and faster. For
example, if you never expect your program to use more than 64K of code, data, and
stack space, the TINY model would be the appropriate model to use.
The STARTUPCODE directive in a module emits instructions that automatically
initialize all necessary registers to conform with the selected model. However, it
preserves the paragraph address of the PSP in ES for the program's use.
APpen di x A, Pro 9ram bIueprj nt s 229
When you load an EXE program, the operating system allocates all remaining memory
to it until the program exits. For programs that don't use a heap, or programs that build
their own heaps in this memory, this behavior is fine. Other programs can allocate
memory from DOS. In this case, the memory must be freed back to DOS before you can
request it from DOS.
To exit from an EXE program, use the EXITCODE directive.
Note EXEPROG.ASM, on your example Turbo Assembler disks, illustrates these topics.
Use the MAKE utility to build the EXE program. The file MAKEFILE should include all
modules link with the program, as follows:
EXEPROG.EXE: EXEPROG.OBJ
TLINK EXEPROGi
EXEPROG.OBJ: EXEPROG.ASM
TASM EXEPROG
COM program blueprint
COM programs are restricted versions of EXE programs. You can represent every COM
program as an EXE program, but not every EXE program as a COM program. The
following restrictions apply:
·A COM program should be written using the TINY memory model.
• You can't have a predefined stack segment for COM programs.
• A COM program can't contain any direc~ segment or group address references. This
means that the program can't contain any direct far calls, nor can it reference any
segments by name. All procedures in a COM program must be declared NEAR.
• Execution must begin at offset lOOh in the code segment. To let this happen, make the
first instruction in the code segment the STARTUPCODE directive.
Turbo Assembler loads a COM. program starting at offset lOOh of the program segment
prefix (PSP). The STARTUPCODE directive for the TINY model automatically places
an ORC lOOh in the program to compensate for this action.
When you load a COM program, the following registers are set:
CS,DS,ES,SS
IP
SP
Contains the paragraph address of the PSP and the program.
Set to lOOh.
Set to OFFFEh (the last word in the program segment).
If you don't want to place the stack at the end of the program segment, you must set up
anew stack. Use the uninitialized data segment (UDATASEG) for this'stack.
Even though COM programs must be defined with the TINY memory model, you
should stillseparate code, data, and uninitialized data using CODESEG, DATASEG,
and UDATASEG.
230 Turbo Assembler User's Guide
Note Use the EXITCODE directive exit from a COM program the same way you exit from an
EXE program.
As with EXE programs, when you load a COM program, Turbo Assembler allocates all
remaining memory to it until it exits. If memory is freed back to DOS, make sure that no
uninitialized data is unintentionally freed.
Note COMPROG.ASM, on your example Turbo Assembler disks, illustrates these points.
Use the MAKE utility to build the COM program. The fileMAKEFILE should include
all modules link with the program, as follows:
COMPROG.COM: COMPROG.OBJ
TLINK It COMPROGi
COMPROG.OBJ: COMPROG.ASM
TASM COMPROG
Windows programs
Turbo Assembler can also be used to create Windows applications. Windows can
run in either real mode (on all 8086 processors) or protected mode (on 80286 and higher
processors). Thus, programs written for Windows can run in protected mode. You
should carefully separate code and data using the CODESEG, DATASEG, and
UDATASEG directives, and use the WARN PRO directive to flag any access problems
that could occur at assembly time. Finally, protected mode programs should not
attempt to set segment registers to calculated paragraph segment values. Segment
values in protected mode are not paragraph addresses, but rather descriptors that have
no meaning to an application program.
Although all the tools you need to write Windows programs are contained in your
Turbo Assembler package, you might ~d it useful to use otherlanguage tools (such as
Borland C++ or Borland Delphi) to help you effectively create Windows applications.
This appendix provides the simplest of blueprints for Windows applications and
Dynamic Link Libraries (DLLs). For a more complete description of Windows
,applications, refer to your. compiler manuals, or the appropriate Microsoft
documentation. .
WindowsDLL blueprint
A Dynamic Link Library (DLL) is a group of procedures that you can call from any
Windows application. DLLs extend the Windows application interface.
DLLs perform many functions; for example, you can convert non-interactive DOS
programs to DLLs. Support can be added for new sorts of screen entities by writing a
DLL.
You can find an example program called DLLWIN.ASM that illustrates how.to write a
DLL in assembler. This assembler project is located in the EXAMPLES USRGUIDE
DLLWIN directory off the main TASM subdirectory.
Appendix A, Program blueprints 231
You can use the MAKE utility to build the DLL. DLLWIN.MAK is provided in the
DLLWIN subdirectory:
dllwin.exe: dllwin.obj dllwin.def
TLINK /v /Twd /s dllwin, dllwin, dllwin"dllwin
dllwin.obj: dllwin.asm
TASM /Zi dllwin, ,
This build process requires the following linker definitions file, DLLWIN.DEF:
LIBRARY DLLWIN
DESCRIPTION 'Simple Assembly Windows DLL'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
EXETYPE WINDOWS
HEAPSIZE 4096
e _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
,
iDefine imported functions. (Not necessary if you link with an
iimport library like IMpORT.LIB or LIBW.LIB.)
.---~------------------------------------------------- -----------------
,
IMPORTS GDI.GETWINDOWEXTEX
Windows 16·bit application blueprint
A Windows application is very much like a DLL, except that only a single procedure is
called: WinMain. Windows calls WinMain to start the procedure. The application usually
has a standard structure, which lets it communicate with the Windows graphical
environment. ..
ASMWIN.ASM, located in the EXAMPLES USRGUIDEASMWIN directory off the .
main TASM subdirectory, shows ari assembly-based Windows program.
Use the MAKE utility to build the Windows application. The file ASMWIN.MAK
includes all modules to be linked in with the application:
asmwin.exe: asmwin.obj asmwin.def
TLINK /v /Twe /s ASMWIN, ASMWIN, ASMWIN"ASMWIN
asmwin.obj: asmwin.asm
TASM /Zi ASMWIN"
This build process requires the linker definitions file ASMWIN.DEF, which is very
similar to the .DEF module used in the preceding example that generates a Windows
DLL using assembly code.
A complete 16-bit Windows program is supplied in the  EXAMPLES WAP
subdirectory off your main TASM directory.
Windows 32·bit application blueprint
In high-level languages like C, C++, and Pascal,Windows 32-bit programs are almost
identical to their 16~bit counterparts because the compiler handles most of the
232 TurboA sse mbIer Use r 's Guide
differences. However, you must deal directly with those differences at the assembly
language level. In particular, you must pay attention to the following details:
• All values pushed on the stack must be 32 bits.
• The parameters to WndProc must all be 32 bits.
• The EBX, ESI and EDI registers must be preserved in a Win32 callback function.
• Many of the Win32 constants are now 32 bits wide as opposed to Windows 16-bit
wide constants.
• Because of Unicode support, there are both ANSI and WIDE CHAR versions of all
the API functions that use strings. The ANSI version of these functions end with a
capital'A' and the WIDE CHAR versions end with a capital'W'; no versions of these
functions exist that don't end with either an 'A' Or 'W.' Because of this, you must use
either TextOutA or TextOutW to get the functionality of TextOut. .
Use the following assembler and linker commands to compile a 32-bit Windows
program:
TASM32 /zi /ml filename.asm
TLINK32 /v filename.obj, filename.exe, filename.map, import32.lib, filename.def
The .DEF file for the project should resemble the following:
NAME
DESCRIPTION
CODE
DATA
EXETYPE
STACKSIZE
EXPORTS
MODNAME
'Description of this module'
PRELOAD MOVEABLE DISCARDABLE
PRELOAD MOVEABLE MULTIPLE
WINDOWS
8192
WndProc
A complete 32-bit Windows program is supplied in the  EXAMPLES WAP32
subdirectory off your main TASM directory.
OS/2 programs
Programs designed to be executed under the OS/2 operating system can use one of
several formats, depending on the capabilities you want. OS/2 can execute programs
designed for DOS program formats, as well as programs and DLLs written for
Windows. However, the most powerful format available under OS/2 is the linear
executable format, in which a program no longer has to manipulate segment registers,
and 512 megabytes of virtual memory is available. This format is also known as flat
model.
OS/2 flat-model program blueprint
Turbo Assembler assumes that all 32-bit segments in a flat-model program belong to a
supergroup called FLAT, and share the same segmentselector. (Segments that are 16-bit
are allowed, but are of little use.)
Appendix A, Program blueprints 233
When you execute a flat-model program, Turbo Assembler initializes the registers as
follows:
.CS,DS,ES,SS
CS:EIP
SS:ESP
FS,GS
Contains the segment selector of the 32-bit linear address for the program. These
registersshould never have to be changed.
Contains the address of the STARTUPCODE directive.
Contains the address of the last word of the stack segment, which the STAC~
directive specifies.
Contain special values that the application should not modify.
You must define linear-executable programs with the FLAT model. This instructs
Turbo Assembler to consider all 32-bit segments and groups to be a member of FLAT
supergroup. You can also optionally specify the OS/2 operating system, which allows
the STARTUPCODE and EXITCODEdirectives to function correctly (for example,
MODEL 082 FLAT).
.The STARTUPCODE directive produces instructions that automatically initialize all
necessary registers to conform to FLAT model. Similarly, the EXITCODE directive
produces instructions that automatically return control to the operating system, while
letting you specify an optional return value.
234 Turbo Assembler User's Guide
Turbo Assembler syntax summary
This appendix describes the syntax of Turbo Assembler expressions in a modified
Backus-Naur form (BNF). The symbol ::= describes a syntactical production. Ellipses
(...) indicate that an element is repeated as many times as it is found. This appendix also
discusses keywords and their precedences.
Lexical grammar
valid_line ::=
white_space valid_line
punctuation valid_line
number_string valid_line
id_string valid_line
null
white_space ::=
space_char white_space
space_char
space_char ::=
All control characters, character> 128, I '
id_string ::=
id_char id_strng2
id_stmg2 ::=
id_chr2 id_strng2
null
id~char ::=
Any of $, %, -' ?, or any alphabetic characters
Appendix B, Turbo Assembler syntax summary 235
, id_chr2 ::=
id_chars plus numerics
number....;string ::=
num_string
str_string
num_string ::=
digits alphtlnums
digits ".' digits exp
digits exp
digits ::=
digit digits
digit
digit ::=
othrough 9
alphanums ::=
digit alphanum
alpha alphanum
, null
alpha ::=
alphabetic characters
exp::=
E+ digits
E-digits
Edigits
null
str_string ::=
;Only MASM mode DO, DQ, or DT
Quoted string, quote enterable by two quotes in a row
punctuation ::=
Everything that is not a space_char, id_char, I " " " , If, or digits
The period (.) character is handled differently in MASM mode and Ideal mode. This
character is not required in floating-point numbers in MASM mode and also can't be
part ofa symbol name in Ideal ,mode. In MASM mode, it is sometimes the start of a
symbol name and sometimes a punctuation character used as the structure member
selector.
Here are the rules for the period (.) character:
1 In Ideal mode, it's always treated as punctuation.
2 In MASM mode, it's treated as the first character of an ID in the following cases:
@ When it is the first character on the line, or in other special cases like EXTRN and
PUBLIC symbols, it gets attached to the following symbol if the character that
follows it is an id_chr2, as defined in the previous rules.
236 TurboA sse mbIer Use r's Guide
• If it appears other than as the first character on the line, or ifthe resulting symbol
would make a defined symbol, the period gets appended to the start of the symbol
following it.
MASM mode expression grammar
Expression parsing starts at MASM_expr.
MASM_expr ::=
mexprl
mexprl ::=
SHORT mexprl
.TYPE mexprl
SMALL mexprl
LARGE mexprl
expr2
expr2 ::=
expr3 OR expr3 .. .
expr3 XOR expr3 .. .
expr3
expr3 ::=
expr4 AND expr4 ...
expr4
expr4 ::=
NOTexpr4
exprS
exprS ::=
expr6 EQ expr6 ...
expr6 NE expr6 '"
expr6 LT expr6 .. ,
expr6 LE expr6 ...
expr6 GT expr6 '"
expr6 GE expr6 ...
expr6
expr6 ::='
expr7 + expr7 .. .
expr7 - expr7 .. .
expr7
;If 386
;If 386
APpen di X B, TurboA sse mbIer syntax sum mar y 237
expr7::=
mexprl0 * mexprl0 ...
mexprl0 /mexprl0 :..
mexprl0 MOD mexprl0 .. .
mexprl0 SHR mexprl0 .. .
mexprl0 SHL mexprl0 .. .
mexprl0
exprS ::=
+ exprB
-exprB
expr12
exprl0 ::=
OFFSET pointer
SEG pointer
SIZE symbol
LENGTH symbol
WIDTH symbol
MASK symbol
THIS itype
symbol
(pointer)
[pointer J
mexprlO ::=
mexprll PTR mexprl0
mexprll
TYPE mexprl0
HIGH mexprl0
LOWmexprl0
OFFSET mexprl0
SEGmexprl0
THIS mexprl0
mexpr11 ::=
exprB : exprB ...
mexpr12 ::=
mexpr13 [mexpr13 ...
mexpr13 (mexpr13 :..
mexpr13 '.' mexprl0
238 Turbo Assembler User's Guide
;Implied addition if bracket
;Implied addition if parenthesis
mexpr13 ::=
LENGTH symbol
SIZE symbol
WIDTH symbol
MASK symbol
(mexprl )
[mexprl ]
exprl0
Ideal mode expression grammar
Expression parsing starts at ideaCexpr.
ideaCexpr ::=
pointer
itype ::=
UNKNOWN
BYTE
WORD
DWORD
PWORD
FWORD
QWORD
TBYTE
SHORT
NEAR
FAR
PROC
DATAPTR
CODEPTR
structure_name
table_name
enum_namer
record_name
TYPE pointer
pointer ::=
SMALL pointer
LARGE pointer
itype PTR pointer
itype LOW pointer
itype HIGH pointer
itype pointer
pointer2
;If 386
;1£ 386
APpen di x B, TurboA sse mbIer synt ax sum mar y 239
pointer2 ::=
pointer3 . symbol ...
pointer3
pointer3 ::=
expr : pointer3
expr
expr::=
SYMTYPE expr
expr2
expr2 ::=
expr3 OR expr3 ...
expr3 XOR expr3 '"
expr3
expr3 ::=
expr4 AND expr4 ...
expr4
expr4 ::=
NOTexpr4
exprS
exprS ::=
expr6 EQ expr6 .. ,
expr6 NE expr6 ...
expr6 LTexpr6 .. ,
expr6 LE expr6 ...
expr6 GT expr6 .. ,
expr6 GE expr6 ...
expr6
expr6 ::=
expr7+ expr7 ...
expr7 - expr7 .. ,
expr7
expr7::=
exprB *exprB ...
exprB / exprB .,.
exprB MOD exprB .. .
exprB SHR exprB .. .
exprB SHL exprB .. .
exprB
exprS ::=
+ exprB
-exprB
expr9
240 Turbo Assembler User's Guide
expr9 ::=
HIGHexpr9
LOWexpr9
exprl0
exprl0 ::=
OFFSET pointer
SEG pointer
SIZE symbol
LENGTH symbol
WIDTH symbol
MASK symbol
THIS itype
symbol
(pointer)
[pointer]
Keyword precedence
It's important to understand how Turbo Assembler parses source lines so that you can
avoid writing code that produces unexpected results. For example, examine the
following program fragment:
NAME SEGMENT
If you had written this line hoping to open a segment called NAME, you would be
disappointed. Turbo Assembler recognizes the NAME directive before the SEGMENT
directive, thus naming your code SEGMENT.
In general, Turbo Assembler determines the meaning of a line based on the first two
symbols on the line. The left-most symbol is in the first position, while the symbol to its
right is in the second position.
Ideal mode precedence
The following precedence rules for parsing lines apply to Ideal mode:
1 All keywords in the first position of the line have the highest priority (priority 1) and
are checked first.
2 The keywords in the second position have priority 2 and are checked second.
APpen di x B, TurboA sse mbIer syn t ax sum mar y 241
MASM mode precedence
The precedence rules for parsing lines in MASM mode are much more complicated than
in Ideal mode. There are three levels of priority instead of two, as follows:
1 The highest priority (priority 1) is assignedto certain keywords found in the first
position, such as NAME or %OUT.
2 The next highest priority (priority 2) belongs to all symbols found in the second
position.
3 All other keywords found in first position have the lowest priority (priority 3).
Note Turbo Assembler treats priority 1 keywords like priority 3 keywords inside structure
definitions. In this case, priority 2 keywords have the highest priority.
For example, in the code fragment
NAME SEGMENT
NAME is a priority 1 keyword, while SEGMENT is a priority 2 keyword. Therefore,
Turbo Assembler will interpret this line as a NAME directive rather than a SEGMENT
directive. In another example,
MOV INSTR,l
MOV is a priority 3 keyword, while INSTR is a priority 2 keyword. Thus, Turbo
Assemblerinterprets this line as an INSTR directive, not a MOV instruction (which you
might have wanted).
Keywords and predefined symbols
This section contains a complete listing of all Turbo Assembler keywords.
The values in parentheses next to keywords indicate the priority of the keyword (lor 2)
in MASM mode. Keywords are labeled with a priority only if they have priority 1 or 2.
All others are assumed to be priority 3. Turbo Assembler recognizes the keyword only if
it finds them. In MASM mode, priority 1 or 3 keywords always are located in the first
position, while priority 2 keywords occur in the second position.
An M next to a keyword indicates that you can use a keyword only in MASM mode,
and an I indicates a keyword that is available only in Ideal mode. If there is no letter, the
keyword works in either mode. A number next to the keyword indicates its priority.
.Directive keywords
The following list contains all Turbo Assembler directive keywords. The keywords are
grouped by the version of Turbo Assembler in which they were introduced.
242 TurboA sse mbIer Use r' s Guide
These keywords were introduced in Turbo Assembler 1.0.
Table B.1 Turbo Assembler v1.0 (VERSION T100) keywords
% (1) CMPSD EMUL FBLD
.186(M) .CODE(M) END FBSTP
.286 (M) CODESEG ENDIF (1) FCHS
.286c (M) COMM(1) ENDM FCLEX
.286p (M) COMMENT (1) ENDP(2) FCOM
.386 (M) %CONDS ENDS (2) FCOMP
.386c (M) CONST ENTER FCOMPP
.386p (M) .CONST(M) EQU(2) FDECSTP
.387(M) %CREF .ERR(l)(M) FDISI
.8086 (M) .CREF(M) ERR FDN
.8087 (M) %CREFALL .ERR1 (1)(M) FDNP
; (2) %CREFREF .ERR2 ,(1)(M) FDNR
=(2) %CREFUREF .ERRB (1)(M) FDNRP
AAA %CTLS .ERRDEF (l)(M) FENI
AAD CWD .ERRDIF (1)(M) FFREE
AAM CWDE .ERRDIFI (1)(M) FIADD
AAS DAA .ERRE (l)(M) FICOM
ADC DAS .ERRIDN (1)(M) FICOMP
ADD .DATA(M) .ERRIDNI (1)(M) FIDN
ALIGN .DATA? (M) ERRIF FIDNR
.ALPHA(M) DATASEG ERRIF1 FILD
AND DB (2) ERRIF2 FIMUL
ARG DD(2) ERRIFB FINCSTP
ARPL DEC ERRIFDEF FINIT
ASSUME %DEPTH ERRIFDIF FIST
%BIN DF (2) ERRIFDIFI FISTP
BOUND DISPLAY ERRIFE FISUB
BSF DN ERRIFIDN FISBR
BSR OOSSEG ERRIFIDNI FLD
BT DP(2) ERRIFNB FLD!
BTC DQ(2) ERRIFNDEF FLDCW
BTR DT(2) .ERRNB (1)(M) FLDENV
BTS DW(2) .ERRNDEF (1)(M) FLDL2E
CALL ELSE (1) .ERRNZ (1)(M) FLDL2T
CATSTR(2) ELSEIF (1) ESC FLDLG2
CBW ELSEIF1 (1) EVEN FLDLN2
CDQ ELSEIF2 (1) EVENDATA FLDPI
CLC ELSEIFB (1) EXITM FLDZ
CLD ELSEIFDEF (1) EXTRN(1) FMUL
CLI ELSEIFDIF (1) F2XM1 FMULP
CLTS ELSEIFDIFI (1) FABS FNCLEX
CMC ELSEIFE (1) FADD FNDISI
CMP ELSEIFIDN (1) FADDP FNENI
CMPBW ELSEIFIDNI (1) FARDATA FNINIT
CMPS ELSEIFNB (1) .FARDATA (M) FNOP
CMPSB ELSEIFNDEF (1) .FARDATA? (M) FNSAVE
Appendix B, Turbo Assembler syntax summary 243
Table B.1 Turbo Assembler v1.0 (VERSION T1 00) keywords (continued)
FNSTCW IFIDN (1) JP LTR
FNSTENV IFIDNI (1, JPE %MACS
FNSTSW IFNB (1) JPO MACRO (2)
FPATAN IFNDEF (1) JS MASM
FPREM IJECXZ JUMP MODEL
FPTAN IMUL JUMPS .MODEL(M)
FRNDINT IN JZ MOV
FRSTOR INC LABEL (2) MOVMOVS
FSAVE %INCL LAHF MOVSB
FSCALE INCLUDE (1) .LALL(M) MOVSD
FSQRT INCLUDELIB (1) LAR MOVSW
FST INS LDS MOVSX
FSTCW INSB LEA MOVZX
FSTENV INSD LEAVE MUL
FSTP INSTR(2) LES MULTERRS
FSTSW INSW· .LFCOND(M) NAME (1)
FSUB INT LFS NEG
FSUBP INTO LGDT %NEWPAGE
FSUBR IRET LGS %NOCONDS
FSUBRP IREID LIDT %NOCREF
FTST IRP (1) %LINUM %NOCTLS
FWAIT IRPC (1) %LIST NOEMUL
FXAM JA .LIST(M) %NOINCL
FXCH JAE LLDT· NOJUMPS
FXTRACT JB LMSW %NOLIST
FYL2X JBE LOCAL NOLOCALS
FYL2xP1 JC LOCALS NOMASM51
FSETPM JCXZ LOCK %NOMACS
FPCOS IE LODS. NOMULTERRS
FPREM1 JG LODSB NOP
FPSIN JGE LODSD NOSMART
FPSINCOS JL LODSW %NOSYMS
FUCOM JLE LOOP NOT
FUCOMP JNA LOOPD %NOTRUNC
FUCOMPP JNAE LOOPDE NOWARN
GLOBAL (1) JNB LOOPDNE OR
GROUP (2) JNBE LOOPDNZ ORG
HLT JNC LOOPDZ OUT
IDEAL JNE LOOPE %OUT(l)
IDIV JNG LOOPNE OUTS
IF (1) JNGE LOOPNZ OUTSB
IF! (1) JNL LOOPW OUTSD
IF2 (1) JNLE LOOPWE OUTSW
IFb (1) JNO LOOPWNE P186
IFDEF (1) JNP LOOPWNZ P286
IFDIF (1) JNS LOOPWZ P286N
IFDIFI (1) JNZ LOOPZ P287
!FE (1) JO LSLLSS P386
244 Turbo Assembler User's Guide
Table B.1 Turbo Assembler v1.0 (VERSION T100) keywords (continued)
P386N REPT (1) SETNE STR
P387 REP SETNG STRUC(2)
P8086 REPE SETNGE SUB
P8087 REPNE SE1NL SUBSTR(2)
PAGE REPNZ SE1NLE SUBTIL (1)
%PAGESIZE REPZ SETNO %SUBTIL
%PCNT RET SETNP %SYMS
PN087 RETF SETNS %TABSIZE
POP RETN SETNZ TEST
paPA ROL SETa %TEXT
POPAD ROR SETP .TFCOND(M)
POPFD SAHF SETPE TITLE (1)
%POPLCTL SAL SETPO %TITLE
PPF .5ALL(M) SETS %TRUNC
PROC (2) SAR SETZ UDATASEG
PUSH SBB .SFCOND(M) UFARDATA
PUSHA SCAS SGDT UNION (2)
PUSHAD SCASB SHL USES
PUSHF SCASD SHLD VERR
PUSHFD SCASW SHR VERW
%PUSHLCTL SEGMENT (2) SHRD WAIT
PUBLIC (1) .5EQ(M) SIDT WARN
PURGE SETA SIZESTR(2) .)eALL(M)
%PAGESIZE SETAE SLDT XCHG
%PCNT SETB SMART .XCREF(M)
PN087 SETBE SMSW XLAT
%POPLCTL SETC SOR XLATB
PROC(2) SETE STACK .XLIST(M)
%PUSHLCTL SETG .5TACK(M) USECS
PUBLIC (1) SETGE .sTARTUP (M) USEDS
PURGE SETL STC USEES
QUIRKS SETLE SID USEFS
RADIX SETNA STI USEGS
.RADIX(M) SETNAE STOS USESS
RCL SETNB STOSB
RCR SETNBE STOSD
RECORD (2) SETNC STOSW
Turbo Assembler version 2.0 supports aUversion 1.0 keywords, with the following
additions:
Table B.2 Turbo Assembler v2.0 (VERSION T200) new keywords
BSWAP
CMPXCHG
INVD
XADD
P486
P486N
P487
INVLPG
STARTUPCODE
WBINVD
PUBLICDLL (I)
RETCODE
APpen dix B, TurboA sse mbIer synt ax sum mar y 245
Turbo Assembler version 2.5 supports all version 2.0 keywords, plus the following
keyword additions:
Table B.3 Turbo Assembler v2.5 (VERSION T250) new keywords
ENTERD LEAVED
ENTERW LEAVEW
Turbo Assembler version 3.0 supports keywords from all previous versions, with the
following additions:
Table B.4 Turbo Assembler v3.0 (VERSION T300) new keywords
CLRFLAG GOTO(l) TBLINIT
ENUM(2) LARGESTACK TBLINST
EXITCODE SETFIELD TYPEDEF
FASTIMUL SETFLAG TBLINIT
FLIPFLAG SMALLSTACK TBLINST
GETFIELD TABLE (2) VERSION
WHILE (1)
Turbo Assembler version 3.1 supports keywords from all previous versions, with the .
following additions:
Table B.5 Turbo Assembler v3.1(VERSION T31 0) new keywords
PUSHSTATE POPSTATE
Turbo Assembler version 3.2 supports keywords from all previous versions, with the
following additions: .
Table B.6 Turbo Assembler v3.2 (VERSION T320) new keywords
IRETW
POPAW
PUSHFW
POPFW
PROCDESC(2)
PROCTYPE(2)
PUSHAW
Turbo Assembler version 4.0 supports keywords from ~ll previous versions, with the
following additions:
Table B.7 Turbo Assembler v4.0 (VERSION T400) new keywords
ALIAS
CMPXCHG8B
CPUID
P586
246 TurboA sse mbI,e r Use r' s Guide
P586N
P587
RDMSR
RDTSC
RSM
WRMSR
Turbo Assembler version 5.0 supports keywords from all previous versions, with the
following additions:
Table B.8 Turbo Assembler v5.0 (VERSION T500) new keywords
BREAK .CONTINUE .ELSE
.ELSEIF .ENDIF .ENDW
.IF .LISTALL .LISTIF
.LISTMACRO .LISTMACROALL NOLIST
NOLISTIF .NOLISTMACRO .REPEAT
.UNTIL .UNTILCXZ .WHILE
CARRY? ECHO EXPORT
EXTERN EXTERNDEF FAR16
FAR32 FOR FORC
NEAR16 NEAR32 OPTION
OVERFLOW? PARITY? PRIVATE
PROTO PUBLIC REALIO
REAL4 REAL8 REPEAT
SBYTE SDWORD SIGN?
STRUCT SUBTITLE SWORD
ZERO?
The following options are supported with the OPTION keyword:
Table B.9 Options supported by OPTION
CASEMAP DOTNAME NODOTNAME
EMULATOR NOEMULATOR EPILOGUE
EXPR16 EXPR32 LANGUAGE
LJMP NOLJMP M510
NOM510 NOKEYWORD NOSIGNEXTEND
OFFSET OLDMACROS NOOLDMACROS
OLDSTRUCTS NOOLDSTRUCTS PROC
PROLOGUE READONLY NOREADONLY
SCOPED NOSCOPED SEGMENT
SETIF2
APpen dix B, TurboA sse mbIer syntax sum mar y 247
248 Turbo AssemblerUser's Guide
MASM 6.1 compatibility
Turbo Assembler 5.0 (TASM32) supports most of the features of Microsoft MASM
version 6.1. This Appendix documents the new features added to Turbo Assembler
specifically to provide compatibility with MASM 6.0/6.1.
Basic data types
Turbo Assembler now supports the use of type names as directives when defuUng
variables. For example, the line:
var DB 10
can now be written as:
var BYTE 10
Table C.1 shows the type names and their equivalent directives.
Table C.1 Turbo Assembler types and their equivalent directives
BYTE DB
DWORD DD
FWORD DF
QWORD DQ
TBYTE DT
WORD DW
APpen dixC, MAS M 6. 1 com pat ibiii t Y 249
Signed types .
Table C.21ist the specifications of the new signed integer types.
Table C.2 Signed integer data types
-128 to +127
-32,768 to +32,767
SBYfE
SWORD
SDWORD·
1
2
4 --'2,147,483,648 to +2,147,483,647
Floating-point types
Table C.31ists the specifications for the new floating point types.
Table C.3 Floating-point data types
REAL8
REALlO
Long real
lO-byte real 80
15-16
19 3.37 x 10--4932 to 1.18 x 104932
Floating point constants can be designated as decimal constants or encoded
hexadecimal constants, as shown in the following examples:
; Real decimal numbers
dshort REAL4 34.56 ;IEEE format
ddouble REAL8
dtenbyte REAL10
3.456El iIEEE format
3456.0E-2 i10-byte real format
; Hexadecimals, note the required trailing "r" and leading decimal digit
hexshort REAL4 4E700000r iIEEE short
hexdouble REAL8 4E70000000000000r iIEEE long
hextenbyte REAL10 4E776000000000000000rilO-byte real
New decision a~d looping directives
Turbo Assembler now supports several high level directives to permit program
structureS similar to those in higher level languages, such as C++and Object Pascal.
These directives generate code for loops and decisions, which are executed depending
on the status of a conditional statement. The conditions are tested at rUn-time, and can
use the new run-time operators ==, !=, >=, <=, >, <, &&, I I, and!.
.IF .ELSE .ELSEIF .ENDIF
The directives .IF, .ELSE, .ENDIF generate conditional jumps. If the expression
following .IF evaluates to true, then the statements following the .IF are executed until
an .ELSE (if any), .ELSEIF (if any), or .ENDIF directive is encountered. If the .IF
expression evaluates tofalse, the statements following the .ELSE (if any) are executed
until an .ENDIF directive is encountered. Use .ELSEIF to cause a secondary expression
to be evaluated if the .IF expression evaluates tofalse.
250 Turbo Assembler User's Guide
The syntax for the.IF directives is:
.IF expression1
statements
[.ELSEIF expression2
statements]
[.ELSE
statements]
.ENDIF
Example
.IF bx == 16
mov ax,20
.. ELSE
if the value in bx' equals 16
mov ax,30
.ENDIF
if the value in bx does not equal 16
.WHILE .ENDW
The .WHILE directive executes the statements between the .WHILE and the .ENDW as
long as the expression following .WHILE evaluates to true, or until a .BREAK directive
is encountered. Because the expression is evaluated at the beginning of the loop, the
statements within the loop will not execute at all if the expression initially evaluates to
false. If a .CONTINUE directive is encountered within the body of the loop, control is
passed immediately back to the .WHILE where the expression is re-evaluated. If
.BREAK is encountered, control is immediately passed to the statement following the
.ENDW directive.
The syntax for the .WHILE directives is: .
.WHILE expression
statements
.ENDW
Example
mov ax,
.WHILE ax < 128
mov dx, cx
.IF dx == bx
mov ax, dx
.CONTINUE
.ELSEIFax == dx
.BREAK
.ENDIF
inc ax
.ENDW
i initialize ax to 0
i while ax is less than 128
put the value of cx in dx
if dx and bx are equal
put the value of dx in ax
re-evaluate .WHILE expression
if ax equals dx
break out of the .WHILE loop
increment ax by 1
end of .WHILE loop
APpen di xC, MAS M 6. 1 com pat ibiii t Y 251
.REPEAT .UNTIL .UNTILCXZ
The .REPEAT directive executes the statements between the .REPEAT and the .UNTIL
as long as the expression following the .UNTIL (or .UNTILCXZ) evaluates to true, or
until a .BREAK directive is encountered. Because the expression is evaluated at the end
of the loop, the statements within the loop will execute at least once, even if the
expression initially evaluates tofalse. If a .CONTINUE directive is encountered within
the body of the loop, control is passed immediately to the .UNTIL where the expression
is re-evaluated. If .BREAK is encountered, control is immediately passed to the
statement following the .UNTIL (or .UNTILCXZ) directive. The .UNTIL directive
generates conditional jumps. The .UNTILCXZ directive generates a LOOP instruction.
The syntax for the .REPEAT directives is:
.REPEAT
statements
.UNTIL expression
Example
mov ax,
.REPEAT
inc ax
.UNTIL ax >= 128
i initialize ax to a
i while ax is less than 128
increment ax by 1
i end of .REPEAT loop
.BREAK .CONTINUE
As noted above, .BREAK and .CONTINUE can be used to alter the program flow
within a loop. .CONTINUE causes the loop to immediately re-evaluate its expression,
bypassing any remaining statements in the loop. .BREAK terminates the loop and
passes control to the statement following the end of the loop.
Both .BREAK and .CONTINUE can be combined with an optional .IF directive. If the
.IF expression evaluates to true, the .BREAK or .CONTINUE are carried out, otherwise
they are ignored.
Example
mov ax, bx
.WHILE ax != ex
.BREAK .IFax == dx
.CONTINUE .IF ax > dx
inc ax
.ENDW
252 TurboA sse mbIer Use r's Guide
Logical operators
Turbo Assembler now supports several C-like logical operators, as shown in Table C.4.
Table C.4 New Turbo Assembler logical operators
is equal to
!= is not equal to
>= is greater than or equal to
<= is less than or equal to
> is greater than
< is less than
&& and
II or
not
& bit test
Using flags in conditions
Turbo Assembler permits the use flag value in conditions. The supported flag names are
ZERO?, CARRY?, OVERFLOW?, SIGN?, and PARITY? For example, to use the value
of the CARRY flag in a loop expression, use:
.WHILE (CARRY?) ; if the CARRY flag is set...
statements
.ENDW
Text Macros
A string of characters can be given a symbolic name, and have that name used in the
source code rather than the string itself. The named text is referred to as a text macro. Use
the TEXTEQU directive to define a text macro.
To assign a literal string to a text macro, enclose the string in angle brackets «».For
example:
myString TEXTEQU <This is my string>
To assign one macro to another text macro, assign the macro name as in the example
below:
myString TEXTEQU <This is my string>
myNewString TEXTEQU myString ;value of myString now in myNewString as well
To assign a text representation of a constant expression to a text macro, precede the
expression with a percent sign (%). For example:
value TEXTEQU %(1 + nurn);assigns text representation of resolved expression t~ value
Appendix C, MASM 6.1 compatibility 253
Text macros are useful for naming strings of text that do not evaluate to integers. For
example: .
pi
WPT
arg
TEXTEQU <3.14159>
TEXTEQU <WORD PTR>
TEXTEQU <[bp+4J>
floating point constant
keyworo-s
expression
Macro repeat blocks with loop directives.
Turbo Assembler supports "repeatblocks", or unnamed macros within a loop directive.
The loop directive generates the statements inside the repeat block a specified number
Qftimes. .
REPEAT loops
Use REPEAT to specify the number of times to generate the statements inside the macro.
The syntax is:
REPEAT constant .
statements
ENDM
Example
number LABEL BYTE
counter = 0
REPEAT 128
BYTE counter
i name the generated data
initialize counter
i repeat 128 times
i allocate a new number
counter = counter + 1; increment counter
ENDM
FOR loops
Use the FORloop to iterate through a list of arguments, using the first argument the first
time through, the second argument the second time through, and so on. The syntax is:
FOR parameter, <argumentList>
statements
ENDM
The parameter represents the name of each argument inside the FOR block. The
argumentList is comma separated and enclosed in angle brackets.
Example
powers LABEL BYTE
FOR arg, <1,2,4,8,16,32,64,128>
BYTE arg DUP (arg)
ENDM
254 Turbo Assembler User's Guide
The first iteration through the FOR loop sets arg to 1. The second iteration sets arg to 2.
The third sets arg to 4, and so on.
Text macros may be used in place of literal strings of values. The VARARG directive
can be used in the argumentList to create a variable number of arguments.
FORC loops
FORC loops are almost identical to FOR loops, except that the argumentList is given a a
string, rather than as a comma separated list. The loop reads the string, character by
character (including spaces), and uses one character per iteration. The syntax is:
FORC parameter, <text>
statements
ENDM
The parameter represents the name of each argument inside the FOR block. The text is a
character string and enclosed in angle brackets.
Example
alphabet LABEL BYTE
FORC arg, <ABCDEFGHIJKLMNOPQRSTUVWXYZ>
BYTE '&arg' i allocate letter
ENDM
New Directives
For MASM compatibility, Turbo Assembler now supports the directive STRUCT,
EXTERN, and PROTO. These directives are synonyms for the STRUC, EXTRN, and
PROCDESC directives, respectively.
ECHO directive
The ECHO directive displays its argument to the standard output device during
assembly. It is useful for debugging purposes. The syntax is:
ECHO argument
EXTERNDEF directive
Turbo Assembler treats EXTERNDEF as a PUBLIC declaration in the defining module,
and as an external declaration in the referencing module(s). Use EXTERNDEF to make
a variable or procedure common to two or more modules. Ifan EXTERNDEF variable
or procedure is defined but not referenced in a givenmodule, the EXTERNDEF is
ignored; you need not create a symbol as you would using EXTERN. The syntax of the
EXTERNDEF statement is:
EXTERNDEF [ZangType] name: type
Appendix C, MASM 6.1 compatibility 255
OPTION directive
The OPTION directive lets you make global changes to the behavior of the assembler.
The basic syntax of the directive is:
OPTION argument
For example, to make the expression word size 16 bits, use the statement:
OPTION EXPR16
To make the expression word size 32 bits, use the statement:
OPTION EXPR32
The available options are listed below:
CASEMAP: NONE/NOTPUBLIC/ALL '
NONE causes internal symbol recognition to be case sensitive, and causes the case of
identifiers in the .OBJ file to be the same as specified in the EXTERNDEF, PUBLIC, or
COMM statement.
NOTPUBLIC (default) causes case insensitivity foriI;1.ternal symbol recognition, and has
the same behavior as NONE for identifiers in .OBJ files.
ALL specifies universal case insensitivity and converts all identifiers to uppercase.
DOTNAME/NODOTNAME
Enables or disables the use of the dot'{.) as the leading character in variable, macro,
structure, union, and member names. The default is disabled.
EMUlATOR/NOEMULATOR
NOEMULATOR (default) tells the assembler to generate floating point math
coprocessor instructions directly.
EMULATOR generates floating point math instructions with special fixups for linking
with a coprocessor emulator library.
EXPR16/EXPR32
Sets the expression word size to 16 or 32 bits. The default is 32 bits.
LJMP/NOLJMP
En';lbles or disables automatic conditional-jump lengthening. The default is enabled.
NOKEYWORD: <keywordLisb
Disables the keywords listed in keywordList. For example:
OPTION NOKEYWORD:<MASK EXPORT NAME>
256 Turbo Assembler User's Guide
PROC: PRIVATE/PUBLIC/EXPORT
Allows you to set the default PROC visibility as PRIVATE, PUBLIC, or EXPORT. The
default is PUBLIC.
SCOPED/NOSCOPED
SCOPED (the default) guarantees that all labels inside procedures are local to the
procedure.
SEGMENT: USE16/USE32/FLAT
Sets the global default segment size and the default address size for external symbols
defined outside any segment.
Visibility in procedure declarations
Turbo Assembler supports three visibility modes in procedure (PROC) declarations;
PRIVATE, PUBLIC, and EXPORT. The visibility indicates whether the procedure is
available to other modules. PUBLIC procedures are availableto other modules. All
procedures are PUBLIC by default. PRIVATE procedures are available only within the
module in which they are declared. Code in other modules cannot call PRIVATE
procedures. If the visibility is EXPORT, the linker places the procedure's name in the
export table for segmented executables. EXPORT also enables PUBLIC visibility.
Distance in procedure declarations
In addition to the ability to specify NEAR or FAR distance in procedure (PROC)
declarations, Turbo Assembler now supports the modifiers NEAR16, NEAR32, FAR16,
and FAR32 when programming for the 80386, and up, and using both 16 and 32-bit
segments.
.SIZE operator in MASM mode
In MASM mode the size operator returns the values in table C.5 for the given labels.
Table C.S Return value of SIZE in MASM mode
SHORT OFFOlh
NEAR16 OFF02h
NEAR32 OFF04h
FAR16 OFF05h
FAR32 OFF06h
APpen di xC, MAS M 6. 1 com pat ibiii tY 257
Compatibility issues
Turbo Assembler in MASM mode is very compatible with MASM version 6.l.
. However,100% compatibility is an ideal that can only be approached, since there is no
formal specification for the language and different versions of MASM are not even
compatible with each other.
For most programs, you will have no problem using Turbo Assembler as a direct
replacement for MASM. Occasionally, Turbo Assembler will issue warnings or errors
where MASM would not, which usuallymeans that MASM has not detected an
erroneous statement. For example, MASM accepts
abc EQU [BP+2]
PUBLIC abc
and.generates a nonsense object file. Turbo Assembler correctly detects this and many
other questionable constructs.
If you are having trouble assembling a program with Turbo Assembler, you might try
using the QUIRKS directive (which enables potentially troublesome features of
MASM). For example, . .
TASM /JQUIRKS MYFILE
might make your program assemble properly. Ifit does, add QUIRKS to the top of your
source file. Evenbetter, review Chapter 3 and determine which statement in your source
file needs the QUIRKS directive. Then you can rewrite the line(s) of code so that you
don't even have to use QUIRKS.
For maximum compatibility with MASM, you should use the NOSMART directive
along with QUIRKS mode. .
One-pass versus two-pass assembly·
Normally, Turbo Assembler performs only one pass when assembling code, while
MASM performs two. This feature gives Turbo Assembler a speed advantage, but can.
introduce minor incompatibilities when forward references and pass-dependent
constructions are involved. The command-line option1m specifies the number of passes
desired. For maximum compatibility with MASM, two passes (1m2) should be used.
(See Chapter 2 for a complete discussion of this option.) The 1m2 command-line switch
will generate a MASM-style compatibility when the following constructs are present:
• IFl and IF2 directives
• ERRl and ERR2 directives
• ESLEIFl and ELSEIF2 directives
• Forward references with IFDEF or IFNDEF
• Forward references with the .TYPE operator
• Recursively defined numbers, such as NMBR=NMBR+l
258 Turbo Assembler User's GuJde
• Forward-referenced or recursively defined text macros, such as
LNAME CATSTR LNAME,<l>
• Forward-referenced macros
Environment variables
Turbo Assembler doesn't use environment variables to control default options.
However, you can place default options in a configuration file and then set up different
configuration files for different projects.
Ifyou use INCLUDE or MASM environment variables to configure MASM, you'll have
to make a configuration file for Turbo Assembler. Any options that you have specified
using the MASM variable can simply be placed in the configuration file. Any directories
that you have specified using the INCLUDE variable should be placed in the
configuration file using the II command-line option.
Microsoft binary floating-point format
By default, older versions of MASM generated floating-point numbers in a format
incompatible with the IEEE standard floating-point format. MASM version 6.1
generates IEEE floating-point data by default and has the .MSFLOAT directive to
specify that the older format be used.
Turbo Assembler does not support the old floating-point format, and therefore does not
letyou use .MSFLOAT.
Appendix C, MASM 6. 1c0 mpat ibiii t Y 259
260 TurboA sse mbIer Use r's Guide
Error messages
This chapter describes all the messages that Turbo Assembler generates. Messages usually appear
on the screen, but you can redirect them to a file or printer using the standard OS/2 redirection
mechanism of putting the device or file name on the command line, preceded by the greater than
(» symbol. For example,
TASMMYFILE >ERRORS
Turbo Assembler generates several types of messages:
• Information messages
• Warnillg messages
• Error messages
• Fatal error messages
Information messages
Turbo Assembler displays two information messages: one when it starts assembling your source
file(s) and another when it has finished assembling each file. Here's a sample startup display:
Turbo Assembler Version 4.0 Copyright (C) 1988, 1993 Borland International
Assembling file: TEST.ASM
When Turbo Assembler finishes assembling your source file, it displays a message that summarizes
the assembly process; the message looks like this:
Error messages: None
Warning messages: None
Passes: 1
Remaining memory: 279k
You can suppress all information messages by using the rr command-line option. This only
suppresses the information messages if no errors occur during assembly. If there are any errors, the
rroption has no effect and the normal startup and ending messages appear.
Appendix D, Error messages 261
Warning and error messages
Warning messages let you know that something undesirable may have happened while assembling
a source statement. This might besomething such as the Turbo Assembler making an assumption
that is usually valid, but might not always be correct. You should always examine the cause of
warning messages to see if the generated code is what you wanted. Warning messages won't stop
Turbo Assembler from generating an object file. These messages are displayed using the following
format:
**Warning** filename (line) message
If the warning occurs while expanding a macro or repeat block, the warning message contains
additional information, naming the macro and the line within it where the warning occurred:
**Warning** filename (line) macroname(macroline) message
Error messages, on the otherh~d, will prohibit Turbo Assembler from generating an object file, but
assembly will continue to the end of the file. Here's a typical error message format:
**Error** filename (line) message
If the error occurs while expanding a macro or repeat block, the error message contains additional
information, naming the macro and the line within it where the error occurred:
**Error** filename (line) macroname(macroline) message
Fatal error messages cause Turbo Assembler to immediately stop assembling your file. Whatever
caused the error prohibited the assembler from being able to continue.
The following list arranges Turbo Assembler's messages in alphabetical order:
32-bit segments not allowed without .386
Has been extended to work with the new abilityto specify USE32 in the .MODEL statement and the LARGESTACK
command. Formerly was "USE32 not allowed without .386."
Argument mismatch
Argument sizes did not agree. For example,
foo proctype pascal :word, :dword
fooproc proc foo al:word, a2:dword
endp
call fooproc,ax,bx ;Argument mismatch.
Argument needs type override
The expression needs to have a specific size or type supplied, since its size can't be determined from the context. For
example,
mov [bx],l
You can usually correct this error by using the PTR operator to set the size of the operand:
mov WORD PTR[bx],l
Argument to operation or instruction has illegal size
An operation was attempted on something that could not support the required operation. For example,
Q LABEL QWORD
QNOT =not Q ;can't negate a qword
262 TurboA sse mbIer Use r' s Guide
Arithmetic overflow
A loss of arithmetic precision occurred somewhere in the expression. For example,
X = 20000h * 20000h ;overflows 32 bits
All calculations are performed using 32-bit arithmetic.
ASSUME must be segment register
You have used something other than a segment register in an ASSUME statement. For example,
ASSUME ax:CODE
You can only use segment regi~ters with the ASSUME directive.
Bad k~yword in SEGMENT statement
One of the align/combine/use arguments to the SEGMENT directive is invalid. For example,
DATA SEGMENT PAFA PUBLIC ;PAFA should be PARA
Bad switch
You have used an invalid command-line option. See Chapter 2 for a'description of the command-line options.
Can't add relative quantities
You have specified an expression that attempts to add together two addresses, which is a meaningless operation.
For example,
ABC DB ?
DEF = ABC + ABC ;errorI can't add two relatives
You can subtract two relative addresses, or you can add a constant to a relative address, as in:
XYZ DB 5 DUP (0)
XYZEND EQU $
XYZLEN = SYZEND - XYZ
XYZ2 = XYZ + 2
;perfectly legal
;legal also
Can't address with currently ASSUMEd segment registers
An expression contains a reference to a variable for which you have not specified the segment register needed to
reach it. For example,
DSEG SEGMENT
ASSUME ds:DSEG
mov si/MPTR
DSEG ENDS
;no segment register to reach XSEG
XSEG SEGMENT
MPTR DW
XSEG ENDS
Can't convert to pointer
Part of the expression could not be converted to a memory pointer, for example, by using the PTR operator,
mov el, [BYTE PTR all ;can't make AL into pointer
Can't emulate 8087 instruction
The Turbo Assembleris set to generate emulated floating-point instructions, either via the /E command-line option
or by using the EMUL directive, but the current instruction can't be emulated. For example,
EMUL
FNSAVE [WPTRl ;can't emulate this
The following instructions are not supported by floating-point emulators: FNSAVE, FNSTCW, FNSTENV, and
FNSTSW.
Can't find @file
You have specified an indirect command file name that does not exist. Make sure that you supply the complete file
name. Turbo Assembler does not presume any default extension for the file name. You've probably run out of space
on the disk where you asked the cross-reference file to be written.
Appendix D, Error messages 263
Can't generate instance of type
You attempted to generate an instance of a named type that does not have an instance. For example,
foo typedef near
foo ? iNEARs have no instance.
Can't locate file
You have specified a file name with the INCLUDE directive that can't be found.
An INCLUDE file could not be located. Make sure that the name contains any necessary disk letter or directory
path.
Can't make variable public
The variable is already declared in such a way that it can't be made public. For example,
EXTRN ABC:NEAR
PUBLIC ABC ierror, already EXTRN
Can't override ES segment
The current statement specifies an override that can't be used with that instruction. For example,
stos DS:BYTE PTR[diJ
Here, the STOS instruction can only use the ES register to access the destination address.
Can't subtract dissimilar relative quantities
An expression subtracts two addresses that can't be subtracted from each other, such as when they are each in a
different segment: .
SEGl SEGMENT
A:
SEGl ENDS
SEG2 SEGMENT
B:
mov aX,B-A
SEG2 ENDS
iillegal, A and B in different segments
Can't use macro name in expression
A macro name was encountered as part of an expression~ For example,
MyMac MACRO
ENDM
mov aX,MyMac iwrong!
Can't use this outside macro
You have used a directive outside a macro definition that can only be used inside a macro definition. This includes
directives like ENDM and EXITM. For example,
DATA SEGMENT
ENDM ierror, not inside macro
Code ordata emission to undeclared segment
A statement that generated code or data is outside of any segment declared with the SEGMENT directive. For
example,
iFirst line of file
inc bx
END
ierror, no segment
You can only emit code or data from within a segment.
Constant assumed to mean immediate constant
This warning appears if you use an expression such as [0], which under MASM is interpreted as simply O. For
example, ,
mov ax, [OJ imeans mov ax,O NOT mov aX,DS: [OJ
264 TurboA sse mbIer Use r 's Guide
Constant too large
You have entered a constant value that is properly formatted, but is too large. For example, you can only use
numbers larger than Offffh when you have enabled 80386 or i486 instructions with the .386/.386P or .486/.486P
directives.
CS not correctly assumed
A near CALL or JMP instruction can't have as its target an address in a different segment. For example,
SEGl SEGMENT
LABl LABEL NEAR
SEGl ENDS
SEG2 SEGMENT
jmp LABl ;error, ,wrong segment
SEG2 ENDS
This error only occurs in MASM mode. Ideal mode correctly handles this situation.
, CS override in protected mode
The current instruction requires a CS override, and you are assembling instructions for the 80286, 80386, or i486 in
protected mode (P286P, P386P, or P486 directives). For example,
P286P
.CODE
CVAL DW
mov CVAL,l ;generates CS override
The /P command-line option enables this warning. When running in protected mode, instructions with CS
overrides won't work without you taking special measures.
CS unreachable from current segment
When defining a code label using colon (:), LABEL or PROC, the CS register is not assumed to either the current
code segment or to a group that contains the current code segment. For example,
PROGl SEGMENT
.ASSUME cs:PROG2
START: ;error, bad CS assume
This error only occurs in MASM mode. Ideal mode correctly handles this situation.
Data or code written to uninitialized segment
You have inadvertently written initialized code or data to an uninitialized segment. For example,
.data?
msg .db 'Hello',O ; error, uninitialized segment
Declaration needs name
You have used a directive that needs a symbol name, but none has been supplied. For example,
PROC ierror, PROC needs a name
ret
ENDP
You must always supply a name as part of a SEGMENT, PROC, or STRUC declaration. In MASM mode, the name
precedes the directive; in Ideal mode, the name comes after the directive. I
Appendix D, Error messages 265
Directive not allowed inside structure definition
You have used a directive inside a STRUC definition block that can't be used there. For example,
X STRU
MEM1 DB
ORG $+4
MEM2 DW 7
ENDS
ierror, can't use ORG inside STRU
Also, when declaring nested structures, you cannot give a name to any that are nested. For example,
FOO STRU
F002 STRUC ican't name inside
ENDS
ENDS
If you want to use a named structure inside another structure; you must first define the structure and then use that
structure name inside the second structure.
Duplicate dummy argument: _
A macro defined with the MACRO directive has more than one dummy parameter with the same name. For
example,
XYZ MACRO A,A ierror, duplicate dummy name
DB A
ENDM
Each dummy parameter in a macro definition must have a different name.
ELSE or ENDIF without IF
An ELSE or ENDIF directive has no matching IF directive to start a conditional assembly block. For example,
BUF DB 10 DUP (7)
ENDIF ierror, no 'matching IFxxx
Error writing to listing file
You've probably run out of space on the disk where you asked the listing file to be written.
Error writing to object file
You've probably run out of space on the disk where you asked the object file to be written.
Expecting METHOD keyword
The extended structure statement for defining objects expects the keyword METHOD after the parent object.
Expecting offset quantity
An expression expected an operand that referred to an offset within a segment, but did not encounter the right sort
of operand. For example,
,-CODE SEGMENT
mov ax, LOW CODE
CODE ENDS
Expecting offset or pointer quantity
An expression expected an operand that referred to an offset within a specific segment, but did not encounter the
right sort of operand. Forexample,
CODE SEGMENT
mov ax,SEG CODE
CODE ENDS
Expecting pointer type
ierror, code is a segment not
i -a location within a segment
The current instruction expected an operand that referenced memory. For example,
les di,4 ina good, 4 is a constant
266 TurboA sse mbIer Use r's Guide
Expecting record field name
You used a SETfIELD or GETFIELD instruction without a field name following it.
Expecting register ID
The USES part of the CALL..METHOD expects register name(s).
Expecting scalar type
An instruction operand or operator expects a constant value. For example,
BB DB 4
rol . ax,BB iROL needs constant
Expecting segment or group quantity
A statement required a segment or group name, but did not find one. For example,
DATA SEGMENT
ASSUME ds:FOO ierror, FOO is not group or segment
iname
FOO DW 0
DATA ENDS
Extra characters on line
A valid expression was encountered, but there are still characters left on the line. For example,
ABC = 4 shl 3 3 imissing operator between 3 and 3
This error often happens in conjunction with another error that caused the expression parser to lose track of what
you intended to do.
File not found
The source file name you specified on the command line does not exist. Make sure you typed the name correctly,
and that you included any necessary drive or path information if the file is not in the current directory.
File was changed or deleted while assembly in progress
Another program, such as a pop-up utility, has changed or deleted the file after Turbo Assembler opened it. Turbo
Assembler can't reopen a file that was previously opened successfully.
Forward reference needs override
An expression containing a forward-referenced variable resulted in more code being required than Turbo
Assembler anticipated. This can happen either when the variable is unexpectedly a far address for a JMP or CALL or
when the variable requires a segment override in order to access it. For example,
ASSUME cs:DATA
call A
A PROC FAR
mov ax,MEMVAR
DATA. SEGMENT
ipresume near call
iOOpS, it's far
idoesn't know it needs override
MEMVAR DW ? ;oops, needs override
Correct this by explicitly supplying the segment override or FAR override.
Globaltype doesn't match symbol type . .
This warning is given when a symbol is declared using the GLOBAL statement and is also defined in the same
module, but the type specified in the GLOBAL and the actual type of the symbol don't agree.
APpen di x D, Err 0 r messag es 267
10 not member of structure
In Ideal mode, you have specified a symbol that is not a structure member name after the period (.) structure
member operator. For example, .
IDEAL
STRUC DEMO
DB
ENDS
COUNT DW 0
mav aX t [(DEMO bx) .COUNT] iCOUNT not part of structure
You must follow the period with the name of a member that belongs to the,structure name that precedes the period.
This error often happens in conjunction with another error that caused the expression parser to lose track of what
you intended to do.
Illegal forward reference
A symbol has been referred to that has not yet been defined, and a directive or operator requires that its argument
not be forward-referenced. For example,
IF MYSYM ierrort MYSYM not defined y~t
ENDIF
MYSYM EQU 1
Forward references may not be used in the argument to any of the IFxxx directives, nor as the count in a DUP
expression. .
Illegal immediate
An instruction has an immediate (constant) operand wher~ one is not allowed. For example,
mov 4tal
Illegal indexing mode
An instruction has an operand that specifies an illegal combination of registers. For example,
mov alt[si+ax]
On all processors except the 80386, the only valid combinations of index registers are: BX, BP, SI, DI, BX+SI, BX+DI,
BP+SI, BP+DI.
Illegal instruction
A source line starts with a symbol that is neither one of the known directives nor a valid instruction mnemonic.
move ax t 4 ishould be 11 MOV11
Illegal instruction for currently selected processor(s)
A source line specifies an instruction that can't be assembled for the current processor. For example,
.8086
push 1234h ino immediate push on 8086
When Turbo Assembler first starts assembling a source file, it generates instructions for the 8086 processor, unless
told to do otherwise.
If you wish to use the extended instruction mnemonics available on the 186/286/386 processors, you must use one
of the directives that enables those instructions (P186; P286, P386).
Illegal local argument
The LOCAL directive inside a macro definition has an argument that is not a valid symbol name. For example,
X MACRO
LOCAL 123
ENDM
inot a symbol
268 TurboA sse mbIer Use r's Guide
Illegal local symbol prefix
The argument to the LOCALS directive specifies an invalid start for local symbols. For example,
LOCALS XYZ ierror, not 2 characters
The local symbol prefix must be exactly two characters that themselves are a valid symbol name, such as _ -' @@,
and so on (the default is @@).
Illegal macro argument
A macro defined with the MACRO directive has a dummy argument that is not a valid symbol name. For example,
X MACRO 123 iinvalid dummy argument
ENDM
Illegal memory reference
An instruction has an operand that refers to a memory location, but a memory location is not allowed for that
operand. For example,
mov [bxl ,BYTE PTR A ierror, can't move from MEM to MEM
Here, both operands refer to a memory location, which is not a legal form of the MOV instruction. On the 80x86
family of processors, only one of the operands to an instruction can refer to a memory location.
Illegal number
A number contains one or more ~haracters that are not valid for that type of number. For example,
Z = OABCGh
Here, G is not a valid letter in a hexadecimal number.
Illegal origin address
You have entered an invalid address to set the current segment location ($). You can enter either a constant or an
expression using the location counter ($), or a symbol in the current segment.
Illegal override in structure
You have attempted to initialize a structure member that was defined using the DUP operator. You can only
initialize structure members that were declared without DUP.
Illegal override register
A register other than a segment register (C5, D5, E5, 55, and on the 80386, F5 and G5) was used as a segment
override, preceding the colon (:) operator. For example,
. mov dX:XYZ,l iDX not a segment register
Illegal radix
The number supplied to the .RADIX directive that sets the default number radix is invalid. For example,
.RADIX 7 ino good
The radix can only be set to one of 2,8, la, or 16. The number is interpreted as decimal no matter what the current
default radix is.
Illegal register for instruction
An illegal register was used as the source of a SETFIELD instruction or the destination of a GETFIELD instruction.
Illegal register multiplier
You have attempted to multiply a register by a value, which is not a legal operation; for example,
movax*3,l
The only context where you can multiply a register by a constant expression is when specifying a scaled index
operand on the 80386 processor.
Illegal segment address
This error appears if an address greater than 65,535 is specified as a constant segment address; for example,
FOO SEGMENT AT 12345h
Illegal use of constant
A constant appears as part of an expression where constants can't be used. For example,
mov bx+4,5
Appendix D, Error messages 269
Illegal use of register
A register name appeared in an expression where it can't be used. For example, .
X = 4 sh1 ax ican't ,use register with SHL operator
Illegal use of segment register
A segment register name appears as part of an instruction or expression where segment registers cannot be used.
For example,
add ·SS,4 iADD can't use segment regs
IllegalUSES register ,
You have entered an invalid register to push and pop as part of entering and leaving a procedure. The valid
registers follow:
AX BX CX Dr
DS DX ES SI
If you have enabled the 80386 processor with the .386 or .386P directive, you can use the 32-bit equivalents for these
registers.
Illegal version ID
Occurs when an illegal version ID was selected in t~e VERSION statement or IV switch.
Illegal.warning ID
You have entered an invalid three-character warning identifier. See the options discussed in Chapter 2 for a
complete list of the allowedwarning identifiers.
Instruction can be compacted with override
The code generated contains NOP padding, due to some forward-referenced symbol. You can either remove the
forward reference or explicitly provide the type information as part of the expression. For example,
jmp X iwarning here
jmp SHORT X ino warning
X:
Insufficient memory to process command line
You have specified a command line that is either longer than 64K or can't be expanded in the available memory.
Either simplify the command line or run Turbo Assembler with more memory free.
Internal error
This message should never happen during normal operation of Turbo Assembler. Save the file(s) that caused the
error and report it to Borland's Technical Support department.
Invalid command line
The command line that you used to start Turbo Assembler is badly formed. For example,
TASM ,MYFILE
does not specify a source file to assemble. See Chapter 2 for a complete description of the Turbo Assembler
command line.
Invalid model type
The model directive has an invalid memory model keyword. For example,
.MODEL GIGANTIC
Valid memory models are tiny, small, compact, medium, large, and huge,
Invalid number after
You have specified a valid command-line switch (option), but have not supplied a valid numeric argument
following the switch. See Chapter 2 for a discussion of the command-line options.
Invalid operand(s) to instruction
The instruction has a combination of operands that are not permitted. For example,
fadd ST(2),ST(3)
Here, FADD can only refer to one stack register by name; the other must be the stack top.
270 TurboA sse mbIer Use r's Guide
Labels can't start with numeric characters
You have entered a symbol that is neither a valid number nor a valid symbol name, such as 123XYZ.
Language differs from procedure type
You attempted to use a different language than what was contained in the procedure type declaration. For example,
foo proctype windows pascal :word
fooproc proc foo al:word
endp
call fooproc c,ax iLanguage doesn't match.
Language doesn't support variable-length arguments
You specified a variable-length stack frame with a language that doesn't support it. For example,
foo proctype pascal :word, :unknown iPascal can't have
ivariable arguments.
Line too long-truncating
The current line in the source file is longer than 255 characters. The excess characters will be ignored.
Location counter overflow
The current segment has filled up, and subsequent code or data will overwrite the beginning of the segment. For
example,
ORG OFFFOh
ARRAY OW 20 OOP (0) ioverflow
Method CALL requires object name
:The CALL..METHOD statement c.annot obtain the object type from this instance pointer. You must specify the
object name.
Missing argument list
An IRP or IRPC repeat block directive does not have an argument to substitute for the dummy parameter. For
example,
IRP X ino argument list
DB X
ENDM
IRP and IRPC must always have both a dummy parameter and an argument list.
Missing argument or <
You forgot the angle brackets or the entire expression in an expression that requires them. For example,
ifb ineeds an argument in <>s
Missing argument size variable .
An ARG or LOCAL directive does not have a symbol name following the optional =at the end of the statement. For
example,
ARG A:WORD,B:DWORD= ierror, no name after =
LOCAL X:TBYTE= isame error here
ARG and LOCAL must always have a symbol name if you have used the optional equal sign (=) to indicate that you
want to define a size variable.
Missing COMM ID
A COMM directive does not have a symbol name before the type specifier. For example,
COMM NEAR ierror, no symbol name before "NEAR"
COMM must always have a symbol name befor~ the type specifier,Jollowed by a colon (:) and then the type
specifier.
APpen di x 0, Err 0 r messag es .271
Missing dummy argument .
An IRP or IRPC repeat block directive does not have a dummy parameter. For example,
RP ino dummy parameter
DB X
ENDM
IRP and IRPC must always have both a dummy parameter and an argument list.
Missing end quote
A string or character constant did not end with a quote character. For example,
DB "abc imissing ' at end of AB
mov aI, 'X imissing , after X
You should always end a character or string constant with a quote character matching the one that started it.
Missing macro ID
A macro defined with the MACRO directive has not been given a name. For example,
MACRO ierror, no name
DB A
ENDM
Macros must always be given a name when they are defined.
Missing module name
You have used the NAME directive but you haven't supplied a module name after the directive. Remember that the
NAME directive only has an effect in Ideal mode.
Missing or illegal language ID
You have entered something other than one of the allowed language identifiers after the .MODEL directive. See
Chapter 7for a complete description of the .MODEL directive.
Missing or illegal type specifier
A statement that needed a type specifier (like BYTE, WORD, and so on) did not find one where expected. For
example, !
RED LABEL XXX ierror, "XXX" is not a type specifier
Missing table member ID .
A CALL..METHOD statement was missing the method name after the METHOD keyword.
Missing term in list
In Ideal mode, a directive that can accept multiple arguments (EXTRN, PUBLIC, and so on) separated by commas
does not have an argument after one of the commas in the list. For example,
EXTRN XXX:BYTE"YYY:WORD
In Ideal mode, all argument lists must have their elements separated by precisely one comma, with no comma at the
end of the list. ,
Missing text macro
You have notsupplied a text macro argument to a directive that requires one. For example,
NEWSTR SUBSTR iERROR - SUBSTR NEEDS ARGUMENTS
Model must be specified first
You used one of the simplified segmentation directives without first specifying a memory model. For example,
.CODE ierror, no .MODEL first
You must always specify a memory model using the .MODEL directive before using any of the other simplified
segmentation directives.
272 TurboA sse mbIer Use r's Guide
Module is pass-dependent-compatibility pass was done
This warning occurs if a pass-dependent construction was encountered and the 1m command-line switch was
specified. A MASM-compatible pass was done.
You put a symbol name after a directive, and the symbol name should come first. For example,
STRUC ABC ;error, ABC must come before STRUC
Since Ideal mode expects the name to come after the directive, you will encounter this error if you try to assemble
Ideal mode programs in MASM mode.
Near jump or call to different cs .
This error occurs if the user attempts to perform a NEAR CALL or JMP to a symbol that's defined in an area where
CS is assumed to a different segment.
Need address or register
An instruction does not have a second operand supplied, even though there is a comma present to separate two
operands; for example,
mov ax, ;no second operand
Need angle brackets for structure fill
A statement that allocates storage for a structure does not specify an initializer list. For example,
STRl STRU
Ml DW
M2 DD
ENDS
STRl ;no initializer list
Need colon
An EXTRN, GLOBAL, ARG, or LOCAL statement is missing the colon after the type specifier (BYTE, WORD, and
so on). For example,
EXTRN XBYTE,Y:WORD ;X has no colon
Need expression
An expression has an operator that is missing an operand. For example,
X = 4 + * 6
Need file name after INCLUDE
An INCLUDE dir~ctive did not have a file name after it. For example,
INCLUDE ;include what?
In Ideal mode, the file name must be enclosed in quotes.
Need left parenthesis
A left parenthesis was· omitted that is required in the expression syntax. For example,
DB 4 DUP 7
You must always enclose the expression after the DUP operator in parentheses.
Need method name
The CALL..METHOD statement requires a method name after the METHOD keyword.
Need pointer expression
This error only occurs in Ideal mode and indicates that the expression between brackets ([ ]) does not evaluate to a
memory pointer. For example,
mov ax, [WORD PTR]
In Ideal mode, you must always supply a memory-referencing expression between the brackets.
APpen di x 0, Err 0 r messag es 273
Need quoted string
You have entered something other than a string ofcharacters between quotes where it is required. In Ideal mode,
several directives require their argument to be a quoted string. For example,
IDEAL .
DISPLAY"ALL 'DONE"
Need register in expression
You have entered an expression that does not contain a register name where one is required.
Need right angle bracket
An expression that initializes a structure, union, or record does not end with a > to match the < that started the
initializer list. For example,
MYSTRUC STRUCNAME <1,2,3
Need right curly bracket
Occurs during a named structure, table, or record fill when a '}' is expected but not found.
Need right parenthesis
An expression contains a left parenthesis, but no matching right parenthesis. For example,
X = 5 * (4 + 3
You must always use left and right parentheses in matching pairs.
Need right square bracket
An expression that references a memory location does not end with a ] to match the [ that started the expression. For
ex~mple,
mov ax, lsi ;error, no closing J after SI
You must always use square brackets in matching pairs.
Need stack argument
A floating-point instruction does not have a second operand supplied, even though there is a comma present to
separate two operands. For example,
fadd ST,
Need structure member name
In Ideal mode, the period C.) structure member operator was followed by something that was not a structure
member name. For example,
IDEAL
STRUC DEMO
DB
ENDS
COUNT DW
mov ax, [(DEMO bx) .J
You must always follow the period operator with the name of a member in the structure to its left.
Not expecting group or segment quantity
You have used a group or segment name where it can't be used. For example,
CODE SEGMENT
rol ax,CObE ;error, can't use segment name here
One non-null field allowed per union expansion
When initializing a union defined with the UNION directive, more than one value was supplied. For example,
U UNION
ENDS
DW
DD
UINSTU <1,2> ierror, should be <7,2> or <1,?>
A union can only be initialized to one value.
274 TurboAsse mbIer Use r 's Guide
Only one startup sequence allowed
This error appears if you have more than one .STARTUP or STARTUPCODE statement in a module.
Open conditional
The end of the source file has been reached as defined with the END directive, but a conditional assembly block
started with one of the IFxxx directives has not been ended with the ENDIF directive. For example,
IF BIGBUF
END ino ENDIF before END
This usually happens when you type END instead of ENDIF to end a conditional block.
Open procedure
The end of the source file has been reached as defined with the END directive, but a procedure block started with
the PROC directive has not been ended with the ENDP directive. For example,
MYFUNC PRO
END ino ENDIF before ENDP
This usually happens when you type END instead of ENDP to end a procedure block.
Open segment
The end of the source file has been reached as defined with the END directive, but a segment started with the
SEGMENT directive has not been ended with the ENDS directive. For example,
DATA SEGMENT
END ino ENDS before END
This usually happens when you type END instead of ENDS to end a segment.
Open structure definition
The end of the source file has been reached as defined with the END directive, but a structure started with the
STRUC directive has not been ended with the ENDS directive. For example,
X STRU
VALl DW
END ino ENDS before it
This usually happens when you type END instead of ENDS to end a structure definition.
Operand types do not match
The size of an instruction operand does not match either the other operand or one valid for the instruction; for
example,
ABC DB 5
mov ax, ABC
Operation illegal with procedure type
You used the structure member operator on an expression whose type is a procedure. For example,
foo proctype pascal :word
mov ax, [foo ptr [bx]) .member iThings of type Faa
ihave no members
Operation illegal with static table member
A '.' operator was used to obtain the address of a static table member. This is illegaL
Out of hash space
The hash space has one entry for each symbol you define in your program. It starts out allowing 16,384 symbols to
be defined, as long as Turbo Assembler is running with enough free memory. If your program has more than this
many symbols, use the /KH command-line option to set the number of symbol entries you need in the hash table.
APpen di x 0 J Err 0 r messag es 275
Out of memory
You don't have enough free memory for Turbo Assembler to assemble your file.
If you have any TSR (RAM-resident) programs installed, you can try removing them from memory and try
assembling your file again. You may have to reboot your system in order for memory to be properly freed.
Another solution is to split the source file into two or more source files, or rewrite portions of it so that it requires
less memory to-assemble. You can also use shorter symbol names, reduce the number of comments in macros, and
reduce the number of forward references in your program.
Out of string space
You don't have enough free memory for symbol names,file nameS, forward-reference tracking information, and
macro text. A maximum of S12K is allowed, and your module has exceeded this maximum.
Pass-dependent construction'encountered
The statement may not behave as you expect,due to the one-pass nature of Turbo Assembler. For example,
IF1
ENDIF
IF2
ENDIF
iHappens on assembly pass
iHappenson listing pass
Most constructs that generate this error can be re-coded to avoid it, often by removing forward references.
Pointer expression needs brackets
In Ideal mode, the operand contained a memory-referencing symbol that was not surrounded by brackets to
indicate that it references a memory location. For example,
B DB 0
mov al,B iwarning, Ideal mode needs [B]
Since MASM mode does not require the brackets, this is only a warning.
Positive count expected
A DUP expression has a repeat count less than zero. For example,
BUF -1 DUP (?) ierror; count < 0
The count preceding a DUP must always be 1 or greater.
Procedure has too many arguments
A procedure was declared with too many arguments. For example,
footype PROCTYPE pascal :word, :dword
foo proc footype
arg a1:word,a2:dword,a3:word
nop
endp
Procedure needs more arguments
itoo many arguments were declared for
ifor this proc
A procedure was declared with to? few arguments. For example,
footyp~ PROCTYPE pascal :word , :dword
foo proc footype
arg a1:word
nop
ret
endp
Record field too large
iNeeds a DWORD argument somewhere too.
When you defined a record, the sum total of all the field widths exceeded 32 bits. For example,
AREC RECORD RANGE:12,TOP:12,BOTTOM:12
276 TurboA sse mbIer Use r's Guide
Record member not found
A record member was specified in a named record fill that was not part of the specified record.
Recursive definition not allowed for EQU
An EQU definition contained the same name that you are defining within the definition itself. For example,
ABC EQU TWOTIMES ABC
Register must be AL or AX
An instruction which requires one operand to be the AL or AX register has been given an invalid operand. For
example,
IN CL,dx ierror, "IN" must be to AL or lJ,.
Register must be DX
An instruction which requires one operand to be the DX register has been given an invalid operand. For example,
IN AL,cx ierror, must be DX register instead of CX
Relative jump out of range by _ bytes
A conditional jump tried to reference an address that was greater than 128 bytes before or 127bytes after the current
location. If this is in a USE32 segment, the conditional jump can reference between 32,768 bytes before and 32,767
bytes after the current location.
Relative quantity illegal
An instruction or directive has an operand that refers to a memory address in a way that can't be known at assembly
time, and this is not allowed. For example,
DATA SEGMENT PUBLI
X DB 0
IF OFFSET X GT 127 inot known at assemble time
Reserved word used as symbol
You have created a symbol name in your program that Turbo Assembler reserves for its own use. Your program
will assemble properly, but it is good practice not to use reserved words for your own symbol names.
Rotate count must be constant or CL
A shift or rotate instruction has been given an operand that is neither a constant nor the CL register. For example,
rol ax,DL ierror, can't use DL as count
You can only use a constant value or the CL register as the second operand to a rotate or shift instruction.
Rotate count out of range
A shift or rotate instruction has been given a second operand that is too large. For example,
.8086
shl DL,3
.286
ierror, 8086 can only shift by
ro~ ax,40 ierror, max shift is 31
The 8086 processor only allows a shift count of I, but the other processors allow a shift count up to 31.
Segment alignment not strict enough
The align boundary value supplied is invalid. Either it is not a power of 2, or it specifies an alignment stricter than
that of the align type in the SEGMENT directive. For example,
DATA SEGMENT PARA
ALIGN 32
ALIGN 3
ierror, PARA is only 16
ierror, not power of 2
APpen di x 0, Err 0 r mes sag es 277
Segment attributes illegally redefined
A SEGMENT directive reopen a segment that has been previously defined, and tries to give it different attributes.
For example,
DATA SEGMENT BYTE PUBLI
DATA ENDS
DATA SEGMENT PARA
DATA ENDS
ierror, previously had byte alignment
If you reopen asegment, the attributes you supply must either match exactly or be omitted entirely. If you don't
supply any attributes when reopening a segment, the old attributes will be used.
Segment name is superfluous i
This warning appears with a .CODE xxx statement, where the model specified doesn't allow more than code
segment.
String too long
You have built a quoted string that is longer than the maximum allowed length of 255.
Style differs from procedure type
Youattempted to use a different language style than the declaration of the procedure type contained. For example,
foo proctype windows pascal :word
fooproc proc foo a1:word
endp
call fooproc normal pascal,ax iStyle doesn't match.
Symbol already defined: _
The indicated symbol has previously been declared with the same type. For example,
BB DB 1,2,3
BB DB ? ierror, BB already defined
Symbol already different kind , ,
The indicated symbol has already been declared before with a different type. For example,
BB DB 1,2,3
BB DW ? ierror, BB already a byte
Symbol has no width or mask
The operand of a WIDTH or MASK operator is not the name of a record or record field. For example,
B DB O·
mov aX,MASK B iB is not a record field
Symbol is'not a segment or already part of a.group
The symbol has either already been placed in a group or it is not a segment name. For example,
DATA SEGMENT
DATA
DGROUP
DGROUP2
ENDS
GROUP DATA
GROUP DATA ierror, DATA already belongs to
iDGROUP
Text macro expansion exceeds maxilhum line length
This error occurs when expansion of a text macro causes the maximum allowable line length to be exceeded.
278 TurboA sse mbIer Use r's Guide
Too few arguments to procedure
You called a procedure using too few arguments. For example,
fooproctype pascal :word, :dword
fooproc proc foo a1:word, a2:dword
endp
call fooproc,ax iToo few arguments.
Too few operands to instruction
The instruction statement requires more operands than were supplied. For example,
add ax imissing second arg
Too many arguments to procedure
You called a procedure using too many arguments. For example,
foo proctype pascal :word, :dword
fooproc proc foo a1:word, a2:dword
endp
call fooproc,ax,bx cx,dx iToo many arguments.
Too many errors found
Turbo Assembler has stopped assembling your file because it contained so many errors. You may have made a few
errors that have snowballed. For example, failing to define a symbol that you use on many lines is really a single
error (failing to define the symbol), but you will get an error message for each line that referred to the symbol.
Turbo Assembler will stop assembling your file if it encounters a total of 100 errors or warnings.
Too many errors or warnings
No more error messages will be displayed. The maximum number of errors that will be displayed is 100; this
number has been exceeded. Turbo Assembler continues to assemble and prints warnings rather than error
messages.
Too many initial values
You have supplied too many values in a structure or union initialization. For example,
XYZ STRU
A1 DB ?
A2 DD,?
XYZ ENDS
ANXYZ XYZ <1,2,3> ierror, only 2 members in XYZ
You can supply fewer initializers than there are members in a structure or union, but never more.
Too many register multipliers in expression
An 80386 scaled index operand had a scale factor on more than one register. For example,
mov EAX, [2*EBX+4*EDX] itoo many scale$
Too many registers in expression
The expression has more than one index and one base register. For example,
mov ax, [BP+S1+D1] ican't have S1 and DI
Too many USES registers
You specified more than 8 USES registers for the current procedure.
Trailing null value assumed
A data statement like DB, OW, and so on, ends with a comma. TASM treats this as a null value. For example,
db 'hello' ,13,10, isame as ... ,13 ,10 ,?
Undefined symbol
The statement contains a symbol that wasn't defined anywhere in the source file.
APpen di x D, Et r0 r messag es 279
Unexpected end of file (no END directive)
The source file does not have an END directive as its last statement. All source files must have an END statement.
Unknown character
The current source line contains a character that is not part of the set ofcharacters that make up Turbo Assembler
symbol names or expressions. For example,
add aX,!l ierror, exclamation is illegal character
Unmatched ENDP:
The ENDP directive has a name that does not match the PROC directive that opened the procedure block. For
example,
ABC PRO
XYZ ENDP ierror, XYZshould be ABC
Unmatched ENDS:
The ENDS directive has a name that does not match either the SEGMENT directive that opened a segment or the
STRUC or UNION directive that started a structure or union definition. For example,
ABC STRU
XYZ ENDS
DATA SEGMENT
CODE ENDS
User-generated error
ierror, XYZ should be AB
ierroI, code should be DATA
An error has been forced by one of the directives, which then forces an error. For example,
.ERR ishouldn't get here
USES has no effect without language
This warning appears if you specify a USES statement when no language is in effect.
Value out of range
The constant is a valid number, but it is too large to be used where it appears. For example,
DB 400
Variable length parameter must be last parameter
If a variable-length parameter is present, it must be the last parameter. For example,
foo proctype pascal :word, :unknown, :word iNot allowed.
280 Turbo Assembler User's Guide
Symbols
! character 167
$symbol 109
% expression evaluation
character 167
%immediate macro
directive 171
& character
in macros 162
+ addition operator 72
. (period) character
Ideal mode 32
.186 directive 76
.286 directive 76
.286C directive 76
.286P directive 76
.287 directive 79
.386 directive 76
386 processor
protected mode 21
.386C directive 76
.386P directive 76
.387 directive 79
.486 directive 76
.486C directive 76
.486P directive 76
.487 directive 76
80186 processor
.186 directive 76
P186 directive 76
80286 processor
.286 directive 76
.286C directive 76
.286P directive 76
protected mode 21
80287 coprocessor
.287 directive 79
P287 directive 79
80386 processor
.386 directive 76
.386C directive 76
.386P directive 76
loop instructions for .147
P386 directive 76
P386N directive 76
P386P directive 76
protected mode 21
80387 coprocessor
.387 directive 79
P387 directive 79
80486 processor
Index
.486 directive 76
.486C directive 76
.486P directive 76
P486 directive 76
P486N directive 76
protected mode 21
80487 processor
.487 directive 76
P487 directive 76
.8086 directive 76
8086 processor
.8086 directive 76
P8086 directive 76
PUSHing constants 149
segments and 81
8087 coprocessor 15,79
.8087 directive 79
Borland C++ and 214
P8087 directive 79
.8087 directive 79
:: directive 114
< > (bracket) initializer 138
nested structures/ unions
and 139,143
records and 140
< > (brackets)
, literal string 166
macros and 159
=(equals) directive 38
=directive 15
=sign, argument lists and 122
? keyword 101, 121
as initial value 137
? symbol 133
@@ symbol 131
@32Bit symbol 85
@-sign 26
[] (square brackets)
describing address
contents 71
Ideal mode 31
MASMmode 31
 line continuation character 165
{}(brace) initializer 137, 142
records and 140
A
/ a option 14, 22
address expressions See
expressions
address subtypes
complex 61
setting 67
address subtypes of symbols
distance parameter and 61
addresses, calculating 70
ALIAS 187
alias values 37
ALIGN directive 99,112
ALPHA directive 14, 23
.ALPHA directive 93
ancestor virtual methods 53
ARG directive 120, 121
Borland C++ and 212
arguments
BYTE 121
names (scope of) 122
substitution (defined) 162
arithmetic operators 66,72
.ASM files 1, 12
assembling
multiple passes 117
number of passes 18
ASSUME directive 91
at-sign 26
attribute values of segments 88
attributes
B
segment
access 90
alignment 89
class 89
combination 88
size 90
values of segments 88
/b option 13
@B symbol 131
Backus-Naur form (BNF)
grammar 62
%BIN directive 194,195
binary coded decimal (BCD)
encoding
DT directive and 136
bit-field records
defining 96
bit shift operators 67
-block scoping of symbols,
defined 130
books
assembly language 9
Boolean algebra and
operators 66
Index 281
symbol expressions and 179
Borland
contacting 4
Borland C++
ARG directive and 211
assembler modules in 15
case sensitivity 19, 206
code segment 200
data types 207
external symbols 208
floating-point emulation .15
linking to 221
LOCAL directive and 211
memory models 200
parameter passing 209
Pascal calling
conventions 220
public functions ~d 205
register preservation 213
returning values 214
segment directives and 200
structures 214
BOUND instruction
Ideal mode 32
buffers, size of 13
BYTE arguments 121
byte values 134
c
Ic option 14,193
calculating addresses 70
CALL instruction
See also CALL..METHOD
155
extended 155
CALL..METHOD
instruction 50,51,54
near tables and 157
case sensitivity
assembler routines and 19
Borland C++ 206
CATSTR directive 160
code-checking 21
.CODE directive 86
code generation, intelligent
(directives for) 145
code segments 82
Borland C++ 200
@code symbol 87
CODESEG directive 86
@CodeSize symbol 85
: (colon) operator 70
: operator 113
CO:M:M directive 186
command files, indirect 26
command-line options 11
command-line syntax 12
help screen 16
comment character 35
CO:M:MENT directive 35
comments
; (semicolon) comment
character 35
.. comment character 163
. comment character 35
COMMENT directive 35
end of line 35
including in macros 163
communal variables 186
MASM mode and 186
comparison operators 67
compatibility, MASM vs. Ideal
mode 258
complementary jumps 146
complex types 103, 104
compressing data, record data
types and 96 . .
conditional blocks (termmating)
See GOTO directive
conditional directives
assembly pass 182
defuUng blocks of code 175
expression 178
nesting 176
symbol-definition 179
text string 180
when to use 175
conditional list directives 191
%CONDS directive 192
CONST directive 86
.CONST directive 86
constants
defined "57
in expressions 62
numeric 57
rotation counts and shift
instructions 151
string 58
constructor and destructor
procedures
writing 126
coprocessor directives 79
@Cpu symbol 77
%CREF directive .193
.CREF directive 193 '
%CREFALL directive 193
%CREFREF directive 193
%CREFUREF directive 193
cross-reference
generating 13
in listing files 14
symbol information 193
282 Tur boA sse mbIe r Use r' s Guide
CS override 21
%CTLS directive 190
@curseg symbol 87
customer assistance 4
o
Id option 15
data
allocatillg 133
constants and 135
WORDS 133
defining 134
initialized (defined) 133
repeated blocks 133
storage in memory 134
uninitialized
defined 133
specifying 133
.DATA directive 86
.DATA? directive 86
@data symbol 87
data types
Borland C++ 207
creating named 142
creating record 140
declaring record 97
enumerated 95
creating instances of 141
initializing instances
of 141
multiline syntax 96
pseudo ops and 96
objects and 45
record, multiline syntax
for 97
table 102
multiline syntax 104
with virtual methods 143
DATASEG directive 86
@DataSize symbol 85
??date symbol 38
DB directive 134
DD directive 134, 136
debugging information 26
%DEPTH directive 195
derived objects 47
DF directive 134
directives
conditional 175
assembly pass 182
symbol-definiti0!l 179
conditional expressIOn 178
coprocessor 79
displaying assembly
messages 40
error-generation. 177
using symbol
expressions 179
include files 37
module names 40
processor 76
program termination 40
startup 17
symbols 17
DISPLAY directive 40
distance parameter
complex subtypes and 61
DLL
defined 231
OOS programs 229
COM blueprint 230
EXE blueprint 229
DOSSEG directive 93
:: directive 114
doubleword values 134
DP directive 134
DQ directive 134, 136
DT directive 134, 136
dummy arguments
defined 162
in macros 165
local 163
recognizing 162
types of 166
DUP keyword 133
DW directive 133,134
E
/ e option 15, 79
ELSEIF directive 178
ELSEIFB directive 181
ELSEIFDEF directive 179
ELSEIFDIF directive 181
ELSEIFDIFI directive 181
ELSEIFE directive 178
ELSEIFIDN directive 181
ELSEIFIDNI directive 181
ELSEIFNB directive 181
ELSEIFNDEF directive 179
ELSEIFxxx directives 177
EMUL directive 15, 79
encoded real numbers 136
END directive 40
ENDM keyword 165
ENDS directive 90,99,100
ENTER instruction 147
ENTERD instruction 147
ENTERW instruction 147
ENUM directive 95
enumerated data types
creating instances of 141
defined 95
initializiilg instances of 141
multiline syntax for 96
pseudo ops and 96
environment variables, MASM
mode 259
epilog code
defined 118
how it works 118
languages and 118
NOLANGUAGE procedures
and 119
register preservation and 123
specifying default style 84
EQU directive 37, 38, 159
Ideal vs. MASM mode 30
equal (=) directive 15
equate substitutions 37
ERR directive 178
.ERR directive 178
.ERR1 directive 182
.ERR2 directive 182
.ERRB directive 180
.ERRDEF directive 179
.ERRDIFI directive 180
.ERRE directive 179
.ERRIDN directive 180
.ERRIDNI directive 180
ERRrF directive 178
ERRIF1 directive 182
ERRIF2 directive 182
ERRIFB directive 180
ERRIFDEF directive 179
ERRIFDIF directive 180
ERRIFDIFI directive 180
ERRIFE directive 178
ERRIFIDN directive 180
ERRIFIDNI directive 180
ERRIFNB directive 180
ERRIFNDEF directive 179
.ERRNB directive 180
.ERRNDEF directive 180
.ERRNZ directive 178
error-generation directives 177
error messages 261-280
fatal 262
reporting 42
source file line display· 25
warning 262
ERRxxx directives 177
EVEN directive 112
EVENDATAdirective 112
.EXE files 1
.EXIT directive 87
EXITCODE directive 87, 230
EXITM directive 164
expressions
16-bit vs. 32-bit 72
BNF grammar and 62
byte values 72
constants in 62
contents of 61
determining
characteristics 70
evaluation character 167
Ideal mode 31
obtaining type of 68
precision of 62
register names and 62
segment overrides of 69
setting address subtypes 67
structure names in 102
symbols in 62
syntax of 235
text macro names and 64
whyto use 57
extended CALL instruction See
CALL..METHOD instruction
extern "C" 200
EXTRN directive 185
Borland C++ and 208
F
@F symbol 131
far data
initialized 82
uninitialized 82
far pointer values 135
FAR procedures 116
far returns, instructions for 148
FARDATA directive 86
.FARDATA directive 86 '
@fardata symbol 87
@fardata? symbol 87
.FARDATA? directive 86
fast immediate multiply
instruction See FASTIMUL
instruction
FASTIMUL instruction 154
fatal error messages 262
field value manipulation
instructions 152
file names 38
object-oriented programming
format 56
??filename symbol 38
@FileName symbol 38
files
.ASM 12
assembly 38
indirect 26
flag instructions, smart 152
Index 283
FLDENV instruction 155
FLIPFLAG instruction 152
floating-point
emulation 15
Ideal vs. MASM mode 259
instructions 2 ,
floating-point instructions See
coprocessor emulation
directives
floating-point numbers 136
FRSTOR instruction 155
FSAVE instruction 155
FSTENV instruction 155
G
GETFIELD instruction 153
GLOBAL directive 185
in .ASO files 56
objects and 47
global symbols, include files
and 185
GOTO directive 164
GROUP directive 91
Ideal vs. MASM mode 33
groups
H
assigning segments to 91
segment registers and 91
segments in Ideal mode 33
Ihoption 16
hardware and software
requirements 2
HELLO.ASM 8
help
displaying screen 16
online 7
HIGH operator 72
Ii option 16
i486 processor
protected mode 21
IDEAL directive 30
Idealmode 1
BOUND instruction 32
expressions 31
features 30
include files 37
operands 31
operators 32
predefined symbols 37
segment groups 33
speed 30
why to use 29, 30
IF directive 176, 178
IFI directive 176, 182
IF2 directive 176, 182
IFB directive 176, 181
IFDEF directive 176, 179
IFDIF directive 176, 181
IFDIFI directive 176, 181
!FE directive 178
IFIDN directive 176, 181
IFIDNI directive 176, 181
IFNB directive 169, 176, 181
IFNDEF directive 176, 179
IFxxx directives 175·
immediate macro directive
(%) 171
implied addition 72
%INCL directive 191
INCLUDE directive 16, 37
include files
Ideal mode 37
setting path 16
INCLUDELIB directive 187
indirect command files 26
information
technical support 4
inheritance
defined 47
example of 53
objects and 106
previous object
definitions 143
structure definitions and 101
initialization code 87
installation instructions 5
instances
creating object 55
creating structure or
union 137
creating table 141
initializing instances 138
initializing table 142
initializing union or
structure 137
named-type, creating 142
ofobjects 143
of records 140
virtual method table 53, ~43
virtual method table
(VMT) 49
INSTR directive 161
intelligent code generation
directives for 145
@Interface symbol
MODEL directive and 85
IRET instruction
284 Turbo Assembler User's Guide
expanded 148 I
lRETW instruction 148
IRP directive 170
IRPC directive 170
J
Ij option 17
jEMUL option 15
}MP instruction 155
}MP..METHOD instruction 54,
158
jumps
complementary 146
conditional 146
JUMPS directive 146
K
keyword precedence 241
Ideal mode 241
MASM mode 242
keywords 19
list of available 242
Ikh option 17
l
II option 14,1020
Ila option 18, 119
language modifiers and 120
LABEL directive 99,113
labels
defining 113
external 205
local in MASM 131
.LALL directive 192
language modifiers
WINDOWS procedures
and 120
languages .
MODEL and 118
modifiers and Windows
procedures 119
overriding default for
procedures 118
preserving registers and 123
procedures and
arguments 120
setting in CALL
. statement 222
LARGE operator 72, 154
instructions it affects 155
LARGESTACK directive 94
LEAVE instruction 147
LEAVED instruction 147
LEAVEWmstruction 147
length of symbols 19
LENGTH unary operator 64
LFCOND directive 25
.LFCOND directive 192
LGDT instruction 155
LIDT instruction 155
line continuation character (
) 165
line number information 25
linker
Borland C++ 209,221
PharLap 90
segment ordering and 92
%LINUM directive 195
%LIST directive 190
.LIST directive 190
listing files 12
IX command-line option
and 192
conditional listing
directives 191
cross-reference
information 14
cross-reference table and 189
directives for 190
false conditionals in 25
format of 189
format parameters 194
generating 17
high-level code in 18
including files in 191
including multiline
macros 172
macro expansions in 192
symbol table and 191
symbol table in 193
symbol tables
suppressing 20
why to use 189
literal string brackets 166
LOCAL directive 120, 121
Borland C++ and 211
in macros 163
local labels
inMASM 131
LOCALS directive 125,130
location counter
creating address
expressions 70
defined 109
directives for 110
location counter symbol 109
LOOP instruction 147
loop instructions for 80386
processor 147
LOOPD instruction 147
LOOPDE instruction 147
LOOPDNE instruction 147
LOOPDNZ instruction 147
LOOPDZ instruction 147
LOOPE instruction 147
LOOPNE instruction 147
LOOPWE instruction 147
LOOPWNE instruction 147
LOOPWNZ instruction 147
LOOPWZ instruction 147
LOOPZ instruction 147
LOWoperator 72
.LST files 12
M
1m option 18,117,258
macros
& character in 162
bodyof 162
controlling expansion 164
defining new text 160
defining substring 160
deleting multiline 168
dummy arguments
within 165
expansions in listing files 192
including comments in 163
invoking arguments with
special.characters 167
invoking general
multiline 166
length of text 161
manipulating string 160
multiline 161
defining general 165
multiline expansions inlisting
file 172
names in expressions 64
nested and recursive 168
redefining general
multiline 168
repeating 169, 170
. returning positions of
strings 161
string repeat 170
terminating assembly of 164
terminating body of 165
text
defined 159
examples of
manipulation 161
how to define 159
why to use 159
%MACS directive 192
MASK unary operator 65
MASKFLAG instruction 152
MASM compatibility 258
environment variables 259
expressions 31 '
floating-point format 259
NOSMART directive 258
predefined symbols 37
Quirks mode 258
segment groups 33
two-pass asssembly 258
MASM directive 30
member functions 218
memory models
available segments 82
Borland C++ 200
FAR code pointers 85
modifiers of 83
NEAR code pointers 85
segment attributes for 227
specifying values 84
standard 84
messages
reporting error 42
suppressing 23
warning 41
METHOD keyword 46, 48
method procedures
creating 125
defined 48
example of 49
structure of 55
methods
calling ancestor virtual 54
calling static 50
calling virtual 51, 52
defined 44
static versus virtual
advantages of 49
tables and 103
virtual 157
IML command-line switch 59
Iml option 18,37, 184
MODEL directive 82, 84
language modifiers and 119
.MODEL directive 82
@Model symbol 84
models, determining procedure
distance 116
modifiers,language 119
modular programming, module
names 40
modules, defined 183
@Mptr member 143
lMU command-line switch 59
Imuoption 19
MULTERRS directive 42
multiline definition syntax 36
multiline macros 161
defining general 165
Index 285
deleting general 168
including in listing file 172
invoking general 166
redefining general 168
multiline syntax
enumerated data types
and 96
record data types and 97
table data type definitions
. and 104
multiple assembly passes 18, 117
IMV command-line switch 59
Imv# option 19
IMX command-line switch 59
Imx option 19,184
N
Inoption 20
NAME directive 40
name-mangling 198
named structures, including 101
naming conventions of
symbols 183
NEAR procedures 116
near returns, instructions for 148
near tables, objects and 51
NEARSTACK modifier 83
nested procedures 124
%NEWPAGE directive 194
%NOCONDS directive 192
%NOCREF directive 193
%NOCTLS directive 190
NOEMUL directive IS, 79
%NOINCL directive 191
NOJUMPS directive 146
NOLANGUAGE interfacing
convention 156
NOLANGUAGE procedures
prolog and epilog code
and 119
%NOLIST directive 190
NOLOCALS directive 130
%NOMACS directive 192
NOMULTERRS directive 42
NOPs, avoiding generation
of 146
NOSMART directive 145
MASM compatibility 258
%NOSYMS directive 191
NOTHING keyword 91
%NOTRUNC directive 195
NOWARN directive 41
NUL device 13
null string, length of 161
numbers
encoded real 136
floating-point 136
numeric constants 57
numeric coprocessor 15
o
10 option 20
.OBJ files 1
·suppressing 22
object files
debugging information in .26
line number information
in 25
module name 40
segment ordering 14, 22
object methods
calling 157
tail recursion for 158
object modules, defined 8
@Object symbol 107
object-oriented programming
advantages of using 43, 44
defined 43
filename format 56
table data types and 103
objects
creating instances of 55,143
data types and 45
declaring 45, 47
defined 44
defining symbols 107
derived 47, 48
differences between
structures and 143
GLOBAL directive and 47
how to define 106
initializing instance's VMT
pointer 158
linked list example 45
method procedures and 106,
125
near tables and 51
structures and 106
TLINK compatable without
overlay code 21
virtual method table
instances 143
what they consist of 105
OFFSEToperator 32, 69
MASM vs. Ideal mode 33
offsets, getting segments and 69
loi option 21
online help 7
lop option 21,90
operands, Ideal mode 31
operators
286 Turbo Assembler User's Guide
bit shift 67
Boolean algebra and 66
comments 36
comparison 67
general arithmetic 66
Ideal vs. MASM mode 32
ORG directive 110
los option 21 .
OS12 programs
flat model format and 233
%OUT directive 40
overlay code
generating 20
IBM linker 21
PharLap linker 21
p
Ip option 21
P186 directive 76
P287 directive 79
P386 directive 76
P386N directive 76
P386P directive 76
P387 directive 79
P486 directive 76
P486N directive 76
P487 directive 76
P8086 directive 76
P8087 directive 79
PAGE directive 194
%PAGESIZE directive 194
parameter passing
Borland C++ 209
%PCNT directive 195
. (period) charactor
MASM vs. Ideal mode 236
. (period) operator 71
period, Ideal mode structures 32
Phar Lap linker 90
plus sign 12
pointers
virtual method table 50, 51,
52,53
POP instruction 155
multiple 148
pointers and 148
POPA instruction
expanded 149
POPAW instruction 149
POPFW instruction 149
%POPLCTL directive 196
POPSTAIE instruction 149
precedence
keyword 241
Ideal mode 241
MASM mode 242
PROC directive 115
PROC keyword, Ideal mode 31
PROCDESC directive 126, 156
procedure prototypes 126
procedure types, defining 123
procedures
calling and having
RETURNS 156
calling with arguments 156
declaring 115
defining types 105
determining distance of 117
FAR 116
interfacing conventions
of 155
languages for
arguments and 120
MODEL and 118
overriding default 118
method 55
creating 125
models and distance
of 116
NEAR 116
nesting and scope rules 124
NOLANGUAGE 119
prototypi.p.g 156
publishing prototypes 185
specifying languages for 118
stack frames and 121,147,
155
writing constructor and
destructor 126
processor directives 76
processor type, determining 77
PROCTYPE directive 105,123
program termination, END
directive and 40
prolog code
defined 118
langUages and 118
NOLANGUAGE procedures
and 119
register preservation and 123
specifying default style 84
what it does 118
protected mode 21
segment registers and 81
prototypes
procedure 126
procedure types and 127
publishing procedure 185
prototyping procedures 156
PUBLIC directive 184
public functions, Borland C++
and 205
PUBLICDLL directive 184
PURGE directive 168
PUSH instruction 155
multiple 148
pointers and 148
PUSHA instruction
expanded 149
PUSHAW instruction 149
PUSHF instruction '
expanded 149
PUSHFW instruction 149
PUSHing constants 149
%PUSHLCTL directive 196
PUSHSTATE instruction 149
Q
Iq option 22
quadword values 135
question mark
symbols using 38
QUIRKS directive 258
R
Ir option 15,22
.RADIX directive 58
RADIX directive 58,136
radixes
available 57
changing default 58
characters determining 58
default 136
real mode, segment registers
and 81
record data types
multiline syntax for 97
RECORD directive 97
records
< > and 140
{} and 140
. creating instances of 140
defining 140
initializing instances 140
retrieving data from 153
setting values in 152
reference books 9
registers
names of and expressions 62
preserving 123
preserving (Borland
C++) 213
segment 81
registration (product)
byphone 4
REPT directive 169
RET instruction, NEAR or FAR
and 147
RETCODE instruction 148
RETF instruction 148
RETN instruction 148
return instructions 147
RETURNS directive 156
s
I s option 14, 22
.sALL directive 192
scope of symbols
defined 129
scope rules for nested
procedures 124
SEG operator 69
SEGCS instruction 151
SEGDS instruction 151
SEGES instruction 151
SEGFS instruction 151
SEGGS instruction 151
SEGMENT directive 88
SEGMENT keyword, Ideal
mode 31
segments
8086 processor and 81
assigning to groups 91
attributes
access 90
alignment 89
class 89
combination 88
size 90
Borland C++ and 200
closing 90, 99
code 82
default attributes 227
directives (Borland C++
and) 200
forced overrides 151
generic'88- '
getting offsets and 69
groups
Ideal mode and 30, 33
MASMmode 33
groups and 82
how the stack is treated 81
memory models and 82
opening 88
ordering 14, 92
alphabetic 93
changing 92
DOS 93
sequential 93
overrides of expressions/69
registers 81
Index 287
registers and 91
sequential order 14, 22
simplified directives 86
size 78
symbols and 87
writing to uninitialized 89
SEGSS instruction 151
semicolon 12
within macros 36
SEQ directive 14
.sEQ directive 93
SETFIELD instruction 152
SETFLAG instruction 152
SFCOND directive 25
.SFCONDS directive 192
SGDT instruction 155
shift instructions, rotation counts '
and 151
SHL operator 67
SHR operator 67
SIDT instruction 155
simplified segment directives
See also individual listings
symbols and 87
size of instructions,
controlling 154
SIZE unary operator 65
SIZESTR directive' 161
SMALL operator 72, 154
instructions it affects 155
SMALLSTACK directive 94
SMART directive 145
MASM compatibility 258
smart flag instructions, why
they're useful 151
software and hardware
requirements 2
source files
include files 16
symbols 15
square brackets
Ideal mode 31
MASMmode 31
stack
changing size of 93
MODEL directive and 93
segments and 81
STACK directive 86
.sTACK directive 86
stack frame
defined 155
specifying arguments 120
@stack symbol 87 .'
.sTARTUP directive 87
@Startup symbol 87
STARTUPCODE directive 87
229 '
static methods
calling 50
versus virtual (advantages
of) 49
statistics, displaying 13
string constants 58
strings, quoted 136
STRUC directive 45,98,100,106
1~ ,
structures
aligning members 99
Borland C++ 214
bracketinitializerand
nested 139
closing 99
creating instances of 137
creating members 99
defined 98
differences between objects
and 143
including named 101
initializing instances 137
membernames and 99, 101
members and 98
names in expressions 102
,nested 102 '
nesting 100
objects and 106
opening a definition 98
SUBSTR directive 160
%SUBTTL directive 196
SUBTTL directive 195
support, technical 4
symbol tables
listing files and cross-
referencing 14
suppressing 20
symbols
address subtypes
complex 61
simple 60
aliases 37
block-scoped 130
block-scoped (disabling) 130
case sensitivity of 18, 19,59
@Cpu 77
??date ,38
defined 59
defining 15
dynamic link entry
points 184
enabling locally scoped 125
external 19,185
Borland C++ and 208
??filename 38
288 Turbo Assembler User's Guide
@FileName 38
global 185
in expressions 62
Jengthof 19
location counter 109
MASM block scoping 131
names of 59
naming conventions for .
languages 183
overriding language
setting 184
public 19, 184
publishing external 184
redefinable 129,130
restrictions 15
scope of (defined) 129
standard values 63
??time 38
types of 59
uppercase 19
values used by themselves 63
why to use 57
@WordSize 78
%SYMS directive 191
SYMTYPE operator 70
T
It option 23
TABLE directive 45
@Table symbol 107
@TableAddr member 143
@Tableaddr symbol 107
tables
creating instances of 141
data types 102
initializing instances of 142
overriding members 104
static members 102
virtual members 102
%TABSIZE directive 196
tags, macro 164
tail recursion code, instruction
for 158
TASM.CFG 27
TBLINIT directive 50
in .ASM files 56
TBLINIT instruction 158
TBLINST directive 49
in .ASM files 56
TBLINST pseudo-op 143
TBLPTR directive 106
TCREF utility 13
Technical Support
contacting 4
termination code 87
termination, END directive
and 40
TESTFLAG instruction 152
%TEXT directive 196
text macro names
in expressions 64
TFCOND directive 25
.TFCOND directive 192
THIS operator 70
time 38
??time symbol 38
%TITLE directive 196
TfTL,E directive 195
TLINK utility 209,221
example of 9
%TRUNC directive 195
two-pass assembly
MASM compatibility 258
type checking, Ideal mode 29
.TYPE operator 70
TYPE operator 68
type override operators 67
type-safe linkage 198
TYPEDEF directive 104 .
typefaces in this manual 3
types
u
complex 103,104
defining named 104
defining procedure 105
of expressions 68
procedure 123
symbol 59
I u command-line switch 39
luoption 23
UDATASEG directive 86
UFARDATA directive 86
underscore, and the C
language 205
UNINIT 89
UNION directive 98,100
unions
bracketinitializerand
nested 139
closing 99
creating instances of 137
defined 98
initialized data 101
initializing instances 137, 138
members and 98
multiple initialized
members 138
nested 102
nesting 100
opening a definition 98
uppercase, converting symbols
to 19
USE32 modifier 83
USES directive 123
v
Iv option 13, 23
variables, communal 186
VERSION directive 39
line continuation and 36
MASM compatability and 40
VIRTUAL keyword 47, 103
virtual method table
initializing 50
initializing pointer to 158
instances of 49, 53, 143 .
modifiers and. 106
objects and 106
pointers 52
pointers to 50, 51, 53, 106
virtual methods
ancestor 53
calling·51,52
object data types and 143
versus static (advantages
of) 49
virtual table pointers
determining size of 107
modifiers and 106
w
Iwoption 24
WARN directive 41
warning messages 41, 262
"mild" 24
generating 24
WHILE directive 170
WIDTH unary operator 65
Windows programs 231
16-bit blueprint 232
32-bit blueprint 232
DLL blueprint 231
word values 134
@WordSize symbol 78
x
/xoption 25
.xALL directive 192
.xCREF directive 193
.xLIST directive 190
.xRF files 13·
z
Iz option 25
/ zd option 25
I zi option 26
I zn option 26
Index 289
290 Turb0, Ass embIer Use r's Guide
97244898-Turbo-Assembler-Version-5-Users-Guide.pdf
Borland
Copyright © 1996 Borland International, Inc. All rights reserved. All Borland product names are trademarks of Borland International,
Inc. Corporate Headquarters: 100 Borland Way, Scotts Valley, CA 95066-3249, (408) 431-1000. Internet: http://www.borland.com
CompuServe: GO BORLAND. Offices in: Australia, Canada, France, Germany, Hong Kong, Japan, Latin America, Mexico,
The Netherlands, Taiwan, and United Kingdom • Part # LSM1350WW21774 • BOR 8907

More Related Content

DOCX
Bài giảng khóa học Google Data Analytics.docx
PDF
Réalisation d'un compilateur de mini langage - Khawarizmi
PDF
ch1_introduction_aux_systemes_embarques.pdf
PDF
Rapport_PFE_Securite (1).pdf
PDF
[BTL] Kiểm tra tính ổn định của hệ thống liên tục
PDF
Archi reseaux
PDF
Thuật toán PID - thích nghi dùng mạng nơ ron điều khiển hệ con lắc ngược đơn.pdf
PDF
Xây dựng giải thuật thích nghi điều khiển tối ưu máy phát điện đồng bộ trên c...
Bài giảng khóa học Google Data Analytics.docx
Réalisation d'un compilateur de mini langage - Khawarizmi
ch1_introduction_aux_systemes_embarques.pdf
Rapport_PFE_Securite (1).pdf
[BTL] Kiểm tra tính ổn định của hệ thống liên tục
Archi reseaux
Thuật toán PID - thích nghi dùng mạng nơ ron điều khiển hệ con lắc ngược đơn.pdf
Xây dựng giải thuật thích nghi điều khiển tối ưu máy phát điện đồng bộ trên c...

What's hot (20)

DOC
Tieu luan trai pho 22.01.2015
PDF
Rapport PFE DOUIEB_HMIDANI
PDF
Cour simulation ns2
PDF
Modulation Analogique
PPT
Buffer overflow(bao cao)
PDF
Chapitre i architectures des processeurs récents
PDF
Rapport stage OCP: Mise à jour des shémas des réseaux industriels aux niveau...
PDF
Nghiên Cứu Phân Hệ Thông Tin Vệ Tinh VINASAT1
DOCX
Conduite d'un projet informatique - Etude Préalable
PPTX
Présentation NAC-NAP PPT HARIFI Madiha
PDF
Hướng dẫn thực hành các trạm MPS.pdf
PPTX
La Voiture Electrique
PDF
Slide metaploit
DOCX
Tp 3 transmission de donné modulation d'amplitude,de fréquence et de phase
PPTX
CHƯƠNG 6-TTHCM VỀ ĐẠO ĐỨC CANKIEMLIEMCHINH.pptx
PDF
MÉMOIRE DE MASTER
PDF
Hệ điều hành (chương 1)
DOCX
TIỂU LUẬN CNXH KHOA HỌC - Copy.docx
PPTX
Chuong 4. lap trinh hop ngu
PDF
Robot Scara - Tính Toán Động Học & Điều Khiển
Tieu luan trai pho 22.01.2015
Rapport PFE DOUIEB_HMIDANI
Cour simulation ns2
Modulation Analogique
Buffer overflow(bao cao)
Chapitre i architectures des processeurs récents
Rapport stage OCP: Mise à jour des shémas des réseaux industriels aux niveau...
Nghiên Cứu Phân Hệ Thông Tin Vệ Tinh VINASAT1
Conduite d'un projet informatique - Etude Préalable
Présentation NAC-NAP PPT HARIFI Madiha
Hướng dẫn thực hành các trạm MPS.pdf
La Voiture Electrique
Slide metaploit
Tp 3 transmission de donné modulation d'amplitude,de fréquence et de phase
CHƯƠNG 6-TTHCM VỀ ĐẠO ĐỨC CANKIEMLIEMCHINH.pptx
MÉMOIRE DE MASTER
Hệ điều hành (chương 1)
TIỂU LUẬN CNXH KHOA HỌC - Copy.docx
Chuong 4. lap trinh hop ngu
Robot Scara - Tính Toán Động Học & Điều Khiển
Ad

Similar to 97244898-Turbo-Assembler-Version-5-Users-Guide.pdf (20)

PDF
Assembly Language Programming Vincent Mahout
PDF
Arm assembly language by Bournemouth Unversity
PDF
eclipse.pdf
PDF
Assembly Codes in C Programmes - A Short Notes by Arun Umrao
PDF
Enterprise Cobol Programming Guide For Zos And Os390 32 Ibm
PDF
PDF
8051 micro controller
PDF
Notes of 8051 Micro Controller for BCA, MCA, MSC (CS), MSC (IT) & AMIE IEI- b...
PDF
Assembly
PPT
9CM405.24.ppt
PPTX
Module 3 Computer Organization Data Hazards.pptx
PDF
PPTX
CHAPTER 4and answer equation and cf.pptx
PPTX
8086 Assembly Language and Serial Monitor Operation of 8086 Trainer Kit
PDF
E views 9 command ref
PDF
Writing a C Compiler Build a Real Programming Language from Scratch Nora Sandler
PDF
Module-2 computer organisation vtu,karnataka
PDF
Assembler directives and basic steps ALP of 8086
PDF
Uni cambridge
 
PDF
Java Programming Notes for Beginners by Arun Umrao
Assembly Language Programming Vincent Mahout
Arm assembly language by Bournemouth Unversity
eclipse.pdf
Assembly Codes in C Programmes - A Short Notes by Arun Umrao
Enterprise Cobol Programming Guide For Zos And Os390 32 Ibm
8051 micro controller
Notes of 8051 Micro Controller for BCA, MCA, MSC (CS), MSC (IT) & AMIE IEI- b...
Assembly
9CM405.24.ppt
Module 3 Computer Organization Data Hazards.pptx
CHAPTER 4and answer equation and cf.pptx
8086 Assembly Language and Serial Monitor Operation of 8086 Trainer Kit
E views 9 command ref
Writing a C Compiler Build a Real Programming Language from Scratch Nora Sandler
Module-2 computer organisation vtu,karnataka
Assembler directives and basic steps ALP of 8086
Uni cambridge
 
Java Programming Notes for Beginners by Arun Umrao
Ad

More from kalelboss (9)

PDF
Antennas-and-Radiowave-Propagation-by-Collin.pdf
PDF
452042223-Modern-Fortran-in-practice-pdf.pdf
PDF
316912609-radar-handbook-pdf.pdf
PDF
351111888-Commodore-64-Assembly-Language-Arcade-Programming-pdf.pdf
PDF
309150037-Fortran-95-2003-for-Scientists-and-Engineers.pdf
PDF
314621436-6502ApplicationsBook-RodneyZaks-pdf.pdf
PDF
300816766-Atari-PILOT-for-Beginners.pdf
PDF
11 lajes macicas
PDF
21 tabelas de lajes
Antennas-and-Radiowave-Propagation-by-Collin.pdf
452042223-Modern-Fortran-in-practice-pdf.pdf
316912609-radar-handbook-pdf.pdf
351111888-Commodore-64-Assembly-Language-Arcade-Programming-pdf.pdf
309150037-Fortran-95-2003-for-Scientists-and-Engineers.pdf
314621436-6502ApplicationsBook-RodneyZaks-pdf.pdf
300816766-Atari-PILOT-for-Beginners.pdf
11 lajes macicas
21 tabelas de lajes

Recently uploaded (20)

PDF
July 2025 - Top 10 Read Articles in International Journal of Software Enginee...
PPTX
UNIT-1 - COAL BASED THERMAL POWER PLANTS
PPTX
Infosys Presentation by1.Riyan Bagwan 2.Samadhan Naiknavare 3.Gaurav Shinde 4...
PDF
TFEC-4-2020-Design-Guide-for-Timber-Roof-Trusses.pdf
PDF
Digital Logic Computer Design lecture notes
PDF
Enhancing Cyber Defense Against Zero-Day Attacks using Ensemble Neural Networks
PDF
PRIZ Academy - 9 Windows Thinking Where to Invest Today to Win Tomorrow.pdf
PDF
Mitigating Risks through Effective Management for Enhancing Organizational Pe...
PPTX
Engineering Ethics, Safety and Environment [Autosaved] (1).pptx
PPTX
Welding lecture in detail for understanding
PPTX
CYBER-CRIMES AND SECURITY A guide to understanding
PDF
Evaluating the Democratization of the Turkish Armed Forces from a Normative P...
PPTX
Recipes for Real Time Voice AI WebRTC, SLMs and Open Source Software.pptx
PPTX
web development for engineering and engineering
PPTX
Internet of Things (IOT) - A guide to understanding
PPTX
FINAL REVIEW FOR COPD DIANOSIS FOR PULMONARY DISEASE.pptx
PDF
keyrequirementskkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
PPTX
CH1 Production IntroductoryConcepts.pptx
PPT
Mechanical Engineering MATERIALS Selection
PPTX
additive manufacturing of ss316l using mig welding
July 2025 - Top 10 Read Articles in International Journal of Software Enginee...
UNIT-1 - COAL BASED THERMAL POWER PLANTS
Infosys Presentation by1.Riyan Bagwan 2.Samadhan Naiknavare 3.Gaurav Shinde 4...
TFEC-4-2020-Design-Guide-for-Timber-Roof-Trusses.pdf
Digital Logic Computer Design lecture notes
Enhancing Cyber Defense Against Zero-Day Attacks using Ensemble Neural Networks
PRIZ Academy - 9 Windows Thinking Where to Invest Today to Win Tomorrow.pdf
Mitigating Risks through Effective Management for Enhancing Organizational Pe...
Engineering Ethics, Safety and Environment [Autosaved] (1).pptx
Welding lecture in detail for understanding
CYBER-CRIMES AND SECURITY A guide to understanding
Evaluating the Democratization of the Turkish Armed Forces from a Normative P...
Recipes for Real Time Voice AI WebRTC, SLMs and Open Source Software.pptx
web development for engineering and engineering
Internet of Things (IOT) - A guide to understanding
FINAL REVIEW FOR COPD DIANOSIS FOR PULMONARY DISEASE.pptx
keyrequirementskkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
CH1 Production IntroductoryConcepts.pptx
Mechanical Engineering MATERIALS Selection
additive manufacturing of ss316l using mig welding

97244898-Turbo-Assembler-Version-5-Users-Guide.pdf

  • 2. User's Guide Borland® Turbo Assemble~ Borland International, Inc., 100 Borland Way P.O. Box 660001, Scotts Valley, CA 95067-0001
  • 3. Borland may have patents and/or pending patent applications covering subject matter in this document. The furnishing of this document does not give you any license to these patents. COPYRIGHT ©1988, 1996 Borland International. All rights resewed. All Borland product names are trademarks or registered trademarks of Borland International, Inc. Other brand and product names are trademarks or registered -trademarks of theirrespective holders. Printed in the U.S.A. LSM1350WW21774 1EOR0196 9697989900-9 8 7 6 5 4 HI
  • 4. The LENGTH unary operator . . . . . . . . . 64 The SIZE unary operator . . . . . . . . . . . . 65 The WIDTH unary operator . . . . . . . . . . 65 MASK unary operator. . . . . . . . . . . . . . 65 General arithmetic operators . . . . . . . . . . . 66 Simple arithmetic operators . . . . . . . . . . 66 Logical arithmetic operators .......... 66 Bit shift operators . . . . . . . . . . . . . . . . 67 Comparison operators. . . . . . . . . . . . . . 67 Setting the address subtype of an expression. . . . . . . . . . . . . . . . . . . . 67 Obtaining the type of an expression. . . . . . 68 Overriding the segment part of an address expression. . . . . . . . . . . . . . . 69 Obtaining the segment and offset of an address expression. . . . . . . . . . . . . . . 69 Creating an address expression using the location counter ................ 70 Determining the characteristics of an expression. . . . . . . . . . . . . . . . . . . . 70 Referencing structure, union, and table member offsets. . . . . . . . . . . . . . . . . 71 Describing the contents of an address. . . . . 71 Implied addition. . . . . . . . . . . . . . . . . 72 Obtaining the high or low byte values of an expression . . . . . . . . .. . . . . . . 72 Specifying a 16- or 32-bit expression 72 Chapter 6 Choosing processor directives and symbols 75 iAPx86 processor directives. . . . . . . . . . . . 76 Predefined symbols . . . . . . . . . . . . . . . . 77 8087 coprocessor directives . . . . . . . . . . . . 78 Coprocessor emulation directives . . . . . . . . 79 Chapter 7 Using program models and 'segmentation 81 The MODEL directive . . . . . . . . . . . 82 Symbols created by the MODEL directive . . . 84 The @Model symbol. . . . . . . . . . . . . . . 84 The @32Bit symbol. . . . . . . . . . . . . . . . 85 The @CodeSize symbol . . . . . . . . . . . . . 85 The @DataSize symbol . . . . . . . . . . . . . 85 The @Interface symbol . . . . . . . . . . . .. 85 Simplified segment directives . . . . . . . . . . 86 Symbols created by the simplified segment directives .................... 87 The STARTUPCODE directive .......... 87 The @Startup symbol . . . . . . . . . . . . . . 87 The EXITCODE directive. . . . . . . . . . . . 87 Defining generic segments and groups. . . . . 88 ii The SEGMENT directive. . . . . . . . . . . . . 88 Segment combination attribute. . . . . . . . . 88 Segment class attribute . . . . . . . . . . . . . 89 Segment alignment attribute . . . . . . . . . . 89 Segment size attribute. . . . . . . . . . . . . . 90 Segment access attribute............. 90 The ENDS directive. . . . . . . . . . . . . . . . 90 The GROUP directive. . . . . . . . . . . . . . . 91 The ASSUME directive. . . . . . . . . . . . . . . 91 Segment ordering . . . . . . . . . . . . . . . . . 92 Changing a module's segment ordering ... 92 The .ALPHA directive . . . . . . . . . . . . 93 The SEQ directive. . . . . . . . . . . . . . . 93 DOS ordering of segments: the DOSSEG directive . . . . . . . . . . . . . . . . . . . . . 93 Changing the size of the stack. . . . . . . . . . 93 ChapterS Defining data types 95 Defining enumerated data types. . . . 95 Defining bit-field records ............. 96 Defining structures and unions. . . . . . . . . . 98 Opening a structure or union definition . . . . 98 Specifying structure and union members . . . 98 I Df~E~~u.c~~~ ~~~~e.r ~a~~l~ ~~~. . . . 99 Aligning structure members . . . . . . . . . . 99 Closing a structure or union definition. . . . . 99 Nesting structures and unions. . . . . . . . . .100 Including one named structure within another.......................101 Using structure names in expressions . . . . .102 Defining tables................... 102 Overriding table members. . . . . . . . . . . .104 Defining a named type. . . . . . . . . . . . . . 104 Defining a procedure type. . . .. . . . . . . . 105 Defining an object. . . . . . . . . . . . . . . . . 105 The TBLPTR directive. . . . . . : . . . . . . . .106 Symbols defined by the extended STRUC directive. . . . . . . . . . . . . . . . . . . . . .107 Chapter 9 Setting and using the location counter 109 The $ location counter symbol . . . . . . . . . 109 Location counter directives ........... 110 The ORG directive. . . . . . . . . . . . . . . . .110 The EVEN and EVENDATA directives ....112 The ALIGN directive . . . . . . . . . . . . . . .112 Defining labels. . . . . . . . . . . . . . . . . . . 113 The : operator. . . . . . . . . . . . . . . . . . . .113
  • 5. Contents Introduction 1 New features . . . . . . . . . . . .'. . . . " . . .2 Hardware and software requirements . . . . . .2 About the manuals. . . . . . . . . . . . . . . . . .2 Typographic conventions. . . . . . . . . . . . . .3 Software registration and technical support ... 4 Chapter 1 Getting started with Turbo Assembler 5 Installing Turbo Assembler. . . . . . . . . . . . . 5 The Turbo Assemblers. . . . . . . . . . . . . . . .6 Utility and example programs . . . . . . . . . . .6 Online Help . . . . . . . . . . . . . . . . . . . . . .7 Writing your first Turbo Assembler program ...........' ............ 7 Assembling your first program. . ~ . . . . . . . .8 Linking your first program . . . . . . . . . . . . .9 Recommended reading . . . ... . . . . . . . . . . 9 Chapter 2 . Using directives and switches 11 Starting Turbo Assembler ............. 11 Command-line options . . . . . . . . . . . . . . 13 Indirect command files. . . . . . . . . . . . . . . 26 The configuration file . . . . . . . . . . . . . '.' 27 Chapter 3 General programming concepts 29 Turbo Assembler Ideal mode. . . . . . .. . . . 29 Why use Ideal mode? . . . . . . . . . . . . . . . 30 Entering and leaving Ideal mode. . . . . . . . . 30 MASM and Ideal mode differences . . . . ... 31 Expressions and operands . . . . . . . . . . . 31 Operators . . . . . . . . '.' . . . . . . . . . . . 32 Suppressed fixups . : . . . . . . . . . . . . . . 32 Operand for BOUND instruction . . . . . . . 32 Segments and groups . . . . . . . . . . . . . . 33 Accessing data in asegment belonging to.a group . . . . . . . . . . . . . . . . . . . . 33 Commenting the program. . . . . . . . . . . . . 35 Comments at the end of the line . . . . . . . . . 35 The COMMENT directive. . . . .. . . . . . . . 35 Extending the line. . . . . . . . . . . . . . . . . . 36 Using INCLUDE files " . . . .'. . . . . . . . . 37 Predefined symbols . . . . . . . . . . . . . . . . 37 Assigning values to symbols . . . .. . . . . . . 38 General module structure . . . . . . . . . . . . . 38 The VERSION directive.............. 39 The NAME directive. . . . . . . . . . . . . . . 40 The END directive . . . . . . . . . . . . . . . . 40 Displaying a message during assembly. . . . . 40 Displaying warning messages . . . . . . . . . . 41 Multiple error-message reporting . . . . . . . . 42 Chapfer 4 . Creating object-oriented programs 43 Terminology. . . . . . . . . . . . . . . . . . . . . 43 Why use objects in Turbo Assembler? . . . . . 44 Whatis an object? . . . . . . . . . . . .. . . . . 44 A sample object . . . . . . . . . . . . . . . . . . 45 Declaring objects. .. . . . . . . . . .. . . . . . 45 Declaring a base object.......... '.... 45 Declaring a derived object. . . . . . . . . . . . 47 Declaring a method procedure . . . .. . . . . . 48 The virtual method table; . . . . . . . : . . . . . 49 Initializing the virtual method table . . . . . . 50 Calling an object method. . . . . . . . . . . . . . 50 Calling a static method . . . . . . . . . . . . . . 50 Calling a virtual method . . . . . . . . . . . . . 51 Calling ancestor virtual methods . . . . . . . . 53 More on calling methods. . . . . . . . . . . . . 54 Creating an instance of an object . . . . . . . . . 55 Programming form for objects . . . . . . . . . . 55 ChapterS Using expressions and symbol values 57 Constants. . . . . . . . . . . . . . . . . . . . . . . 57 Numeric constants. . . . . . . . . . . . . . . . . 57 Changing the default radix . . . . . . . . . . . 58 String constants ; . . . . . . . . . . . . . . . . . 58 Symbols. . . . ...'. .... . . .'. . . . . . .. . . . . 59 Symbol names . . . . . . . . . . . . . . . . . . . 59 Symbol types. . . . . . . . . . . . . . . . . . . . 59 Simple address subtypes . . . . . . . . . . . . . 60 Describing a complex address subtype..... 61 Expressions.................... , .61 Expression precision. . . . . . . . . . . . . . . . 62 Constants in expressions . . . . . . . . . . . . . 62 Symbols in expressions. . . . . . . . . . . . . . 62 Registers. . . . . . . .. . . . . . . . . . . . . . 62 Standard symbol values. . . . . . . . . . . . . 63 Simple symbol values . . . . . . . . . . . . . . 63
  • 6. The LABEL directive. . . . . . . . . . . . . . . 113 The:: directive................... 114 Chapter 10 Declaring procedures 115 Procedure definition syntax. . . . . . . . . . . 115 Declaring NEAR or FAR procedures . . . . . 116 Declaring a procedure language. . . . . . . . 118 Specifying a languagemodifier......... 119 Defining arguments and local variables. . . . 120 ARC and LOCAL syntax . . . . . . . . . . . . 121 The scope of ARC and LOCAL variable names....................... 122 Preserving registers . . . . . . . . . . . . . . . 123 Defining procedures using procedure types .......................123 Nested procedures and scope rules . . . . . . 124 Declaring method procedures for objects. . . 125 Using procedure prototypes . . . . . . . . . . 126 Chapter 11 ContrOlling the scope of symbols 129 Redefinable symbols. . . . . . . . . . . .. . . 129 Block scoping . . . . . . . . . . . . . . . . . . . 130 The LOCALS and NOLOCALS directives. . . . . . . . . . . . . . . . . . . . . 130 MASM block scoping . . . . . . . . . . . . . . 131 MASM-style locallabels ............. 131 Chapter 12 Allocating data 133 Simple data directives . . . . . . . . . . . . . . 134 Creating an instance of a structure or union . . . . '. . . . . . . . . . . . . . . . . . . 137 Initializing union or structure instances. . . . 137 Creating an instance of a record . . . . : . . . 140 Initializing record instances .......... 140 Creating an instance of an enumerated data type . . . . . . . . . . . . . . . . . . . . . 141 Initializing enumerated data type instances. . . . . . . . . . . . . . . . . . . . .141 Creating an instance of a table . . . . . . . . . 141 Initializing table instances ............ 142 Creating and initializing a named-type instance. . . . . . . . . . . . . . . . . . . . . . 142 Creating an instance of an object. . . . . . . . 143 Creating an instance of an object's virtual methoa table ................... 143 iii Chapter 13 Advanced coding instructions 145 Intelligent code generation: SMART and NOSMART .................... 145 Extended jumps. . . . . . . . . . . . . . . . . . 146 Additional 80386 LOOP instructions . . . . . 147 Additional 80386 ENTER and LEAVB instructions. . . . . . . . . . . . . . . . . . . . 147 Additional return instructions . . . . . . . . . 147 Additional IRET instructions . . . . . . . . . . 148 Extended PUSH and POP instructions . . . . 148 Multiple PUSH and POPs . . . . . . . . . . . .148 Pointer PUSH and POPs .............148 PUSHing constants on the 8086 processor. . .149 Additional PUSHA, paPA, PUSHF and POPF instructions. . . . . . . . . . . . . . . . 149 The PUSHSTATE and POPSTATE instructions. . . . . . . . . . . . . . . . . . . . 149 Extended shifts . . . . . . . . . . . . . . . . . . 151 Forced segment overrides: SEGxx ' instructions. . . . . . . . . . . . . . . . . . . . 151 Additipnal smart flag instructions . . . . . . . 151 Additional field value manipulation instructions. . . . . . . . . . . . . . . . . . . . 152 The SETFIELD instruction . . . . . . . . . . . .152 The CETFIELD instruction. . . . . . . . . . . .153 Additional fast immediate multiply instruction . . . . . . . . . . . . . . . . . . . . 154 Extensions to necessary instructions for the 80386 processor . . . . . . . . . . . . . . . . . 154 Calling procedures with stack frames. . . . . 155 Calling procedures that contain RETURNS. . . . . . . . . . . . . . . . . . . . .156 Calling procedures that have been prototyped . . . . . . . . . . . . . . . . . . . .156 Calling metho<i procedures for objects: CALL..METHOD ................157 Tail recursion for object methods: JMP..METHOD..................158 Additional instruction for. object-oriented programming . . . . ; . . . . . . . . . . . . . 158 Chapter 14 Using macros 159 Textmacros .................... 159 Defining text macros with the EQU directive. . . . . . . . .. . . . . . . . . . . . .159
  • 7. String macro manipulation directives. . . . . 160 The CATSTR directive.............. 160 The SUBSTR directive. . . . . . . . . . . . . .160 The INSTR directive. . . . . . . . . . . . . . .161 The SIZESTR directive . . . . . . . . . . . . .161 Text macro manipulation examples. . . ... .161 Multiline macros . . . . . . . . . . . . . . . . .' 161 The multiline macro body. . . . . . . . . . . . 162 Using & in macros. . . ............. 162 Including comments in macro bodies..... 163 Local dummy arguments. . . . . . . . . . . .163 The EXITM directive. . . . . . . . . . . . . . .164 Tags and the GOTO directive . . . . . . .'. .164 General multiline macros . . . . . . . . . . . . 165 Invoking a general multiline macro. . . . . .166 The < > literal s~ing brackets . . . . . . . 166 The ! character . . . . . . . . . .. . . .. . 167 The % expression evaluation character. . 167 Redefining a general multiline macro..... 168 Deleting a general multiline macrQ: The PURGE directive . . . . . . . . . . . . .168 Defining nested and recursive macros . . . .168 The count repeat macro . . . . . . . . . . . . . 169 The WHILE directive. . ., . . . . . . . . . . . 170 String repeat macros. . . . . . . . . . . . . . . 170 The %immediate macro directive. . . . . . . 171 Including multiline macro expansions in the list file ................... 172 Saving the current operating state... . . . . . 172 Chapter 15 Using conditional directives 175 General conditional directives syntax ..... 175 IFxxx conditional assembly directives... ~ . 175 ELSEIFxxx conditional assembly directives. . . . . . . . . . . . . . . . . . . . . 177 ERRxxx error-generation directives ...... 177 Specific directive descriptions . . . . . . . . . 178 Unconditional error-generation directives .. 178 Expression-conditional directives ....... 178 Symbol-definition conditional directives ... 179 Text-string conditional directives ....... 180 Assembler-pass conditionals . . . . . . . . . . 182 Including conditionals in the list file. ... . . . 182 Chapter 16 Interfacing with the linker 183 Publishing symbols externally . . . . . . . . . 183 Conventions for a particular language .. . . 183 Declaring public symbols . . . . . . . . . . . . 184 Declaring librarysymbols. '.' . . . . . . . . . 184 iv Defining external symbols . . . . . . . . . . . .185 Defining global symbols . . . . . . . . . . . . .185 Publishing a procedure prototype. . . . . . . .185 Defining communal variables . . . .. . . . . .186 Including a library . . . . . . . . . . ..' . . . . 187 The ALIAS directive . . . . . . . . . . . . . . . 187 Chapter 17 Generating alisting 189 Listing format . . . . . . . . . . . . . . 189 . General list directives............... 190 Include file list directives.. . . . . . . . . . . . 191 Conditional list directives ............. 191 Macro list directives. . . . . . . . . . . . . . . . 192 Cross-reference list directives .......... 193 Changing list format parameters. . . . . . . . 194 Chapter 18 Interfacing Turbo Assembler with Borland C++ 197 Calling Turbo Assembler functions from Borland C++ . . . . . . . . . . . . . . . . . . . 197 The framework. . . . . . . . . . . .. . . . . . .198 Linking assembly language modules with C++ . ............. , ......198 Using Extern "c" to simplify linkage ...200 Memory models and segments. . . . . . . . .200 Simplified segment directives and Borland C++ .................200 Old-style segment directives and Borland C++ . . . . . . . . . . . . . . . . .202 Segment defaults: When is it necessary to load segments? ..............203 Publics and externals. . '.' . . . . . . . . . . .205 Underscores and the C language ......205 The significance of uppercase and lowercase. . . . . . . . . . . . . . . . . . .206 Label types. . . . . . . . . . . . . . . . . . .207 Far extemaJs ..................208 Linker command line . . . . . . . . . . . . . .209 Parameter passing. . . . . . . . . . . . . . . . .209 Preserving registers . . . . . . . . . ". . . . .213 Returningvalues ............ , ....214 Calling an assembler function from C++. . . .215 Writing C++ member functions in assembly language............ : ...218 Pascal calling conventions . . . . . . . . . . . .220 Calling Borland C++ from Turbo Assembler . . . . . . . . . . . . . . . . 221 Link in the Ct+ startup code...........221 The segment setup. . . . . . . . . . . . . . . . .221
  • 8. Performing the call. . ; . . . . . . . . . . . . . 222 Calling a Borland C++ function from Turbo Assembler . . . . . . . . . . . . . . . . 223 Appendix A Program blueprints 227 Simplified segmentation segment , description .................... 227 DOS programs . . . . . . . . . . . . . . . . . . 229 DOS EXE program blueprint. . . . . . . . . . 229 COM program blueprint . . . . . . . . . . . . 230 Windows programs . . . . . . . . . . . . . . . 231 Windows DLL blueprint . . . . . . . . . . . . 231 Windows 16-bit application blueprint. . . . . 232 Windows 32-bit application blueprint. . . . . 232 OS/2 programs . . . . . . . . . . . . . . . . . . 233 OS/2 flat-model program blueprint. ..... 233 AppendixB Turbo Assembler syntax summary 235 Lexical grammar . . . . . . . . . . . . . . . . . 235 MASM mode expression grammar . . . . . . 237 Ideal mode expression grammar. . . . . . . . 239 Keyword precedence............... 241 Ideal mode precedence . . . . . . . . . . . . . 241 MASM mode precedence. . . . . . . . . . . . 242 Keywords and predefined symbols. . . . . . 242 Directive keywords. . . . . . . . . . . . . . . . 242 AppendixC MASM 6.1 compatibility 249 Basic data types. . . . . . . . . . . . . . . . . . 249 Signed types . . . . . . . . . . . . . . . . . . .250 Floating-point types. . . . . . . . . . . . : . .250 New decision and looping directives . . . . . 250 .IF .ELSE .ELSEIF .ENDIF. . . . . . . . . . . . 250 Example. . . . . . . . . . . . . . . . . . . . 251 .WHILE .ENDW .. " ............. 251 Example .................... 251 v .REPEAT .UNTIL .UNTILCXZ .........252 Example ....................252 .BREAK .CONTINUE ...............252 Example ....................252 Logical operators . . . . . . . . . . . . . . . . . 253 Using flags in conditions............. 253 Text Macros . . . . . . . . . . . . . . . . . . . . 253 Macro repeat blocks with loop directives. . . 254 REPEAT loops . . . . . . . . . . . . . . . . . ..254 Example ....................254 FOR loops. . . . . . . . . . . . . . . . . . . . . .254 Example ....................254 FORC loops. . . . . . . . . . . . . . . . . . . . .255 Example ....................255 New Directives . . . . . . . . . . . . . . . . . . 255 ECHO directive . . . . . . . . . . . . . . . . . .255 EXTERNDEF directive ..............255 OPTION directive. . . . . . . . . . . . . . . . .256 CASEMAP: NONE/NOTPUBLIC/ALL ...256 DOTNAME/NODOTNAME . . . . . . . . .256 EMULATOR/NOEMULATOR ........256 EXPR16/EXPR32. . . . . . . . . . . . . . . . .256 LJMP/NOLJMP . . . . . . . . . . . . . . . . .256 NOKEYWORD: <keywordList>. . . . . ...256 PROC: PRIVATE/PUBLIC/EXPORT. . . . .257 SCOPED/NOSCOPED . . . _ . . . . . . . . .257 SEGMENT: USE16/USE32/FLAT.......257 Visibility in procedure declarations . . . . . . 257 Distance in procedure declarations. . . . . . . 257 SIZE operator in MASM mode . . . . . . . . . 257 Compatibility issues . . . . . . . . . . . . . . . 258 One-pass versus two-pass assembly ...... 258 Environment variables. . . . . . . . . . . . . . 259 Microsoft binary floating-point format . . . . 259 AppendixD Error messages 261 Information messages . . . . . . . . . . . . . . 261 Warning and error messages . . . . . . . . . . 262 Index 281
  • 9. Tables 1.1 Turbo Assemblers.................. 6 4.1 Object-oriented programming terrrUnology . 43 4.2 Symbols defined for objects. . . . . . . . . . . 44 5.1 Radixes....................... 57 5.2 Characters determining radixes . . . . . . . . 58 5.3 Numeric constants . . . . .. . . . . . . . . . . 58 5.4 Symbol types ................... 59 5.5 Address subtypes. . . . . . . . . . . . . . . . . 60 5.6 Complex address subtypes . . . . . . . . . . . 61 5.7 Distance syntax . . . . . . . . . . . . . . . . . . 61 5.8 Simple expressions. . . . . . . . . . . . . . . . 62 5.9 Standard symbols. . . . . . . . . . . . . . . . . 63 5.10 Values of symbols used by themselves .... 63 5.11 LENGTH operator return values........ 64 5.12 SIZE values . . . . . . . . . . . . . . . . . . . . 65 5.13 WIDTHvalues .................. 65 5.14 MASK return values ............... 66 5.15 Simple arithmetic operators........... 66 5.16 Logical arithmetic operators .......... 66 5.17 Bit shift operators ................. 67 5.18 Comparison operators. . . . . . . ....... 67 5.19 Type override operators ............. 67 5.20 TYPE values. . . . . . . . . . . . . . . . . . . . 68 5.21 Bit fields from SYMTYPE and .TYPE ..... 71 6.1 Processor directives................ 76 6.2 8087 coprocessor directives . . . . . . . . . . . 78 7.1 Standard memory models............ 83 7.2 Model modifiers ................. 84 7.3 Model modifiers . . . . . . . . . . . . . . . . . 85 7.4 Simplified segment directives . . . . . . . . . 86 7.5 Symbols from simplified segment directives ...................... 87 7.6 Segment combination attribute......... 88 7.7 Segment alignment attribute . . . . . . . . . . 89 7.8 Segment size attribute values. . . . . . . . . . 90 7.9 Segment access attribute. . . . . . . . . . . . . 90 7.10 Stack size modification directives ....... 94 8.1 STRUC, UNION, and ENDS directives. . . 100 8.2 Block members . . . . . . . . . . . . . . . . . 101 8.3 Available modifiers. .. . . . . . . . . . . . . . 106 8.4 Symbols used or defined by STRUC . . . . 107 12.1 Data size directives ............... 134 13.1 Intelligent code generation directives .... 145 13.2 Return instructions. . . . . . . . . . . . . . . 147 13.3 Segment override instructions.. . . . . . . . 151 13.4 Smart flag instructions. . . . . . . . . . . . . 152 13.5 Instructions for setting and retrieving values ....................... 152 vi 13.6 Instructions affected by SMALL and LARGE ...................... 154 14.1 Dummy argument types ............ 166 14.2 Uses for the ! character . . . . . . . . . . . . . 167 15.1 Conditional assembly directives using expressions .................... 178 15.2 Error-generation directives using expressions . . . . . . ... . . . . . . . . . . . . 179 15.3 Evaluation of defined and undefined symbol. . . . . . . . . . . . . . . .'. . . . . . . 179 15.4 Symbol-expression directives using symbol_expr ................... 179 15.5 Error-generation directives...... ; .... 180 15.6 Conditional assembly directives using text_strings ........ , ........... 180 15.7 Error-generation directive~ using text_strings . . . . . . . . . . . ......... 181 18.1 Register settings when Borland C++ enters assembler . . . . . . . . . .. . . . . . . 203 A.l Default segments and types for TINY memory model. . . . . . . . . ......... 227 A.2 Default segments and types for SMALL memory model. . . . . . . . . ......... 227 A.3 Default segments and types for MEDIUM memory model. . . . .. . . . ......... 228 A.4 Default segments and types for COMPACT memory model..... , . . . ......... 228 A.5 Default segments and types for LARGE or HUGE memory model . . . ........ 228 A.6 Default segments and types for Borland C++ HUGE (TCHUGE) memory model. ..... 229 B.l Turbo Assembler v1.0 keywords ....... 243 B.2 Turbo Assembler v2.0 new keywords .... 245 B.3 Turbo Assemblerv2.5 new keywords .... 246 B.4 /Turbo Assembler v3.0 new keywords . . . . 246 B.5 Turbo Assembler v3.1 new keywords .... 246 B.6 Turbo Assembl~r v3.2 new keywords . . . . 246 B.7 Turbo Assembler v4.0 new keywords . . . . 246 B.8 Turbo Assembler v5.0 new keywords . . . . 247 B.9 Options supported by OPTION. . . . . . . . 247 C.l Turbo Assembler types and their equivalent directives .............. 249 C.2 Signed integer data types. . . . . . . . . . . . 250 C.3 Floating-point data types. . . . . . . . . . . . 250 C.4 New Turbo Assembler logical operators... 253 C.5 Return value of SIZE in MASM mode .... 257
  • 10. Introduction Welcome to Borland's Turbo Assembler,® a multi-pass assembler with forward- reference resolution, assembly speeds of up to 48,000 lines per minute (on an IBM PS/2 model 60), Microsoft Macro Assembler (MASM) compatibility, and an optional Ideal mode extended syntax. Whether you're a novice or an experienced programmer, you'll appreciate these features and others we've provided to make programming in assembly language easier. Here are the highlights-we'll describe them in detail later: • Object-oriented programming capabilities • 32-bit model and stack frame support • Full 386, i486, and Pentium support • Simplified segmentation directives • Table support • Enumerations • Smart flag instructions • Fast immediate multiply operation • Multiline definition support • VERSION specification directive • Nested directives • Quirks mode to emulate MASM • Full source debugging output • Cross-reference utility (TCREF) • Configuration and command files • File converter utility (converts C .h files to TASM .ash files) • Procedure prototyping and argument checking capabilities • Alias support • Windows 95 flat thunking support Turho Assembler is a powerful command-line assembler that takes your source (.ASM) files and produces object (.OBD modules. You then use TLINK.EXE, Borland's high- speed linker program, to link your object modules and create executable (.EXE) files. Introduction 1
  • 11. New features Turbo Assembler version 5.0 incorporatesthe following new feature enhancements: • Enhanced MASM compatibility, as described in "MASM 6.1 compatibility" on page 249. • Windows 95 flat thunking support with the -utthk command-line option. For more information on thunking, refer to the sample program and documentation contained' in the subdirectory EXAMPLES THUNK95 off your main TASM directory. Hardware.and software requirements Turbo Assembler generates instructions for the 8086,80186,80286,80386, i486, Pentium, and Pentium Pro, and compatible processors. Essentially Turbo Assembler runs on all Intel-processor based computers, including all true compatibles. Turbo Assembler also generates floating-point instructions for the 8087,80287, and 80387 numeric coprocessors. (For more information about the instruction sets of the 80x86/80x87 families, consult the Intel data books.) About the manuals Turbo Assembler comes with the Turbo Assembler User's Guide (this book) and the Turbo Assembler Quick Reference Guide. The User's Guide provides basic instructions for using Turbo Assembler, explores how to interface Turbo Assembler with other languages, and describes in detail the operators, predefined symbols, and directives Turbo Assembler uses. The Quick Reference Guide is a handy guide to directives and processor and coprocessor instructions. Here's a more detailed look at what the User's Guide contains. Chapter 1, "Getting started with Turbo Assembler," tells you how to install Turbo Assembler on your system Chapter 2, "Using directives and switches," describes how you can control the way the assembler runs when you use directives and switches. Chapter 3, "General programming concepts," discusses the differences between Ideal and MASM modes, how to use predefined symbols, using comment characters, and so forth. ~ Chapter 4, "Creating object-oriented programs," describes how you can use object- ~QJ oriented programming techniques in assembly language. Chapter 5, "Using expressions and symbol values," talks about evaluating and defining expressions and operators. Chapter 6,'IIChoosing processor directives and symbols," tells you how to generate code for particular processors. 2 Turbo Assembler User's Guide
  • 12. Chapter 7, IIUsing program models and segmentation," talks about program models, creating symbols, simplified segments, and ordering of segments. Chapter 8, IIDefining data types," explains how to define structures, unions, tables, bit-field records, and objects. Chapter 9, IISetting and using the location counter," describes how and why you'd want to use the location counter, as well as how to define labels. Chapter 10, IIDeclaring procedures,"examines how to use various types of procedures, and how to define and use arguments and local variables. . Chapter 11, II Controlling the scope of symbols," discusses how you can limit or expand the area in which a symbol has a particular value. Chapter 12, IIAllocating data,"describes simple data directives, and how to create instances of structures, unions, records, enumerated data types, tables, and objects. Chapter 13, IIAdvanced coding instructions," covers Turbo Assembler's extended instructions, including prototyping and calling language procedures. Chapter 14, IIUsing macros," tells you how to use macros in your code. Chapter 15, "Using conditional directives," talks about the directives that let you execute your code conditionally. Chapter 16, "Interfacing with the linker," describes how you can include libraries and publish symbols as you link your code. Chapter 17, IIGenerating a listing," talks about Turbo Assembler listing files and how to use them. Chapter 18, "Interfacing Turbo Assembler with Borland C++," explains how to use Borland's line of C++ compilers with assembly language. Appendix A, IIProgram blueprints," contains example program structures for Windows and DOS programs. Appendix B, "Turbo Assembler syntax summary," illustrates Turbo Assembler expressions (be>th MASM and Ideal modes) in modified Backus-Naur form (BNF). Appendix C, "MASM 6.1 compatibility," covers the differences between MASM and Turbo Assembler MASM mode. Appendix D, IIError messages," describes all the error messages1hat can be generated when using Turbo Assembler: information messages, fatal error messages, warning messages, and error messages. Typographic conventions When we talk about IBM PCs or compatibles, we're referring to any computer that uses the 8088, 8086, 80186, 80286, 80386, i486, Pentium, and Pentium Pro processors (all of these chips are commonly referred to as 80x86). Introduction 3
  • 13. The following typefaces are used in this book: Italics Boldface CAPITALS Monospace Keycaps In text, italics represent labels, placeholders, variables, and arrays. In syntax expressions, placeholders are set in italics to indicate they are user-defined. Boldface is used ~ text for directives, instructions, symbols, and operators, as well as for command-line options. In text, capital letters are used to represent instructions, directives, registers, and operators. Monospace type is used to display any sample code or text that appears on your screen, and any text that you must actually type to assemble, link, and run a program In text, keycaps indicate a key on your keyboard. It is often used when describing a key you must press to perform a particular function; for example, "Press Enter after typing yourprogram name at the prompt." Software registration and technical support The Borland® Assist program offers a range of technical support plans to fit the different needs of individuals, consultants, large corporations, and developers. To receive help with this product, send in the registration card and select the Borland Assist plan that best suits your needs. North American customers can register by phone 24 hours a day by calling 1-800-845-0147.. For additionaldetails on these and other Borland services, see the Borland Assist and Services Guide included with thls product. 4 Turbo Assembler User's Guide
  • 14. Getting started with Turbo Assembler You might have heard that programming in assembly language is a black art suited only to hackers and wizards. However, assembly language is nothing more than the human form ofthe language of the computer. And, as you'd expect, the computer's language is highly logical. As you might also expect, assembly language is very powerful-in fact, assembly language is the only way to tap the full power of the Intel80x86 family, the processors at the heart of the IBM PC family and compatibles. You can write whole programs using nothing but assembly language or you can mix assembly language with programs written in high-level languages such as Borland® C++ and Borland® Pascal. Either way, assembly language lets you write small and blindingly fast programs. In addition to the advantage of speed, assembly language gives you the ability to control every aspect of your computer's operation, all the way down to the last tick of the computer's system clock. Installing Turbo Assembler· The Turbo Assembler package consists of a set of executable programs, utilities, and example programs. In addition, the package includes a Quick Reference Guide and this User's Guide. For instructions on installing Turbo Assembler, refer to the TSM_INST .TXT file on your installation disk: 1 Insert the TASM Install disk in drive A of your computer. 2 User your text editor to open TSM_INST.TXT, or issue the following command at the cominand line: TYPE A:TSM_INST.TXT I MORE Chapter 1, Getting started with Turbo Assembler 5
  • 15. The Turbo Assemblers The Turbo Assembler package comes complete with 3 different assemblers, as outlined in Table 1.1: Table 1.1 TASM.EXE TASMX.EXE TASM32.EXE Turbo Assemblers Real-mode assembler. Assembles 16- and 32-bit .oBJs using the 640K memory space addressable by DOS. Produces only 16-bit debug information. Protected-mode assembler. Assembles 16- and 32-bit .OBJs using memory above 640K. Produces only 16-bitdebug information. Pr9tected-mode assembler. Assembles 16- and 32-bit .oBJs using memory above 640K. Produces only 32-bit debug information. All three assemblers are capable of producingboth 16- and 32-bit object files, depending on the directives contained in your assembler source files. If you produce a 16-bit object file, then you must use the 16-bit linker (TLINK.EXE) to link your application. If you produce a 32-bit object file, then you must use the 32-bit linker (TLINK32.EXE) to link your application. TASM.EXE is a real-mode assembler, meaning that it is capable ofusing only the lower 640K of memory addressable by DOS. If you're assembling larger applications, use either TASMX.EXE or TASM32.EXE. Both of these assemblers use the DPMI server to take advantage of extended memory. The biggest difference between the three assemblers is the type of debug information they produce when you assemble your source files with the /zi command-line option. Both TASM.EXEand TASMX.EXE produce only 16-bit debug information. TASM32.EXE produces only 32-bit debug information. If youplanto use Turbo Debugger to debug your assembler application, then youmust assemble 16-bit files with either TASM.EXE or TASMX.EXE. To produce 32-bit debug information, then you must assemble your files with TASM32.EXE. Utility and example programs The Turbo Assembler package includes several utility programs to help you build assembly programs. The utilities include the Turbo Linkers, the MAKE utility, the GREP file search utility, and the resource compilers and linkers. These utility programs are described in the online text files located in the DOC subdirectory located off your main TASM directory. To get you started writing assembler programs, the Turbo Assembler package includes various example programs that demonstrate different assembler programming techniques. The example programs, located in the. EXAMPLES directory under the main TASM directory, even include complete 16- and 32-bit Windows assembly programs. 6 Turbo Assembler User's Guide
  • 16. Online Help You can get online Help for Turbo Assembler using the Windows Help facility. To access the online Help, do one of the following: • From Windows, click the TASM Reference icon in the TASM program group • From Windows, run the TASM.HLP file located in the TASMBIN subdirectory You can run TASM.HLP from a DOS box in Windows. On the DOS command line, enter the following command from the TASMBIN directory: winhelp tasm.hlp Writing your first Turbo Assembler program If you have not yet written an assembly program, the DOS-based "Greetings, World!" program is a good place to start. To begin writing this program, open your favorite program editor and enter the following lines of code to create the HELLO.ASM program: .MODEL SMALL .STACK lOOh .DATA TimePrompt GoodMorningMessage GoodAfternoonMessage DefaultMessage .CODE start: mov aX,@data mov ds,ax DB 'Is it after 12 noon (Y/N)?$' DB 13,10, 'Good morning, world!' ,13,10, '$' DB 13,10, 'Good afternoon, world!' ,13,10,'$' DB 13,10,'Good day, world!' ,10,13, '$' mov dx,OFFSET TimePrompt iset DS to point to the data segment ipoint to the time prompt mov int mov int or cmp je cmp je ah,9 21h ah,l 21h al,20h al,'y' IsAfternoon al, 'n' IsMorning iDOS: print string idisplay the time prompt ;DOS: get character iget a single-character response iforce character to lower case ;typed Y for afternoon? ityped N for morning? mov dx,OFFSET DefaultMessage ;default greeting jmp DisplayGreeting IsAfternoon: mov dx,OFFSET GoodAfternoonMessage iafternoon greeting .jmp DisplayGreeting Chapter 1, Getting started with Turbo Assembler 7
  • 17. IsMorning: mov dX,OFFSET GOQdMorningMessage ibefore noon greeting DisplayGreeting: mov ah,9 int 21h mov ah,4ch mov aLO int 21h END start iDOS: print string idisplay the appropriate greeting iDOS: terminate program ireturn code will be 0 iterminate the program After you've entered the preceding program, save it to disk as HELLO.ASM. (For convenience, HELLO.ASM is supplied in the EXAMPLES USRGUIDE directory located under your main TASM directory.) If you're familiar with high-level languages (such as C, C++, or Pascal), you might think that HELLO.ASM is a bit long for a uGreetings, World!" program. Indeed, assembler programs tend to be much longer than high-level language programs because each high-level language statement actually breaks down to form many assembler instructions. However, assembly language gives you complete freedom over the actual instructions that are given to the computer's CPU. With assembly language, you can write programs that tell the computer to do anything that it's.capable of doing. ,Assembling your first program Now that you've saved HELLO.ASM, you'll want to run it. However, before you can run it, you'll have to assemble it into an.OBJ file, and then'link the file to form an executable program. The assembly step turns your source code into an intermediate form called an object module, and the linking step combines one or more object modules into an executable program. You can do your assembling and linking from the command line. To assemble HELLO.ASM, type the following line at the command line: TASM hello Unless you specify another file name, HELLO.ASM will be assembled to form the object file HELLO.OBJ. (Note that you don't need to type in the file extension name; Turbo Assembler assumes all source files end with .ASM.) If you entered the HELLO.ASM program correctly, you'll see a listing similar to the following one displayed onscreen: Turbo Assembler Version 5.0 Copyright (c) 1988, 1996 by Borland International, Inc. Assembling file: HELLO.ASM Error messages: None Warning messages: None Passes: 1 Remaining memory: 439K If you get warnings or errors, they are displayed with the program line numbers to indicate where they occurred. If you do get errors, edit HELLO.ASM make sure it's 8 Turbo Assembler User's Guide
  • 18. precisely th~ same as the program shown above. After editing the program, reassemble it with the TASM hello command. Linking your first program After you've successfully assembled HELLO.ASM, you'll need to link the program using TLINK. At the command line, type: TLINK hello If no errors or warnings are reported, an executable file is created, named HELLO.EXE. To run this program, enter the command HELLO from the command line. Errors can occur during the linking process, although it's unlikely with this example program. If you do receive linker errors, modify your code to exactly match the code shown here, then assemble and link again. Recommended reading Although HELLO.ASM is a good program for testing TASM.EXE and TLINK.EXE, the example is of little use if you're trying to learn assembly language. However, many books are available that teach both the fundamentals and the advanced features of assembly language. To help you get started with assembly language, refer to one or more of the following book titles: • Hummel, Robert 1. Programmers Technical Reference: Processor and coprocessor. Emeryville, CA: Ziff Davis Press, 1992. . • Mischel, Jim. Macro Magic with Turbo Assembler. New York, NY: John Wiley & Sons, 1993. • Swan, Tom. Mastering Turbo Assembler, Second Edition. Indianapolis, IN: Sams Publishing, 1995. • Yao, Paul. Borland C++ 4.0 ProgrammingforWindows. New York, NT: Random House, Inc., 1994. In particular, Part 6 of this book offers useful insights into programming Windows prologue and epilogue code, along with code showing the Windows callback mechanism. In addition to these books, Intel Corporation offers fact sheets and reference manuals on the workings of their processor products. Contact Intel at the following address: Intel Literature Sales P.O. Box 7641 Mount Prospect, IL 60056-7641 1 (800) 548-4725 Chapter 1, Getting started with Turbo Assembler 9
  • 19. 10 Turbo Assembler User's Guide
  • 20. Using directives and switches This chapter is dedicated to familiarizing you with Turbo Assembler's command-line options. We'll describe each of the command-line options you can use to alter the assembler's behavior, and then show how and when to use command files. We'll also describe the configuration file, and how you can control the display of warning and error messages. Starting Turbo Assembler If you start Turbo Assembler from your operating system command line without giving it any arguments, like this, TASM you'll get a screenful of help describing many of the command-line options, and the syntax for specifying the files you want to assemble. Figure 2.1 shows you how this looks. Figure 2.1 Turbo Assembler command line Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International Syntax: TASM [options] source [,object] [,listing] [,xref] la,/s Alphabetic or Source-code segment ordering Ic Generate cross-reference in listing IdSYM[=VAL] Define symbol SYM = 0, or = value VAL le,/r Emulated or Real floating-point instructions Ih,l? Display this help screen lipATH Search PATH for include files IjCMD Jam in an assembler directive CMD (e.g. IjIDEAL) Ikh# Hash table capacity # symbols Il,/la Generate listing: l=normal listing, la=expanded listing Iml,/mx,/mu Case sensitivity on symbols: ml=all, mx=globals, mu=none lmv# Set maximum valid length for symbols Im# Allow # mUltiple passes to resolve forward references Chapter 2, Using directives and switches 11
  • 21. In Suppress symbol tables in listing, los,/o,/op,/oi Object code: standard, standard w/overlays, Phar Lap, or IBM Ip Check for code segment overrides in protected mode Iq Suppress OBJ records not needed for linking It Suppress messages if successful assembly luxxxx IwO, Iwl, Iw2 Iw-xxx,/w+xxx Ix Iz Izi, Izd,/zn Set version emulation, version xxxx Set warning level: wO=none, wl=w2=warnings on Disable (-) or enable (+) warning xxx Include false conditionals in listing Display source line with error message Debug info: zi=full,zd=line numbers only, zn=none With the command-line options, you can specify the name of one or more files that you want to assemble, as well as any options that control how the files get assembled. The generalform of the command line looks like this: TASM fileset [i fileset] .. , ,The semicolon (;) after the left bracket (D lets you assemble multiple groups of files on one command line by separating the file groups. If you prefer, you can set different options for each set of files; for example, TASM Ie FILEli la FILE2 assembles FILEl.ASM with the fe command-line option and assembles file FILE2.ASM with the fa command-line option. In the generalform of the command line,fileset can be [option] ... sourcefile [[+] sourcefile] ... , [, [objfile] [, [listfile] [, [xreffile]JJ] This syntax shows that a group of files can start off with any options you want to apply ,to those files, followed by the files you want to assemble. A file name can be a single file name, or it can use the normal wildcard characters * and? to specify multiple files to assemble. Ifyour file name does not have an extension, Turbo Assembler adds the .ASM extensIon. For example, to assemble all the .ASM files in the current directory, you would type TASM * If you want to assemble multiple files, you can separate their names with the plus sign (+): TASM MYFILEl + MYFILE2 You can follow the file name youwant to assemble by an optional object file name, listing file name, and a cross-reference file name. If you do not specify an object file or listing file, Turbo Assembler creates an object file with the same name as the source file and an extension of .OBJ. A listing file is not generated unless you explicitly request one. To request one, place a comma after the object file name, followed by a listing file name. If you don't explicitly , provide a listing file name, Turbo Assembler creates a listing file with the same name as the source file and the extension .LST. If you supply a listing file name without an extension, .LST is appended to it. 12 T urboA sse mbIerUse r's Gui de
  • 22. A cross-reference file is not generated unless you explicitly request one. To request one, place a comma after the listing file name, followed by a cross-reference file name. If you don't explicitly provide a cross-reference file name, Turbo Assembler creates a cross- reference file with the same name as the source file and the extension .XRF. If you supply a cross-reference file namewithout an extension, .XRF is appended to it. (TCREF, a cross-reference utility, is described on disk.) If you want to accept the default object file name and also request a listing file, you must supply the comma that separates the object file name from the listing file name: TASM FILE1, ,TEST This assembles FILE1.ASM to FILE1.0BJ and creates a listing file named TEST.LST. If you want to accept the default object and listing file names and also request a cross- reference file, you must supply the commas that separate the file names: TASM MYFILE, "MYXREF This assembles file MYFILE.ASM to MYFILE.OBJ, with a listing in file MYFILE.LST and a cross-reference in MYXREF.XRF. If you use wildcards to specify the source files to assemble, you can also use wildcards to indicate the object and listing file names. For example, if your current directory contains XXl.ASM and XX2.ASM, the command line TASM XX*,YY* assembles all the files that start with XX, generates object files that start with YY, and derives the remainder of the name from the source file name. The resulting object files are therefore called YY1.0BJ and YY2.0BJ. If you don't want an object file but you do want a listing file, or if you want a cross- reference file but don't want a listing file or object file, you can specify the null device (NUL) as the file name. For example, TASM FILE1, ,NUL, assembles file FILE1.ASM to object file FILE1.0BJ, doesn't produce a listing file, and creates a cross-reference file FILEl.XRF. Command-line options The command-line options let you control the behavior of the assembler, and how it outputs information to the screen, listing, and object file. Turbo Assembler provides you with some options that produce no action, but are accepted for compatibilitY with the current and previous versions of MASM: Ib Sets buffer size Iv Displays extra statistics You can enter options using any combination of uppercase and l~wercase letters. You can also enter your options in any order except where you have multiple Ii or Ij options; these are processed in sequence. When using the Id option, you must also be careful to define symbols before using them in subsequent Id options. Chap ter 2, Usin 9 dire ctivesan d swit c hes 13
  • 23. fa la Note You can override command-line options by using conflicting directives in your source code. . . Figure 2.1 on page 11 summarizes the Turbo Assembler command-line options; here's a detailed de~cription of each option. Function Specifies alphabetical segment-ordering Syntax la Remarks The fa option tells Turbo Assembler to place segments in the object file in alphabetical order. This is the same as using the .ALPHA directive in your source file. You usually only have to use this option if you want to assemble a source file that was written for very early ve!sions of the IBM or Microsoft assemblers. The Is option reverses the effect of this option by returning to the defaultsequential segment-ordering. If you specify sequential segment-ordering with the .SEQ directive in your source file, it will override any fa you provide on the command line. Example TASM la TESTl Ib This command line creates an object file, TESTl.OBJ, that has its segments in alphabetical order. Syntax Ib Remarks The Ib option is included for compatibility. It performs no action and has no effect on the assembly. Ie Function Enables cross-reference in listing file Syntax Ie Remarks The Ie option enables cross-reference information in the listing file. Turbo Assembler adds the cross-reference information to the symbol table at the end of the listing file. This means that, in order to see the cross-reference information, you must either explicitly specify a listing file on the command line or use the II option to enable the listing file. : For each symbol, the cross-reference shows the line on which-it is defined and all lines that refer to it. ! Example TASM /l IcTESTl 14 Turbo Assembler User's Guide
  • 24. Id This code creates a listing file that also has cross-reference information in the symbol table. /d Function Defines a symbol Syntax Idsymbol [=value or expression] Remarks The Id option defines a symbol for your source file, exactly as if it were defined on the first line of your file with the =directive. You can use this option as many times as you wanf on the command line. You can only define a symbol as being equal to another symbol or a constant value. You can't use an expression with operators to the right of the equal sign (=). For example, IdX=9 and IdX=Yare allowed, but IdX=Y-4 is not. Example TASM IdMAX=lO IdMIN=2 TESTl Ie This command line defines two symbols, MAX and MIN, that other statements in the source file TESTl.ASM can refer to. Function Generates floating-point emulator instructions Syntax Ie Remarks The Ie option tells Turbo Assembler to generate floating-point instructions that will be executed by a software floatinS-point emulator. Use this option if your program contains a floating-point emulation library that mimics the functions of the 80x87 numeric coprocessor. Normally, you would only use this option if your assembler module is part of a program written in a high-level language that uses a floating-point emulation library. (Borland's line of C++ compilers, Borland Pascal, Turbo Basic, and Turbo Prolog all support floating-point emulation.) You can't just link an assembler program with the emulation library, since the library expects to have been initialized by the compiler's startup code. ' The Ir option reverses the effect of this option by enabling the assembly of real floating- point instructions that can only be executed by a numeric coprocessor. If you use the NOEMUL directive in your source file, it will override the Ie option on the command line. The Ie command-line option has the same effect as using the EMUL directive at the start of your source file, and is also the same as using the IjEMUL command-line option. Example TASM I e SECANT TCC -f TRIG.C SECANT.OBJ Chap t er 2, Usin 9 dire cti vesan d swit ches 15
  • 25. · Ih or I? Ih or 11 The first command line assembles a module with emulated floating-point instructions. The second command line compiles a C source module with floating-point emulation and then links it with the object file from the assembler. . Function Displays a help screen Syntax Ih or I? Remarks The /h option tells Turbo Assembler to display a help screen that describes the command-line syntax. This includes a list of the options, as well as the various file names you can supply. The I? option does the same thing. Example TASM Ih Ii Function Sets an include file path Syntax lipATH Remarks The Ii option lets you tell Turbo Assembler where to look for files that are included in your source file by using the INCLUDE directive. You can place more than one Ii option on the command line (the number is only limited by RAM). When Turbo Assembler encounters an INCLUDE directive, the location where it searches for the include file is determined by whether the file name in the INCLUDE directive has a directory path or is just a simple file name. If you supply a directory path as part of the file name, that path is tried first, then Turbo Assembler searches the directories specified by Ii command-line options in the order they appear on the command line. It then looks in any directories specified by Ii options in a configuration file. If you don't supply a directory path as part of the file name, Turbo Assembler searches first in the directories specified by Ii command-line options, then it looks in any directories specified by Ii options in a configuration file, and finally it looks in the current directory. Example TASM Ii INCLUDE liD: INCLUDETESTl If the source file contains the statement INCLUDE MYMACS.INC Turbo Assembler will first look for INCLUDEMYMACS.INC, then it will look for D:INCLUDEMYMACS.INC. If it still hasn't found the file, it will look for MYMACS.INC in the current directory. If the statement in your source file had been INCLUDE INCSMYMACS.INC 16 Turbo Assembler User's Guide
  • 26. Ij Turbo Assembler would first look for INCSMYMACS.INC and then it would look for INCLUDEMYMACSJNC, and finally for D: INCLUDE MYMACSJNC. Function Defines an assembler startup directive Syntax Ijdirective Remarks The Ij option lets you specify a directive that will be assembled before the first line of the source file. directive can be any Turbo Assembler directive that does not take any arguments, such as .286, IDEAL, %MACS, NOJUMPS, and so on. / j You can put more than one Ij option on the command line; they are processed from left to right across the command line. Example TASM I j .286 I j IDEAL TESTl This code assembles the file TESTl.ASM with 80286 instructions enabled and Ideal mode expression-parsing enabled. Ikh Function Sets the maximum number of symbols allowed Syntax Ikhnsymbols Remarks The Ikh option sets the maximum number of symbols that your program can contain. If you don't use this option, your program can only have a maximum of 8,192 symbols; using this option increases the number of symbols to nsymbols, up to a maximum of 32,768. Use this option if you get the Out of hash space message when assembling your , program. You can also use this option to reduce the total number of symbols below the default 8,192. This releases some memory that can be used when you are trying to assemble a program but don't have enough available memory. Example TASM IkhlOOOOO BIGFILE This command tells Turbo Assembler to reserve space for 10,000 symbols when assembling the file BIGFILE. II Function Generates a listing file Syntax II Chap t er 2, Us in9 dire ct ivesan d swit ches 17
  • 27. / I a. Remarks The II option indicates that you want a listing file, even ifyou did not explicitly specify it on the command line. The listing file will have the same name as the source file, with an extension of .LST. Example TASM 11 TESTl This command line requests a listing file that will be named TESTl.LST. Iia Function Shows high-level interface code in listing file Syntax Ila Remarks The IIa option tells Turbo Assembler to show all generated code in the listing file, including the code that gets generated as a result of the high-level language interface .MODEL directive. Example TASM Ila FILEl 1m Function Sets the maximum number of assembly passes Syntax 1m [npasses 1 Remarks Normally, Turbo Assembler functions as a single-pass assembler. The 1m option lets you specify the maximum number of passes the assembler should make during the assembly process. TASM automatically decides whether it can perform less than the number of passes specified. Ifyou select the 1m option, but don't specify npasses, a default of five is used. You might want to specify multiple passes either if you want TurboAssembler to remove Nap instructions added because of forward references or if you are assembling a module containing instructions that require two passes. If multiple passes are not enabled, such a module will produce atleast one ilPass-dependent construction encountered" warning. If the1m option is enabled, Turbo Assembler assembles this module correctly but will not optimize the code by removing Naps, no matter how many passes are allowed. The warning ilModule is pass dependent-compatibility pass was done" is displayed if this occurs; Example TASM 1M2 TESTl This tells Turbo Assembler to use up to two passes when assembling TE~Tl. Iml Function Treats symbols as case-sensitive Syntax Iml 18 TurboA sse mbIer Use r's Guide
  • 28. 1m u Remarks The Iml option tells Turbo Assembler to treat all symbol names as case-sensitive. Normally, uppercase and lowercase letters are considered equivalent so that the names ABCxyz, abcxyz, and ABCXYZ would all refer to the same symbol. If you specify the Iml option, these three symbols will be treated as distinct. Even when you specify Iml, you can still enter any assembler keyword in uppercase or lowercase. Keywords are the symbols built into the assembler that have special meanings, such as instruction mnemonics, directives, and operators. Example TASM /ml TESTl Imu where TEST1.ASM contains the following statements: abc DW 0 ABC DW 1 Mov Ax, [Bp] inot a duplicate symbol imixed case OK in keywords The Iml switchused together with Imx has a special meaning for Pascal symbols. See the Imx section for further details. Function Converts symbols to uppercase Syntax /mu Remarks The Imu option tells Turbo Assembler to ignore the case of all symbols. By default, Turbo Assembler specifies that any lowercase letters in symbols will be converted to uppercase unless you change it by using the Iml directive. Example TASM /mu TESTl Imv# makes sure that all symbols are converted to uppercase (which is the default): EXTRN myfunc:NEAR call myfunc idon't know if declared as i MYFUNC, Myfunc, ... Function Sets the maximum length of symbols. Syntax /mv# Remarks The Imv# option sets the maximum length of symbols that TASM will distinguish between. For example, if you set Imv12, TASM will see ABCDEFGHIJKLM and ABCDEFGHIJIKLL as the same symbol, but not ABCDEFGHIJKL. Note that the minimum number you ~an have here is 12. Imx Function Makes public and external symbols case-sensitive Chapter 2, Using directives and switches 19
  • 29. In Syntax Imx Remarks The Imx option tells Turbo Assembler to treat only external and public symbols as case- sensitive. All other symbols used (within: thesource file) are treated as uppercase. You should use this directive when you call routines in other modules that were compiled or assembled so that case-sensitivity is preserved; for example, modules compiled by one of Borland's line of C++ compilers. Example TASM Imx TEST1; In where TESTl.ASM contains the following source lines: EXTRN Cfunc:NEAR myproc PROC NEAR call Cfunc Note Using the Imx and Iml options together has a special meaning for symbols declared as Pascal; if you use these symbols together, the symbols will be published as all uppercase to the linker. Function Suppresses symbol table in listing file Syntax In Remarks The In op#on indicates that you don't want the usual symbol table at the end of the listing file. Normally, a complete symbol table listing appears at the end of the file, showing all symbols, their types, and their values. You must specify a listing file, either explicitly on the command line or by using the 11 option; otherwise, In has no effect. Example TASM 11 In TESTl 10 This code generates a listing file showing the generated code only, and not the value of your symbols. Function Generates overlay code for TLINK Syntax. 10 Remarks Specifying the 10 switch on the command line causes overlay-compatible fixups to be generated. When this switch is used, 386 references to USE32 segments should not be made since they won't link properly. 20 .'TurboA sse mbIer Use r's Guide
  • 30. loi loi Function Generates overlay code for the IBM linker Syntax 10 Remarks Specifying the loi switch on the command will generate overlay-compatible fixups for the IBM linker. The resulting object file will not be compatible with TLINK, Borland's linker. lop Function Generates overlay code for the Phar Lap linker Syntax lop Remarks Specifying the lop switch on the command will generate overlay-compatible fixups for the Phar Lap linker. The resulting object file will not be compatible with TLINK, Borland's linker. los Function Outputs TLINK-compatible objects without overlay support. This is the default selection. Syntax los Remarks Specifying the los switch on the command will generate objects without overlay support for use with TLINK. Ip Function Checks for impure code in protected mode Syntax Ip Remarks The Ip option specifies that you want to be warned about any instructions that generate "impure" code in protected mode. Instructions that move data into memory by using a CS: override in protected mode are considered impure because they might not work correctly unless you take special measures. You only need to use this option if you are writing a program that runs in protected mode on the 80286, 386, or i486. Example TASM Ip TESTl where TESTl.ASM contains the following statements: Chap t er 2, Us i n9 dire ct ivesan d swit ches 21
  • 31. /q Iq .286P , CODE SEGMENT temp DW ? mov CS:temp,O ;impure in protected mode Function Suppresses .OBI re'cords not needed f()r linking Syntax /q Remarks The Iq option removes the copyright and file dependency records from the resulting .OBI files, making it smaller. Don't use this option if you are using MAKE or a similar program that relies on the dependency records. ' Ir Function Generates real fl0Cl.ting~point instructions Syntax /r Remarks The Ir option tells Turbo Assembler to generate real floating-point instructions (instead of generating emulated floating-point instructions). Use this option if your program is going to run on machines equipped with an 80x87 numeric coprocessor. The Ie option reVerses the effect of this option in generating emulated floating~point instructions. If you use the EMUL directive in your source file, it will override the Ir option on the command line. The Ir command-line option has the same effect as using the NOEMUL directive at the start of your source file, and is also the same as using the IjNOEMUL command-line option. . Example TASM /r SECANT Is TPC /$N+ /$E TRIG.PAS The first command line assembles a module with real floating-point instructions. The second compiles a Pascal source module with real floating-point instructions that links in the object file from the assembler. Function Specifies sequential segment-ordering Syntax /s Remarks The Is option tells Turbo Assembler to place segments in the object file in the order in which they were encountered in the source file. By default, Turbo Assembler uses segment-ordering, unless you change itby placing an la option in the configuration file. 22 Tur boA 55 embIer U5 er '5 GU ide
  • 32. If you specify alphabetical segment-ordering in your source file with the .ALPHA directive, it will override Is on the command line. Example TASM Is TESTl It This code creates an object file (TESTl.OBJ) that has its segments ordered exactly as they were specified in the source file. It Function Suppresses messages on successful assembly Syntax It Remarks The It option stops any display by Turbo Assembler unless warning or error messages result from the assembly. You can use this option when you are assembling many modules, and you only want warning or error messages to be displayed onscreen. Example TASM It TESTl lu Function Sets version ID in command line Syntax lu version Remarks The lu option lets you specify which version of Turbo Assembler or MASM you want to use to run your modules. This is the command-line version of the VERSION directive. lutthk Function Enables support for Windows 95 flat thunking. Syntax lutthk Remarks The lutthk option tells Turbo Assemblerto assemble code generated by the Microsoft thunk compiler. For more information, see the thunking example and documentation provided in the EXAMPLES THUNK95 directory off your main TASM directory. Iv Syntax Iv Remarks The Iv option is included for compatibility. It performs no action and has no effecton the assembly. Chap ter 2, Us i ng dire ct ivesan d swit ches 23
  • 33. /w /w Function Contro~s the generation of warning messages Syntax /w w- [warnclassl w+ [warnclass1 Remarks The Iw option controls which warning messages are emitted by Turbo Assembler. If you specify Iw by itself, "mild" warnings are enabled. Mild warnings merely indicate that you can improve some aspect of your code's efficiency. Ifyou specify Iw- without warnclass, all warnings are disabled. If you follow Iw- with warnclass,only that warning is disabled. Each warning message has a three-letter identifier: , ALN ASS BRI< GTP ICG !NT LCO MCP OPI OPP OPS OVF PDC PQK PRO RES TPI UNI Segment alignment Assuming segment is 16-bit , Brackets needed Global type doesn't match symbol type Inefficient code'generation INT 3 generation Location coun,ter overflow MASM compatibility pass Open IF conditional Open procedure Open segment Arithmetic overflow Pass-dependertt construction Assuming constant for [const] warning Write-to memory in protected mode needs CS override Reserved word warning illegal warning For turning off uninitialized segment warning If you specify Iw+ without warnclass, all warnings are enabled. If you specify Iw+ with' warnclass from the preceding list, only that warning will be enabled. By default, Turbo Assembler first starts assembling your file with all warnings enabled except the inefficient code-generation (lCG) and the write-to-memory in protected mode (PRO) warnings. You can use the WARN and NOWARN directives within your source file to control whether a particular warning is allowed for a certain range of source lines. These directives are described later in this chapter. Example TASM /w TESTl 24 TurboA sse mbIer Use r' s Guide
  • 34. Ix Ix The following statement in TESTl.ASM issues a warning message that would not have appeared without the Iw option: mov bx,ABC ABC =1 iinefficient code generation warning With the command line TASM Iw-OVF TEST2 no warnings are generated if TEST2.ASM contains dw lOOOh * 20h Function Includes false conditionals in listing Syntax Ix Remarks If a conditional IF, IFNDEF, IFDEF, and so forth evaluates to False, the Ix option causes the statements inside the conditional block to appear in the listing file. This option also causes the conditional directives themselves to be listed; normally they are not. You must specify a listing file on the command line or use the 11 option, otherwise Ix has no effect. You can use the .LFCOND, .SFCOND, and .TFCOND directives to override the effects of the Ix option. Example TASM /x TESTl /z Function Displays source lines along with error messages Syntax /z Remarks The Iz option tells Turbo Assembler to display the corresponding line from the source file when an error message is generated. The line that caused the error is displayed before the error message. With this option disabled, Turbo Assembler justdisplays a message that describes the error.. Example TASM /z TESTl /zd Function Enables line-number information in object files Syntax· /zd Remarks The Izd option causes Turbo Assembler to place line-number information in the object file. Thislets the debugger display the current location in your source code, but does not Chapt er 2, Us i n9 dire ct ivesan d swit ches 25
  • 35. Izi put the information in the object file that would allow the debugger to access your data items. Ifyou run out of memory when trying to debug your program, you can use Izd for some modules and Izi for others. Example TASM /zd TESTl /zi Function Enables debug information in object file Syntax /zi .Remarks· The Izi option tells Turbo Assembler to output complete debugging information to the object file. This includes line-number records to synchronize source code display and data type information to let you examine an~ modify your program's data. The Izi option lets you use all the features of the debugger to step through your program and examine or change your data items. You can use Izi on all your program's modules, or just on those you're interested in debugging. Since the Izi switch adds information to the object and executable programs, you might not want to use it on all your modules if you run out of memory when running a program under the debugger. Example TASM. zi TESTl /zn Function Disables debug information in object file Syntax /zn Remarks The Izn option tells Turbo Assembler to disable the output of debuggmg information to the object file. It's useful for overriding any prevailing Izi switch in a configurationfile. Indirect command files At any point when entering a command line, Turbo Assembler lets you specify an indirect command file by preceding its name with an "at" sign (@). For example, TASM /dTESTMODE @MYPROJ.TA causes the contents of the file MYPROJ.TA to become part of the command line, exactly as if you had typed in its contents. directly. This useful feature lets you put your most frequently used command lines and file lists in a separate file. And you don't have to place your entire command linein one indirect file, since you can use more than one indirect file on the command line and can also mix indired command files with normal arguments. For example, TASM @MYFILES @IOLIBS /dBUF=1024 26 Turbo Assembler User's Guide
  • 36. / z n This way you can keep long lists of standard files and options in files, so that you can quickly and easily alter the behavior of an individual assembly run. You can either put all your file names and options on a single line in the command file, or you can split them across as many lines as you want. The configuration file Turbo Assembler also lets you put your most frequently used options into a configuration file in the current directory. This way, when you run Turbo Assembler, it looks for a file called TASM.CFG in your current directory. If Turbo Assembler finds the file, it treats it as an indirect file and processes it before anything else on the command line. This is helpful when you have all the source files for a project in a single directory, and you know that, for example, you always want to assemble with emulated floating-point instructions (the Ie option). You can place that option in the TASM.CFG file, so you don't have to specify th~t option each time you start Turbo Assembler. The contents of the configuration file have exactly the same format as an indirect file. The file can contain any valid command-line options, on as many lines as you want. The options are treated as if they all appeared on one line. The contents of the configuration file are processed before any arguments on the command line. This lets you override any options set in the configuration file by simply placing an option with the opposite effect on the command line. For example, if your • configuration file contains la Ie and you invoke Turbo Assembler with TASM Is Ir MYFILE MYFILE is your program file, and your file will be assembled with sequential segment- ordering (Is) and real floating-point instructions (/r), even though the configuration file contained the la and Ie options that specified alphabetical segment-ordering and emulated floating-point instructions. Chap t er 2, Usin 9 dire cti vesan d swit c hes 27
  • 37. 28 Turbo Assembler User's Guide
  • 38. General programming concepts This chapter introduces you to the basic concepts of Turbo Assembler. We'll look at Ideal mode versus MASM mode, commenting your programs and extending lines of code, includingfiles, using predefined symbols, and using several important directives that produce module information. Although this is a lot of ground to cover, it will give you a good idea of what assembly language is all about. Turbo Assembler Ideal mode For those of you struggling to make MASM do your bidding, this may be the most important chapter in the manual. In addition to near-perfect compatibility with MASM syntax, Turbo Assembler smooths the rough areas of assembly language programming with a MASM derivative we call Ideal mode. Among other things, Ideal mode lets you know solely by looking at the source text exactly how an expression or instruction operand will behave. There's no need to memorize all of MASM's many quirks and tricks. Instead, with Ideal mode, you write clear, concise expressions that do exactly what you want. Ideal mode uses nearly all MASM's same keywords, operators, and statement constructions. This means you can explore Ideal mode's features one at a time without having to learn a large number of new rules or keywords. Ideal mode adds strict type checking to expressions. Strict type checking helps reduce errors caused by assigning values of wrong types to registers and variables, and by using constructions that appear correct in the source text, but are assembled differently than you expect. Instead of playing guessing games with values and expressions, you can use Ideal mode to write code that makes logical and aesthetic sense. With strict type checking, Ideal mode expressions are both easier to understand and less prone to producing unexpected results. And, as a result, many of the MASM idiosyncrasies we warn you about in other chapters disappear. Chap ter 3, Genera I pro 9ram min 9 con cepts 29
  • 39. Ideal mode also has a number offeatures that make programming easier for novices aI}d experts alike. These features include the following: • duplicate member names among multiple structures • complex HIGH and LOW expressions • predictable EQU processing • correct handling of grouped data segments • improved consistency among directives • sensible bracketed expressions Why use Ideal mode? There are many good reasons why you should use Turbo Assembler's Ideal mode. .If you are justlearning assembly language, you can easily construct Ideal mode expressions and statements that have the effects you desire. You don't have to experiment trying different things until you get an instruction that does what you want. If you are an experienced assembly language programm~r, you can use Ideal mode features to·write complex programs using language extensions such as nestable structures and unions. As a direct benefit of cleaner syntax, Ideal mode assembles files 30% faster than MASM mode. The larger your projects and files, the more savings in assembly time you'll gain by switching to Ideal mode. Strong type-checking rules, enforced by Ideal mode, let Turbo Assembler catch errors that you would otherwise have to find at run time or by debugging your code. This is. similar to the way high-level language compilers point out questionable constructions and mismatched data sizes. Although Ideal mode uses a different syntax for some expressions, you can still write programs that assemble equally well in both MASM and Ideal modes. You can also switch between MASM and Ideal modes as often as necessary within the same source file. This is especially helpful when you're experimenting with Ideal mode features, or when you're converting existing programs written in the MASM syntax. You can switch to Ideal mode for new code that you add to your source files and maintain full MASM compatibility for other portions of your program. Entering and leaving Ideal mode Use the IDEAL and MASM directives to switch between Ideal and MASM modes. Turbo Assembler always starts assembling a source file in MASM mode. To switch to . Ideal mode, include the IDEAL directive in your source file before using any Ideal mode capabilities. From then on, or until the next MASM directive, all statements behave as described in this chapter. You can switch back and forth between MASM and Ideal modes in a source file as many times as you wish and at any place. Here's a sample: DATA SEGMENT abc LABEL·BYTE xyz DW 0 DATA ENDS istart in MASM mode iabc addresses xyz as a byte idefine a word at label xyz iend of data segment 30 Turbo Assembler User's Guide
  • 40. IDEAL SEGMENT CODE PROC MyProc ENDP MyProc ENDS MASM CODE SEGMENT Func2 PROC IDEAL MASM Func2 ENDP CODE ENDS iswitch to Ideal mode isegment keyword now comes first iproc keyword comes first, too iIdeal mode programming goes here irepeating MyProc label is optional irepeating segment name not required iswitch back to MASM mode iname now required before segment keyword iname now comes before proc keyword, too iMASM-mode programming goes here iswitch to Ideal mode again! ido some programming in Ideal mode ;back to MASM mode. Getting dizzy? iname again required before keyword iname again required here In Ideal mode, directive keywords such as PROC and SEGMENT appear before the identifying symbol names, which is the reverse of MASM's order. You also have the option of repeating a segment or procedure name after the ENDP and ENDS directives. Adding the name can help clarify the program by identifying the segment or procedure that is ending. This is a good idea, especially in programs that nest multiple segments and procedures. Youdon't have to include the symbol name after ENDP and ENDS, however. MASM and Ideal mode differences This section describes the main differences between Ideal and MASM modes; If you know MASM, you might want to experiment with individual features by converting small sections of your existing programs to Ideal mode. Further details of these differences are in Chapter 5, "Using expressions and symbol values." Expressions and operands The biggest difference between Ideal and MASM mode expressions is'the way square . brackets function. In Ideal mode, square brackets always refer to the contents of the enclosed quantity. Brackets never cause implied additions to occur. Many standard MASM constructions, therefore, are not permitted by Ideal mode. In Ideal mode, square brackets must be used in order to get the contents of an item. For example, mov ax,wordptr displays a warning message. You're trying to load a pointer (wordptr) into a register (AX). The correct form is mov ax, [wordptr] Using Ideal mode, it's clear you are loading the contents of the location addressed by wordptr (in the current data segment at DS) into AX Chap t er3 , General programming concepts 31
  • 41. If you wish to refer to the offset of a symbol within a segment, you must explicitly use the OFFSET operator, as in this example: mov ax/OFFSET wordptr Operators The changes made to the expression operators in Ideal mode increase the power and flexibility of some operators while leaving unchanged the overall behavior of expressions. The precedence levels of some operators have been changed to facilitate common operator combinations. The period (.) structure member operator is far more strict in Ideal mode when accurately specifying the structure members you're referring to. The expression to the left of a period must be a structure pointer. The expression.to the right must be a member name in that structure. Here's an example of loading registers with the values of specific structure members: iDeclare variables using the structure types S_Stuff SomeStuff <> O_Stuff OtherStuff <> mov aX1 [S_Stuff.AmountJ mov bl l [O_Stuff.AmountJ Suppressed fixups iload word value ilaad byte value Turbo Assembler in Ideal mode does not generate segment-relative fixups for private segments that are page- or pgragraph-aligned. Because the linker does not require such fixups, assembling programs in Ideal mode can result in smaller objectfiles that also link more quickly than object files generated by MASM mode. The following demonstrates how superfluous fixups occur in MASM but not in Ideal mode: SEGMENT DATA PRIVATE PARA VARl DB 0 VAR2 DW ENDS SEGMENT CODE ASSUME ds:DATA mav ax/VAR2 ENDS ina fixup needed Note .This difference has no effect on code that you write. The documentation here is simply for your information. Operand for BOUND instruction The BOUND instruction expects a WORD operand, not a DWORD. This lets you define the lower and upper bounds as two constant words, eliminating the need to convert the operand to a DWORD with an explicit DWORD PTR. In MASM mode, you must write BOUNDS DW 1/4 BOUND AXI DWORD PTR BOUNDS ilawer and upper bounds irequired far MASMmade but in Idealmode, you need only write 32 Tu rboA sse mbIer Use r' s Guide
  • 42. BOUNDS DW 1,4 BOUND AX, [BOUNDS] Segments and groups ;lower and upper bounds ;legal in Ideal mode The way Turbo Assembler handles segments and groups in Ideal mode can make a difference in getting a program up and running. If you're like most people, you probably shudder at the thought of dealing with a bug that has anything to do with the interaction of segments and groups. Much of the difficulty in this process stems from the arbitrary way that MASM and, therefore, Turbo Assembler's MASM mode, makes assumptions about references to data or code within a group. Fortunately, Ideal mode alleviates some of the more nagging problems caused by MASM segment and group directives, as you'll see in the information that follows. Accessing data in asegment belonging to agroup In Ideal mode, any data item in a segment that is part of a group is considered to be principally a member of the group, not of the segment. An explicit segment override must be used for Turbo Assembler to recognize ,the data item as a member of the segment. MASM mode handles this differently; sometimes a symbol is considered to be part of the segment instead of the group. In particular, MASM mode treats a symbol as part of a segment when the symbol is used with the OFFSET operator, but as part of a group when the symbol is used as a pointer in a data allocation. This canbe confusing because When you directly access the data without OFFSET, MASM incorrectly generates the reference relative to the segment instead of the group. Here's an example of how easily you can get into trouble with MASM's addressing quirks. Consider the following incomplete MASM program, which declares three data segments: dseg1 SEGMENT PARA PUBLIC 'data' v1 DB 0 dseg1 ENDS dseg2 SEGMENT PARA PUBLIC 'data' v2 DB 0 dseg2 ENDS dseg3 SEGMENT PARA PUBLIC 'data' v3 DB 0 dseg3 ENDS DGROUP GROUP dseg1,dseg2,dseg3 cseg SEGMENT PARA PUBLIC 'code' ASSUME cs:cseg,ds:DGROUP start: Chap t er 3,G enera I. pro 9ra mmin 9 con cept s 33
  • 43. mov ax/OFFSET vI mov bx/OFFSET v2 mov ex/OFFSET v3 eseg ENDS END start The threesegments, dsegi, dseg2, and dseg3, are grouped under one name, DGROUP. As a result, all the variables in the individual segments are stored together in memory. In the program source text, each of the individual segments declares a BYTE variable, labeled vi, v2, and v3. In the codeportion of this MASM program, the offset addresses of the three variables are loaded into registers AX, BX, and ex. Because of theearlier ASSUME directive and because the data segments weregrouped together, you might think that MASM would calculate the offsets to the variables relative to the entire group in which the variables are eventually stored in memory. But this is not what happens. Despite your intentions, MASM calculates the offsets of the variables relative to the individual segments, dsegi, dseg2, and dseg3. It does this even though the three segments are combined into one data segment in memory, addressed here by register DS. It makes no sense to take the offsets of variables relative to individual segments in the program text when those segments are combined into a single segment in memory. The only wayto address such variables is to refer to their offsets relative to the entire group. To fix the problem in MASM, you must specify the group name along with the OFFSET keyword: mov ax/OFFSET DGROUP:vl mav bx/OFFSET DGROUP:v2 mav ex/OFFSET DGROUP:v3 Although this now assembles correctly and loads the offsets of vi, v2, and v3 relative to DGROUP (which collects the individual segments), you might easily forget to specify the DGROUP qualifier. If you make this mistake, the offset values will not correctly locate the variables in memory and you'll receive no indication from MASM that anything is amiss. In Ideal mode, there's no need to go to ,all this trouble: IDEAL SEGME_NT dsegl PARA PUBLIC 'data' vI DB 0 ENDS SEGMENT dseg2 PARA PUBLIC 'data' v2 DB 0 ENDS SEGMENT dseg3 PARA PUBLIC 'data' v3 DB 0 ENDS GROUP DGROUP dsegl/dseg2/dseg3 SEGMENT eseg PARA PUBLIC 'code' ASSUME es:eseg, ds:DGROUP 34 Turbo Assembler User's Guide
  • 44. start: mov ax, OFFSET vl mov ax, OFFSET v2 mov ax, OFFSET v3 ENDS END start The offsets to vI, v2, and v3 are correctly calculated relative to the group that collects the individual segments to which the variables belong. Ideal mode does not require the DGROUP qualifier to refer to variables in grouped segments. MASM mode does require the qualifier and, even worse, gives no warning of a serious problem should you forget to specify the group name in every single reference. Commenting the program Commenting your code is a great way to help you (or anyone who has to maintain your code in the future) quickly understand how it functions. Using comments is good programming practice in any language. They can describe the semantic as opposed to syntactic function of your code. We recommend that you use comments liberally in your Turbo Assembler code, and this section describes how you can do so. Comments at the end of the line There are several ways to·comment assembler code. One approach is to add a comment at the end of a line using the semicolon (;), such as mov [bxl, al istore the modified character Another way to comment assembler code is to use the line continuation character () as a comment char~cter. See the section called "Extending the line" for an example of how this is done. The COMMENT directive The COMMENT directive lets you comment blocks of code. COMMENT ignores all text from the first delimiter character and the line containing the next occurrence of the delimiter. The following example uses * as a delimiter character: COMMENT * Work long and late to get free pizza Note COMMENT only works in MASM mode. Chapter 3, General programming concepts 35
  • 45. Extending the line For lines of code that are longer than 80 characters, Turbo Assembler provides the line continuation character. Use this character at the end of your line, because Turbo Assembler ignores any characters that follow it on the same line. The maximum line length is·1024 when you use ; however, tables, records, and enums might have definitions that are longer than 1024 characters. An alternative that does not have the 1024 character limitation is the multiline definition syntax. Here's an example of the syntax (for an enum definition): faa enum { £1 ;Multiline version f2 f3 f4 f5 f6 f7 f8 } A more compact version of the same definition: faa enum £1, f2 , { f3, f4 f5,f6 f7 ,f8} ; compact multiline version When using multiline definitions, remember these rules: • The left brace that starts the definition must be the last token on the starting line. It does not, hpwever, have to precede the first element in the list. • You c~ot include any directives such as IF or INCLUDE inside the multiline definition. MASM-mode line continuationis available if you select VERSION M510, M520. Strings and other tokens can be extended across multiple lines if the 1/" character is the last character on the line. For example, VERSION M510 DB 'Hello out there you guys' You can place standard Turbo Assembler mode line continuation anywhere in a line, and it is always available. It functions as a comment as well. For example, ARG al :word, a2:word, a3:word 36 Turbo Assembler User's Guide first argument second argument ;final argument
  • 46. Using INCLUDE files Include files let you use the same block of code in several places in your program, insert the block in several source modules, or reduce the size of your source program without having to create several linkable modules. Using the INCLUDE directive tells Turbo Assembler to find the specified files on disk and assemble them as if they were a part of the source program. The Ideal mode syntax: INCLUDE "filename" The MASM mode syntax: INCLUDE filename Note You can nest INCLUDE directives as deep as you want. filename can specify any drive, directory, or extension. Iffilename does not include a directory or drive name, Turbo Assembler first searches for the file in any directories you specify with the II command-line option, and then in the current directory. Predefined symbols Turbo Assembler provides a number of predefined symbols that you can use in your programs. These symbols can have different values at different places in your source file, and are similar to equated symbols you define using the EQU directive. When Turbo Assembler encounters one of these symbols in your source file, it replaces it with the current value of that predefined symbol. Some of these symbols are text (string) equates, some are numeric equates, and others are aliases. The string values can be used anywhere that you would use a character string, for example, to initialize a series of data bytes using the DB directive: NOW DB ??time Numeric predefined values can be used anywhere that you would use a number: ·IF ??version GT 100h Alias values make the predefined symbol into a synonym for the value it represents, allowing you to use the predefined symbol name anywhere you would use an ordinary symbol name: ASSUME cs:@code All the predefined symbols can be used in both MASM and Ideal mode. If you use the 1m! command-line option when assembling, you must use the predefined symbol names exactly as they are described on the following pages. Note The following rule applies to predefined symbols starting with an at-sign (@): Thefirst letter ofeach word that makes up part ofthe symbol name is an uppercase letter (exceptfor segment names); the rest ofthe word is lowercase. As an example, @FileName Chap t er 3, Genera I pro 9ram min 9 con cept 5 37
  • 47. Notice that@FileNameperforms an alias-equate for the current assembly line. The exception is redefined symbols/which refer to segments. Segment names begin with an ahsign (@)and are all lowercase. Ear example, , '@curseg @fardata For symbols that start with two question marks (??), the letters are all lowercase. For example, ??date ??version Note that the ??date symbol defines a text equate that represents today's date. The exact format of the date string is determined by the country code. The ??version symbol lets you write source files that can take advantage of features in particular versions of Turbo Assembler. This equate also lets your source files know whether they are being assembled by MASM or Turbo Assembler, since ??version is not defined by MASM. ,Similarly, ??filename defines an eight-character string that represents the file name being assembled. The file name is padded with spaces if it contains fewer than eight characters. The ??time symbol defines a text equate that represents the current time. The exact format of the time string is determined by the country code. ASSigning values to symbols . Turbo Assembler provides two directives that let you assign values to symbols: EQU and =. The EQU directive defines a string, alias, or numeric equate. To use it, specify the following syntax, name EQU expression where name is assigned the result of evaluating expression. name must be anew symbol name that you haven't previously defined in a different manner. ill MASM mode, you can only redefine a symbol that youdefined using the EQU directive if you first define it as a string equate. In MASM mode, EQU can generate anyone of three kinds of equates: alias, expression,or string. The = directive defines only a numeric equate:.To use it, specify name = expression where nameis assigned the result of evaluating expression, which must evaluate to either ,a constant or an address within a segment. name can either be a new symbol name, or a symbol that you previously defined with =. Since the = directive has far more predictable behavior than the EQU directiv~ in MASM mode, use =instead of EQU wherever you can. General module structure Turbo Assembler provides several directives to help you work with modules of code. The remainder of this chapter describes these directives. 38 Turbo Assembler User's Guide
  • 48. The VERSION directive Using the VERSION directive lets you specify which version of Turbo Assembler or MASM you've written particular modules for. This is helpful for upward and downward compatibility of various versions of TASM and MASM. The VERSION directive also puts you into the operating mode for the specified version. You can specify the VERSION directive as either a command-line switch or within program source code. Within code, the syntax is VERSION <versioD_ID> You can specify the following legal version IDs: M400 MASM4.0 MSOO MASMS.O MS10 MASMS.1 MS20 MASM S.2 (Quick ASM) 100 Turbo Assembler 1.0 T101 Turbo Assembler 1.01 T200 Turbo Assembler 2.0 T2S0 Turbo Assembler 2.5 T300 Turbo Assembler 3.0 T310 Turbo Assembler 3.1 T320 Turbo Assembler 3.2 T400 Turbo Assembler 4.0 T410 Turbo Assembler 4.1 TSOO Turbo Assembler S.O The command-line syntax is: IV<version_ID> As an example, if you wanted to assemble a program written for MASM S.O, you could leave the source for the program intact and use the switch /uM510. Here are the general rules: Note 1 The VERSION directi~e always selects MASM mode by default, because that is the starting mode of operation for both MASM and Turbo Assembler. 2 The VERSION directive limits the high-priority keywords available to those in the specified compiler and version. As a result, some features that were added to later versions are unavailable to you. . 3 From Ideal mode, the VERSION directive is unavailable if you select a version prior to T300. To use the VERSION directive in this case, you must switch to MASM mode first. 4 No attempt is made to limit access to low priority keywords, since these will not affect compatibility. Chap t er 3, Genera I pr 0 9ram min 9 con cept s 39
  • 49. Previous versions of Turbo Assembler controlled MASMcompatibility with directives such as MASM51, NOMASM51, QUIRKS, SMART, and NOSMART. The VERSION directive supersedes these older directives. See Appendix Bfor a complete list of keywords available with eadl prior version of Turbo Assembler. The NAME directive Use the NAME directive to set the object file's module name. Here is the syntax for it: NAME modulename Turbo Assembler usually uses the source file name with any drive, directory, or - extension as the module name. Use NAME if you wish to change this default name; modulename will be the new name of the module. For example, NAME loader Note' The NAME directive only works in Ideal mode. The END directive Use the END directive to mark the end of your source file. The syntax looks like this: END [ startaddress 1 startaddress is an optional symbol or expression that specifies the address in your program where you want execution to begin. If your program is linked from multiple source files, only one file can specify a startaddress. startaddress can be an address within the module; it can also be an external symbol defined in another module, declared with the EXTRN directive. Turbo Assembler ignores any text after the END directive in the source file. Example .MODEL small .CODE START: iBody of program goes here END START iprogram entry point is "START" THIS LINE IS IGNORED SO IS THIS ONE Displaying amessage during assembly Turbo Assembler provides two directives that let you display a string on the console during assembly: DISPLAY and %OUT. You can use these directives to report on the progress of an assembly, either to let you know how far the assembly has progressed, or to let you know that a certain part of the code has been reached. The two directives are essentially the same except that DISPLAY displays a quoted string onscreen, and %OUT displays a nonquoted string onscreen. In both Ideal anq MASM modes,the syntax for DISPLAY is DISPLAY "text" 40 Turbo Assembler User's Guide
  • 50. where text is any message you want to display. The syntax for %OUT in both Ideal and MASM modes is %OUT text where, again, text is the message that you want displayed. Displaying warning messages Turbo Assembler lets you choose what (if any) warning messages you'll receive when you assemble different parts of your code. Each warning message contains a three-letter identifier, which you can specify ahead of time to let the assembler know whether or not you want to see warnings of that kind. You can use the WARN directive to enable warning messages, and the NOWARN directive to disable them. The syntax of the WARN directive is WARN [warnclassl where warnclass is the three-letter identifier that represents a particular type of warning message. The available warnclasses are: ALN BRK GTP ICG INT LCO MCP OPI OPP OPS OVF PDC PRO PQK RES TPI Segment alignment Brackets needed Global type doesn't match symbol type Inefficient code generation !NT 3 generation Location counter overflow MASM compatibility pass Open IF conditional Open procedure Open segment Arithmetic overflow Pass-dependent construction Write-to-memory in protected mode using CS Assuming constant for [const] warning Reserved word warning illegal warning Note WARN without a wamc1ass enables all warnings. WARN followed by an identifier only enables that particular warning. Notice that the identifiers used by WARN are the same as those used by the IW command-line option. Here's an example using WARN: WARN OVF DW 1000h * I234h ;enables arithmetic overflow warning ;overflow warning will occur Chap t er 3, Genera I pro 9ram min 9 con cept s 41
  • 51. Use the NOWARN directive to disable specific (or all) warning messages. NOWARN uses the same identifiers described earlier under WARN. Here's an example that uses NOWARN: NOWARN OVF- DW 1000h * 1234h idisable arithmetic overflow warnings idoesn't warn how Note NOWARN without a wamclass disables all warnings. NOWARN with an identifier disables only that particular warning. Multiple error-message reporting By default, Turbo Assembler only allows one error message to be reported for each line of source code. If a source line contains multiple errors, Turbo Assembler reports the most-significant error first. You can control the number of error messages you get for each source line by using the MULTERRS and NOMULTERRS directives. The MULTERRS directive allows the assembler to report more than one error message for each source line. This is sometimes helpful in locating the cause of a subtle error or when the source line contains more than one error. Note that sometimes additional error messages can be a "chain reaction" caused by the first error condition; these IFchain" error messages may disappear once you correct the first error. Here's an example of the MULTERRS directive: MULTERRS mov ax, [bp+abc iproduces two errors: i1) Undefined symbol: abc i2) Need right square bracket Note The NOMULTERRS directive only lets one error or warning message (the most significant message) appear for each source line. When you correct this error, the other error messages may disappear as well. To avoid this problem, use the MULTERRS directive to see all of the error messages. Here is an example of using the NOMULTERRS directive: NOMULTERRS mov ax, [bp+abc ione error: i1) Undefined symbol: abc 42 Turbo Assembler User's Guide
  • 52. Creating object-oriented programs Object-oriented programming is an approach to software design that is based on objects rather than procedures. This approach maximizes modularity and information hiding. The underlying premise behind object-oriented programming is the binding or encapsulation of a data structure with procedures for manipulating the data in the structure into a unit. Object-oriell.ted design provides many advantages. For example, every object encapsulates its data structure with the procedures used to manipulate instances of the data structure. This removes interdependencies in code that can quickly make maintenance difficult. Objects can also inherit a data structure and other characteristics from a parent object, which saves work and lets you transp~rent1y use a single chunk of code for many purposes. If you're not an experienced Turbo Assembler user, you might want to skim through this chapternow, but come back to it later after reading the other chapters of this manual. We've put it here to make you aware of these features, but object-oriented programming in Turbo Assembler is really an advanced topic. It will make more sense after going through the rest of the manual. Terminology Assembler, C++, and Pascal use different terms for various entities in object-oriented programming. The following table outlines the differences among these languages. Table 4.1 Object-oriented programming terminology method method procedure object base object member function class base class method object base object Chapter 4, Creating object-oriented programs 43
  • 53. Table 4.1 Object-oriented programming terminology (continued) parent object derived object field parent class derived class data member parent object derived object field Why use objects in Turbo Assembler? Most people think of assembly language as a low-level language. Turbo Assembler, however, provides many of the features of a high-level language (such as abstract data types, and easy interfacing to other languages). The addition of object-oriented data structures gives Turbo Assembler the power to create object-oriented programs as easily as high-level languages while retaining- the speedand flexibility of assembly language. What is an object? An object consists of a data structure and associated procedures (called methods) that manage data stored in instances of the data structure. An object can inherit characteristics from a parent object. This means that the new object's data structure includes the parent object's data structure, as well as any new data. Also, the new object can call all the method procedures of the parent object, as well as any new method procedures it declares. Note We strongly recommend that you use Ideal mode for object-oriented programming in Turbo Assembler because symbol scoping is global in MASM, which means you can't distinguish the different positions of shown methods. An object having no inheritance is called a base object; an object that inherits another is a derived object. Turbo Assembler defines several symbols you can use when declaring objects. The following table lists these symbols. Table 4.2 Symbols defined for objects @Object <objedname> @Table_<objedname> @TableAddr_<objectname> 44 Turbo Assembler User's Guide A text macro containing the name of the current object (the object last declared). A STRUC data type that describes the object's data structure. A TABLE data type containing the object's method table, which is not the same as an instance of the virtual method table. A label describing the address of the instance of the object's virtual method table, if there is one.
  • 54. Asample object As an example of where you can use objects, consider any program that useslinked lists. Think of a linked list as an object consisting of the linked list data and the operations (methods) that you can perform on it. The linked list data consists of pointers to the head and tail of the linked list (this example contains a doubly linked list because of its flexibility). Each element of the linked list is a separate object instance. The following operations provide the power needed to use a linked list: • Creating the linked list (allocating memory for it). • Destroying the linked list (deallocating memory for it). • Initializing the linked list. • Deinitializing the linked list. • Inserting an item into the middle of the linked list before an existing item. • Appending an item to the end of the linked list. • Deleting an item from the linked list. • Returning the first item in the linked list. • Returning the last item in the linked list. Keep in mind that create and initialize, as well as destroy and deinitialize methods are not synonymous. create and destroy methods allocate and deallocate memory for the linked list object, while the initialize and deinitialize methods only initialize and deinitialize previously allocated instances of the object. If you don't combine initialization with creation, it's possible to statically allocate linked list objects. You'can see how the linked list object canbe inherited by a queue or stack object, since a queue or a stack can be implemented as a linked list with limited operations. For example, you can implement a queue as a linked list where items can be added to the start and taken off the end. If you implement a queue in this way, you must disable the inherited linked list methods that are illegal on a queue (such as inserting into the middle of the list). Declaring objects Declaring an object consists of declaring the data structure for the object, and declaring the method procedures that you can call for the object. Declaring an object does not involve creating an instance of the object. You'llieam how to do this later. Declaring abase object When you declare an object, Turbo Assembler creates a STRUC that declares the data for the object, and a TABLE that declares the methods for the object. The object's data declaration is a structure with the same name as the object. The object's method declarations are stored in a TABLE data type, named@Table_<objectname>. Chapter 4, Creating object-oriented programs 45
  • 55. For example, for the list object, two data types are declared: list A STRUC declaring the following members: list_head dword pointer to head of list lisCtail dword pointer to tail of list A TABLE declaring the following methods: construct dword pointer to the procedure list_construct destroy dword pointer to the procedure list_destroy and so on... STRUC declares the data for the object that is created whenever you create an instance of the object. TABLE declares the table of default method procedures for the declaration. Turbo Assembler maintains this data type; it does not create an instance of the table anywhere in your program memory. However, you'll see later that you must include an instance of the table for any object that uses virtual methods. Here's an example of anobject declaration for a linked list (for more on STRUC as it applies to declaring objects, see Chapter 8): list STRUC GLOBAL METHOD { construct:dword = list_construct destroy:dword = list_destroy init:dword = list_init deinit:dword =list_deinit virtual insert:word = list_insert virtual append:word = list_append virtual remove:word = list_delete virtual 'first:word = lisLfirst virtual last:word = list_last } list_head dd? list_tail dd? ENDS ilist constructor procedure ilist destructor procedure ilist initializer procedure ilist deinitializer procedure ilist node insert procedure ilist node append procedure ilist node remove procedure ilist first node procedure ilist last node procedure ilist h~adpointer ilist tail pointer In this example, the METHOD keyword shows that you're using an extended form of STRUC, and are defining an object called list. Each entry consists of a method name, a colon, and the size of a pointer to the method procedure (WORD for near procedures, DWORD for far procedures). This is followed by an equal sign, and the name of the procedure to call for that method. Let's look at this example to see what's happening. METHOD indicates an object method call and is followed by a list of the method procedure declarations for the object. These declarations are enclosed in braces ({ }) because the list of methods requires more than one line. Each method declaration tells Turbo Assembler which procedure it should use to manipulate the object when invoking that method name. For example, the first method procedure declaration construct:dword = list_construct 46 Turbo Assembler User's Guide
  • 56. declares a method named construct that is a far procedure (because a DWORD stores the pointer to it). The actualprocedure name of the method is list_construct, which should be defined elsewhere in the source code. Turbo Assembler considers a method to be virtual if it's preceded by the keyword VIRTUAL. When you call such a method, Turbo Assembler will locate the method's procedure address by looking it up from a table present in memory at run time. Otherwise, the method is a static method, meaning that Turbo Assembler can determine its address at compile time. For example, the method construct is a static method, while the method insert is declared as a virtual method. Later in this chapter, we'll explain why you might want to choose virtual or static methods. ' The data structure for the method immediately follows the method procedure declaration section. This definition uses the syntax for the standard STRUC directive. This example contains declarations for the linked list's head and tail pointers. The method declaration portion of the object declaration doesn't place any data in the object's datastructure unless you've used virtual methods. Instead, these declarations cause Turbo Assembler to build a separate table data structure that contains the specified method procedure addresses as default values. You should have an instanceof this table for every object, and you must explicitly place the table. We'll explain how to do this later in this chapter. Since the object declaration must exist in the module containing the method procedures for the object (as well as included in any source code that uses the object), you should declare the object itself in a separate file that can be INCLUDEd into the source code. We recommend using a file name in the form objectname.ASO (ASsembly Object). This file should consist of only the object declaration. The object methods should be in . another source file so that you can include the object declaration wherever you need it. For example, the linked list object declaration in the previous example wouldbe placed in the file LIST.ASO. The file LIST.ASM could be used to define the object's method procedures. Any program making use of the objects would include LIST.ASO, but not LIST.ASM. The keyword GLOBAL in the object declaration causes Turbo Assembler to publish information that lets you use the object in a module other than the one it's defined in. The object declaration must also be included in all modules that use the object. Declaring aderived object An object that inherits another object's methods and data is called a derived object. You can't override the members of the parent data structure, but you can override the individual methods by respecifying them in the new object method list. An object can inherit any other single object, whether that other object is a base or derived object itself. The inherited object is called the parent object. The derived object inherits the data and methods of the parent object, so you should only use inheritance when these methods and data are useful to the new object. For example, you can define a queue object that inherits the linked list object because you can implement a queue as a linked list. Here's an example of sucha derived object: Chap t er 4, ere at i ngob j ect -0 ri ented pro 9ram s 47
  • 57. queue STRUC GLOBAL list METHOD { init:DWORD=queue_init virtual insert:word = queue_insert virtual rernove:word = queue_delete virtual first:word = queue_first virtual last:word = queue_last virtualenqueue:word = list_append virtual dequeue:word = queue_dequeue } ENDS ; (queue node insert ; procedure) ; (queue node delete ; procedure) ; (queue first node procedure) ; (queue end node procedure) ;queue enqueue procedure . ;queue dequeue procedure Placing the object name list before the METHOD keywords tells Turbo Assembler that the new object queue inherits the methods and data of the object, list. Any object name placed in this location will be inherited.by the object being declared. You Can use only one name (only single inheritance is supported). The new queue object inherits all the data and methods from the list object, unless you override it. Note that queue needs its own init to install the pointer to the virtual method table for queues. The.inherited insert, remove,first, and last method declarations for the queue are respecified in the declaratio~, so these,methods are replaced with the indicated procedures. . Two new methods have been declared for the queue: enqueue and dequeue. Notice that the method procedure for enqueue is the same as for appending to a linked list. However, we need a new procedure to dequeue from the queue, and this we call queue_dequeue. Th~ queue object has no additional data declared other than what it inherits from list. It inherits the linked list's head and tail pointers, which are still needed for the queue because of the linked list methods used to manage the queue. Declaring amethod procedure Method procedures manipulate instances of the object. They are much like library routines in that they should have a well-defined call and a return value/interface, but knowledge of how the method procedures work internally is not necessary. The method procedures for an object should provide comprehensive management of the objects; thatis, they should be the only procedures allowed direct access to the objects. Furthermore, you should use the concepts of data abstraction when you design the methods: You should be able to call the method procedures without having any knowledge of the inner workings of the method procedures. In all other respects, you can write method procedures for any language or interface you want, although usually C++ or Pascal calling conventions are used. Any arguments to the procedures are up to you as well. One argument that is usually required is a pointer to an object instance. Some method procedures might require additional parameters. For example, the initialization method for the list object requires justthe pointer to the 48 Turbo Assembler User's Guide
  • 58. list object, while the list insert method requires a pointer to the list, a pointer to the new node to insert, and a pointer to the node it's inserted after. Note There are advantages and disadvantages to using both static and virtual methods. Static methods are resolved at compile time, and result in direct calls to the method procedure. This makes the call faster, and does not require you to use intermediate registers (as in virtual method calls). However, since these calls are resolved at compile time, static method calls don't have the flexibility of virtual method calls. Virtual method calls are made indirectly through an instance of the virtual method table for the object. The fact that the call is indirect gives virtual methods the disadvantage of requiring you to use intermediate registers when you make the call (which could complicate your code). A big advantage, however, is that virtual method calls are resolved at run time. Thus, you can make virtual method calls for a derived object by calling a common ancestor object's method without having to know exactly what sort of descendant object you're dealing with. Note Declare static and virtual method procedures exactly the same way as any other procedure, with the following exception: if you omit the procedure name for virtual methods, you'll cause an empty uninitialized location in the virtual method table and Turbo Assembler won't warn you if you do this. Omitting the procedure name is an error if the method is not virtual, since virtual methods don't go into the table. Here's an example of a method procedure: iConstruct a Linked-List object. iThis is the method "construct". iThis must be a static method. iReturns DX:AX pointing to linked-list object, null if none. iObject is allocated but not yet initialized. list_construct PROC PASCAL FAR USES ds i-- Allocate the Linked-List object -- ii«do the allocation here» ret ENDP The virtual method table The virtual method table (VMT) is a table of addresses of the procedures that perform virtual methods. Usually this table is placed in the program's data segment. Any object having virtual methods requires an instance of the VMT somewhere in the program. Use the TBLINST directive to create the instance of the VMT for an object. Since this directive creates a table for the most recently declared object, you should place this directive immediately after the object declaration, as in the following: INCLUDE list.aso DATASEG TBLINST Chap t er 4, Crea tin gob j ect -0 ri ented pro 9ra: ms 49
  • 59. Initializing the virtual method table Simply creatirig the instance of the VMT is not enough to let you make calls to virtual methods. Every object with virtual methods includes a pointer to the VMT in its data structure. You must initialize this pointer whenever you create an instance of an object, and can use TBLINIT to' do so. Initialize the VMT pointer in the init method for the object as follows: iInitialize a Linked List object. iThis is the method "init". iThis must be a static method! list_init PROC PASCAL FAR ARG @@list:dword USES ds,bx ENDP Ids bX,@@list i-- Initialize any virtual method table for the object at ds:bx TBLINIT ds:bx i-- Initialize the object's data -- ii«initialize any data for the object here ...» ret Notice that the init method must be static because you can't call a virtual method for an object instance until after you initialize the virtual table pointer. Calling an object method Use the CALL instruction to invoke object methods.Turbo Assembler provides an extension to the standard CALL instruction, CALL..METHOD, for calling method procedures. ,Notice that the syntax for CALL is similar for calling both static or virtual ~ethods. Calling astatic method When making a call to a method procedure, you should write the CALL.'.METHOD instruction as if you were making a call to a virtual method, even if you know that you're calling a static method. Doing so will have no ill effects on static method calls, and gives you the flexibility of changingmethods from static to virtual or back again without having to change all the calls to the method. For the same reasons, you should specify a reasonable selection for the mtermediate calling registers, even if you know that the method you're calling is static. Calls to static methods are resolved at compile time to direct calls to the desired method procedure for the object. However, when making the call, you should not make a direct call to the method procedure; instead, use the extended CALL..METHODinstruction. The following example shows a sample call to the static init method for the linked list object. ' 50 Turbo Assembler User's Guide
  • 60. CALL foolist METHOD list: init pasc,al, ds offset foolist CALL es:di METHOD list:init pascal,es di The call address itself is the address of aninstance of the object. This address is used for syntactic reasons only; the actual call generated is a direct call to the method procedure. In this example, the first call is to the init method for the object list. Since this is a static method, you make a direct call to the method procedure list_init. Turbo Assembler ignores the object instance,foolist (except that it's passed as an argument to the method procedure). The method name is followed by the usual extended call language and parameter list. The language and parameters depend on the method you're calling, and one of the parameters is generally a pointer to the instance of the object. In this example, the method accepts a single parameter, which is a pointer to the instance of the object. Calling avirtual method Any call to a virtual method requires an indirect call to the method procedure. You can use the extended CALL..METHOD instruction to let this happen. Turbo Assembler generates the following instructions to perform the call: 1 Loa,d intermediate registers from the object instance with a pointer to the VMT. 2 Make an indirect call to the appropriate table member. Therefore, when you specify CALL <instance> METHOD <object>:<method> USES <seg>:<reg> <calling_stuff> the generated instructions are as follows: MOV <reg>, [<instance>. <virtual_method_table_pointer>] CALL [«seg>:<reg» .<method>] <calling_stuff> The first instruction loads the selected register <reg> with the address of the table from the VMT pointer field of the object structure. The second instruction makes an indirect call to the appropriate method in the table. For example, a call of the form CA~L es:di method list:insert uses ds:bx pascal,es di,es dX,es cx generates a sequence like mov bx, [es:di.@Mptr_list] CALL [ds:bx.insert] pascal, es di,es dX,es cx Note that for objects declared with NEAR tables, only the offset register will be loaded by the CALL..METHOD instruction. The segment register should already contain the correct value. The following example shows how to make sure that the segment register is properly set up. ' ;Append a node at the end of a Linked-List object. ;This is the virtual method "listiappend". list_append PROC PASCAL NEAR Chap t er 4, ere atin gob j ect -0 riented pro 9ram s 51
  • 61. ARG @@list:dword, @@new:dword USES ds,bx,es,di movax,@Data mov ds,ax les di,@@list sub ax,ax CALL es:di method list:insert uses ds:bx pascal, es di,@@new,ax ax ret ENDP Note You can't call any virtual methods until after you initializethe VMT pointer in the object's data. This is because the pointer loads tJ::te address of the VMT (from which the address of the desired virtual method procedure is retrieved). Thus, if you haven't initialized the pointer to the VMT, any virtual method call will result in a call to some random address. As another example, consider the base object node, which you can include in any object placed in a linked list or a queue. node STRUC GLOBAL METHOD { construct:dword =node_construct destroy:dword = node_destroy init:dword =node_init deinit:dword = node_deinit virtual next:word =node_adv virtual prev:word = node_back virtual print:word = node_print } node_next node_prey ends dd ? dd ? inode constructor routine inode destructor routine inode initialization routine inode deinitializ·ation routine inext node routine iprevious node routine iprint contents of node inext node pointer iprev node pointer You can define any number of other objects inheriting the node object, to let it use a linked list or queue. Here are two examples: mlabel STRUC GLOBAL node METHOD { virtual print:word = label_print } label_name label_addr label_city ,label_state label..:.zip ENDS db 80 dup (?) db 80*2 dup (?) db 80 dup (?) db 2 dup (?) db 10 dup (?) book STRUC GLOBAL node METHOD virtual print:word = book_print } book_title db 80 dup (?) book_author db 80 dup (?) ENDS 52 TurboA sse mbIer Use r's Guide
  • 62. In the next example, we're making calls to methods by calling printit for both label and book objects. It doesn't matter what object gets passed to printit, as long as node is an ancestor. Because the print method is a virtual method" the call is made indirectly through the VMT for the object. For the first call to printit, the method procedure labelyrint is called, because we're passing an instance of a label object. For the second call to printit, the method procedure bookyrint is called, because we're passing an instance of a book object. Note that if the method print were static, then the call in printit would always call the nodeyrint procedure (which is not desirable). call printit pascal,«instance address of label object» call printit pascal, «instance address of book object» printit proc pascal near arg @@obj:dword uses ds,si,es,bx mov aX,@data mov eS,ax Ids si,@@obj call ds:si method node:print uses es:bx pascal,ds si ret endp Calling ancestor virtual methods Using ancestor virtual methods can help you write methods for derived classes since you can reuse some of the code. For example, queues can use the same listing method as a list, as long as you specify whether the item is a queue or a list. Within the list class, you can have virtual show:word = list_show and within the queue class, virtual show:word = queue_show The list_show routine might print LIST SHOW:, followed by a listing of the individual items in the list. However, if the derived class queue_show uses the listing routine, it should print its own title, QUEUE SHOW: and use list_show only for the mechanics of sequentially going through the list and printing individual elements. list_show can determine the kind of structure passed to it, and whether it should print the list title. If the routine for list_show looks at the pointer to the virtual method table (VMT) of the structure passed to it, it can determine whether the pointer matches the one installed for lists in the list3nit routine (or if it differs). If the VMT pointer in the structure does not point to the VMT for lists, the structure is probably a derived type. list_show can do this checking with the following statements: cmp [([es:di]) .@mptr_list];offset @TableAddr_LIST jne @@not a list '; Skip over printing the list title Chapter 4, Creating object-oriented programs 53
  • 63. If we come here, it is a list, and the list title should be printed. @@not a list: ;- Now show the individual list elements. So how do'we call the list class show method from within a queue_show routine? Ifyou were to directly call list_show, you could have a problem if the name of the routine used for the show method of the list class ever changes. (You might not remember to change what queue_show calls.) If you put the following statement in queue_show, call (es:di) method list:show you'd have an infinite loop because even though list is specified as the class for which show should be called, the VMTwill be used because show is a virtual method. Since the VMT for the structure would have been pointing to queue_show, you'd end up back in the same routine. " The best way to call the list class show method would be call t@table_list I show Turbo Assembler automatically translates this statement to a direct call to list_show, , since lisCshow was specified as the value for the show element of the @table_list when' the list class was declared. Note that even though list declares show to be virtual, specifying the call causes Turbo Assembler to make a direct call without the VMT lookup. Note Virtual routines are usually called through an indirect lookup to a VMT. In the event that you need to use the VMT for the list class (for example, some initialization routine might'change the show element of the table to point to different routines depending on whatoutput device to use for the show command of all list class elements), the following statements use the list class VMT: mov bx,offset @TABLEADDR_LIST call [(@table_list ptr es:bx) .SHOW] This is very similar to the sequence of instructions that Turbo Assembler uses to make the indirect call using the VMT. More on calling methods Often, you might find it necessary to calla parent object's method from inside a derived method procedure. You can also use the CALL..METHOD statement to do this. You can use the IMP instruction with the METHOD extension in the same way you use the CALL..METHOD instruction. This instruction provides optimal tail recursion. See Chapter 12 for more information about the CALL..METHOD and IMP..METHOD instructions. 54 Turbo Assembler User's Guide
  • 64. Creating an instance of an object To create an instance of an object, you can call an object's constructor method (which allocates memory for an object instance) or allocate an instance of the object in a predefined (static) data segment. You can create an instance of the object exactly the same way you create an instance of a structure. For example, examine the following instances of objects: foolist list {} iinstance of a list fooqueue label queue queue {} iinstance of a queue queue {list_head=mynode,list_tail=mynode} iinstance of a queue When you create an instance of an object, you can override any of the object's default data values as defined in the object declaration by specifying the overriding values inside the braces. You can't, however, override the methods for an object when you create an instance of an object. Programming form for objects It's a good idea to keep method procedures in a separate file from the method declaration, and from the code that uses the object. We recommend placing method procedures in a file with the name of the object and an extension of .ASM. For example, the method procedures for the linked-list object would go into the file LIST.ASM. The method procedure file must INCLUDE the method declaration from the .ASO file. An example of the method procedures for the list object is described at the end of this chapter. This excerpt from the LIST.ASM file (on the example disks) shows the general structure of this file. ._------------------------------- , i-- Define Linked-List objects -- ._------------------------------- , MODEL SMALL LOCALS i** Define Linked-List object ** INCLUDE node.aso i** Create instance of Linked-List virtual method table ** DATASEG TBLINST i** Linked-List methods ** CODESEG ii«include all method procedures here» Chapter 4, Creating object-oriented programs 55
  • 65. In general, you should use the following form for object-oriented programming in Turbo Assembler: <object>.ASO <object>.ASM INCLUDEs <parenCobject>.ASO, if any; contains GLOBAL object declaration <md a GLOBAL directive for each method procedure. INCLUDEs <object>.ASO; contains TBLINST directive and method procedure .declarations; has an init method with a TBLINIT somewhere inside. Note that you can use the TBLINST and TBLINIT directives even when there are ~currentlyno virtual methods in the object; in that case, no action is taken. We therefore recommend using the TBLINST and TBLINIT directives regardless of whether virtual methods are currently pte~ent in an object: Place the TBLINST directive in an appropriate data segment and the TBLINIT directive in the object's initialization method (which must be a static method). You must call this method before using any other methods for the object. 56 Turbo Assembler User's Guide
  • 66. Using expressions and symbol values Expressions and symbols are fundamental components of an assembly language program. Use expressions to calculate values and memory addresses. Symbols represent different kinds of values. This chapter describes the different types of these language components, and how you can use them. Constants Constants are numbers or strings that Turbo Assembler interprets as a fixed numeric value. You can use a variety of different numeric formats, including decimal, hexadecimal, binary, and octal. Numeric constants A numeric constant in Turbo Assembler always starts with a digit (0-9), and consists of an arbitrary number of alphanumeric characters. The actual value of the constant depends on the radix you select to interpret it. Radixes available inTurbo Assembler are binary, octal, decimal, and hexadecimal, as shown in Table 5.1: Table 5.1 Radixes Binary 01 Octal 01234567 Decimal 0123456789 Hexadecimal 0 1 2345 6 789 ABC D E F Note that for hexadecimal constants, you can use both upper- and lowercase letters. Chap ter 5, Usin 9 expre ssion san d symb0 I val ues 57
  • 67. Turbo Assembler determmes the radix of a numeric constantby first checking the LAST character of the constant. The characters in the following table determille the radix used to interpret the numeric constant. Table 5.2 Characters determining radixes B o Q D H Binary Octal Octal Decimal Hexadecimal You can use both uppercase and lowercase characters to specify the radix of a number. If the last character of the numeric constant is not one of these values, Turbo Assembler will use the current default radix to interpret the constant. The following table lists the available numeric constants and their values. Table 5.3 Numeric constants 77d 77h ffffh Offffh 88 77 decimal 77 hexadecimal illegal; doesn't start with a digit FFFF hexadecimal Interpretation depends on current default radix Changing the default radix You can use the RADIX or .RADIX directivesto change the current default radix. Use the following syntax for Ideal mode: RADIX expr~ssion Here's the MASM mode syntax: .RADIX expression expression must have a value of either 2 (binary), 8 (octal), 10 (decimal), or 16 (hexadecimal). Turbo Assembler assumes that the current default radix is decimal while it processes the RADIX directive. String constants String constants always begin with a single or double quote, and end with a matching single or double quote. Turbo Assembler converts the characters between the quotes to ASCII values. Sometimes, you might want to include a quote within a string constant.' To do this, use a pair of matching quotes as a single matching quote character within the string. For example, 58 Turbo Assembler User's Guide
  • 68. 1 It lIS 1 represents It 1 S Symbols A symbol represents a value, which canbe a variable, address label, or an operand to an assembly instruction and directive. Symbol names Symbol names are combinations of letters (both uppercase and lowercase), digits, and special characters. Symbol names cadt start with a digit. Turbo Assembler treats symbols as either case sensitive or case insensitive. The command line switches !ML, !MU, and IMX control the case sensitivity of symbols. For more information about these command-line switches, see Chapter 2. Symbols names can be up to 255 characters in length. By default, symbol names are significant up to 32 characters. You can use the !MV command-line switch to change the number of charac~ers of significance in symbols. The underscore C), question mark (?), dollar sign ($), and at-sign (@) can all be used as part of a symbol name. In MASM mode only, you can use a dot (.) as the first character of a symbol name. However, since it's easy to confuse a dot at the start of a symbol with the dot operator (which performs a structure member operation), it's better not to use it in symbol names. Symbol types Each symbol has a type that describes the characteristics and information associated with it. The way you define a symbol determines its type. For example, you can declare a symbol to represent a numeric expression, a text string, a procedure name, or a data variable. Table 5.4 lists the types of symbols that Turbo Assembler supports. Table 5.4 Symbol types text_macro alias numericatexpr multiline_macro struc/union table struc/table_member record record-field enum An address. subtypes are PWORD or FWORD, QWORD, TBYTE, and an address of a named structure or table. Code subtypes are SHORT, NEAR, and FAR A text string An equivalent symbol The value of a numerical expression Multiple text lines with dummy arguments A structure or union data type A table data type A structure or table member A record data type A record field An enumerated data type Chap ter 5, Usin 9 expre ssion san d symb0 I val ues 59
  • 69. Table 5.4 Symbol types (continued) segment group type proctype A segment A group A named type A procedure description type Simple address subtypes Symbols subtypes describe whether the symbol represents the address of a byte, a word, and so forth. Table 5.5 shows the simple address subtypes that Turbo Assembler provides. , Table 5.5 Address subtypes UNKNOWN Unknown or undetermined address subtype. BYTE Address describes a byte. WORD Address describes a word. DWORD Address describes a 4-byte quantity. PWORD or FWORD QWORD TBYTE SHORT NEAR FAR PROC DATAPTR CODEPTR struclunion_name table_name record_name type_name TYPE expression Address describes a6-byte quantity. Address describes an 8-byte quantity. Address describes a lO-byte quantity. Address describes a short label/procedure address. Address describes a near label/procedure address. Address describes a far label/procedureaddress. Address describes either a near or far label/procedure address, depending on the currently selected programming model. Address describes either a word, dword, or pword quantity, depending on the currently selected programming model. Address describes either a word, dword, or pword quantity, depending on the currently selected programming model. Address describes an instance of the named structure or union. Address describes an instance of the named table. Address describes an instance of the named record; either a byte, word, or dword quantity. . Address describes an instance of the named enumerated data type; either a byte, word,or dword quantity. Address describes an instance of the named type; Address describes an item whose subtype is the address subtype of the expression; Ideal mode only. Address describes procedure of proctype. 60 Turbo Assembler User'& Guide
  • 70. Describing acomplex address subtype Severaldirectives let you declare and use complex address subtypes. These type expressions are similar to C in that they can represent multiple levels of pointer indirection, for example, the complex type expression PTR WORD represents a pointer to a word. (The size of the pointer depends on the segmentation model you selected with MODEL.) Table 5.6 shows a syntax summary of complex address subtypes: Table 5.6 Complex address subtypes ···synt~"···' simple_address_subtype [dist]PTR[complex_address_subtype] the specified address subtype a pointer to the specified complex address subtype, the size of which is determined by the current MODEL or by the specified distance, if present You can describe the optional distance parameter in the following ways: Table 5.7 Distance syntax NEAR FAR SMALL NEAR LARGE NEAR SMALL FAR LARGE FAR use a near pointer;.can be either 16 or 32 bits, depending on the current model use a far pointer; can be either 32 or 48 bits, depending on current model use a 16-bit pointer; 80386 and 80486 only use a 32-bit near pointer; 80386 and 80486 only use a 32-bit far pointer; 80386 and 80486 only use a 48-bit far pointer; 80386 and 80486 only The type of the object being pointed to is not strictly required in complex pointer types; Turbo Assembler only needs to know the size of the type. Therefore, forward references are permitted in complex pointer types (but not in simple types). Expressions Using expressions lets you produce modular code, because you can represent program values symbolically. Turbo Assembler performs any recalculations required because of changes (rather than requiring you to do them). Turbo Assembler uses standard infix notation for equations. Expressions can contain operands and unary or binary operators. Unary operators are placed before a single Chapter 5, Using expressions and symbol values 61
  • 71. operand; binary operators are placed between two operands. Table 5.8 shows examples of simple expressions. .. TableS.S Simple expressions 5 constant 5 -5 constant-5 4+3 constant 7 4*3 constant 12 4*3+2*1 constant 14 4*(3+2)*1 constant 21 Appendix Bcontains tp.e full Backus-Naur form (BNF) grammar that Turbo Assembler uses for expression parsing in both MASM and Ideal modes. This grammar inherently describes the valid syntax of Turbo Assembler expressions, as well as operator precedence. Expression precision Turbo Assembler always uses 32-bit arithmetic in Ideal mode. In MASM mode, Turbo Assembler uses either 16- or 32-bit arithmetic, depending on whether you select the 80386 processor. Therefore, some expressions might produce different results depending on which processor you've selected. For example, (1000h 1000h) /1000h evaluates to 1000h if you select the 80386 processor, or to 0 if you select the 8086, 80186, or 80286 processors. Constants in expressions You can use constants as operands in any expression. For example, mov ax,S ;"5" is a constant operand Symbols in expressions When you use a symbol in an expression; the returned value depends on the type of symbol. You can use a symbol by itself or in conjunction with certain unary operators that are designed to extract other information from the entity represented by the symbol. Registers Register names represent 8086-family processor registers, and are set aside as part of the expression value. For example, 5+ax+7 62 TurboA sse mbIer Use r's Guide
  • 72. This expression has a final value ofax+12, because AX is a register symbol that Turbo Assembler sets aside. The following list contains register symbols: 8086 80186,80286 80386 AX,BX,CX,DX,SI,D1,BP,CS,DS,ES,SS Same as 8086 8086 registers, plus EAX, EBX, ECX, EDX, ES1, ED1, EBP, FS, GS, CRO, CR2, CR3, DRO, DR!, DR2, DR3, DR6, DR7 80486 80386 registers, plus: TR3, TR4, TR5 Standard symbol values Some symbols always represent specific values and don't have to be defined for you to use them. The following table lists these symbols and their values. Table 5.9 Standard symbols $ Current program counter NOTHING 0 ? 0 UNKNOWN 0 BYTE 1 WORD 2 DWORD 4 PWORD 6 FWORD 6 QWORD 8 TBYTE 10 NEAR Offffh FAR Offfeh PROC Either Offffh or Offfeh, depending on current model CODEPTR Either 2 or 4, depending on current model DATAPTR Either 2 or 4, depending on current model Simple symbol values Turbo Assembler returns the following values for symbols used by themselves: Table 5.10 Values of symbols used by themselves address_name Returns the address. numericaCexpr--,-name Returns the value of the numerical expression. table_name I table_member_name Returns the default value for the table memberspecified in the definition of the table. Returns the offset of the member within the table or structure (MASM mode only). " Chapter 5, Using expressions and symbol values 63
  • 73. Table 5.10 Values of symbols used by themselves (continued) record_name <...> record_name {...} recordJield_name segment_name group_name struc/union_name Returns a mask where the bits reserved to represent bit fields in the record definition are1, the rest are O. Returns the initial value a record instance would have if it were declared with the Salne text enclosed in angle brackets (see Chapter 12 for details). Similar to record_name <...>. Returns the number ofbits the field is displaced fromthe low orderbit of the record (also known as the shift value). Returns a mask where the bits required to represent the maximum value present in the enum definition are 1, the rest are O. Returns the segment value. Returns the group value. Returns the size in bytes of the structure or union, but only ifit is 1, 2, or 4; all other sizes return a value of O. If the type is defined as a synonym for a structure or union, the value returned is the same as for a structure or union. Otherwise, the size of the type is returned (with Offffh for short and near labels, and Offfeh for far labels). Returns OFFFFh if the proctype describes a near procedure, or OFFFEh for a far procedure. All other symbols types return the value O. Note that when you use a text macro name in an expression, Turbo Assembler substitutes the string value of the text macro for the text macro symbol. Similarly, when you use an alias name, Turbo Assembler substitutes the symbol value that the alias represents for the alias symboL The LENGTH unary operator The LENGTH operator returns information about the count or number of entities represented by a symbol. The actual value returned depends on the type of the symbol, as shown in the following table. Table 5.11 LENGTH operator return values LENGTH address_name Returns the count of items allocated when the address name was defined. LENGTH strucltable_member_name Returns the count of items allocated when the member was defined (MASM mode only). The length operator (when applied to all other symbol types) returns the value 1. Here are some examples using the LENGTH operator: MSG DB "Hello" array DW 10 DUP (4 DUP (1),0) nurnbrs DD 1,2,3,4 lrnsg =LENGTH rnsg larray =LENGTH array Inurnbrs = LENGTH nurnbrs 64 Turbo Assembler User's Guide ; =1, no DUP ;=10, DUP repeat count ; =1, rio DUP
  • 74. The SIZE unary operator The SIZE operator returns size information about the allocated data item. The value returned depends on the type of the symbol you've specified. The following table lists the available values for SIZE. Table 5.12 . SIZE values Expression SIZE address_name SIZE struc/union_name SIZE table_name SIZE struc/table_member_name SIZE record_name SIZE segment_name SIZE type_name .Value In Ideal mode, returns the actual number of bytes allocated to the data variable. In MASM mode, returns the size of the subtype of address_name (UNKNOWN=O, BYfE=l, WORD=2, DWORD=4, PWORD=FWORD=6, QWORD=8, TBYTE=10, SHORT=NEAR=Offffh, FAR=Offfeh, structure address=size of structure) multiplied by the value of LENGTH address_name. Returns the number of bytes required to represent the structure or union. Returns the number of bytes required to represent the table. Returns the quantity TYPE struc/table_member_name * LENGTH struc/ table_member_name (MASM mode only). Returns the number of bytes required to represent the total number ofbits reserved in the record definition; either 1, 2, or 4. Returns the number of bytes required to represent the maximum value present in the enum definition; either 1, 2, or 4. Returns the size of the segment in bytes. Returns the number of bytes required to represent the named type, with short and near labels returning Offffh, and far labels returning Offfeh. The SIZE operator returns the value 0 when used on all other symbol types. The WIDTH unary operator The WIDTH operator returns the width in bits of a field in a record. The value depends on the type of symbol. The following table shows these types of symbols. You can't use WIDTH for any other symbol types. Table 5.13 WIDTH values WIDTH record_name Returns the total number of bits reserved in the record definition. WIDTH recordJield_name Returns the number of bits reserved for the field in the record definition. WIDTH enum_name Returns the number of bits required to represent the maximum value in the enum definition. MASK unary operator The MASK operator creates a mask from a bit field, where bits are set to 1 in the returned value and correspond to bits in afield that a symbol represents. The value Chapter 5, Using expressions and symbol values 65
  • 75. returned depends on the type of symbol, as shown in the following table. Note that you can't use MASK on any other symbols. Table 5.14 MASK return values MASK record_name Returns a mask where the bits reserved to represent bit fields in the record definition are I, the rest O. MASK recordJield_name ' Returns a mask where the bits reserved for the field in the record definition are I, the rest O. Returns a mask where the bits required to represent up to the maxiinum value present in the enum definition are I, the rest O. General arithmetic operators General arithmetic operators manipulate constants, symbol values, and the values of other general arithmetic operations. Common operators are addition, subtraction, multiplication, and division. Others operators are more specifically tailored for assembly language programming. We'll;discuss a little about all of these in the next few sections. Simple arithmetic operators Turbo Assembler supports the simple arithmetic operators shown in the following table. Table 5.15 Simpl~ arithmetic operators + expression Expression. - expression exprl + expr2 exprl - expr2 exprl *expr2 exprl / expr2 exprl MOD expr2 Negative of expression. exprl plus expr2. exprl minus expr2. exprl multiplied by expr2. exprl divided by expr2 using signed integer division; note that expr2 cannot be 0 or greater than 16 bits in extent. Remainder of exprl divided by expr2; same rules apply as for division. Logical arithmetic operators Logical operators let you perform Boolean algebra. Each of these operators performs in a bitwise manner; that is, the logical operation is performed one bit at a time. The following table shows the logical operators. Table 5.16 Logical arithmetic operators NOT expression exprl ANDexpr2 exprl OR expr2 exprl XOR expr2 . expression bitwise complemented exprl bitwise ANDed with expr2 exprl bitwise ORed with expr2 exprlbitwise XORed with expr2 66 Turbo Assembler User's Guide
  • 76. Bit shift operators. Shift operators move values left or right by a fixed number of bits. You can use them to do quick multiplication or division, or to access the value of a bitfield within a value. The following table lists the bit shift operators. Table 5.17 Bit shift operators ···~~~~~S~Q~··";· exprl SHL expr2 exprl SHR expr2 exprl shifted left by expr2 bits (shifted right if expr2 is negative). exprl shifted right by expr2 bits (shifted left if expr2 is negative). Note that the SHL and SHR operators shift in Os from the right or left to fill the vacated bits. Comparison operators Comparison operators compare two expressions to see if they're equal or unequal, or if one is greater than or less than the other. The operators return a value of -1 if the condition is true, or a value of 0 if the condition is not true. The following table shows how you can use these operators. Table 5.18 Comparison operators exprl EQ expr2 exprl NE expr2 exprl GT expr2 exprl GE expr2 exprl LT expr2 exprl LE expr2 -1 if exprl is equal to expr2; otherwise, O. -1 if exprl is not equal to expr2; otherwise, O. -1 if exprl is greater than expr2; otherwise, O. -1 if exprl is greater than or equal expr2; otherwise, O. -1 if exprl is less than expr2; otherwise, O. -1 if exprl is less than or equal expr2; o~erwise, O. EQ and NE treat expressions as unsigned numbers. For example; -1 EQ offffh has a value of -1 (unless you've selected the 80386 processor or used Ideal mode; then, -1 EQ offffffffh has a value of -1). GT, GE, LT, and LE treat expressions as signed numbers. For example, 1 GE -1 has a value of -1, but 1 GE Offffh has a value of O. Setting the address subtype of an expression Turbo Assembler provides operators that let you override or change the type of an expression. The following table lists these operators. Table 5.19 Type override operators ~ '.'h exprl PTR expr2 type PTR expression or type expression Converts expr2 to the type determined by exprl, where O=UNKNOWN, l=BYTE, 2=WORD,4=DWORD, 6=PWORD, 8=QWORD, 10=TBYTE, Offffh=NEAR, Offfeh=FAR, all others=UNKNOWN; MASM mode only. ! Converts expression to the specified address subtype; Ideal mode only. Chap ter 5, Usin 9 ex pre ssion san d symb0 I val ues 67
  • 77. Table 5.1~ Type override operators (continued) type LOW expression Converts expression to the specified address subtype. Type described mustbe smaller in size than the type of the expression; Ideal mode only. type HIGH expression Converts expression to the specified address subtype. Type described must be smaller in size than the type of the expression; the resulting address is adjusted to point to the high part of the object described by the address expression; Ideal mode only. Here are some examples: IDEAL big DD 12345678h MOV ax, [WORD big] MOV aI, [BYTE PTR big] MOV ax, [WORD HIGH big] MOV ax, [WORD LOW big] MOV aI, [BYTE LOW WORD HIGH big] MASM MOV aX,2 PTR big MOV aX,WORDPTR big iax=5678h ial=78h iax=1234h iax=5678h ial = 3rd byte of big = 34h iax=5678h iax=5678h (WORD has value 2) Obtaining the type of an expression In MASM mode, you can obtain the numeric value of the type of an expression by using the TYPE operator. (You can't do this in Ideal mode, because types can never be described numerically). The syntax of the TYPE operator is TYPE expression The TYPE operator returns the size of the object described by the address expression,as follows: Table 5.20 byte word dword pword qword tbyte short near far struct/union table proctype TYPE values 1 2 4 6 8 10 Offffh Offffh Offfeh Size of a structure or union instance Size of a table instance Returns OFFFFh if the proctype describes a near procedure, or OFFFEh for a far procedure Here's an example: 68 TurboA 55 embIer U5 er's Guide
  • 78. avar = 5 bvar db 1 darray dd 10 dup (1) x struc dw ? dt ? ends fp label far tavar = TYPE avar tbvar = TYPE bvar tdarray =TYPE darray tx = TYPE x tfp = TYPE fp i=O i= 1 i= 4 i= 12 . i= OFFFEh Overriding the segment part of an address expression Address expressions have values consisting of a segment and an offset. You can specify the segment explicitly as a segment register, or as a segment or group value. (If you specify it as a group value, Turbo Assembler determines which segment register to use based on the values that the segment registers are ASSUMEd to be.) Use the following syntax to change the segment part of an address expression: exprl : expr2 This operation returns an address expression using the offset of expr2, and exprl as a segment or group value. For example, VarPtr dd dgroup:memvar mov el, es: [si+4J idgroup is a group isegment override ES Obtaining the segment and offset of an address expression You can use the SEG and OFFSET operators to get the segment and offset of an expression. The SEG operator returns the segment value of the address expression. Here's its syntax: SEG expression Here is a code example: DATASEG temp DW 0 CODESEG mov aX,SEG temp mov ds,ax ASSUME ds:SEG temp The OFFSET operator returns the offset of the address expression. Its syntax follows: OFFSET expression Note that when you use the offset operator, be sure that the expression refers to the correct segment. For example, if you are using MASM mode and not using the simplified segmentation.directives, the expression Chap ter 5, U5 in9 expre 5 5 ion 5 and symb0 I val ue5 69
  • 79. OFFSET BUFFER ;buffer is a memory address is not the same as OFFSET DGROUP:BUFFER ;Dgroup is the group containing the segment that contains BUFFER unless the segment that contains BUFFER happens to the first segment in DGROUP. In Ideal mode, addresses are automatically calculated relative to any group that a segment belongs to unless you override them with the: operator. In MASM mode, the same is true ifyou use the simplified segment directives. Otherwise, addresses are calculated relative to the segment an object is in, rather than any group. Creating an address expression using the location count~r You can use the THIS operator to create an address expression that points to the current segment and location counter, and has a specific address subtype. You can use the following syntax in Ideal mode: THIS type The Ideal mode syntax lets you build an address expression from the current segment and location counter ofthe specified type. You can use the next syntax in MASM mode: THIS expression The MASM mode syntax functions like the syntax in Ideal mode, but uses the numerical value of the expression to determine the type. These values are: O=UNKNOWN, l=BYTE, 2=WORD, 4=DWORD, 6=PWORD, 8=QWORD, lO=TBYTE, Offffh=NEAR, Offfeh=FAR. For example, ptrl LABEL WORD ptr2 EQU THIS WORD ;similar to ptrl Determining the characteristics of an expression Sometimes, it's useful to determine (within a macro) whether an expression has specific characteristics. The SYMTYPE and .TYPE operators let this happen. The Ideal mode syntax: SYMTYPE expression The MASM mode syntax: .TYPE expression Note The SYMTYPE and .TYPE operators are exactly equivalent; however, .TYPE is available only in MASM mode, and you can use SYMTYPE·only in Ideal mode. 70 TurboA sse mbIer Use r 's Guide
  • 80. SYMTYPE and .TYPE both return a constant value that describes the expression. This value is broken down into the bit fields shown in the following table. Table 5.21 Bit fields from SYMTYPE and .TYPE o Expression is a program relative memory pointer. 1 Expression is a data relative memory pointer. 2 Expression is Ii constant value. 3 Expression uses direct addressing mode. 4 Expression contains a register. 5 .. Symbol is defin~d. 7 Expression contains an externally defined symbol. The expression uses register indirection ([BX]) if bits 2 and 3 are both zero. If Turbo Assembler can't evaluate the expression, SYMTYPE returns appropriate errors. .TYPE, however, will return a value in these situations (usually 0). Referenc-ing structure, union, and table member offsets Structure, union, and table members are global variables whose values are the offset of the member within the structure, union, or table in MASM mode. In Ideal mode, however, members of these data types are considered local to the data type. The dot (.) operator lets you obtain the offsets of members. Here's the Ideal mode syntax: expression . symbol expression must represent an address of a structure, union, or table instance. symbol must be a member of the structure, union, or table. The dot operator returns the offset of the member within the structure. MASM mode also contains a version of the dot operator. However, its function is similar to the + operator, and has the following syntax: exprl . expr2 Describing the contents of an address Many instructions reqtt:iie you to distinguish between an address and the contents of an address. You can do this by using square brackets ([]). For example, MOV M,BX MOV AX, [BX] imove EX into M. imove contents of address BX into AX Here's the general syntax for using square brackets: [ expression ] In MASM mode, the brackets are optional for expressions that are addresses. Complete addresses can't be used as an operand for any 80x86 instruction; rather, only the segment (obtained with the SEC operator) or the offset (obtained with the OFFSET operator) is used. Chapter 5, Using expressions and symbol values 71
  • 81. In Ideal mode, a warning is given when an expression is clearly an address, but no brackets are present. You can disable this warning (see Chapter 12 for further information). However, it's good programming practice to include_these brackets. Implied addition In MASM mode, you can add expressions in several ways: usingthe addition operator (+), using the dot operator (.), or by implied addition (when expressions are separated by brackets or parentheses). For example, MOV AX, 5[BX] MOV AX,5 (XYZ) icontents of address BX+5 icontents of addressXYZ+5 Here's the general syntax for implicit addition: exprl [ expr2 ] or exprl ( expr2 ) Obtaining the high or low byte values of an exp'ression You can use the HIGH and LOW operators on an expression to return its high and low. byte values. This information can be useful in circumstances where, for example, only the high 8 bits of an address offset is required. Here's the syntax of the HIGH and LOW operators: HIGH expression LOW expression For example, magic equ 1234h mov cl,HIGH magic mov cl,LOW magic icl=12h icl=34h Specifying a16· or 32·bit expression When the currently selected processor is the 80386 or higher, Turbo Assembler provides two operators that let you control whether an expression is interpreted as a 16-bit value or as a 32-bit value: the SMALL and LARGE operators. Here are their syntaxes: SMALL expression LARGE expression The SMALL operator flags the expression as representing a 16-bit value. LARGE flags it as representing a 32-bit value. These operators are particularly important when you program for an environment in which some segments are 32-bit and others are 16-bit. For example, the instruction JMP [DWORD PTR ABC] represents an indirect jump to the contents of the memory variable ABC. If you have enabled the 80386 processor, this instruction could be interpreted as either a far jump with a segment and 16-bitoffset, or a near jump to a 32-bit offset. You can use SMALL or LARGE to remove the ambiguity, as follows: 72 Turbo Assembler User's Guide
  • 82. JMP SMALL [DWORD PTR ABC] This instruction causes Turbo Assembler to assemble the jump instruction so that the value read from ABC is interpreted as a 16-bit segment and 16-bit offset. Turbo Assembler then performs an indirect FAR jump. When you use SMALL or LARGE within the address portion of an expression, the operators indicate that the address is a 32-bit address. For example, JMP SMALL [LARGE DWORD PTR ABC] indicates that a large 32-bit address describes the memory variable ABC, but its contents are interpreted as a 16-bit segment and 16-bit offset. Cha pte r 5, Usin 9 expre ssion san d symb0 I val ues 73
  • 83. 74 Turbo Assembler User's Guide
  • 84. Choosing processor directives and symbols The 8086 processor is actually only one of a family of processors known as the iAPx86 family. Members of this family include • The 8088 (which contains an 8-bit data bus), the 8086 (containing a 16-bit data bus) • The 80186 and 80188 (like the 8086 and 8088 but contain additional instructions and run faster than their predecessors) • The 80286 (which contains instructions for protected mode) • The 80386 (which can process 16- and 32-bit data) • The 80486 (an enhanced version of 80386 that runs even faster). • The Pentium (an even faster version of the 80486). Math coprocessors such as the 8087, 80287, and 80387 work with the iAPx86 family so that you can perform floating-point operations. Turbo Assembler provides directives and predefined symbols that let you use the instructions included for particular processors. This chapter describes these directives and symbols. Chapter 6, Choosing processor directives and symbols 75
  • 85. iAPx86 processor directives The iAPx86 family provides a variety of directives for you to use. In the following directives, note that those beginning with. are only available in MASM mode. Table 6.1 P8086 .8086 P186 .186 P286 ·P286N P286P .286 .286C .286P P386 P386N P386P .386 .386C .386P P486 P486N .486 .486C .486P .487 P487 P586 P586N .586 Processor directives Enables assembly of 8086 instructions only. Enables assembly of the 8086 instructions and disables all instructions available only on the 80186,80286, and 386 processors. It also enables the 8087 coprocessor instructions exactly as if the .8087 or 8087 had been issued. Enables assembly of 80186 instructions. Enables assembly of 80186 instructions. Enables assembly of all 80286 instructions. Enables assembly of nonprivileged 80286 instructions. Enables assembly of privileged 80286 instructions. Enables assembly of nonprivileged 80286 instructions. It also enables the 80287 numeric processor instructions exactly as if the .286 or P287 directive had been issued. .Enables assembly of nonprivileged 80286 instructions. Enables assembly of all the additional instructions supported by the 80286 processor, including the privileged mode instructions. It also enables the 80287 numeric processor instructions exactly as if the .287 or P287 directive had been issued. Enables assembly of all 386 instructions. Enables assembly of all nonprivileged 386 instructions. Enables assembly of privileged 386 instructions. Enables assembly of the additional instructions supported by the 386 processor in nonprivileged mode. It also enables the 80387 numeric processor instructions exactly as if the .387 or P387 directive had been issued. Enables assembly of 386 instructions. Enables assembly of all the additional instructions supported by the 386 processor, includirig the privileged mode instructions. It also enables the 80387 numeric processor instructions exactly as if the .387 or P387 directive had been issued. Enables assembly of all i486 instructions. Enables assembly of nonprivileged i486 instructions. Enables assembly of the additional instructions supported by the i486 processor in nonprivileged mode. It also enables the 387 numeric processor instructions exactly as if the .387 or P387 directive had been issued. Enables assembly of all i486 instructions. Enables assembly of all the additional instructions supported by the i486 processor, including the privileged mode instructions. It also enables the 80387 numeric processor instructions exactly as if the .387 or P387 directive had been issued. . Enables assembly of 487 numeric processor instructions. This instruction works only in MASMmode. Enables assembly of 487 numeric processor instructions. This instruction works inboth MASM and Ideal modes. Enables assembly of all Pentium instructions. Enables assembly of nonprivileged Pentium instructions. Enables assembly of the additional instructions supported by the Pentium processor in nonprivileged mode. 76 TurboA S S 9 mbI9 r US9 r' S Gui d9
  • 86. Table 6.1 l.1it¢ctiy~ .586C .586P .587 P587 @Cpu Processor directives (continued) Enables all Pentium instructions. Enables assembly of all the additional instructions supported by the PentiUm processor, including the privileged mode instructions. Enables assembly of Pentium numeric processor instructions. This instruction works only in MASMmode. Enables assembly of Pentium numeric processor instructions. This instruction ~orks in both MASM and Ideal modes. Note The Quick Reference Guide contains details on the assembly instructions supported by each processor. For additional information, refer to the books listed in Chapter 1. Predefined symbols @Cpu Two predefined symbols, @Cpu and @WordSize, can give you information about the type of processor you're using, or the size of the current segment. Here are descriptions of these symbols: Function Numeric equate that returns information about current processor Remarks The value returned by @Cpu encodes the processor type in a number of single-bit fields: o 8086 instructions enabled 1 80186 instructions enabled 2 80286 instructions enabled 3 386 instructions enabled 4 486 instructions enabled 5 586 instructions enabled 7 Privileged instructions enabled (80286, 386, 486) 8 8087 numeric processor instructions 10 80287 numeric processor instructions 11 387 numeric processor instructions The bits not defined here are reserved for future use. Mask them off when using @Cpu so that your programs will remain compatible with future versions of Turbo Assembler. Since the 8086 processor family is upward compatible, when you enable a processor type with a directive like .286, the lower processor types (8086, 80186) are automatically enabled as well. Chapter 6, Choosing processor directives and symbols 77
  • 87. @WordSize This equate only provides information about the processor you've selected at assembly time using the .286 and related directives. The processor type and the CPU your program is executing on at run time are not indicated. Example IPUSH = @Cpu AND 2 ;allow immediate push on 186 and above IF IPUSH PUSH 1234 ELSE , mov ax,1234 push ax ENDIF @WordSize Function Numeric equate that indicates 16- or 32-bit segments Remarks @WordSize returns 2 if the current segment is a 16-bit segment, or 4 if the segment is a 32-bit segment. Example IF @WordSize EQ 4 mov esp,0100h ELSE mov sp,0100h ENDIF 8087 coprocessor directives The following table contains the a.vailable math coprocessor directives. Again, directives beginning with a dot (.) work only in MASM mode.Turbo Assembler User's Guide Table 6.2 .287 .387 .487 .587 .8087 P287 P387 P487 8087 coprocessor directives Enables assembly of all the 80287 numeric coprocessor instructions. Use this directive ifyou know you'll never run programs using an 8087 coprocessor. This directive causes floating- point instructions to be optimized in a manner incompatible with the 8087, so don't use it if you want your programs to run using an 8087. Enables assembly of all the 80387 numeric coprocessor instructions. Use this directive if you knowyou'll never run programs using an 8087 coprocessor. This directive causes floating- point instructions to be optimized in a manner incompatible with the 8087, so don't use it if you want yout programs to run using an 8087. Enables assembly of all 80486 numeric instructions. Enables assembly of all Pentium numeric instructions. Enables all the 8087 coprocessor instructions, and disables all those coprocessor instructions available only on the 80287 and 80387 (the default). Enables assembly of 80287 coprocessor instructions. Enables assembly of 80387 coprocessor instructions. Enables assembly of all 80486 numeric instructions. 78 TurboA sse mbIer Use r's Guide
  • 88. @WordSize Table 6.2 8087 coprocessor directives (continued) Di~~~Y'~;' ,',M~¥1ng; P587 Enables assembly of all Pentium numeric instructions. P8087 Enables assembly of 8087 coprocessor instructions. Coprocessor emulation directives If you need to use real floating-point instructions, you must use an 80x87 coprocessor. If your program has installed a software floating-point emulation package, you can use the EMUL directive to use it. (EMUL functions like Ie.) For example, Finit EMUL Fsave BUF ;real 80x87 coprocessor instruction ;emulated instruction] Note Both EMUL and NOEMUL work in MASM and Ideal modes. If you're using an 80x87 coprocessor, you can either emulate floating-point instructions using EMUL, or force the generation of real floating-point instructions with the NOEMUL directive. Note that you can use EMUL and NOEMUL when you want to generate real floating-point instructions in one portion of a file, and emulated instructions in another. Here's an example using NOEMUL: NOEMUL finit EMUL ;assemble real FP instructions iback to emulation Chapter 6, Choosing processor directives and symbols 79
  • 89. 80 TurboA 5 5 em bIer U5 er'5 Guide
  • 90. Using program models and segmentation Each processor in the 80x86 family has at least four segment registers: CS, DS, ES, and SS. These registers contain a segment value that describes a physical block of memory up to 64K in length (or up to 4 gigabytes on the 80386 and above). All addresses are calculated using one of these segmentregisters as a base value. The meaning of the value stored in a segment register differs depending on whether the processor is using real mode (the ONLY mode available for the 8086 and 80186), where the segment value is actually a paragraph number, or protected mode, where a segment register contains a selector (which has no numerical significance). The operating system or platform for a program determines whether the program operates in real mode or protected mode. If you use protected mode on the 80386 or 80486, the operating system also determines whether large (4 gigabyte) segments are permitted. Turbo Assembler supports all of these environments equally well. In the general80x86 model, programs are composed of one or more segments, where each segment is a physically distinct piece of code or data (or both) designed to be accessed by usinga segment register. From this general scheme, many arbitrary organizations are possible. To apply some order to the chaos, some standard memory models have been devised. Since many high-level languages adhere to these conventions, your assembly language programs should also. One obvious way to break up a program is to separate the program instructions from program data. You can classify each piece of program data as initialized (containing an initial value, such as text messages), or uninitialized (having no starting value). Turbo Assembler usually assigns uninitialized data to a separate segment so that it can be placed at the end of the program, reducing the size of the executable program file. The stack is usually a fairly large portion of the uninitialized data. It's also special because the SS and SP registers are usually initialized automatically to the stack area Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 81
  • 91. when you execute a program. Thus, the standard memory models treat the stack as a separate segm~nt. You can also combine segments into groups. The advantage of using groups is that you can use thesame segment value for all the segments in the group. For example, initialized data, uninitialized data, and stack segments are often combined into a group so that the same segment value can be used.for all of the program data. . This chapter describes how to use models and segments in your code and the directives that make this possible. The MODEL directive The MODEL directive lets you specify one of several standard segmentation models for your program. You can also use it to specify a language for the procedures in your program. Here's the syntax for the MODEL directive: MODEL [model_modifier] memory_model [code_segment_name] [, [language_modifier] language] [, modeLmodifier] In MASM mode, you can use the same syntax, but with the .MODEL directive. memory_model and modeCmodifier specify the segmentation memory model to use for the program. The standard memory models available in Turbo Assembler have specific segments available for: • code • initialized data • uninitialized data • far initialized data • far uninitialized data • constants • stack The code segment usually contains a module's code (but it can also contain data if nece~sary). Initialized data and constants are treated separately for compatibility with some high level languages. They contain data suchas messageswhere the initial value is important. Uninitialized data and stack contain data whose initial value is unimportant. Far initialized data is initialized data that is not part of the standard data segment, and can be reached only by changing the value of a segment register. A module can have more than one far initialized data segment. Far uninitialized data is similar, except that it contains uninitialized data instead of initialized data. The specific memory model determines how these segments are referenced with segment registers, and how they are combined into groups (if at all). When writing a program, you should keep these segments separate, regardless of the program's size. 82 TurboA sse mbIer Use r 's Guide
  • 92. Table 7.1 TINY SMALL MEDIUM COMPACT LARGE HUGE TCHUGE TPASCAL FLAT Then, you can select the proper model to group the segments together. If you keep these segments separate and your program grows, you can choose a larger model. The memory model is the only required parameter of the MODEL directive. Table 7.1 describes each of the standard memory models. • The modeCmodifier field lets you change certain aspects of the model. You can specify more than one model modifier, if you wish. Table 7.2 shows the available model modifiers. Note that you can specify the model modifier in two places, for compatibility with MASM 5.2. If you don't use a model specifier, Turbo Assembler as.sumes the NEARSTACK modifier, and USE32 (if the 80386 or 80486 processor is selected). Unless otherwise specified, DOS is the platform. Use the optional code_segment_name field in the large code models to override the default name of the code segment. Normally, this is the module name with _TEXT appended to it. Standard memory models near near cs=dgroup All code and data combined into a single group called ds=ss=dgroup DGROUP. This model is used for .COM assembly programs. Some languages don't support this model. near near cs= text Code is in a single segment. All data is combined into a ds=ss=dgroup group called DGROUP. This is the most common model for stand-alone assembly programs. far near cs=<module> text Code uses multiple segments, one per module. Data is in a ds=ss=dgroup group called DGROUP. near far cs= text Code is in a single segment. All near data is in a group called ds=ss=dgroup DGROUP. Far pointers are used to reference data. far far cs=<module> text Code uses multiple segments, one per module. All near data ds=ss=dgroup is in a group called DGROUP. Far pointers are used to reference data. far far cs=<module> text Same as LARGE model, as far as Turbo Assembler is ds=ss=dgroup concerned. far far cs=<module>_text This is the same as the LARGE model, but with different ds=nothing segment register assumptions. ss=nothing near far cs=code This is a model to support early versions of Borland Pascal. ds=data It's not required for later versions. ss=nothing near near cs= text This is.the same as the SMALL model, but tailored for use ds=ss=flat under 32-bit flat memory models (Win32 and OS/2). Chapter 7, Using program models and segmentation 83
  • 93. Table 7.2 Model modifiers NEARSTACK Indicates that the stack segment should be included in DGROUP (if DGROUP is • present), and SS should point to DGROUP. FARSTACK Specifies that the stack segment should never be included in DGROUP, and SS should point to nothing. USE16 Specifies (when the 80386 or 80486 processor is selected) that 16-bit segments should be used for all segments in the selected model. USE32 Indicates (when the 80386 or 80486 processor is selected) that 32-bit segments should be used for all segments in the selected model. . DOS, OS_DOS Specifies that the applica~onplatform is DOS. NT, OS_NT Specifies that the application platform is Win32 (Windows NT or Windows 95). OS2, OS_OS2 Specifies that the application platform is OS/2~ language and language_modifier together specify the defaultprocedure calling conventions/and the default style of the prolog and epilog code present in each procedure. They also control how to publish symbols externally for the linker t use. Turbo Assembler will automatically generate the procedure entry and exit code that is proper for procedures using any of the following interfacing conventions: PASCAL, C, CPP (C++), SYSCALL, STDCALL, BASIC, FORTRAN, PROLOG, and NOLANGUAGE. If you don't specify a language, Turbo Assembler assumes the default language to be NOLANGUAGE. Use language_modifier to specify additional prolog and epilog code when you write procedures for Windows, or for the Borland Overlay loader. These options are: NORMAL, WINDOWS, ODDNEAR and ODDFAR. If you don't specify an option, Turbo Assembler assumes the default to be NORMAL. Also note that you can override the default language and language modifier when you define a procedure. See Chapter 10 for further details. You can additionally override the default language when you publish a symbol. Symbols created by the MODEL directive When you use the MODEL directive, Turbo Assembler creates and initializes certain variables to reflect the details of the selected model. These variables can help you write code that's model independent, through the use of conditional assembly statements. See Chapter 15 for information about how you can use variables to alter the assembly process. The @Model symbol The @Model symbol contains a representation of the model currently in effect. It is defined as a text macro with any of the following values: 1 = tiny model is in effect 2 = small or flat 3 = compact 4 = medium 5 =·large 84 Turbo Assembler User's Guide
  • 94. 6 =huge 7 = tchuge o = tpascal The @32Bit symbol The @32Bit symbol contains an indication of whether segments in the currently specified model are declared as 16 bit or 32 bit. The symbol has a value of 0 ifyou specified 16-bit segments in the MODEL directive, or 1if you indicated 32-bit segments. The @CodeSize symbol The @CodeSize text macro symbol indicates the default size of a code pointer in the current memory model. It's set to 0 for the memory models that use NEAR code pointers (TINY, SMALL, FLAT, COMPACT, TPASCAL), and 1 for memory models that use FAR code pointers (all others). The @DataSize symbol The @DataSize text macro symbol indicates the default size of a data pointer in the current memory model. It's set to 0 for the memory models using NEAR data pointers (TINY, SMALL, FLAT, MEDIUM), 1 for memory models that use FAR data pointers (COMPACT, LARGE, TPASCAL), and 2 for models using huge data pointers (HUGE and TCHUGE). The @Interface symbol The @Interface symbol provides information about the language and operating system selected by the MODEL statement. This text macro contains a number whose bits represent the following values: Table 7.3 Model modifiers 0 NOLANGUAGE 1 C 2 SYSCALL 3 STDCALL 4 PASCAL 5 FORTRAN 6 BASIC 7 PROLOG 8 CPP Bit 7 can have a value of 0 for DOS/Windows, or 1 for 32-bit flat models (Windows 95, Windows NT, or OS/2). For example, the value 01h for @Interface shows that you selected the DOS operating system and the C language. Chapter 7, Using program models and segmentation 85
  • 95. Simplified segment directives Once you select a memory model, you can use simplified segment directives to begin the individual segments. You can only lise these segmentation directives after a MODEL directive specifies the memory model for the module. Place as many segmentation directives as you want in a module; Turbo Assembler combines all the pieces with the same name to produce one segment (exactly as if you had entered all the pieces at once after a single segmentation directive). Table 7.4 contains alist of these directives. Table 7.4 Simplified segment directives CODESEG [name] .CODE [name] DATASEG .DATA CONST .CONST UDATASEG .DATA? STACK [size] .STACK [size] FARDATA {name] .FARDATA [name] UFARDATA [name] .FARDATA? [name] Begins or continues the module's code segment. For models whose code is FAR, you can specify a name thatis the actual name of the segment. Note that you can generatemore than one code segment per module in this way. Same as CODESEG. MASM mode only. Begins or continu~s the module's NEAR or default initialized data segment. Same as DATASEG. MASM mode only. Begins or continues a module's constant data segment. Constant data is always NEAR and is equivalent to initialized data. Same as CONST. MASM mode only. Begins or continues a module's NEAR or default uninitialized data segment. Be careful to include only uninitialized data in this segment or the resulting executable program will be larger than necessary. See Chapter 12 for a description of how to allocate uninitialized data. Same as UDATASEG. MASM mode only. Begins or continues a module's stack segment. The optional size parameter specifies the amount of stack to reserve, in words. Ifyou don't specify a size, Turbo Assembler assumes 200h words (lKbytes). In MASM mode, any labels, code, or data following the STACK statementwill not be considered part of the stac~ segment. Ideal mode, however, reserves the specified space, and leaves the stack segment open so that you can add labels or other uninitialized data. You usually only need to use the stack directive if you are writing a stand-alone assembly language program; most high-level languages will create a stack for you. Same as STACK. MASM mode only. Begins or continuesa FAR initialized data segment of the specified name. If you don't specify a name, Turbo Assembler uses the segment name FAR_DATA. You can have more than one FAR initialized data segment per module. Same as FARDATA. MASM mode only. Begins or continues a FAR uninitialized data segment of the specified name. Ifyou don't specify a name, Turbo Assembler uses segment name FAR_BSS. You can have more than one FAR uninitialized data segmentper module. Same as UFARDATA. MASM mode only. Note See Appendix A if you need to know the actual names, class names, and alignments of the segments created with the simplified segmentdirectives. 86 TurboA sse mbIer Use r 's Gu ide
  • 96. Symbols created by the simplified segment directives When you use the simplified segment directives, they create variables that reflect the details of the selected segment, just as the MODEL directive does. See Chapter 15 for further information. The following table lists these symbols. Table 7.5 Symbols from simplified segment directives @code @data @fardata @fardata? @curseg @stack the segment or group that C5 is assumed to be the segment or group that D5 is assumed to be the current FARDATA segment name the current UFARDATA segment name the current segment name the segment or group that 55 is assumed to be The STARTUPCODE directive The STARTUPCODE directive provides initialization code appropriate for the current model and operating system. It also marks the beginning of the program. Here's its syntax: STARTUPCODE or .STARTUP; (MASM mode only) STARTUPCODE initializes the DS, SS, and SP registers. For the SMALL, MEDIUM, COMPACT, LARGE, HUGE, and TPASCAL models, Turbo Assemblersets DS and SS to @data, and SP to the end of the stack. For TINY and TCHUGE models, the STARTUPCODE directive doesn't change the segment registers. The @Startup symbol The @Startup sytpbol is placed at the beginning of the startup code that the STARTUPCODE directive generates. It is a near label marking the start of the program. The EXITCODE directive You can use the EXITCODE directive to produce termination code appropriate for the current operating system. You can use it more than once in a module, for each desired exit point. Here's its syntax: EXITCODE [return_value_exprJ You can use the following syntax only in MASM mode: .EXIT [return_value_exprJ Th; optional return_value_expr describes the number to be returned to the operating system. If you don't specify a return value, T~rbo Assembler assumes the value in the AX register. . Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 87
  • 97. Defining generic segments and groups Most applications can use segments created using the standard models. These standard models, however, are limited in their flexibility. Some applications require full control over all aspects of segment generation; generic segment directives provide this flexibility. The SEGMENT directive The SEGMENT directive opens a segment. All code or data following it will be included in the segment, until a corresponding ENDS directive closes the segment. The Ideal mode syntax for the SEGMENT directive is: SEGMENT name [attributes] You can use the following syntax for MASM mode: name SEGMENT [attributes] name is the name of the segment. You should name segments according to their usages. See Appendix Bfor examples of segment names. You can open and close a segment of the same name many times in a single module. In this case, Turbo Assembler concatenates together the sections of the segment in the order it finds them. You only need to specify the attributes for the segment the first time you open the segment. attributes includes any and all desired segment attribute values, for each of the following: • segment combination attribute • segment class attribute • segment alignment attribute • segment size attribute • segment access attribute Note Turbo Assembler processes attribute values from left to right. Segment combination attribute The segment combination attribute tells the linker how to combine segments from different modules thathave the same name. The following table lists the legal values of the segment combination attribute. Note tp.at if you don't specify the combine type, Turbo Assembler assumes PRIVATE. Table 7.6 Segment combination attribute PRIVATE Segment will not be combined with any other segments of the same name outsideof this module. PUBLIC Segment will be concatenated with other segments of the same name outside of this module to form a single contiguous segment. 88 Turbo Assembler User's Guide
  • 98. Table 7.6 Segment combination attribute (continued) MEMORY Same as PUBLIC. Segment will be concatenated with other segments of the same name outside this module to form a single contiguous segment, used as the default stack. The linker initializes values for the initial SS and SP registers so that they point to the end of these segments. COMMON Locates this segment and all other segments with the same name at the same address. All segments of this name overlap shared memory. The length of the resulting common segment is the length of the longest segment from a single module. VIRTUAL Defines a special kind of segment that must be declared inside an enclosing segment. The linker treats it as a common area and attaches it to the enclosing segment. The virtual segment inherits its attributes from the enclosing segment. The assume directive considers a virtual segment to be a part of its parent segment; in all other ways, a virtual segment is a common area that is combined across modules. This permits the sharing of static data that comes into many modules from included files. AT xxx Locates the segment at the absolute paragraph address that the expression xxx specifies. The linker doesn't emit any data or code for AT segments. Use AT to allow symbolic access to fixed memory locations, such as the display screen or ROM areas. UNINIT Produces a warning message to let you know that you have inadvertently written initialized data to uninitialized data segments. For example, you can specify the following to produce a warning message: BSS SEGMENT PUBLIC WORD UNINIT I BSS I • To disable this warning message, use the NOWARN UNI directive. You can reenable the message by using the WARN UNI directive. Segment class attribute The segment class attribute is a quoted string that helps the linker determine the proper ordering of segments when it puts together a program from modules. The linker groups together all segments with the same class name in memory. A typical use of the class name is to group all the code segments of a program together (usually the class CODE is used for this). Data and uninitialized data are also grouped using the class mechanism. Segment alignment attribute The segment alignment attribute tells the linker to ensure that a segment begins on a specified boundary. This is important because data can be loaded faster on the 80x86 processors if it's properly aligned. The following table lists legal values for this attribute. Table 7.7 BYTE WORD DWORD PARA PAGE MEMPAGE Segment alignment attribute No special alignment; start segment on the next available byte. Start segment on the next word-aligned address. Start segment on the next doubleword-aligned address. Start segment on the next paragraph (16-byte aligned) address. Start segment on the next page (256-byte aligned) address. Start segment on the next memory page (4Kb aligned) address. Turbo Assembler assumes the PARA alignment if you don't specify the alignment type. Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 89
  • 99. Segment size attribute If the currently selected processor is the 80386, segments can be either 16 bit or 32 bit. The segnlent size attribute tells the linker which of these you want for a specific segment. The following table contains the legal attribute values. .Table 7.8 Segment size attribute values USE16 USE32 Segment is 16 bit. A 16-bit segment can contain up to 64K of code and/or data. Segment is 32 bit. A 32-bit segment can contain up to 4 gigabytes ofcode and/or data. Turbo Assembler assumes the USE32 value if you selected the 80386 processor in MASM mode. In Ideal mode, Turbo Assembler assumes USE16 by default. Segment access attribute For any segment in protected mode, you can control access so that certain kinds of memory operations are not permitted. (Note that this feature is currently supported only by the Phar Lap linker. You must generate object code compatible with it using the lop switch if you want to be able to use the segment access attribute.) The segment access attribute tells the linker to apply specific access restrictions to a segment. The following table lists the legal values for this attribute. Table 7.9 Segment access attribute EXECONLY the segment is executable only EXECREAD the segment is readable and executable REAOONLY the segment is readable only READWRITE the segment is readable and writable The Phar Lap linker assumes that the segment is meant to run in protected mode if you select any of these attributes, or if you select the USE32 attribute.·Turbo Assembler assumes the READONLY attribute if you selected the USE32 attribute but did not specify any of these four attributes. The ENDS directive You can use the ENDS directive to close a segment so that no further data is emitted into it. You should use the ENDS directive to close any segments opened with the SEGMENT directive. Segments openedusing the simplified segment directives don't require the ENDS directive. Here's the syntax of the ENDS directive: ENDS [name] For MASM mode only, you can use the following syntax: name ENDS 90 TurboA sse mbIer Use r's Guide .
  • 100. name specifies the name of the segment to be closed. Turbo Assembler will report an error message if name doesn't agree with the segment currently open. If you don't specify a name, Turbo Assembler assumes the currently-open segment. The GROUP directive You can use the GROUP directive to assign segments to groups. A group lets you specify a single segment value to access data in all segments in the group. Here's the.Jdeal mode syntax for the GROUP directive: GROUP name segment_name [I segment_name... J You can use the following syntax for MASM mode: name GROUP segment_name [I segment_name. .. J name is the name of the group. segment_name is the name of a segment you want to assign to that group. The ASSUME directive A segment register must be loaded with the correct segment value for you to access data in a segment. Often, you must do this yourself. For example, you could use the following code to load the address of the current far data segment into DS: MOV AX/@fardata MOV DS/AX When a program loads a segment value into a segment register, you use that segment register to access data in the segment. It rapidly becomes tiring (and is also poor programming practice) to specify a specific segment register every time you process data in memory. Note Use the ASSUME directiveto tell Turbo Assembler to associate a segment register with a segment or-group name. This allows Turbo Assembler to be "smartenough" to use the correct segment registers when data is accessed. In fact, Turbo Assembler uses the information about the association between the segment registers and group or segment names for another purpose as well: in MASM mode, the value tllat the CS register is ASSUMEd to be is used to determine the segment or group a label belongs to. Thus, the CS register must be correctly specified in an ASSUME directive, or Turbo Assembler will report errors every time you define a label or procedure. Here's the syntax of the ASSUME directive: ASSUME segreg : expression [/segreg : expression J or ASSUME NOTHING segreg is one ofCS, DS, ES or SS registers. Ifyou specify the 80386 or 80486 processor, you can also use the FS and GS segment registers. expression can be any expression that Chap t er 7, Usin 9 pro 9ram mod eIsan d se9men tat ion 91
  • 101. evaluates to a group or segment name. Alternatively, it can be the keyword NOTHING. The NOTHING keyword cancels the association between the designated segment register and anysegment or group name. ASSUME NOTHING removes associations between all segment registers and segment or group names. You can use the ASSUME directive whenever you modify asegment register, or at the start of a procedure to specify the assumptions at that point. In practice, ASSUMEs are usually used at the beginning of a module and occasionally within it. If you use the MODEL statement, Turbo Assembler automatically sets up the initial ASSUMEs for. you. If you don't specify asegment in an ASSUME directive, its ASSUMEd value isnot changed. For example, the following code shows how you can load the current initialized far data segment into the DS register, access memory in that segment, and restore the DS register to the data segment value. MOV AX,@fardata MOV DS,AX ASSUME DS:@fardata MOV BX,<far_data_variable> MOV AX,@data MOV DS,AX ASSUME DS:@data Segment ordering The linker arranges and locates all segments defined in a program's modules. Generally, the linker starts with the order in which it encounters the segments in a program's modules. You can alter this order using mechanisms such as segment combination and segment classing. There are other ways to affect the way the linker arranges segments in the final program. For example, the order in which seglnents appear in a module's source can be changed. There are also directives that affect segment ordering. Descriptions of these follow. Changing amodule's segment ordering The order of segments in each module determines the startingpoint for the linker's location of segments in the program. In MASM 1.0,2.0, and 3.0, segments were passed to the linker in alphabetical order. Turbo Assembler provides directives (in MASM mode only) that let you reproduce this behavior. Note that these directives have the same function as thelA and /S command line switches. See Chapter 2 for further details. 92 TurboA sse mbIer Use r's Guide
  • 102. The .ALPHA directive The .ALPHA directive specifies alphabetic segment ordering. This directive tells Turbo Assembler to place segments in the object file in alphabetical order (according to the segment name). Its syntaxis .ALPHA The .SEQ directive The .SEQ directive specifies sequential segment ordering, and tells Turbo Assembler to place segments in the object file in the order in which they were encountered in the source file. Since this is the default behavior of the assembler, you should usually use the .SEQ directive only to override a previous .ALPHA or a command line switch. Here's the syntax of .SEQ: .SEQ DOS ordering of segments: the DOSSEG directive Normally, the linker arranges segments in the sequential order it encounters them during the generation of the program. When you include a DOSSEG directive in any module in a program, it instructs the linker to use standard DOS segment ordering instead. The linker defines this convention to mean the following arrangement of segments in the final program: • segments having the class name CODE (typically code segments) • segments that do not have class name CODE and are not part of DGROUP • segments that are part of DGROUP in the following order: 1 segments not of class BSS or STACK (typically initialized data) 2 segments of class BSS (typically uninitialized data) 3 segments of class STACK (stack space) Thesegments within DGROUP are located in the order in which they were defined in the source modules. Note DOSSEG is included in TASM for backward compatibility only. It is recommended that you do not use the DOSSEG directive in new assembly programs. In addition, do not use the DOSSEG directive if you're interfacing assembly programs with C programs. Changing the size of the stack A procedure's prolog and epilog code manipulates registers that point into the stack. On the 80386 or 80486 processor, the stack segment canbe either 16 bits or 32 bits. Turbo Assembler therefore must know the correct size of the stack before it can generate correct prolog and epilog code for a procedure. The stacksize is automatically selected if you selected a standard model using the MODEL statement. Chap t er 7, Usin 9 pro 9ram mod eIsan d se9in entat ion 93
  • 103. Turbo Assembler provides directives that can set or override the default stack size for procedure prolog and epilog generation. The following table lists these directives. Table 7.10 Stack size modification directives SMALLSTACK LARGESTACK Indicates that the stack is 16 bit Indicates that the stack is 32 bit 94 Tur boA sse mbIe r Use r' s Guide
  • 104. Defining data types Defining data types symbolically helps you write modular code. You can easily change or extend data structures without having to rewrite code by separating the definition of a data type from the code that uses it, and allowing symbolic access to the data type and its components. Turbo Assembler supports as many or more symbolic data types than most high-level languages. This chapter describes how to define various kinds of data types. Defining enumerated data types An enumerated data type represents a collection of values that canbe stored in a certain number of bits. The maximum value stored determines the actual number of bits required. Here is the Ideal mode syntax for declaring an enumerated data type: ENUM name [enum_var [,enum_var... ]] You can use the following syntax in MASM mode: name ENUM [enum_var [, enum_var. .. ]] The syntax of each enum_var is: var_name [=valuel Turbo Assembler will assign a value equal to that of the last variable in the list plus one if value isn't present when you assign the specified value to the variable var_name. Values can't be relative or forward referenced. Variables that ENUM created are redefinable numeric variables of global scope. Warning! If you use the same variable name in two enumerated data types, the first value of the variable will be lost, and errors could result. Chap ter 8, Defin i n9 data, types 95
  • 105. name is the name of the ENUM data type. You can use it later in the module to obtain a variety of information about the values assigned to the variables detailed. See Chapter 5 for information about using enumeration data type names in Turbo Assembler expressions. Note You can also use enumerated data type names to create variables and allocate memory: See Chapter 12 for details. Enumerated data types are redefinable. You can define the same name as an enumerated data type more than once in a module. Turbo Assembler provides a multiline syntax for enumerated data type definitions requiring a large number of variables. The symbol {starts the multiline definition, and the symbol} stops it. The Ideal mode syntax follows: ENUM name [enum_var [,enum_var . .. ]] [enum_var [, enum_var] ... ] [enum_var [, enum_var] ... ] You can use the following syntax in MASM mode: name ENUM [enum_var [,enum_var . .. ]] [enum_var [, enum_var] ... ] [enum_var [, enum_var] ... ] For example, all of the following enumerated data type definitions are equivalent: foo ENUM fl l f2 l f3 ,f4 foo ENUM { fl f2 f3 f4 } foo ENUM fl, f2, { f3 I f4} ;Original version ;Multiline version ;More compact multiline version Note Turbo Assembler doesn't recognize any pseudo ops inside the multiline enumerated . data type definition. Defining bit·field records A record data type represents a collection ofbit fields. Each bit field has a specific width (in bits) and an initial value. The record data type width is the sum of the widths of all the fields. . You can use record data types to compress data into a form that's as compact as possible. For example, you can represent a group of 16 flags (which can be either ON or 96 Turbo Ass embIer Use r's Guide
  • 106. OFF) as 16 individual bytes, 16individualwords, or as a record containing 16 1-bit fields (the efficient method). Here's the Ideal mode syntax for declaring a record data type: RECORD name [recjield [, recjield. .. JJ The MASM mode syntax is: name RECORD [rec_field [,rec_field .. .JJ Each reeJield has the following syntax: field_name: width_expression [~valueJ field_name is the name of a record field. Turbo Assembler will allocate a bit field of the width width_expression for it. value describes the initial value of the field (the default value used when an instance of the record is created). Values and width expressions can't be relative or forward referenced. Record field names are global in scope and can't be redefined. name is the name of the record data type. You can use it later in the module to obtain a variety of information about the record data type. You can also use the names of individual record fields to obtain information. See Chapter 5 for details about how to obtain information from record data type names and record field names using Turbo Assembler expressions. You can redefine record data types, and define the same name as a record data type more than once in a module. Note You can also use record data type names to create variables and allocate memory. See Chapter 12 for details. Turbo Assembler provides special support for record fields that represent flags and enumerated data types. Additional and extended instructions provide efficient access to record fields. Chapter 13 describes this concept further. For record data type definitions requiring a large number of fields, Turbo Assembler provides a multiline syntax similar to that for enumerated data types. For example, all of the following record data type definitions are equivalent: faa RECORD f1:1,f2:2,f3:3,f4:4 faa RECORD { f1: 1 f2:2 f3: 3 f4:4 } faa .RECORD £1:l,f2:2, { f3:3,f4:4} iOriginal version ;Multiline version ;More compact multiline version Note Turbo Assembler does not recognize any pseudo ops inside the multiline record data type definition. Chap t er 8, Defin i n9 datat ypes 97
  • 107. Defining structures and unions Structures and unions let you mix and match various types. A structure in Turbo Assembler is a data type that contains one or more data elements called members. Structures differ from records because structure members, are always an integral number of bytes, while records describe the breakdown of bit fields within bytes. The size 01 a structure is the combined size of all data elements within it. Unions are similar to structures, except that all of the members in a union occupythe same memory. The sizeof a unionis the size of its largest member. Unions are useful when a block of memory must represent one of several distinct possibilities, each with different data storage requirements. Turbo Assembler lets you fully nest structures and unions within one another, but this can become complicated. For example, you could have a structure member that is really a union. A union could also have a full structure as each member. Opening astructure or union definition Use the following Ideal mode syntaxes to open a structure or union dat~ type definition: STRUC name or UNION name You Cqn use the following MASM mode syntaxes to do the same thing: name STRUC or name UNION name is the name of the structure or union data type. Turbo Assembler considers all data or code emitted between the time a structure or union data type definition is opened and the time a corresponding ENDS directive is encountered to be part of that structure or union data type. Turbo Assembler treats structure and union data type names as global but redefinable. You can define the same name as a structure or union data type more than once in a module. Specifying structure and union members Turbo Assembler includes data one line at a time in structures or unions. To allocate data and create members in a structure or union definition, use the same directives as those for allocating data and creating labels in an open segment. For example, memberl DW 1 is equally valid in a segment or in a structure definition. In a segment, this statement means "reserve a word of value 1, whose name is memberl." Irl'a structureo! union definition, this statement means "reserve a word of initial value 1, whose member name is memberl." , You can use the initial value for a structure member if an instance of the structure or union is allocated in a segmentor a structure. If you don't intend to allocate structure 98 TurboA sse mbIer Use r's Guide
  • 108. instances this way, the initial value of the structure member is not important. You can use the data value? (the uninitialized data symbol) to indicate this. Turbo Assembler allows all methods of allocating data with a structure definition, including instances of other structures, unions, records, enumerated data types, tables, and objects. For more information on how to allocate data, see Chapter 12. ' MASM and Ideal modes treat structure member names differently. In MASM mode, structure member names are global and can't be redefined. In Ideal mode, structure member names are considered local toa structure or union data type. Defining structure member labels with LABEL The LABEL directive lets you create structure members without allocating data. Normally, the LABEL directive establishes a named label or marker at the point it's encountered in a segment. LABEL directives found inside structure definitions define members of the structure. Here's the syntax of the LAB,EL directive: LABEL name complex_type In MASM mode only, you can use the following syntax: name LABEL complex_type name is the name of the structure member. type is the desired type for the structure member. It can be any legal type name. See Chapter 5 for a description of the available type specifiers. Aligning structure members You can use the ALIGN directive within structure definitions to align structures members on appropriate boundaries. For example, iDWORD alignment ALIGN 4 member dd? imember will be DWORD aligned Closing astructure or union definition You must close the structure or union definition after you define all structure or union members. Use the ENDS directive to do this. ENDS has the following syntax in Ideal mode: ENDS [name] In MASM mode, you can use the following syntax: name ENDS name, ifpresent, is the name of the currently open structure or union data type definition..If name is not present, the currently open structure or union will be closed. You can also use the ENDS directive to close segments. This isnot a conflict, because you can't open a segment inside a structure or union definition. Chapter 8, Defining data types 99
  • 109. Nesting structures and unions Turbo Assembler lets you nest the STRUC, UNION, and ENDS directives inside open structure and union data type definitions to control the offsets assigned to structure members. . In a structure, each data element begins where the previous one ended. In a union, each data elementbegins at the same offset as the previous data element. Allowing a single data element to consist of an entire union or structure provides enormous flexibility and power. The following table contains descriptions of STRUC, UNION, and ENDS. Table 8.1 STRUC UNION ENDS STRUC, UNION, and ENDS directives Used inside an open structure or union, this directive begins a block of elements that the enclosing structure or union considers a single member. The members in the block are assigned offsets in ascending order. The size of the block is thesurn. of the sizes of all of the members in it. Used inside an open structure or union, this begins a block of members that the enclosing structure or union considers a single unit. The members in the block are all assigned the same offset. The size of the block is the-size of the largest member in it. Terminates a block of members started with a previous STRUC or UNION directive. For example, the composite has five members in the following structure/union data definition: CUNION STRUC CTYPE DB ? UNION iStart of union i If CTYPE=O I use this ... STRUC CTOPARl DW 1 CTOPAR2 DB 2 ENDS i If CTYPE=l, use this ... STRUC CTIPARl DB 3 CTIPAR2 DD 4 ENDS ENDS iEnd of union ENDS iEnd of structure data type The following table lists these members: 100· TurboA sse mbIe r Use r' s Guide
  • 110. Table 8.2 Block members CTYPE Byte 0 CTOPARl Word 1 CTOPAR2 Byte 3 CTIPARl Byte 1 CTIPAR2 Dword 2 The length of this structure/ union is 6 bytes. Including one named structure within another ? (uninitialized) 1 2 3 4 Turbo Assembler provides a way of incorporating an entire existing structure or union data type, including member names, into an open structure definition to assist in the inheritance of objects. It treats the incorporated structure or union as if it were nested inside the open structure or union definition at that point. In this way, incorporating a structure or union into another is intrinsically different from including an instance of a structure or union in another; an instance includes only initialized or uninitialized data, while incorporation includes data, structure, and member names. Here's the Ideal mode syntax: STRUC struc_name fill_parameters You can use the following syntax in MASM mode: struc_name STRUC fill_parameters Use a statement of this form only inside a structure or union definition. struc_name is the name of the previously defined structure or union that is to be included. fill-parameters represents the changes you want to make to the initial (default) values of the included structure's members. A ? keyword indicates that all of the incorporated structure's members should be considered uninitialized. Otherwise, the syntax for the fi1tparameters field is: { [member_name [=expression] [,member_name [=expression] ... ]] } member_name is the name of any member of the included structure whose initial value should be changed when it's included. expression is the value you want to change it to. If you have expression, then the initial value for that member of the structure will be unchanged when it is included. If you specify a ? keyword for the expression field, that member's initial value will be recorded as uninitialized when it's included. Since structure member names are global in MASM mode, they are not redefined when you copy a structure. Thus, including a structure within another is most useful in MASM mode when you do it at the beginn}ng of the structure or union you're defining. Usually, when you create an instance of a union, you would have to make sure that only one of the union's members contains initialized data. (See Chapter 12 for details.) Since incorporating a structure in another does not involve creating an instance, this restriction does not apply. More than one member of an included union can contain initialized data. For example, Chapter 8, Defining data types 101
  • 111. FOO STRUC ABC DW 1 DEF DW 2 UNION Al DB '123' A2 DW ? ENDS ENDS F002 STRUC FOO STRUC {Al=2} iIncorporates struc FOO into struc F002', with override iNote that both Al and A2 are initialized by idefault in F002! GHI DB 3 ENDS The definition of structure F002 in the previous example is equivalent to the following nested structure/union: F002 STRUC STRUC iBeginning of nested structure ... ABC DW 1 DEF DW 2 UNION iBeginning of doubly nested union ... Al DB '123' A2 DW 2 ENDS ENDS GHI DB 3 ENDS iEnd of doubly nested union ... iEnd of nested structure ... Note that when an instance of the structure F002 is made, be sure that only one value in the union is initialized. Using structure names in expressions Once you define a, structure or union, information about the structure or union is available in many ways. You can use both the structure or union data type name and a structure member name to obtain information using Turbo Assembler expressions. See Chapter 5 for further information. Defining tables A table data type represents a collection of table members. Each member has a specific size (in bytes) and an initial value. A table member can be either virtual or static. A virtual member of a table is assigned an offset within the table data type; space is reserved for it in any instance of the table. A static member does not have an offset; space isn't reserved for it in an instance ofthe table. The size of the table data type as a whole is the sum of the sizes of all of the virtual members. . 102 Turbo Assembler User's Guide
  • 112. Table data types represent method tables, used in object-oriented programming. An object usually has a number of methods associated with it, which are pointers to procedures that manipulate instances of the object. Method procedures can either be called directly (static methods) or indirectly, using a table of method procedure pointers (virtual methods). You can use the following Ideal mode syntax for declaring a table data type: TABLE name [table_member [,table_member.. .]] The following syntax works only in MASM mode: name TABLE [table_member [,table_member.. .]] Here's the syntax of each table_member field: or [VIRTUAL] member_name [[countl_expression]] [: complex_type [:count2_expression]] [= expression] table_name is the name of an existing table data type whose members are incorporated entirely in the table you define. Use this syntax wherever you want inheritance to occur. member_name is the name of the table member. The optional VIRTUAL keyword indicates that the member is virtual and should be assigned to a table offset. complex_type can be any legal complex type expression. See Chapter 5 for a detailed description of the valid type expressions. If you don't specify a complex_type field, Turbo Assembler assumes it to be WORD (DWORD is assumed if the currently selected model is a 32-bit model). count2_expression specifies how many items of this type the table member defines. A table member definition of faa TABLE VIRTUAL tmp:DWORD:4 defines a table member called tmp, consisting of four doublewords. The default value for count2_expression is 1 if you don't specify it. countl_expression is an array element size multiplier. The total space reserved for the member is count2_expression times the length specified by the memtype field, times countl_expression. The default value for countl_expression is also 1 if you don't specify one. countl:....expression multiplied by count2_expression specifies the total count of the table member. Note Table member names are local to a table in Ideal mode, but are global in scope in MASM mode. name is the name of the table data type. You can use it later in the module to get a variety of information about the table data type. You can also use the names of individual table members to obtain information. See Chapter 5 for further information. Table data types are redefinable. You can define the same name as a table data type more than once in a module. Chap ter 8, De fin i n9 datat ypes 103
  • 113. You can also use table data type names to create variables and allocate memory. See Chapter 12 for details. Alternatively, Turbo Assembler provides a multiline syntax for table data type definitions requiringa large number of members. This syntax is similar to that of enumerated data type definitions. Here's an example: foo TABLE tl:WORD,t2:WORD,t3:WORD,t4:WORD foo TABLE { iOriginal version iMultiline version t1 :WORD t2:WORD t3 :WORD t4:WORD } foo TABLE tl:WORD,t2:WORD,{ t3:WORD,t4:WORD} iMore compact multiline version Overriding table members If you declare two or more members of the same name as part of the same table data type, Turbo Assembler will check to be sure that their types and sizes agree. If they don't, it will generate an error. Turbo Assembler will use the last initial value occurring in the table definition for the member. In this way, you can override the initial value of a table after it is incorporated into another. For example, FOO TABLE VIRTUAL MEM1:WORD=MEM1PROC, VIRTUAL MEM2:WORD=MEM2PROC F002 TABLE FOO, VIRTUAL MEM1:WORD=MEM3PROC iOverrides inherited iMEMl Defining anamed type Named types represent simple or complex types. You can use the TYPEDEF directive to define named types. Here's the Ideal mode syntax: TYPEDEF type_name complex_type The MASM mode syntax is: type_name TYPEDEF complex_type complex_type describes any type or multiple levels of pointer indirection. See Chapter 5 for further information about complex types. type_name is the name of the specified type. . When you use a named type in an expression, it functions as if it were a simple type of the appropriate size. For example, MOV aX,wordptr [bx] foo TYPEDEF near ptr byte MOV ax,foo ptr [bx] 104 TurboA sse mbIer Use rJ s Guide i8imple statementi iFOO is basically a word iSO this works too
  • 114. Defining aprocedure type For Turbo Assembler version 3.2 or higher, you can use a user-defined data type (called a procedure type) to describe the arguments and calling conventions of a procedure. Turbo Assembler treats procedure types like any other types; you can use it wherever types are allowed. Note that since procedure types don't allocate data, you can't create an instance of a procedure type. Use the PROCTYPE directive to create a procedure type. Here is the Ideal mode syntax: PROCTYPE name [procedure_description] The MASM mode syntax is: name PROCTYPE [procedure_description] procedure_description is similar to the language and argument specification for the PROC directive. Its syntax is: [[language_modifier] language] [distance] [argument_list] specify language_modifier, language, and distance exactly the same way you would for the corresponding fields in the PROC directive. For more information about the PROC directive, see Chapter 10. Use the following form for argument_list: argument [,argument] ... An individual argument has the following syntax:' [argname] [[countl_expression]] :complex_type [:count2_expression] complex_type is the data type of the argument. It can be either a simple type or a pointer expression. Note See Chapter 5 for a discussion of the syntax of complex types. count2_expression specifies how many items of this type the argument defines. Its default value is 1, except for BYTE arguments. Those arguments have a default count of 2, since you can't PUSH a byte value onto the 80x86 stack. In procedure types whose calling convention permits variable-length arguments (like C), count2_expression (for the last argument) can be the special keyword ?, which indicates that the procedure caller will determine the size of the array. The type UNKNOWN also indicates a variable-length parameter. The name of each argument is optional, but complex_type is required because procedure types are used mainly for type checking purposes. The names of the arguments don't have to agree, but the types must. Defining an object ~~ An object consists of both a data structure and a list of methods that correspond to the CQ)~ object. Turbo Assembler uses a structure data type to represent the data structure Chap t er 8, 0 el i ni n9 datat ypes 105
  • 115. associated with an object, and a table data type to represent the list of methods associated with an object. . An extension to the STRUC directive lets you define objects. The Ideal mode syntax follows: STRUC name [modi fi ersl [parent_namel [METHOD [table_membeF [,·table_member. .·.lll structure_members. ENDS [namel You can use the following syntax in MASM mode: name STRUC [modifiersl [parent_name] [METHOD [table_member 1 [,table_member.. .ll] structure_members [name] ENDS' name is the name of the object. parent_name is the optional name of the parent object. (Turbo Assembler explicitly supports only single inheritance.) The parent object's structure data will automatically be included in the new object's structure data, and the parentobject's table of methods will be included in the new object's table of methods as well. Each table_member field describes a method name and method procedure associated with the object. The syntax of a table_member field is exactly the same as in a table definition. . structure_members describe any additional structure members you want within the object's data structure. These are formatted exactly the same as in an open structure definition. The optional modifiers field can be one or more of the following keywords: TableS.3 GLOBAL NEAR FAR Available modifiers Causes the address of the virtual method table (if any) to be published globally. Forces the virtual table pointer (if any) to be an offset quantity, either 16 or 32 bits, depending on whether the current model is USE16 or USE32. Forces the virtual table pointer (ifany) to be a segment and offset quantity, either 32 or 48 bits, depending on whether the current model is USE16 or USE32. . The size of the virtual table pointer (if any) depends on whether data in the current model is addressed as.NEAR or FAR if you don't specify a modifier. The TBLPTR directive Inherent in the idea of objects is the concept of the virtual method table. An instance of this table exists once for any object having virtual methods. The data structure for any object having virtual methods also must contain a pointer to the virtual method table for· that object. Turbo Assembler,automatically provides a virtual method table pointer in 106 Turbo Assembler User'.s Guide
  • 116. an object's data structure (if required) and if you don't specify it explicitly using the TBLPTR directive. You should use the TBLPTR directive within an object data structure definition. TBLPTR lets you explicitly locate the virtual table pointer wherever you want. Here's its syntax: TBLPTR The size of the pointer that TBLPTR reserves is determined by whether the current model is USE16 or USE32, and what modifiers you used in the object definition. Symbols defined by the extended STRUC directive The extended STRUC directive defines or uses several symbols, which reflect the object being defined. The following table shows these symbols. Table 8.4 Symbols used or defined by STRUC Syilil101,····'·.·M¢c#rln8.·· OObject @Table_<object_name> @Tableaddr_<object_name> A text macro containing the name of the current object A table data type containing the object's method table A label describing the address of the object's virtual method table Chap t er 8, De fin i n9 datat y pes 107
  • 117. 108 Turbo Assembler User's Guide
  • 118. Setting and using the location counter The location counter keeps track of the current address as your source files assemble. This lets you know where you are at any time during assembly of your program. Turbo Assembler supplies directives that let you manipulate the location counter to move it to a desired address. Labels are the names used for referring to addresses within a program. Labels are assigned the value of the location counter at the time they are defined. Labels let you give names to memory variables and the locations of particular instructions. This chapter discusses the available directives for manipulating the location counter, and declaring labels at the current location counter. The $location counter symbol The predefined symbol $ represents the current location counter. The location counter consists of two parts: a segment, and an offset. The location counter is the current offset within the current segment during ~ssembly. The location counter is an address that is incremented to reflect the current address as each statement in the source file is assembled. As an example, helpMessage helpLength DB 'This is help for the program.' = $ - helpMessage Once these two lines are assembled, the symbol helpLength will equal the length of the help message. Chap t er 9, Sett in9 and us in 9 the 10 cat ion co un ter 109
  • 119. Location counter directives Turbo Assembler provides several directives for setting the location counter. The next few sections describe these directives. Note that all of these directives work in both MASM and Ideal modes. The ORG directive You can use the ORG directive to set the location counter in the current segment. ORG has the following syntax: ORG expression expression can't contain any forward-referenced symbol names. It can either be a constant or an offset from a symbol inthe current segment or from the current location counter. You can back up the location counter before data or code that has already been emitted into a segment. You can use this to go back and fill in table entries whose values weren't known at the time the table was defined. Be careful when using this technique; you mightaccidentally overwrite something you didn't intend to. You can use the ORG directive to connect a label with a specific absolute address. The ORG directive can also set the starting location for .COM files. Here's an example of how to use ORG: This program shows how to create a structure and macro for declaring instances of the structure, that allows additional elements to be added to the linked list without regard to other structures already declared in the list. If the macro is invoked in a section of code that is between two other instances of the structure, the new structure will automatically be inserted in the linked list at that point without your needing to know the names of the previous or next structure variables. Similarly, using the macro at the end of the program easily adds new structures to the linked list without regard for the name of the previous element. The ma~ro also maintains variables that point to the first and last elements of the linked list. ideal p386 model OS_NT flat codeseg struc a· prev dd 0 next dd 0 info db 100 dup (0) ends a last a name equ <> 110 TurboA sse mbIer Use r's Guide
  • 120. i Maintain the offsets of the head and tail of the list. list_a_head dd 0 list a tail dd 0 macro makea name: req, args ifidni __last_a_name,<> i There is no, previous item of this type. name a <O,O,args> Setup the head and tail pointers org list a head dd name org list a tail dd name i Return to the offset after the structure element org nametsize a last a name equ name else name a Declare it, with previous pointing to previous item of structure a. <__last_a_name,O,args> i Make the next pointer of the previous structure i point to this structure. org last a name.next dd name i Setup the tail pointer for the new member org list a tail dd name i Go back to location after the current structure org nametsize a i Set up an equate to remember the name of the ; structure just declared last a name endif equ name endm makea first ; Miscellaneous other data db 5 dup (0) makea second ; More miscellaneous, data db 56 dup (0) Give a string to put in the info element of this structure makea third,<'Hello'> end Chap t er 9, Set tin 9 and us i n9 the 10 cat ion co un ter 111
  • 121. The EVEN and EVENDATA directives You can use the EVEN directive to round up the location counter to the next even address. EVEN lets you align code for efficient access by processors that use a 16-bit data bus. It does not improve performance for processors that have an 8-bit data bus. EVENDATA aligns evenly by advancing the location counter without emitting data, which is useful for uninitialized segments. Both EVEN and EVENDATA will cause Turbo Assembler to generate a warning message if the current segment's alignment isn't strict enough. If the location counter is odd when an EVEN directive appears, Turbo Assembler places a single byte of a NOP instruction in the segment to make the location counter even. By padding with a NOP, EVEN can be used in code segments without causing erroneous instructions to be executed at run time. This directive has no effect if the location is already even. Note In code segments, NOPs are emitted. :rndata segments, zeros are emitted. Similarly, if the location counter is odd when an EVENDATA directive appears, Turbo Assembler emits an uninitialized byte. An example of using the EVEN directive follows: EVEN @@A: lodsb xor bl,al ialign for efficient access loop @@A Here's an example of using the EVENDATA directive: EVENDATA VARl DW ialign for efficient 8086 access The ALIGN directive You'll use the ALIGN directive to round up the location counter to a power-of-two address. ALIGN has the following syntax: ALIGN boundary boundary must be a power of two. Turbo Assembler inserts NOP instructions into the segment to bring the location counter up to the desired address if the location counter is not already at 'an offset that is a multiple of boundary. This directive has no effed if the location counter is already at a multiple of boundary. You can't reliably align to a boundary that's more strict than the segment alignment in which ALIGN appears. The segment's alignment is specified when the segment is first started with the SEGMENT directive. For example, if you've defined a segment with CODE SEGMENT PARA PUBLIC 112 T urboA sse mbIer Use r's Guide
  • 122. you can then say ALIGN 16 (same as PARA) but not ALIGN 32, since that's more strict than the alignment that PARA indicated in the SEGMENT directive. ALIGN generates a warning if the segment alignment isn't strict enough. The following example shows how you can use the ALIGN directive: ALIGN 4 ia1ign to DWORD boundary for 386 BigNum DD 12345678 Defining labels Labels let you assign values to symbols. There are three ways of defining labels: • using the: operator • using the LABEL directive • using the :: operator (from MASM 5.1) The :operator The : operator defines a near code label, and has the syntax name: where name is a symbol that you haven't previously defined in the source file. You can place a near code label on a line by itself or at the start of a line before an instruction. You usually would use a near code label as the destination of a JMP.or CALL instruction from within the same segment. The code label will only be accessible from within the current source file unless you use the PUBLIC directive to make it accessible from other source files. This directive functions the same as using the LABEL directive to define a NEAR label; for example, A: is the same as A LABEL NEAR. For example,' A: is the same as A LABEL NEAR Here's an example of using the: operator. jne A iskip following function inc si A: ijne goes here The LABEL directive You'll use the LABEL directive to denne a symbol with a specified type. Note that the syntax is different for Ideal and MASM modes. In Ideal mode, specify LABEL name complex_type In MASM mode, use the following: Chap ter 9, Set tin 9 and usin 9 the I0 cat ion C 0 unter 113
  • 123. name LABEL complex_type name is a symbqlthat you haven'tpreviously defined in the source file. complex_type describes the size of the symbol and whether it refers to code or data. See Chapter 5 for f4rther information about complex types. The label is only accessible from within the current source file, unless you use PUBLIC to make it accessible from other source files. You can use LABEL to access different-sized items than those in the data structure; the following example illustrates this concept. WORDS LABEL WORD BYTES DB 64 DUP (0) mov WORDS [2] ,1 The :: directive iaccess "BYTES" as WORDS iwrite WORD of 1 The:: directive lets you define labels with a scope beyond the procedure you're in. This differs from the: directive in that labels defined with: have a scope of only within the current procedure. Note that:: is different from :.only when you specify a language in the .MODEL statement. Note The :: directive only works when you're using MASM51. 114 Turbo Assembler User-'s Guide
  • 124. Declaring procedures Turbo Assembler lets you declare procedures in many ways. This chapter discusses NEAR and FAR procedures, declaring procedure languages using arguments and variables in procedures, preserving registers, nesting procedures, declaring method procedures for objects, and declaring procedure prototypes. You can find more information about how to call language procedures in Chapter 13. Procedure definition syntax You can use the PROC directive to declare procedures. Here's its Ideal mode syntax: PROC name [[ laI?guage modifier] language] [distance] [ARG argument_list] [RETURNS item_list] [LOCAL argument_list] [USES item_list] ENDP [name] Use the following syntax in MASM mode: name PROC [[language modifier] language] [distance] [ARG.argument_list] [RETURNS item_list] [LOCAL argument_list] [USES item_list] [name] ENDP Turbo Assembler also accepts MASM syntax for defining procedures. For more information on MASM syntax, see Chapter 3. If you're using Turbo Assembler version T310 or earlier, use the following Ideal mode syntax: Chap t er 10, 0 eel ari n9 pro cedures 115
  • 125. PROC [[language modifier] language] name [distance] [ARG argument_list] [RETURNS item_list] [LOCAL argument_list] , [USES item_list] ENDP Note Unless you specifyversion T310 or earlier, the Ideal mode syntax is no longer allowed in MASMmode. ) Note that the only difference between the older versions of Turbo Assembler and the later versions is that language and language_modifier have been moved to follow the' procedure name to facilitate consistent function prototyping. Declaring NEAR or FAR procedures NEAR procedures are called with a near call, and contain a near return; you must call them only from within the same segment in which they're defined. A near call pushes the return address onto,the stack, and sets the instruction pointer (IP) to the offset of the procedure. Since the code segment(CS) is not changed, the procedure must be in the same segment as the caller. When the processor encounters a near return, it pops the return address from the stack and sets IP to it; again, the code segment is not changed. FAR procedures are called with a far call and contain far returns. You can call FAR procedures from outside the segment in which they're defined. A far call pushes the return address onto the stack as a segment and offset, and then sets CS:IP to the address of the procedure. When the processor encounters a far return, it pops the segment and offset of the return address from the stack and sets CS:IP to it. The currently selected model determines the default distance of a procedure. For tiny, small, and compact models, the default procedure distance is NEAR. For all other models, FAR is the default. If you don't use the simplified segmentation directives, the default procedure distance is always N13AR. Note that you can specify NEAR or FAR as an argumentto the MODEL statement. See Chapter 7 for more information. You can override the default distance of a procedure by specifying the desired'distance in the procedure definition. To do this, use the NEAR or FAR keywords. These , keywords override the default procedure distance, but only for the current procedure. For example, MODEL TINY ;default distance near ;testl is a far procedure testl PROC FAR ;body of procedure RET . ;this will be a far return ENDP 116 Turbo Assembler User's Guide
  • 126. itest2 is by default a near procedure test2 PROC ibody of procedure RET ithis will be a near return ENDP The same RET mnemonic is used inboth NEAR and FAR procedures; Turbo Assembler uses the distance of the procedure to determine whether a near or far return is required. Similarly, Turbo Assembler uses the procedure distance to determine whether a near or far call is required to reference the procedure: CALL testlithis is a far call CALL test2ithis is a near call When you make a call to a forward referenced procedure, Turbo Assembler might have to make multiple passes to determine the distance of the procedure. For example, testl PROC NEAR MOVax,lO CALL test2 RET testl ENDP test2 PROC FAR ADD ax,ax RET test2 ENDP When Turbo Assembler reaches the "call test2" instruction during thefirst pass, it has not yet encountered test2, and therefore doesn't know the distance. It assumes a distance of NEAR, and presumes it can use a near call. When it discovers that test2 is in fact a FAR procedure, Turbo Assembler determines that it needs a second pass to correctly generate the call. If you enable multiple passes (with the 1m command-line switch), a second pass will be made. Ifyou don't enable multiple passes, Turbo Assembler will report a "forward reference needs override" error. You can specify the distance of forward referenced procedures as NEAR PTR or FAR PTR in the call to avoid this situation (and to reduce the number of passes). testl PROC NEAR MOVax,lO CALL FAR PTR test2 RET testl ENDP The previous example tells Turbo Assembler to use a far call, so that multiple assembler passes aren't necessary. Chap ter 10, Dec Iarin 9 pro cedures 117
  • 127. Declaring aprocedure language You can easily define procedures that use high-level language interfacing conventions in Turbo Assembler. Interfacing conventions are supported for the NOLANGUAGE (Assembler), BASIC, FORTRAN, PROLOG, C, CPP (C++), SYSCALL, STDCALL, and PASCAL languages. Turbo Assembler does all the work of generating the correct prolog (procedure entry) and epilog (procedure exit) code necessary to adhere to the specified language convention. You can specify a default language as a parameter of the MODEL directive. See Chapter 7 for further details. Ifa default language is present, all procedures that don't otherwise specify a language use the conventions of the default language. To override the default language for an individual procedure, include the language name in the procedure definition. You can specify a procedure language by including a language identifier keyword in its declaration. For example, a definition in MASM modeforaPASCAL procedure would be pascalproc PROC PASCAL FAR iprocedure body pascalproc ENDP Turbo Assembler uses the language of the procedure to determine what prolog and epilog code is automatically included in the procedure's body. The prolog code sets up the stack frame for passed argUments and local variables in the procedure; the epilog code restores the stack frame before returning from the procedure. Turbo Assembler automatically inserts prolog code into the procedure before the first instruction of the procedure, or before the first "label:" tag. Prolog code does the following: • Saves the current BP on the stack. • Sets BP to the current stack pointer. • Adjusts the stack pointer to allocate local variables. • Saves the registers specified by USES on the stack. Turbo Assembler automatically inserts epilog code irlto the procedure at each RET instruction in the procedure (1£ there are multiple RETs, the epilog code will be inserted multiple times). Turbo Assembler also inserts epilog code before any object-oriented method jump (see Chapter 4). Epilog code reverses the effects of prolog code in the following ways: • Pops the registers specified by USES off the stack. • Adjusts the stack pointer to discard local arguments. • Pops the stored BP off the stack. • Adjusts the stack to discard passed arguments (if the language requires it) and returns. 118 Turbo Assembler User's G.uide
  • 128. The last part of the epilog code, discarding passed arguments, is performed only for those languages requiring the procedure to discard arguments (for example, BASIC, FORTRAN, PASCAL). The convention for other languages (C, C++, PROLOG) is to leave the arguments on the stack and let the caller discard them. SYSCALL behaves like C++. For the STDCALL language specification, C++ calling conventions are used if the procedure has variable arguments. Otherwise, PASCAL calling conventions are used. Note Turbo Assembler always implements the prolog and epilog code using the most , efficient instructions for the language and the current processor selected. Turbo Assembler doesn't generate prolog or epilog code for NOLANGUAGE procedures. If such procedures expect arguments on the stack, you must specifically include the prolog and epilog code yourself. In general, the language of a procedure affects the procedure in the manner shown in the following figure. Figure 10.1 How language affects procedures Language: None Basic Fortran Pascal C CPP Prolog Argument L-R L-R L-R L-R R-L R-L R-L ordering (Ieft-to-right, right-to-Ieft) Who cleans PROC PROC PROC PROC CALLER CALLER CALLER up stack (caller, procedure) You can use the /la command-line switch to include procedure prolog and epilog code in your listing file. This lets you see the differences between the languages. See Chapter 13 for further information. Specifying alanguage modifier Language modifiers tell Turbo Assembler to include special prolog and epilog code in procedures that interface with Windows or the VROOM overlay manager. To use them, specify one before the procedure language in the model directive, or in the procedure header. Valid modifiers are NORMAL, WINDOWS, ODDNEAR, and ODDFAR. Additionally, you can specify a default language modifier as a parameter of the MODEL directive. If a default language modifier exists, all procedures that don't otherwise specify a language modifier will use the conventions of the default. See Chapter 7 for more information. Include the modifier in the procedure definition to specify the language modifier for an individual procedure. Forexample, sample PROC WINDOWS PASCAL FAR iprocedure body ENDP Chapter 10, Declaring procedures 119
  • 129. Note If you don't specify a language modifier, Turbo Assembler uses the language modifier specified in the MODEL statement. Turbo Assembler will generate the standard prolog or epilog code for the procedure if there isn't a MODEL statement, or if NORMAL is specified. If you've selected the WINDOWSlanguage modifier, Turbo Assembler generates prolog and epilog code that let~ you call the procedure from Windows. Turbo Assembler generates special prolog and epilog code only for FAR Windows procedures. You can't call NEAR proc~dures from Windows, so they don't need special prolog or epilog code. Procedures called by Windows typically use PASCAL calling conventions. For example, winproc PROC'WINDOWS PASCAL FAR ARG @@hwnd:WORD,@@mess:WORD,@@wparam:WORD,@@lparam:DWORD ibody of procedure ENDP Note Refer to your Windows documentation for more information on Windows procedures. The ODDNEAR and ODDFAR language modifiers are used in connection with the VROOM overlay manager. VROOM has'two modes of operation: oddnear and oddfar. You can use the Ila switch option on the Turbo Assembler command line to see the prolog and epilog code that these language modifiers produce. Defining arguments and local variables Turbo Assembler passes arguments to higher-level language procedures in stack frames by pushing the arguments onto the stack before the procedure is called. A language procedure reads the arguments off the stack when it needs them. When the procedure returns, it either removes the arguments from the stack at that point (the Pascal calling convention),or relies on the caller to remove the arguments (the C calling convention). The ARG directive specifies, in the procedure declaration, the stack frame arguments passed to procedures. Arguments are defined internally as positive offsets from the BP or EBP registers. The procedure's language convention determines whether or not the arguments will be assigned in reverse order on the,Stack. You should always list arguments in the ARG statement in the same order they would appear in a high-level declaration of the procedure." The LOCAL directive specifies, in the procedure declaration, the stack frame variables local to procedures. Arguments are defined internally as negative offsets from the BP or EBP register. Allocate space for local stackframe variables on the stack frame by including procedure prolog code, which adjusts the stack pointer downward by the ~mount of space required. The procedure's epilog code must discard this extra space by restoring the stack pointer. (Turbo Assembler automatically generates this prolog code when the procedure obeys any language convention other than NOLANGUAGE.) 120 TurboA sse mbIer Use r 's Guide
  • 130. Remember that Turbo Assembler assumes that any procedure using stack frame arguments will include proper prolog code in it to set up the BP or EBP register. (Turbo Assembler automatically generates prolog code when the procedure obeys any language convention other than NOLANGUAGE). Define arguments and local variables with the ARG and LOCAL directives even if the language interfacing convention for the procedure is NOLANGUAGE. No prolog or epilog code will automatically be generated, however, in this case. ARG and LOCAL syntax Here's the syntax for defining the arguments passed to the procedure: ARG argument [,argument] ... [=symbol] [RETURNS argument [,argument]] To define the local variables for the procedure, use the following: LOCAL argument [,argument] ... [=symbol] An individual argument has the following syntax: argname [[countl_expression]] [: complex_type [:count2_expression]] complex_type is the data type of the argument. It canbe either a simple type, or a complex pointer expression. See Chapter 5 for more information about the syntax of complex types. Ifyou don't specify a complex_type field, Turbo Assembler assumes WORD. It assumes DWORD if the selected model is a 32-bit model. count2_expression specifies how many items of this type the argument defines. An argument definition of ARG tmp:DWORD:4 defines an argument called tmp, consisting of 4 double words. The default value for count2_expression is 1, except for arguments of type BYTE. Since you can't push a byte value, BYTE arguments have a default count of 2 to make them word-sized on the stack. This corresponds with the way high-level languages treat character variables passed as parameters. If you really want to specify an argument as a ~ingle byte on the stack, you must explicitly supply a count2_expression field of 1, such as ARG realbyte:BYTE:l countl_expression is an array element size multiplier. The total space reserved for the argument on the stack is count2_expression times the length specified by the argtype field, times countl_expression. The default value for countl_expression is 1 if it is not specified. countl_expression times count2_expression specifies the total count of the argument. For Turbo Assembler versions 3.2 or later, you can specify count2_expression using the? keyword to indicate that a variable number of arguments are passed to the procedure. For example, an argument definition of ARG tmp:WORD:? defines an argument called tmp, consisting of a variable number of words. Chap ter 10, 0 eel ari n9 pro cedures 121
  • 131. ? must be used as the last item in the argument list. Also, you can use? only in procedures that support variable-length arguments (such as procedures that use the C calling conventions). If you end the argument list with an equal sign' (=) and a symbol, Turbo Assembler will equate that symbol to the totalsize of the ·argument block in bytes. If you are not using Turbo Assembler's automatic handling of high level language interfacing conventions, you can use this value at the end of the procedure as an argument to the RET instruction. Notice that this causes a stack cleanup of any pushed arguments before returning (this is the Pascal calling convention). The arguments and variables are defined within the procedure as BP-relative memory operands. Passed arguments defined with ARG are positive offset from BP; local variables defined with LOCAL are negative offset from BP. For example, func1 PROC NEAR ARG a:WORD,b:DWORD:4,c:BYTE=d LOCAL x:DWORD,y:WORD:2=z defines aas [bp+4], b as [bp+6], cas [bp+14], and d as 20; x is [bp-2], y is [bp-6], and zis 8. The scope of ARG and LOCAL variable names . All argument names specified in the procedure header, whether ARGs (passed arguments), RETURNs (return arguments), or LOCALs (local variables), are global in scope unless you give them names prepended with the local symbol prefix. The LOCALS directive enables locally 'scoped symbols. For example, LOCALS testl PROC PASCAL FAR ARG @@a:WORD,@@b:DWORD,@@c:BYTE LOCAL @@x:WORD,@@y:DWORD MOVax,@@a MOV @@x,ax LES di,@@b MOV WORD ptr @@y,di MOV'WORD ptr @@y+2,es MOV @@c, 'a' RET ENDP test2 PROC PASCAL FAR ARG @@a:DWORD,@@b:BYTE LOCAL @@x:WORD LES di,@@a MOV ax, es: [di1 MOV @@x,ax CMP al,@@b 122 TurboA sse mbIer Use r's Guide
  • 132. jz @@dn MOV @@x,o @@dn: MOV ax,@@x RET ENDP Since this example uses locally scoped variables, the names exist only within the body of the procedure. Thus, test2can reuse the argument names @@a, @@b, and @@x. See Chapter 11 for more information about controlling the scope of symbols. Preserving registers Most higher-level languages require that called procedures preserve certain registers. You can do this by pushing them at the start of the procedure, and popping them again at the end of it. Turbo Assembler can automatically generate code to save and restore these registers as part of a procedure's prolog and epilog code. You can specify these registers with the USES statement. Here's its syntax: USES item [,item] '" item can be any register.or single-token data item that can legally be pushed or popped. There is a limit of eight items per procedure. For example, myproc PROC PASCAL NEAR ARG @@source:DWORD,@@dest:DWORD,@@count:WORD USES cx,si,di,foo MOV cX,@@count MOV foo,@@count LES di,@@dest LDS si,@@source REP MbvSB RE ENDP See Chapter 18 for information about what registers are normally preserved. USES is only available when used with procedures that have a language interfacing convention other than NOLANGUAGE. Defining procedures using procedure types You can use a procedure type (defined with PROCTYPE) as a template for the procedure declaration itself. For example, Chap t er 10, 0 eel ari h9 pro cedures 123
  • 133. footype PROCTYPE pascal near :word, :dword,:word foo PROC footype arg al:word,a2:dword,a3:word ipascal near procedure ian error would occur if iarguments did not match ithose of footype When you declare a procedure using a named procedure description, the number and . types of the.arguments declared for PROC are checked against those declared by PROCTYPE. The procedure description supplies the language and distance of the procedure declaration. Nested procedures and scope rules All procedures have global scope, even if you nest them within another procedure. For example, testl PROC FAR isome code here CALL test2 isome more code here RET test2 PROC NEAR isome code here RET inear return test2 ENDP testl ENDP In this example, it's legal to call testl or test2 from outside the outer procedure. If you want to have localized subprocedures, use a locally scoped name. For example, LOCALS testl PROC FAR RET @@test2 PROC NEAR RET @@test2 ENDP testl ENDP isome code here isome code here . In this case, you can only access the procedure @@test2 from within the procedure testl. In fact, there can be multiple procedures named@@test2 as Jong as no two are within the same procedure. For example, the following is legal: LOCALS testl PROC FAR MOV si,OFFSET Buffer CALL @@test2 124 TurboA sse mbIer Use r' s Guide
  • 134. RET @@test2 PROC NEAR RET @@test2 ENDP testl ENDP test2 PROC FAR isorne code here MOV si,OFFSET Buffer2 CALL @@test2 RET @@test2 PROC NEAR RET @@test2 ENDP test2 ENDP isorne code here The following code is not legal: LOCALS testl PROC FAR MOV si,OFFSET Buffer CALL@@test2 RET testl ENDP @@test2 PROC NEAR isorne code here RET @@test2 ENDP since the CALL to @@test2 specifies a symbol local to the procedure testl, and no such symbol exists. Note The LOCALS directive enables locally scoped symbols. See Chapter 11 for further information. Declaring method procedures for objects Some special considerations apply when you create method procedures for objects. Object method procedures must be able to access the object that they are operating on, and thus require a pointer to that object as a parameter to the procedure. Turbo Assembler's treatment of objects is flexible enough to allow a wide range of conventions for p~ssing arguments to method procedures. The conventions are , constrained only by the need to interface with objects created by a high-level language. If you are writing a native assembly-language object method procedure, you might want to use register argument passing conventions. In this case, you should write a method procedure to expect a pointer to the object in a register or register pair (such as ES:DI). Chap t er 10, De cIari n9 pro cedures 125
  • 135. If you are writing a method procedure that uses high-level language interfacing conventions, your procedure should expect the object pointer to be one of the arguments passed to the procedure. The object pointer passed from high-level OOP languages like C++ is an implicit argument usually placed at the start of the list of arguments. A method procedure written in assembly language must include the object pointer explicitly in its list of arguments, or unexpected results will occur. Remember . that the object pointer can be either a WORD or DWORD quantity, depending on whether the object is NEAR or FAR. Other complexities arise when you write constructor or destructor procedures in assembly language. C++ uses other implicit arguments (under some circumstances) to indicate to the constructor or destructor that certain actions must be taken. Constructors written for an application using native assembly language do not necessarily need a pointer to the object passed to them. If an object is never statically allocated, the object's constructor will always allocate the object from the heap. Note You can find information about the calling conventions of Borland C++ in Chapter 18. Using procedure prototypes For versions 3.2 and later, Turbo Assembler lets you declare procedure prototypes much like procedure prototypes in C. To do so, use thePROCDESC directive. The Ideal mode syntax of PROCDESC is: PROCDESC name [procedure_description] Use the following syntax in MASM mode: ( name PROCDESC [procedure_description] procedure_description is similar to the language and argument specification used in the PROC directive. Its syntax is: [[language_modifier] language] [distance] [argument_list] language_modifier, language, and distance have the same syntax as in the PROC directive. argument_list has the form: argument [,argument] ... For more information about PROC, see the beginning of this chapter. An individual argument has the following syntax: [argname] [[countl_expression]] :complex_type [:count2_expression] complex_type is the data type of the argument, and can be either a simple type or a pointer expression. count2_expression specifies how many items of this type the argument defines. The default value of count2_expression is 1,except for arguments of BYTE, which have a default count of2 (since you can't PUSH a byte value onto the . 80x86 stack). See Chapter 5 for further information about the syntax of complex types. 126 TurboA sse mbIer Use r's Guide
  • 136. For the last argument, in procedure types whose calling convention allows variable- length arguments (like C), count2_expression can be?, to indicate that the procedure caller will determine the size of the array. Note that the name of each argument (argname) is optional, but complex_type is required for each argument because procedure types are used mainly for type checking purposes. The names of the arguments do not have to agree, but the types must. Here's an example: test PROCDESC pascal near a:word,b:dword,c:word This example defines a prototype for the procedure test as a PASCAL procedure taking three arguments (WORD, DWORD, WORD). Argument names are ignored, and you can omit them in the PROCDESC directive, as follows: test PROCDESC pascal near :word, :dword, :word The procedure prototype is used to check calls to the procedure, and to check the PROC declaration against the language, number of arguments, and argument types in the prototype. For example, test PROC pascal near ARG al:word,a2:dword,a3:word imatches PROCDESC for test PROCDESC also globally publishes the name of the procedure. Procedures that are not defined in a module are published as externals, while procedures that are defined are published as public. Be sure that PROCDESC precedes the PROC declaration, and any use of the procedure name. Procedure prototypes can also use procedure types (defined with PROCTYPE). For example, footype PROCTYPE pascal near :word, :dword, :word foo PROCDESC footype Chap t er 10, Dec Iari n9 pro Gedures 127
  • 137. 128 TurboA sse mbIer Use r's Guide
  • 138. Controlling the scope of symbols In Turbo Assembler and most otherprogramming languages, a symbol can have more than one meaning depending on where it's located in a module. For example, some symbols have the same meaning across a whole module, while others are defined only within a specific procedure. Symbol scope refers to the range of lines over which a symbol has a specific meaning. Proper scoping of symbols is very important for modular program development. By controlling the scope of a symbol, you can control its use. Also, properly selecting the scope of a symbol can eliminate problems thatoccur when you try to define more than one symbol of the same name. Redefinable symbols Some symbol types that Turbo Assembler supports are considered redefinable. This means that you can redefine a symbol of this type to be another symbol of the same type at any point in the module. For example, numeric symbols have this property: foo· = 1 movax/foo iMoves 1 into AX. faa = 2 movax/foo iMoves 2 into AX. Generally, the scope of a given redefinable symbol starts at the point of its definition, and proceeds to the point where it's redefined. The scope of the last definition of the symbol is extended to include the beginning of the module up through the first definition of the symbol. For example, movax/foo ;Moves 2 into AX! foG = 1 movax/foo ;Moves 1 into AX. foo = 2 ;This definition is carried around to the start ;of the module ... movax/foo ;Moves 2 into AX. Chap ter 11 , Co nt roll i n9 the scop e of symb0 Is 129
  • 139. The following list contains the redefinable symbol types. • text_macro • numerical_expr • multiline_macro • struc/union • table • record • enum See Chapter 5 for mbre information about these redefinable symbols. Block seoping Block scoping makes a symbol have a scope that corresponds to a procedure in a module. Turbo Assembler supports two varieties of block scoping: MASM-style, and native Turbo Assembler style. By default, block-scoped symbols are disabled in Turbo Assembler. The LOCALS and NOLOCALS directives Turbo Asse~bler uses a two-character code prepended to symbols, which determines whether a symbol in a procedure has block scope. This local-symbol prefix is denoted with "@@.'~ You can use the LOCALS directive to both enable block-scoped symbols, and to set the local symbol prefix. Its syntax looks like this: LOCALS [prefix_symbol] The optional prefix_symbol field contains the symbol (of two character length) that Turbo Assembler will use as the local-symbol prefix. For example, LOCALS i@@ is assu;ed to be the prefix by default. foo proc @@a: jmp @@a i This @@a symbol belongs to procedure FOO., foo endp bar proc @@a: jmp @@a iThis @@a symbol belongs to procedure BAR. bar endp Ifyou want to disable block-scoped symbols, you can use the NOLOCALS directive. Its syntax follows: NOLOCALS Note that you can also use block-scoped symbols outside procedures. In this case, the scope of a symbol is determined by the labels defined with the colon directive (:),which are not block-scoped symbqls. For example, foo: @@a: @@b =1 iStart of scope. iBelongs to scope starting at FOO: iBelongs to scope starting at FOO: 130 Tu rboA sse mbIer Use r' s Guide
  • 140. ;Start of scope. bar: @@a = 2 ;Belongs to scope starting at BAR: MASM block seoping In MASM versions 5.1 and 5.2, NEAR labels defined with the colon directive (:) are considered block-scoped if they are located inside a procedure, and you've selected a language interfacing convention with the MODEL statement. However, these symbols are not truly block-scoped; they can't be defined as anything other than a near label elsewhere in the program. For example, version m510 model small, c ' codeseg foo proc a: jmp a ;Belongs to procedure FOO foo endp bar proc a: jmp a ;Belongs to procedure BAR bar endp a = l;Illegal! MASM-style local labels MASM 5.1 and 5.2 provide special symbols that you can use to control the scope of near 'labels within a small range of lines. These symbols are: @@, @F, and @B. When you declare @@ as a NEAR label using the colon (:) directive, you're defining a unique symbol of the form @@xxxx (where xxxx is a hexadecimal number). @B refers to the last symbol defined in this way. @F refers to the next symbol with this kind of definition. For example, version m510 @@: jmp @B jmp @F @@: jmp @B jmp @F ;Goes to the previous @@. ;Goes to the next @@. ;Goes to the previous @@. ;Error: no next @@. Chapter 11, Controlling the scope of symbols 131
  • 141. 132 TurboA sse mbIer Use r 's Guide
  • 142. Allocating data Data allocation directives are used for allocating bytes in a segment. You can also use them for filling those bytes with initial data, and for defining data variables. All data allocation directives have some features in common. First, they can generate initialized data and set aside room for uninitialized data. Initialized data is defined with some initial value; uninitialized data is defined without specifying an initial value (its initial value is said to be indeterminate). Data allocation directives indicate an uninitialized dataNalue with a ? Anything else should represent an initialized data value. Chapter 7 explaiJ;ts why you should distinguish between initialized and uninitialized data. Another feature common to all data allocation directives is the use of the DUP keyword to indicate a repeated block of data. Here's the general syntax of all data allocation directives: [name] directive dup_expr [,dup_expr ... ] Turbo Assembler initializes name to point to the space that the directive reserves. The variable will have a type depending on the actual directive used. The syntax of each dup_expr can be one of the following: • ? • value • count_expression DUP ( dup_expr [, dup_expr. .. J ) count_expression represents the number of times the data block will be repeated. , count_expression cannot be relative or forward referenced. Use the ? symbol if you want uninitialized data. The amount of space reserved for the uninitialized data depends on the actual directive used. value stands for the particular description of an individual data element that is appropriate for each directive. For some directives, the value field can be very complex and contain many components; others may only require a simple expression. The following example uses the DW directive, which allocates WORDS: Chap t er 12, AII 0 cat i n9 dat a 133
  • 143. DW 2 DUP (3 DUP (1,3),5) i8ame as DW 1,3,1,3,1,3,5,1,3,1,3,1,3,5 Simple data directives You can define data with the DB, OW, DO, OQ, OF, OP, or OT directives. These directives define different sizes of simple data, as shown in the following table. Table 12.1 Data size directives DB Define byte-siZe data. DW Define ward-size data: DD Define daubleward-size data. DQ Define quadward-size data. DF Define 48-bit 80386 far-painter-size (6 byte) data. DP Define 48-bit 80386 far-painter-size (6 byte) data. DT Define tenbyte (lO-byte) size data. Note Data is always stored in memory low value before high value. The syntax of the value field for each of these directive differs, based on the capability of each data size to represent certain quantities. (For example, it's never appropriate to interpret byte data as a floating-point number.) DB (byte) values can be • A constant expression that has a value between -128 and 255 (signed bytes range from -128 to +127; unsigned byte values are from 0 to 255). . • An 8-bit relative expression using the HIGH or LOW operators. • A character string of one or more characters, using standard quoted string format. In this case, multiple bytes are defined, one for each character in the string. OW (word) values can be • A constant expression that has a value between -32,768 and 65,535 (signed words range from -32,768 to 32,767; unsigned word values are from 0 to 65,535). • A relative expression that requires 16 bits or fewer, (including an offset in a 16-bit segment, or a segment or group value). • A one or two-byte string in standard quoted string format. DO (doubleword) values can be • A constant expression that has a value between -2,147,483,648 and 4,294,967,295 (when the 80386 is selected), or-32,768 and 65,535 otherwise. • A relative expression or address that requires 32 bits or fewer (when the 80386 is selected), 16 bits or fewer for any other processor. / • A relative address expression consisting of a 16-bit segment and a 16-bit offset. 134 TurboA sse mbIer Use rJ s Guide
  • 144. • A string of up to four bytes in length, using standard quoted string format. • A short (32-bit) floating-point number. DQ (quadword) values can be • A constant expression that has a value between --':2,147,483,648 and 4,294,967,295 (when the 80386 is selected), or -32,768 and 65,535 otherwise. • A relative expression or address that requires 32 bits or fewer (when the 80386 is selected), or 16 bits or fewer for any other processor. • A positive or negative constant that has a value between _263 and 264-1 (signed quadwords range in value from _263 to 263-1; unsigned quadwords have values from oto 264-1). • A string of up to 8 bytes in length, using standard quoted string format. .• A long (64-bit) floating-point number. DF, DP (80386 48-bit far pointer) values can be • A constant expression that has a value between -2,147,483,648 and 4,294,967,295 (when the 80386 is selected), or -32,768 and 65,535 otherwise. • A relative expression or address that requires 32 bits or fewer (when the 80386 is selected), or 16 bits or fewer for any other processor. • A relative address expression consisting of a 16-bit segment and a 32-bit offset. • A positive or negative constant that has a value between -:-247 and 248-1 (signed 6-byte values range in value from _247 to 247-1; unsigned 6-byte values have values from 0 to 248-1). • A string of up to 6 bytes in length, in standard quoted string format. DT values can be • A constant expression that has a value between -2,147,483,648 and 4,294,967,295 (when the 80386 is selected), or -32,768 and 65,535 otherwise. • A relative expression or address that requires 32 bits or fewer (when the 80386 is selected), or 16 bits or fewer for any other processor. • A positive or negative constant that has a value between _279 and 28°-1 (signed tenbytes range in value from _279 to 279-1; unsigned tenbytes have values from 0 to 28°-I). • A 10-byte temporary real formatted floating-point number. • A string of up to 10 bytes in length, in standard quoted string format. • A packed decimal constant'that has a value between 0 and 99,999,999,999,999,999,999. Numerical and string constants for the simple data allocation directives differ in some cases from those found in standard Turbo Assembler expressions. For example, the DB, DP, DQ, and DT directives accept quoted strings that are longer than those accepted within an expression. Chap t er 12, AII 0 cat i n9 dat.a 135
  • 145. Quoted strings are delimited either by single quotes(') or double quotes ("). Inside of a string, two delimiters together indicate that the delimiter character should be part of the string. For example, 'what','s up doc?' represents the following characters: what's up doc? You can have floating-point numbers as the value field for the DD, DQ, and DT directives. Here are some examples of floating-point numbers: 1.0EjO ;Stands for 1.0 x 1030 2.56E-21 ;Stands for 2.56 x 10-21 1.28E+5 ;Stands for 1.28 x 105 0.025 ;Stands for .025 Turbo Assembler recognizes these floating-point numbers because they contain a'.' after a leading digit. These rules are relaxed somewhat in MASM mode. For example, DD lE30 ;L~gal MASM mode floating point value! DD .123 ;Legal in MASM mode only. Note For clarity, we recommend using the form with the leading digit and the decimal point. Turbo Assembler also allows encoded real numbers for the DD, DQ, and DT directives. An encoded real number is a hexadecimal number of exactly acertain length. A suffix of R indicates that the number will be interpreted as an encoded real number. The length of the number must fill the required field (plus one digit if the leading digit is a zero); for example, DD 12345678r ;Legal number DD 012345678r ;Legal number DD 1234567r ;Illegal number (too short) The other suffix values (D, H, 0, Q, and B) function similarly to those found on numbers in normal expressions. Some of the simple data allocation directives treat other numerical constant values specially. For-example, if you don't specify radix for a value in the DT directive, Turbo Assembler uses binary coded decimal (BCD) encoding. The other directives assume a decimal value, as follows: DD 1234 DT 1234 ;Decimal ;BCD The default radix (that the RADIX directive specifies) is not applied for the DD, DQ, and DT directives if a value is a simple positive or negative constant. For example, RADIX 16 DW 1234 DD 1234 ;1234 hexidecimal ;1234 decimal Chapter 5 details numerical constants and the RADIX directive. 136 TurboA sse mbIe r Use r' s Guide
  • 146. Creating an instance of astructure or union To create an instance of a structure or a union data type, use the structure or union name as a data allocation directive. For example, assume you've defined the following: ASTRUe STRUe B DB "xyz" e DW 1 D DD 2 ASTRue ENDS BUNION UNION X DW? Y DD? Z DB? BUNION ends Then the statements ATEST ASTRUe BTEST BUNION would create instances of the structure astrue (defining the variable atest) and the union bunion (defining the variable btest). Since the example contained the? uninitialized data value, no initial data will be emitted to the current segment. Initializing union or structure instances Initialized structure instances are more complex than uninitialized instances. When you define a structure, you have to specify an initial default value for each of the structure members. (You can use the? keyword as the initial value, which indicates that no speCific initial value should be saved.) When you create an instance of the structure, you can create it using the default values or overriding values. The simplest initialized instance of a structure contains just the initial data specified in the definition. For example, ASTRUe {} is equivalent to DB "xyz" DW 1 DD 2 The braces ({ }) represent a null initializer value for the structure. The initializer value determines what members (if any) have initial values that should be overridden, and by what new values, as you allocate data for the structure instance. The syntax of the brace initializer follows: { [member_name = value [,member_name =value ... ]] } member_name is the name of a member of the structure or union. value is the value that you want the member to have in this instance. Specify a null value to tell Turbo Assembler to use the initial value of the member from the structure or union definition. A ? value indicates that the member should be uninitialized. Turbo Assembler sets any Chap t er 12, AII 0 cat i n9 dat a 137
  • 147. member that doesn't appear in the initializer to the initial value of the member from the structure or union definition. For example, ASTRUC {C=2,D=?} is equivalent to DB IxyZ" DW 2 DD ? You can use the brace initializer to specify the value of any structure or union member, even in a nested structure or union. Unions differ from structures because elements in.a union overlap one another. Be careful when youinitialize a union instance since if several union members overlap, Turbo Assembler only lets one of those members have an initialized value in an instance. For example, BUNION {} is valid because all three members of the union are uninitialized in the union definition. This statement is equivalent to DB 4 DUP (?) In this example, four bytes are reserved because the size of the union is the size of its largest member (in this case a DWORD). If the initialized member of the union is not the largest member of the union, Turbo Assembler makes up the difference by reserving space but not emitting data. For example, BUNION {Z=l} is equivalent to DB 1 DB 3 DUP (?) Finally, multiple initialized members in a union produce an error. For example, this is illegal: BUNION {X=l, Z=2} Note that if two or more fields of the union have initial values in the union definition, then using the simple brace initializer ({}) will also produce an error. The initializer must set all but one value to? for a legal instance to be generated. An alternative method of initializing structure and union instances is to use the bracket « » initializer. The values in the initializer are unnamed but are laid out in the same order as the corresponding members in the structure or union definition. Use this syntax for the bracket initializer: < [value [,value ... ]] > value represents the desired value of the corresponding member in the structure or union definition. A blank value indicates that you'll use the initial value of the member from the structure or: union definition. A ? keyword indicates that the member should be uninitialized. For example, 138 TurboA sse mbIer Use r's Guide
  • 148. ASTRUC <"abc",,?> is equivalent to DB "abc" DW 1 DD ? If you specify fewer values than there are members, Turbo Assembler finishes the instance by using the initial values from the structure or union definition for the remaining members. ASTRUC <"abc"> ;Same as ASTRUC <"abc",,> When you use the bracket initializer, give special consideration to nested structures and unions. The bracket initializer expects an additionaLmatching pair of angle brackets for every level of nesting, so that Turbo Assembler will treat the nested structure or union initializer as a single entity (to match the value in the instance). Alternatively, you can skip an entire level of nesting by leaving the corresponding entry blank (for the default value of the nested structure or union), or by specifying the ? keyword (for an uninitialized nested structure or union). For example, examine the following nested structure and union: CUNION CTYPE UNION STRUC DB ? ;Start of union ;If CTYPE=O, use this ... STRUC ENDS CTOPARl DW 1 CTOPAR2 DB 2 ;If CTYPE=l, use this ... STRUC ENDS ENDS ENDS CT1PARl DB 3 CT1PAR2 DD 4 ;End of union ;End of structure data type The bracket initializer for this complex structure/union has two levels of nesting. This nesting must appear as matched angle brackets within the initializer, like CUNION <O,«2,>,?» This directive is equivalent to DB 0 DW 2 DB 2 DB 2 DUP (?) Chapter 12, Allocating data 139
  • 149. Creating an instance of arecord To create an instance of a record data type, use the name of the record data type as a data allocation directive. For example, assume you've defined the following: MYREC RECORD VAL:3=4,MODE:2,SZE:4=15 Then, the statement MTEST'MYREC'? would create an instance of the record myrec (defining the variable mtest). No initial data is emitted to the current segment in this example because the ? uninitialized data value was specified. Record instances are always either a byte, a word, or a doubleword, depending on the ,number of bits allocated in the record definition. Initializing record instances You must specify an initial value for some or all of the record fields when you define a record. (Turbo Assembler assumes that any unspecified initial values are 0.) The , simplest initialized instance of a record contains justthe initial field data specified in the definition. For example, MYREC {} is equivalent to DW (4 SHL 6) + (0 SHL 4) + (15 SHL 0) iSHL is the shift left operator for expressions The braces ({ }) represent a null initializer value for the record. The initializer value determines what initial values should be overridden, and by what new values (as you allocate data for the record instance). Use this syntax of the brace initializer for records: { [field_name = expression [,field_name =expression... ]] field_name is the name of a field in the record. expression is the value that you want the field to have in this instance. A blank value indicates that you'll use the initial value of the field from the record definition. A ?value is equivalent to zero. Turbo Assembler sets any field that doesn't appear in the initializer to the initial value of the field from the record definition. For example, MYREC {VAL=2,SZE=?} is equivalent to DW (2 SHL 6) + (0 SHL 4) + (0 SHL 0) An alternative method of initializing record instances is to use the bracket « » initializer. Inthis case, brackets delineate the initializer. The values in the initializer are unnamed but are laid out in the same order as the corresponding fields in therecord definition. The syntax of the bracket initializer follows: < [expression [,expression... ]] > 140 Turbo Assembler User's Guide
  • 150. expression represents the desired value of the corresponding field in the record definition. A blank value indicates that you'll use the initial value of the field from the record definition. A ? keyword indicates that the field should be zero. For example, MYREC <,2,?> is equivalent to DW (4 SHL 6) + (2 SHL 4) + (0 SHL 0) If you specify fewer values than there are fields, Turbo Assembler finishes the instance by using the initial values from the record definition for the remaining fields. MYREC <l>isame as MYREC <1, ,> Creating an instance of an enumerated data type You can create an instance of an enumerated data type by using the name of the enumerated data type as a data allocation directive. For example, assume you have defined the following: ETYPE ENUM FEE,FIE,FOO,FUM Then the statement ETEST ETYPE ? would create an instance of the enumerated data type etype (defining the variable etest). In this example, no initial data is emitted to the current segment because the l' uninitialized data value is specified. Enumerated data type instances are always either a byte, a word, or a doubleword, depending on the maximum value present in the enumerated datatype definition. Initializing enumerated data type instances You can use any expression that evaluates to a number that will fit within the enumerated data type instance; for example, ETYPE ? ETYPE FOO ETYPE 255 iuninitialized instance iinitialized instance, value FOO ia number outside the ENUM that also fits Creating an instance of atable To create an instance of a table data type, use the table name as a data allocation directive. For example, assume you have defined the following table: TTYPE TABLE VIRTUAL DoneProc:WORD=DoneRtn, VIRTUAL MsgProc:DWORD-MsgRtn, VIRTUAL DoneProc:WORD-DoneRtn Then, the statement TTEST TTYPE ? Chapter 12, Allocating data 141
  • 151. would create an instance of the table ttype (defining the variable ttest). No initial data will be emitted to the current segment in this example because the ? uninitialized data value was specified. Initializing table instances When you define a table, you must specify an initial value for all table members. The simplest initialized instance of a table contains just the initial data specified in the definition. For example, ' . ' TTYPE {} is equivalent to DW MoveRtn DD MsgRtn DW DoneRtn The braces ({}) represent a null initializer value for the structure. The initializer value determines what members (if any) have initial values that should be overridden, andby what new values, as you allocate data for the table instance. Here's the syntax of the brace initializer: { [member_name = value [,member_name = value ... ]] member_name is the name of a member of the table. value is the value that you want the member to have in this instance. A blank value indicates that you'll use the initial value of the member from the table definition. A ? value indicates that the member should be uninitialized. Turbo Assembler sets any member that doesn't appear in the initializer to the initial value of the member from the table definition. For example, ' TTYPE {MoveProc=MoveRtn2,DoneProc=?} is equivalent to DW MoveRtn2 DD MsgRtn DW ? ,Creating'and initializing anamed-type instance You can create an instance of a named type by using the type name as a data allocation directive. For example; if you define the following type: NTTYPE TYPEDEF PTR BYTE the statement NTTEST NTTYPE ? creates an instance of the named type nttype (defining the variable nttest). No initial data is emitted tothe current segment in this example because you specified the ? uninitialized data value. 142 Turbo Assembler User's Guide
  • 152. The way that you initialize a named-type instance depends on the type that the named type represents. For example, NTTYPE in the previous example is a word, so it will be initialized as if you had used the DW directive, as follows: NTTYPE 1,2,3 DW 1, 2,3 iRepresents pointer values 1,2,3. iSame as NTTYPE 1,2,3. However, if the named type represents a structure or table, it must be initialized the same way as structures and tables are. For example, foo STRUC f1 DB? ENDS bar TYPEDEF foo bar {f1=1} iMust use structure initializer. Creating an instance of an object Creating an instance of an object in an initialized or uninitialized data segment is exactly the same as creating an instance of a structure. In fact, objects in Turbo Assembler are structures, with some extensions. One of these extensions is the @Mptr_<obj ecLname> structure member. An object data type with virtual methods is a structure having one member that points to a table of virtual method pointers. The name of this member is @Mptr_<obj ect_name>. Usually, you would initialize an instance of an objectusing a constructor method. However, you could have objects designed to be static and have no constructor, but are instead initialized with an initializer in a data segment. If you use the @Mptr_<object_name> member's default value, Turbo Assembler will correctly initialize the object instance. Another difference between structures and objects is that objects can inherit members from previous object definitions. When this inheritance occurs, Turbo Assembler handles it as a nested structure. Because of this, we do not recommend using bracket « » initializers for object data. Creating an instance of an object's virtual method table Every object that has virtual methods requires an instance of a table of virtual methods to be available somewhere. A number of factors determine the proper placement of this table, including what program model you're using, whether you want near or far tables, and sq forth. Turbo Assembler requires you to place this table. You can create an instance for the most recently defined object by using the TBLINST pseudo-op, with this syntax: TBLINST TBLINST defines @TableAddr_<object_name> as the address of the virtual table for the object. It is equivalent to @TableAddr_<object_name> @Table_<object_name> {} Chapter 12, Allocating data 143
  • 153. 144 Turbo Assembler User's Guide
  • 154. Advanced coding instructions Turbo Assembler recognizes all standard Intel instruction mnemonics applicable to the currently selected processor(s). You can find a detailed summary of these instructions in the quick reference guide. This chapter describes Turbo Assembler's extensions to the instruction set, such as the extended CALL instruction for calling language procedures. Intelligent code generation: SMART and NOSMART Intelligent code generation means that Turbo Assembler can determine when you could have used different instructions more efficiently than those you supplied. For example, there are times when you could have replaced an LEA instruction by a shorter and faster MOV instruction, as follows: LEA AX/lval can be replaced with MOV AX/OFFSET lval Turbo Assembler supplies directives that let you use intelligent code generation. The following table lists these directives. Table 13.1 Intelligent code generation directives NOSMART Disables smart code generation. By default, smart code generation is enabled. However, smart code generation is affected not only by the SMART and NOSMART directives,but also by the VERSION directive (see Chapter 3 for details on VERSION). Smart code generation affects the following code generation situations: Chapter 13, Advanced coding instructions 145
  • 155. .• Replacement of LEA instructions with MOV instructions if the operand of the LEA instruction is a simple address. • Generation of signed Boolean instructions, where possible. For example, AND AX,+02 vs. AND AX,0002. • Replacement of CALL FAR xxxx with a combination of PUSH CS, CALL NEAR xxxx, when the target xxxx shares the same CS register. Using smart instructions make it easier to write efficient code. Somestandard Intel instructions have also been extended to increase their power and ease of use. These are discussed in the next few sections. Extended jumps ! Conditional jumps such as JC or JE on the, 80186, and 80286 processors are only allowed to be near (within a single segment) and have a maximum extent of -128 bytes to 127bytes, relative to the current location counter. The same is true of the loop conditional instructions such as JCXZ or LOOP on all the Intel processors. Turbo Assembler can generate complementary jump sequences where necessary and remove this restriction. For example, Turbo Assembler might convert to JC xxx JNC temptag JMP xxx Note You can enable thiscomplementary jump sequences with the JUMPS directive, and disable it with the NOJUMPS directive. By default; Turbo Assembler doesn't generate this feature. When you enable JUMPS, Turbo Assembler reserves enough space for all forward- referenced conditional jumps for a complementary jump sequence. When the actual distance of the forward jump is determined, you might not need a complementary sequence. When this happens, Turbo Assembler generates NOP instructions to fill the extra space. To avoid generating extra NOPs, you can • You can usean override for conditional jumps that you know are in range; for example, JC SHORT abc ADD ax,ax abc: • Specify the 1m command-line switch. See Chapter 2 for more about this switch. 146 Turbo Assembler Us.er's Guide
  • 156. Additional 80386 LOOP instructions The loop instructions for the 80386 processor can either use CX or ECX as the counting register. The standard LOOP, LOOPE, LOOPZ, LOOPNE, and LOOPNZ mnemonics from Intel select the counting register based on whether the current code segment is a 32-bit segment (when using ECX) or a 16-bit segment (when using CX). Turbo Assembler has special instructions that increase the flexibility of the LOOP feature. The LOOPW, LOOPWE, LOOPWZ, LOOPWNE, and LOOPWNZ instructions use CX as the counting register, regardless of the size of the current segment. Similarly, the LOOPD, LOOPDE, LOOPDZ, LOOPDNE, and LOOPDNZ instructions use ECX as the counting register. Additional 80386 ENTER and LEAVE instructions Use the ENTER and LEAVE instructions for setting up and removing a procedure's frame on the stack. Depending on whether the current code segment is a 32-bit segment or a 16-bit segment, the standard ENTER and LEAVE instructions will modify either the EBP and ESP 32-bit registers, or the BP and SP 16-bit registers. These instructions might be inappropriate if the stack segment is a 32-'bit segment and the code segment is a 16-bit segment, orthe reverse. Turbo Assembler provides four additional instructions that always select a particular stack frame size regardless of the code segment size. The ENTERW and LEAVEW instructions always use BP and SP as the stack frame registers, while the ENTERD and the LEAVED instructions always use EBP and ESP. Additional return instructions The standard RET instruction generates code that terminates the current procedure appropriately. This includes generating epilog code for a procedure that uses a high- level language interfacing convention. Even for a procedure with NOLANGUAGE as its calling convention, the RET instruction will generate different code if you declare the procedure NEAR or FAR. For a NEAR procedure, Turbo Assembler generates a near return instruction. For a FAR procedure, Turbo Assembler generates a far return . instruction. (Outside of a procedure, a near return is always generated.) Turbo Assembler contains additional instructions to allow specific return instructions to be generated (without epilog sequences). The following table lists them. Table 13.2 Return instructions RETN Always generates anear return. RETF Always generates a far return. RETCODE Generates a return appropriate for the currently selected mod~l. Generates a near return for models TINY, SMALL, COMPACT, and TPASCAL. Generates afar return for models MEDIUM, LARGE, HUGE, and TCHUGE. Chap t er 13, Advan ced cod i ngin st rue t ion s 147
  • 157. AdditionallRET instructions For Turbo Assembler version 3.2 or later, you can use an expanded form of the IRET instruction. IRET will pop flags from the stack DWORD-style if the current code segment is 32-bit. Otherwise, a WORD-style POP is used. The IRETW instruction always pops WORD-style. Note that you can use these enhancements only if you select version T320. Otherwise, IRET will pop flags WORD-style, and IRETW is unavailable. Extended PUSH and POP instructions Turbo Assembler supports several extensions to the PUSH and POP instructions. These extensions greatly reduce the quantity of typing required to specify an extensive.series of PUSH or POPs. Multiple PUSH and POPs You can specify more than one basic PUSH or POP instruction per line. For example, PUSH ax PUSH bx PUSH ex POP ex POP bx POP ax can be written as PUSH ax bx ex POP ex bx ax For Turbo Assembler to recognize there are multiple operands present, make sure that any operand cannot conceivably be considered part of an adjacent operand. For example, PUSH foo [bxl might produce unintended results because foo I [bx], and foo [bx] are all legal expressions. You can use brackets or parentheses to clarify the instruction, as follows: PUSH [fool [bxl Pointer PUSH and POPs The standard PUSH and POP instructions can't push far pointers, which require 4 bytes on the 8086, 80186, and 80286 processors, and up to 6 bytes on the 80386 processor. Turbo Assembler permits PUSH and POP instructions to accept DWORD-sized pointer operands for the 8086, 80186, and 80286 processors, and PWORD and QWORD-sized pointer operands for the 80386 processor. When such a PUSH or POP is encountered, Turbo Assembler will generate code to PUSH or POP the operand into two pieces. 148 Turbo Assembler User's Guide
  • 158. PUSHing constants on the 8086 processor While the 80186, 80286, and 80386'processors have basic instructions available for directly PUSHing a constant value, the 8086 does not. Turbo Assembler permits constants to be PUSHed on the 8086, and generates a sequence of instructions that has the exact same result as the PUSH of a constant on the 80186 and higher processors. Note You can only do this if you've turned smart code generation on. The sequence of instructions Turbo Assembler uses to perform the PUSH of a constant is about tenbytes long. There are shorter and faster ways of performing the same function, but they all involve the destruction of the contents of a register; for example, MOV ax/constant PUSH ax This sequence is only four bytes long, but the contents of the AX register is destroyed in the process. . Additional PUSHA, POPA, PUSHF and POPF instructions For Turbo Assembler versions 3.2 or later, you can use an expanded form of the PUSHA, POPA, PUSHF and POPF instructions. If the current code segment is 32-bit, the PUSHA instruction will push registers in DWORD-style, and POPA will pop registers in DWORD-style. Otherwise, Turbo Assembler uses WORD-style PUSH and POP. Similarly, PUSHF and POPF will push and pop flags DWORD-style for a 32-bit code segment, or WORD-style otherwise. ! The PUSHAW, POPAW, PUSHFW, and POPFW instructions always push and pop WORD-style. Remember that you can use these enhancements only if you're using version T320 or later; otherwise, the pushes and pops will be done WORD-style. The PUSHSTATE and POPSTATE instructions The PUSHSTATE directive saves the current operating state on an internal stack that is 16 levels deep. PUSHSTATE is particularly useful if you have code inside a macro that functions independently of the current operating state, but does not affect the current operating mode. The state information that Turbo Assembler saves consists of: • Current emulation version (for example T310) • Mode selection (for example IDEAL, MASM, QUIRKS, MASM51) • EMUL or NOEMUL switches • Current processor or coprocessor selection • MULTERRS or NOMULTERRS switches • SMART or NOSMART switches • The current radix Chap t er 13, Advan ced cod i ngin st rue t ion s 149
  • 159. • JUMPS or NOJUMPS switches • LOCALS or NOLOCALS switches • The current local symbol prefix Use the POPSTATE directive to return to the last saved state from the stack. ; PUSHSTATE and POPSTATE example .386 ideal model small dataseg pass_string db 'passed' ,13,10,36 fail_string db 'failed' ,13,10,36 codeseg jumps nextl : pass1: fini: end ; Show changing processor selection, number radix, and JUMPS mode xor eax,eax pushstate Zero out eax. Can use EAX in 386 mode Preserve state of processor, radix and JUMPS nojumps radix p286 mov cmp jne mov popstate cmp je mov jmp mov mov mov mov int mov int 2 ax,l ax,l next1 ax,100 eax,4 pass1 dX,OFFSET fini Set to binary radix Only AX available now. EAX would give errors. No extra NOPS after this Assemble with /la and check in .lst file. Now 100 means binary 100 or 4 decimal. Restores JUMPS and 386 mode and default radix. EAX available again. Back in decimal mode. Extra NOPS to handle JUMPS. Check in .1st file faiLstring ; Load the fail string dX,OFFSET pass_string ; Load the pasp string. aX,@data ; Print the string out ds,ax ah,9h 21h ah, 4ch 21h Return to DOS 150 TurboA sse mbIer Use r's Guide
  • 160. Extended shifts On the 8086 processor, the shift instructions RCL, RCR, ROL, ROR, SHL, SHR, SAL, and SAR cannot accept a constant rotation count other than 1. The 80186, 80286, and 80386 processors accept constant rotation counts up to 255. When Turbo Assembler encounters a shift instruction with a constant rotation count greater than 1 (with the 8086 processor selected), it generates an appropriate number of shift instructions with a rotation count of 1. For example, .8086 SHL ax/4 generates SHL ax,l SHL ax,l SHL ax,l SHL ax,l Forced segme'nt overrides: SEGxx instructions Turbo Assembler provides six instructions that cause the generation of segment overrides. The following table lists these instructions. Table 13.3 Segment override instructions SEGSS Generates an SS override prefix byte. SEGDS Generates a DS override prefix byte. SEGES Generates an ES override prefix byte. SEGFS Generates an FS override prefix byte. SEGGS Generates a GS override prefix byte. You can use these instructions in conjunction with instructions such as XLATB, which do not require arguments, but can use a segment override. For example: SEGCS XLATB Note that most such instructions have an alternative form, where you can provide a dummy argument to indicate that an override is required. For example, XLAT BYTE PTR cs: [bxl These two examples generate exactly the same code. Additional smart flag instructions Often, you can simplify an instruction that manipulates bits in a flag to improve both code size and efficiency. For example, Chap ter 13, Advan ced cod ingin st rue tio ns 151
  • 161. OR aX,lOOOh might be simplified to OR ah,lOh if the only result desired was to set a specific bit in AX, and the processor flags that the instruction affects are unimportant. Turbo Assembler provides four additional instructions that have this functionality, as shown in the following table: Table 13.4 Smart flag instructions SETFLAG MASKFLAG TESTFLAG FLIPFLAG Set flag bites) Mask off ~ag bites) Test flag bites) Complement flag bites) OR AND TEST XOR Use these instructions to enhance the modularity of records; for example, FOO RECORD RO:l,Rl:4,R2:3,R3:l TESTFLAG AX, MASK RO In this example, TESTFLAG will generate the most efficient instruction regardless of where RO exists in the record. Additional field value manipulation instructions Turbo Assembler can generate specific code sequences for setting and retrieving values from bit fields specified with the RECORD statement. This lets you write code that is independent of the actual location of a field within a record. Used in conjunction with the ENUM statement, records can thus achieve an unprecedented level of modularity in assembly language. The following table lists these instructions: Table 13.5 Instructions for setting and retrieving values SETFIELD Sets a value in a record field. GETFIELD Retrieves a value from a record field. The SETFIELD instruction SETFIELD generates code that sets a value in a record field. Its syntax follows: SETFIELD field_name destination_rim , source_reg field_name is the name of a record member field. destination~r/m for SETFIELD is a register or memory address of type BYTE or WORD (or DWORD for the 80386). - source_reg must be a register of the same size or smaller. If the source is smaller than the destination, the source register must be the least significant part ofanother register that is the same size as the destination. This full-size register is called the operating register, 152 TurboA sse mbIer Use r's Guide
  • 162. Use this register to shift the value in the source register so that it's aligned with the destination. For example, Faa RECORD RO:l,Rl:4,R2:3,R3:1 SETFIELD Rl AX,BL SETFIELD Rl AX,BH ioperating register is BX iillegal! SETFIELD shifts the source register efficiently to align it with the field in the destination, and ORs the result into the destination register. Otherwise, SETFIELD modifies only the operating register and the processor flags. Note Using SETFIELD will destroy the contents of the operating register. To perform its function, SETFIELD generates an efficient but extended series of the following instructions: XOR, XCHG, ROL, ROR, OR, and MOVZX. If you're using SETFIELD when your source and target registers are the same, the instruction does not OR the source value to itself. Instead, SETFIELD ensures that the fields of the target register not being set will be zero. SETFIELD does not attempt to clear the target field before ~Ring the new value. If this is necessary, you must explicitly clear the field using the MASKFLAG instruction. The GETFIELD instruction GETFIELD retrieves data from a record field. It functions as the logical reverse qf the SETFIELD instruction. Its syntax follows: GETFIELD field_name destination_reg, source_rim field_name and destiiwtion_reg function as they do for SETFIELD. You can use source_rim as you would for source_reg (for SETFIELD). For example, Faa RECORD RO:l,Rl:4,R2:3,R3:1 GETFIELD Rl BL,AX ioperating register is BX GETFIELD Rl BH,AX iillegal! Note Note that GETFIELD destroys the entire contents of the operating register. GETFIELD retrieves the value of a field found in the source register or memory address, and sets the pertinent portion of the destination register to that value. This instruction affects no other registers than the operating register and the processor flags. To accomplish its function, GETFIELD generates an efficient but extended series of the following instructions: MOV, XCHG, ROL, and ROR. If you're using the GETFIELD instructi~n when your source and target registers are the same, the instruction will not generate the nonfunctional MOV target, source instruction. Chap t er 13, Advan ced cod ingin st rue t ion s 153
  • 163. Additional fast immediate multiply instruction Turbo Assembler provides a special immediate multiply operation for efficient array indexing. FASTIMUL addresses a typical problem that occurs when you create an array of structures. There is no immediate multiply operation available for the 8086 processor. Even for the more advanced processors, multiplication using shifts and adds is significantly faster in some circumstances than using the standard immediate IMUL instruction. Based on the currently specified processor, Turbo Assembler's FASTIMUL instruction chooses between the most efficientsequence of shifts and adds available, and the current processor's immediate IMUL operation (if any). FASTIMUL has the following syntax: FASTlMUL dest_reg, source_rim, value This instruction is much like the trinary IMUL operation available on the 80186, 80286, and 80386 processors. The desCreg destination register is a WORD register (or it can be DWORD on the 80386). source_rim is a register or memory address that must match the size of the destination. value is a fixed, signed constant multiplicand. FASTIMUL uses a combination of IMUL, MOV, NEG, SHL, ADD, and SUB instructions to perform its function. This function destroys the source register or memory address, and leaves the processor flags in an indeterminate state. Extensions to necessary instructions for the 80386 processor The 80386 processor has,the ability to operate in both 16-bit and 32-bit mode. Many of the standard instructions have different meanings in these two modes. In Turbo Assembler, you can control the operating size of the instruction using the SMALL and LARGE overrides in expressions. ' In general, when you use SMALL or LARGEas part of an address expression, the operator controls the generation of the address portion of the instruction, determining whether it should be 16- or,32-bit. When SMALL or LARGE appears outside of the address portion of an expression, it can control whether a 16-bit instruction or a 32-bit instruction is performed. In cases where you can determine the size of the instruction from the type of the operand, Turbo Assembler selects the size'of the instruction. The following table shows the instructions ,that SMALL and LARGE affect. Table 13.6 Instructions affected by SMALL and LARGE PUSH [SMALL/LARGE] segreg POplSMALL/LARGE] segreg FSAVE [SMALL/LARGE] memptr FRSTOR [SMALL/LARGE] memptr FSTENV [SMALL/LARGE] memptr FLDENV [SMALL/LARGE] memptr LGDT [SMALL/LARGE] memptr Selects whether 16-bit or 32-bit form of segment register is PUSHed. Selects whether 16-bit or 32-bit form of segment register is POpped. Selectswhether small or large version of floating-point state is saved. Selects whether small or large version of floating-point state is restored. Selects whether small or large version of floating-point state is stored. Selects whether small or large version of floating-point state is loaded. Selects whether small or large version of global descriptortable is loaded. 154 TurboA sse mbIer Use r 's Guide
  • 164. Table 13.6 Instructions affected by SMALL and LARGE (continued) hlst);uction SGOT [SMALL/LARGE] memptr LIOT [SMALL/LARGE] memptr SlOT [SMALL/LARGE] memptr JMP [SMALL/LARGE] memptr Selects whether small or large version of global descriptor table is saved. Selects whether small or large version of interrupt descriptor table is loaded. Selects whether small or large version of interrupt descriptor table is saved. For DWORD-sized memory addresses, selects between FAR 16-bit JMP and NEAR 32-bit JMP. CALL [SMALL/LARGE] memptr For DWORD-sized memory addresses, selects between FAR 16-bit CALL and NEAR 32-bit CALL. Note Turbo Assembler selects the size of the instruction using SMALL and LARGE only when no other information is available. For further information about overriding address sizes with the SMALL and LARGE operators, see Chapter5. Calling procedures with stack frames Turbo Assembler supports an extended form of the CALL instruction that lets you directly call procedures that use high-level language interfacing conventions. Arguments to procedures that use high-level language interfacing conventions are passed on the stack in a stackframe. The caller must push these arguments onto the stack before calling the procedure. The interfacing convention of the procedure determines the order arguments should be pushed into the stack frame. For BASIC, FORTRAN, and PASCAL procedures, arguments are pushed onto the stack in the order they are encountered; for C and CPP (C++), the arguments are pushed in the reverse order. The interfacing convention of a procedure also determines whether the procedure or the caller of the procedure must remove the arguments from the stack once the procedure is called. C and c++ require the caller to clean up the stack. In all other languages, the procedure itself must remove the arguments from the stack before returning. Turbo Assembler handles both the proper argument ordering and stack cleanup for you with the extended CALL instruction. The syntax for calling a procedure with parameters follows: CALL expression [language] [,argument_list] expression is the target of the CALL instruction. language specifies the interfacing convention to use for the call. Ifyou don't specify a language, Turbo Assembler uses the default language set by MODEL (see Chapter 7 for further information about using MODEL). Arguments, if any, follow the language identifier. The syntax of each argument in the argument list is the same as for the extended PUSH and POP instructions. You can separate these arguments with commas; for example, CALL test PASCAL,ax,es OFFSET buffer,blen PASCAL, the language in the example, causes Turbo Assembler to push the arguments in the same order that it encounters them. This example call is equivalent to Cha pte r 13, Advan ced cod i ngin strue tion s 155
  • 165. PUSH ax PUSH es OFFSET buffer PUSH word PTR bIen CALL test A call to a C procedure requires that the arguments be pushed onto the stack in the reverse order. Turbo Assembler automatically does this so that a call of the form CALL test C,ax,es OFFSET buffer,word PTR bIen results in the following code: PUSH word PTR bIen PUSH esOFFSET buffer PUSH ax CALL test SUB sp,8 When ~alling a procedure with arguments, you should always list the arguments in the same order theywere listed in the procedure header. Turbo Assembler reverses them if ,necessary. Note Remember to separate arguments with commas and components ofarguments with spaces. Turbo Assembler, depending on the interfacing convention, can push arguments in reverse order on the stack, but it won't alter the ordering of argument components. If the interfacing ~onvention for the call is NOLANGUAGE, Turbo Assembler reports an error if any arguments are present. Although you can define arguments to a NOLANGUAGE procedure with the ARG directive, you must explicitly push the arguments when you make a call to a NOLANGUAGE procedure. Calling procedures that contain RETURNS Procedures that define some oftheir arguments with the RETURNS keyword must be considered specially. These arguments are used to return values to the caller; therefore, the caller always pops them. There is no special extension to the CALL instruction in Turbo Assembler to help pass those arguments specified in a procedure declaration after the RETURNS directive. You must explicitly PUSH these arguments before the CALL, and POP them afterward. ' Calling procedures that have been prototyped If you've defined the procedure prior to the call or used PROCDESC to prototype the procedure (see Chapter 10), Turbo Assembler will type check any language and arguments specified in the call and generate a warning if the language, number of parameters, or types of parameters don't match. For example, 156 TurboA sse mbIer Use r's Guide
  • 166. test PROCDESC pascal far :word, :dword, :word call test pascal aX,ds bx,cx call test c, aX,dx, bx,cx call test pascal, eax, ebx, ecx call test pascal, aX,ds bx iworks fine iwrong language! iwrong parameter types! itoo few parameters! Since the language of the procedure has been specified, you don't have to include it in the calL If you omit it, however, make sure to include the comma that would normally follow it: call test,ax,ds bx,cx iworks fine You can also use procedure types (declared with PROCTYPE) to supply a distance and language, and force type-checking to occur. For example, footype proctype pascal near :word, :dword, :word call footype ptr[bx],ax,ds bx,cs ino error! Calling method procedures for objects: CALL..METHOD The CALL instruction is extended to support the calling of object methods. A call to an object method can generate either a direct call (for static methods) or an indirect call (for .virtual methods). Because you can use an indirect call, the instructions that pertbrm the call can destroy the contents of some registers. Therefore, Turbo Assembler lets you select the proper registers if you're using a virtual method call. i!} .Here's the syntax of the CALL..METHOD extension: ~ CALL instance_ptr METHOD [object_name:]method_name [USES [segreg:]offsreg] [language_and_args] instance-rtr must describe an instance of an object. In MASM mode,it's often impossible to determine the name of the object associated with an instance. Therefore, Turbo Assembler allows the object_name field, so that you can specify the instance's object name. method_name contains the name of the method to be called for the specified object instance. See Chapter 8 for further information about how to specify a method as virtual or static. If the method is virtual and an indirect call is required, the CALL..METHOD instruction normally calls indirectly through ES:BX (or ES:EBX for USE32 models on the 80386 processor). If you want to use other registers, you can override them with the USES clause. segreg is the optional segment register to use, and offsreg is the offset register to use for the call. For objects declared with near tables, CALL..METHOD only loads the offset register. Turbo Assembler assumes that the segment register is already set up to the correct value. Chap t er 13, Adv anee d cod i ngin st rue t ion s 157
  • 167. Note It's good programming practice to specify an appropriate selection for indirect calling registers, even if you know that the method you're calling is static. As objects are modified, met~ods can change from being static to virtual. The language_and_args field of theCALL..METHOD instruction contains the optional language and argument specifications, which are identical in form to that listed previously under "Calling procedures with stack frames." Calling method procedures for C++or Pascal usually requires that the instance of the object be passedas an argument on the stack. SeeChapter 18 for further information. Tail recursion for object methods: JMP..METHOD Turbo Assembler provides a IMP..METHOD instruction that corresponds to the CALL..METHOD mstruction. Here's its syntax: JMP instance_ptr METHOD [object_name:]method_name [USES [segreg:]offsreg] . ~ IMP..METHOD functions exactly like CALL..METHOD except that ©) • It generates a IMP instead of a CALL instruction. • It generates procedure epilog code to clean up the stack before the IMP instructionis generated. The IMP..METHOD instruction makes it possible to write efficient tail recursion code. It's intended to replace the common situation where a CALL..METHOD instruction is issued to the current method, followed by a RET instruction. Additional instruction for object-oriented programming When an object instance is constructed, you must initialize the instance's virtual table pointer (if any) to point to the correct virtual method table. The TBLINIT instruction lets you do this automatically. The syntax of the TBLINIT instruction is TBLINIT object_instance_pointer The object_instance~ointer field is the address of the object whose virtual table pointer is to be initialiied. The TBLINIT instruction assumes that the object instance should be of the current object type (in other words, the immediately preceding object definition determines the object type that TBLINIT initializes). For example, TBLINIT DS:SI would initialize the virtual table pointer of the object at DS:SI, if it has one. 158 TurboA sse mbIer Use r's Guide
  • 168. ·Using macros Macros let you give a symbolic nameto a text string or a block of code that will be used frequently throughout your program. Macros go beyond this simple substitution, however. Turbo Assembler has macro operators that provide great flexibility in designing macros. Combined with the ability to use multiline macros with arguments, this makes Turbo Assembler's macro facility a very powerful tool. This chapter discusses how to use text and multiline macros in your program. Text macros A text macro is a symbol that represents a string of text characters. When Turbo Assembler encounters the symbol in expressions (and other situations), it substitutes the text characters for the symbol. For example, if DoneMsg is a text macro whose value is "Returning to the OS", the following statement GoodBye DB DoneMsg results in GoodBye DB 'Returning to the OS' Defining text macros with the EQU directive You can use the EQU directive to define simple text macros. Here's the syntax for defining a text macro: name EQU text_string text_string associates with the text macro name name. You should enclose text_string in brackets « » to qelineate the text; for example, DoneMsg EQU <'Returning to the OS'> Note If you omit the brackets in MASM mode, Turbo Assembler will try to evaluate text_string to an expression, and an error may result. Only if it can't evaluate text_string Chap ter 14, Usin 9 mac r0 s 159
  • 169. will Turbo Assembler treat it as a text macro (to remain compatible with MASM). In Ideal mode, EQU always defines a text macro. If you don't enclose text_string in brackets and it's the name of another text macro, Turbo Assembler will use that macro's contents. Otherwise, the macro will be defined to the text. You should always enclose text macro strings in angle brackets to make sure they're properly defined. Consider the following mistake that can occur when you don't: IDEAL Earth EQU dirt Planet EQU Earth Planet EQU <Earth> ;Earth = "dirt" ;Planet = "dirt" (wrong!) . ;Planet = "Earth" (correct!) In Ideal mode, the EQU statement always defines a text macro. Text macros are redefinable; you can redefine a text macro name in the same module to another text string. String macro manipulation directives Turbo Assembler provides directives that can manipulate strmg macros. These directives are available in Ideal mode, and for versions M510, M520, and T300 orlater (as specified by the VERSION directive). A string argument for any of these directives can be any of the following: • 'a text string enclosed in brackets; for irlstance, <abc> • the name of a previously defined text macro • an expressionpreceded by a % character, whose value is converted to the equivalent numerical string representation appropriate for the current radix The CATSTRdirective The CATSTR directive defines a new text macro by concatenating strings together. CATSTR has the following syntax: name CATSTR string[,string] ... CATSTR concatenates from left to right. Turbo Assembler creates a new text macro of the name name. The SUBSTR directive The SUBSTR directive defines a newtext macro to be a substring of a string. Here's its syntax: name SUBSTR string,position_expression[,size_expression] The new text macro, name consists of the portion of string that starts at the . position_expression character, and is size_expression characters in length. If you don't supply size_expression, the new text macro consists of the rest of stringfrom the character at position_expression. Turbo Assembler considers the first character of string to be at po~sition 1. 160 Turbo Assembler User's Guide
  • 170. The INSTR directive The INSTR directive returns the position of one string inside another string. INSTR has the following syntax: name INSTR [start_expression,lstring1,string2 Turbo Assembler assigns name a numeric value that is the position of the first instance of string2 in stringl. The first character in stringl has a position of 1. Ifstring2 does not appear anywhere within stringl, Turbo Assembler returns a value of O. If you include start_expression, the search begins at the start_expression character. The first character of a string is 1. The SIZESTR directive The SIZESTR directive returns the length of a text macro (the number of characters in the string). Here's its syntax: name SIZESTR string name is set to the numeric value of the length of the string. A null string < > has a length of zero. Text macro manipulation examples The following examples show how these operators work: VERSION T300 IDEAL ABC ABC2 ABC ABC3 ABCLEN EQU <abc> EQU ABC EQU <deb CATSTR ABC2,<,>,ABC,<,>,ABC2 SIZESTR ABC ABC3LEN SIZESTR ABC3 COMMA1 INSTR ABC3,<,> COMMA2 INSTR ABC4 SUBSTR ABC5 SUBSTR ABC6 EQU ABC7 EQU ABC8 EQU Multiline macros COMMA1+1,ABC3,<,> ABC3,5 ABC3,5,3 3+2+1 %3+2+1 % COMMA1 ;ABC = "abc" ;ABC2 = "abc" ;ABC = "def" (redefined) ;ABC3 = "abc, def, abc" . ;ABCLEN = 3 ;ABC3LEN = 11 ;COMMA1 = 4 ;COMMA2 = 8 ;ABC4 = "def,abc" ;ABC5 = "def" ;ABC6 = 6 (numeric equate) ;ABC7 = "6" (text macro) ;ABC8 = "4" The multiline macro facility lets you define a body of instructions, directives, or other macros that you'll include in your source code whenever the macro is invoked. You can supply arguments to the macro that Turbo Assembler will substitute into the macro body when you include the macro in the module. .There are several types of multiline macros. One version substitutes each element of a string, one after the other, as an argument to the macro. Another version repeats the macro body a certain number of times. Finally, you can define still another version in Chap t er 14, Us in 9 mac r0 s 161
  • 171. one place, and invoke it many times. All versions have the definition of a macro body in common. The multiline macro body Regardless of its actual content, Turbo Assembler's macro processing facility treats a multiline macro body as merely a number of lines of text. Turbo Assembler lets you replace symbols within the macro body with text specified at the time a macro is invoked. This feature is called argument substitution. The symbols in the macro body that will be replaced are called dummy arguments. For example, suppose the symbolfaa is a dummy argument in the following macro body: PUSH foo MOV foo,l If you assignfaa with the text string AX when you invoke this macro, the actual text included in the module will be . PUSH AX MOV AX,l The rules Turbo Assembler uses for recognizing a dummy argument are fairly complex. Examine the following macro body lines where the dummy argumentfoowould not be recognized: symfoo: DB 'It is foo time' In general, Turbo Assembler will not recognize a dummy argument without special help in the following situations: • when it is part of another symbol • when it is inside of quotation marks (' or ") • in Ideal mode, when it appears after a semicolon not inside of quotes Using &in macros The & character has aspecial meaning when used with the macro parameters. In general, & separates a d~mmy argument name from surrounding text, so Turbo Assembler can recognize it for substitution. For example, given the following Ideal mode macro: macro mac1 foo sym&foo: DB 'It is &foo time' endm if you assignfaa the text string party When this macro is invoked, the actual text included in the module will be symparty: . DB 'It is party time' Another example might be foo&sym: 162 TurboA sse mbIer Use r' s Gui d.e
  • 172. DB 'We are in O&foo&o' Ifyou assignfaa the text string hi when this macro is invoked, the text included in the module will be hisym: DB 'We are in Ohio' Note Here are the rules for the & character: • Outside quoted strings, the & serves only as a general separator. • Inside quoted strings and after a semicolon that's not in a quoted string in Ideal mode, & must precede a dummy argument for it to be recognized. • Turbo Assembler removes one & from any group of &s during a macro expansion. The last point makes it possible to place macro definitions requiring & characters inside other macro definitions. Turbo Assembler will remove only one & from any group. Including comments in macro bodies For particularly complicated macros, you might want to include (in the macro body text) comments that won't be included when the macro is invoked. This also reduces the memory required for Turbo Assembler to process macros. To do this, use the double semicolon comment at the beginning of a line. For example, the following macro body iiWOW, this is a nasty macro! DB 'Nasty macro' will only include the following text when it is invoked: DB 'Nasty macro' Note Comments preceded by single semicolons are always included in a macro expansion. Local dummy arguments At the beginning of any macro body, you can include one or more LOCAL directives. LOCAL declares special dummy arguments that, each time the macro expands, will be assigned a unique symbol name. The syntax for the LOCAL directive in macro bodies looks like this: LOCAL dummy_argumentl [,dummy_argument21 ... When using this syntax, note that the LOCAL-directive must come before any other statements in a macro body. If the dummy_argument name used in the LOCAL directive does not have a local symbol prefix the uniqu~ symbol name assigned to it will be in the form ??xxxx, where xxxx represents a hexadecimal number. Otherwise, the unique symbol name will be <local prefix>xxxx. For details on how to enable local symbols and set the local symbol prefix, see Chapter 11. You can use LOCAL dummy arguments to define labels within the macro body. For example, Chap t er 14, Us i n9 mac r0 S 163
  • 173. LOCAL @@agn,@@zero XOR dX,dx MOV cX,exp MOVax,l JCXZ @@zero MOV bX,factor @@agn: MUL bx LOOP @@agn @@zero: Note In macros, you don't have to use @@ since local labels in macros are turned into consecutive numbers, like ??OOOL Their names are not easily accessible outside macros. The EXITM directive You can use the EXITM directive within a macro body to prematurely terminate the assembly of an included macro body. Its syntax follows: EXITM When Turbo Assembler encounters EXITM in a macro body that has been included m the module source code, assembly of the expanded macro body stops immed~ately. Instead, Turbo Assembler will continue assembling the module at the end of the macro. You can use the EXITM statement with a conditional assembly directive to terminate a macro expansion when certain conditions are met. Tags and the GOTO directive Using macro tags and the GOTO directive lets you control the sequence in which lines within the macro body expand. You can place a macro tag at any place within the macro body. The tag occupies an entire line in the macro, with the following syntax: :tag_symbol When the macro expands, all macro tags are discarded. The GOTO directive tells the assembler to go to a specified point in your code, namely the tag_symbol. GOTO has the followingsyntax: GOTO tag_symbol GOTO also terminates any conditional block that contains another GOTO. This lets you place GOTO inside conditional assembly blocks. For example, IF faa GOTO tagl ENDIF DISPLAY "faa was false!" :tagl ;resume macro here ... ;works the same whether faa was false or true Note Be careful not to create infinite macro loops when you use the GOTO directive. Infinite loops can cause Turbo Assembler to run out of memory, or even appear to stop functioning. 164 TurboA sse mbIer Us erJ s Guide
  • 174. See Chapter 15 for further information about conditional assembly directives. General multiline macros Turbo Assembler associates a general multiline macro's body of directives, instructions, and other macros with a symbolic macro name. Turbo Assembler inserts the body of statements into your program wherever you use the macro name as a directive. In this way, you can use a general multiline macro more than once. Note You can invoke a macro before you define it only when you use the / m command-line switch as explained in Chapter 2. However, this is considered to be poor programming practice. Here's the Ideal mode syntax for defining a general multiline macro: MACRO name parameter_list macro_body ENDM Here's the MASM mode syntax for defining a general multiline macro: name MACRO parameter_list macro_body ENDM name is the name of the multiline macro you're defining. macro_body contains the statements that make up the body of the macro expansion. You can place any valid (and any number of) Turbo Assembler statements within a macro. The ENDM keyword I terminates the macro body. This example defines a macro named PUSHALL that, when invoked, includes the macro body consisting of three PUSH instructions into your program. PUSHALL MACRO PUSH AX BX CX DX PUSH DS S1 PUSH ES DI ENDM parameter_list is a list of dummy argument symbols for 'the macro. Here's its syntax: [dummy_argument [,dummy_argument ... JJ You can l,lse any number of dummy arguments with a macro, as long as they fit on one line, or you use the line continuation character ( ) to continue them to the next line. For example, ADDUP MACRO dest, sl,s2 MOV dest,sl ADD dest,s2 ENDM idest is 1st dummy argument is1,s2 are 2nd and 3rd dummy arguments Each dummy argument has the following syntax: Chap ter 14, Usin 9 .mac r0 s 165
  • 175. dummy_name is a symbolic name used as a place holder for the actual argument passed to the macro when it's invoked. The optional dummy_type specifies something about the form the actual argument must take when you invoke the macro. The following types are supported: Table 14.1 Dummy argument types REQ =<texCstring> VARARG REST Argument cannot be null or spaces. Bracketed text string is the default value for the dummy argument when the actual argument is null or contains spaces. ' Actual argument consists of the rest of the macro invocation, interpreted as a list of arguments. Commas and angle brackets are added to ensure this interpretation. Actual argument consists of the rest of the macro invocation, interpreted as raw text. Invoking ageneral multiline macro To invoke a general multiline macro, use the name of the macro as a directive in your program. Turbo Assembler inserts the macro body (after all the dummy arguments are substituted) at that point in the module. The syntax for invoking a general multiline macro is as follows: macro_name [argument [ [' I Jargument J ••• J macro_name is the symbolic name of a macro. Ifyou invoke a macro with arguments, the arguments are listed following the, macro name. You can specify any number of arguments, but they must all fit on one line. Separate multiple arguments with commas or spaces. When the macro expands, Turbo Assembler replaces the first dummy argument in the macro definition with the first argument passed, the second dummy argument,with the second argument, and so forth. Each argument represents a text string. You can specify this text string in the following ways: • as a contiguous group of characters, not containing any whitespace, commas, or semicolons • as a group of characters delineated by angle brackets « », which can contain spaces, commas, and semicolons • as a single character preceded by a !character, which is equivalent to enclosing the character in angle brackets • as an expression preceded by a % character,which represents the text value of the expression appropriate for the currently selected radix The < > literal string brackets Use angle brackets to delineate a literal string that contains the characters between them. You should use them like this: <text> 166 Turbo Assembler User's Guide
  • 176. text is treated as a single string parameter, even it if contains commas, spaces, or tabs that usually separate each parameter. Use this operator when you want to pass an argument that contains any of these separator characters. You can also use this operator to force Turbo Assembler to treat a character literally, without giving it any special meaning. For example, if you want to pass a semicolon (;) as a parameter to a macro invocation, you have to enclose it in angle brackets «;» to prevent it from being treated as the beginning of a comment. Turbo Assembler removes only one level of angle brackets when it cpnverts a bracketed string to a text argument. This makes it possible to invoke a macro requiring angle brackets from inside another macro body. The !character The !character lets you invoke macros with arguments that contain special characters. Using this character prior to another is similar to enclosing the second character in angle brackets. For example, !; functions the same as <;>. Some common uses are shown in the following table. Table 14.2 Uses for the !character !> > !< < !! The %expression evaluation character The % character causes Turbo Assembler to evaluate an expression. The assembler converts the result of the expression to an ASCII number in the current radix, which is the text that the % character produces. Use this character when you want to pass the string representing a calculated result, rather than the expression itself, as a macro argument. The syntax follows: %expr expr can be either an expression (using any lega1 operands and operators), or it can be the name of a text macro. If it is an expression, the text that is produced is the result of the expression, represented as a numerical string in the current radix. If expr is a text macro name, the text that's produced is the string that the text macro represents. See Chapter 5 for more information about Turbo Assembler expressions. For example, this code DEFSYM MACRO NUM TMP_&NUM: ENDM TNAME EQU <JUNK> DEFSYM %5+4 DEFSYM %TNAME ;defining a text macro Chap t er 14, Us in 9 mac r0 s 167
  • 177. results in the following code macro expansions: TMP_9: TMP_JUNK: Redefining ageneral multiline macro You can redefine general multilme macros. The new definition automatically replaces the old definition. All preceding places where the macro had already been invoked will not change. All invocations of the macro following the redefinition use the new definition. Deleting ageneral multiline macro: The PURGE directive. You can use the PURGE directive to delete a macro. PURGE has the following syntax: PURGE macroname [,macroname] ... PURGE deletes the general multiline macro definition associated with macroname. After you PURGE a macro, Turbo Assembler no longer treats the symbol macroname as if it were a macro; for example, ADD MACRO al,a2 SUB al, a2 ENDM ADD aX,bx ;This invocation will produce SUB aX,bx PURGE ADD ADD aX,bx ;This is no longer a macro, so ADD aX,bx is produced You can purge several macros at a time byseparating their names with commas. Note, however, that you can't redefine a purged macro symbol as anything other than another macro. Defining nested and recursive macros The statements in a macro body can include statements that invoke or define other macros. If you take this example, . MCREATE MACRO opname,opl,op2,op3,op4,op5,op6,op7 IFNB opname DO&opnameMACRO op,count IF count LE 4 REPT count opname op,l ENDM ELSE MOV CL,count opname op,CL ENDIF ENDM MCREATE opl,op2,op3,op4,op5,op6,op7 ENDIF ENDM 168 Turbo Assembler User's Guide ;end of DOopname macro ;recurse! ;end of if ;end of MCREATE macro ..
  • 178. and invoke it with MCREATE ror,rol,rcl,rcr,shl,shr,sal,sar it will create the additional macros DOror, DOrol, and so forth, which you can then use like this: DOshr ax,S DOrcr bX,3 You can call recursive macros with a list of parameters, and set them up so that the macro will work with anywhere from zero to a maximum number of parameters. To do this, have the macro body use the first parameter to do its expansion, then call itselfwith the remaining parameters. Every time it recurses, there will be one fewer parameter. Eventually, it will recurse with no parameters. When you call the macro recursively, it·always needs some way to test for the end of the recursion. Usually, an IFNB conditional statement will do this for only the macro body if the passed parameter is present. Here is a simpler example of a recursive macro: PUSHM MACRO rl,r2,r3,r4,r5,r6,r7,r8 IFNB rl push rl PUSHM r2,r3,r4,r5,r6,r7,r8 ENDIF ENDM Note See Chapter 15 for more information about the IFNB directive. The count repeat macro. You can use the REPT repeating macro directive to repeat a macro body a specific number of times, using this syntax: REPT expression macro_body ENDM expression tells Turbo Assembler how many times to repeat the macro body specified between the REPT and END directives. expression must evaluate to a constant and can't contain any forward-referenced symbol names. Use ENDM to mark the end of the repeat block. For example, this code REPT 4 SHL aX,l ENDM produces the following: SHL aX,l SHL aX,l SHL aX,l SHL aX,l Another example shows how to use REPTin a macro to generate numbers that are the various powers of two: Chap t er 14, Usin 9 mac r0 s 169
  • 179. count = 0 defname macro num Bit&num dd (1 SRL (&num)) endin rept 32 defname %count count = count + 1 endin The WHILE directive You can use the WHILE macrodirective to repeat a macro body until a certain expression evaluates to a(false). WHILE has the following syntax: WHILE while_expression macro_body ENDM Turbo Assembler evaluates while_expression before each iteration of the macro body. Be careful to avoid infinite loops, which can cause Turbo Assembler to run out of memory or appear to stop functioning. Here's an example using WHILE: WHILE 1 IF some_condition EXITM ENDIF i i 'Do nothing ENDM i We never make it this far unless some_condition is true The EXITM directive can be used to break out of a WHILE loop. String ,repeat macros You can use the IRP and IRPC string repeat macro directives to repeat a macro body once for each element in a list or each character in a string. Each of these directives requires you to specify a single dummy argument. Here's the IRP syntax: IRP dummy_argument, argument_list macro_body ENDM IRPC has the following syntax: IRPC dummy_argument, string' macro_body ENDM In both'cases, dummy_argument is the dummy argument used in the macro body. ENDM marks the end of the macro body. For IRP, argument_list consists of a list of arguments separated by commas. The arguments can be any text, such as symbols, strings, numbers, and so on. The form of 170 TurboA sse mbIer Use r' s Guide
  • 180. each argument in the list is similar to that described for general multiline macro invocations, described earlier in this chapter. You must always surround the argument list with angle brackets « ». For IRPC, the argument consists of a single string. The string can contain as many characters as you want. For each argument or character in a string, Turbo Assembler will include the macro body in the module, substituting the argument or character for the dummy argument wherever it finds it. For example, IRP reg,<ax,bx,cx,dx> PUSH reg ENDM produces the following: PUSH ax PUSH bx PUSH ex PUSH dx and the directive IRPC IRPC LUCKY, 1379 DB LUCKY ENDM produces this: DB 1 DB 3 DB 7 DB 9 Note Be careful when using IRPC because Turbo Assembler places each character in the string"as is" in the expanded macro, so that a string repeat macro such as IRPC CHAR, HELLO DB CHAR ENDM might not produce DB 'H', 'E', 'L', 'L', 'a', but instead would produce DB H, E, L, L, a (where each letter is treated as a symbol name). The % immediate macro directive The % immediate macro directive treats a line of text as if it's a macro body. The dummy argument names used for the macro body include all of the text macros defined at that time. Here's its syntax: %maero_body_line macro_body_line represents the macro body to use for the immediate macro expansion; for example: Chap t er 14, Us i n9 mac r0 s 171
  • 181. SEGSIZE EQU <TINY> LANGUAGE EQU .<WINDOWS PASCAL> %MODEL SEGSIZE,LANGUAGE ;Produces MODEL TINY,WINDOWS PASCAL Including multiline macro expansions in the list file Multiline macro expansions are not normally included in the listing file. However, Turbo Assembler provides the following directives that let you list macro expansions: • .lAll • .SAll • .XAll • %MACS • %NOMACS Refer to Chapter 17 for more details onthese directives. Saving the current operating state The PUSHSTATE directive saves the current operating state on an internal stack that is 16 levels deep. PUSHSTATE is particularly useful if you have code inside a macro that functions independently of the current operating state, but does not affect the current operating mode. Note that you can use PUSHSTATE outside of macros. This can be useful for include files. The state information that Turbo Assembler saves consists of: • current emulation version (for example, T310) • mode selection (for example, IDEAL, MASM, QUIRKS, MASM51) • EMUL or NOEMUL switches • current processor or coprocessor selection • MULTERRS or NOMULTERRS switches • SMART or NOSMART switches • the current radix • JUMP? or NOJUMPS switches • LOCALS or NOLOCALS switches • the current local symbol prefix Use the POPSTATE directive to return to the last saved state from the stack. Here's an example of how to use PUSHSTATEand POPSTATE. ; PUSHSTATE and POPSTATE examples ideal model small codeseg 172 TurboA sse mbIer Use r's Guide
  • 182. jumps locals @@ Show changing processor selection, number radix, and JUMPS mode pushstate next1: nojumps radix p386 jl mov popstate next1 eax,100 Set to binary radix No extra NOPS after this Now 100 means binary 100 or 4 decimal. Restores JUMPS and non 386 mode. Back to jumps directive, no 386, and decimal radix jl next2 Three extra NOPS to handle JUMPS xor eax, eax mov cx,100 pushstate MULTERRS mov ax, [bp+abc popstate mov ax, [bp+abc Not in 386 mode anymore! Now 100 means decimal 100 i Show disabling local scoping of symbols locals next2: @@a: loop @@a next3: @@a: loop @@a Allowed because of scoping of NEXT2: and NEXT3: pushstate nolocals next4 : @@b: loop @@b next5: @@b: loop @@b This will conflict because of nolocals popstate i Show changing local symbol prefix and MASM/IDEAL mode pushstate masm locals @$ testproc proc jmp @$end @$end: nop @@end: ret testproc endp testproc2 proc jmp @$end MASM mode for procedure declaration @$end: nop This doesn't conflict with label in TESTPROC @@end: ret This label does conflict Chap ter 14, Us i n9 mac r0 s 173
  • 183. testproc2 endp popstate ; Now back to @@ as a local label prefix, arid IDEAL mode testproc2b proc This won't work since we are back in IDEAL mode! ret testproc2b endp proc testproc3 jmp @$end2 @$end2: nop @@end2: ret endp testproc3 proc testproc4 jmp @$end2 @$end2: nop' @@end2: ret endp testproc4 end 174 Turbo Assembler User's Guide And this will give an error also. This label does'conflict This label doesn't conflict with label in TESTPROC3
  • 184. Using conditional directives There are two classes of conditional directives: conditional assembly directives and conditional error-generation directives. With conditional assembly directives, you can control which code gets assembled in your program under certain conditions. Conditional error-generation directives let you generate an assembly-time error message if certain conditions occur. Turbo Assembler displays the error message on the screen and in the listing file, and it acts like any other error message in that it prevents the emission of an object file. This chapter describes how you can use the available conditional directives. General conditional directives syntax The three types of conditional assembly directives are IFxxx directives, ELSEIFxxx directives, and ERRxxx directives. Use these directives as you would conditional statements in high-level languages. IFxxx conditional assembly directives You can use IFxxx conditional assembly directives to define blocks of code that are included in the object file if certain conditions are met (such as whether a symbol is defined or set to a particular value). Here's the syntax of a conditional assembly statement: or IFxxx true_conditional_body ENDIF IFxxx true_conditional_body ELSE Chap t er 15, Us i n9 con dit ion aI dire ct ives 175
  • 185. false_conditional_body ENDIF Here, IFxxx represents any of the following <:onditional assembly directives: • IF • IFNB • IFl • IFIDN • IF2 • IFIDNI • IFDEF • IFDIF • IFNDEF • IFDIFI • IFB Each IFxxx conditional assembly directive specifies a specific condition that evaluates to either true or false. If the condition is true, the block of assembly code in true_conditionaCbody is assembled into the output object file. If the condition evaluates to false, Turbo Assembler skips over true_conditionaCbody and does not include it in the object file. If there is an ELSE directive, thefalse_conditionaCbody is assembled into the object file if the condition is false; it's ignored if the condition is true. All conditionals are terminated with an ENDIF directive. Note Except for the special cases of IFl and IF2 (which are discussed later), the two bodies of code are mutually exclusive: Either true_conditionaCbody will be included in the object file orfalse_conditionaCbody, but never both. Also, if you use the IFxxx...ELSE...ENDIF form, one of the two bodies will be included in the generated object file. If only the IFxxx...ENDIF form is used, true_conditionaCbody mayor may not be included, depending on the condition. . When you nest IFs and ELSEs, ELSE always pairs with the nearest preceding IF directive. In this example, test is a symbol that flags the inclusion of test code (if the symbol is defined, then test code is generated). color is a symbol set to nonzero if the display is color, or 0 for a monochrome display. The actual code generated depends on these values: IFDEF test itest code 1 IF color icolor code ELSE iffiono code ENDIF itest code 2 ELSE inon-test code ENDIF 176 Turbo Assembler User's Guide iT if test defined i if test defined iT if color <> 0 if color <> 0 if color = 0 if test defined if test not defined
  • 186. code: test code 1 mono code test code 2 test code 1 color code test code 2 non-test code non-test code Note If test is undefined, neither the color nor monochrome debug code based on the value of color is assembled, as this lies entirely within the conditional assembly for a defined test. ELSEIFxxx conditional assembly directives You can use the ELSEIFxxx as a shortcut where multiple IFs are required. ELSEIFxxx is equivalent to an ELSE followed by a nested IFxxx, but provides more compact code. For example, IF mode EQ 0 ;mode 0 code ELSEIF mode LT 5 imode 1-4 code ELSE imode 5+ code ENDIF compares to IF mode EQ 0 ;mode 0 cod~ ELSE IF mode LT 5 imode 1-4 code ELSE imode 5+ code ENDIF ENDIF You can't use the ELSEIFxxx directives outside of an IFxxx statement. ERRxxx error-generation directives ERRxxx directives generate user errors when certain conditions are met. These conditions are the same as for the IFxxx conditional assembly directives. Here's thegeneral syntax: ERRxxx [arguments] [message] Chapter 15, Using conditional directives 177
  • 187. In this case, ERRxxx represents any of the conditional error-generating directives (such as ERRIFB, .ERRB, and so on). arguments represents argwnents that the directive might require to evaluate its condition. Some directives require an expression, some require a symbol expression, and some require one or two text expressions. Other directives require no arguments at all. If message is included, it represents an optional message that's displayed along with the error. The message must be enclosed in single (') or double (") quotation marks. The error-generating directives generate a user error that is displayed onscreen and included in the listing file (if there is one) at the location of the directive in your code. If the directive specifies a m,essage,it displays on the same 1.ifle immediately following the error. For example, the directive . ERRIFNDEF foo "foonot defined!" generates the error User error: "foo not defined!" if the·symbolfaa is not defined when the directive is encountered. No error would be generated in this case iffaa were already defined. Specific directive descriptions Unconditional error-generation directives The unconditional error-generation directives are ERR and .ERR. These directives always generate an error and require no ar~ents, although they can have an optional message. You can,only use .ERR in MASM mode. Expression-conditional directives These directives provide conditional assembly or error generation based on the results of evaluating a Turbo Assembler expression. For all of these directives, the expression must evaluate to a constant and can't contain any forward references. If it evaluates to 0, Turbo Assembler considers the expression to be false; otherwise, it considers the expression to be true. The following table shows conditional assembly directives that use expressions. Table 15.1 Conditional assembly directives using expressions IF expression IFE expression ELSEIF expression ELSEIFE expression 178 Turbo Assembler User:s Guide . Expression evaluates to true. Expression evaluates to false. Expression evaluates to true. Expression evaluates to false.
  • 188. The following table shows the error-generation directives that use expressions. Table 15.2 Error-generation directives using expressions ~~~cijjte ERRIF expression .ERRNZ expression ERRIFE expression .ERRE expression Expression evaluates to true. Expression evaluates to true (MASM mode only). Expression evaluates to false. Expression evaluates to false (MASM mode only). Symbol-definition conditional directives These directives provide conditional assembly or error generation based on whether one or more symbols are defined. These symbols are organized into a symboCexpression. A symboCexpressionis an expression made up of symbol names, the Boolean operators AND, OR, and NOT, and parentheses. In a symboCexpression, each symbol name is treated as a Boolean value that evaluates to true if the symbol currently exists, or false if the symbol does not exist (even if it's defined later in the module). Turbo Assembler combines these values using the Boolean operators to produce a final true or false result. In its simplest form, a symbol expression consists of a single symbol name and evaluates to true if the symbol is defined. The parsing and syntax rules for symboCexpression are similar to those for other Turbo Assembler expressions. For example, if the symbolfoo is defined but the symbol bar is not, the following symbol- expression evaluations are returned: Table 15.3 Evaluation of defined and undefined symbol foo bar notfoo not bar foo OR bar foo AND bar NOT (foo AND bar) NOTfoo OR NOT bar True False False True True False True True (same as "(NOTfoo) OR (NOT bar)") The directives that control assembly and use symboCexpressions are shown in the following table. Table 15.4 Symbol-expression directives using symbol_expr IFDEF symboCexpr IFNDEF symboCexpr ELSEIFDEF symboCexpr ELSEIFNDEF symbol;,..expr symboCexpr evaluates to true. symboCexpr evaluates to false. symboCexpr evaluates to true. symboCexpr evaluates to false. Chap t er 15, Us i n9 con dit ion aI dire cti ves 179
  • 189. The error-generation directives that use symboCexpressions are shown in the following table. Table 15.5 Error-generation directives .......~.."u. ....,I ...... symboCexpr .ERRDEF symboCexpr ERRIFNDEF symboCexpr .ERRNDEF symboCexpr symboCexpr evaluates to true. symboCexpr evaluates to true (MASM mode only). symboCexpr evaluates to false. ' symboCexpr evaluates to false (MASM mode only). For example, the following error-generating conditionals are equivalent, and would generate an error only if bothfaa and bar are currently defined: ERRIFDEF foo AND bar ERRIFNDEF NOT ( foo AND bar ) ERRIFNDEF NOT foo OR NOT bar Text-string conditional directives These directives provide conditional assembly or error generationbased on the contents of text_string. A text_string can be either a string constant delineated by brackets « » or a text macro name preceded by a percent sign (%). For example, <ABC> %foo ; text string ABC ; the contents of text macro foo Note See Chapter 14 for information about how to define and manipulate text macros. The conditional assembly directives that use text_string are shown in the following table: Table 15.6 Conditional assembly directives using texCstrings IFNB txt_str txt-,-str is not blank. IFB txt_str txt_str is blank (empty). IFIDN txt_strl, txt_str2 IFIDNI txt_strl, txt_str2 IFDIF txt_strl, txt_str2 IFDIFI txt_strl, txt_str2 ELSEIFNB txt_str ELSEIFB txt_str ELSEIFIDN txt_strl, txt_str2 ELSEIFIDNI txt_strl, txt_str2 ELSEIFDIF txCstrl, txt_str2 ELSEIFDIFI txt_strl, txt_str2 180 TurboA sse mbIer Use r' s Guide txt_strl and txt,-str2 are identical text strings. txt_strl and txt_str2 are identical text strings, ignoring case distinctions. txt_strland txCstr2 are different text strings. txt_strl and txt_str2 are different text strings, ignoring case distinctions. txt_str is not blank. txt_str is blank (empty). txt~strl and txt_str2 are identical text strings. txt_strl and txt_str2 are identical text strings, ignoring case distinctions. txt_strl and txt_str2 are different text strings. txt_strl and txt_str2 are different text strings, ignoring case distinctions.
  • 190. The error-generation directives that use text_string are shown in Table 15.7: Table 15.7 Error-generation directives using texCstrings .ERRNB txt_str ERRIFB txt_str .ERRB txt_str ERRIFIDN txt_strl, txt_str2 .ERRIDN txt_strl, txt_str2 ERRIFIDNI txt_strl, txCstr2 .ERRIDNI txt_strl, txt_str2 ERRIFDIF txt_strl, txt_str2 .ERRDIF txt_strl, txt_str2 ERRIFDIFI txCstrl, txCstr2 .ERRDIFI txt_strl, txt_str2 txt_str is not blank. txt_str is not blank (MASM mode only). txCstr is blank (null). txt_str is blank (MASM mode only). .txt_strl and txt_str2 are identical text strings. txt_strl and txt_str2 are identical text strings (MASM mode only). txt_strl and txt_str2 are identical text strings, ignoring case distinctions. txt_strl and txt_str2 are identical text strings, ignoring case distinctions (MASM mode only). txt_strl and txt_str2 are different text strings. txt_strl ahd txt_str2 are different text strings (MASM mode only). txt_strl and txt_str2 are different text strings, ignoring case distinctions. txt_strl and txt_str2 are different text strings, ignoring case distinctions (MASM mode only). Use these directives to check the arguments passed to macros. (Note that they are not restricted to use within macros.) When used within a macro definition, IFB and IFNB can determine whether you've supplied the proper number of arguments to the macro. When invoking a macro, Turbo Assembler does not generate an error message if you've supplied too few arguments; instead, the unspecified arguments are blank. In this way, you can define a macro that may take arguments. For example, load MACRO addr,reg IFNB <reg> MOV reg,addr ELSE MOV aX,addr ENDIF ENDM You could invoke this example with load test, ex, which would generate a mov ex, test instruction (or invoke simply load test, which will generate a mov ax, test instruction because the second parameter is blank). Alternately, you could use ERRIFB to generate an error for a macro invocation with a missing critical argument. Thus, load MACRO addr ERRIFB <addr> MOV aX,addr ENDM Chap t er 15, Us in 9 con dit ion aI dire ct ives 181
  • 191. generates an error when invoked with load, but would not when invoked with load test. Assembler-pass conditionals These directives provide conditional assembly or error generation based on the current assembly pass: IFl IF2 ERRIFl .ERRl ERRIF2 .ERR2 Assembler pass 1 Assembler pass 2 Assembling pass 1 Assembling pass 1 (MASM mode only) Assembling pass 2 Assembling pass 2 (MASM mode only) Normally, Turbo Assembler acts as a single-pass assembler. If you use Turbo Assembler's multi-pass capability (invoked with the 1m command-line switch), multiple passes are used if necessary. Since there is always at least one pass through the assembler, the IF1 conditional assembly directive will always assemble the code in its conditional block, and the .ERR1 and ERRIF1 directives will always generate an error (but only during the first assembly pass). If you use any of these directives and have not enabled multiple pa~ses, Turbo Assembler will generate Pass dependent construction warnings for all of these directives to alert you to a potentially hazardous code omission. If you enable multiple passes, Turbo Assembler will perform exactly two passes, artdwill generate the warning Maximum compatibility pass was done Including conditionals in the list file Normally, false conditional assembly code is not included in a listing file. You can override this through the use of assembler directives and command-line switches. Note See Chapter 2 and Chapter 17 for further information on this subject. 182 TurboA sse mbIer Use r 's Guide
  • 192. Interfacing with the linker Modular programs are typically constructed from several independent sections of code, called modules. The compiler processes each of these modules independently, and the linker (TLINK) puts the resulting pieces together to create an executable file. The README file explains where you can find information about how to use TLINK, but it's also important to know how to define and include all the files and libraries you might want prior to linking. This chapter describes how to do these things. Publishing symbols externally You may find that you'll need to use some variables and procedures in all of your program modules. Turbo Assembler provides several directives that let you define symbols and libraries so that you can use them globally, as well as use communal variables (which the linker allocates space for). You'll also have to be careful about how you name your symbols, since different languages have particular requirements. The next few sections discuss these directives and naming requirements. Conventions for aparticular language When you name symbols that you plan to use externally, remember to use the language specifier'for your particular language. These requirements for variable names are: • Pascal • C/C++ uppercase characters name must start with _. Rest of name should be in lowercase characters Lname). When you specify a language in the MODEL directive or in the PROC declaration, or declare the language in a symbol's PUBLIC declaration, Turbo Assembler will automatically use the proper naming conventions for that language, as follows: Chap t er 16, In t erf acin9 wit h the lin ker 183
  • 193. • C, CPP, and PROLOG use the CjC++ naming conventions. • BASIC, PASCAL, FORTRAN, and NOLANGUAGE languages use the Pascal naming conventions. • SYSCALL specifies C calling conventions, but without prepending underscores to symbol names (like Pascal naming conventions). • .STDCALL uses C calling conventions for procedures with variable arguments, and Pascal calling conventions for procedures with fixed arguments. It always uses the C naming convention; The Iml switch (described in Chapter 2) tells Turbo Assembler to treat all symbol names as case sensitive. The/rnx switch (also described in Chapter 2) tells the assembler to treat only external and public symbols as case sensitive, and that all other symbols within the source file are uppercase. When you use these two switches together, they have a special meaning for symbols declared as Pascal: These switches cause the symbols in question to be published as all uppercase to the linker. Declaring public symbols When you declare a public symbol, you intend it to be accessible from other modules. The following types of symbols can be public: • data variable names • program labels • numeric constants defined with EQU You can use the PUBLIC directive to define public symbols. Its syntax follows: PUBLIC [language] symbol [, [language] symbol] '" Notice that in order to use public symbols outside the module where they're defined, you must use the EXTRN directive. language is either C, CPP, PASCAL, BASIC, FORTRAN, PROLOG, or NOLANGUAGE, and defines any language-specific conventions to be applied to the symbol name. Using a language in the PUBLIC directive temporarily overrides the current language setting (the default, NOLANGUAGE, or one that you've established with .MODEL). Turbo Assembler publishes symbol in the object file so that other modules can access it. If you don't make a symbol public, you can access it 0nly from the current source file; for example: . . . PUBLIC XYPROC XYPROC PROC NEAR Declaring library symbols ;make procedure public You can also use symbols as dynamic link entry points for a dynamic link library. Use the PUBLICDLL directive to declare symbols to be accessible this way. Here's its syntax: PUBLICDLL [language] symbol [, [language] symbol] ", 184 TurboA sse mbIer Use r' s Guide ,
  • 194. Turbo Assembler publishes symbol in the object file as a dynamic link entry point (using EXPDEF and IMPDEF records) so that it can be accessed by other programs. language causes any language-specific conventions to be applied to the symbol name. Valid language specifiers are C, PASCAL, BASIC, FORTRAN, PROLOG, and NOLANGUAGE. Here's an example of code using PUBLICDLL: PUBLICDLL XYPROC XYPROC PROC NEAR ;make procedure XYPROC ;accessible as dynamic link entry point Defining external symbols External symbols are symbols that are defined outside a module, that you can use within the module. These symbols must have been declared using the PUBLIC directive. EXTRN has the following syntax: EXTRN definition [,definition] ... definition describes a symbol and has the following format: [language] name [[countl]] :complex_type [:count2] Defining global symbols Global symbols function like public symbols, without your having to specify a PUBLIC or an EXTRN. If the variable is defined in the module, it functions like PUBLIC. If not, it functions like EXTRN. You can use the GLOBAL directive to define global symbols. GLOBAL has the same syntax as PUBLIC and EXTRN (see the previous few sections for syntax descriptions.) GLOBAL lets you have an INCLUDE file included by all source files; the INCLUDE file contains all shared data defined as global symbols. When you reference these data items in each module, the GLOBAL definition acts as an EXTRN directive, describing how the data is defined in another module. You must define a symbol as GLOBAL before you first use it elsewhere in your source file. Also note that each argument of GLOBAL accepts the same syntax as an argument ofEXTRN. Here's an example: GLOBAL X:WORD, Y:BYTE X DW 0 mov al, Y ;made public for other module ;Y is defined as external Publishing aprocedure prototype If you're using version T320 or later and you use PROCDESC to describe a procedure prototype, Turbo Assembler treats the procedure name as if it were a GLOBAL symbol. If you've defined the procedure within the module, it is treated as PUBLIC. Otherwise, Turbo Assembler assumes it to be EXTRN. Chap t er 16, In t erfa ci n9 wit h the lin ker 185
  • 195. You can place PROCDESC directives in an include file. When you reference the procedure name in the module, PROCDESC acts as an EXTRN directive, describing how the procedure is defined in another module. If the procedure is defined in the module,PROCDESC acts as a PUBLIC directive to publish the procedure. Defining communal variables Communal variables function like external variables, with a major difference: communal variables are allocated by the linker. Communal variables are actually like global variables, but you can't assign them initial values. These uninitialized variables can be referenced from multiple modules. ' One drawback to using communal variables is that there's no guarantee they'll appear in consecutive memory locations. If this is an issue for you, use global variables instead. You can use the COMM directive to define a communal ~ariable. Here's its syntax: COMM definition [, definition] ... Each definition describes a symbol and has the following format: [distance] [language] symbolname[[countll] :complex_type [:count2] distance is optional and can be either NEAR or FAR. Ifyou don't specify a distance, it will default to the size of the default data memory model. If you're not using the simplified segmentation directives, the default size is NEAR. With the tiny, small, and medium models, the default size is also NEAR; all other models are FAR. language is either C, PASCAL, BASIC; FORTRAN, PROLOG, or NOLANGUAGE. Using a language in the COMM directive temporarily overrides the currentlanguage setting (default or one established with .MODEL). Note that you don't need to have a .MODEL directive in effect to use this feature. symbolname is the symbol that is to be communal and have storage allocated at li1)k time. symbolname can also specify an array element size multiplier countl to be included in the total space computation. If distance is NEAR, the linker uses countl to calculate the total size of the array. If distance is FAR, the linker uses count2 to indicate how many elements there are of size countl timesthe.basic element size (determined by type). countl defaults to a value of l. complex_type is the data type of the argument. It can be either a simple type, or a complex pointer expression. See Chapter 5 for more information about the syntax of complex types. The optional count2 specifies how many items this communal symbol defines.If you do not specify a count2, a value of 1 is assumed. The total space allocated for the communal variable is count2 times the length specified by the type field times countl. In MASM mode, communal symbols declared outside of any segment are presumed to be reachable using the DS register, which may not always be avalid assumption. Make sure thatyou either place the correct segment value in DS or use an explicit segment override when referring to these variables. In Ideal mode, Turbo Assembler correctly checks for whether the commUnal variable is addressable, using any of the current s~gment registers as described with the ASSUME directive. 186 TurboA sse mbIe r Use r's Guide
  • 196. Here's an example using the COMM directive. COMM buffer:BYTE:512 COMM abc[41] :WORD:l0 COMM FAR abc[41] :WORD:l0 Including alibrary j512 bytes allocated at link time j820 bytes (10 items of 41 words jeach) allocated at link time jl0 elements of 82 bytes (2 bytes jtimes 41 elements) allocated at jlink time For the times when you know that your source file will always need to use routines in a specified library, you can use the INCLUDELIB directive. Using INCLUDELIB also prevents you from having to remember to specify the library name in the linker commands; INCLUDELIB tells the linker to include a particular library. The appropriate syntaxes for this directive are: Ideal mode: INCLUDELIB "filename" jnote the quotes! MASMmode: INCLUDELIB filename filename is the name of the library you want the linker to include at link time. If you don't supply an extension withfil(!name, the linker assumes .LIB. Here's an example: INCLUDELIB "diskio" jincludes DISKIO.LIB The ALIAS directive Turbo Assembler supports ALIAS to allow the association of an alias name with a substitute name. When the linker encounters an alias name, it resolves the alias by referring to the substitute name. Chap t er 16, In t erfa ci n9 wit h the lin ker .187
  • 197. 188 Turbo Assembler User's Guide
  • 198. Generating alisting A listing file is useful if you want to see exactly what Turbo Assembler generates when each instruction or directive is assembled. The file is basically the source file annotated with a variety of information about the results of the assembly. Turbo Assembler lists the actual machine code for each instruction, along with the offset in the current segment of the machine codefor each line. What's more, Turbo Assembler provides tables of information about the labels and segments used in the program, including the value and type of each label, and the attributes of each segment. For additional information on creating listings, refer to the /1 and /Ia command-line switches documented in Chapter 2. Turbo Assembler can also, on demand, generate a cross-reference table for all labels used in a source file, showing you where each label was defined and where it was referenced. See the Ie command-line option in Chapter 2 for more information on generating cross-reference tables. Listing format The top of each page of the listing file displays a header consisting of the version of Turbo Assembler that assembled the file, the date and time of assembly, and the page number within the listing. There are two parts to the listing file: the annotated source code listing and the symbol tables. The original assembly code is displayed first, with a header containing the name of the file where the source code resides. The assembler source code is annotated with information about the machine code Turbo Assembler assembled from it. Any errors or warnings encountered during assembly are inserted immediately following the line they occurred on. The code lines in the listing file follow this format: <depth> <line number> <offset> <machine code> <source> <depth> indicates the level of nesting of Include files and macros within your listing file. Chap t er 17, Genera tin 9 a lis tin 9 189
  • 199. <line number> is the number of the line in the listing file (not including header and title lines). Line numbers are particularly useful when the cross-:reference feature of Turbo Assembler, which refers to lines by line number, is used. Be aware that the line numbers in <line number> are not the source module line numbers. For example, if a macro is expanded or a file is included, the line-number field will continue to advance, even though the current line in the source module stays the same. To translate a line n.umber (for example, one that the cross-referencer produced) back to the source file, ypu must look up the line number in the listing file, and then find that same line (by eye, not by number) in the source file. <offset> is the offset in the current segment of the start of the ~achine code generated by the associated assembler source line. <machine code> is the actual sequence of hexadecimal byte and word values that is assembled from the associated assembler source line. <source> is simply the original assembler line, comments and all. Some assembler lines, suchas those that contain only comments, don't generate any machine code; these lines have no <offset> or <machine code> fields, but do have a line number. General list directives There are a variety of list directives that let you control what you want in your listing file. The general list directives follow: • .LIST ;MASM mode only • .XLIST ;MASM mode only • %LIST • %NOLIST • %CTLS • %NOCTLS • %SYMS • %NOSYMS The %LIST directive shows all of the source lines in your listing. This is the default condition when you create a listing file. To tum off the display of all the source lines, use the %NOLIST directive. Here's an example: %NOLIST INCLUDE MORE .INC %LIST iturn off listing iturn on listing The .LIST and .XLIST directives function the same way as %LIST and %NOLIST. Here's an example: .LIST jrnp xyz .XLIST add dx,ByteVar ithis line always listed inbt in listing You can use the%CTLS and %NOCTLS directives to control the listing directives. %CTLS causes listing control directives (such as %LIST, %INCL, and so on) to be 190 TurboA sse mbIer Use r' s Guide
  • 200. placed in the listing file; normally, they are not listed. It takes effect on all subsequent lines, so the %CTLS directive itself will not appear in the listing file. %NOCTLS reverses the effect of a previous %CTLS directive. After issuing %NOCTLS, all subsequent listing-control directives will not appear in the listing file. (%NOCTLS is the default listing-control mode that Turbo Assembler useswhen it starts assembling a source file.);for example, %CTLS %NOLIST %NOCTLS %LIST ithis will be in listing file ithis will not appear in listing You can .qse the %SYMS and %NOSYMS directives to cause the symbol table to either appear or not to appear in your' listing file (the default is for it to appear). The symbol table will appear at the end of the listing file. Here's the syntax for %SYMS: %SYMS Here's the syntax for %NOSYMS: %NOSYMS Include file list directives In the event that you might want to list the include files in your listing file, you can tum this capability on and off using the %INCL and %NOINCL directives. By default, . INCLUDE files are normally contained in the listing file. %NOINCL stops all subsequent INCLUDE files source lines from appearing inthe listing until a %INCL is enabled. This is useful ifyou have a large INCLUDE file that contains things such as a lot of EQU definitions that never change. Here's an example: %INCL INCLUDE DEFS.INC %NOINCL icontents appear in listing INCLUDE DEF1.INC icontents don't appear Conditional list directives When you have conditional blocks of code in your source files, you might not want all of that informationto appear in the listing file. Showing conditional blocks can be very helpful in some instances when you want to see exactly how your code is behaving. Turbo Assembler provides the following conditional list directives: • .LFCOND ;MASM mode only • .SFCOND ;MASM mode only • .TFCOND ;MASMmodeonly • %CONDS • %NOCONDS Chap t er 17, Genera tin 9 a lis tin 9 191
  • 201. Turbo Assembler does not usually list conditional blocks. The %CONDS directive displays all statements in conditional blocks in the listing file. This includes the listing offalse conditional blocks in assembly listings. The .LFCOND directive functions the sam,e as %CONDS. %NOCONDS prevents statements in false conditional blocks from appearing in the listing file. The .SFCONDS directive functions exactly the same as %NOCOND. If you want to toggle conditional block-listing mode, use the .TFCOND directive. The first .TFCOND that Turbo Assembler encounters enables a listing of conditional blocks. If you use the IX command-line option, conditional blocks start off being listed, and the first .TFCOND encountered disables listing them. Each time .TFCOND appears in_ the source file, the state of false conditional listings is reversed. To invoke any of these directives, place it by itself on a-line in your code. They will affect the conditional blocks that immediately follow them. Macro list directives Macro expansions are not normally included-in listing files. Having this information in listing files can be very helpful when you want to see what your code is :doing. Turbo Assembler provides several directives that let tum this feature on and off. They are: • .LALL ;MASM mode only • .SALL ;MASM mode only' • .XALL ;MASM mode only • %MACS • %NOMACS The %MACS directive enables the listing of macro expansions. The .LALL directive does the same thing, but only works in MASM mode. You can use these macros to toggle macro expansion in listings on. %MACS has the following syntax: %MACS You can specify .LALL as follows: .LALL If you want to suppress the listing of all statements in macro expansions, use either the %NOMACS or .sALL directives. Note that you can use these directives to toggle macro expansion in listings off. %NOMACS has the following syntax: %NOMACS You can specify .sALL as follows: .SALL The .XALL directive, which is only available in MASM mode, lets you list only the macro expansions that generate code or data..XALL has the syntax .XALL • 192 Turbo Assembler User's Guide
  • 202. Cross-reference list directives The symbol table portion of the listing file normally tells you a great deal about labels, groups, and segments, but there are two things it doesn't tell you: where labels, groups, and segments are defined, and where they're used. Cross-referenced symbol information makes it easier to find labels and follow program execution when debugging a program. There are several ways of enabling cross-referencing information in your listing file. You can tise Ie to produce cross-referencing information for an entire file (see Chapter 2 for details), or you can include directives in your code that let you enable and disable cross-referencing in selected portions of your listings. These directives are: • .CREF ;MASM mode only • .XCREF ;MASM mode only • %CREF • %NOCREF • %CREFALL • %CREFREF • %CREFUREF Turbo Assembler includes cross referencing information in the listing file. In addition, you can specify a .XRF file in your Turbo Assembler command to get a separate .XRF file. The %CREF and .CREF directives let you accumulate cross-reference information for all symbols encountered from that point forward in the source file. %CREF and .CREF reverse the effects of any %NOCREF or .XCREF directives, which inhibit the collection of cross-reference information. %CREF and .CREF have the following syntaxes: %CREF or .CREF %NOCREF and .XCREF have the following syntaxes: %NOCREF [symbol, ... J or .XCREF [symbol, ... J If you use %NOCREF or .XCREF alone withoutspecifying any symbols, cross- referencing is disabled completely. If you supply one or more symbol names, cross- referencing is disabled only for those symbols. The %CREFALL directive lists all symbols in the cross reference. %CREFALL reverses the effect of any previous %CREFREF (which disables listing of unreferenced symbols in the cross reference), or %CREFUREF (which lists only the unreferenced symbols in the cross reference). After issuing %CREFALL, all subsequent symbols in the source file Chap t er 17, Genera tin 9 a lis tin 9 193
  • 203. will appear in the cross-reference listing. This is the default mode that Turbo Assembler uses when assembling your source file. The syntax for %CREFALL, %CREFREF, and %CREFUREF follows: %CREFALL %CREFREF %CREFUREF Changing list format parameters The listing format control directives alter the format of the listing file. You can use these directives to tailor the appearance of the listing file to your tastes and needs. The PAGE directive sets the listing page height and width, and starts new pages. PAGE onlyworks in MASM mode. PAGE has the following syntax: PAGE [rows] Lcols] PAGE + rows specifies the number of lines that will appear on each listing page. The minimum is 10 and the maximum is 255. eols specifies the number of columns wide the page will be. The minimum width is 59; the maximum is 255. If you omit either rows or eols, the current setting for that parameter will remain unchanged. To change only the number of columns, precede the column width with a comma; otherwise, you'll end up changing the number of rows instead. If you follow the PAGE directive with a plus sign (+), a new page starts, the section number is incremented, and the page number restarts at 1. If you use PAGE with no arguments, the listing resumes on a new page, with no change in section number. The %PAGESIZE directive functions exactly like the PAGE directive, except that it doesn't start a new page and that it works in both MASM and Ideal modes. %PAGESIZE has the following syntax: %PAGESIZE [rows] [,cols] %NEWPAGE functions like PAGE, with no arguments. Source lines appearing after %NEWPAGE will begin at the start of a new page in the listingfile. %NEWPAGE has the following syntax: %NEWPAGE The %BIN directive sets the width of the object code field in the listing file. %BIN has the following syntax: %BIN size size is a constant. If you don't use this directive, the instruction opcode field takes up 20 columns in the listing file. For example, %BIN 12 ;set listing width to 12 columns 194 TurboA sse mbIe r Use r' s Guide
  • 204. %DEPTH sets the size of the depth field in the listing file. %DEPTH has the following syntax: %DEPTH width width specifies how many columns to reserve for the nesting depth field in the listing file. The depth field indicates the nesting level for INCLUDE files and macro expansions. If you specify a width of 0, this field does not appear in the listing file. Usually, you won't need to specify a width of more than 2, since that would display a depth of up to 99 without trun,cation. The default width for this field is 1 column. %LINUM sets the width of the line-number field in the listing file. %LINUM has the following syntax: %LINUM size %LINUM lets you set how many columns the line numbers take up in the listing file. size must be a constant. If you want to make your listing as narrow as possible, you can reduce the width of this field. Also, if your source file contains more than 9,999 lines, you can increase the width of this field so that the line numbers are not truncated. The default width for this field is 4 columns. %TRUNC truncates listing fields that are too long. %TRUNC has the following syntax: %TRUNC The object code field of the listing file has enough room to show the code emitted for most instructions and data allocations. You can adjust the width ofthis field with %BIN. If a single sofuce line emits more code than can be displayed on a single line, the rest is normally truncated and therefore notvisible. When you want to see all the code generated, use %NOTRUNC (which word-wraps too-long fields in the listing file). Otherwise, use %TRUNC. You can use these directives to toggle truncation on and off. %NOTRUNC has the following syntax: %NOTRUNC %PCNT sets the segment:offset field width in the listing file. %PCNT has the following syntax: %PCNT width where width is the number of columns you want to reserve for the offset within the current segment being assembled. Turbo Assembler sets the width to 4 for ordinary 16- bit segments and sets it to 8 for 32-bit segments used by the 386 processor. %PCNT overrides these defaults. The TITLE directive, which you can use only in MASM mode, sets the title in the listing file. TITLE has the following syntax: TITLE text The title text appears at the top of each page, after the name of the source file and before any subtitle set with the-SUBTTL directive. You can use TITLE as many times as you want. Chapter 17, Generating a listing 195
  • 205. %TITLE functions like TITLE, but you can use it for either MASM or Ideal mode. %TITLE has the following syntax: %TITLE II text" SUBTTL, which only works in MASM mode, sets the subtitle in the listing file. SUBTTL has the following syntax: SUBTTL text The subtitle appears at the top of eachpage, after the name of the source file, and after any title set with TITLE. You can place as many SUBTTL directives in your program as you wish. Each directive changes the subtitle that will appear at the top of the next listing page. %SUBTTL functions like SUBTTL, but it works in both MASM and Ideal modes. %SUBTTL has the following syntax: %SUBTTL "text" %TABSIZE sets the tab column width in the listing file. %TABSIZE has the following syntax: %TABSIZE width width is the number of columns between tabs in the listing file. The default tab column width is 8 columns. You can use the %TEXT directive to set the width of the source field in the listing file. It has the followingsyntax: %TEXT width width is the number of columns to use for source lines in the listing file. If the source line is longer than this field, it will either be truncated or wrapped to the following line, depending on whether you've used %TRUNC or %NOTRUNC. You can use the %PUSHLCTL directive to save the listing controls on a 16-level stack. It only saves the listing controls that can be enabled or disabled (%INCL, %NOINCL, and so on). The listing field widths are not saved. This directive is particularly useful in macros, where you can invoke special listing modes that disappear once the macro expansion terminates. %PUSHLCTL has the following syntax: %PUSHLCTL Conversely, the %POPLCTL directive recalls listing controls from the stack. Here's its syntax: %POPLCTL %POPLCTLresetsthe listing controls to the way they were when the last %PUSHLCTL directive was issued. None of the listing controls that set field width are restored (such as %DEPTH, %PCNT). 196 TurboA sse mbIer Use rJ s Guide
  • 206. Interfacing Turbo Assembler with Borland C++ While many programmers can-and do-develop entire programs in assembly language, many others prefer to do the bulk of their programming in a high-level language, dipping into assembly language only when low-level control or very high- performance code is required. Still others prefer to program primarily in assembler, taking occasional advantage of high-level language libraries and constructs. Borland C++ lends itself particularly well to supporting nuxed C++ and assembler code on an as-needed basis, providing not one but three mechanisms for integrating assembler and C++ code. The inline assembly feature of Borland C++ provides a quick and simple way to put assembler code directly into a C++ function. You can assemble the inline code with Turbo Assembler or use Borland C++'s built-in assembler. For further information about using in-line assembly in Borland C++ or the built-in assembler, see the Borland C++ Programmer's Guide. For those who prefer to do their assembler programming in separate modules written entirely in assembly language, Turbo Assembler modules can be assembled separately and linked to Borland C++ code. First, we'll discuss the details of linking separately assembled Turbo Assembler modules to Borland C++, and explore the process of calling Turbo Assembler functions from Borland C++ code. Then, we'll cover calling Borland C++ functions from Turbo Assembler code. Calling Turbo Assembler functions from Borland C++ C++ and assembler have traditionally been mixed by writing separate modules entirely in C++ or assembler, compiling the C++ modules and assembling the assembler modules, and then linking the separately compiled modules together. Borland C++ modules can readily be linked with Turbo Assembler modules in this fashion. Chap ter 18, In t erfa cin 9 TurboA sse mbIer wit h B0 rIand C++ 197
  • 207. The executable file is produced from mixed c++ and assembler source files. You start this cycle with bee filenaml.epp filenam2.asm This instructs Borland C++ to first compile FILENAMl.CPP to FILENAMl.OBJ~ then invoke Turbo Assembler to assemble FILENAM2.ASM to FILENAM2.0BJ, and finally invoke TLINK to link FILENAMl.OBJ and FILENAM~.OBJ into FILENAMl.EXE. Separate compilation is very useful for programs that have sizable amounts of assembler code, since it makes the full power of Turbo Assembler available and allows you to do.your assembly language programming in a pure assembler environment, without the asm keywords, extra compilation time, and C++-related overhead of inline assem~ly. There is a price to be paid for separate compilation: The assembler programmer must attend to all the details of interfacing C++ and assembler code. Where Borland C++ handles segment specification, parameter-passing, reference to C++ variables, register variable preservation, and the like for inline assembly, separately compiled assembler functions must explicitly do all that and more. There are two major aspects to interfacing Borland C++ and Turbo Assembler. First, the various parts of the C++ and assembler code must be linked together properly, and functions and variables in each part of the code must be made available to the rest of the code as needed. Second, the assembler code must properly handle C-style function calls. This includes accessing passed parameters, returning values, and following the register preservation rules required of C++ functions. Let's start by examining the rules for linking together Borland C++ and Turbo Assembler code. The framework In order to link Borland C++ and Turbo Assembler modules together, three things must happen: . • The Turbo Assembler modules must use a Borland C++-compatible segment-naming scheme. • The Borland C++ and Turbo Assembler modules must share appropriate function and variable names in a form acceptable to Borland C++. • TLINK must be used to combine the modules into an exe~utable program. This says nothing about what the Turbo Assembler modules actually do; at this point, we're only concerned with creating a framework within which C++-compatible Turbo Assembler functions can be written. Linking assembly language modules with C++ Type-safe linkage is an important concept in C++. The compiler and linker must work together to ensure function calls between modules use the correct argument types.· A process called name-mangling provides the necessary argument type information. 198 TurboA sse mbIer Use r' s Guide
  • 208. Name-mangling modifies the name of the function to indicate what arguments the function takes. When you build a program entirely in C++, name-mangling occurs automatically and transparently. However; when you write a module in assemblylanguageto be linked into a c++ program, you must be sure the assembler module contains mangled names. You can do this easily by writing a dummy function in C++ and compiling it to assembler. The .ASM file that Borland C++ generates will have the proper mangled names. You use these names when you write the real assembler module. For example, the following coq.e fragment defines four different versions of the function named test: void test () { } voidtest( int ) { } void test( int, int ) { } void test( float, double) { } Ifthe code is compiled using the -5 option, the compiler produces an assembly language output file (.ASM). This is how the output looks (edited to remove extraneous details): void test () @test$qv proc near push bp mov bp, sp pop bp ret @test$qv endp void test( int @test$qi proc near push bp mov bp, sp pop bp ret @test$qi endp void test( int, int ) @test$qii proc near push bp mov bp,sp pop bp ret @test$qii endp void test( float, double) Chapter 18, Interfacing Turbo Assembler with Borland C++ 199
  • 209. @test$qfd proc near push bp mov bp, sp pop bp ret @test$qfd endp Using Extern "e" to simplify linkage If you prefer, you can use unmangled names for your assembler functions, instead of trying to figure out what the mangled names would be. Using unmangled names will protect your assembler functions from possible future changes in the name-mangling algorithm. Borland C++ allows you to define standard C function names in your C++ programs. Look at this example: extern "e" { int add(int *a,int b) ; Any functions dedared within the braces will be given C style names. Here is the ~atching assembler procedure.definition. public _add _add proc Declaring an assembler function with an extern "c" block can save you the trouble of determining what the mangled names will be. Your code will be more readable, also. Memory models and segments For a given assembler function to be callable from C++, that function must use the same memory model as the C++ program and must use a C++-compatible code segment. Likewise, in order for data defined in an assembler module to be accessed by C++ code (or for C++ data to be accessed by assembler code), the assembler code must follow C++ data segment-naming conventions. Memory models and segment handling can be quite complex to implement in assembler. Fortunately, Turbo Assembler does virtually all the work of implementing Borland C++-compatible memory models and segments for you in the form of the simplified segment directives. Simplified segment directives and Borland C++ The .MODEL directive tells Turbo Assembler that segments created with the simplified segment directives should be compatible with the selected memory model (tiny, small, compact, medium, large, huge, or tchuge), and controls the default type (near or far) of procedures created with the PROC directive. Memory models defined with the .MODEL directive are compatible with the equivalently named Borland C++ models except that you should use Turbo Assembler's tchuge memory model when you want '" to support Borland C++'s huge memory model. (The huge memory modelis more appropriate for compatibility withother C compilers.) You should use theFARSTACK modifier with the .MODEL directive for large model, so the stack does not become a part of DGROUP. 200 TurboA sse mbIer Use r's Guide
  • 210. Finally, the .CODE, .DATA, .DATA?, .FARDATA, and .FARDATA? simplified segment directives generate Borland C++-compatible segments. (Don't use .DATA? orFARDATA? in huge model as they do not exist in Borland C++.) For example, consider the following Turbo Assembler module, named DOTOTAL.ASM: select Intel-convention segment ordering .MODEL small iselect small model (near code and data) .DATA iTC-compatibleinitialized data segment EXTRN _Repetitions:WORD iexternally defined PUBLIC _StartingValue iavailable to other modules _StartingValue DW 0 .DATA? iTC-compatibleuninitialized data segment RunningTotal .CODE PUBLIC _DoTotal mov mov mov TotalLoop: inc loop mov ret Do't'otal END DW iTC-compatible code segment DoTotal PROC ifunction (near-callable in small model) ex, [_Repetitions] i# of counts to do ax, [_StartingValue] [RunningTotal] ,ax iset initial value [Rumi.ingTotal] TotalLoop ax, [RunningTotal] ENDP iRunningTotaltt ireturn final total The assembler procedure _DoTotal is readily callable from a small-model Borland C++ program with the statement DoTotal()i Note that _DoTotal expects some other part of the program to define the external variable Repetitions. Similarly, the variable StartingValue is made public, so other portions ofthe program can access it. The following Borland C++ module, SHOWTOT.CPP, accesses public data in DOTOTAL.ASM and provides external data to DOTOTAL.ASM: #include <stdio.h> extern "C" int DoTotal(void) i extern int StartingValuei int Repetitionsi int main() { Repetitions =10i StartingValue = 2i printf("%dn", DoTotal()) i return 0i Chap t er 18, Int erf aci n9 TurboA sse mbIer wit h B0 rIand C++ 201
  • 211. StartingValue doesn't have to go in the Extern nCff block because variable names are not mangled. To create the executable program SHOWTOT.EXE from SHOWTOT.CPP and DOTOTAL.ASM, enter the command line bcc showtot.cpp dototal.asm If you wanted to link _DoTotal to a compact-model C++ program, you would simply change the .MODEL directive to .MODEL COMPACT. If you wanted to use a far segment in DOTOTAL.ASM, you could use the .FARDATA directive. In short, generating the correct segment ordering, memory model, and segment names for linking with Borland C++ is easy with the simplified segment directives. Old-style segment directives and Borland C++ Simply put, it's a nuisance interfacing Turbo Assembler code to C++ code using the old- style segment directives. For example, 1£ you replace the simplified segment directives in DOTOTAL.ASM with old-style segment directives, you get DGROUP GROUP _DATA,_BSS DATA SEGMENT WORD PUBLIC 'DATA' EXTRN _Repetitions:WORD PUBLIC _StartingValue -,-StartingValue DW 0 DATA ENDS BSS SEGMENT WORD PUBLIC 'BSS' RunningTotal DW? BSS ENDS TEXT SEGMENT BYTE PUBLIC 'CODE' iexternally defined iavailable to other modules ASSUME es:_TEXT,ds:DGROUP,ss:DGROUP PUBLIC DoTotal _DoTotal PROC mov ex, [_Repetitions] . mov ax, [_StartingValue] mov [RunningTotal],ax TotalLoop: inc [RunningTotal] loop TotalLoop mov ax, [RunningTotal] ret _DoTotal ENDP TEXT ENDS END ifunction (near-callable. i in small model) i# of eo.unts to do iset initial value iRunningTotal++ ireturn final total The version with old~style segment directives is not only longer, but also much harder to read and harder to change to match a different C++ memory model. When you're interfacing to.Borland C++, there's generally noadvantage to using the old-style segment directives. If you still want to use the old-style segment directives when interfacing to Borland C++j you'll have to identify the correct segments for the membry model your C+-:1- code uses. . 202 TurboA sse mbIer User .s Guide
  • 212. Note. The easy way to determine the appropriate old-style segment directives for linking with a given Borland C++ program is to compile the main module of the Borland C++ program in the desired memory model with the -5 option. This causes Borland C++ to generate an assembler version of the C++ code. In that C++ code, you'll find all the old- style segment directives used by Borland C++; just copy them into your assembler code. You can also find out what the appropriate old-style directives are by compiling as you normally would (without the -5 option) and then using TDUMP, a utility that comes with Turbo Assembler, to display all the segment definition records. Use this command line: tdump -OIsegdef module.obj Segment defaults: When is it necessary to load segments? Under some circumstances, your C++-callable assembler functions might have to load DS and/or ES in order to access data. It's also useful to know the relationships between the settings of the segment registers on a call from Borland C++, since sometimes assembler code can take advantage of the equivalence of two segment registers. Let's take a moment to examine the settings of the segment registers when an assembler function is called from Borland C++, the relationships between the segment registers, and the cases in which an assembler function might need to load one or more segment registers. On entry to an assembler function from Borland C++, the CS and DS registers have the following settings, depending on the memory model in use.(SS is always used for the stack segment, and ES is always used as a scratch segment register): Table 18.1 Register settings when Borland C++ enters assembler Tiny _TEXT DGROUP Small _TEXT DGROUP Compact _TEXT DGROUP Medium filename_TEXT DGROUP Large filename_TEXT DGROUP Huge filename_TEXT callingJilename_DATA filenameis the name of the assembler module, and callingJilename is the name. of the module calling the assembler module. In the tiny model, _TEXT and DGROUP are the same, so CS equals DS on entry to functions. Also in the tiny, small, and medium models, SS equals DS on entry to functions. , So, when is it necessary to load a segment register in a C++-callable assembler function? First, you should never have to (or wantto) directly load the CS or SS registers. CS is automatically set as needed on far calls, jumps, and returns, and can't be tampered with otherwise, SS always points to the stack segment, which should never change during the course of a program (unless you're writing code that switches stacks, in which case you had best know exactly what you're doing). Chap ter 18, Interf aci n9 TurboA ssembIer wit h B0 rIand C++ 203
  • 213. ES is always available for you to use as you wish. You can use ES to point at far data, or you can load ES with the destination segment for a string instruction. That leaves the DS register; in all Borland C++ models other than the huge model, DS points to the static data segment (DGROUP) on entry to functions, and that's generally where you'll want to"leave it. You can always use ES to access far data, although you may find it desirable to instead temporarily point DS to far data that you're going to access intensively, thereby saving many segment override instructions in your code. For example, you could access a far segment in either of the following ways: or .FARDATA Counter DW 0 .CODE PUBLIC _AsmFunction AsmFunction PROC mov aX,@fardata mov . inc eS,ax es: [Counter] AsmFunction ENDP .FARDATA Counter DW 0 .CODE PUBLIC AsmFunction - AsmFunction PROC - ASSUME ds:@fardata mov aX,@fardata mov ds,ax inc [Counter] ASSUME ds:@data mov aX,@data mQv ds,ax _AsmFunction ENDP ipoint ES to far data segment iincrement counter variable ipoint DS to far data segment iincrement counter variable ipoint DS back to DGROUP The second version has the advantage of not requiring an ES: override on each memory access to the far data segment. If you do load DS to point to a far segment, be sure to restore it like in the preceding example before attempting to access any variables in DGROUP. Even if you don't access DGROUP in a given assembler function, be sure to restore DS before exiting since Borland C++ assumes that' functions leave OS unchanged. 204 TurboAs sembIer Use r's Guide
  • 214. Handling DS in C++-callable huge model functions is a bit different. In the huge model, Borland C++ doesn't use DGROUP at all. Instead, each module has its own data segment, which is a far segment relative to all the other modules in the program; there is 'no commonly shared near data segment. On entry to a function in the huge model, DS should be set to point to that module's far segment and left there for the remainder of the function, as follows: .FARDATA .CODE PUBLIC_AsmFunction AsmFunction PROC push ds mov aX,@fardata mov ds,ax pop ds ret _AsmFunction ENDP Note that the original state of DS is preserved with a PUSH on entry to AsmFunction and restored with a POP before exiting; even in the huge model, Borland C++ requires all functions to preserve DS. Publics and externals Turbo Assembler code can call C++ functions and reference external C++ variables. Borland C++ code can likewise call public Turbo Assembler functions and reference public Turbo Assembler variables. Once Borland C++-compatible segments are set up in Turbo Assembler, as described in the preceding sections, only the following few simple rules are necessary to share functions and variables between Borland C++ and Turbo Assembler. Underscores and the Clanguage If you are programming in C or C++, all external labels should start with an underscore character C). The C and C++ compilers automatically prefix an underscore to all function and external variable names when they're used iri C/C++ code, so you only need to attend to underscores in your assembler code. You must be sure that all assembler references to C and C++ functions and variables begin with underscores, and you must begin all assembler functions and variables that are made public and referenced by C/C++ code with underscores. For example, the following C code (link2asm.cpp), int ToggleFlag() i int Flagi " main () { ToggleFlag(); Chap t er 18, Int e.r f aci n9 TurboA sse mbIer wit h B0 rIan d C++ 205
  • 215. links properly with the following assembler program (CASMLINK.ASM): .MODEL small .DATA EXTRN _Flag:WORD .CODE PUBLIC _ToggleFlag _ToggleFlag PROC cmp []lag] ,0 jz SetTheFlag mov []lag], 0 jmp short EndToggleFlag SetTheFlag: mov' [_Flag],l' EndToggleFlag: ret _ToggleFlag ENDP END ;is the flag reset? ;yes, set it ;no, reset it ;done ;set flag Note Labels not referenced by C code, such as SetTheFlag, don't need leading underscores. When you use the C language specifier in your EXTRN and PUBLIC directives, as in the following program (CSPEC.ASM), .MODEL small .DATA EXTRN C Flag:word .CODE PUBLIC C ToggleFlag ToggleFlag PROC cmp [Flag] ,0 jz SetTheFlag mov [Flag] , 0 jmp short EndToggleFlag SetTheFlag: mov [Flag], 1 EndToggleFlag: ret ToggleFlag ENDP END Turbo Assembler c';luses the underscores to be prefixed automatically when Flag and ToggleFlagare published in the object module. The Significance of uppercase and lowercase Turbo Assembler is normally insensitive to case when handling symbolic names, making no distinction between uppercase and lowercase letters. Since C++ is case- sensitive, it's desirable to have Turbo Assembler be case-sensitive, at least for those symbols that are shared between assembler and C+-i::; Iml and Imx make this possible. The Iml command-line switch causes Turbo Assembler to become case-sensitive for all symbols. The Imx command-line switch causes Turbo Assembler to become case- . sensitive for public (PUBLIC), external (EXTRN), global (GLOBAL), and communal 206 TurboA sse mbIer Use r 's Guide
  • 216. (COMM) symbols only. When Borland C++ calls Turbo Assembler, it uses the Iml switch. Most of the time you should use Iml also. Label types While assembler programs are free to access any variable as data of any size (8 bit, 16 bit, 32 bit, and so on), it is generally a good idea to access variables in their native size. For instance, it usually causes problems if you write a word to a byte variable: SmallCount DB mov WORD PTR [SmallCount] ,Offffh Consequently, it's important that your assembler EXTRN statements that declare external C++ variables specify the right size for those variables, since Turbo Assembler has only your declaration to go by when deciding what size access to generate to a C++ variable. Given the statement char c in a C++ program, the assembler code EXTRN c:WORD inc [c] could lead to problems, since every 256th time the assembler codeincremented c, c would tum over. And, since c is erroneously declared as a word variable, the byte at OFFSET c + 1 is incorrectly incremented, and with unpredictable results. Correspondence between C++ and assembler data types is as follows: unsigned char byte char byte enum word unsigned short word short word unsigned int word int word unsigned long dword long dword float dword double qword long double tbyte near * word far * dword Chap t er 18, Int erf aci n9 TurboA sse mbIer wit h 8 0 rIand C++ 207
  • 217. Far externals If you're using the simplified segment directives, EXTRN declarations of symbols in far segments must not be placed within any segment, since Turbo Assembler considers symbols declared within a given segment to be associated with that segment. This has its drawbacks: Turbo Assembler cannot check the addressability of symbols declared EXTRN outside any segment,and so can neither generate segment overrides as needed nor inform you when you attempt to access that variable when the correct segment is notloaded. Turbo Assembler still assembles the correct code for references to such external symbols, but can no longer provide the normal degree of segment addressability checking. You can use the old-style segment directives to explicitly declare the segment each external symbol is in, and then place the EXTRN directive for that symbol inside the segment declaration. This is a lot of work, however; if you make sure that the correct segment is loaded when you access far data, it's easiest to just put EXTRN declarations of far symbols outside all segments. For example, suppose that FILEl.ASM contains .FARDATA FilelVariable DB Then if FILEl.ASM is linked to FILE2.ASM, which contains .DATA EXTRN FilelVariable:BYTE .CODE Start PROC mav mav ax/SEG FilelVariable ds/ax SEG FilelVariable will not return the correct segment. The EXTRN directive is placed within the scope of the DATA directive of FILE2.ASM, so Turbo Assembler considers FilelVariable to be in the near DATA segment of FILE2.ASM rather than in the FARDATA segment. The following code for FILE2.ASM allows SEG FilelVariable to return the correct segment: .DATA @curseg ENDS EXTRN FilelVariable:BYTE .CODE Start PROC mav ax/SEG FilelVariable mav dsiax Here, the @cursegENDS directive ends the .DATA segment, so no segment directive is ,in effect when FilelVariable is declared external. 208 'T urboA ssemb Ier Use r 's Guide
  • 218. Linker command line The simplest way to link Borland C++ modules with Turbo Assembler modules is to enter a single Borland C++ command line and let Borland C++ do all the work. Given the proper command line, Borland C++ will compile the C++ code, invoke Turbo Assembler to do the assembling, and invoke TLINK to link the object files into an executable file. Suppose, for example, that you have a program consisting of the C++ files MAIN.CPP andSTAT.CPP and the assembler files SUMM.ASM and DISPLAY.ASM. The command line bee main.cpp stat.epp summ.asm display.asm compiles MAIN.CPP and STAT.CPP, assembles SUMM.ASM and DISPLAY.ASM, and links all four object files, along with the C++ start-up code and any required library functions, into MAIN.EXE. You only need remember the .ASM extensions when typing your assembler file names. If you use TLINK in stand-alone mode, the object files generated by Turbo Assembler are standard object modules and are treated just like C++ object modules. See Appendix C for more information about using TLINK in stand-alone mode. Parameter passing Borland C++ passes parameters to functions on the stack. Before calling a function, Borland C++ first pushes the parameters to that function onto the stack, starting with the right-most parameter and ending with the left-most parameter. The C++ function call Test(L j, 1); compiles to mov ax,l push ax push WORD PTR DGROUP:_j push WORD PTR DGROUP:_i call NEAR PTR _Test add sp,6 in which you can clearly see the right-most parameter, I, being pushed first, then j, and finally i. Upon return from a function, the parameters that were pushed on the stack are still there, but are no longer useful. Consequently, immediately following each function call, Borland C++ adjusts the stack pointer back to the value it contained before the parameters were pushed, thereby discarding the parameters. In the previous example, the threeparameters of 2 bytes each take up 6bytes of stack space altogether, so Borland C++ adds 6 to the stack pointer to discard the parameters after the call to Test. The important point here is that under the default C/C++ calling conventions, the calling code is responsible for discarding the parameters from the stack. Chap t er 18, Int erf aci n9 TurboA sse mbIer wit h B0 rIand C++ 209
  • 219. Assembler functions can access parameters passed on the stack relative to the BP register. For example, suppose the function Test in the previous example is the following assembler function, called·PRMSTACK.ASM: .MODEL small .CODE PUBLIC _Test Test PROC push mov mov add sub pop ret Test ENDP END bp bp,sp ax, [bp+4] ax, [bp+6] ax, [bp+8] bp iget parameter 1 iadd parameter 2 to parameter 1 isubtract parameter 3 from sum You can see that Test is getting the parameters passed by the C++ code from the stack, relative to BP. (Remember that BP addresses the stack segment.) Butjust how are you to know where to find the parameters relative to BP? i = 25i j = 4i Test (i, j, 1) i The parameters to Test are at fixed locations relative to SP, starting at the stack location 2 bytes higher than the location of the·retum address that was pushed by the call. After, loading BP with SP, you can access the parameters relative to BP. However, you must 'first preserve BP, since the calling C++ code expects you to return with BP unchanged. Pushing BP changes all the offsets on the stack. push bp mov bp,sp This is the standard C++ stack frame, the organization of a function's parameters and automatic variables on the stack. As you can see, no matter how many parameters a C++ program might have; the left-most parameter is always stored at the stack address immediately above the pushed return address, the next parameter to the right is stored just above the left-most parameter, and so on. As long as you know the order and type of the passed parameters, you always know where to find them on the stack. Space for automatic variables can be reserved by subtracting the required number of bytes from SP. For example, room for a IOO-byte automatic array could be reserved by starting Test with - push bp mov bp,sp sub 'sp, 100 210 TurboA sse mbIer Use r' s Guide
  • 220. Since the portion of the stack holding automatic variables is at a lower address than BP, negative offsets from BP are used to address automatic variables. For example, mov BYTE PTR [bp-100],O would set the first byte of the lOO-byte array you reserved earlier to zero. Passed parameters, on the other hand, are always addressed at positive offsets from BP. While you can, if you wish, allocate space for automatic variables as shown previously, Turbo Assembler provides a special version of the LOCAL directive that makes allocation and naming of automatic variables a snap. When LOCAL is encountered within a procedure, it is assumed to define automatic variables for that procedure. For example, LOCAL LocalArray:BYTE:1oo,LocalCount:WORD = AUTO_SIZE defines the automatic variables LocalArray and LocalCount. LocalArray is actually a label equated to [BP-100], and LocalCount is actually a label equated to [BP-102], but you can use them as variable names without ever needing to know their values. AUTO_SIZE is the total number of bytes of automatic storage required; you must subtract this value from SP in order to allocate space for the automatic variables. Here's how you might use LOCAL: TestSub PROC LOCAL LocalArray: BYTE: 100, LocalCount:WORD=AUTO_SIZE push bp mov . bp, sp sub sp,AUTO_SIZE mov [LocalCount] ,10 mov ex, [LocalCount] mov aI,' A' lea bx, [LocalArray] FillLOOp: mov [bx],al inc bx loop FillLoop mov sp,bp pop bp ret TestSub ENDP ipreserve caller's stack frame pointer iset up our own stack frame pointer iallocate room for automatic variables iset local count variable to 10 i (LocalCount is actually [BP-102]) iget count from local variable iwe'll fill with character "A" ipoint to local array i (LocalArray is actually [BP-100]) ifill next byte ipoint to following byte ido next byte, if any ideallocate storage for automatic i variables (add sp,AUTO_SIZE would i also have worked) irestore caller's stack frame pointer In this example, note that the first field after the definition of a given automatic variable is the data type of the variable: BYTE, WORD, DWORD, NEAR, and so on. The second field after the definition of a given automatic variable is the number of elements of that variable's type to reserve for that variable. This field is optional and definesan automatic array if used; if it is omitted, one element of the specified type is reserved. Chap ter 18, In terf acin9 TurboA sse mbIer wit h B0 rIand C++ 211
  • 221. Consequently, LocalArray consists of 100 byte,-sized elements, while LocalCount consists of 1 word-sized element. Also note that the LOCAL line in the preceding example ends with =AUTO_SIZE. This field, beginning with an equal sign, is optional; ifpresent, it sets the label following the equal signto the number of bytes of automatic storage required. You must then use that label to allocate and deallocate storage for automatic variables, since the LOCAL directive only generates labels, and doesn't actually generate any code or data storage. To put this another way: LOCAL doesn't allocate automatic variables, but simply generates labels thatyou can readily use to both allocate storage for and access automatic variables. As you can see, LOCAL makes it much easier to define and use automatic variables. Note that the LOCAL directive has a completely different meaning when used in macros. By the way, Borland C++ handles stack frames in just the way we've described here. You might find it instructive to compile a few Borland C++ modules with the -5 option, and then look at the assembler code Borland C++ generates to see how Borland C++ creates and uses stack frames. This looks good so far, but there are further complications. First of all, this business of accessing parameters at constantoffsets from BP is a nuisance; not only is it easy to make mistakes, but if you add another parameter, all the other stack frame offsets in the function must be changed. For example, suppose you change Test to accept four parameters: Test (Flag, i, j, 1); Suddenly i is at offset 6, not offset 4, j is at offset 8/not offset 6, and so on. You can use equates for the parameter offsets: Flag EQU AddParm1 EQU AddParm2 EQU SubParm1 EQU 10 mav ax, [bp+AddParm1] add ax," [bp+AddParm2] sub ax, [bp+SubParm1] but it's still a nuisance to calculate the offsets and maintain them. There's a more serious problem, too: Thesize ofthe pushed return address grows by 2bytes in far code models, as do the sizes of passed code pointers and data pointer in far code and far data models, respectively. Writing a function that Cat} be easily assembled to access the stack frame properly in any memory model would thus seem to be a difficult task. Turbo Assembler, however, provides you with the ARG directive, which makes it easy to handle passed parameters in your assembler routines. 212 TurboA sse mbIe r Use r' s Guide
  • 222. The ARG directive automatically generates the correct stack offsets for the variables you specify. For example, arg FillArray:WORD,Count:WORD,FillValue:BYTE specifies three parameters: FillArray, a word-sized parameter; Count, a word-sized parameter, and FillValue, a byte-sized parameter. ARG actually sets the label FillArray to [BP+4] (assuming the example code resides in a near procedure), the label Count to [BP+6], and the label FillValue to [BP+S]. However, ARG is valuable precisely because you can use ARG-defined labels without ever knowing the values they're set to. For example, suppose you've got a function FillSub, called from c++ as follows: extern "C" { void FillSub( main() { char *FillArray, int Count, char FillValue) i const int ARRAY_LENGTH=100i char TestArray[ARRAY_LENGTH]i FillSub(TestArray,ARRAY_LENGTH, '*') i You could use ARGin FillSub to handle the parameters as follows: FillSub PROC NEAR ARG FillArray:WORD,Count:WORD,FillValue:BYTE push bp ipreserve caller's stack frame mov bp,sp iset our own stack frame mov bx, [FillArray] iget pointer to array to fill mov cx, [Count] iget length to fill mov al, [F{llValue] iget value to fill with FillLoop: mov [bx] ,al inc bx loop FillLaop pop bp ret FillSub ENDP ifill a character ipoint to next character ida next character irestore caller's stack frame That's really all it takes to handle passed parameters with ARG. Better yet, ARG automatically accounts for the different sizes of near and far returns. Preserving registers . As far as Borland C++- is concerned, C++-callable assembler functions can do anything as long as they preserve the following registers: BP, SP, CS, DS, and SS. While these registers c?Jl- be altered during the course of an assembler function, when the calling code is returned, they must be exactly as they were when the assembl~r function was called. AX, BX, CX, DX,ES, and the flags can be changed in any way. Chap t er 18, In t erfa cin 9 TurboA sse mbIe r wit h B0 rIan d C++ 213
  • 223. S1 and D1 are special cases, since they're used by Borland C++ as register variables. If register variables are enabled in the C++ module calling your assembler function, you must preserve S1 and D1; but if register variables are not enabled, S1 and D1 need not be preserved. ' It's good practice to always preserve S1 and D1 in your C++-callable assembler functions, regardless of whether register variables are enabled. You never know when you might link a given assembler module to a different C++ module, or recompile your C++ code with register variables enabled, without remembering that your assembler code needs to be changed as well. Returning values A C++-callable assembler function can return a value, just like a C++ function. Function values are returned as follows: unsigned char char enum unsigned short short unsigned int int unsigned long long float double long double near * far * _ AX AX AX AX AX AX AX DX:AX DX:AX 8087 top-of-stack (TOS) register (ST(O» 8087 top-of-stack (TOS) register (ST(O) 8087 top-of-stack (TOS) register (ST(O» AX DX:AX In general, 8- and 16-bit values are returned in AX, and 32-bit values are returned in DX:AX, with the high 16 bits of the value in DX. Floating-point values are returned in ST(O), which is the 8087's top-of-stack (TOS) register, or in the 8087 emulator's TOS register if the floating-point emulator is being used. - Structures are a bit more complex. Structures that are 1 or 2 bytes in length are returned in AX, and structures that are 4 bytes in length are returned in DX:AX. When a function that returns a three-byte structure or astructure larger than 4 bytes is called, the caller must allocate space for the return value (usually on the stack), and pass the address of this space to the function as an additional Jihidden" parameter. The function assigns the return value through this pointer argument, and returns that pointer as its result. As with aUpointers, near pointers to structures are returned in AX, and far pointers to structures are returned in DX:AX. 214 Turbo Assembler User's Guide
  • 224. Let's look at a small model C++-callable assembler function, FindLastChar, that returns a near pointer to the last character of a passed string. The C++ prototype for this function would be extern char * FindLastChar(char * StringToScan)i where StringToScan is the non-empty string for which a pointer to the last character is to be returned. Here's FindLastChar, from FINDCHAR.ASM: .MODEL small .CODE PUBLIC FindLastChar . FindLastChar PROC ARG StringToScan:WORD push bp mov bp, sp, cld iwe need string instructions to count up mov mov mov mov mov repnz dec dec mov pop ret aX,ds eS,ax iset ES to point to the near data segment di, [StringToScan] ipoint ES:DI to start of ipassed string al,O isearch for the null that ends the string ,cx, Offffh isearch up to 64K-l bytes ~casb ;look for the null di ipoint back to the null di ipoint back to the last character aX,di ireturn the near pointer in AX bp FindLastChar END ENDP The final result, the nearpointer to the last character in the passed string, is returned in AX. Calling an assembler function from C++ Now look at an example of Borland C++ code calling a Turbo Assembler function. The following Turbo Assembler module, COUNT.ASM, contains the function LineCount, which returns counts of the number of lines and characters in a passed string: Small model C++-callable assembler function to count the number of lines and characters in a zero-terminated'string. Function prototype: Input: extern unsigned int LineCount(char * near StringToCount, unsigned int near * CharacterCountptr) i char near * StringToCount:pointer to the string on which a line count is to be performed Chap ter 18, In t erfa ci n9 TurboA sse mbIer wit h B0 rIand C++ 215
  • 225. unsigned int near * CharacterCountPtr: pointer to the int variable in which the character count is to be stored NEWLINE EQU .MODEL .CODE PUBLIC LineCount push mov push mov sub mov LineCountLoop: lodsb and jz inc cmp jnz inc jmp EndLineCount: inc mov mov mov pop Oah small LineCount PROC bp bp, sp si si, [bp+4] cx, cx dx,cx al,al EndLineCount cx al,NEWLINE LineCountLoop dx LineCountLoop dx bx, [bp+6] pop bp ret LineCount ENDP END ithe linefeed character is C's inewline character ipreserve calling program's i register variable, if any ipoint SI to the string iset character count to 0 ;set line count to a iget the next character iis it null, to end the string? iyes, we're done ino, count another character iis it a newline? ino, check the next character iyes, count another line icount the line that ends with the i null character ipoint to the location at which to i return the character count iset the character count variable ireturn line count as function value irestore calling program's register i variable, if any The following C++ module, CALLCT.CPP, is a sample invocation of the LineCount function: . #include <stdio.h> char ~ TestString="Line lnline 2nline3"i extern "CO unsigned int LineCount(char * StringToCount, int main () { unsigned int LCounti unsigned int CCount; unsigned int * CharacterCountPtr)i LCount = LineCount(TestString, &CCount) i 216 Turbo Assembler User's Guide
  • 226. printf("Lines: %dnCharacters: %dn", LCount, CCount); return 0; The two modules are compiled and linked together with the command line bcc -ms callct.cpp count.asm As shown here, LineCount will work only when linked to small-model C++ programs since pointer sizes and locations on the stack frame change in other models. Here's a version of LineCount, COUNTLG.ASM, that will work with large-model C++ programs (but not small-model ones, unless far pointers are passed, and LineCount is declared far): Large model C++-callable assembler function to count the number of lines and characters in a zero-terminated string. Function prototype: extern unsigned int LineCount(char * far StringToCount, unsigned int * far CharacterCountPtr); char far * StringToCount: pointer to the string on which a line count is to be performed unsigned int far * CharacterCountptr: pointer to the int variable in which the character count is to be stored NEWLINE EQU Oah ;the linefeed character is C's newline ; character .MODEL large .CODE PUBLIC LineCount push mov push push lds sub mov LineCountLoop: lodsb and jz inc cmp jnz inc jmp EndLineCount: inc les LineCount PROC bp bp,sp si ds si, [bp+6] cx, cx dx,cx al,al EndLineCount cx al,NEWLINE LineCountLoop dx LineCountLoop dx bx, [bp+l0] ;preserve calling program's ; register variable, if any ;preserve C's standard data seg ;point DS:SI to the string ;set character count to 0 ;set line count to 0 ;get the next character ;is it null, to end the string? ;yes; we're done ;no, count another character ;is it a newline? ;no, check the next character iyes, count another line icount line ending with null i character ;point ES:BX to the location at i which to return char count Chapt er 18, I nterfa ci n9 TurboA sse mbIer wit h B0 rIand C++ 217
  • 227. mov es: [bx] ,ex ;set the char count variable mov ax,dx ireturn the line count as ; the function value pop ds irestore C's standard data seg pop si irestore calling program's i register variable, if any pop bp ret LineCount ENDP END COUNTLG.ASM can be linked to CALLCT.CPP with the following command line: bcc -ml callct.cpp countlg.asm Writing C++ member functions in assembly language While you can write a member function of a C++ class completely in assembly language, it is not easy. For example, all member functions of C++ classes are name- mangled to provide the type-safe linkage that makes thingslike overridden functions available, and your assembler function would have to know exactly what name C++ would be expecting for the member function. To access the member variables you must prepare a STRUC definition in your assembler code that defines all the member variables with exactly the same sizes and locations. If your class is a derived class, there may be other membet variables derived from a base class. Even if your class is not a descendant of another class, the location of meIJ;lber variables in memory changes if the class includes any virtual functions. If you write your function using inline assembler, Borland C++ can take care of these issues for you. But if you must write your function in assembly language, (perhaps because you are reusing some existing assembler code), there are some special techniques'you can use to make things easier. Create a dummy stub C++ function definition for the assembler function. This stub will satisfy the linker because it will have a properly mangled name for the member function. The dummy stub then calls your assembler function and passes to it the member variables and other parameters. Since your assembler code has all the parameters it needs passed as arguments, you don't have to worry about changes in the class definition. Your assembler function can be declared in the C++ code as an extern "C" function, just as we have shown you in otherexamples. Note For an example of how to write assembly functions using mangled names, see the example on page 199. Here's an example, called COUNTER.CPP: #include <stdio.h> class counter { II Private member variables: int counti II The ongoing count public: counter (void) { count=Oi } int get_count (void) {return count;} 218 TurboA sse mbIer Use r' s Guide
  • 228. }; II Two functions that will actually be written II in assembler: void increment (void) ; void add(int what_to_add=-l); II Note that the default value only II affects calls to add, it does not II affect the code for add. extern "C" { II To create some unique, meaningful names for the II assembler routines, prepend the name of the class II to the assembler routine. Unlike some assemblers, II Turbo Assembler has no problem with long names. void counter_increment (int *count); II We will pass a II pointer to the II count variable. II Assembler will II do the incrementing. void counter_add(int *count,int what_to_add); } void counter::increment(void) counter_increment (&count) ; void counter::add(int what_to_add) counter_add(&count, what_to_add); int main() { counter Counter; printf( "Before count: %dn", Counter.get_count()); Counter.increment() ; Counter.add( 5 ); printf( "After count: %dn", Counter.get_count()); return 0; Your assembler module that defines the count_add_increment and count_add_add routines could look like this example, called COUNTADD.AISM: .MODEL small .CODE ; Select small model (near code and data) PUBLIC counter increment counter_increment PROC ARG count.offset:word push bp mov bp,sp mov bx, [count_offset] inc word ptr [bxl pop bp ret counter_increment ENDP Address of the member variable Preserve caller's stack frame Set our own stack frame , Load pointer Increment member variable Restore callers stack frame Chap t er 18, In t erfa cin 9 TurboA sse mbIer wit h B0 r Iand C+ + 219
  • 229. counter_add PROC ARG count_offset:word,what_to_add:word push bp mov bp, sp mov bx, [count_offset] Load pointer mov ax, [what_to_add] add [bx] ,ax . pop bp ret counter_add ENDP end Using this method, you don't have to worry about changes in your class definition. Even if you add or delete member variables, make this class a derived class, or add virtual functions, you won't have to change your assembler module. You need to reassemble your module only ifyou change the structure of the count member variable, or if you make a large model version of this class. You need to reassemble because you have to deal with a segment and an offset whenreferring to the count member variable. Pascal calling conventions So far, you've seeri how c++ normally passes parameters to functions by having the calling code push parameters right to left, call the function, and discard the parameters from the stack after the call. Borland C++ is also capable of following the conventions used by Pascal programs in which parameters are passed from left to right, and the called function discards the parameters from the stack. In Borland C++,·Pascal conventions are enabled with the -p command-line option or the pascal keyword. The following example, ASMPSCL.ASM, shows an assembler function that uses Pascal conventions: ; Called as: TEST_PROC(i, j, k) ; i equ ;leftmost parameter j equ k equ 4 ;rightmost parameter .MODEL small .CODE PUBLIC TEST_PROC TEST_PROC PROC push bp mov bp,sp mov ax, [bp+i] ;get i add ax, [bp+j] ;add j to i sub ax, [bp+k] ;subtract k from the sum pop bp ret 6 ;return, discarding 6 parameter bytes TEST_PROC ENDP END 220 TurboA sse mbIer Use r'.s Guide
  • 230. Note that RET 6 is used by the called function to clear the passed parameters from the stack. Pascal calling conventions also require all external and public symbols to be in uppercase, with no leading underscores. Why would you want to use Pascal calling conventions in a C++ program? Code that uses Pascal conventions tends to be somewhat smaller and faster than normal C++ code since there's no need to execute an ADD SP n instruction to discard the parameters after each call. Caning Borland C++ from Turbo Assembler Although it's most common to call assembler functions from C++ to perform specialized tasks, you might occasionally want to call C++ functions from assembler. As it turns out, it's actually easier to call a Borland C++ function from a Turbo Assembler function than the reverse since no stack-frame handling on the part of the assembler code is required. Let's take a quick look at the requirements for calling Borland C++ functions from assembler. Link in the C++ startup code As a general rule, you should only call Borland C++ library functions from assembler code in programs that link in the C++ startup module as the first module linked. Note Generally, you should not call Borland C++ library functions from programs that don't link in the C++ startup module since some Borland C++ library functions will not operate properly if the startup code is not linked in. If you really want to call Borland C++ library functions from such programs, we suggest you look at the startup source code (the file CO.ASM on the Borland C++ distribution disks) and purchase the C++ library source code from Borland. This way, you can be sure to provide the proper initialization for thelibrary functions you need. . Note Calling user-defined C++ functions that in tum call C++ library functions falls into the same category as calling library functions directly; lack of the C++ startup can potentially cause problems for any assembler program that calls C++ library functions, directly ot indirectly. The segment setup As we learned earlier, you must make sure that Borland C++ and Turbo Assembler are using the same memory model and that the segments you use in Turbo Assembler match those used by Borland C++. Turbo Assembler has a tchuge memory model that supports Borland C++'s huge memory model..Refer to the previous section if you need a refresher on matching memory models and segments. Also, remember to put EXTRN directives for far symbols either outside all segments or inside the correct segment. Chap t er 18, I nterf aci n9 TurboA sse mbIer wit h B0 rIand C++ 221
  • 231. Performing the call All you need to do when passing parameters to a Borland C++ function is push the right-most parameter first, then the next right-most parameter, and so on, until the left- most parameter has been pushed. Then just call the function. For example, when programming in Borland C++, to call the Borland C++ library function strcpy to copy SourceString to DestString, you would type strcPY,(DestString, SourceString) i To perform the same call in assembler, you would use lea lea ax,SourceString bX,DestString push ax push bx call _strcpy sp,4 add irightmost parameter ileftmost parameter iPush rightmost first iPush leftmost next iCOPY the string idiscard the parameters Don't forget to discard the paramEfters by adjusting SP after the call. You can simplify your code and make it language independent at the same time by taking advantage of Turbo Assembler's CALL instruction extension: call destination [language [,arglJ ... J where language is C, CPP, PASCAL, BASIC, FORTRAN, PROLOG or NOLANGUAGE, and arg is any valid argument to the routine that can be directly pushed onto the processor stack. . Using this feature, the preceding code can be reduced to lea aX,SourceString lea bX,DestString call strcPY c,bx,ax . Turbo Assembler automatically inserts instructions to push the arguments in the correct order for C++ (AX first, then BX), performs the call to _strcpy (Turbo Assembler automatically inserts an underscore in front of the name for C++), and cleans up the stack after the call. If you're calling a C++ function that uses Pascal calling conventions, you have to push the parameters left to right and not adjust SP afterward: . lea bx,DestString lea ax,SourceString push bx push ax i call STRCPY ileftmost parameter irightmost parameter iPush leftmost first iPush rightmost next iCOPY the string ileave the stack alone Again, you can use Turbo Assembler's CALL instruction extension to simplify your code: lea bX,DestString ileftmostparameter lea ax,SourceString irightmost parameter call strcPY pascal,bx,ax 222 TurboA sse mbIer. Use r' s Guide
  • 232. Turbo Assembler automatically inserts instructions to push the arguments in the correct order for Pascal (BX first, then AX) and performs the call to STRCPY (converting the name to all uppercase, as is the Pascalconvention). The last example assumes that you've recompiled strcpy with the -p switch, since the standard library version of strcpy uses C++ rather than Pascal calling conventions. Rely on C++ functions to preserve the following registers and only the following registers: 51, DI, BP, DS, 55, SP, and CS. Registers AX, BX, CX, DX, ES, and the flags may be changed arbitrarily. Calling aBorland C++ function from Turbo Assembler One case in which you may wish to call a Borland C++ function from Turbo Assembler is when you need to perform complex calculations. This is especially true when mixed integer and floating-point calculations are involved; while it's certainly possible to perform such operations in assembler, it's simpler to let C++ handle the details of type conversion and floating-point arithmetic. . Let's look at an example of assembler code that calls a Borland C++ function in order to get a floating-point calculation performed. In fact, let's look at an example in which a Borland C++ function passes a series of integer numbers to a Turbo Assembler function, which sums the numbers and in tum calls another Borland C++ function to perform the floating-point calculation of the average value of the series. The C++ portion of the program in CALCAVG.CPP is #include <stdio.h> extern "C" float Average(int far * Valueptr, int NurnberOfValues); #define NUMBER_OF_TEST_VALUES 10 int TestValues[NUMBER_OF_TEST_VALUES] = { I, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int main () { printf ("The average value is: %fn", Average (TestValues, NUMBER_OF_TEST_VALUES)); return 0; extern "C" float IntDivide(int Dividend, int Divisor) { return ( (float) Dividend / (float) Divisor); and the assembler portion of the program in AVERAGE.ASM is Borland C++-callable small-model function that returns the average of a set of integer values. Calls the Borland C++ function IntDivide() to perform the final division. Chap ter ,1 8, Int erf aci n9 TurboA sse mbIer wit h B 0 rIand C++ 223
  • 233. ~J Function prototype: extern float Average(int far * Valueptr, int NumberOfValues) i Input: int far * Valueptr: int NumberOfValues: .,MODEL small EXTRN IntDivide:PROC .CODE PUBLIC _Average _Average PROC push bp mov bp,sp les bx, [bp+4] mov cx, [bp+8] mov ax,O AverageLoop: add ax, es: [bx] add bX,2 loop AverageLoop push WORD PTR [bp+8] push ax call IntDivide add sp,4 pop bp ret _Average ENDP END ithe array of values to average ithe number of values to average ipoint ES:BX to array of values i# of values to average iclear the running total iadd the current value' ipoint to the next value iget back the number of values i passed to IntDivide as the i rightmost parameter ipass the total as the leftmost parameter icalculate the floating-point average idiscard the parameters iaverage is in 8087's TOS register The C++ main function passes a pointer to the array of integers TestValues and the length of the array to the assembler function Average. Average sums the integers, then passes the sum and the number of values to the C++ function IntDivide. IntDivide casts the sum and number of values to floating-point numbers and calculates the average value, doing in a single line of C++ code what would have taken several assembler lines. JntDivide returns the average to Average in the 8087 TOS register, and Averagejust leaves the average in the TOS register and returns to main. CALCAVG.CPP and AVERAGE.ASM could be compiled and linked into the executable program CALCAVG.EXE with the command bcc calcavg.cpp average.asm Note that Average will handle both small and large data models without the need for any code change since a far pointer is passed in all models. All that would be needed to support large code models (huge, large, and medium) would be use of the appropriate .MODEL directive. Taking full advantage of Turbo Assembler's language-independent extensions, the assembly code in the previous example could be written more concisely as shown here in CONCISE.ASM: ' 224 TurboA sse mbIer Use r' s Guide
  • 234. .MODEL small,C EXrrRN C IntDivide: PROC .CODE PUBLIC Average les mov C Average PROC C ValuePtr:DWORD,NumberOfValues:WORD bx,Valueptr cx,NumberOfValues mov ax,O AverageLoop: add ax,es: [bx] add bx,2 ;point to the next value loop AverageLoop call IntDivide C,ax,NumberOfValues ret Average ENDP END Chapter 18, Interfacing Turbo Assembler with Borland C++ 225
  • 235. 226 TurboA sse mbIer Use rl s Guide
  • 236. Program blueprints This appendix describes basic program construction information depending on specific memory models and executable object formats. Simplified segmentation segment description The following tables show the default segment attributes for each memory model. Table A.1 Default segments and types for TINY memory model .CODE _TEXT WORD PUBLIC 'CODE' DGROUP .FARDATA FAR_DATA PARA private 'FAR_DATA' .FARDATA? FAR_BSS PARA private 'FAR_BSS' DATA _DATA WORD PUBLIC 'DATA' DGROUP .CONST CaNST WORD PUBLIC 'CaNST' DGROUP .DATA? _BSS WORD PUBLIC 'BSS' DGROUP .sTACKl STACK PARA STACK 'STACK' DGROUP 1. STACK not assumed to be in DGROUP if FARSTACK specified in the MODEL directive. Table A.2 Default segments and types for SMALL memory model .FARDATA .FARDATA? DATA .CONST FAR_DATA FAR_BSS _DATA CaNST PARA PARA WORD WORD private private PUBLIC PUBLIC 'FAR_DATA' 'FAR_BSS' 'DATA' DGROUP 'CaNST' ·DGROUP APpen di x A, Pro 9ram bIueprj nt s 227
  • 237. Table A.2 Default segments and types for SMALL memory model (continued) .DATA? .STACKl _BSS STACK WORD PARA PUBLIC STACK 'BSS' 'STACK' 1. STACK not assumed to be in DGROUP if FARSTACK specified in the MODEL directive. TableA.3 Default segments and types for MEDIUM memory model .CODE name_TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA private ' 'FAR_DATA' .FARDATA? FAR_BSS PARA private 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .STACKl STACK PARA STACK 'STACK' 1. STACK not assumed to be in DGROUP if FARSTACK specified in the MODEL directive. TableA.4 Default segments and types for COMPACT memory model .CODE _TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA private 'FAR_DATA' .FARDATA? FAR_BSS PARA private 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .sTACKl STACK PARA STACK 'STACK' 1. STACKnot assumed to be in DGROUP if FARSTACK specified in the MODEL directive. Table A.S Default segments and types for LARGE or HUGE memory model .CODE name_TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA private 'FAR_DATA' .FARDATA? FAR_BSS PARA private 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .sTACKl STACK PARA STACK 'STACK' 1. .STACK not assumed to bein DGROUP if FARSTACK specified in the MODEL directive. 228 Turbo Assem'bler User's Guide DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP DGROUP
  • 238. Table A.6 Default segments and types for Borland C++ HUGE (TCHUGE) memory model 'fQ~.·~~~,V,'1:/ii"hd:;,/i;'i'i .CODE name_TEXT WORD PUBLIC 'CODE' .FARDATA .FARDATA? DATA .5TACK1 FAR_DATA FAR_BSS name_DATA STACK PARA PARA PARA PARA private 'FAR_DATA' private 'FAR_BSS' private 'DATA' STACK 'STACK' 1. STACK is automatically FAR. DOS programs Programs designed to be executed under DOS are stored in two formats: • EXE format (for EXEcutable) • COM format (for COre iMage) EXE format permits the most general program segmentation under DOS. A program can have multiple segments, and can reference segment and group names symbolically. EXE programs are thus permitted to exceed 64K in size. COM format is essentially a throwback to a simpler era. Programs using COM format can't contain symbolic references to group and segment names. Thus, COM programs are written using the TINY model, and are limited to 64K of code and data. To build DOS programs, you need to use a DOS.linker (like TLINK) and a program construction utility (like MAKE). DOS EXE program blueprint When you load an EXE program, the operating system sets up the registers as follows: DS,ES CS:IP SS:sp Contains the paragraph address of the program segment prefix (PSP) for the program. The PSP contains arguments passed to the program from the command line, and a pointer to the environment string for the program. Contains the starting address specified byEND in one of the program's modules, or the address of the STARTUPCODE directive. Contains the address of the last word that the stack segment specified in the program. You can define EXE programs with any memory model. You should use the simplest memory model possible because it makes programming simpler and faster. For example, if you never expect your program to use more than 64K of code, data, and stack space, the TINY model would be the appropriate model to use. The STARTUPCODE directive in a module emits instructions that automatically initialize all necessary registers to conform with the selected model. However, it preserves the paragraph address of the PSP in ES for the program's use. APpen di x A, Pro 9ram bIueprj nt s 229
  • 239. When you load an EXE program, the operating system allocates all remaining memory to it until the program exits. For programs that don't use a heap, or programs that build their own heaps in this memory, this behavior is fine. Other programs can allocate memory from DOS. In this case, the memory must be freed back to DOS before you can request it from DOS. To exit from an EXE program, use the EXITCODE directive. Note EXEPROG.ASM, on your example Turbo Assembler disks, illustrates these topics. Use the MAKE utility to build the EXE program. The file MAKEFILE should include all modules link with the program, as follows: EXEPROG.EXE: EXEPROG.OBJ TLINK EXEPROGi EXEPROG.OBJ: EXEPROG.ASM TASM EXEPROG COM program blueprint COM programs are restricted versions of EXE programs. You can represent every COM program as an EXE program, but not every EXE program as a COM program. The following restrictions apply: ·A COM program should be written using the TINY memory model. • You can't have a predefined stack segment for COM programs. • A COM program can't contain any direc~ segment or group address references. This means that the program can't contain any direct far calls, nor can it reference any segments by name. All procedures in a COM program must be declared NEAR. • Execution must begin at offset lOOh in the code segment. To let this happen, make the first instruction in the code segment the STARTUPCODE directive. Turbo Assembler loads a COM. program starting at offset lOOh of the program segment prefix (PSP). The STARTUPCODE directive for the TINY model automatically places an ORC lOOh in the program to compensate for this action. When you load a COM program, the following registers are set: CS,DS,ES,SS IP SP Contains the paragraph address of the PSP and the program. Set to lOOh. Set to OFFFEh (the last word in the program segment). If you don't want to place the stack at the end of the program segment, you must set up anew stack. Use the uninitialized data segment (UDATASEG) for this'stack. Even though COM programs must be defined with the TINY memory model, you should stillseparate code, data, and uninitialized data using CODESEG, DATASEG, and UDATASEG. 230 Turbo Assembler User's Guide
  • 240. Note Use the EXITCODE directive exit from a COM program the same way you exit from an EXE program. As with EXE programs, when you load a COM program, Turbo Assembler allocates all remaining memory to it until it exits. If memory is freed back to DOS, make sure that no uninitialized data is unintentionally freed. Note COMPROG.ASM, on your example Turbo Assembler disks, illustrates these points. Use the MAKE utility to build the COM program. The fileMAKEFILE should include all modules link with the program, as follows: COMPROG.COM: COMPROG.OBJ TLINK It COMPROGi COMPROG.OBJ: COMPROG.ASM TASM COMPROG Windows programs Turbo Assembler can also be used to create Windows applications. Windows can run in either real mode (on all 8086 processors) or protected mode (on 80286 and higher processors). Thus, programs written for Windows can run in protected mode. You should carefully separate code and data using the CODESEG, DATASEG, and UDATASEG directives, and use the WARN PRO directive to flag any access problems that could occur at assembly time. Finally, protected mode programs should not attempt to set segment registers to calculated paragraph segment values. Segment values in protected mode are not paragraph addresses, but rather descriptors that have no meaning to an application program. Although all the tools you need to write Windows programs are contained in your Turbo Assembler package, you might ~d it useful to use otherlanguage tools (such as Borland C++ or Borland Delphi) to help you effectively create Windows applications. This appendix provides the simplest of blueprints for Windows applications and Dynamic Link Libraries (DLLs). For a more complete description of Windows ,applications, refer to your. compiler manuals, or the appropriate Microsoft documentation. . WindowsDLL blueprint A Dynamic Link Library (DLL) is a group of procedures that you can call from any Windows application. DLLs extend the Windows application interface. DLLs perform many functions; for example, you can convert non-interactive DOS programs to DLLs. Support can be added for new sorts of screen entities by writing a DLL. You can find an example program called DLLWIN.ASM that illustrates how.to write a DLL in assembler. This assembler project is located in the EXAMPLES USRGUIDE DLLWIN directory off the main TASM subdirectory. Appendix A, Program blueprints 231
  • 241. You can use the MAKE utility to build the DLL. DLLWIN.MAK is provided in the DLLWIN subdirectory: dllwin.exe: dllwin.obj dllwin.def TLINK /v /Twd /s dllwin, dllwin, dllwin"dllwin dllwin.obj: dllwin.asm TASM /Zi dllwin, , This build process requires the following linker definitions file, DLLWIN.DEF: LIBRARY DLLWIN DESCRIPTION 'Simple Assembly Windows DLL' CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE SINGLE EXETYPE WINDOWS HEAPSIZE 4096 e _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ , iDefine imported functions. (Not necessary if you link with an iimport library like IMpORT.LIB or LIBW.LIB.) .---~------------------------------------------------- ----------------- , IMPORTS GDI.GETWINDOWEXTEX Windows 16·bit application blueprint A Windows application is very much like a DLL, except that only a single procedure is called: WinMain. Windows calls WinMain to start the procedure. The application usually has a standard structure, which lets it communicate with the Windows graphical environment. .. ASMWIN.ASM, located in the EXAMPLES USRGUIDEASMWIN directory off the . main TASM subdirectory, shows ari assembly-based Windows program. Use the MAKE utility to build the Windows application. The file ASMWIN.MAK includes all modules to be linked in with the application: asmwin.exe: asmwin.obj asmwin.def TLINK /v /Twe /s ASMWIN, ASMWIN, ASMWIN"ASMWIN asmwin.obj: asmwin.asm TASM /Zi ASMWIN" This build process requires the linker definitions file ASMWIN.DEF, which is very similar to the .DEF module used in the preceding example that generates a Windows DLL using assembly code. A complete 16-bit Windows program is supplied in the EXAMPLES WAP subdirectory off your main TASM directory. Windows 32·bit application blueprint In high-level languages like C, C++, and Pascal,Windows 32-bit programs are almost identical to their 16~bit counterparts because the compiler handles most of the 232 TurboA sse mbIer Use r 's Guide
  • 242. differences. However, you must deal directly with those differences at the assembly language level. In particular, you must pay attention to the following details: • All values pushed on the stack must be 32 bits. • The parameters to WndProc must all be 32 bits. • The EBX, ESI and EDI registers must be preserved in a Win32 callback function. • Many of the Win32 constants are now 32 bits wide as opposed to Windows 16-bit wide constants. • Because of Unicode support, there are both ANSI and WIDE CHAR versions of all the API functions that use strings. The ANSI version of these functions end with a capital'A' and the WIDE CHAR versions end with a capital'W'; no versions of these functions exist that don't end with either an 'A' Or 'W.' Because of this, you must use either TextOutA or TextOutW to get the functionality of TextOut. . Use the following assembler and linker commands to compile a 32-bit Windows program: TASM32 /zi /ml filename.asm TLINK32 /v filename.obj, filename.exe, filename.map, import32.lib, filename.def The .DEF file for the project should resemble the following: NAME DESCRIPTION CODE DATA EXETYPE STACKSIZE EXPORTS MODNAME 'Description of this module' PRELOAD MOVEABLE DISCARDABLE PRELOAD MOVEABLE MULTIPLE WINDOWS 8192 WndProc A complete 32-bit Windows program is supplied in the EXAMPLES WAP32 subdirectory off your main TASM directory. OS/2 programs Programs designed to be executed under the OS/2 operating system can use one of several formats, depending on the capabilities you want. OS/2 can execute programs designed for DOS program formats, as well as programs and DLLs written for Windows. However, the most powerful format available under OS/2 is the linear executable format, in which a program no longer has to manipulate segment registers, and 512 megabytes of virtual memory is available. This format is also known as flat model. OS/2 flat-model program blueprint Turbo Assembler assumes that all 32-bit segments in a flat-model program belong to a supergroup called FLAT, and share the same segmentselector. (Segments that are 16-bit are allowed, but are of little use.) Appendix A, Program blueprints 233
  • 243. When you execute a flat-model program, Turbo Assembler initializes the registers as follows: .CS,DS,ES,SS CS:EIP SS:ESP FS,GS Contains the segment selector of the 32-bit linear address for the program. These registersshould never have to be changed. Contains the address of the STARTUPCODE directive. Contains the address of the last word of the stack segment, which the STAC~ directive specifies. Contain special values that the application should not modify. You must define linear-executable programs with the FLAT model. This instructs Turbo Assembler to consider all 32-bit segments and groups to be a member of FLAT supergroup. You can also optionally specify the OS/2 operating system, which allows the STARTUPCODE and EXITCODEdirectives to function correctly (for example, MODEL 082 FLAT). .The STARTUPCODE directive produces instructions that automatically initialize all necessary registers to conform to FLAT model. Similarly, the EXITCODE directive produces instructions that automatically return control to the operating system, while letting you specify an optional return value. 234 Turbo Assembler User's Guide
  • 244. Turbo Assembler syntax summary This appendix describes the syntax of Turbo Assembler expressions in a modified Backus-Naur form (BNF). The symbol ::= describes a syntactical production. Ellipses (...) indicate that an element is repeated as many times as it is found. This appendix also discusses keywords and their precedences. Lexical grammar valid_line ::= white_space valid_line punctuation valid_line number_string valid_line id_string valid_line null white_space ::= space_char white_space space_char space_char ::= All control characters, character> 128, I ' id_string ::= id_char id_strng2 id_stmg2 ::= id_chr2 id_strng2 null id~char ::= Any of $, %, -' ?, or any alphabetic characters Appendix B, Turbo Assembler syntax summary 235
  • 245. , id_chr2 ::= id_chars plus numerics number....;string ::= num_string str_string num_string ::= digits alphtlnums digits ".' digits exp digits exp digits ::= digit digits digit digit ::= othrough 9 alphanums ::= digit alphanum alpha alphanum , null alpha ::= alphabetic characters exp::= E+ digits E-digits Edigits null str_string ::= ;Only MASM mode DO, DQ, or DT Quoted string, quote enterable by two quotes in a row punctuation ::= Everything that is not a space_char, id_char, I " " " , If, or digits The period (.) character is handled differently in MASM mode and Ideal mode. This character is not required in floating-point numbers in MASM mode and also can't be part ofa symbol name in Ideal ,mode. In MASM mode, it is sometimes the start of a symbol name and sometimes a punctuation character used as the structure member selector. Here are the rules for the period (.) character: 1 In Ideal mode, it's always treated as punctuation. 2 In MASM mode, it's treated as the first character of an ID in the following cases: @ When it is the first character on the line, or in other special cases like EXTRN and PUBLIC symbols, it gets attached to the following symbol if the character that follows it is an id_chr2, as defined in the previous rules. 236 TurboA sse mbIer Use r's Guide
  • 246. • If it appears other than as the first character on the line, or ifthe resulting symbol would make a defined symbol, the period gets appended to the start of the symbol following it. MASM mode expression grammar Expression parsing starts at MASM_expr. MASM_expr ::= mexprl mexprl ::= SHORT mexprl .TYPE mexprl SMALL mexprl LARGE mexprl expr2 expr2 ::= expr3 OR expr3 .. . expr3 XOR expr3 .. . expr3 expr3 ::= expr4 AND expr4 ... expr4 expr4 ::= NOTexpr4 exprS exprS ::= expr6 EQ expr6 ... expr6 NE expr6 '" expr6 LT expr6 .. , expr6 LE expr6 ... expr6 GT expr6 '" expr6 GE expr6 ... expr6 expr6 ::=' expr7 + expr7 .. . expr7 - expr7 .. . expr7 ;If 386 ;If 386 APpen di X B, TurboA sse mbIer syntax sum mar y 237
  • 247. expr7::= mexprl0 * mexprl0 ... mexprl0 /mexprl0 :.. mexprl0 MOD mexprl0 .. . mexprl0 SHR mexprl0 .. . mexprl0 SHL mexprl0 .. . mexprl0 exprS ::= + exprB -exprB expr12 exprl0 ::= OFFSET pointer SEG pointer SIZE symbol LENGTH symbol WIDTH symbol MASK symbol THIS itype symbol (pointer) [pointer J mexprlO ::= mexprll PTR mexprl0 mexprll TYPE mexprl0 HIGH mexprl0 LOWmexprl0 OFFSET mexprl0 SEGmexprl0 THIS mexprl0 mexpr11 ::= exprB : exprB ... mexpr12 ::= mexpr13 [mexpr13 ... mexpr13 (mexpr13 :.. mexpr13 '.' mexprl0 238 Turbo Assembler User's Guide ;Implied addition if bracket ;Implied addition if parenthesis
  • 248. mexpr13 ::= LENGTH symbol SIZE symbol WIDTH symbol MASK symbol (mexprl ) [mexprl ] exprl0 Ideal mode expression grammar Expression parsing starts at ideaCexpr. ideaCexpr ::= pointer itype ::= UNKNOWN BYTE WORD DWORD PWORD FWORD QWORD TBYTE SHORT NEAR FAR PROC DATAPTR CODEPTR structure_name table_name enum_namer record_name TYPE pointer pointer ::= SMALL pointer LARGE pointer itype PTR pointer itype LOW pointer itype HIGH pointer itype pointer pointer2 ;If 386 ;1£ 386 APpen di x B, TurboA sse mbIer synt ax sum mar y 239
  • 249. pointer2 ::= pointer3 . symbol ... pointer3 pointer3 ::= expr : pointer3 expr expr::= SYMTYPE expr expr2 expr2 ::= expr3 OR expr3 ... expr3 XOR expr3 '" expr3 expr3 ::= expr4 AND expr4 ... expr4 expr4 ::= NOTexpr4 exprS exprS ::= expr6 EQ expr6 .. , expr6 NE expr6 ... expr6 LTexpr6 .. , expr6 LE expr6 ... expr6 GT expr6 .. , expr6 GE expr6 ... expr6 expr6 ::= expr7+ expr7 ... expr7 - expr7 .. , expr7 expr7::= exprB *exprB ... exprB / exprB .,. exprB MOD exprB .. . exprB SHR exprB .. . exprB SHL exprB .. . exprB exprS ::= + exprB -exprB expr9 240 Turbo Assembler User's Guide
  • 250. expr9 ::= HIGHexpr9 LOWexpr9 exprl0 exprl0 ::= OFFSET pointer SEG pointer SIZE symbol LENGTH symbol WIDTH symbol MASK symbol THIS itype symbol (pointer) [pointer] Keyword precedence It's important to understand how Turbo Assembler parses source lines so that you can avoid writing code that produces unexpected results. For example, examine the following program fragment: NAME SEGMENT If you had written this line hoping to open a segment called NAME, you would be disappointed. Turbo Assembler recognizes the NAME directive before the SEGMENT directive, thus naming your code SEGMENT. In general, Turbo Assembler determines the meaning of a line based on the first two symbols on the line. The left-most symbol is in the first position, while the symbol to its right is in the second position. Ideal mode precedence The following precedence rules for parsing lines apply to Ideal mode: 1 All keywords in the first position of the line have the highest priority (priority 1) and are checked first. 2 The keywords in the second position have priority 2 and are checked second. APpen di x B, TurboA sse mbIer syn t ax sum mar y 241
  • 251. MASM mode precedence The precedence rules for parsing lines in MASM mode are much more complicated than in Ideal mode. There are three levels of priority instead of two, as follows: 1 The highest priority (priority 1) is assignedto certain keywords found in the first position, such as NAME or %OUT. 2 The next highest priority (priority 2) belongs to all symbols found in the second position. 3 All other keywords found in first position have the lowest priority (priority 3). Note Turbo Assembler treats priority 1 keywords like priority 3 keywords inside structure definitions. In this case, priority 2 keywords have the highest priority. For example, in the code fragment NAME SEGMENT NAME is a priority 1 keyword, while SEGMENT is a priority 2 keyword. Therefore, Turbo Assembler will interpret this line as a NAME directive rather than a SEGMENT directive. In another example, MOV INSTR,l MOV is a priority 3 keyword, while INSTR is a priority 2 keyword. Thus, Turbo Assemblerinterprets this line as an INSTR directive, not a MOV instruction (which you might have wanted). Keywords and predefined symbols This section contains a complete listing of all Turbo Assembler keywords. The values in parentheses next to keywords indicate the priority of the keyword (lor 2) in MASM mode. Keywords are labeled with a priority only if they have priority 1 or 2. All others are assumed to be priority 3. Turbo Assembler recognizes the keyword only if it finds them. In MASM mode, priority 1 or 3 keywords always are located in the first position, while priority 2 keywords occur in the second position. An M next to a keyword indicates that you can use a keyword only in MASM mode, and an I indicates a keyword that is available only in Ideal mode. If there is no letter, the keyword works in either mode. A number next to the keyword indicates its priority. .Directive keywords The following list contains all Turbo Assembler directive keywords. The keywords are grouped by the version of Turbo Assembler in which they were introduced. 242 TurboA sse mbIer Use r' s Guide
  • 252. These keywords were introduced in Turbo Assembler 1.0. Table B.1 Turbo Assembler v1.0 (VERSION T100) keywords % (1) CMPSD EMUL FBLD .186(M) .CODE(M) END FBSTP .286 (M) CODESEG ENDIF (1) FCHS .286c (M) COMM(1) ENDM FCLEX .286p (M) COMMENT (1) ENDP(2) FCOM .386 (M) %CONDS ENDS (2) FCOMP .386c (M) CONST ENTER FCOMPP .386p (M) .CONST(M) EQU(2) FDECSTP .387(M) %CREF .ERR(l)(M) FDISI .8086 (M) .CREF(M) ERR FDN .8087 (M) %CREFALL .ERR1 (1)(M) FDNP ; (2) %CREFREF .ERR2 ,(1)(M) FDNR =(2) %CREFUREF .ERRB (1)(M) FDNRP AAA %CTLS .ERRDEF (l)(M) FENI AAD CWD .ERRDIF (1)(M) FFREE AAM CWDE .ERRDIFI (1)(M) FIADD AAS DAA .ERRE (l)(M) FICOM ADC DAS .ERRIDN (1)(M) FICOMP ADD .DATA(M) .ERRIDNI (1)(M) FIDN ALIGN .DATA? (M) ERRIF FIDNR .ALPHA(M) DATASEG ERRIF1 FILD AND DB (2) ERRIF2 FIMUL ARG DD(2) ERRIFB FINCSTP ARPL DEC ERRIFDEF FINIT ASSUME %DEPTH ERRIFDIF FIST %BIN DF (2) ERRIFDIFI FISTP BOUND DISPLAY ERRIFE FISUB BSF DN ERRIFIDN FISBR BSR OOSSEG ERRIFIDNI FLD BT DP(2) ERRIFNB FLD! BTC DQ(2) ERRIFNDEF FLDCW BTR DT(2) .ERRNB (1)(M) FLDENV BTS DW(2) .ERRNDEF (1)(M) FLDL2E CALL ELSE (1) .ERRNZ (1)(M) FLDL2T CATSTR(2) ELSEIF (1) ESC FLDLG2 CBW ELSEIF1 (1) EVEN FLDLN2 CDQ ELSEIF2 (1) EVENDATA FLDPI CLC ELSEIFB (1) EXITM FLDZ CLD ELSEIFDEF (1) EXTRN(1) FMUL CLI ELSEIFDIF (1) F2XM1 FMULP CLTS ELSEIFDIFI (1) FABS FNCLEX CMC ELSEIFE (1) FADD FNDISI CMP ELSEIFIDN (1) FADDP FNENI CMPBW ELSEIFIDNI (1) FARDATA FNINIT CMPS ELSEIFNB (1) .FARDATA (M) FNOP CMPSB ELSEIFNDEF (1) .FARDATA? (M) FNSAVE Appendix B, Turbo Assembler syntax summary 243
  • 253. Table B.1 Turbo Assembler v1.0 (VERSION T1 00) keywords (continued) FNSTCW IFIDN (1) JP LTR FNSTENV IFIDNI (1, JPE %MACS FNSTSW IFNB (1) JPO MACRO (2) FPATAN IFNDEF (1) JS MASM FPREM IJECXZ JUMP MODEL FPTAN IMUL JUMPS .MODEL(M) FRNDINT IN JZ MOV FRSTOR INC LABEL (2) MOVMOVS FSAVE %INCL LAHF MOVSB FSCALE INCLUDE (1) .LALL(M) MOVSD FSQRT INCLUDELIB (1) LAR MOVSW FST INS LDS MOVSX FSTCW INSB LEA MOVZX FSTENV INSD LEAVE MUL FSTP INSTR(2) LES MULTERRS FSTSW INSW· .LFCOND(M) NAME (1) FSUB INT LFS NEG FSUBP INTO LGDT %NEWPAGE FSUBR IRET LGS %NOCONDS FSUBRP IREID LIDT %NOCREF FTST IRP (1) %LINUM %NOCTLS FWAIT IRPC (1) %LIST NOEMUL FXAM JA .LIST(M) %NOINCL FXCH JAE LLDT· NOJUMPS FXTRACT JB LMSW %NOLIST FYL2X JBE LOCAL NOLOCALS FYL2xP1 JC LOCALS NOMASM51 FSETPM JCXZ LOCK %NOMACS FPCOS IE LODS. NOMULTERRS FPREM1 JG LODSB NOP FPSIN JGE LODSD NOSMART FPSINCOS JL LODSW %NOSYMS FUCOM JLE LOOP NOT FUCOMP JNA LOOPD %NOTRUNC FUCOMPP JNAE LOOPDE NOWARN GLOBAL (1) JNB LOOPDNE OR GROUP (2) JNBE LOOPDNZ ORG HLT JNC LOOPDZ OUT IDEAL JNE LOOPE %OUT(l) IDIV JNG LOOPNE OUTS IF (1) JNGE LOOPNZ OUTSB IF! (1) JNL LOOPW OUTSD IF2 (1) JNLE LOOPWE OUTSW IFb (1) JNO LOOPWNE P186 IFDEF (1) JNP LOOPWNZ P286 IFDIF (1) JNS LOOPWZ P286N IFDIFI (1) JNZ LOOPZ P287 !FE (1) JO LSLLSS P386 244 Turbo Assembler User's Guide
  • 254. Table B.1 Turbo Assembler v1.0 (VERSION T100) keywords (continued) P386N REPT (1) SETNE STR P387 REP SETNG STRUC(2) P8086 REPE SETNGE SUB P8087 REPNE SE1NL SUBSTR(2) PAGE REPNZ SE1NLE SUBTIL (1) %PAGESIZE REPZ SETNO %SUBTIL %PCNT RET SETNP %SYMS PN087 RETF SETNS %TABSIZE POP RETN SETNZ TEST paPA ROL SETa %TEXT POPAD ROR SETP .TFCOND(M) POPFD SAHF SETPE TITLE (1) %POPLCTL SAL SETPO %TITLE PPF .5ALL(M) SETS %TRUNC PROC (2) SAR SETZ UDATASEG PUSH SBB .SFCOND(M) UFARDATA PUSHA SCAS SGDT UNION (2) PUSHAD SCASB SHL USES PUSHF SCASD SHLD VERR PUSHFD SCASW SHR VERW %PUSHLCTL SEGMENT (2) SHRD WAIT PUBLIC (1) .5EQ(M) SIDT WARN PURGE SETA SIZESTR(2) .)eALL(M) %PAGESIZE SETAE SLDT XCHG %PCNT SETB SMART .XCREF(M) PN087 SETBE SMSW XLAT %POPLCTL SETC SOR XLATB PROC(2) SETE STACK .XLIST(M) %PUSHLCTL SETG .5TACK(M) USECS PUBLIC (1) SETGE .sTARTUP (M) USEDS PURGE SETL STC USEES QUIRKS SETLE SID USEFS RADIX SETNA STI USEGS .RADIX(M) SETNAE STOS USESS RCL SETNB STOSB RCR SETNBE STOSD RECORD (2) SETNC STOSW Turbo Assembler version 2.0 supports aUversion 1.0 keywords, with the following additions: Table B.2 Turbo Assembler v2.0 (VERSION T200) new keywords BSWAP CMPXCHG INVD XADD P486 P486N P487 INVLPG STARTUPCODE WBINVD PUBLICDLL (I) RETCODE APpen dix B, TurboA sse mbIer synt ax sum mar y 245
  • 255. Turbo Assembler version 2.5 supports all version 2.0 keywords, plus the following keyword additions: Table B.3 Turbo Assembler v2.5 (VERSION T250) new keywords ENTERD LEAVED ENTERW LEAVEW Turbo Assembler version 3.0 supports keywords from all previous versions, with the following additions: Table B.4 Turbo Assembler v3.0 (VERSION T300) new keywords CLRFLAG GOTO(l) TBLINIT ENUM(2) LARGESTACK TBLINST EXITCODE SETFIELD TYPEDEF FASTIMUL SETFLAG TBLINIT FLIPFLAG SMALLSTACK TBLINST GETFIELD TABLE (2) VERSION WHILE (1) Turbo Assembler version 3.1 supports keywords from all previous versions, with the . following additions: Table B.5 Turbo Assembler v3.1(VERSION T31 0) new keywords PUSHSTATE POPSTATE Turbo Assembler version 3.2 supports keywords from all previous versions, with the following additions: . Table B.6 Turbo Assembler v3.2 (VERSION T320) new keywords IRETW POPAW PUSHFW POPFW PROCDESC(2) PROCTYPE(2) PUSHAW Turbo Assembler version 4.0 supports keywords from ~ll previous versions, with the following additions: Table B.7 Turbo Assembler v4.0 (VERSION T400) new keywords ALIAS CMPXCHG8B CPUID P586 246 TurboA sse mbI,e r Use r' s Guide P586N P587 RDMSR RDTSC RSM WRMSR
  • 256. Turbo Assembler version 5.0 supports keywords from all previous versions, with the following additions: Table B.8 Turbo Assembler v5.0 (VERSION T500) new keywords BREAK .CONTINUE .ELSE .ELSEIF .ENDIF .ENDW .IF .LISTALL .LISTIF .LISTMACRO .LISTMACROALL NOLIST NOLISTIF .NOLISTMACRO .REPEAT .UNTIL .UNTILCXZ .WHILE CARRY? ECHO EXPORT EXTERN EXTERNDEF FAR16 FAR32 FOR FORC NEAR16 NEAR32 OPTION OVERFLOW? PARITY? PRIVATE PROTO PUBLIC REALIO REAL4 REAL8 REPEAT SBYTE SDWORD SIGN? STRUCT SUBTITLE SWORD ZERO? The following options are supported with the OPTION keyword: Table B.9 Options supported by OPTION CASEMAP DOTNAME NODOTNAME EMULATOR NOEMULATOR EPILOGUE EXPR16 EXPR32 LANGUAGE LJMP NOLJMP M510 NOM510 NOKEYWORD NOSIGNEXTEND OFFSET OLDMACROS NOOLDMACROS OLDSTRUCTS NOOLDSTRUCTS PROC PROLOGUE READONLY NOREADONLY SCOPED NOSCOPED SEGMENT SETIF2 APpen dix B, TurboA sse mbIer syntax sum mar y 247
  • 258. MASM 6.1 compatibility Turbo Assembler 5.0 (TASM32) supports most of the features of Microsoft MASM version 6.1. This Appendix documents the new features added to Turbo Assembler specifically to provide compatibility with MASM 6.0/6.1. Basic data types Turbo Assembler now supports the use of type names as directives when defuUng variables. For example, the line: var DB 10 can now be written as: var BYTE 10 Table C.1 shows the type names and their equivalent directives. Table C.1 Turbo Assembler types and their equivalent directives BYTE DB DWORD DD FWORD DF QWORD DQ TBYTE DT WORD DW APpen dixC, MAS M 6. 1 com pat ibiii t Y 249
  • 259. Signed types . Table C.21ist the specifications of the new signed integer types. Table C.2 Signed integer data types -128 to +127 -32,768 to +32,767 SBYfE SWORD SDWORD· 1 2 4 --'2,147,483,648 to +2,147,483,647 Floating-point types Table C.31ists the specifications for the new floating point types. Table C.3 Floating-point data types REAL8 REALlO Long real lO-byte real 80 15-16 19 3.37 x 10--4932 to 1.18 x 104932 Floating point constants can be designated as decimal constants or encoded hexadecimal constants, as shown in the following examples: ; Real decimal numbers dshort REAL4 34.56 ;IEEE format ddouble REAL8 dtenbyte REAL10 3.456El iIEEE format 3456.0E-2 i10-byte real format ; Hexadecimals, note the required trailing "r" and leading decimal digit hexshort REAL4 4E700000r iIEEE short hexdouble REAL8 4E70000000000000r iIEEE long hextenbyte REAL10 4E776000000000000000rilO-byte real New decision a~d looping directives Turbo Assembler now supports several high level directives to permit program structureS similar to those in higher level languages, such as C++and Object Pascal. These directives generate code for loops and decisions, which are executed depending on the status of a conditional statement. The conditions are tested at rUn-time, and can use the new run-time operators ==, !=, >=, <=, >, <, &&, I I, and!. .IF .ELSE .ELSEIF .ENDIF The directives .IF, .ELSE, .ENDIF generate conditional jumps. If the expression following .IF evaluates to true, then the statements following the .IF are executed until an .ELSE (if any), .ELSEIF (if any), or .ENDIF directive is encountered. If the .IF expression evaluates tofalse, the statements following the .ELSE (if any) are executed until an .ENDIF directive is encountered. Use .ELSEIF to cause a secondary expression to be evaluated if the .IF expression evaluates tofalse. 250 Turbo Assembler User's Guide
  • 260. The syntax for the.IF directives is: .IF expression1 statements [.ELSEIF expression2 statements] [.ELSE statements] .ENDIF Example .IF bx == 16 mov ax,20 .. ELSE if the value in bx' equals 16 mov ax,30 .ENDIF if the value in bx does not equal 16 .WHILE .ENDW The .WHILE directive executes the statements between the .WHILE and the .ENDW as long as the expression following .WHILE evaluates to true, or until a .BREAK directive is encountered. Because the expression is evaluated at the beginning of the loop, the statements within the loop will not execute at all if the expression initially evaluates to false. If a .CONTINUE directive is encountered within the body of the loop, control is passed immediately back to the .WHILE where the expression is re-evaluated. If .BREAK is encountered, control is immediately passed to the statement following the .ENDW directive. The syntax for the .WHILE directives is: . .WHILE expression statements .ENDW Example mov ax, .WHILE ax < 128 mov dx, cx .IF dx == bx mov ax, dx .CONTINUE .ELSEIFax == dx .BREAK .ENDIF inc ax .ENDW i initialize ax to 0 i while ax is less than 128 put the value of cx in dx if dx and bx are equal put the value of dx in ax re-evaluate .WHILE expression if ax equals dx break out of the .WHILE loop increment ax by 1 end of .WHILE loop APpen di xC, MAS M 6. 1 com pat ibiii t Y 251
  • 261. .REPEAT .UNTIL .UNTILCXZ The .REPEAT directive executes the statements between the .REPEAT and the .UNTIL as long as the expression following the .UNTIL (or .UNTILCXZ) evaluates to true, or until a .BREAK directive is encountered. Because the expression is evaluated at the end of the loop, the statements within the loop will execute at least once, even if the expression initially evaluates tofalse. If a .CONTINUE directive is encountered within the body of the loop, control is passed immediately to the .UNTIL where the expression is re-evaluated. If .BREAK is encountered, control is immediately passed to the statement following the .UNTIL (or .UNTILCXZ) directive. The .UNTIL directive generates conditional jumps. The .UNTILCXZ directive generates a LOOP instruction. The syntax for the .REPEAT directives is: .REPEAT statements .UNTIL expression Example mov ax, .REPEAT inc ax .UNTIL ax >= 128 i initialize ax to a i while ax is less than 128 increment ax by 1 i end of .REPEAT loop .BREAK .CONTINUE As noted above, .BREAK and .CONTINUE can be used to alter the program flow within a loop. .CONTINUE causes the loop to immediately re-evaluate its expression, bypassing any remaining statements in the loop. .BREAK terminates the loop and passes control to the statement following the end of the loop. Both .BREAK and .CONTINUE can be combined with an optional .IF directive. If the .IF expression evaluates to true, the .BREAK or .CONTINUE are carried out, otherwise they are ignored. Example mov ax, bx .WHILE ax != ex .BREAK .IFax == dx .CONTINUE .IF ax > dx inc ax .ENDW 252 TurboA sse mbIer Use r's Guide
  • 262. Logical operators Turbo Assembler now supports several C-like logical operators, as shown in Table C.4. Table C.4 New Turbo Assembler logical operators is equal to != is not equal to >= is greater than or equal to <= is less than or equal to > is greater than < is less than && and II or not & bit test Using flags in conditions Turbo Assembler permits the use flag value in conditions. The supported flag names are ZERO?, CARRY?, OVERFLOW?, SIGN?, and PARITY? For example, to use the value of the CARRY flag in a loop expression, use: .WHILE (CARRY?) ; if the CARRY flag is set... statements .ENDW Text Macros A string of characters can be given a symbolic name, and have that name used in the source code rather than the string itself. The named text is referred to as a text macro. Use the TEXTEQU directive to define a text macro. To assign a literal string to a text macro, enclose the string in angle brackets «».For example: myString TEXTEQU <This is my string> To assign one macro to another text macro, assign the macro name as in the example below: myString TEXTEQU <This is my string> myNewString TEXTEQU myString ;value of myString now in myNewString as well To assign a text representation of a constant expression to a text macro, precede the expression with a percent sign (%). For example: value TEXTEQU %(1 + nurn);assigns text representation of resolved expression t~ value Appendix C, MASM 6.1 compatibility 253
  • 263. Text macros are useful for naming strings of text that do not evaluate to integers. For example: . pi WPT arg TEXTEQU <3.14159> TEXTEQU <WORD PTR> TEXTEQU <[bp+4J> floating point constant keyworo-s expression Macro repeat blocks with loop directives. Turbo Assembler supports "repeatblocks", or unnamed macros within a loop directive. The loop directive generates the statements inside the repeat block a specified number Qftimes. . REPEAT loops Use REPEAT to specify the number of times to generate the statements inside the macro. The syntax is: REPEAT constant . statements ENDM Example number LABEL BYTE counter = 0 REPEAT 128 BYTE counter i name the generated data initialize counter i repeat 128 times i allocate a new number counter = counter + 1; increment counter ENDM FOR loops Use the FORloop to iterate through a list of arguments, using the first argument the first time through, the second argument the second time through, and so on. The syntax is: FOR parameter, <argumentList> statements ENDM The parameter represents the name of each argument inside the FOR block. The argumentList is comma separated and enclosed in angle brackets. Example powers LABEL BYTE FOR arg, <1,2,4,8,16,32,64,128> BYTE arg DUP (arg) ENDM 254 Turbo Assembler User's Guide
  • 264. The first iteration through the FOR loop sets arg to 1. The second iteration sets arg to 2. The third sets arg to 4, and so on. Text macros may be used in place of literal strings of values. The VARARG directive can be used in the argumentList to create a variable number of arguments. FORC loops FORC loops are almost identical to FOR loops, except that the argumentList is given a a string, rather than as a comma separated list. The loop reads the string, character by character (including spaces), and uses one character per iteration. The syntax is: FORC parameter, <text> statements ENDM The parameter represents the name of each argument inside the FOR block. The text is a character string and enclosed in angle brackets. Example alphabet LABEL BYTE FORC arg, <ABCDEFGHIJKLMNOPQRSTUVWXYZ> BYTE '&arg' i allocate letter ENDM New Directives For MASM compatibility, Turbo Assembler now supports the directive STRUCT, EXTERN, and PROTO. These directives are synonyms for the STRUC, EXTRN, and PROCDESC directives, respectively. ECHO directive The ECHO directive displays its argument to the standard output device during assembly. It is useful for debugging purposes. The syntax is: ECHO argument EXTERNDEF directive Turbo Assembler treats EXTERNDEF as a PUBLIC declaration in the defining module, and as an external declaration in the referencing module(s). Use EXTERNDEF to make a variable or procedure common to two or more modules. Ifan EXTERNDEF variable or procedure is defined but not referenced in a givenmodule, the EXTERNDEF is ignored; you need not create a symbol as you would using EXTERN. The syntax of the EXTERNDEF statement is: EXTERNDEF [ZangType] name: type Appendix C, MASM 6.1 compatibility 255
  • 265. OPTION directive The OPTION directive lets you make global changes to the behavior of the assembler. The basic syntax of the directive is: OPTION argument For example, to make the expression word size 16 bits, use the statement: OPTION EXPR16 To make the expression word size 32 bits, use the statement: OPTION EXPR32 The available options are listed below: CASEMAP: NONE/NOTPUBLIC/ALL ' NONE causes internal symbol recognition to be case sensitive, and causes the case of identifiers in the .OBJ file to be the same as specified in the EXTERNDEF, PUBLIC, or COMM statement. NOTPUBLIC (default) causes case insensitivity foriI;1.ternal symbol recognition, and has the same behavior as NONE for identifiers in .OBJ files. ALL specifies universal case insensitivity and converts all identifiers to uppercase. DOTNAME/NODOTNAME Enables or disables the use of the dot'{.) as the leading character in variable, macro, structure, union, and member names. The default is disabled. EMUlATOR/NOEMULATOR NOEMULATOR (default) tells the assembler to generate floating point math coprocessor instructions directly. EMULATOR generates floating point math instructions with special fixups for linking with a coprocessor emulator library. EXPR16/EXPR32 Sets the expression word size to 16 or 32 bits. The default is 32 bits. LJMP/NOLJMP En';lbles or disables automatic conditional-jump lengthening. The default is enabled. NOKEYWORD: <keywordLisb Disables the keywords listed in keywordList. For example: OPTION NOKEYWORD:<MASK EXPORT NAME> 256 Turbo Assembler User's Guide
  • 266. PROC: PRIVATE/PUBLIC/EXPORT Allows you to set the default PROC visibility as PRIVATE, PUBLIC, or EXPORT. The default is PUBLIC. SCOPED/NOSCOPED SCOPED (the default) guarantees that all labels inside procedures are local to the procedure. SEGMENT: USE16/USE32/FLAT Sets the global default segment size and the default address size for external symbols defined outside any segment. Visibility in procedure declarations Turbo Assembler supports three visibility modes in procedure (PROC) declarations; PRIVATE, PUBLIC, and EXPORT. The visibility indicates whether the procedure is available to other modules. PUBLIC procedures are availableto other modules. All procedures are PUBLIC by default. PRIVATE procedures are available only within the module in which they are declared. Code in other modules cannot call PRIVATE procedures. If the visibility is EXPORT, the linker places the procedure's name in the export table for segmented executables. EXPORT also enables PUBLIC visibility. Distance in procedure declarations In addition to the ability to specify NEAR or FAR distance in procedure (PROC) declarations, Turbo Assembler now supports the modifiers NEAR16, NEAR32, FAR16, and FAR32 when programming for the 80386, and up, and using both 16 and 32-bit segments. .SIZE operator in MASM mode In MASM mode the size operator returns the values in table C.5 for the given labels. Table C.S Return value of SIZE in MASM mode SHORT OFFOlh NEAR16 OFF02h NEAR32 OFF04h FAR16 OFF05h FAR32 OFF06h APpen di xC, MAS M 6. 1 com pat ibiii tY 257
  • 267. Compatibility issues Turbo Assembler in MASM mode is very compatible with MASM version 6.l. . However,100% compatibility is an ideal that can only be approached, since there is no formal specification for the language and different versions of MASM are not even compatible with each other. For most programs, you will have no problem using Turbo Assembler as a direct replacement for MASM. Occasionally, Turbo Assembler will issue warnings or errors where MASM would not, which usuallymeans that MASM has not detected an erroneous statement. For example, MASM accepts abc EQU [BP+2] PUBLIC abc and.generates a nonsense object file. Turbo Assembler correctly detects this and many other questionable constructs. If you are having trouble assembling a program with Turbo Assembler, you might try using the QUIRKS directive (which enables potentially troublesome features of MASM). For example, . . TASM /JQUIRKS MYFILE might make your program assemble properly. Ifit does, add QUIRKS to the top of your source file. Evenbetter, review Chapter 3 and determine which statement in your source file needs the QUIRKS directive. Then you can rewrite the line(s) of code so that you don't even have to use QUIRKS. For maximum compatibility with MASM, you should use the NOSMART directive along with QUIRKS mode. . One-pass versus two-pass assembly· Normally, Turbo Assembler performs only one pass when assembling code, while MASM performs two. This feature gives Turbo Assembler a speed advantage, but can. introduce minor incompatibilities when forward references and pass-dependent constructions are involved. The command-line option1m specifies the number of passes desired. For maximum compatibility with MASM, two passes (1m2) should be used. (See Chapter 2 for a complete discussion of this option.) The 1m2 command-line switch will generate a MASM-style compatibility when the following constructs are present: • IFl and IF2 directives • ERRl and ERR2 directives • ESLEIFl and ELSEIF2 directives • Forward references with IFDEF or IFNDEF • Forward references with the .TYPE operator • Recursively defined numbers, such as NMBR=NMBR+l 258 Turbo Assembler User's GuJde
  • 268. • Forward-referenced or recursively defined text macros, such as LNAME CATSTR LNAME,<l> • Forward-referenced macros Environment variables Turbo Assembler doesn't use environment variables to control default options. However, you can place default options in a configuration file and then set up different configuration files for different projects. Ifyou use INCLUDE or MASM environment variables to configure MASM, you'll have to make a configuration file for Turbo Assembler. Any options that you have specified using the MASM variable can simply be placed in the configuration file. Any directories that you have specified using the INCLUDE variable should be placed in the configuration file using the II command-line option. Microsoft binary floating-point format By default, older versions of MASM generated floating-point numbers in a format incompatible with the IEEE standard floating-point format. MASM version 6.1 generates IEEE floating-point data by default and has the .MSFLOAT directive to specify that the older format be used. Turbo Assembler does not support the old floating-point format, and therefore does not letyou use .MSFLOAT. Appendix C, MASM 6. 1c0 mpat ibiii t Y 259
  • 269. 260 TurboA sse mbIer Use r's Guide
  • 270. Error messages This chapter describes all the messages that Turbo Assembler generates. Messages usually appear on the screen, but you can redirect them to a file or printer using the standard OS/2 redirection mechanism of putting the device or file name on the command line, preceded by the greater than (» symbol. For example, TASMMYFILE >ERRORS Turbo Assembler generates several types of messages: • Information messages • Warnillg messages • Error messages • Fatal error messages Information messages Turbo Assembler displays two information messages: one when it starts assembling your source file(s) and another when it has finished assembling each file. Here's a sample startup display: Turbo Assembler Version 4.0 Copyright (C) 1988, 1993 Borland International Assembling file: TEST.ASM When Turbo Assembler finishes assembling your source file, it displays a message that summarizes the assembly process; the message looks like this: Error messages: None Warning messages: None Passes: 1 Remaining memory: 279k You can suppress all information messages by using the rr command-line option. This only suppresses the information messages if no errors occur during assembly. If there are any errors, the rroption has no effect and the normal startup and ending messages appear. Appendix D, Error messages 261
  • 271. Warning and error messages Warning messages let you know that something undesirable may have happened while assembling a source statement. This might besomething such as the Turbo Assembler making an assumption that is usually valid, but might not always be correct. You should always examine the cause of warning messages to see if the generated code is what you wanted. Warning messages won't stop Turbo Assembler from generating an object file. These messages are displayed using the following format: **Warning** filename (line) message If the warning occurs while expanding a macro or repeat block, the warning message contains additional information, naming the macro and the line within it where the warning occurred: **Warning** filename (line) macroname(macroline) message Error messages, on the otherh~d, will prohibit Turbo Assembler from generating an object file, but assembly will continue to the end of the file. Here's a typical error message format: **Error** filename (line) message If the error occurs while expanding a macro or repeat block, the error message contains additional information, naming the macro and the line within it where the error occurred: **Error** filename (line) macroname(macroline) message Fatal error messages cause Turbo Assembler to immediately stop assembling your file. Whatever caused the error prohibited the assembler from being able to continue. The following list arranges Turbo Assembler's messages in alphabetical order: 32-bit segments not allowed without .386 Has been extended to work with the new abilityto specify USE32 in the .MODEL statement and the LARGESTACK command. Formerly was "USE32 not allowed without .386." Argument mismatch Argument sizes did not agree. For example, foo proctype pascal :word, :dword fooproc proc foo al:word, a2:dword endp call fooproc,ax,bx ;Argument mismatch. Argument needs type override The expression needs to have a specific size or type supplied, since its size can't be determined from the context. For example, mov [bx],l You can usually correct this error by using the PTR operator to set the size of the operand: mov WORD PTR[bx],l Argument to operation or instruction has illegal size An operation was attempted on something that could not support the required operation. For example, Q LABEL QWORD QNOT =not Q ;can't negate a qword 262 TurboA sse mbIer Use r' s Guide
  • 272. Arithmetic overflow A loss of arithmetic precision occurred somewhere in the expression. For example, X = 20000h * 20000h ;overflows 32 bits All calculations are performed using 32-bit arithmetic. ASSUME must be segment register You have used something other than a segment register in an ASSUME statement. For example, ASSUME ax:CODE You can only use segment regi~ters with the ASSUME directive. Bad k~yword in SEGMENT statement One of the align/combine/use arguments to the SEGMENT directive is invalid. For example, DATA SEGMENT PAFA PUBLIC ;PAFA should be PARA Bad switch You have used an invalid command-line option. See Chapter 2 for a'description of the command-line options. Can't add relative quantities You have specified an expression that attempts to add together two addresses, which is a meaningless operation. For example, ABC DB ? DEF = ABC + ABC ;errorI can't add two relatives You can subtract two relative addresses, or you can add a constant to a relative address, as in: XYZ DB 5 DUP (0) XYZEND EQU $ XYZLEN = SYZEND - XYZ XYZ2 = XYZ + 2 ;perfectly legal ;legal also Can't address with currently ASSUMEd segment registers An expression contains a reference to a variable for which you have not specified the segment register needed to reach it. For example, DSEG SEGMENT ASSUME ds:DSEG mov si/MPTR DSEG ENDS ;no segment register to reach XSEG XSEG SEGMENT MPTR DW XSEG ENDS Can't convert to pointer Part of the expression could not be converted to a memory pointer, for example, by using the PTR operator, mov el, [BYTE PTR all ;can't make AL into pointer Can't emulate 8087 instruction The Turbo Assembleris set to generate emulated floating-point instructions, either via the /E command-line option or by using the EMUL directive, but the current instruction can't be emulated. For example, EMUL FNSAVE [WPTRl ;can't emulate this The following instructions are not supported by floating-point emulators: FNSAVE, FNSTCW, FNSTENV, and FNSTSW. Can't find @file You have specified an indirect command file name that does not exist. Make sure that you supply the complete file name. Turbo Assembler does not presume any default extension for the file name. You've probably run out of space on the disk where you asked the cross-reference file to be written. Appendix D, Error messages 263
  • 273. Can't generate instance of type You attempted to generate an instance of a named type that does not have an instance. For example, foo typedef near foo ? iNEARs have no instance. Can't locate file You have specified a file name with the INCLUDE directive that can't be found. An INCLUDE file could not be located. Make sure that the name contains any necessary disk letter or directory path. Can't make variable public The variable is already declared in such a way that it can't be made public. For example, EXTRN ABC:NEAR PUBLIC ABC ierror, already EXTRN Can't override ES segment The current statement specifies an override that can't be used with that instruction. For example, stos DS:BYTE PTR[diJ Here, the STOS instruction can only use the ES register to access the destination address. Can't subtract dissimilar relative quantities An expression subtracts two addresses that can't be subtracted from each other, such as when they are each in a different segment: . SEGl SEGMENT A: SEGl ENDS SEG2 SEGMENT B: mov aX,B-A SEG2 ENDS iillegal, A and B in different segments Can't use macro name in expression A macro name was encountered as part of an expression~ For example, MyMac MACRO ENDM mov aX,MyMac iwrong! Can't use this outside macro You have used a directive outside a macro definition that can only be used inside a macro definition. This includes directives like ENDM and EXITM. For example, DATA SEGMENT ENDM ierror, not inside macro Code ordata emission to undeclared segment A statement that generated code or data is outside of any segment declared with the SEGMENT directive. For example, iFirst line of file inc bx END ierror, no segment You can only emit code or data from within a segment. Constant assumed to mean immediate constant This warning appears if you use an expression such as [0], which under MASM is interpreted as simply O. For example, , mov ax, [OJ imeans mov ax,O NOT mov aX,DS: [OJ 264 TurboA sse mbIer Use r 's Guide
  • 274. Constant too large You have entered a constant value that is properly formatted, but is too large. For example, you can only use numbers larger than Offffh when you have enabled 80386 or i486 instructions with the .386/.386P or .486/.486P directives. CS not correctly assumed A near CALL or JMP instruction can't have as its target an address in a different segment. For example, SEGl SEGMENT LABl LABEL NEAR SEGl ENDS SEG2 SEGMENT jmp LABl ;error, ,wrong segment SEG2 ENDS This error only occurs in MASM mode. Ideal mode correctly handles this situation. , CS override in protected mode The current instruction requires a CS override, and you are assembling instructions for the 80286, 80386, or i486 in protected mode (P286P, P386P, or P486 directives). For example, P286P .CODE CVAL DW mov CVAL,l ;generates CS override The /P command-line option enables this warning. When running in protected mode, instructions with CS overrides won't work without you taking special measures. CS unreachable from current segment When defining a code label using colon (:), LABEL or PROC, the CS register is not assumed to either the current code segment or to a group that contains the current code segment. For example, PROGl SEGMENT .ASSUME cs:PROG2 START: ;error, bad CS assume This error only occurs in MASM mode. Ideal mode correctly handles this situation. Data or code written to uninitialized segment You have inadvertently written initialized code or data to an uninitialized segment. For example, .data? msg .db 'Hello',O ; error, uninitialized segment Declaration needs name You have used a directive that needs a symbol name, but none has been supplied. For example, PROC ierror, PROC needs a name ret ENDP You must always supply a name as part of a SEGMENT, PROC, or STRUC declaration. In MASM mode, the name precedes the directive; in Ideal mode, the name comes after the directive. I Appendix D, Error messages 265
  • 275. Directive not allowed inside structure definition You have used a directive inside a STRUC definition block that can't be used there. For example, X STRU MEM1 DB ORG $+4 MEM2 DW 7 ENDS ierror, can't use ORG inside STRU Also, when declaring nested structures, you cannot give a name to any that are nested. For example, FOO STRU F002 STRUC ican't name inside ENDS ENDS If you want to use a named structure inside another structure; you must first define the structure and then use that structure name inside the second structure. Duplicate dummy argument: _ A macro defined with the MACRO directive has more than one dummy parameter with the same name. For example, XYZ MACRO A,A ierror, duplicate dummy name DB A ENDM Each dummy parameter in a macro definition must have a different name. ELSE or ENDIF without IF An ELSE or ENDIF directive has no matching IF directive to start a conditional assembly block. For example, BUF DB 10 DUP (7) ENDIF ierror, no 'matching IFxxx Error writing to listing file You've probably run out of space on the disk where you asked the listing file to be written. Error writing to object file You've probably run out of space on the disk where you asked the object file to be written. Expecting METHOD keyword The extended structure statement for defining objects expects the keyword METHOD after the parent object. Expecting offset quantity An expression expected an operand that referred to an offset within a segment, but did not encounter the right sort of operand. For example, ,-CODE SEGMENT mov ax, LOW CODE CODE ENDS Expecting offset or pointer quantity An expression expected an operand that referred to an offset within a specific segment, but did not encounter the right sort of operand. Forexample, CODE SEGMENT mov ax,SEG CODE CODE ENDS Expecting pointer type ierror, code is a segment not i -a location within a segment The current instruction expected an operand that referenced memory. For example, les di,4 ina good, 4 is a constant 266 TurboA sse mbIer Use r's Guide
  • 276. Expecting record field name You used a SETfIELD or GETFIELD instruction without a field name following it. Expecting register ID The USES part of the CALL..METHOD expects register name(s). Expecting scalar type An instruction operand or operator expects a constant value. For example, BB DB 4 rol . ax,BB iROL needs constant Expecting segment or group quantity A statement required a segment or group name, but did not find one. For example, DATA SEGMENT ASSUME ds:FOO ierror, FOO is not group or segment iname FOO DW 0 DATA ENDS Extra characters on line A valid expression was encountered, but there are still characters left on the line. For example, ABC = 4 shl 3 3 imissing operator between 3 and 3 This error often happens in conjunction with another error that caused the expression parser to lose track of what you intended to do. File not found The source file name you specified on the command line does not exist. Make sure you typed the name correctly, and that you included any necessary drive or path information if the file is not in the current directory. File was changed or deleted while assembly in progress Another program, such as a pop-up utility, has changed or deleted the file after Turbo Assembler opened it. Turbo Assembler can't reopen a file that was previously opened successfully. Forward reference needs override An expression containing a forward-referenced variable resulted in more code being required than Turbo Assembler anticipated. This can happen either when the variable is unexpectedly a far address for a JMP or CALL or when the variable requires a segment override in order to access it. For example, ASSUME cs:DATA call A A PROC FAR mov ax,MEMVAR DATA. SEGMENT ipresume near call iOOpS, it's far idoesn't know it needs override MEMVAR DW ? ;oops, needs override Correct this by explicitly supplying the segment override or FAR override. Globaltype doesn't match symbol type . . This warning is given when a symbol is declared using the GLOBAL statement and is also defined in the same module, but the type specified in the GLOBAL and the actual type of the symbol don't agree. APpen di x D, Err 0 r messag es 267
  • 277. 10 not member of structure In Ideal mode, you have specified a symbol that is not a structure member name after the period (.) structure member operator. For example, . IDEAL STRUC DEMO DB ENDS COUNT DW 0 mav aX t [(DEMO bx) .COUNT] iCOUNT not part of structure You must follow the period with the name of a member that belongs to the,structure name that precedes the period. This error often happens in conjunction with another error that caused the expression parser to lose track of what you intended to do. Illegal forward reference A symbol has been referred to that has not yet been defined, and a directive or operator requires that its argument not be forward-referenced. For example, IF MYSYM ierrort MYSYM not defined y~t ENDIF MYSYM EQU 1 Forward references may not be used in the argument to any of the IFxxx directives, nor as the count in a DUP expression. . Illegal immediate An instruction has an immediate (constant) operand wher~ one is not allowed. For example, mov 4tal Illegal indexing mode An instruction has an operand that specifies an illegal combination of registers. For example, mov alt[si+ax] On all processors except the 80386, the only valid combinations of index registers are: BX, BP, SI, DI, BX+SI, BX+DI, BP+SI, BP+DI. Illegal instruction A source line starts with a symbol that is neither one of the known directives nor a valid instruction mnemonic. move ax t 4 ishould be 11 MOV11 Illegal instruction for currently selected processor(s) A source line specifies an instruction that can't be assembled for the current processor. For example, .8086 push 1234h ino immediate push on 8086 When Turbo Assembler first starts assembling a source file, it generates instructions for the 8086 processor, unless told to do otherwise. If you wish to use the extended instruction mnemonics available on the 186/286/386 processors, you must use one of the directives that enables those instructions (P186; P286, P386). Illegal local argument The LOCAL directive inside a macro definition has an argument that is not a valid symbol name. For example, X MACRO LOCAL 123 ENDM inot a symbol 268 TurboA sse mbIer Use r's Guide
  • 278. Illegal local symbol prefix The argument to the LOCALS directive specifies an invalid start for local symbols. For example, LOCALS XYZ ierror, not 2 characters The local symbol prefix must be exactly two characters that themselves are a valid symbol name, such as _ -' @@, and so on (the default is @@). Illegal macro argument A macro defined with the MACRO directive has a dummy argument that is not a valid symbol name. For example, X MACRO 123 iinvalid dummy argument ENDM Illegal memory reference An instruction has an operand that refers to a memory location, but a memory location is not allowed for that operand. For example, mov [bxl ,BYTE PTR A ierror, can't move from MEM to MEM Here, both operands refer to a memory location, which is not a legal form of the MOV instruction. On the 80x86 family of processors, only one of the operands to an instruction can refer to a memory location. Illegal number A number contains one or more ~haracters that are not valid for that type of number. For example, Z = OABCGh Here, G is not a valid letter in a hexadecimal number. Illegal origin address You have entered an invalid address to set the current segment location ($). You can enter either a constant or an expression using the location counter ($), or a symbol in the current segment. Illegal override in structure You have attempted to initialize a structure member that was defined using the DUP operator. You can only initialize structure members that were declared without DUP. Illegal override register A register other than a segment register (C5, D5, E5, 55, and on the 80386, F5 and G5) was used as a segment override, preceding the colon (:) operator. For example, . mov dX:XYZ,l iDX not a segment register Illegal radix The number supplied to the .RADIX directive that sets the default number radix is invalid. For example, .RADIX 7 ino good The radix can only be set to one of 2,8, la, or 16. The number is interpreted as decimal no matter what the current default radix is. Illegal register for instruction An illegal register was used as the source of a SETFIELD instruction or the destination of a GETFIELD instruction. Illegal register multiplier You have attempted to multiply a register by a value, which is not a legal operation; for example, movax*3,l The only context where you can multiply a register by a constant expression is when specifying a scaled index operand on the 80386 processor. Illegal segment address This error appears if an address greater than 65,535 is specified as a constant segment address; for example, FOO SEGMENT AT 12345h Illegal use of constant A constant appears as part of an expression where constants can't be used. For example, mov bx+4,5 Appendix D, Error messages 269
  • 279. Illegal use of register A register name appeared in an expression where it can't be used. For example, . X = 4 sh1 ax ican't ,use register with SHL operator Illegal use of segment register A segment register name appears as part of an instruction or expression where segment registers cannot be used. For example, add ·SS,4 iADD can't use segment regs IllegalUSES register , You have entered an invalid register to push and pop as part of entering and leaving a procedure. The valid registers follow: AX BX CX Dr DS DX ES SI If you have enabled the 80386 processor with the .386 or .386P directive, you can use the 32-bit equivalents for these registers. Illegal version ID Occurs when an illegal version ID was selected in t~e VERSION statement or IV switch. Illegal.warning ID You have entered an invalid three-character warning identifier. See the options discussed in Chapter 2 for a complete list of the allowedwarning identifiers. Instruction can be compacted with override The code generated contains NOP padding, due to some forward-referenced symbol. You can either remove the forward reference or explicitly provide the type information as part of the expression. For example, jmp X iwarning here jmp SHORT X ino warning X: Insufficient memory to process command line You have specified a command line that is either longer than 64K or can't be expanded in the available memory. Either simplify the command line or run Turbo Assembler with more memory free. Internal error This message should never happen during normal operation of Turbo Assembler. Save the file(s) that caused the error and report it to Borland's Technical Support department. Invalid command line The command line that you used to start Turbo Assembler is badly formed. For example, TASM ,MYFILE does not specify a source file to assemble. See Chapter 2 for a complete description of the Turbo Assembler command line. Invalid model type The model directive has an invalid memory model keyword. For example, .MODEL GIGANTIC Valid memory models are tiny, small, compact, medium, large, and huge, Invalid number after You have specified a valid command-line switch (option), but have not supplied a valid numeric argument following the switch. See Chapter 2 for a discussion of the command-line options. Invalid operand(s) to instruction The instruction has a combination of operands that are not permitted. For example, fadd ST(2),ST(3) Here, FADD can only refer to one stack register by name; the other must be the stack top. 270 TurboA sse mbIer Use r's Guide
  • 280. Labels can't start with numeric characters You have entered a symbol that is neither a valid number nor a valid symbol name, such as 123XYZ. Language differs from procedure type You attempted to use a different language than what was contained in the procedure type declaration. For example, foo proctype windows pascal :word fooproc proc foo al:word endp call fooproc c,ax iLanguage doesn't match. Language doesn't support variable-length arguments You specified a variable-length stack frame with a language that doesn't support it. For example, foo proctype pascal :word, :unknown iPascal can't have ivariable arguments. Line too long-truncating The current line in the source file is longer than 255 characters. The excess characters will be ignored. Location counter overflow The current segment has filled up, and subsequent code or data will overwrite the beginning of the segment. For example, ORG OFFFOh ARRAY OW 20 OOP (0) ioverflow Method CALL requires object name :The CALL..METHOD statement c.annot obtain the object type from this instance pointer. You must specify the object name. Missing argument list An IRP or IRPC repeat block directive does not have an argument to substitute for the dummy parameter. For example, IRP X ino argument list DB X ENDM IRP and IRPC must always have both a dummy parameter and an argument list. Missing argument or < You forgot the angle brackets or the entire expression in an expression that requires them. For example, ifb ineeds an argument in <>s Missing argument size variable . An ARG or LOCAL directive does not have a symbol name following the optional =at the end of the statement. For example, ARG A:WORD,B:DWORD= ierror, no name after = LOCAL X:TBYTE= isame error here ARG and LOCAL must always have a symbol name if you have used the optional equal sign (=) to indicate that you want to define a size variable. Missing COMM ID A COMM directive does not have a symbol name before the type specifier. For example, COMM NEAR ierror, no symbol name before "NEAR" COMM must always have a symbol name befor~ the type specifier,Jollowed by a colon (:) and then the type specifier. APpen di x 0, Err 0 r messag es .271
  • 281. Missing dummy argument . An IRP or IRPC repeat block directive does not have a dummy parameter. For example, RP ino dummy parameter DB X ENDM IRP and IRPC must always have both a dummy parameter and an argument list. Missing end quote A string or character constant did not end with a quote character. For example, DB "abc imissing ' at end of AB mov aI, 'X imissing , after X You should always end a character or string constant with a quote character matching the one that started it. Missing macro ID A macro defined with the MACRO directive has not been given a name. For example, MACRO ierror, no name DB A ENDM Macros must always be given a name when they are defined. Missing module name You have used the NAME directive but you haven't supplied a module name after the directive. Remember that the NAME directive only has an effect in Ideal mode. Missing or illegal language ID You have entered something other than one of the allowed language identifiers after the .MODEL directive. See Chapter 7for a complete description of the .MODEL directive. Missing or illegal type specifier A statement that needed a type specifier (like BYTE, WORD, and so on) did not find one where expected. For example, ! RED LABEL XXX ierror, "XXX" is not a type specifier Missing table member ID . A CALL..METHOD statement was missing the method name after the METHOD keyword. Missing term in list In Ideal mode, a directive that can accept multiple arguments (EXTRN, PUBLIC, and so on) separated by commas does not have an argument after one of the commas in the list. For example, EXTRN XXX:BYTE"YYY:WORD In Ideal mode, all argument lists must have their elements separated by precisely one comma, with no comma at the end of the list. , Missing text macro You have notsupplied a text macro argument to a directive that requires one. For example, NEWSTR SUBSTR iERROR - SUBSTR NEEDS ARGUMENTS Model must be specified first You used one of the simplified segmentation directives without first specifying a memory model. For example, .CODE ierror, no .MODEL first You must always specify a memory model using the .MODEL directive before using any of the other simplified segmentation directives. 272 TurboA sse mbIer Use r's Guide
  • 282. Module is pass-dependent-compatibility pass was done This warning occurs if a pass-dependent construction was encountered and the 1m command-line switch was specified. A MASM-compatible pass was done. You put a symbol name after a directive, and the symbol name should come first. For example, STRUC ABC ;error, ABC must come before STRUC Since Ideal mode expects the name to come after the directive, you will encounter this error if you try to assemble Ideal mode programs in MASM mode. Near jump or call to different cs . This error occurs if the user attempts to perform a NEAR CALL or JMP to a symbol that's defined in an area where CS is assumed to a different segment. Need address or register An instruction does not have a second operand supplied, even though there is a comma present to separate two operands; for example, mov ax, ;no second operand Need angle brackets for structure fill A statement that allocates storage for a structure does not specify an initializer list. For example, STRl STRU Ml DW M2 DD ENDS STRl ;no initializer list Need colon An EXTRN, GLOBAL, ARG, or LOCAL statement is missing the colon after the type specifier (BYTE, WORD, and so on). For example, EXTRN XBYTE,Y:WORD ;X has no colon Need expression An expression has an operator that is missing an operand. For example, X = 4 + * 6 Need file name after INCLUDE An INCLUDE dir~ctive did not have a file name after it. For example, INCLUDE ;include what? In Ideal mode, the file name must be enclosed in quotes. Need left parenthesis A left parenthesis was· omitted that is required in the expression syntax. For example, DB 4 DUP 7 You must always enclose the expression after the DUP operator in parentheses. Need method name The CALL..METHOD statement requires a method name after the METHOD keyword. Need pointer expression This error only occurs in Ideal mode and indicates that the expression between brackets ([ ]) does not evaluate to a memory pointer. For example, mov ax, [WORD PTR] In Ideal mode, you must always supply a memory-referencing expression between the brackets. APpen di x 0, Err 0 r messag es 273
  • 283. Need quoted string You have entered something other than a string ofcharacters between quotes where it is required. In Ideal mode, several directives require their argument to be a quoted string. For example, IDEAL . DISPLAY"ALL 'DONE" Need register in expression You have entered an expression that does not contain a register name where one is required. Need right angle bracket An expression that initializes a structure, union, or record does not end with a > to match the < that started the initializer list. For example, MYSTRUC STRUCNAME <1,2,3 Need right curly bracket Occurs during a named structure, table, or record fill when a '}' is expected but not found. Need right parenthesis An expression contains a left parenthesis, but no matching right parenthesis. For example, X = 5 * (4 + 3 You must always use left and right parentheses in matching pairs. Need right square bracket An expression that references a memory location does not end with a ] to match the [ that started the expression. For ex~mple, mov ax, lsi ;error, no closing J after SI You must always use square brackets in matching pairs. Need stack argument A floating-point instruction does not have a second operand supplied, even though there is a comma present to separate two operands. For example, fadd ST, Need structure member name In Ideal mode, the period C.) structure member operator was followed by something that was not a structure member name. For example, IDEAL STRUC DEMO DB ENDS COUNT DW mov ax, [(DEMO bx) .J You must always follow the period operator with the name of a member in the structure to its left. Not expecting group or segment quantity You have used a group or segment name where it can't be used. For example, CODE SEGMENT rol ax,CObE ;error, can't use segment name here One non-null field allowed per union expansion When initializing a union defined with the UNION directive, more than one value was supplied. For example, U UNION ENDS DW DD UINSTU <1,2> ierror, should be <7,2> or <1,?> A union can only be initialized to one value. 274 TurboAsse mbIer Use r 's Guide
  • 284. Only one startup sequence allowed This error appears if you have more than one .STARTUP or STARTUPCODE statement in a module. Open conditional The end of the source file has been reached as defined with the END directive, but a conditional assembly block started with one of the IFxxx directives has not been ended with the ENDIF directive. For example, IF BIGBUF END ino ENDIF before END This usually happens when you type END instead of ENDIF to end a conditional block. Open procedure The end of the source file has been reached as defined with the END directive, but a procedure block started with the PROC directive has not been ended with the ENDP directive. For example, MYFUNC PRO END ino ENDIF before ENDP This usually happens when you type END instead of ENDP to end a procedure block. Open segment The end of the source file has been reached as defined with the END directive, but a segment started with the SEGMENT directive has not been ended with the ENDS directive. For example, DATA SEGMENT END ino ENDS before END This usually happens when you type END instead of ENDS to end a segment. Open structure definition The end of the source file has been reached as defined with the END directive, but a structure started with the STRUC directive has not been ended with the ENDS directive. For example, X STRU VALl DW END ino ENDS before it This usually happens when you type END instead of ENDS to end a structure definition. Operand types do not match The size of an instruction operand does not match either the other operand or one valid for the instruction; for example, ABC DB 5 mov ax, ABC Operation illegal with procedure type You used the structure member operator on an expression whose type is a procedure. For example, foo proctype pascal :word mov ax, [foo ptr [bx]) .member iThings of type Faa ihave no members Operation illegal with static table member A '.' operator was used to obtain the address of a static table member. This is illegaL Out of hash space The hash space has one entry for each symbol you define in your program. It starts out allowing 16,384 symbols to be defined, as long as Turbo Assembler is running with enough free memory. If your program has more than this many symbols, use the /KH command-line option to set the number of symbol entries you need in the hash table. APpen di x 0 J Err 0 r messag es 275
  • 285. Out of memory You don't have enough free memory for Turbo Assembler to assemble your file. If you have any TSR (RAM-resident) programs installed, you can try removing them from memory and try assembling your file again. You may have to reboot your system in order for memory to be properly freed. Another solution is to split the source file into two or more source files, or rewrite portions of it so that it requires less memory to-assemble. You can also use shorter symbol names, reduce the number of comments in macros, and reduce the number of forward references in your program. Out of string space You don't have enough free memory for symbol names,file nameS, forward-reference tracking information, and macro text. A maximum of S12K is allowed, and your module has exceeded this maximum. Pass-dependent construction'encountered The statement may not behave as you expect,due to the one-pass nature of Turbo Assembler. For example, IF1 ENDIF IF2 ENDIF iHappens on assembly pass iHappenson listing pass Most constructs that generate this error can be re-coded to avoid it, often by removing forward references. Pointer expression needs brackets In Ideal mode, the operand contained a memory-referencing symbol that was not surrounded by brackets to indicate that it references a memory location. For example, B DB 0 mov al,B iwarning, Ideal mode needs [B] Since MASM mode does not require the brackets, this is only a warning. Positive count expected A DUP expression has a repeat count less than zero. For example, BUF -1 DUP (?) ierror; count < 0 The count preceding a DUP must always be 1 or greater. Procedure has too many arguments A procedure was declared with too many arguments. For example, footype PROCTYPE pascal :word, :dword foo proc footype arg a1:word,a2:dword,a3:word nop endp Procedure needs more arguments itoo many arguments were declared for ifor this proc A procedure was declared with to? few arguments. For example, footyp~ PROCTYPE pascal :word , :dword foo proc footype arg a1:word nop ret endp Record field too large iNeeds a DWORD argument somewhere too. When you defined a record, the sum total of all the field widths exceeded 32 bits. For example, AREC RECORD RANGE:12,TOP:12,BOTTOM:12 276 TurboA sse mbIer Use r's Guide
  • 286. Record member not found A record member was specified in a named record fill that was not part of the specified record. Recursive definition not allowed for EQU An EQU definition contained the same name that you are defining within the definition itself. For example, ABC EQU TWOTIMES ABC Register must be AL or AX An instruction which requires one operand to be the AL or AX register has been given an invalid operand. For example, IN CL,dx ierror, "IN" must be to AL or lJ,. Register must be DX An instruction which requires one operand to be the DX register has been given an invalid operand. For example, IN AL,cx ierror, must be DX register instead of CX Relative jump out of range by _ bytes A conditional jump tried to reference an address that was greater than 128 bytes before or 127bytes after the current location. If this is in a USE32 segment, the conditional jump can reference between 32,768 bytes before and 32,767 bytes after the current location. Relative quantity illegal An instruction or directive has an operand that refers to a memory address in a way that can't be known at assembly time, and this is not allowed. For example, DATA SEGMENT PUBLI X DB 0 IF OFFSET X GT 127 inot known at assemble time Reserved word used as symbol You have created a symbol name in your program that Turbo Assembler reserves for its own use. Your program will assemble properly, but it is good practice not to use reserved words for your own symbol names. Rotate count must be constant or CL A shift or rotate instruction has been given an operand that is neither a constant nor the CL register. For example, rol ax,DL ierror, can't use DL as count You can only use a constant value or the CL register as the second operand to a rotate or shift instruction. Rotate count out of range A shift or rotate instruction has been given a second operand that is too large. For example, .8086 shl DL,3 .286 ierror, 8086 can only shift by ro~ ax,40 ierror, max shift is 31 The 8086 processor only allows a shift count of I, but the other processors allow a shift count up to 31. Segment alignment not strict enough The align boundary value supplied is invalid. Either it is not a power of 2, or it specifies an alignment stricter than that of the align type in the SEGMENT directive. For example, DATA SEGMENT PARA ALIGN 32 ALIGN 3 ierror, PARA is only 16 ierror, not power of 2 APpen di x 0, Err 0 r mes sag es 277
  • 287. Segment attributes illegally redefined A SEGMENT directive reopen a segment that has been previously defined, and tries to give it different attributes. For example, DATA SEGMENT BYTE PUBLI DATA ENDS DATA SEGMENT PARA DATA ENDS ierror, previously had byte alignment If you reopen asegment, the attributes you supply must either match exactly or be omitted entirely. If you don't supply any attributes when reopening a segment, the old attributes will be used. Segment name is superfluous i This warning appears with a .CODE xxx statement, where the model specified doesn't allow more than code segment. String too long You have built a quoted string that is longer than the maximum allowed length of 255. Style differs from procedure type Youattempted to use a different language style than the declaration of the procedure type contained. For example, foo proctype windows pascal :word fooproc proc foo a1:word endp call fooproc normal pascal,ax iStyle doesn't match. Symbol already defined: _ The indicated symbol has previously been declared with the same type. For example, BB DB 1,2,3 BB DB ? ierror, BB already defined Symbol already different kind , , The indicated symbol has already been declared before with a different type. For example, BB DB 1,2,3 BB DW ? ierror, BB already a byte Symbol has no width or mask The operand of a WIDTH or MASK operator is not the name of a record or record field. For example, B DB O· mov aX,MASK B iB is not a record field Symbol is'not a segment or already part of a.group The symbol has either already been placed in a group or it is not a segment name. For example, DATA SEGMENT DATA DGROUP DGROUP2 ENDS GROUP DATA GROUP DATA ierror, DATA already belongs to iDGROUP Text macro expansion exceeds maxilhum line length This error occurs when expansion of a text macro causes the maximum allowable line length to be exceeded. 278 TurboA sse mbIer Use r's Guide
  • 288. Too few arguments to procedure You called a procedure using too few arguments. For example, fooproctype pascal :word, :dword fooproc proc foo a1:word, a2:dword endp call fooproc,ax iToo few arguments. Too few operands to instruction The instruction statement requires more operands than were supplied. For example, add ax imissing second arg Too many arguments to procedure You called a procedure using too many arguments. For example, foo proctype pascal :word, :dword fooproc proc foo a1:word, a2:dword endp call fooproc,ax,bx cx,dx iToo many arguments. Too many errors found Turbo Assembler has stopped assembling your file because it contained so many errors. You may have made a few errors that have snowballed. For example, failing to define a symbol that you use on many lines is really a single error (failing to define the symbol), but you will get an error message for each line that referred to the symbol. Turbo Assembler will stop assembling your file if it encounters a total of 100 errors or warnings. Too many errors or warnings No more error messages will be displayed. The maximum number of errors that will be displayed is 100; this number has been exceeded. Turbo Assembler continues to assemble and prints warnings rather than error messages. Too many initial values You have supplied too many values in a structure or union initialization. For example, XYZ STRU A1 DB ? A2 DD,? XYZ ENDS ANXYZ XYZ <1,2,3> ierror, only 2 members in XYZ You can supply fewer initializers than there are members in a structure or union, but never more. Too many register multipliers in expression An 80386 scaled index operand had a scale factor on more than one register. For example, mov EAX, [2*EBX+4*EDX] itoo many scale$ Too many registers in expression The expression has more than one index and one base register. For example, mov ax, [BP+S1+D1] ican't have S1 and DI Too many USES registers You specified more than 8 USES registers for the current procedure. Trailing null value assumed A data statement like DB, OW, and so on, ends with a comma. TASM treats this as a null value. For example, db 'hello' ,13,10, isame as ... ,13 ,10 ,? Undefined symbol The statement contains a symbol that wasn't defined anywhere in the source file. APpen di x D, Et r0 r messag es 279
  • 289. Unexpected end of file (no END directive) The source file does not have an END directive as its last statement. All source files must have an END statement. Unknown character The current source line contains a character that is not part of the set ofcharacters that make up Turbo Assembler symbol names or expressions. For example, add aX,!l ierror, exclamation is illegal character Unmatched ENDP: The ENDP directive has a name that does not match the PROC directive that opened the procedure block. For example, ABC PRO XYZ ENDP ierror, XYZshould be ABC Unmatched ENDS: The ENDS directive has a name that does not match either the SEGMENT directive that opened a segment or the STRUC or UNION directive that started a structure or union definition. For example, ABC STRU XYZ ENDS DATA SEGMENT CODE ENDS User-generated error ierror, XYZ should be AB ierroI, code should be DATA An error has been forced by one of the directives, which then forces an error. For example, .ERR ishouldn't get here USES has no effect without language This warning appears if you specify a USES statement when no language is in effect. Value out of range The constant is a valid number, but it is too large to be used where it appears. For example, DB 400 Variable length parameter must be last parameter If a variable-length parameter is present, it must be the last parameter. For example, foo proctype pascal :word, :unknown, :word iNot allowed. 280 Turbo Assembler User's Guide
  • 290. Symbols ! character 167 $symbol 109 % expression evaluation character 167 %immediate macro directive 171 & character in macros 162 + addition operator 72 . (period) character Ideal mode 32 .186 directive 76 .286 directive 76 .286C directive 76 .286P directive 76 .287 directive 79 .386 directive 76 386 processor protected mode 21 .386C directive 76 .386P directive 76 .387 directive 79 .486 directive 76 .486C directive 76 .486P directive 76 .487 directive 76 80186 processor .186 directive 76 P186 directive 76 80286 processor .286 directive 76 .286C directive 76 .286P directive 76 protected mode 21 80287 coprocessor .287 directive 79 P287 directive 79 80386 processor .386 directive 76 .386C directive 76 .386P directive 76 loop instructions for .147 P386 directive 76 P386N directive 76 P386P directive 76 protected mode 21 80387 coprocessor .387 directive 79 P387 directive 79 80486 processor Index .486 directive 76 .486C directive 76 .486P directive 76 P486 directive 76 P486N directive 76 protected mode 21 80487 processor .487 directive 76 P487 directive 76 .8086 directive 76 8086 processor .8086 directive 76 P8086 directive 76 PUSHing constants 149 segments and 81 8087 coprocessor 15,79 .8087 directive 79 Borland C++ and 214 P8087 directive 79 .8087 directive 79 :: directive 114 < > (bracket) initializer 138 nested structures/ unions and 139,143 records and 140 < > (brackets) , literal string 166 macros and 159 =(equals) directive 38 =directive 15 =sign, argument lists and 122 ? keyword 101, 121 as initial value 137 ? symbol 133 @@ symbol 131 @32Bit symbol 85 @-sign 26 [] (square brackets) describing address contents 71 Ideal mode 31 MASMmode 31 line continuation character 165 {}(brace) initializer 137, 142 records and 140 A / a option 14, 22 address expressions See expressions address subtypes complex 61 setting 67 address subtypes of symbols distance parameter and 61 addresses, calculating 70 ALIAS 187 alias values 37 ALIGN directive 99,112 ALPHA directive 14, 23 .ALPHA directive 93 ancestor virtual methods 53 ARG directive 120, 121 Borland C++ and 212 arguments BYTE 121 names (scope of) 122 substitution (defined) 162 arithmetic operators 66,72 .ASM files 1, 12 assembling multiple passes 117 number of passes 18 ASSUME directive 91 at-sign 26 attribute values of segments 88 attributes B segment access 90 alignment 89 class 89 combination 88 size 90 values of segments 88 /b option 13 @B symbol 131 Backus-Naur form (BNF) grammar 62 %BIN directive 194,195 binary coded decimal (BCD) encoding DT directive and 136 bit-field records defining 96 bit shift operators 67 -block scoping of symbols, defined 130 books assembly language 9 Boolean algebra and operators 66 Index 281
  • 291. symbol expressions and 179 Borland contacting 4 Borland C++ ARG directive and 211 assembler modules in 15 case sensitivity 19, 206 code segment 200 data types 207 external symbols 208 floating-point emulation .15 linking to 221 LOCAL directive and 211 memory models 200 parameter passing 209 Pascal calling conventions 220 public functions ~d 205 register preservation 213 returning values 214 segment directives and 200 structures 214 BOUND instruction Ideal mode 32 buffers, size of 13 BYTE arguments 121 byte values 134 c Ic option 14,193 calculating addresses 70 CALL instruction See also CALL..METHOD 155 extended 155 CALL..METHOD instruction 50,51,54 near tables and 157 case sensitivity assembler routines and 19 Borland C++ 206 CATSTR directive 160 code-checking 21 .CODE directive 86 code generation, intelligent (directives for) 145 code segments 82 Borland C++ 200 @code symbol 87 CODESEG directive 86 @CodeSize symbol 85 : (colon) operator 70 : operator 113 CO:M:M directive 186 command files, indirect 26 command-line options 11 command-line syntax 12 help screen 16 comment character 35 CO:M:MENT directive 35 comments ; (semicolon) comment character 35 .. comment character 163 . comment character 35 COMMENT directive 35 end of line 35 including in macros 163 communal variables 186 MASM mode and 186 comparison operators 67 compatibility, MASM vs. Ideal mode 258 complementary jumps 146 complex types 103, 104 compressing data, record data types and 96 . . conditional blocks (termmating) See GOTO directive conditional directives assembly pass 182 defuUng blocks of code 175 expression 178 nesting 176 symbol-definition 179 text string 180 when to use 175 conditional list directives 191 %CONDS directive 192 CONST directive 86 .CONST directive 86 constants defined "57 in expressions 62 numeric 57 rotation counts and shift instructions 151 string 58 constructor and destructor procedures writing 126 coprocessor directives 79 @Cpu symbol 77 %CREF directive .193 .CREF directive 193 ' %CREFALL directive 193 %CREFREF directive 193 %CREFUREF directive 193 cross-reference generating 13 in listing files 14 symbol information 193 282 Tur boA sse mbIe r Use r' s Guide CS override 21 %CTLS directive 190 @curseg symbol 87 customer assistance 4 o Id option 15 data allocatillg 133 constants and 135 WORDS 133 defining 134 initialized (defined) 133 repeated blocks 133 storage in memory 134 uninitialized defined 133 specifying 133 .DATA directive 86 .DATA? directive 86 @data symbol 87 data types Borland C++ 207 creating named 142 creating record 140 declaring record 97 enumerated 95 creating instances of 141 initializing instances of 141 multiline syntax 96 pseudo ops and 96 objects and 45 record, multiline syntax for 97 table 102 multiline syntax 104 with virtual methods 143 DATASEG directive 86 @DataSize symbol 85 ??date symbol 38 DB directive 134 DD directive 134, 136 debugging information 26 %DEPTH directive 195 derived objects 47 DF directive 134 directives conditional 175 assembly pass 182 symbol-definiti0!l 179 conditional expressIOn 178 coprocessor 79 displaying assembly messages 40 error-generation. 177
  • 292. using symbol expressions 179 include files 37 module names 40 processor 76 program termination 40 startup 17 symbols 17 DISPLAY directive 40 distance parameter complex subtypes and 61 DLL defined 231 OOS programs 229 COM blueprint 230 EXE blueprint 229 DOSSEG directive 93 :: directive 114 doubleword values 134 DP directive 134 DQ directive 134, 136 DT directive 134, 136 dummy arguments defined 162 in macros 165 local 163 recognizing 162 types of 166 DUP keyword 133 DW directive 133,134 E / e option 15, 79 ELSEIF directive 178 ELSEIFB directive 181 ELSEIFDEF directive 179 ELSEIFDIF directive 181 ELSEIFDIFI directive 181 ELSEIFE directive 178 ELSEIFIDN directive 181 ELSEIFIDNI directive 181 ELSEIFNB directive 181 ELSEIFNDEF directive 179 ELSEIFxxx directives 177 EMUL directive 15, 79 encoded real numbers 136 END directive 40 ENDM keyword 165 ENDS directive 90,99,100 ENTER instruction 147 ENTERD instruction 147 ENTERW instruction 147 ENUM directive 95 enumerated data types creating instances of 141 defined 95 initializiilg instances of 141 multiline syntax for 96 pseudo ops and 96 environment variables, MASM mode 259 epilog code defined 118 how it works 118 languages and 118 NOLANGUAGE procedures and 119 register preservation and 123 specifying default style 84 EQU directive 37, 38, 159 Ideal vs. MASM mode 30 equal (=) directive 15 equate substitutions 37 ERR directive 178 .ERR directive 178 .ERR1 directive 182 .ERR2 directive 182 .ERRB directive 180 .ERRDEF directive 179 .ERRDIFI directive 180 .ERRE directive 179 .ERRIDN directive 180 .ERRIDNI directive 180 ERRrF directive 178 ERRIF1 directive 182 ERRIF2 directive 182 ERRIFB directive 180 ERRIFDEF directive 179 ERRIFDIF directive 180 ERRIFDIFI directive 180 ERRIFE directive 178 ERRIFIDN directive 180 ERRIFIDNI directive 180 ERRIFNB directive 180 ERRIFNDEF directive 179 .ERRNB directive 180 .ERRNDEF directive 180 .ERRNZ directive 178 error-generation directives 177 error messages 261-280 fatal 262 reporting 42 source file line display· 25 warning 262 ERRxxx directives 177 EVEN directive 112 EVENDATAdirective 112 .EXE files 1 .EXIT directive 87 EXITCODE directive 87, 230 EXITM directive 164 expressions 16-bit vs. 32-bit 72 BNF grammar and 62 byte values 72 constants in 62 contents of 61 determining characteristics 70 evaluation character 167 Ideal mode 31 obtaining type of 68 precision of 62 register names and 62 segment overrides of 69 setting address subtypes 67 structure names in 102 symbols in 62 syntax of 235 text macro names and 64 whyto use 57 extended CALL instruction See CALL..METHOD instruction extern "C" 200 EXTRN directive 185 Borland C++ and 208 F @F symbol 131 far data initialized 82 uninitialized 82 far pointer values 135 FAR procedures 116 far returns, instructions for 148 FARDATA directive 86 .FARDATA directive 86 ' @fardata symbol 87 @fardata? symbol 87 .FARDATA? directive 86 fast immediate multiply instruction See FASTIMUL instruction FASTIMUL instruction 154 fatal error messages 262 field value manipulation instructions 152 file names 38 object-oriented programming format 56 ??filename symbol 38 @FileName symbol 38 files .ASM 12 assembly 38 indirect 26 flag instructions, smart 152 Index 283
  • 293. FLDENV instruction 155 FLIPFLAG instruction 152 floating-point emulation 15 Ideal vs. MASM mode 259 instructions 2 , floating-point instructions See coprocessor emulation directives floating-point numbers 136 FRSTOR instruction 155 FSAVE instruction 155 FSTENV instruction 155 G GETFIELD instruction 153 GLOBAL directive 185 in .ASO files 56 objects and 47 global symbols, include files and 185 GOTO directive 164 GROUP directive 91 Ideal vs. MASM mode 33 groups H assigning segments to 91 segment registers and 91 segments in Ideal mode 33 Ihoption 16 hardware and software requirements 2 HELLO.ASM 8 help displaying screen 16 online 7 HIGH operator 72 Ii option 16 i486 processor protected mode 21 IDEAL directive 30 Idealmode 1 BOUND instruction 32 expressions 31 features 30 include files 37 operands 31 operators 32 predefined symbols 37 segment groups 33 speed 30 why to use 29, 30 IF directive 176, 178 IFI directive 176, 182 IF2 directive 176, 182 IFB directive 176, 181 IFDEF directive 176, 179 IFDIF directive 176, 181 IFDIFI directive 176, 181 !FE directive 178 IFIDN directive 176, 181 IFIDNI directive 176, 181 IFNB directive 169, 176, 181 IFNDEF directive 176, 179 IFxxx directives 175· immediate macro directive (%) 171 implied addition 72 %INCL directive 191 INCLUDE directive 16, 37 include files Ideal mode 37 setting path 16 INCLUDELIB directive 187 indirect command files 26 information technical support 4 inheritance defined 47 example of 53 objects and 106 previous object definitions 143 structure definitions and 101 initialization code 87 installation instructions 5 instances creating object 55 creating structure or union 137 creating table 141 initializing instances 138 initializing table 142 initializing union or structure 137 named-type, creating 142 ofobjects 143 of records 140 virtual method table 53, ~43 virtual method table (VMT) 49 INSTR directive 161 intelligent code generation directives for 145 @Interface symbol MODEL directive and 85 IRET instruction 284 Turbo Assembler User's Guide expanded 148 I lRETW instruction 148 IRP directive 170 IRPC directive 170 J Ij option 17 jEMUL option 15 }MP instruction 155 }MP..METHOD instruction 54, 158 jumps complementary 146 conditional 146 JUMPS directive 146 K keyword precedence 241 Ideal mode 241 MASM mode 242 keywords 19 list of available 242 Ikh option 17 l II option 14,1020 Ila option 18, 119 language modifiers and 120 LABEL directive 99,113 labels defining 113 external 205 local in MASM 131 .LALL directive 192 language modifiers WINDOWS procedures and 120 languages . MODEL and 118 modifiers and Windows procedures 119 overriding default for procedures 118 preserving registers and 123 procedures and arguments 120 setting in CALL . statement 222 LARGE operator 72, 154 instructions it affects 155 LARGESTACK directive 94 LEAVE instruction 147 LEAVED instruction 147 LEAVEWmstruction 147
  • 294. length of symbols 19 LENGTH unary operator 64 LFCOND directive 25 .LFCOND directive 192 LGDT instruction 155 LIDT instruction 155 line continuation character ( ) 165 line number information 25 linker Borland C++ 209,221 PharLap 90 segment ordering and 92 %LINUM directive 195 %LIST directive 190 .LIST directive 190 listing files 12 IX command-line option and 192 conditional listing directives 191 cross-reference information 14 cross-reference table and 189 directives for 190 false conditionals in 25 format of 189 format parameters 194 generating 17 high-level code in 18 including files in 191 including multiline macros 172 macro expansions in 192 symbol table and 191 symbol table in 193 symbol tables suppressing 20 why to use 189 literal string brackets 166 LOCAL directive 120, 121 Borland C++ and 211 in macros 163 local labels inMASM 131 LOCALS directive 125,130 location counter creating address expressions 70 defined 109 directives for 110 location counter symbol 109 LOOP instruction 147 loop instructions for 80386 processor 147 LOOPD instruction 147 LOOPDE instruction 147 LOOPDNE instruction 147 LOOPDNZ instruction 147 LOOPDZ instruction 147 LOOPE instruction 147 LOOPNE instruction 147 LOOPWE instruction 147 LOOPWNE instruction 147 LOOPWNZ instruction 147 LOOPWZ instruction 147 LOOPZ instruction 147 LOWoperator 72 .LST files 12 M 1m option 18,117,258 macros & character in 162 bodyof 162 controlling expansion 164 defining new text 160 defining substring 160 deleting multiline 168 dummy arguments within 165 expansions in listing files 192 including comments in 163 invoking arguments with special.characters 167 invoking general multiline 166 length of text 161 manipulating string 160 multiline 161 defining general 165 multiline expansions inlisting file 172 names in expressions 64 nested and recursive 168 redefining general multiline 168 repeating 169, 170 . returning positions of strings 161 string repeat 170 terminating assembly of 164 terminating body of 165 text defined 159 examples of manipulation 161 how to define 159 why to use 159 %MACS directive 192 MASK unary operator 65 MASKFLAG instruction 152 MASM compatibility 258 environment variables 259 expressions 31 ' floating-point format 259 NOSMART directive 258 predefined symbols 37 Quirks mode 258 segment groups 33 two-pass asssembly 258 MASM directive 30 member functions 218 memory models available segments 82 Borland C++ 200 FAR code pointers 85 modifiers of 83 NEAR code pointers 85 segment attributes for 227 specifying values 84 standard 84 messages reporting error 42 suppressing 23 warning 41 METHOD keyword 46, 48 method procedures creating 125 defined 48 example of 49 structure of 55 methods calling ancestor virtual 54 calling static 50 calling virtual 51, 52 defined 44 static versus virtual advantages of 49 tables and 103 virtual 157 IML command-line switch 59 Iml option 18,37, 184 MODEL directive 82, 84 language modifiers and 119 .MODEL directive 82 @Model symbol 84 models, determining procedure distance 116 modifiers,language 119 modular programming, module names 40 modules, defined 183 @Mptr member 143 lMU command-line switch 59 Imuoption 19 MULTERRS directive 42 multiline definition syntax 36 multiline macros 161 defining general 165 Index 285
  • 295. deleting general 168 including in listing file 172 invoking general 166 redefining general 168 multiline syntax enumerated data types and 96 record data types and 97 table data type definitions . and 104 multiple assembly passes 18, 117 IMV command-line switch 59 Imv# option 19 IMX command-line switch 59 Imx option 19,184 N Inoption 20 NAME directive 40 name-mangling 198 named structures, including 101 naming conventions of symbols 183 NEAR procedures 116 near returns, instructions for 148 near tables, objects and 51 NEARSTACK modifier 83 nested procedures 124 %NEWPAGE directive 194 %NOCONDS directive 192 %NOCREF directive 193 %NOCTLS directive 190 NOEMUL directive IS, 79 %NOINCL directive 191 NOJUMPS directive 146 NOLANGUAGE interfacing convention 156 NOLANGUAGE procedures prolog and epilog code and 119 %NOLIST directive 190 NOLOCALS directive 130 %NOMACS directive 192 NOMULTERRS directive 42 NOPs, avoiding generation of 146 NOSMART directive 145 MASM compatibility 258 %NOSYMS directive 191 NOTHING keyword 91 %NOTRUNC directive 195 NOWARN directive 41 NUL device 13 null string, length of 161 numbers encoded real 136 floating-point 136 numeric constants 57 numeric coprocessor 15 o 10 option 20 .OBJ files 1 ·suppressing 22 object files debugging information in .26 line number information in 25 module name 40 segment ordering 14, 22 object methods calling 157 tail recursion for 158 object modules, defined 8 @Object symbol 107 object-oriented programming advantages of using 43, 44 defined 43 filename format 56 table data types and 103 objects creating instances of 55,143 data types and 45 declaring 45, 47 defined 44 defining symbols 107 derived 47, 48 differences between structures and 143 GLOBAL directive and 47 how to define 106 initializing instance's VMT pointer 158 linked list example 45 method procedures and 106, 125 near tables and 51 structures and 106 TLINK compatable without overlay code 21 virtual method table instances 143 what they consist of 105 OFFSEToperator 32, 69 MASM vs. Ideal mode 33 offsets, getting segments and 69 loi option 21 online help 7 lop option 21,90 operands, Ideal mode 31 operators 286 Turbo Assembler User's Guide bit shift 67 Boolean algebra and 66 comments 36 comparison 67 general arithmetic 66 Ideal vs. MASM mode 32 ORG directive 110 los option 21 . OS12 programs flat model format and 233 %OUT directive 40 overlay code generating 20 IBM linker 21 PharLap linker 21 p Ip option 21 P186 directive 76 P287 directive 79 P386 directive 76 P386N directive 76 P386P directive 76 P387 directive 79 P486 directive 76 P486N directive 76 P487 directive 76 P8086 directive 76 P8087 directive 79 PAGE directive 194 %PAGESIZE directive 194 parameter passing Borland C++ 209 %PCNT directive 195 . (period) charactor MASM vs. Ideal mode 236 . (period) operator 71 period, Ideal mode structures 32 Phar Lap linker 90 plus sign 12 pointers virtual method table 50, 51, 52,53 POP instruction 155 multiple 148 pointers and 148 POPA instruction expanded 149 POPAW instruction 149 POPFW instruction 149 %POPLCTL directive 196 POPSTAIE instruction 149 precedence keyword 241 Ideal mode 241
  • 296. MASM mode 242 PROC directive 115 PROC keyword, Ideal mode 31 PROCDESC directive 126, 156 procedure prototypes 126 procedure types, defining 123 procedures calling and having RETURNS 156 calling with arguments 156 declaring 115 defining types 105 determining distance of 117 FAR 116 interfacing conventions of 155 languages for arguments and 120 MODEL and 118 overriding default 118 method 55 creating 125 models and distance of 116 NEAR 116 nesting and scope rules 124 NOLANGUAGE 119 prototypi.p.g 156 publishing prototypes 185 specifying languages for 118 stack frames and 121,147, 155 writing constructor and destructor 126 processor directives 76 processor type, determining 77 PROCTYPE directive 105,123 program termination, END directive and 40 prolog code defined 118 langUages and 118 NOLANGUAGE procedures and 119 register preservation and 123 specifying default style 84 what it does 118 protected mode 21 segment registers and 81 prototypes procedure 126 procedure types and 127 publishing procedure 185 prototyping procedures 156 PUBLIC directive 184 public functions, Borland C++ and 205 PUBLICDLL directive 184 PURGE directive 168 PUSH instruction 155 multiple 148 pointers and 148 PUSHA instruction expanded 149 PUSHAW instruction 149 PUSHF instruction ' expanded 149 PUSHFW instruction 149 PUSHing constants 149 %PUSHLCTL directive 196 PUSHSTATE instruction 149 Q Iq option 22 quadword values 135 question mark symbols using 38 QUIRKS directive 258 R Ir option 15,22 .RADIX directive 58 RADIX directive 58,136 radixes available 57 changing default 58 characters determining 58 default 136 real mode, segment registers and 81 record data types multiline syntax for 97 RECORD directive 97 records < > and 140 {} and 140 . creating instances of 140 defining 140 initializing instances 140 retrieving data from 153 setting values in 152 reference books 9 registers names of and expressions 62 preserving 123 preserving (Borland C++) 213 segment 81 registration (product) byphone 4 REPT directive 169 RET instruction, NEAR or FAR and 147 RETCODE instruction 148 RETF instruction 148 RETN instruction 148 return instructions 147 RETURNS directive 156 s I s option 14, 22 .sALL directive 192 scope of symbols defined 129 scope rules for nested procedures 124 SEG operator 69 SEGCS instruction 151 SEGDS instruction 151 SEGES instruction 151 SEGFS instruction 151 SEGGS instruction 151 SEGMENT directive 88 SEGMENT keyword, Ideal mode 31 segments 8086 processor and 81 assigning to groups 91 attributes access 90 alignment 89 class 89 combination 88 size 90 Borland C++ and 200 closing 90, 99 code 82 default attributes 227 directives (Borland C++ and) 200 forced overrides 151 generic'88- ' getting offsets and 69 groups Ideal mode and 30, 33 MASMmode 33 groups and 82 how the stack is treated 81 memory models and 82 opening 88 ordering 14, 92 alphabetic 93 changing 92 DOS 93 sequential 93 overrides of expressions/69 registers 81 Index 287
  • 297. registers and 91 sequential order 14, 22 simplified directives 86 size 78 symbols and 87 writing to uninitialized 89 SEGSS instruction 151 semicolon 12 within macros 36 SEQ directive 14 .sEQ directive 93 SETFIELD instruction 152 SETFLAG instruction 152 SFCOND directive 25 .SFCONDS directive 192 SGDT instruction 155 shift instructions, rotation counts ' and 151 SHL operator 67 SHR operator 67 SIDT instruction 155 simplified segment directives See also individual listings symbols and 87 size of instructions, controlling 154 SIZE unary operator 65 SIZESTR directive' 161 SMALL operator 72, 154 instructions it affects 155 SMALLSTACK directive 94 SMART directive 145 MASM compatibility 258 smart flag instructions, why they're useful 151 software and hardware requirements 2 source files include files 16 symbols 15 square brackets Ideal mode 31 MASMmode 31 stack changing size of 93 MODEL directive and 93 segments and 81 STACK directive 86 .sTACK directive 86 stack frame defined 155 specifying arguments 120 @stack symbol 87 .' .sTARTUP directive 87 @Startup symbol 87 STARTUPCODE directive 87 229 ' static methods calling 50 versus virtual (advantages of) 49 statistics, displaying 13 string constants 58 strings, quoted 136 STRUC directive 45,98,100,106 1~ , structures aligning members 99 Borland C++ 214 bracketinitializerand nested 139 closing 99 creating instances of 137 creating members 99 defined 98 differences between objects and 143 including named 101 initializing instances 137 membernames and 99, 101 members and 98 names in expressions 102 ,nested 102 ' nesting 100 objects and 106 opening a definition 98 SUBSTR directive 160 %SUBTTL directive 196 SUBTTL directive 195 support, technical 4 symbol tables listing files and cross- referencing 14 suppressing 20 symbols address subtypes complex 61 simple 60 aliases 37 block-scoped 130 block-scoped (disabling) 130 case sensitivity of 18, 19,59 @Cpu 77 ??date ,38 defined 59 defining 15 dynamic link entry points 184 enabling locally scoped 125 external 19,185 Borland C++ and 208 ??filename 38 288 Turbo Assembler User's Guide @FileName 38 global 185 in expressions 62 Jengthof 19 location counter 109 MASM block scoping 131 names of 59 naming conventions for . languages 183 overriding language setting 184 public 19, 184 publishing external 184 redefinable 129,130 restrictions 15 scope of (defined) 129 standard values 63 ??time 38 types of 59 uppercase 19 values used by themselves 63 why to use 57 @WordSize 78 %SYMS directive 191 SYMTYPE operator 70 T It option 23 TABLE directive 45 @Table symbol 107 @TableAddr member 143 @Tableaddr symbol 107 tables creating instances of 141 data types 102 initializing instances of 142 overriding members 104 static members 102 virtual members 102 %TABSIZE directive 196 tags, macro 164 tail recursion code, instruction for 158 TASM.CFG 27 TBLINIT directive 50 in .ASM files 56 TBLINIT instruction 158 TBLINST directive 49 in .ASM files 56 TBLINST pseudo-op 143 TBLPTR directive 106 TCREF utility 13 Technical Support contacting 4 termination code 87
  • 298. termination, END directive and 40 TESTFLAG instruction 152 %TEXT directive 196 text macro names in expressions 64 TFCOND directive 25 .TFCOND directive 192 THIS operator 70 time 38 ??time symbol 38 %TITLE directive 196 TfTL,E directive 195 TLINK utility 209,221 example of 9 %TRUNC directive 195 two-pass assembly MASM compatibility 258 type checking, Ideal mode 29 .TYPE operator 70 TYPE operator 68 type override operators 67 type-safe linkage 198 TYPEDEF directive 104 . typefaces in this manual 3 types u complex 103,104 defining named 104 defining procedure 105 of expressions 68 procedure 123 symbol 59 I u command-line switch 39 luoption 23 UDATASEG directive 86 UFARDATA directive 86 underscore, and the C language 205 UNINIT 89 UNION directive 98,100 unions bracketinitializerand nested 139 closing 99 creating instances of 137 defined 98 initialized data 101 initializing instances 137, 138 members and 98 multiple initialized members 138 nested 102 nesting 100 opening a definition 98 uppercase, converting symbols to 19 USE32 modifier 83 USES directive 123 v Iv option 13, 23 variables, communal 186 VERSION directive 39 line continuation and 36 MASM compatability and 40 VIRTUAL keyword 47, 103 virtual method table initializing 50 initializing pointer to 158 instances of 49, 53, 143 . modifiers and. 106 objects and 106 pointers 52 pointers to 50, 51, 53, 106 virtual methods ancestor 53 calling·51,52 object data types and 143 versus static (advantages of) 49 virtual table pointers determining size of 107 modifiers and 106 w Iwoption 24 WARN directive 41 warning messages 41, 262 "mild" 24 generating 24 WHILE directive 170 WIDTH unary operator 65 Windows programs 231 16-bit blueprint 232 32-bit blueprint 232 DLL blueprint 231 word values 134 @WordSize symbol 78 x /xoption 25 .xALL directive 192 .xCREF directive 193 .xLIST directive 190 .xRF files 13· z Iz option 25 / zd option 25 I zi option 26 I zn option 26 Index 289
  • 299. 290 Turb0, Ass embIer Use r's Guide
  • 301. Borland Copyright © 1996 Borland International, Inc. All rights reserved. All Borland product names are trademarks of Borland International, Inc. Corporate Headquarters: 100 Borland Way, Scotts Valley, CA 95066-3249, (408) 431-1000. Internet: http://www.borland.com CompuServe: GO BORLAND. Offices in: Australia, Canada, France, Germany, Hong Kong, Japan, Latin America, Mexico, The Netherlands, Taiwan, and United Kingdom • Part # LSM1350WW21774 • BOR 8907