ALP Programming Guide and Reference/Assembler Directives: Difference between revisions
mNo edit summary |
|||
Line 374: | Line 374: | ||
|DQ or QWORD | |DQ or QWORD | ||
|Allocates 64-bit (quad-word) values. | |Allocates 64-bit (quad-word) values. | ||
|Both DQ and QWORD allow an integer Initializer with 64-bit (8-byte) precision. | |Both DQ and QWORD allow an integer Initializer with 64-bit (8-byte) precision. If the DQ directive is being used, the Initializer field may resolve to a 64-bit Floating-Point-ExpressionType. | ||
|- | |- | ||
|DT or TBYTE | |DT or TBYTE | ||
Line 488: | Line 488: | ||
ret | ret | ||
CODE ends | CODE ends | ||
end</pre> | end | ||
</pre> | |||
==== Initialization of Vector Types ==== | ==== Initialization of Vector Types ==== | ||
Line 963: | Line 964: | ||
'''Remarks''' | '''Remarks''' | ||
If names are specified, only the given names are suppressed. Same as '''.XCREF'''. | If names are specified, only the given names are suppressed. Same as '''.XCREF'''. | ||
==== .NOLIST (Suppress List Output) ==== | ==== .NOLIST (Suppress List Output) ==== | ||
Line 1,169: | Line 1,170: | ||
; Call operating system to read input into a buffer | ; Call operating system to read input into a buffer | ||
;------------------------------------------------------------------------------ | ;------------------------------------------------------------------------------ | ||
ReadBuffer PROC, ; need comma to continue the PROC statement | ReadBuffer PROC, ; need comma to continue the PROC statement | ||
hFile:dword, ; parm 1: Read handle | hFile:dword, ; parm 1: Read handle | ||
pBuffer: ptr byte, ; parm 2: Address of input buffer | pBuffer: ptr byte, ; parm 2: Address of input buffer | ||
Line 1,309: | Line 1,310: | ||
: .286P | : .286P | ||
: .287 | : .287 | ||
: .386 | : .386 | ||
: .386P | : .386P | ||
: .387 | : .387 | ||
: .486 | : .486 | ||
: .486P | : .486P | ||
Line 1,318: | Line 1,319: | ||
: .686 | : .686 | ||
: .686P | : .686P | ||
: .MMX | : .MMX | ||
: .NOMMX | : .NOMMX | ||
Line 1,485: | Line 1,486: | ||
'''Example''' | '''Example''' | ||
; Top of file - no processor currently selected | ; Top of file - no processor currently selected | ||
.MMX ; enables both MMX and 586 mnemonics | .MMX ; enables both MMX and 586 mnemonics | ||
.NOMMX ; 586 mnemonics still enabled | .NOMMX ; 586 mnemonics still enabled | ||
.686 ; 686 mnemonics now being recognized | .686 ; 686 mnemonics now being recognized | ||
.MMX ; 686 and MMX mnemonics now being recognized | .MMX ; 686 and MMX mnemonics now being recognized | ||
.NOMMX ; 686 mnemonics still enabled | .NOMMX ; 686 mnemonics still enabled | ||
Line 1,594: | Line 1,595: | ||
|FLAT||CODE32 | |FLAT||CODE32 | ||
|} | |} | ||
The ''module'' entry is replaced with base file name of the top-level module being assembled. | The ''module'' entry is replaced with base file name of the top-level module being assembled. | ||
Line 1,778: | Line 1,778: | ||
;; Note use of group name | ;; Note use of group name | ||
;; in producing offset | ;; in producing offset | ||
INT 21H | INT 21H | ||
ENDM | ENDM | ||
DSEG SEGMENT BYTE PUBLIC 'DATA' | DSEG SEGMENT BYTE PUBLIC 'DATA' | ||
Line 1,835: | Line 1,835: | ||
*[[#.FARDATA?]] | *[[#.FARDATA?]] | ||
*[[#.STACK]] | *[[#.STACK]] | ||
Each of these directives close any segment that is currently opened, then open a different segment whose name and attributes are determined by the ''Memory-Model'' argument. | Each of these directives close any segment that is currently opened, then open a different segment whose name and attributes are determined by the ''Memory-Model'' argument. | ||
Line 1,889: | Line 1,888: | ||
<pre> | <pre> | ||
ORG 120H | ORG 120H | ||
ORG $+2 ; SKIP NEXT 2 BYTES</pre> | ORG $+2 ; SKIP NEXT 2 BYTES | ||
</pre> | |||
To conditionally skip to the next 256-byte boundary: | To conditionally skip to the next 256-byte boundary: | ||
<pre> | <pre> | ||
CSEG SEGMENT PAGE | CSEG SEGMENT PAGE | ||
BEGIN = $ | BEGIN = $ | ||
. | . | ||
. | . | ||
. | . | ||
IF ($-BEGIN) MOD 256 | IF ($-BEGIN) MOD 256 | ||
; IF NOT ALREADY ON 256 BYTE BOUNDARY | ; IF NOT ALREADY ON 256 BYTE BOUNDARY | ||
Line 2,017: | Line 2,016: | ||
; Move data from record to register | ; Move data from record to register | ||
; other than segment register | ; other than segment register | ||
AND DX, MASK Y | AND DX, MASK Y | ||
; Mask out fields X and Y | ; Mask out fields X and Y | ||
; to remove unwanted fields | ; to remove unwanted fields | ||
Line 2,037: | Line 2,036: | ||
'''Syntax''' | '''Syntax''' | ||
<pre>Structure-Name STRUCT | <pre>Structure-Name STRUCT | ||
FieldDeclaration | FieldDeclaration | ||
. | . | ||
. | . | ||
. | . | ||
Structure-Name ENDS</pre> | Structure-Name ENDS | ||
</pre> | |||
Where ''FieldDeclaration'' has the following form: | Where ''FieldDeclaration'' has the following form: | ||
[FieldName] Allocation-TypeName InitialValue[,InitialValue...] | [FieldName] Allocation-TypeName InitialValue[,InitialValue...] | ||
Line 2,065: | Line 2,065: | ||
One DB 0 | One DB 0 | ||
Two WORD 0 | Two WORD 0 | ||
BYTE 3 | BYTE 3 | ||
Four DWORD ? | Four DWORD ? | ||
Numbers ENDS </pre> | Numbers ENDS | ||
</pre> | |||
Allocate a structure variable called '''Values''' using the '''Numbers''' ''Structure-TypeName'', overriding the '''One, Two,''' and '''Four''' ''Structure-FieldName'' entries with explicit values, and the third (unnamed) entry is initialized with the default ''InitialValue''inherited from the ''FieldDeclaration'': | Allocate a structure variable called '''Values''' using the '''Numbers''' ''Structure-TypeName'', overriding the '''One, Two,''' and '''Four''' ''Structure-FieldName'' entries with explicit values, and the third (unnamed) entry is initialized with the default ''InitialValue''inherited from the ''FieldDeclaration'': | ||
Values Numbers <1, 2, , 4> | Values Numbers <1, 2, , 4> | ||
Line 2,297: | Line 2,298: | ||
Data SEGMENT | Data SEGMENT | ||
Stuff WORD 0 | Stuff WORD 0 | ||
Data ENDS | Data ENDS | ||
Code SEGMENT | Code SEGMENT | ||
Line 2,410: | Line 2,411: | ||
or | or | ||
Name: | Name: | ||
or | or | ||
Name:: | Name:: | ||
'''Remarks''' | '''Remarks''' | ||
Line 2,442: | Line 2,443: | ||
. | . | ||
. | . | ||
RET | RET | ||
SUBRT ENDP | SUBRT ENDP | ||
</pre> | </pre> | ||
Line 2,469: | Line 2,470: | ||
'''SCOPED'''| '''NOSCOPED'''The '''NOSCOPED''' keyword forces all code label names defined within procedures to be visible to the entire module and not just from within the defining procedure. | '''SCOPED'''| '''NOSCOPED'''The '''NOSCOPED''' keyword forces all code label names defined within procedures to be visible to the entire module and not just from within the defining procedure. | ||
'''SEGMENT''':''Address-Size'' Explicitly sets the default address size value. This is used to control the address size of segments that are opened without | '''SEGMENT''':''Address-Size'' Explicitly sets the default address size value. This is used to control the address size of segments that are opened without explicit '''USE16''' or '''USE32''' keywords, and of global identifiers that are declared outside of segment boundaries. The possible values for ''Address-Size'' are '''USE16''', '''USE32''', and '''FLAT'''. | ||
==== .RADIX (Set the Default Base for Numeric Literals) ==== | ==== .RADIX (Set the Default Base for Numeric Literals) ==== | ||
Line 2,476: | Line 2,477: | ||
'''Syntax''' | '''Syntax''' | ||
.RADIX Expression | .RADIX Expression | ||
'''Remarks''' | '''Remarks''' | ||
Line 2,487: | Line 2,489: | ||
The statement: | The statement: | ||
.RADIX 16 | .RADIX 16 | ||
DW 120B | DW 120B | ||
produces an error because 2 is not a valid binary number. The correct specification is: | produces an error because 2 is not a valid binary number. The correct specification is: | ||
DW 120BH | DW 120BH | ||
The following example: | The following example: | ||
.RADIX 16 | .RADIX 16 | ||
DW 89CD | DW 89CD | ||
also produces an error because C is not a valid decimal number. The correct specification is: | also produces an error because C is not a valid decimal number. The correct specification is: | ||
Line 2,502: | Line 2,504: | ||
The following two move instructions are the same: | The following two move instructions are the same: | ||
MOV BX, OFFH | |||
MOV BX, OFFH | .RADIX 16 | ||
.RADIX 16 | MOV BX , OFF | ||
MOV BX , OFF | |||
The following example: | The following example: | ||
.RADIX 8 | |||
DQ 19.0 ; Treated as decimal | |||
DQ 19.0 ; Treated as decimal | |||
produces a constant whose value is 19 decimal because 19.0 is a real number. However, if you leave off the decimal point, the following: | produces a constant whose value is 19 decimal because 19.0 is a real number. However, if you leave off the decimal point, the following: | ||
.RADIX 8 | |||
DQ 19 ; uses current radix | |||
.RADIX 8 | |||
DQ 19 ; uses current radix | |||
produces a syntax error because nine is not a valid number in .RADIX 8. | produces a syntax error because nine is not a valid number in .RADIX 8. | ||
[[Category:ALP Programming Guide and Reference]] | [[Category:ALP Programming Guide and Reference]] |
Revision as of 12:21, 18 June 2022
This section describes the various types of ALP directives:
Type | Function | Directives |
---|---|---|
Conditional error | Debugs programs and checks for assembly-time errors. | .ERR
.ERR1 .ERR2 .ERRDEF .ERRNDEF .ERRE .ERRNZ .ERRB .ERRDIF .ERRDIFI .ERRIDN .ERRIDNI .ERRNB |
Data allocation | Allows you to create and initialize variables for use within your program. | BYTE
DB DD DF DQ DT DW DWORD FWORD QWORD REAL4 REAL8 REAL10 SBYTE SDWORD SWORD TBYTE WORD |
Intermodule linkage | Simplifies data sharing and a provides a high-level interface to multiple-module programming. | COMM
END EXTERN/EXTRN EXTERNDEF INCLUDELIB NAME PUBLIC |
Listing control | Controls the assembler listing of your source file. | %BIN
.CREF .LALL .LIST .LISTALL .LISTIF .LISTMACRO .LISTMACROALL .NOCREF .NOLIST .NOLISTIF .NOLISTMACRO PAGE .SALL .SFCOND SUBTITLE SUBTTL .TFCOND TITLE .XALL .XCREF .XLIST |
Procedure control | Allows you to organize your code into procedures. | PROC
LOCAL ENDP |
Processor control | Selects processors and coprocessors. | .186
.286 .286P .287 .386 .386P .387 .486 .486P .586 .586P .686 .686P .8086 .8087 .MMX .NOMMX |
Segments | Creates and manages segments. | ALIGN
.ALPHA .CODE .CONST .DATA .DATA? DOSSEG .DOSSEG ENDS EVEN .FARDATA .FARDATA? GROUP .MODEL ORG SEGMENT .SEQ .STACK |
Type definition | Allows the creation of complex user-defined data types. | RECORD
STRUC STRUCT TYPEDEF UNION |
Miscellaneous | Provides miscellaneous functions. | =
.ABORT ASSUME EQU LABEL OPTION .RADIX |
Conditional Error Control
Use conditional error control directives to debug programs and check for assembly-time errors. If you insert a conditional assembly directive in your code, you can test assembly-time conditions at that point. You can also test for boundary conditions in macros by using conditional error control directives.
Errors generated by conditional error control directives cause ALP to return a nonzero return code. If a severe error is detected during assembly, ALP does not generate the object module.
This section describes the following conditional error control directives:
- .ERR
- .ERR1
- .ERR2
- .ERRB
- .ERRDEF
- .ERRDIF
- .ERRDIFI
- .ERRE
- .ERRIDN
- .ERRIDNI
- .ERRNB
- .ERRNDEF
- .ERRNZ
.ERR/.ERR1/.ERR2 (Force Assembly Error Condition)
The .ERR, .ERR1, and .ERR2 directives cause errors at the points at which they occur in the source file.
Syntax
.ERR or .ERR1 or .ERR2
Remarks
The .ERR directive causes an error regardless of the pass. .ERR1 causes an error on the first pass only. .ERR2 causes an error on the second pass only. If you use the -Lp:1option to request a first pass listing, the . ERR1 error message appears on the screen and in the listing file. Like other error conditions occurring during pass one, the error generated by .ERR1 does not cause the assembly to fail.
Example
This example ensures that you define either the DOS or the OS2 symbol. If you define neither, the assembler assembles the nested ELSE condition and produces an error message. The .ERR directive causes an error on each pass.
IFDEF DOS . . . ELSE IFDEF OS2 . . . ELSE . ERR ENDIF ENDIF
.ERRB/.ERRNB (Error if Argument Blank/Non-Blank)
The .ERRB and .ERRNB directives test the given Text-Argument.
- Syntax
.ERRB Text-Argument
or
.ERRNB Text-Argument
- Remarks
If Text-Argument is blank, the .ERRB directive produces an error. If Text-Argument is not blank, .ERRNB produces an error.
You can test for the existence of parameters by using these directives within macros.
- Example
In this example, the directives ensure that only one argument is passed to the macro. If no argument is passed to the macro, the .ERRB directive produces an error. If more than one argument is passed, the .ERRNB directive produces an error.
WORK MACRO REALARG,TESTARG .ERRB <REALARG> ;; Error if no parameters .ERRNB <TESTARG> ;; Error if more than one parameter . . . ENDM
.ERRDEF/.ERRNDEF (Error if Symbol Defined/Not Defined)
The .ERRDEF and .ERRNDEF directives test whether a symbol has been defined.
- Syntax
.ERRDEF Identifier
or
.ERRNDEF Identifier
- Remarks
If Identifier is defined as a label, a variable, or a symbol, the .ERRDEF directive produces an error. If you have not defined Identifier, .ERRNDEF produces an error. When Identifier is a forward reference, the assembler considers it undefined on the first pass and defined on the second pass.
- Example
In this example, .ERRDEF ensures that SYMBOL is not defined before entering the blocks, and .ERRNDEF ensures that you defined SYMBOL somewhere within the blocks.
.ERRDEF SYMBOL IFDEF CONFIG1 . . SYMBOL EQU 0 . ENDIF IFDEF CONFIG2 . . SYMBOL EQU 1 . ENDIF .ERRNDEF SYMBOL
.ERRDIF/.ERRDIFI (Error if Arguments are Different)
The .ERRDIF and .ERRDIFI directives generate an assembler error if the two #Text-Arguments are different.
- Syntax
.ERRDIF Text-Argument-1, Text-Argument-2
or
.ERRDIFI Text-Argument-1, Text-Argument-2
- Remarks
The .ERRDIF directive performs a case-sensitive comparision, and the .ERRDIFI directive performs a case-insensitive comparision.
- Example
In this example, a check is made to verify that the currently opened segment is _TEXT. This helps to insure that the macro is used only from within the default near code segment, and not from a program with a memory model that uses far code pointers (MEDIUM, LARGE, or HUGE).
RETURN MACRO ;; Use the expansion operator (%) to resolve @CurSeg equate % .errdif <_TEXT>,<@CurSeg> ;; Must be in near .CODE segment RETN ;; Force a near return ENDM
.ERRE/.ERRNZ (Error if Expression False/True)
The .ERRE and .ERRNZ directives test the value of an Expression.
Syntax
.ERRE Expression or .ERRNZ Expression
Remarks
If the Expression evaluates to be false (zero), the .ERRE directive produces an error. If the Expression evaluates to be true (not zero), the .ERRNZdirective produces an error. The Expression must evaluate to an absolute value and cannot contain forward references.
Example
In this example, .ERRE checks the boundaries of a parameter that the program passes to the macro BUFFER. If count is less than or equal to 128, the expression that the directive tests is true (not zero) and the directive produces no error. If COUNT is greater than 128, the expression is false (zero) and the directive produces an error.
BUFFER MACRO COUNT,BNAME .ERRE COUNT LE 128 BNAME DB COUNT DUP (0) ;; Reserve memory, but no more than 128 bytes ENDM BUFFER 128,BUF1 ; Data reserved - no error BUFFER 129,BUF2 ; Error produced
.ERRIDN/.ERRIDNI (Error if Arguments are Identical)
The .ERRIDN and .ERRIDNI directives generate an assembly error if the two #Text-Arguments are identical.
Syntax
.ERRIDN Text-Argument-1, Text-Argument-2 or
.ERRIDNI Text-Argument-1, Text-Argument-2
Remarks
The .ERRIDN directive performs a case-sensitive comparision, and the .ERRIDNI directive performs a case-insensitive comparision.
Example
In this example, .ERRIDN protects against the passing the AX register as the second parameter, because the macro does not work if this happens. This example uses the .ERRIDNI directive since the macro needs to check for all possible spellings of the AX register.
ADDEM MACRO AD1 , AD2 , SUM .ERRIDNI <ax>,<AD2> ;; ERROR IF AD2 is ax MOV AX, AD1 ;; Would overwrite if AD2 were AX ADD AX, AD2 MOV SUM, AX ;; SUM must be register or memory ENDM
Data Allocation
Data allocation statements allow you to reserve storage for your program data. To initiate a data allocation statement, an Old-Style-Allocation-Directive may be used, but in modes other than #M510 it is preferable to use a Scalar-TypeName or UserDefined-TypeName, which the assembler treats as a pseudo-directive. To introduce consistency into the descriptions, all such variations will be referred to as the Allocation-TypeName.
The Allocation-TypeName that you select determines the data-type of the allocated storage. An optional symbolic name may be associated with the storage, and the storage may also be initialized with specific values if so desired.
Syntax
[Name] Allocation-TypeName Initializer [,Initializer ...]
Allocation-TypeName:
- Old-Style-Allocation-Directive
- Scalar-TypeName
- Record-TypeName
- Structure-TypeName
- Union-TypeName
- Typedef-TypeName
Old-Style-Allocation-Directive: one of
DB DW DD DF DQ DT
Remarks
The various fields of the data allocation statement are described as follows:
Name If the Nameentry is present, it must be specified as a valid Identifier unique to the scope in which it appears. If the allocation statement is assembled into an open segment, the assembler converts the identifier to a Data-LabelName to allow referencing the storage by a symbolic variable name. If the allocation statement is assembled into the body of a #STRUCT or #UNION type definition, then the assembler converts the identifier to a Structure-FieldName or Union-FieldName.
Allocation-TypeName If the Allocation-TypeName is specified as a Typedef- TypeName, the assembler resolves it to its underlying data type to determine what type of initialization is to be performed.
If the Allocation-TypeName entry resolves to a Scalar-TypeName or a pointer to some other type, then the Initializer field must be specified using an expression syntax that can be resolved to a Scalar-Initializer-ExpressionType. See the following section on #Initialization of Scalar Types for a full description of this topic.
If the Allocation-TypeName entry resolves to a Record-TypeName, Structure- TypeName, or Union-TypeName, then the Initializer field must be specified using the Compound-Initializer syntax. See the following section on #Initialization of Aggregate Types for a full description of this topic.
If the Allocation-TypeName entry resolves to an array of any other type, then the Initializer field must be specified using the Compound-Initializer syntax. See the following section on #Initialization of Vector Types for a full description of this topic.
Initializer Each Initializer entry is an Expression that must resolve to an Initializer-ExpressionType appropriate for the type of data described by the Allocation-TypeName field.
Each Initializer entry may also be duplicated by making it the operand of a Duplicative-Expression. When assembling in #ALP mode however, the DUP operator is considered obsolete and its use is discouraged. Instead, a Typedef-TypeName associated with the declaration of a true array should be used in the Allocation-TypeName field along with the appropriate compound initializer.
Initialization of Scalar Types
A scalar data item represents a numeric quantity that may be increased or decreased in magnitude as a single unit. Thus, an Initializer expression for a scalar data item must be coded such that it resolves to a single scalar value. See the section on Scalar-Initializer-ExpressionType for the syntax and semantics of such expressions.
The old-style allocation directives (DB, DW, DD, DF, DQ, and DT) are supported in all assembler emulation modes, but for modes other than #M510, the Scalar-TypeName keywords should be used instead.
When the Scalar-TypeName keywords are used instead of the old-style allocation directives, the assembler has full knowledge of the data types of the variables being created. This allows the assembler to make more intelligent code generation decisions, and it enables the assembler to correctly describe the variable in the symbolic debugging information that it generates for the source level debugger. #Scalar-TypeNames may only be used as allocation directives in the #ALP or #M600 modes.
To allocate an uninitialized scalar data item, use the Indeterminate-Value-Alias($) in the Initializer field.
Type Name | Data Type | Initializer Description |
---|---|---|
DB, BYTE, or SBYTE | Allocates 8-bit (byte) values. | Each Initializer must be in the range from 0 to 255 (unsigned) for a DB or BYTE directive, and from -128 to 127 (signed) for a SBYTE directive. |
DW, WORD, or SWORD | Allocates 16-bit (word) values. | Each Initializer must be in the range from 0 to 65535 (unsigned) for a DW or WORD directive, and from -32768 to 32767 (signed) for a SWORD directive. |
DD, DWORD, or SDWORD | Allocates 32-bit (double-word) values. | If the Initializer is an integer, each must be in the range from 0 to 4,294,967,295 (unsigned) for a DD or DWORD directive, and from -2,147,483,648 to 2,147,483,647 (signed) for a SDWORD directive. If the DD directive is being used, an Initializer may also resolve to a 32-bit Floating-Point-Expression Type. |
DF or FWORD | Allocates 48-bit (6-byte far-word) values. | Each Initializer typically specifies the full address of a 32-bit far code or data label, but normal 32-bit integer values may also be used. The processor does not support 48-bit integer operations, thus the assembler does support 48-bit integer precision when initializing such variables. These directives are typically only useful for defining pointer variables for use on 32-bit processors. |
DQ or QWORD | Allocates 64-bit (quad-word) values. | Both DQ and QWORD allow an integer Initializer with 64-bit (8-byte) precision. If the DQ directive is being used, the Initializer field may resolve to a 64-bit Floating-Point-ExpressionType. |
DT or TBYTE | Allocates 80-bit (10-byte) values | Both DT and TBYTE allow an integer Initializer with 80-bit (10-byte) precision. If the DT directive is being used, the Initializer field may resolve to a 80-bit Floating-Point-ExpressionType. |
REAL4, REAL8, or REAL10 | Allocates real (floating-point) values of a specific size (4 bytes, 8 bytes, or 10 bytes). | Each Initializer must resolve to a Floating-Point-ExpressionType. The assembler converts the floating-point literal to the IEEE format appropriate for the type of variable being allocated. |
Examples
Here are some examples of scalar initialization:
; Allocate some integer variables uint8 BYTE 0, 255 ; min, max values for unsigned byte sint8 SBYTE -128, 127 ; min, max values for signed byte USHORT_T TYPEDEF WORD ; Define a typedef alias for WORD ushort USHORT_T 0, 0FFFFh ; and use it as allocation type name ; Some things to know about string-literal initializers char BYTE "a" ; a single BYTE value (061h) is_int WORD "ab" ; a single WORD value (06162h) this_too DWORD "abcd" ; a single DWORD value (061626364h) too_long WORD "abcd" ; error, expression too big for a word string BYTE "string",0 ; but strings can allocate many bytes ; Integers, pointers, and old-style initializations PDWORD_T TYPEDEF PTR DWORD ; First, define a pointer type ulong DWORD 0, 0FFFFFFFFh ; min, max values for unsigned dword pulong PDWORD_T OFFSET ulong ; pointer to the ulong variable old_style DD 1.314 ; old style, floats are accepted new_int SDWORD 1.314 ; new style, error - must use integers new_real REAL4 1314 ; new style, error - must use floats ; Allocate some real numbers using decimal floating-point literals float_f REAL4 123.45 ; 4-byte IEEE real double_f REAL8 98.7654E1 ; 8-byte IEEE real longdbl_f REAL10 1000.0E-2 ; 10-byte IEEE real ; The same values using hexdecimal floating-point literals float_h REAL4 42F6E666r ; 4-byte IEEE real double_h REAL8 408EDD3B645A1CACr ; 8-byte IEEE real longdbl_h REAL10 4002A000000000000000r ; 10-byte IEEE real
Initialization of Aggregate Types
An aggregate data item is a collection of one or more sub-items of possibly dissimilar types that are allocated, initialized, and treated as a single unit. The sub-items usually have unique names, and their positions relative to other sub-items is significant. The assembler provides the ability to define aggregate types through use of the #RECORD, #STRUCT, and #UNION directives.
Initialization of an aggregate data item requires a programming notation that isolates the entire aggregate from surroundings constructs, and denotes the position of each sub-item within the aggregate. The syntax for this construct is as follows:
Aggregate-Initializer:
- {[Initializer-List]}
- <[Initializer-List]>
Initializer-List:
- Initializer-Item
- Initializer-List,[LineBreak] Initializer-Item
Initializer-Item:
- [Scalar-Initializer]
- [Aggregate-Initializer]
- [Array-Initializer]
The syntax requires that an Aggregate-Initializer be enclosed in an outer set of braces or angle brackets, but the Initializer-List or individual comma-separated Initializer-Items may be left unspecified, in which case a default initializer value is used. Commas are used to denote the position of each sub-item within the entire aggregate, and nested initializers are allowed to accommodate imbedded occurrences of other aggregates (or vector types, which share the same initializer syntax).
When initializing an instance of a union, the assembler only allows an initializer to be specified for the first field defined in the union type.
Examples
Here are some examples of aggregate initialization:
YES equ 1 NO equ 0 MAYBE equ -1 BOOL_T typedef sbyte IDEAS_T struct sanctum BOOL_T ? ; For scalar data, use the ? operator peace BOOL_T ? ; to request an uninitialized value. pilzner BOOL_T ? IDEAS_T ends PROBLEM_T struct work BOOL_T YES ; Establish default initial values that car BOOL_T NO ; can be inherited when an instance of house BOOL_T MAYBE ; the structure is allocated PROBLEM_T ends SOLUTION_T struct fixing PROBLEM_T {} ; Outermost set of braces required even IDEAS_T {} ; with unspecified (default) initializers SOLUTION_T ends DATA segment ProblemWith PROBLEM_T { NO , , MAYBE } ; First-level structure ThinkOf SOLUTION_T { { YES , YES , YES }, ; Intializer syntax for { NO , NO , NO } } ; imbedded structures DATA ends CODE segment assume ds : DATA mov al, NO or al, ProblemWith.work or al, ProblemWith.car or al, ProblemWith.house jz exit mov ThinkOf.fixing.work, NO ; References to named fields in mov ThinkOf.fixing.car, NO ; imbedded structures must be mov ThinkOf.fixing.house, NO ; fully qualified. exit: mov ThinkOf.pilzner, YES ; Reference to "promoted" field ret CODE ends end
Initialization of Vector Types
A vector data item is a linear collection of one or more sub-items of identical type that are allocated, initialized, and treated as a single unit. A vector (more commonly referred to as an array) is defined to have a specific number of items n, which are numbered from 0 to n - 1 and occupy a contiguous area of allocated storage. The items in the vector may be of any type, possibly even other vectors (commonly known as a multi-dimensional array). The assembler provides the ability to define vector types through the use of the standard Type-Declaration syntax.
The syntax required to initialize a vector is similar to that used for an aggregate data type, and is as follows:
Array-Initializer:
- {[Initializer-List] }
- <[Initializer-List] >
Initializer-List:
- Initializer-Item
- Initializer-List, [LineBreak] Initializer-Item
Initializer-Item:
- [Scalar-Initializer]
- [Aggregate-Initializer]
- [Array-Initializer]
The syntax requires that an Array-Initializer be enclosed in an outer set of braces or angle brackets, but the Initializer-List or individual comma-separated Initializer-Items may be left unspecified, in which case a default initializer value is used. Commas are used to denote the position of each sub-item within the entire array, and nested initializers are allowed to accommodate imbedded occurrences of other arrays (or aggregate types, which share the same initializer syntax).
Examples
Here are some examples of vector initialization:
; Data structures to define a "computer" data type TRUE equ 1 FALSE equ 0 MB equ 1024 ; Megabytes BOOL_T typedef BYTE ; true or false value INCHES_T typedef BYTE ; number of inches MONITOR_T typedef INCHES_T ; size of monitor in inches KEYBOARD_T typedef BOOL_T ; is a keyboard installed? MOUSE_T typedef BOOL_T ; is a mouse installed? KBYTES_T typedef WORD ; number of kilobytes MBYTES_T typedef WORD ; number of megabytes FPRESENT_T typedef BOOL_T [2] ; up to two floppies installed FSIZE_T typedef KBYTES_T [2] ; how big they are DPRESENT_T typedef BOOL_T [4] ; up to four hardfiles installed DSIZE_T typedef MBYTES_T [4] ; how big they are RAM_T typedef DWORD ; how much memory we have NAME_T typedef BYTE [64] ; what we call the system FLOPPIES_T struct DriveCount FPRESENT_T { TRUE, FALSE } ; assume one floppy installed DriveSize FSIZE_T { 360, 0 } ; assume 360KB in size :-) FLOPPIES_T ends DRIVES_T struct DriveCount DPRESENT_T { TRUE, FALSE, FALSE, FALSE } ; one drive installed DriveSize DSIZE_T { 20, 0, 0, 0 } ; 20MB in size (!) DRIVES_T ends COMPUTER_T struct Monitor MONITOR_T 14 ; Assume a 14 inch monitor Keyboard BOOL_T TRUE ; We have a keyboard Mouse BOOL_T FALSE ; but no mouse Memory RAM_T 640 ; Assume 640KB Floppies FLOPPIES_T {} ; Go with the defaults HardFiles DRIVES_T {} ; Go with the defaults ModelName NAME_T {} ; No default name COMPUTER_T ends DATA segment Circa1997 COMPUTER_T \ ; initializer begins on next line { 17, ; of course, we have a 17 " monitor TRUE, TRUE, ; a keyboard and a mouse 32 * MB, ; 32 Megabytes of ram { { }, ; still one floppy { 1440 } }, ; but it has a 1.2 MB capacity { { , TRUE, TRUE }, ; also have second and third hardfiles { 512, 1024, 4096 } }, ; 512MB, 1 GIG, and 4 GIG { "Spiffatron 9000", 10, 13, ; with a fancy system name "Acme Computers", 10, 13, 0 } } DATA ends end
Intermodule Linkage
To use symbols and procedures in more than one module, ALP must recognize shared data as global to all modules. ALP provides directives to simplify data sharing and a high-level interface to multiple-module programming. With these directives, you can define shared symbols and refer to them from other modules.
This section describes the following intermodule linkage directives:
- COMM
- END
- EXTERN/EXTRN
- EXTERNDEF
- INCLUDELIB
- NAME
- PUBLIC
COMM (Declare Communal Variable)
Declares an uninitialized common or communal variable that is allocated by the linker.
Syntax
COMM [Language-Name] [Distance] Name [[Count]]:TypeName[:Size] [, ...]
Remarks
The arguments to the COMM directive are as follows:
Language-Name Optional parameter that determines how Name is spelled when written to the object file. Used when interfacing with routines written in high-level languages. If not specified, the language defaults to the value set by #.MODEL or #OPTION LANGUAGE.
Distance One of NEAR or FAR; determines the distance of allocated variable. If not specified, the current memory model determines the distance. The default is NEAR if no memory model is active.
Name The name of the variable to be allocated by the linker. This field is required.
[Count] Optional; if specified, this parameter must be surrounded by square brackets. The Count parameter can be thought of as a major (row) array dimension. It defaults to 1 if not specified.
TypeName Required parameter that specifies the type of the variable being allocated. It must be a single keyword or identifier that specifies a Distance-TypeName, Scalar-TypeName, or UserDefined-TypeName.
Size Size is an optional parameter that can be thought of as a minor (column) array dimension. It defaults to 1 if not specified.
Communal variables are allocated by the linker. When the linker combines object modules together, all instances of an identically-named communal variable are merged into a single instance (union), and are uninitialized.
The allocated size of a communal variable is the largest size requested by all encountered references.
The allocation order with respect to the addresses of other global symbols is undefined; an application must not depend on the address of a communal variable being less than or greater than that of another global symbol.
A variable allocated with the COMM directive need not be declared in all referencing modules as communal; the linker matches all #EXTERN/EXTRN references with that of the communal variable. Similarly, a variable allocated in one module with the #PUBLIC directive may be declared in other modules as communal.
Since communal variables cannot be initialized and their address positions cannot be compared, use of the COMM directive is discouraged. The #EXTERNDEF directive should be used instead.
END (Define End of Module and Entry Point)
The ENDdirective has two functions:
- Identifies the end of the source program.
- Identifies the symbol that is the name of the entry point (through the Expression on the END directive)
Syntax
END [Expression]
Remarks
All source files must have the END directive as the last statement. Any lines following the END statement are ignored by the assembler.
When the linker builds an application program from one or more object modules, it needs to know where the entry point is for the operating system to pass initial control. If you do not specify an entry point, none is assumed. Only one module can identify a label as the entry point by specifying that label on its END statement. Any module not defining an operating system entry point must not have an entry point identified on its END statement. If you fail to define an entry point for the main module, your program may not be able to initialize correctly. It will assemble and link without error, but it cannot run.
Example
The following example is the END statement for the section of code that starts with the name BEGIN.
END BEGIN
EXTERN/EXTRN (Declare External Identifier)
The EXTERN directive specifies a declaration for the external symbol Name so that it may be referred to within this module. The actual definition for the symbol occurs in some other module, and the linker resolves all such external declarations to a single definition for Name.
Syntax
EXTERN [Language-Name] Name [(Default-Resolution)]:Type [, ...]
Where Typeis one of:
- ABS
- Type-Declaration
Remarks
The obsolete spelling for the EXTERN directive is EXTRN.
The external source module that defines the symbol must give it public visibility in the corresponding object module, which is accomplished in assembler language by declaring it with the #COMM directive, defining the symbol in association with an #EXTERNDEF or #PUBLIC directive, or by specifying the PUBLIC or EXPORT attributes in a #PROC directive.
If the EXTERN directive is given within a segment, the assembler assumes that the symbol is located within that segment. If the segment is not known, place the EXTERN directive outside all segments and either use an explicit segment prefix or an ASSUME directive.
A Type value of ABS indicates that Name is an externally-defined constant value. Local references to Nameare treated as immediate values having an Operand Size equal to the Address Size of the segment containing the reference.
Note: If the Type of EXTERN is ABS, it may not be used anywhere in this module where conversion to an immediate value of type BYTE is required. Additionally, the defining module must define the value as a constant symbol.
For example:
FOO EQU 5 PUBLIC FOO
Use of the (default_resolution) syntax declares the external symbol Name to be a "weak" symbol, in which case the linker will pair all such declarations with the symbol default_resolution unless a standard "strong" public definition for Name is encountered during the link.
Example
IN THE SAME SEGMENT | IN ANOTHER SEGMENT |
---|---|
IN MODULE 1:
cseg segment public tagn . . . tagn: . . . cseg ends IN MODULE 2: cseg segment extern tagn:near . . . jmp tagn cseg ends |
IN MODULE 1:
csega segment public tagf . . . tagf: . . . csega ends IN MODULE 2: extern tagf:far csegb segment . . . jmp tagf csegb ends |
EXTERNDEF (Declare Global Identifier)
The EXTERNDEF directive combines the functionality of the #EXTERN/EXTRN and #PUBLIC directives. It provides a uniform way to declare global symbols that are to be shared across multiple modules.
Syntax
EXTERNDEF [Language-Name] Name:Type [, ...]
Where Typeis one of:
- ABS
- Type-Declaration
Remarks
A symbol declared with EXTERNDEF is treated as #PUBLIC if a definition for the symbol is encountered during the assembly, otherwise the symbol is assumed to be defined in another module and is treated as if it were declared with the #EXTERN/EXTRN directive.
Example
The following example shows how a declaration for the ReturnCode symbol can be shared between two modules (Main.asm and FileErr.asm) by way of a common header file (ErrNum.inc):
; ---------------------------------------------------------------------- ; ErrNum.inc RETCODE_T typedef DWORD RC_NoError equ 0 RC_FileNotFound equ 1 RC_SystemError equ 3 EXTERNDEF ReturnCode:RETCODE_T ; declaration ; ---------------------------------------------------------------------- ; FileErr.asm .386 .MODEL FLAT INCLUDE ErrNum.inc ; bring in error number definitions ; and declaration for ReturnCode .CODE ; Tell the user about the file error, ; then make sure the program has a non-zero exit status FileError proc . . . mov ReturnCode, RC_FileNotFound ret FileError endp end ; ---------------------------------------------------------------------- ; Main.asm .386 .MODEL FLAT INCLUDE ErrNum.inc ; bring in error number definitions ; and declaration for ReturnCode EXTERNDEF FileError:PROC ; This could be in a common header too .DATA ReturnCode RETCODE_T RC_NoError ; actual definition of ReturnCode .CODE Main proc . . . . . . call FileError ; hypothetical error condition . . . . . . mov eax, ReturnCode ; load the exit status call Exit ; and shutdown the program Main endp end Main
INCLUDELIB (Pass Library Name to Linker through Object File)
The INCLUDELIB directive is used to inform the linker that a library file of a given name is to be used when attempting to resolve external references declared by this module.
Syntax
INCLUDELIB FileName
Remarks
The FileName argument is parsed as a contiguous string of arbitrary characters, and should constitute a file name that is valid in the context where it will be used. The FileName should be coded as a <text-literal> if it is to contain embedded spaces or other special characters.
The assembler emits a special record into the object file which contains the string of characters given by the FileName entry. This record instructs the linker to include the named library file in its list of libraries to be searched during the process of resolving external references. The assembler attaches no other meaning to the object file record, and it is up to the linker to interpret the file name for any special meaning (such as search path information, file name extension, and so on).
Use of this directive avoids the need to explicitly reference the library name in a linker invocation parameter, and helps to avoid the problems that can arise when such parameters are specified incorrectly.
Example
INCLUDELIB OS2386.LIB
NAME (Specify Module Name)
The NAME directive assigns a module a name.
Syntax
NAME module-name
Remarks
The NAME directive is ignored; it is provided for backward compatibility with other assemblers.
PUBLIC (Make Symbol Visible to Other Modules)
The PUBLIC directive makes defined symbols available to other programs that are to be linked. The information referred to by the PUBLIC directive is passed to the linker.
Syntax
PUBLIC [Language-Name] Identifier[, ...]
Remarks
Identifier can be a variable or a label (including PROC labels). Register names and any symbols defined by EQU or = to floating-point numbers or integers larger than 4 bytes are incorrect entries.
Example
PUBLIC GETINFO ; Make GETINFO visible to linker GETINFO PROC FAR PUSH BP ; Save caller's register MOV BP,SP ; Get address of parameters ; BODY OF SUBROUTINE POP BP ; restore caller's register RET ; return to caller GETINFO ENDP
Listing Control
ALP creates an assembler listing of your source file whenever you use a related source code directive or specify the #+Fl option on the ALP command line.
The assembler listing contains:
- Cumulative Listing Line Number
- Individual Source File Line Number
- Macro Expansion Line Number
- Macro Definition Line Number
- Macro Expansion Indentation Level
- Macro Expansion Nesting Level
- Include File Nesting Level
- Conditional Assembly Nesting Level
- True or False Conditional Flag
- Location Counter Offset Value
- Generated Machine Code Data
- Source Line Data
If requested (via the [[#+Ls] ]command line option) a symbol table listing is produced that shows the names and values of all of the user-defined identifiers created during the assembly. The values of certain predefined identifiers are also show in the symbol table listing.
The symbol table listing is divided into the following categories:
- Macro Names
- Text Equate Names
- Structures/Union Type Names
- Orphaned Structure Fields
- Record Type Names
- Typedef Type Names
- Group Names
- Segment Names
- Numeric Equate Names
- Code Label Names
- Procedure Names
- Variable Names
ALP places the symbol table listing at the end of the listing output. ALP lists only the types of symbols encountered in the program. For example, if your program does not define any macros, the Macro Names section is omitted from the listing output.
This section describes the following listing control directives:
- %BIN
- .CREF
- .LALL
- .LFCOND
- .LIST
- .LISTALL
- .LISTIF
- .LISTMACRO
- .LISTMACROALL
- .NOCREF
- .NOLIST
- .NOLISTIF
- .NOLISTMACRO
- PAGE
- .SALL
- .SFCOND
- SUBTITLE
- SUBTTL
- .TFCOND
- TITLE
- .XALL
- .XCREF
- .XLIST
%BIN (Set Listing Width for Object Code Field)
Sets the width of the object code field in the listing file to size columns.
Syntax
%BIN size
.CREF/.XCREF (Control Symbol Cross Referencing)
The output of the cross-reference information is controlled by these directives. The default condition is the .CREF directive. When the assembler finds a .XCREF directive, cross-reference information results in no output until the assembler finds
Note: The assembler does not produce cross-referencing information. These directives are provided for source file compatibility with other assemblers.
Syntax
.CREF
or
.XCREF [[operand[, ...]]
Remarks
The .XCREF directive can have an optional operand consisting of a list of one or more variable names suppressed in the cross-reference listing.
.LFCOND (List False Conditionals)
You use the .LFCOND (List False Conditionals) directive to list conditional blocks that are evaluated as false.
Syntax
.LFCOND
Remarks
Equivalent to the .LISTIF directive.
.LFCOND does not have an operand. You can end this state either by issuing .TFCOND, which reverts to the default state concerning listing of false conditionals (but with the default state redefined as being in the opposite state,) or by issuing the .SFCOND, which suppresses the listing of false conditionals.
The assembler does not print false conditionals within macros when .LALL is set.
.LIST/.XLIST (Control Listing File Output)
These two directives control output to the listing file.
Syntax
.LIST
or
.XLIST
Remarks
If a listing is not being created, these directives have no effect. The .LIST is the default condition. When the assembler finds an .XLIST, the assembler does not list the source and the object code until it finds a .LIST directive.
.LISTALL (List All Statements)
Starts the listing of all statements.
Syntax
.LISTALL
Remarks
Equivalent to the combination of .LIST, .LISTIF, and .LISTMACROALL.
.LISTIF (List False Conditionals)
Starts the listing of all statements, including those in false conditional blocks.
Syntax
.LISTIF
Remarks
Equivalent to the combination of .LIST, .LISTIF, and .LISTMACROALL.
.LISTMACRO/.XALL (List Code and Data Statements in Macros)
Starts listing of only those statements that generate code or data when processing macro expansions.
Syntax
.LISTMACRO
or
.XALL
Remarks
ALP does not support this mode; it is provided for compatibility with other assemblers.
.LISTMACROALL/.LALL (List All Statements in Macros)
Starts listing of all statements when processing macros expansions.
Syntax
.LISTMACROALL
or
.LALL
.NOCREF (Suppress Symbol Cross Referencing)
Suppresses the listing of symbols in the symbol table and cross-referencing output.
Note: The assembler does not produce cross-referencing information. This directive is provided for source file compatibility with other assemblers.
Syntax
.NOCREF [name[,name]...]
Remarks
If names are specified, only the given names are suppressed. Same as .XCREF.
.NOLIST (Suppress List Output)
Suppresses program listing.
Syntax
.NOLIST
Remarks
Same as .XLIST.
.NOLISTIF (Do Not List False Conditionals)
Suppresses listing of conditional blocks whose condition evaluates to false (0).
Syntax
.NOLISTIF
Remarks
This is the default. Same as .SFCOND.
.NOLISTMACRO (Do Not List Macro Expansions)
Suppresses listing of macro expansion.
Syntax
.NOLISTMACRO
Remarks
Same as .SALL.
This is the default setting for ALP.
PAGE (Control Listing Page Length and Width)
The PAGE directive controls the length and width of each listing page. Place the PAGE directive in the source file to control the format of the listing file produced during assembly.
Syntax
PAGE [operand-1][,operand-2]
or
PAGE +
Remarks
Using PAGE + or the PAGE directive without an operand entries causes the printer to go to the top of the page and increases the page number by 1. The assembler normally takes this action only when a page is full.
The operand-1 entry specifies the actual number of lines that can be physically printed on the page; the default value is 66.
Use the operand-2 entry to control the width of the page. The page width without a specified number is 132.
Note: The PAGE directive does not set the printer to the desired line width. For proper formatting of the listing, initialize the printer to operate at a corresponding line width before printing the listing file.
SUBTITLE/SUBTTL (Specify Listing Page Subtitle)
Defines the subtitle displayed in the user area of each page in the listing output.
Syntax
SUBTITLE text
or
SUBTTL text
.TFCOND (Toggle Listing of False Conditionals)
Toggles listing of false conditional blocks.
Syntax
.TFCOND
TITLE (Specify Listing Page Title)
Defines the title displayed in the user area of each page in the listing output.
Syntax
TITLE text
Procedure Control
Procedure control directives allow you to organize your code into procedures. The PROC and ENDP directives mark the beginning and end of a procedure. Also, PROC can automatically:
- Preserve higher register values that should not change but that the procedure might otherwise alter
- Set up a local stack pointer, so that you can access parameters and local variables placed on the stack
- Adjust the stack when the procedure ends
This section describes the following procedure control directives:
- PROC
- LOCAL
- ENDP
PROC (Identify Code Procedure)
The PROC directive identifies a block of code. By dividing the code into blocks, each of which performs a distinct function, you can clarify the overall function of the complete module.
The PROC directive also identifies the procedure distance to help insure that the assembler generates the appropriate instructions for calling and returning from the procedure while maintaining the integrity of the run-time stack.
Syntax
Procedure-Name PROC [Attributes] [Register-List] [Parameter-List] . . . RET [Constant] . . . Procedure-Name ENDP
Refer to the following sections for descriptions of the optional arguments to the PROC directive:
- Attributes
- Register-List
- Parameter-List
Remarks
You can execute the block of code identified by the PROC directive in-line, jump to it, or start it with a CALL instruction. If the PROC is called from code that has another ASSUME CS value, you must use the appropriate FAR, FAR16, or FAR32 distance attribute.
The NEAR attribute causes any RET instruction coded within the procedure to be an intra-segment return that pops a return offset from the stack. You can call a NEAR subroutine only from the same segment. However, the FAR attribute causes RET to be an inter-segment return that pops both a return offset and a segment base from the stack. You can call a FAR subroutine from any segment; a FAR subroutine is usually called from a segment other than the one containing the subroutine.
Example
In this example, the Near_Name subroutine is called by the Far_Name subroutine.
PUBLIC Far_Name Far_Name PROC FAR CALL Near_Name RET ; Pops return offset and seg base value Far_Name ENDP PUBLIC Near_Name Near_Name PROC NEAR . . . RET ; pops only return offset Near_Name ENDP
You can call the Near_Name subroutine directly from a NEAR segment by using:
CALL Near_Name
A FAR segment can indirectly call the second subroutine by first calling the Far_Name subroutine with:
CALL Far_Name
A CALL to a forward-referenced symbol assumes the symbol is NEAR. If that symbol is FAR, the CALL must have an override, for example:
CALL FAR PTR Forward_Reference
Attributes
The optional fields in the Attributes argument control how the procedure is defined.
Syntax
[Distance] [Language] [Visibility]
Remarks
The various Attribute fields are defined as follows:
Distance Determines the type of CALL instruction that should be used to invoke the procedure, and the type of RET instruction generated by the assembler. The default is NEAR if no #.MODEL directive has been specified, or if the model has been set to TINY, SMALL, COMPACT, or FLAT. The default is FAR if the model has been set to LARGE, MEDIUM, or HUGE. If the programmer is using segments with mixed address sizes (USE16 and USE32) on a 32-bit processor, then the NEAR16, FAR16, NEAR32, and FAR32 keywords may also be used.
Language Determines the calling convention used by the procedure, and the naming convention used when writing the procedure name to the object file. The calling convention defines the layout of the stack frame upon entry to the procedure and how the stack frame is destroyed upon procedure exit. See the section on #LabelNames for more information on language naming conventions.
With the BASIC, FORTRAN, and PASCAL calling conventions, the called procedure expects arguments to be pushed on the stack from left to right, causing the rightmost parameter to be at the lowest stack address and closest in proximity to the frame pointer (the BP or EBP register). With this arrangement, the called procedure always knows the exact amount of stack space used by the parameters, and is responsible for removing them from the stack with a RET Constant instruction when the procedure exits. Such procedures are unable to accept a variable number of arguments.
With the C, STDCALL, SYSCALL, and OPTLINK calling conventions, the called procedure expects arguments to be pushed on the stack from right to left, causing the leftmost parameter to be at the lowest stack address and closest in proximity to the frame pointer (the BP or EBP register). With this arrangement, the calling procedure is free to push additional arguments on the stack, and is responsible for restoring the stack after the called procedure returns (STDCALL requires the called procedure to restore the stack if a fixed number of arguments is being passed).
With the OPTLINK 32-bit calling convention (as defined by the IBM VisualAge C/C++ Compiler environment), up to three parameters will be passed in machine registers to the called procedure, provided they not larger than a DWORD in size. The EAX, EDX, and ECX registers (respectively) are used for this purpose. Stack space for the parameters is still allocated, but the parameter values are not actually copied onto the stack. Refer to the documentation for the IBM VisualAge C++ compiler for more information on the OPTLINK calling convention.
Visibility Determines if the procedure name is written to the object file as a global identifier, allowing it to be referenced by other modules. The allowable values are PRIVATE, PUBLIC, and EXPORT. If operating in #M510 mode and no #.MODEL directive with a Language-Name has been specified, then the default visibility is PRIVATE. In all other situations, the default visibility is PUBLIC unless the default has been overridden by an #OPTION LANGUAGE directive.
When the PRIVATE keyword is used, the procedure name is visible only within the defining module at assembly-time. When the visibility is PUBLIC, the procedure name is made visible to other modules at link-time. The same is true of EXPORT visibility, but in this case the assembler emits a special record into the object file that causes the linker to also make the symbol visible as an exported entry point in the executable module, allowing it be called by other modules at program run-time.
Register List
The optional Register-List defines those registers used in the body of the procedure that must be preserved on behalf of the caller. The assembler generates code to save these registers on the stack when the procedure is entered, and to restore them when the procedure exits.
Syntax
USES Register [Register ...]
Register:
- 16-Bit-Register
- 32-Bit-Register
- Segment-Register
Remarks
When more than one register is specified, do not use commas to separate the register keywords; use blanks or tabs instead.
Parameter List
The optional Parameter-List defines the parameters that the caller passes to the procedure on the run-time stack.
Syntax
[,[LineBreak]] Parm-List
Parm-List:
- Parm-Spec[,[LineBreak]Parm-Spec...]
Parm-Spec:
- Parameter-Name[:Type]
The introductory comma in front of the Parm-List is only required if a LineBreak is used to put the first Parm-Spec on the line following the PROC directive.
The optional LineBreak entry allows you to end a Parm-Spec entry with a comma, enter an optional EndOfLine-Comment followed by a physical NewLine character, then continue the Parm-List on the next line.
Remarks
Each Parameter-Name is defined as a Numeric-EquateName]] that is visible only from within the body of the procedure. The value assigned to the parameter name is an expression that defines the parameter type and its location on the stack relative to the value of the frame pointer (the BP or EBP register). The assembler automatically calculates the correct offset value based upon the size of the parameter type.
The Type field is specified as a Type-Declaration and defines the data type associated with the Parameter-Name. If this field is omitted, the data type defaults to WORD if the procedure is defined within a USE16 segment, and DWORD if the procedure is defined within a USE32 segment.
The programmer can read from and store into the locations defined by the Parm-Spec entries as though they were regular named variables, but if the parameter names are to be combined in indexed expressions with other registers, the normal rules for specifying BP - and EBP - relative expressions must be followed.
Example
This example defines a ReadBuffer procedure to accept four arguments passed on the stack.
.386 ; Assemble for 32-bit processors .model flat ,syscall ; OS/2 programming model/calling convention EXTERN DosRead:PROC ; OS/2 DosRead() API INCLUDELIB os2386.lib ; This lets us link to the API .code ; Open the code segment ;------------------------------------------------------------------------------ ; Call operating system to read input into a buffer ;------------------------------------------------------------------------------ ReadBuffer PROC, ; need comma to continue the PROC statement hFile:dword, ; parm 1: Read handle pBuffer: ptr byte, ; parm 2: Address of input buffer cbRead: dword, ; parm 3: Size of input buffer pulActual:ptr dword ; parm 4: Address of byte count from read ; set up to call the OS/2 DosRead entry point PUSH pulActual ; arg 4 PUSH cbRead ; arg 3 PUSH pBuffer ; arg 2 PUSH hFile ; arg 1 CALL DosRead ; Invoke syscall (SYSTEM) function ADD ESP, DWORD*4 ; Remove the four parameters we pushed ; onto the stack for the DosRead call RET ReadBuffer ENDP
LOCAL (Define Local Procedure Variables)
The LOCAL directive defines local stack variables from within a code procedure.
Syntax
LOCAL Local-Spec [,[LineBreak] Local-Spec...]
Local-Spec:'
- Local-Name[:Type-Declaration]
- Local-Name[Count][:Type-Declaration]
The optional LineBreak entry allows you to end a Local-Spec entry with a comma, enter an optional EndOfLine-Comment followed by a physical NewLine character, then continue with a new Local-Spec on the next line.
Remarks
The LOCAL assembler directive can only appear within the body of a procedure. If used, the LOCAL directive(s) must immediately follow the PROC statement that encloses them, and they must appear before any instructions, code labels, or directives that modify the location counter. Multiple LOCAL directives may appear in succession.
Each Local-Name is defined as a Numeric-EquateName that is visible only from within the body of the procedure. The value assigned to the variable name is an expression that defines the type of the variable and its location on the stack relative to the value of the frame pointer (the BP or EBP register). The assembler reserves space on the stack for each local variable and automatically calculates their locations. After all Local-Spec entries have been processed, the assembler allocates the space by generating instructions to adjust the stack pointer. The assembler also generates instructions to restore the state of the stack and frame pointers when the procedure exits.
The optional [Count] entry can be used to indicate that the variable is a simple "array" of values, where Count is a constant expression. If used, the square brackets surrounding the Countmust be specified. Use of this notation is discouraged however, because it does not associate a "true array" data type with the variable, and cannot be viewed as such from within a symbolic debugger. ALP allows the variable to be associated with a "true array" data type through use of the native Type-Declaration syntax.
The Type-Declaration field specifies the data type to be associated with the Local-Name. If this field is omitted, the data type defaults to WORD if the procedure is defined within a USE16 segment, and DWORD if the procedure is defined within a USE32 segment.
- Example
; bootdrv.asm : Returns value of OS/2 boot drive as exit code ; assemble as : alp +Od bootdrv.asm ; link as : link386 /de bootdrv; .386 ; Assemble for 32-bit processors .model flat, syscall ; OS/2 flat model/calling convention .stack 4096 EXTERN DosExit:PROC ; OS/2 DosExit() API EXTERN DosQuerySysInfo:PROC ; OS/2 DosQuerySysInfo() API INCLUDELIB os2386.lib ; link with these routines ; These are values taken from OS/2 API headers. See the OS/2 Toolkit ; Control Program Programming Guide and Reference for more information. EXIT_PROCESS EQU 1 ; for DosExit QSV_BOOT_DRIVE EQU 5 ; For DosQuerySysInfo ULONG TYPEDEF DWORD ; use OS/2 type convention .code ; open code segment main PROC LOCAL BootDrive:ULONG ; place to put value of boot drive ; Push parameters to DosQuerySysInfo onto the stack PUSH sizeof BootDrive ; arg 4 : size of output buffer LEA EAX, BootDrive ; arg 3 : Address of buffer PUSH EAX PUSH QSV_BOOT_DRIVE ; arg 2 : last ordinal value to return PUSH QSV_BOOT_DRIVE ; arg 1 : first ordinal, same as last CALL DosQuerySysInfo ; invoke API ADD ESP, DWORD*4 ; remove the parameters from the stack CMP EAX,0 ; Did the API succeed? MOV EAX,0 ; if not, use zero as a return code JNZ SomeKindOfError ; and skip around to the exit logic MOV EAX, BootDrive ; else, return the boot drive value SomeKindOfError: push EAX ; exit code push EXIT_PROCESS ; terminates all threads call DosExit ; exit to calling process RET ; never executed main ENDP END main
ENDP (Close a Procedure Definition Block)
Every procedure block opened with the PROC directive must be ended with the ENDP directive.
Syntax
procedure-name ENDP
Remarks
If the ENDP directive is not used with the PROC directive, an error occurs. An unmatched ENDP also causes an error.
Note: See the PROC directive in this chapter for more detail and examples of ENDP use.
Example
PUSH AX ; Push third parameter PUSH BX ; Push second parameter PUSH CX ; Push first parameter CALL ADDUP ; Call the procedure ADD SP,6 ; Bypass the pushed parameters . . . ADDUP PROC NEAR ; Return address for near call ; takes two bytes PUSH BP ; Save base pointer - takes two more ; so parameters start at 4th byte MOV BP,SP ; Load stack into base pointer MOV AX,[BP+4] ; Get first parameter ; 4th byte above pointer ADD AX,[BP+6] ; Get second parameter ; 6th byte above pointer ADD AX,[BP+8] ; Get third parameter ; 8th byte above pointer POP BP ; Restore base RET ; Return ADDUP ENDP
In this example, three numbers are passed as parameters for the procedure ADDUP. Parameters are often passed to procedures by pushing them before the call so that the procedure can read them off the stack.
Processor Control
ALP provides a set of directives for selecting processors and coprocessors. Once you select a processor, you must only use the instruction set available for that processor. The default is the 8086 processor. If you always want your code to run on this processor, you need not add any processor directives.
This section describes the following processor control directives:
- .8086
- .8087
- .186
- .286
- .286P
- .287
- .386
- .386P
- .387
- .486
- .486P
- .586
- .586P
- .686
- .686P
- .MMX
- .NOMMX
.8086 (Select 8086 Processor Instruction Set)
The .8086 directive tells the assembler to recognize and assemble 8086 instructions. This directive assembles only 8086 and 8088 instructions (the 8088 instructions are identical to the 8086 instructions). ALP assembles 8086 instructions by default.
Syntax
.8086
Remarks
The .8086directive does not have an operand.
Note: The .8086 directive does not end ALP 8087/80287 mode.
.8087 (Select 8087 Coprocessor Instruction Set)
The .8087 directive tells the assembler to recognize and assemble 8087 instructions and data formats. ALP assembles 8087 instructions by default.
Syntax
.8087
Remarks
The .8087 directive does not have an operand.
.186 (Select 80186 Processor Instruction Set)
The .186 directive tells the assembler to recognize and assemble 8086 or 8088 instructions and the additional instructions for the 80186 microprocessor.
Syntax
.186
Remarks
The .186 directive does not have an operand. Use it only for programs that run on an 80186 microprocessor.
.286 (Select 80286 Processor Instruction Set)
Enables assembly of nonprivileged instructions for the 80286 processor. Disables assembly of instructions introduced with later processors. Also enables 80287 instructions.
Syntax
.286
.286P (Select 80286 Processor Protected Mode Instruction Set)
The .286P directive tells the assembler to recognize and assemble the protected instructions of the 80286 in addition to the 8086, 8088, and nonprotected 80286 instructions.
Syntax
.286P
Remarks
The .286P directive does not have an operand. Use it only for programs run on an 80286 processor using both protected and nonprotected instructions.
.287 (Select 80287 Coprocessor Instruction Set)
The .287 directive tells the assembler to recognize and assemble instructions for the 80287 floating point math coprocessor. The 80287 instruction set consists of all 8087 instructions, plus three additional instructions.
Syntax
.287
Remarks
The .287 directive does not have an operand. Use it only for programs that have 80287 floating point instructions and run on an 80287 math coprocessor.
.386 (Select 80386 Processor Instruction Set)
Enables assembly of nonprivileged instructions for the 80386 processor. Disables assembly of instructions introduced with later processors. Also enables 80387 instructions.
Syntax
.386
.386P (Select 80386 Processor Protected Mode Instruction Set)
Enables assembly of all instructions (including privileged) for the 80386P processor. Disables assembly of instructions introduced with later processors. Also enables 80387 instructions.
Syntax
.386P
.387 (Select 80387 Coprocessor Instruction Set)
Enables assembly of instructions for the 80387 coprocessor.
Syntax
.387
.486 (Select 80486 Processor Instruction Set)
Enables assembly of instructions for the 80486 processor. Also enables 80387 (and later) floating point instructions.
Syntax
.486
Remarks
The .486 directive is not available in M510 mode.
.486P (Select 80486 Processor Protected Mode Instruction Set)
Enables assembly of all instructions (including privileged) for the 80486 processor. Also enables 80387 (and later) floating point instructions.
Syntax
.486P
Remarks
The .486Pdirective is not available in #M510 mode.
.586 (Select Pentium/586 Processor Instruction Set)
Enables assembly of instructions for the Pentium processor family. Also enables 80387 (and later) floating point instructions.
Syntax
.586
Remarks
The .586directive is not available in M510 mode or M600 mode.
.586P (Select Pentium/586 Processor Protected Mode Instruction Set)
Enables assembly of all instructions (including privileged) for the Pentium processor family. Also enables 80387 (and later) floating point instructions.
Syntax
.586P
Remarks The .586P directive is not available in M510 mode or M600 mode.
.686 (Select Pentium Pro/686 Processor Instruction Set)
Enables assembly of instructions for the Pentium Pro processor family. Also enables 80387 (and later) floating point instructions.
Syntax
.686
Remarks
The .686directive is not available in #M510 mode or #M600 mode.
.686P (Select Pentium Pro/686 Processor Protected Mode Instruction Set)
Enables assembly of all instructions (including privileged) for the Pentium Pro processor family. Also enables 80387 (and later) floating point instructions.
Syntax
.686P
Remarks
The .686P directive is not available in #M510 mode or #M600 mode.
.MMX (Select MMX Processor Instruction Set Extensions)
Enables recognition of mnenomics for the MMX instruction set extensions.
Syntax
.MMX
Remarks
If 586 mnemonics (or later) are not already being recognized, the .MMX directive also causes an implicit .586 directive to be executed.
Issuing any .486 (or earlier) processor selection directive causes recognition of MMX mnemonics to be disabled.
If MMX mnemonics are being recognized, issuing a .586 (or later) processor selection directive does not cause recognition of MMX mnemonics to be disabled. If this behavior is desired, use the .NOMMX directive.
The .MMX directive is not available in #M510 mode or #M600 mode.
.NOMMX (Deselect MMX Processor Instruction Set Extensions)
Disables recognition of mnenomics for the MMX instruction set extensions.
Syntax
.NOMMX
Remarks
Does not affect recognition of instruction mnemonics for the currently selected primary processor; it only disables recognition of the MMX mnemonics.
The .NOMMX directive is not available in #M510 mode or #M600 mode.
Example
; Top of file - no processor currently selected .MMX ; enables both MMX and 586 mnemonics .NOMMX ; 586 mnemonics still enabled .686 ; 686 mnemonics now being recognized .MMX ; 686 and MMX mnemonics now being recognized .NOMMX ; 686 mnemonics still enabled
.SIMD (Select SIMD Processor Instruction Set Extensions)
Enables recognition of mnenomics for the SIMD (single instruction, multiple data) instruction set extensions.
- Syntax
.SIMD
- Remarks
If 686 mnemonics (or later) are not already being recognized, the .SIMD directive also causes an implicit .686 directive to be executed.
Issuing any .586 (or earlier) processor selection directive causes recognition of SIMD mnemonics to be disabled.
If SIMD mnemonics are being recognized, issuing a .686 (or later) processor selection directive does not cause recognition of SIMD mnemonics to be disabled. If this behavior is desired, use the .NOSIMD directive.
The .SIMD directive is not available in the M510, M600, M611, M612, or M613 emulation modes.
.NOSIMD (Deselect SIMD Processor Instruction Set Extensions)
Disables recognition of mnenomics for the SIMD (single instruction, multiple data) instruction set extensions.
- Syntax
.NOSIMD
- Remarks
Does not affect recognition of instruction mnemonics for the currently selected primary processor; it only disables recognition of the SIMD mnemonics.
The .NOSIMD directive is not available in the M510, M600, M611, M612, or M613 emulation modes.
- Example
; Top of file - no processor currently selected .586 ; 586 mnemonics now being recognized .SIMD ; enables both SIMD and 686 mnemonics .NOSIMD ; 686 mnemonics still enabled
.XMM (Select SIMD Processor Instruction Set Extensions)
Enables recognition of mnenomics for the SIMD (single instruction, multiple data) instruction set extensions.
- Syntax
.XMM
- Remarks
- This directive has the same functionality as the .SIMD directive; it is included for compatibility with MASM 6.14.
- The .XMM directive is not available in the M510, M600, M611, M612, or M613 emulation modes.
Segments
A segment is a collection of instructions or data whose addresses are all relative to the same segment register. The code in your assembler language program defines and organizes segments.
You can define segments by using segment directives or full segment definitions.
This section describes the following directives used to create and manage segments:
- ALIGN
- .CODE
- .CONST
- .DATA
- .DATA?
- DOSSEG
- .DOSSEG
- ENDS
- EVEN
- .FARDATA
- .FARDATA?
- GROUP
- .MODEL
- ORG
- SEGMENT
- .SEQ
- .STACK
ALIGN (Align Code or Data Item)
Advances the current location counter to the next byte boundary that is a multiple of Expression.
Syntax
ALIGN Expression
Example
To align to a 2-byte boundary:
ALIGN 2
To align to a 4-byte boundary:
ALIGN 4
.CODE (Opens Default or Named Code Segment)
Closes the currently opened segment (if any) and opens the default code segment or a segment with the name given by an optional SegmentName parameter. The .CODE directive may only be used if previous .MODEL directive has been processed.
Syntax
.CODE [SegmentName]
Remarks
When the SegmentName parameter is omitted from the .CODE directive, the assembler generates a default code segment whose name is determined by the memory model as follows:
Memory Model | Value for @code |
---|---|
TINY | _TEXT |
SMALL | _TEXT |
MEDIUM | module_TEXT |
COMPACT_TEXT | LARGE module_TEXT |
HUGE | module_TEXT |
FLAT | CODE32 |
The module entry is replaced with base file name of the top-level module being assembled.
When operating in #M510 mode, the SegmentName parameter may only be specified for those memory models that allow multiple code segments (MEDIUM , LARGE, and HUGE), and the value of the #@code symbol is not altered from the default. For other modes of operation, the SegmentName parameter is allowed for any model other than TINY, and the #@code symbol is updated to reflect the SegmentName value.
.CONST (Opens Default Constant Data Segment)
When used with .MODEL, starts a constant data segment for initialized read-only data.
Syntax
.CONST
Remarks
The name of the segment is CONST32 in flat model, and CONST for all other models.
.DATA (Opens Default Data Segment)
When used with .MODEL, starts a near data segment for initialized data.
Syntax
.DATA
Remarks
The name of the segment is DATA32 in flat model, and _DATA for all other models.
.DATA? (Opens Default Uninitialized Data Segment)
When used with .MODEL, starts a near data segment for uninitialized data.
Syntax
.DATA ?
Remarks
The name of the segment is BSS32 in flat model, and _BSS for all other models.
.DOSSEG/DOSSEG (Specify Standard DOS Segment Ordering)
Orders the segments according to the DOS segment convention: CODE first, then segments not in DGROUP, and then segments in DGROUP. The segments in DGROUP follow this order:
- Segments not in BSS or STACK
- BSS segments
- STACK segments
Syntax
.DOSSEG (preferred form) or DOSSEG
Remarks
.DOSSEG is the preferred form.
Use of this directive allows the linker to control the segment ordering according to conventions used in many high-level languages.
ENDS (Close a Segment, Structure, or Union Declaration)
Closes a program segment opened with SEGMENT directive, or ends a structure or union definition opened with the STRUCT or UNION directives. Every SEGMENT, STRUCT, and UNION directive must end with a corresponding ENDS directive.
Syntax
Segment-Name ENDS or Structure-Name ENDS or Union-Name ENDS
Remarks
If the ENDS directive is not used with the corresponding SEGMENT, STRUCT, or UNION directive, an error occurs. An unmatched ENDS also causes an error.
Note: See the #SEGMENT, #STRUCT, and #UNION directives for more details and examples of the use of ENDS.
Example
CONST SEGMENT word public 'CONST' SEG1 DW ARRAY_DATA SEG2 DW MESSAGE_DATA CONST ENDS
EVEN (Align Code or Data Item on an Even Boundary)
The EVEN directive causes the program counter to go to an even boundary (an address that begins a word). This ensures that the code or data that follows is aligned on an even boundary.
Syntax
EVEN
Remarks
If the program counter is not already at an even boundary, EVEN causes the assembler to add a NOP (no operation) instruction so that the counter reaches an even boundary. An error message occurs if EVEN is used with a byte-aligned segment. If the program counter is already at an even boundary, EVEN does nothing.
Example
Before: PC points to 0019 hex (25 decimal).
EVEN
After: PC points to 001A hex (26 decimal).
.FARDATA (Opens Default or Named Far Data Segment)
When used with .MODEL, starts a far data segment for initialized data.
Syntax
.FARDATA [SegmentName]
Remarks
If the SegmentName parameter is not specified, the assembler sets it to FAR_DATA.
.FARDATA? (Opens Default or Named Uninitialized Far Data Segment)
When used with .MODEL, starts a far data segment for uninitialized data.
Syntax
.FARDATA? [SegmentName]
Remarks
If the SegmentName parameter is not specified, the assembler sets it to FAR_BSS.
GROUP (Treat Multiple Segments as a Single Unit)
The GROUP directive associates a group Name with one or more segments, and causes all labels and variables defined in the given segments to have addresses relative to the beginning of the group, rather than to the segments where they are defined.
Syntax
Name GROUP Segment-Name [, ...]
Remarks
Each Segment-Name entry must be a unique segment name assigned by the #SEGMENT directive. A Segment-Name entry may be a forward reference to a subsequently declared segment name.
An additional occurrence of a given group Name in a subsequent GROUP directive does not constitute a redefinition, but instead the effect is cumulative. The group Name itself is declared the first time it appears in a GROUP directive, but the group definition is not complete until the end of the source module is reached. The final group definition is the cumulative list of all unique segments named in all occurrences of a GROUP directive for that group Name.
Segments in a group need not be contiguous. Segments that do not belong to the group can be loaded between segments that do belong to the group. The only restriction is that for USE16 segments the distance (in bytes) between the first byte in the first segment of the group and the last byte in the last segment must not exceed 65535 bytes.
Group names can be used with the #ASSUME directive and as an operand prefix with the segment override operation (:).
Example
The following example shows how to use the GROUP directive to combine segments:
In Module A:
CGROUP GROUP XXX,YYY XXX SEGMENT ASSUME CS:CGROUP . . . XXX ENDS YYY SEGMENT ASSUME CS:CGROUP . . . YYY ENDS
In Module B:
CGROUP GROUP ZZZ ZZZ SEGMENT ASSUME CS:CGROUP . . . ZZZ ENDS
The next example shows how to set DS with the paragraph number of the group called DGROUP.
As immediate:
MOV AX,DGROUP MOV DS,AX
In assume:
ASSUME DS:DGROUP
As an operand prefix:
MOV BX,OFFSET DGROUP:FOO DW FOO DW DGROUP:FOO
Note:
- DW FOO returns the offset of the symbol within its segment.
- DW DGROUP:FOO returns the offset of the symbol within the group.
The next example shows how you can use the GROUP directive to create a .COM file type.
PAGE ,132 TITLE GRPCOM - Use GROUP to create a DOS.COM file ; Use the DOS EXE2BIN utility to convert GRPCOM.EXE to GRPCOM.COM. CG GROUP CSEG,DSEG ; ALL SEGS IN ONE GROUP DISPLAY MACRO TEXT LOCAL MSG DSEG SEGMENT BYTE PUBLIC 'DATA' MSG DB TEXT,13,10,"$" DSEG ENDS ;; Macro produces partly in DSEG, ;; partly in CSEG MOV AH,9 MOV DX,OFFSET CG:MSG ;; Note use of group name ;; in producing offset INT 21H ENDM DSEG SEGMENT BYTE PUBLIC 'DATA' ; Insert local constants and work areas here DSEG ENDS CSEG SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CG, DS:CG, SS:CG, ES:CG ; SET BY LOADER ORG 100H ; Skip to end of the PSP ENTPT PROC NEAR ; COM file entry at 0100H DISPLAY "USING MORE THAN ONE SEGMENT" DISPLAY "YET STILL OBEYING .COM RULES" RET ; Near return to DOS ENTPT ENDP CSEG ENDS END ENTPT
.MODEL (Define Program Memory Segmentation Model)
The .MODEL directive establishes a predefined set of definitions, conventions, and modifications to various default operating behaviors of the assembler. These adjustments are designed to simply certain programming tasks and to allow a more seamless integration with routines written in high level languages.
- Syntax
.MODEL Memory-Model [,Language-Type][,OS-Type][,Stack-Distance]
Memory-Model:
- TINY
- SMALL
- COMPACT
- MEDIUM
- LARGE
- HUGE
- FLAT
Language-Type:
- BASIC
- C
- FORTRAN
- OPTLINK
- PASCAL
- STDCALL
- SYSCALL
OS-Type:
- OS_DOS
- OS_OS2
Stack-Distance:
- FARSTACK
- NEARSTACK
- Remarks
The .MODEL directive should be placed at the top of the file, after any #processor control directives, but before any of the following simplified segmentation directives are encountered:
Each of these directives close any segment that is currently opened, then open a different segment whose name and attributes are determined by the Memory-Model argument.
- Memory-Model
The fundamental purpose of establishing a programming memory model is to define how the program will be organized within the constraints of the segmented processor architecture. It defines whether there are single or multiple default code and data segments, or whether the default code and data segments are merged into a single segment. The operating system upon which the program will run is a determining factor of which memory models can be used. The following table describes these relationships.
Memory Model | Default Code | Default Data | Merged? | Operating Systems |
---|---|---|---|---|
Tiny | Near | Near | Yes | DOS |
Small | Near | Near | No | DOS, 16-Bit OS/2, Win16 |
Medium | Far | Near | No | DOS, 16-Bit OS/2, Win16 |
Compact | Near | Far | No | DOS, 16-Bit OS/2, Win16 |
Large | Far | Far | No | DOS, 16-Bit OS/2, Win16 |
Huge | Far | Far | No | DOS, 16-Bit OS/2, Win16 |
Flat | Near | Near | Yes | 32-Bit OS/2, Win32 |
The assembler creates the default code and data segments, then automatically generates an #ASSUME CS:#@code and an #ASSUME DS:#@data statement to refer to them.
Language-Type
Specifies the default naming convention for all public identifiers written that to the object file, and the method whereby parameters are passed to procedures (the calling convention). See the section on the #PROC directive for a detailed explanation of the effects of the Language-Type setting.
OS-Type
This parameter identifies the target operating system upon which the program will run, and is provided for compatibility with other assemblers. ALP ignores this parameter.
Stack-Distance
The NEARSTACK parameter causes the assembler to assume that the stack segment and the default data segment are the same, and that the DS register is equal to the SS register. This is the default setting. The assembler performs an automatic #ASSUME SS:#@data statement when a near stack is used.
The FARSTACK parameter causes the assembler to assume that the stack is in a different physical segment from that of the default data, and that SS register is not equal to DS. This is typically the case for code in a 16- bit dynamic link library that must use the caller's stack. The assembler performs an automatic #ASSUME SS:STACK when this keyword is used.
ORG (Adjust Segment Location Counter)
The ORG directive sets the location counter to the value of Expression. Subsequent instructions are generated beginning at this new location.
Syntax
ORG Expression
Remarks
The assembler must know all names used in Expression on pass 1, and the value must be either absolute or in the same segment as the location counter.
The numeric value of Expression must not be a quantity larger than that which is representable by an unsigned integer having the same word size as the current segment.
You can use the current address operator ($) to refer to the current value of the location counter.
Example
ORG 120H ORG $+2 ; SKIP NEXT 2 BYTES
To conditionally skip to the next 256-byte boundary:
CSEG SEGMENT PAGE BEGIN = $ . . . IF ($-BEGIN) MOD 256 ; IF NOT ALREADY ON 256 BYTE BOUNDARY ORG ($-BEGIN)+256-(($-BEGIN) MOD 256) ENDIF
SEGMENT (Open a Program Information Segment)
Defines or reopens a segment called Segment-Name which will contain all subsequently emitted code or data.
Syntax
Segment-Name SEGMENT [align][combine][use]['class']
Remarks
A segment definition may be followed by zero or more segment attributes, at most one from each of the following selections:
align Instructs the linker to align the segment at the next align boundary. One of:
- BYTE The next 8-bit boundary.
- DWORD The next 32-bit boundary.
- PAGE The next 256-byte boundary (4096 under 32-bit OS/2).
- PARA The next 16-byte boundary (default).
- WORD The next 16-bit boundary.
combine Controls how the linker will combine this segment with identically-named segments from other modules. One of:
- AT address Locates the segment at the absolute paragraph given by address.
- COMMON Unioned with segments from other modules.
- PRIVATE Will not be combined with other segments (default).
- PUBLIC Concatenated to segments from other modules.
- STACK Concatenated to segments from other modules. At load time:
- SS=beginning of segment
- SS:(E)SP=end of segment
use Word size of the segment.
- USE16 The segment will have a 16-bit word size.
- USE32 The segment will have a 32-bit word size.
'class' Instructs the linker to order segments according to the class name given by class. Segments will not be combined if their class names differ.
.SEQ (Specifies Sequential Segment Ordering)
Orders segments sequentially (the default order).
Syntax
.SEQ
.STACK (Defines Default Stack Segment With Optional Size)
When used with .MODEL, defines a stack segment with the segment name STACK. The optional Size specifies the number of bytes for the stack (default 1024).
Syntax
.STACK [Size]
Remarks
The .STACK directive does not leave the stack segment open when the statement is completed, since it is not a common practice to emit initialized data into the stack segment.
The name of the segment is STACK32 in flat model, and STACK for all other models.
Type Definition
Type definition directives allow the creation of user-defined data types.
This section describes the following type definition directives:
- RECORD
- STRUCT/STRUC
- TYPEDEF
- UNION
RECORD (Define a Record Type Name)
A record is a bit pattern you define to format bytes, words, or dwords for bit-packing. The RecordName becomes a Record-TypeName that can be used create record variables.
- Syntax
RecordName RECORD FieldDeclaration [,[LineBreak] FieldDeclaration...]
Where FieldDeclaration has the following form:
FieldName:Width[=InitialValue]
The optional LineBreak entry allows you to end a FieldDeclaration with a comma, enter an optional EndOfLine-Comment followed by a physical NewLine character, then continue the record definition on the next line.
- Remarks
The RecordName and FieldName entries are unique globally-scoped #Identifiers that must be specified. Upon successful processing of the RECORD definition, the RecordName entry is converted to a Record-TypeName, and all FieldNames are converted to #Record-FieldNames.
Each Width entry in a FieldDeclaration is specified as an Expression which must evaluate to an Absolute-ExpressionType. The cumulative value of all Width entries becomes the total RecordWidth and must not exceed 32, the size of a DWORD, the maximum size for a Record-TypeName. The Operand Size of the record becomes 1 (BYTE) if the RecordWidth is from 1 through 8, 2 (WORD) if the RecordWidth is from 9 through 16, and 4 (DWORD) if the RecordWidth is from 17 through 32. Any other value causes an error. If the total number of bits in the RecordWidth is not evenly divisible by the Operand Size, the assembler right-justifies the fields into the least-significant bit positions of the record.
When a Record-FieldName is referenced in an expression, the value returned is the shift value required to access the field. The #WIDTH operator is used on the Record-FieldName to return the width of the field in bits, and the #MASK operator is used to obtain the value necessary for isolating the field within the record.
The InitialValue entry contains the default value to used for the field when a record variable is allocated. If the field is at least 7 bits wide, you can initialize it to an ASCII character (for example, FIELD:7='Z').
To initialize a record, use the form:
[Identifier] Record-TypeName Expression[,Expression...]
The Identifier entry is an optional name for the first byte, word, or dword of the reserved memory. The Record-TypeName defines the format and default field values, and is the RecordName you assigned to the record in the RECORD directive.
At least one Expression entry must be specified; additional entries are optional. The Expression must resolve to a Compound-ExpressionType, which may also be duplicated by specifying it as a sub-expression of a Duplicated-ExpressionType. Each Compound-ExpressionType represents a single allocated record entry; each explicit sub-expression of the Compound-ExpressionType represents a field value which overrides the default InitialValue for the field given in the record definition.
- Example
Define the record fields; begin with the most significant fields first:
MODULE RECORD R:7, ; First field. ",LineBreak" syntax E:4, ; may be used to split RECORD D:5 ; definition across multiple lines
Fields are 7 bits, 4 bits, and 5 bits; the assembler gives no default value. Most significant byte first, this looks like:
RRRR RRRE EEED DDDD
To reserve its memory:
STG_FLD MODULE <7,,2> ; Initializer is a Compound-ExpressionType
This defines R=7 and D=2 and leaves E unknown; it produces 2 bytes, the least significant byte first:
02 0E
Define the record fields:
AREA RECORD FLA:8='A', FLB:8='B'
To reserve its memory:
CHAR_FLD AREA <,'P'>
This defines FLA='A' (the default) and changes FLB='P'.
To use a field in the record:
DEFFIELD RECORD X:3, Y:4, Z:9 . . . TABLE DEFFIELD 10 DUP (<0,2,255>) . . . MOV DX, TABLE [2] ; Move data from record to register ; other than segment register AND DX, MASK Y ; Mask out fields X and Y ; to remove unwanted fields ; The MASK of Y equals 1E00H ; 00011111000000000B (1E00H) Is the value MOV CL,Y ; Get shift count ; 9 is the value SHR DX,CL ; Field to low-order ; bits of register, DX is now ; equal to the value of field Y MOV CL,WIDTH Y ; Get number of bits ; in field-4 is the value, ; the WIDTH of Y is 4
STRUCT/STRUC (Define a Structure Type Name)
Defines a Structure-TypeName that represents an #aggregate data type containing one or more fields.
Syntax
Structure-Name STRUCT FieldDeclaration . . . Structure-Name ENDS
Where FieldDeclaration has the following form:
[FieldName] Allocation-TypeName InitialValue[,InitialValue...]
Remarks
The obsolete spelling for the STRUCT directive is STRUC.
The syntax for the FieldDeclaration is that of a normal data allocation statement. See the section on #Data Allocation for a full description of this syntax.
The various parts of the FieldDeclaration are described as follows:
FieldName Each FieldName entry is converted to Structure-FieldName when processing of the structure definition is complete. If this field is omitted and the Allocation-TypeName resolves to a Structure-TypeName or Union-TypeName, then all of the fields defined within the imbedded structure or union are promoted to be visible at the same level as other FieldName entries in the current structure given by the Structure-Name.
Allocation-TypeName The allowable values for this field are described in detail in the #Data Allocation section. In modes other than #M510, the assembler accepts imbedded occurrences of other structures or unions by specifying an identifier that resolves to a Structure-TypeName or Union-TypeName in this field.
InitialValue The InitialValue field must be an Expression that resolves to an ExpressionType appropriate for the Allocation-TypeName utilized in the FieldDeclaration. The InitialValue expressions become part of the structure type definition. These values are used as default initializers when an instance of the structure is allocated and no explict override values are specified for a particular field.
Example
Define a Structure-TypeName called Numbers:
Numbers STRUCT One DB 0 Two WORD 0 BYTE 3 Four DWORD ? Numbers ENDS
Allocate a structure variable called Values using the Numbers Structure-TypeName, overriding the One, Two, and Four Structure-FieldName entries with explicit values, and the third (unnamed) entry is initialized with the default InitialValueinherited from the FieldDeclaration:
Values Numbers <1, 2, , 4>
TYPEDEF (Create a User-Defined Type Name)
Defines a Typedef-TypeName that is an alias for another type declaration.
Syntax
TypeName TYPEDEF Type-Declaration
Remarks
The TypeName entry is a unique globally-scoped Identifier that must be specified. Upon successful processing of the TYPEDEF directive, the TypeName entry is converted to a Typedef-TypeName which can then be used in expressions or as a directive in data allocation statements.
The TYPEDEF directive can be used to create a direct alias for another intrinsic type (a Scalar-TypeName, Record-TypeName, Structure-TypeName, Union-TypeName, or other Typedef-TypeName), a pointer to another type, or it can be used to create vector types (arrays).
Examples
The following are examples of TYPEDEF usage:
CHAR typedef byte ; CHAR is an alias for intrinsic type PCHAR typedef ptr CHAR ; PCHAR is a pointer to CHAR BUFFER_T struct pLetter PCHAR ? ; current position in buffer Letters CHAR "ABCDEF",0 ; array of characters BUFFER_T ends BUFFER typedef BUFFER_T ; alias for intrinsic type PBUFFER typedef ptr BUFFER_T ; pointer to the BUFFER type DATA SEGMENT HexChars BUFFER <> ; allocate structure via typedef pHexChars PBUFFER offset HexChars ; point to the allocated structure DATA ENDS
UNION (Define a Union Type Name)
Defines a Union-TypeName that represents an #aggregate data type containing one or more fields. All of the fields occupy the same physical position in storage.
Syntax
Union-Name UNION FieldDeclaration . . . Union-Name ENDS
Where FieldDeclaration has the following form:
[FieldName] Allocation-TypeName InitialValue [, InitialValue ...]
Remarks
This directive is not available in #M510 mode.
The syntax for the FieldDeclaration is that of a normal data allocation statement. See the section on #Data Allocation for a full description of this syntax.
The various parts of the FieldDeclaration are described as follows:
FieldName Each FieldName entry is converted to Union-FieldName when processing of the union definition is complete. If this field is omitted and the Allocation-TypeName resolves to a Structure-TypeName or Union- TypeName, then all of the fields defined within the imbedded structure or union are promoted to be visible at the same level as other FieldName entries in the current union given by the Union-Name.
Allocation-TypeName The allowable values for this field are described in detail in the #Data Allocation section. The assembler accepts imbedded occurrences of other structures or unions by specifying an identifier that resolves to a Structure-TypeName or Union-TypeName in this field.
InitialValue The InitialValue field must be an Expression that resolves to an ExpressionType appropriate for the Allocation-TypeName utilized in the FieldDeclaration. Only the InitialValue expression for the first field becomes part of the union type definition; expressions specified for the remaining fields are ignored. This value is used as the default initializer when an instance of the union is allocated and no explict override value is specified for the field.
Example
.386 IS_sint32 equ -4 IS_sint16 equ -2 IS_sint8 equ -1 NO_TYPE equ 0 IS_uint8 equ 1 IS_uint16 equ 2 IS_uint32 equ 4 TYPE_T typedef SBYTE DATA_T union uint8 BYTE ? sint8 SBYTE ? uint16 WORD ? sint16 SWORD ? uint32 DWORD ? sint32 SDWORD ? DATA_T ends VALUE_T struct DataType TYPE_T NO_TYPE DataValue DATA_T {} VALUE_T ends .data Value VALUE_T {IS_uint8 ,{1}} ; unsigned 8-bit value of 1 .code ;Procedure: IsNegative ; Returns: 1 in EAX if Value. DataValue holds a negative number ; 0 in EAX if Value. DataValue holds a positive number IsNegative proc cmp Value.DataType, NO_TYPE ; check sign of TYPE_T jns short Positive ; if positive, so is value ; check for signed 8-bit integer cmp Value.DataType,IS_sint8 jne short @F ; not 8, check for 16 movsx EAX, Value.DataValue.sint8 ; convert 8 bits to 32 jmp short Check ; and check the value ; check for signed 16-bit integer @@: cmp Value.DataType, ISuuuuuuu_sint16 jne short @F ; not 16, check for 32 movsx EAX, Value.DataValue.sint16 ; convert 16 bits to 32 jmp short Check ; and check the value ; check for signed 32-bit integer @@: cmp Value.DataType, IS_sint32 jne short Positive ; unknown, assume positive mov EAX, Value.DataValue.sint32 ; get full 32 bit number Check: or EAX, EAX ; check for negative value jns short Positive ; no sign bit, positive mov EAX,1 ; indicate negative ret ; and return Positive: mov EAX,0 ; indicate positive ret ; and return IsNegative endp end
Miscellaneous
This section describes the following miscellaneous directives:
- =
- .ABORT
- ASSUME
- EQU
- LABEL
- OPTION
- .RADIX
= (Assign an Expression to an Assembler Variable)
The = directive lets you create a symbolic assembler-time variable. Numeric expressions may be assigned to the variable as many times as necessary.
Syntax
Name = Expression
Remarks
The = directive is similar to the #EQU assembler directive except you can redefine Name without causing an error condition. However, the = directive is more restrictive about the allowable #ExpressionTypes that can be utilized in the Expression field, and it cannot be used to create #Text-EquateNames.
Name is a globally-scoped Identifier. The Expression entry must evaluate to an Operand-ExpressionType. If an evaluation error occurs, or if the Expression references an external identifier, or if the Expression evaluates to one of the following #Operand-ExpressionTypes:
- Indexed-ExpressionType
- Register-ExpressionType
- Floating-Point-ExpressionType
- Compound-ExpressionType
- Duplicated-ExpressionType
then an error message is issued and the assignment does not take place. Otherwise, the Identifier is converted to a Numeric-EquateName.
See also the #EQU assembler directive and the EQU preprocessor directive.
Example
EMP = 6 ; Establish as redefineable numeric equate EMP EQU 6 ; OK, value is the same, EMP remains redefinable EMP EQU 7 ; Error, can't change value with EQU EMP = 7 ; OK, EMP is redefineable with = EMP = EMP + 1 ; Can refer to its previous definition
Note: The #Expression-Attributes inherited from the Expression during an assignment are not retained in subsequent assignments. For example:
VECTOR = WORD PTR 4 ; Type-Declaration attribute MOV [BX], VECTOR ; Store the 4 as a word VECTOR = 6 ; Type-Declaration attribute discarded MOV [BX], VECTOR ; Error, no size for operands
.ABORT (Terminate the Assembly)
Terminates the assembly at the point where the .ABORT directive is encountered. The remainder of the input stream is not read.
Syntax
.ABORT
Remarks
The .ABORT directive is only available in #ALP mode.
ASSUME (Inform Assembler of Register Contents)
The ASSUME directive establishes an assembly-time association between a machine register and a program object or data type. By informing the assembler of the type of information to which a register points, certain programming tasks can be simplified and the assembler can perform some operations automatically.
Syntax
ASSUME Association [,Association ...]
Association:
- Segment-Register-Assocation
- General-Purpose-Register-Assocation
- NOTHING
Segment-Register-Association:
- Segment-Register: Expression
- Segment-Register: NOTHING
General-Purpose-Register-Association:
- General-Purpose-Register: Type-Declaration
- General-Purpose-Register: NOTHING
Remarks
If the NOTHING keyword is specified for the Association field, all register associations are cancelled.
If the NOTHING keyword is specified for a particular Segment-Register or General-Purpose-Register, only the association for that register is cancelled.
The following sections describe the two types of register associations:
- Segment-Register-Association
- General-Purpose-Register-Association
Segment Register Association
A Segment-Register-Association establishes an assembly-time association between a Segment-Register and an expression that resolves to a GroupName or SegmentName. It allows the programmer to describe for the assembler what values are held in the segment registers at program run-time.
When the user program executes, all instructions that access memory do so through a particular segment register. To generate the correct encoding for an instruction that accesses a memory location, the assembler must know which segment register will be used in the effective memory address. In general, accessing a memory location from within a user program is done by referencing a named variable defined within a particular named segment.
Before accessing a named program variable (in a named memory segment), it is the programmer's responsibility to insure that the desired segment register actually references the correct physical segment at program run-time. Unless the ASSUME directive is used to describe this association, the assembler has no way of knowing which segment register (if any) is addressing a named segment when a reference to a named variable contained therein is encountered. In this situation, the programmer is forced to use the #Segment Override (: Operator) in every instruction to "reach" the desired variable and cause the assembler to generate the proper instruction encoding. The association established by the ASSUME directive allows the assembler to take over the task of verifying memory references and generating the appropriate instructions.
If you temporarily use a segment register to contain a value other than the segment or group identified in the ASSUME association, then you should reflect the change with a new ASSUME statement, or cancel the association with an ASSUME xS:NOTHING construct.
When the contents of a segment register are used for addressability, the register value should never contradict the association established for that register.
When the #Reference directive is utilized and the program is designed to follow the conventions that it establishes, the ASSUME directive is no longer needed in most cases.
Example
Data SEGMENT Stuff WORD 0 Data ENDS Code SEGMENT ASSUME NOTHING ; Cancel all register assumptions mov ax, Data ; Load general-purpose register with segment frame, mov DS, ax ; then establish addressablity through DS mov ES, ax ; and ES. The assembler doesn't "know" this yet mov Stuff, 1 ; Error, can't reach Stuff ASSUME ES:Data ; Associate ES register with Data segment add Stuff, bx ; Now we can reach Stuff, but the assembler needs ; to generate an ES override instruction byte ASSUME DS:SEG Stuff ; Expression to extract the segment value of Stuff ; This has the same effect as ASSUME DS:Data ; Now both DS and ES are associated with Data add Stuff, cx ; This time, the instruction doesn't need an ; override byte because DS is the default ; register for normal accesses to memory ASSUME DS:NOTHING ; Cancel the association between DS and Data add Stuff, dx ; Once again, the ES override is generated add DS:Stuff, dx ; Must use "force" if we want the default encoding Code ends end
Warning:
If an ASSUME CS:Expression is placed before the code segment it is referencing, the assembler will ignore the ASSUME. The ASSUME CS:Expression statement must follow the SEGMENT definition statement of the code segment it is referencing.
The ASSUME statement for the CS register should be placed immediately following the code #SEGMENT statement, before any labels are defined in that code segment.
General-Purpose Register Association
A General-Purpose-Register-Association establishes an assembly-time association between a General-Purpose-Register and a Type-Declaration. It allows the programmer to describe for the assembler what type of data is being held in the general purpose register at program run-time.
This feature can be very useful when the programmer is treating a general- purpose register as a "pointer" to a particular type of storage. If this " pointer" is being utilized many times in the program, (perhaps changing in value but never in the type of data to which it points), the ASSUME directive can be used to associate the register with the type of data to which it points. This frees the programmer from having to use an explicit #Type Conversion (PTR Operator) every time the register is used to access memory.
A register may only be associated with a data type whose operand size matches that of the register. For instance, the following construct is illegal:
ASSUME EBX:BYTE ; Error, EBX is a DWORD register
The most useful situation is for the register to contain a pointer to another data type. In this situation, the [[Indirection ([] Operator)]] may be used store or retrieve data through the register without the need for an explicit conversion operation:
ASSUME EDI:NOTHING ; This is the assembler default setting MOV [EDI],1 ; What is the size supposed to be? MOV byte ptr [EDI],1 ; Fixes the problem, but this can get tiring ASSUME EDI:PTR BYTE ; EDI is now a pointer to a byte MOV [EDI],1 ; assembler knows what to do with this now INC [EDI] ; and this too
The following constructs are legal but not particularly useful since they simply restate what is already known about the registers (the operand size), and the assembler doesn't enforce a strict level of type checking against register operands:
ASSUME ECX:SDWORD ; Signed double-word matches size of ECX ASSUME EBX:DWORD ; Unsigned double-word matches size of EBX MOV ECX, 0FFFFEEEEh ; Register type-checking is not strict MOV EBX, -1 ; enough to flag these as errors
In fact, any data type that matches the size of the register may be used; the assembler checks the sizes and reports mismatches, but effectively ignores any settings that are not pointers to other types. Consider the following example:
STRUCT_T STRUCT One BYTE 1 Two BYTE 2 Three BYTE 3 Four BYTE 4 STRUCT_T ENDS ASSUME EBX:STRUCT_T ; Ok, STRUCT_T is 4 bytes in size MOV EBX, -1 ; Legal, but not very meaningful ... ; A more useful situation (given that EBX is now holding data of type ; STRUCT_T) would be for the assembler to allow the following notation: MOV EBX,{ 4 , 3 , 2 , 1 } ; Hypothetical (UNSUPPORTED!) syntax... ; It would also be nice at this point if the symbolic debugger could ; show us the value of EBX in the appropriate format, but the assembler ; does not support the emitting of context-sensitive symbolic debugging ; information.
EQU (Assign an Expression to a Symbolic Constant)
The EQU directive assigns the value of Expression to Name.
- Syntax
Name EQU Expression
- Remarks
If Name has already been defined as a Numeric-EquateName and its currently assigned value differs from the value given by Expression, an error message is produced. Unlike symbols created with the = (equal sign) directive, symbols created with the EQU directive cannot be redefined with different values.
The Expression entry must evaluate to an Operand-ExpressionType. If an evaluation error occurs or if the Expression evaluates to one of the following Operand-ExpressionTypes:
- Indexed-ExpressionType
- Floating-Point-ExpressionType
- Compound-ExpressionType
- Duplicated-ExpressionType
then the Identifier is converted to a Text-EquateName. Otherwise, the Identifier is converted to a Numeric-EquateName.
Example
A EQU <BP+> ; explicit text literal, A is a text equate B EQU BP+ ; invalid expression - text equate equivalent to A B EQU 1+2 ; valid expression - but still a text equate <1+2> C EQU 1+2 ; converted to assembler symbolic constant, value=3 C EQU <3> ; illegal, cannot convert back to text equate
LABEL (Associate a Symbolic Name With Current Address)
The LABEL directive defines the following attributes of Name:
- Segment: current segment being assembled
- Offset: current position within this segment
- Type: the operand of the LABEL directive
Syntax
Name LABEL Type-Declaration or Name: or Name::
Remarks
The LABEL directive provides a method of labeling a memory location and assigning it a type without allocating any storage. It can be used to create multiple labels of differing types that are aliases for the same memory location.
The Name entry is an Identifier that is converted to a LabelName according to the value given by Type-Declaration. See the section on #label names for more information on the details of this conversion.
The : and :: forms of this directive are used for defining code labels. In this case, the Nameentry is converted to a Target-LabelName. The double-colon form of the directive is used when the Name must be visible outside of the procedure block in which it is defined.
Example
To refer to a data area but use a length different from the original definition of that area:
BARRAY LABEL BYTE ARRAY DW 100 DUP (0) . . . ADD AL, BARRAY [99] ; ADD 100th BYTE TO AL ADD AX, ARRAY [98] ; ADD 50th WORD TO AX
To define multiple entry points within a procedure:
SUBRT PROC FAR . . . SUB2 LABEL FAR ; Should have same attribute as containing PROC . . . RET SUBRT ENDP
OPTION (Modify Default Behaviors)
The OPTION directive allows the user to alter certain default behaviors of the assembler, normally to provide backward compatibility with older assemblers. The OPTION directive is not available when assembling in #M510 mode.
Syntax
OPTION Option-Item [,[LineBreak] Option-Item ...]
Remarks
The Option-Item arguments are defined as follows (the underlined keywords denote the default values):
DOTNAME| NODOTNAME Allows user identifiers to begin with an introductory dot (.) character.
EXPR16| EXPR32 Specifies whether expressions are evaluated using 16-bit or 32-bit arithmetic. Some programs may require reverting back to EXPR16 in order to assemble without problems. Once this value has been set it cannot be changed. The use of a processor selection directive to select a 32-bit processor is equivalent to selecting OPTION EXPR32, which prevents any further attempt to select OPTION EXPR16.
LANGUAGE:Language-Name Specifies the default language type for identifiers with PUBLIC or EXPORT visibility. This option overrides any setting given in the #.MODEL directive.
OFFSET:Offset-Type Determines how relocatable offset values are written to the object file output, encoded in the form of a linker "fixup" record. The possible values for Offset-Type are SEGMENT, GROUP, and FLAT.
OLDSTRUCTS| NOOLDSTRUCTS The OLDSTRUCTS keyword causes structure field names to become global identifiers rather than local names private to the structure type. It also prevents the #Structure/Union Field Selection (. Operator) from performing strict checking on its operands, requiring its left operand to have a structure type and its right operand to be the name of a field contained therein.
PROC:Visibility Specifies the default visibility for procedure names. This can be one of PRIVATE, PUBLIC, or EXPORT.
SCOPED| NOSCOPEDThe NOSCOPED keyword forces all code label names defined within procedures to be visible to the entire module and not just from within the defining procedure.
SEGMENT:Address-Size Explicitly sets the default address size value. This is used to control the address size of segments that are opened without explicit USE16 or USE32 keywords, and of global identifiers that are declared outside of segment boundaries. The possible values for Address-Size are USE16, USE32, and FLAT.
.RADIX (Set the Default Base for Numeric Literals)
The .RADIX directive lets you change the default RADIX (decimal) to base 2, 8, 10, or 16.
Syntax
.RADIX Expression
Remarks
The Expression entry is in decimal radix regardless of the current radix setting.
The .RADIX directive does not affect real numbers initialized as variables with DD, DQ, or DT.
When using .RADIX 16, be aware that if the hex constant ends in either B or D, the assembler thinks that the B or D is a request to cancel the current radix specification with a base of binary or decimal, respectively. In such cases, add the H base override (just as if .RADIX 16 were not in use).
Example
The statement:
.RADIX 16 DW 120B
produces an error because 2 is not a valid binary number. The correct specification is:
DW 120BH
The following example:
.RADIX 16 DW 89CD
also produces an error because C is not a valid decimal number. The correct specification is:
DW 89CDH
The dangerous case is when no error is produced. For example:
.RADIX 16 DW 120D
produces a constant whose value is 120 decimal, not '120D' hex, which might have been the intended value.
The following two move instructions are the same:
MOV BX, OFFH .RADIX 16 MOV BX , OFF
The following example:
.RADIX 8 DQ 19.0 ; Treated as decimal
produces a constant whose value is 19 decimal because 19.0 is a real number. However, if you leave off the decimal point, the following:
.RADIX 8 DQ 19 ; uses current radix
produces a syntax error because nine is not a valid number in .RADIX 8.