< First steps towards system programming under MS-DOS 7

Chapter 7 Debugger's assembler commands

Are commands of archaic DEBUG.EXE worth the time getting acquainted with? This question invokes recollections. Over half century ago, computers implemented time sharing for several terminals. Each terminal was used to launch a separate program, and computer's memory had to be distributed between these programs. For claiming memory requirements program's file headers were introduced. Since then those assemblers, which couldn't automatically compile file's headers, have been regarded obsolete. Just that attitude has been inherited by DEBUG.EXE.

More sophisticated assemblers automatically compile headers and jump target addresses. This great convenience, however, has its back side : freedom of manipulation with address space and with segment registers becomes lost. Applications don't suffer because of that, but for research and system tasks it may become a sufficient obstacle. Just because of this reason the MASM assembler wouldn't compile program examples shown in parts 9.06, 9.08, 9.10 of this book. Similar unacceptable fragments were found in BIOS codes and even in loader modules of Windows XP operating system. Codes that can't pass through MASM, can be compiled by DEBUG.EXE.

For firsthand acquaintance with programming the most simple assembler tool should be preferred. Besides simplicity, DEBUG.EXE has a number of advantages, which make it the best suitable choice for this book :

  • first, it gives the most clear notion of machine codes usage ;
  • second, it combines assembling and debugging capabilities ;
  • third, it provides access to hardware and to OS structures ;
  • fourth, it is able to interact with the user.

Though DEBUG.EXE can send to CPU for execution just any machine code, variety of all x86 machine commands is too vast and may bewilder a newbie. Therefore the 7th chapter presents an easily comprehensible selection : command set of that DEBUG.EXE version, which is supplied within Windows 95/98 release. These commands constitute a subset of all modern CPU's commands, but this subset plays a very important role. It includes the most widely used commands. About 96% of the whole currently active computer park "understands" these commands. Compatibility of machine codes for the majority of modern computers is based on just that subset, which is presented here.

More recent commands in modern CPU's may differ. Nevertheless a number of such commands is widely acknowledged and is necessary for tuning modern computers. As exception, the 7th chapter includes descriptions of several commands, which are not "known" to DEBUG.EXE. Descriptions elucidate the ways to employ these commands in a form of machine codes. In any case this wouldn't be an obstacle for debugging programs with these codes.

DEBUG.EXE accepts assembler commands, when it is switched to translation of assembler language into executable machine codes (6.05-02). Though each dialect of assembler language has its peculiarities, general composition of assembler commands is common. A line begins with command name, followed by operand(s). Operands in some commands must be preceded by a marker of operand's type. If an assembler command contains several operands, these are separated by a comma. Commands that produce any numeric result overwrite their first (leftmost) operand with this result. Former contents of this register (or memory cell) become lost. Examples of command files with switching DEBUG.EXE to assembler language translation and with a lot of assembler commands are presented in articles 9.05, 9.06, 9.08, 9.10.

As far as alternative command's specifications are too numerous, some variables and operands in chapter 7 are given easily recognizable fixed lower case designators, just the same in all examples. Allowable substitutions for these designators together with some other terms, used in DEBUG's assembler dialect, are shown and explained in the table below.

Table 7.00
DesignatorsExplanations and allowed alternatives
blany of 8-bit registers: AL, CL, DL, BL, AH, CH, DH, BH.
bxany of 16-bit registers: AX, CX, DX, BX, SP, BP, SI, DI.
ssany of segment registers: CS, DS, ES, SS.
STcoprocessor's top stack register ST(0) [Note 1]
ST(0-7)any coprocessor's stack register from ST(0) to ST(7)
farmarker of a 4-byte address (segment + offset).
byteptr marker of a one-byte operand (ptr = "pointer")
wordptr marker of a 2-bytes operand (a word)
dwordptr marker of a 4-bytes operand (a double word)
qwordptr marker of a 8-bytes operand (a quadruple word)
tbyteptr marker of a 10-bytes operand
fany single hexadecimal digit from "0" to "F"
±7fany signed hexadecimal number from –7Fh to +7Fh
ffany hexadecimal number from 00h to FFh
ffffany hexadecimal number from 0000h to FFFFh
aaaaaddress for "short" jumps [Note 2]
[bp+si+ffff]expression enclosed in square brackets means addressing to operand in memory. Offset of the corresponding memory cell(s) is to be found by evaluating the given expression. Two groups of expression forms are allowed.[Note 3][Note 4]
Notes
  1. ^ Coprocessor's top stack register is normally named ST(0), but in several positions, where no other register can be addressed instead, DEBUG.EXE accepts abridged name ST.
  2. ^ Offset is specified as "aaaa" for control transfer commands with one address byte. This offset must be within ±7Fh vicinity from the next machine command. If vicinity condition is violated, DEBUG.EXE responds with error message.
  3. ^ a b c Expressions of the first group produce offset, alluding by default to segment address in DS. These expressions are either
    just an offset specification :[ffff], or
    a single register reference :[BX], [DI], [SI], or
    a sum of 2 registers :[BX+DI], [BX+SI], or
    a sum with displacement :[BX±7f], [BX+ffff], [BX+DI±7f], [BX+DI+ffff], [BX+SI±7f], [BX+SI+ffff], [DI±7f], [DI+ffff], [SI±7f], [SI+ffff].
  4. ^ a b c Expressions of the second group include a reference to BP register and produce offset, alluding by default to segment address in SS : [BP+DI], [BP+SI], [BP±7f], [BP+ffff], [BP+DI±7f], [BP+DI+ffff], [BP+SI±7f], [BP+SI+ffff].
  5. Register names in expressions may be separated by minus sign, but this doesn't affect the result: sum is counted in any case.
  6. The default segment register may be changed by explicit specification of a prefix byte (7.02-01).
  7. When operand's type marker ("byte ptr", "word ptr"...) isn't present in assembler command, DEBUG.EXE determines type of operand according to the register, containing the other operand: 16-bit register (AX, BX...) sets word type, 8-bit register (AL,AH,BL...) sets byte type. If a command doesn't address to a register, then operand's type marker becomes a required item. In any case these markers may be truncated to 2 characters: "by", "wo", etc.

7.01 Control instructions

Having been switched to translation of assembler commands, DEBUG.EXE accepts commands and also control instructions. The latter are not translated into machine code, but rather affect the process of translation and may play a very important role.

7.01-01 DB – data bytes insertion instruction

The DB (= Data Byte) instruction informs DEBUG.EXE that the following string of bytes must be treated not as an assembler command, but rather as separate bytes of data, which should to be written into assembled code "as they are". Each byte is represented by two hexadecimal digits without the trailing "h". String of bytes may include group(s) of ASCII characters, enclosed in quotes or in double quotes. ASCII characters (except the enclosing quotes) are translated into hexadecimal code byte-by-byte. Comments after the DB instruction are not allowed. Here is a DB instruction usage example :

DB 71 6C 65 'data array'
Notes
  1. While unassembling machine codes there may be encountered byte(s), which can't be identified as machine commands, "known" to DEBUG.EXE. Then "DB" is displayed as a prefix to each such byte.

7.01-02 DW – data words insertion instruction

The DW (= Data Word) instruction informs DEBUG.EXE that the following string must be treated not as an assembler command, but rather as separate words of data, which should be written into assembled code "as they are". Each word consists of up to 4 hexadecimal digits. If there are less than 4 digits in a word, it will be automatically supplemented to 4 digits with higher order zeros. In assembled machine code the least significant 2 digits of each word constitute the first byte, and the most significant 2 digits constitute the following byte. String of words after the "DW" instruction may include group(s) of ASCII characters, enclosed in quotes or in double quotes. These characters (except the enclosing quotes) are translated into hexadecimal representation one byte per character, just as after the "DB" instruction (7.01-01). Comments after the DW instruction are not allowed. Here is a DW instruction usage example :

DW 71A0 F01 06D5 "other key" 0FFF

7.01-03 ORG – target address change instruction

The ORG instruction informs DEBUG.EXE that machine codes of the assembled commands, from the next one and on, must be written into other place, starting from that address, which is specified after the ORG instruction. Processor's registers are not affected by ORG instruction. In course of interactive debugger's sessions the ORG instruction enables to navigate along the assembled code in order to correct errors and those references forward, which couldn't be specified in advance.

In debugger's trial command files the ORG instruction is used to fix positions of restart points and of jump target points (example in 9.02-02). Reserved free space, preceding positions of fixed points, helps to avoid tedious address recalculations, which otherwise would be necessary each time you have to add or delete bytes of code in any previous part of command file. Comments after the ORG instruction are allowed. Usage examples are shown in the following table :

Examples Performed action
ORG ffff:ffffSet explicitly both segment address and offset
ORG fffLeave segment address intact, set offset 0fffh
ORG ss:ffffRefer to segment register "ss:", set offset ffffh
ORG ss:Refer to segment register "ss:", offset 0000h is implied

7.01-04 Instruction "Empty line"

Empty line, containing no commands, no instructions, no data and no comments, is itself regarded by DEBUG.EXE as an instruction, forcing to return from translation of assembler commands, described in chapter 7, to normal control with those commands, described in articles 6.05-02 – 6.05-23. In order to input this instruction from keyboard you have to leave the last presented line empty and just press ENTER. When assembler commands are received from a command file via input redirection, return to normal control is induced by the first encountered empty line in this command file. Therefore a care should be taken about absence of empty lines inside any block of assembler commands in command file(s), and equally about presence of an empty line, marking the end of each block of assembler commands.

7.01-05 Semicolon – comments insertion instruction

Being encountered in any position in a line with assembler command, semicolon ( ; ) is interpreted by DEBUG.EXE as instruction to proceed to the next assembler command line at once, skipping translation into machine codes of the semicolon itself and of all following characters in the rest part of current command line. This mission of semicolon is used in order to append comments to assembler commands.

Notes
  1. A line beginning with semicolon is not regarded empty (7.01-04) and doesn't force DEBUG.EXE to cease translation of assembler commands into machine code. This gives an opportunity to insert headers and multi-line commentaries.
  2. DEBUG's message " ^ Error" pointing upwards just towards semicolon in the previous line means that an error is present in preceding part of this line. Most probably command line is considered not complete because of absence of some required parameter.
  3. Semicolon can't be used to append commentaries to those assembler command lines, which begin with control instruction DB or DW, and also when DEBUG.EXE is not switched to translation of assembler commands.

7.02 Prefixes

In machine code of x86 platform CPU's several particular bytes are given a special prefix status. Each such byte is not a separate machine command. But a prefix byte, being encountered preceding a machine command, forces CPU to change the manner of its interpretation or execution. Effect of a prefix byte isn't spread beyond the nearest following command.

DEBUG.EXE allows to specify a prefix byte in a separate line before that assembler command, which is to be affected by prefix, for example :

CS:
ADD byte ptr [BX],0F

Specification of a prefix byte just before the affected command in the same line is equally allowable :

CS: ADD byte ptr [BX],0F

Machine command's code may be preceded by up to four prefixes, if their effects don't contradict to each other. All prefixes in one command must be different, duplicate prefixes are not allowed.

7.02-01 Segment override prefixes

Most assembler commands don't include explicit segment address specification. Absolute memory addresses (see note 2 to 6.05-01) for such commands are calculated by CPU on the basis of default segment register assignment (see notes [3] and [4] of table 7.00). Segment override prefixes force CPU to read segment address from another segment register instead of the default one for the nearest following command. Naturally, segment override prefixes can be applied to those commands only, which appeal to memory and therefore imply calculation of absolute address.

DEBUG.EXE "knows" four segment override prefixes, inheriting names of corresponding segment registers (see second column of the table below). Examples of CS: prefix usage have been shown in article 7.02. Numerous other similar prefix usage examples can be found in assembler texts, presented in articles 9.06 and 9.08.

Modern CPUs have not four, but six segment registers. Segment override prefixes for auxiliary segment registers FS and GS are not "known" to DEBUG.EXE, but it allows to specify these prefixes as data by means of DB instruction (7.01-01) and doesn't hinder to proper interpretation of these prefixes by CPU. Of course, for proper interpretation of these prefixes the 80386 or newer CPU is required.

CodeExampleComments
2ECS:
3EDS:
26ES:
36SS:
64DB 64relative to FS: segment register
65DB 65relative to GS: segment register
Notes
  1. Segment override prefixes can't affect default assignment of ES: register, in particular, for string commands CMPSB, CMPSW, INSB, INSW, MOVSB, MOVSB, SCASB, SCASW, STOSB, STOSW.

7.02-02 LOCK – system bus lock prefix

Prefix LOCK corresponds to prefix byte F0h, which induces CPU to send "Bus busy" signal and to keep it active until execution of the following command terminates. This is necessary in computers with several processors in order to prevent uncoordinated access to shared memory resources. For ordinary computers with a single processor prefix LOCK is not needed.

The LOCK prefix can be specified for writing into memory with commands ADC, ADD, AND, DEC, INC, NEG, NOT, OR, SBB, SUB, XCHG, XOR. But when the same commands perform reading only or operate with registers, then LOCK prefix shouldn't be specified. In such cases CPU responds to byte F0h with exception 06h, inducing a call for interrupt INT 06 handler (8.01-07).

CodeExample
F0LOCK
Notes
  1. Intel's CPUs don't allow combinations of LOCK prefix with any of repetition prefixes (7.02-03, 7.02-04) in one command.
  2. Modern processors, having more than 8 control registers, allow prefix byte F0h for access to control registers with MOV command.[Note 1 to 7.03-58] In this case F0h byte is interpreted not as LOCK prefix, but as a prefix for access to registers CR8–CR15.

7.02-03 Repetition prefix REPNZ

REPNZ stands for "REPeat while Not Zero". The REPNZ prefix induces cyclic execution of the nearest following command. Repetition cycle terminates when at least one of two conditions is met :

  • the executed command finds equal operands and therefore sets zero flag (ZF) into ZR state (6.05-15).
  • a number in CX register becomes zero because the prescribed number of repetitions in CX register has exhausted.

The prefix REPNE, standing for "REPeat while Not Equal", is accepted by DEBUG.EXE as equivalent to REPNZ, since both correspond to the same prefix byte F2h, which forces the CPU to perform cyclically the following operations :

  1. check the CX==0 condition. If met, terminate the cycle. If not, decrement by a unit the number in CX register.
  2. reset zero flag into NZ state.
  3. perform that command, which is preceded by repetition prefix.
  4. check whether the zero flag is set into ZR state. If so, terminate the cycle ; if not, repeat from the beginning CX==0 check.

As long as both described conditions are not met, the CPU continues the cycle. As soon as at least one condition is met, CPU leaves the cycle and proceeds to the next command, following the one executed within the cycle.

Repetition prefixes are used with string commands, which automatically increment or decrement contents of index register (SI, or DI, or both) at each iteration. Hence the actual address of their operand(s) is shifted from one iteration to the other. String commands CMPSB, CMPSW, SCASB, SCASW affect not only the index, but also the state of ZF flag. Therefore repetition prefixes with these commands enable to analyze string(s) of bytes or words. When repetition prefix is used with commands, not affecting the ZF flag (INSB, INSW, MOVSB, MOVSW, OUTSB, OUTSW, STOSB, STOSW), then these commands will be just repeated that number of times, which is preset in CX register.

CodeExample
F2REPNE
F2REPNZ
Notes
  1. When a command is preceded by several prefixes, including a repetition prefix, then archaic processors (more obsolete, than 80386) sometimes can't resume execution of the repetition cycle after interrupts. If such combination of prefixes can't be avoided, one has either to recheck explicitly the conditions of cycle's termination or else inhibit interrupts with CLI command (7.03-12) for the time of cycle execution.
  2. Repetition prefixes shouldn't be applied to non-string commands, because it results in those byte combinations, which may be interpreted by modern processors as operation code extensions (7.02-08), in particular, denoting the SSE commands.
  3. When repetition prefix is used in combination with operand size override prefix (7.02-06), the prescribed number of repetitions is read not from 16-bit CX register, but from 32-bit ECX register. For such cases it's important to remind about preparation of a proper value in upper part (bits 31–16) of ECX register.

7.02-04 Repetition prefix REPZ

REPZ stands for "REPeat while Zero". The REPZ prefix causes iterative execution of the command it precedes. Repetition cycle terminates, when at least one of the following two conditions is met :

  • the executed command finds different operands and therefore resets zero flag (ZF) into NZ state (6.05-15).
  • a number in CX register becomes zero because the prescribed number of repetitions in CX register has exhausted.

Prefix names REP (=REPeat), REPE (= REPeat while Equal) and REPZ are regarded by DEBUG.EXE as equivalent: either of them corresponds to the same prefix byte F3h. Having encountered byte F3h, processor performs the same sequence of operations as for prefix REPNZ (7.02-03), except that initial state of ZF flag is reversed to ZR, and the target state of ZF flag is the opposite (NZ). All other peculiarities of command's execution with REPNZ prefix, described in article 7.02-03 and in the following notes, are equally inherent to execution of commands with prefix REPZ.

CodeExample
F3REP
F3REPE
F3REPZ
Notes
  1. Prefix REPZ is often used with commands CMPSB and CMPSW for comparison between two strings of characters – names, paths, signatures. When comparison cycle terminates, the result is expressed by state of ZR flag: the set state (ZR) proves identity, the reset state (NZ) is evidence of the difference.

7.02-05 Synchronizing prefixes WAIT and FWAIT

Prefix names WAIT and FWAIT correspond to the same prefix byte 9Bh. It is used with commands, which imply code transfer between CPU and asynchronous coprocessor. Byte 9Bh forces CPU to wait for arrival of readiness confirmation signal from coprocessor to CPU's pin "BUSY". In particular, prefix byte 9Bh should precede to ESC commands (7.03-22) and to coprocessor's commands (7.04), if machine code is to be executed by a CPU without internal arithmetic coprocessor.

For modern CPUs, comprising an integrated arithmetic coprocessor with hardware synchronization means, former mission of prefix byte 9Bh is not needed. Byte 9Bh is ignored, if bit 01h "coprocessor synchronization" in control register CR0 (A.11-4) is reset to zero. By default bit 01h is set, and then byte 9Bh may induce a call for INT 07 handler, if at the same time task switch flag (bit 03h in CR0) is set too. Task switching is conjoined with necessity to process exceptions, registered by coprocessor. The INT 07 handler can be charged with this mission, and then its fulfillment will be ensured by pertinent usage of the WAIT prefix.

CodeExample
9BFWAIT
9BWAIT

7.02-06 Operand's size override prefix

In real mode modern CPUs by default emulate 16-bit operations of obsolete processor 8086. But in fact modern CPUs have 32-bit general purpose registers. Sometimes it is desirable to get access to the whole 32-bit register, while CPU is still kept in real mode. This can be achieved with operand size override prefix byte 66h, which is properly "understood" by all 32-bit CPUs, belonging to x86 platform.

As far as DEBUG.EXE doesn't "know" operand size override prefix, it should be introduced by DB instruction (7.01-01), for example :

DB 66
SHR AX,CL

In the shown example presence of prefix byte 66h modifies action of SHR command (7.03-83) so that it will affect the whole 32-bit register EAX. In particular, if the number of shifts, preset in CL register, is 10h (= 16 decimal), then contents of bits 31–16 in EAX will be shifted into bits 15–0 and will become accessible in AX as an ordinary 16-bit operand.

Being preceded by prefix byte 66h, stack commands PUSH, PUSHF, POP, POPF operate with four bytes at once. Bytes are pushed into stack in descending significance order: from the most significant to the least. Contents of bits 31–16 in EAX register may be read via stack in the following way :

DB 66
PUSH AX
POP BX
POP BX

In this example prefix byte 66h forces to push into stack the whole contents of 32-bit register EAX. Then first POP command moves into BX register two least significant bytes of EAX, but these are available just from AX and therefore are not needed. The next POP command overwrites former data in BX register with the required most significant bytes of EAX contents.

The CMP command (7.03-14) with operand size override prefix compares four-byte operands, including those stored in 32-bit registers. For commands with operand size override prefix the operands stored in memory as well as operands, specified directly in executable code, also must be of the DWORD type, i.e. 4 bytes long. Unfortunately, DEBUG.EXE can't assemble machine commands for CPU with attached operands of DWORD type. If necessary, extra data bytes may be appended by DB instruction (7.01-01).

Notes
  1. Programs using operand size override prefix can't be executed by 16-bit CPUs.
  2. Operand size override prefix can't be specified before commands with one-byte operand(s), including those in one-byte registers (AH, AL, BH, etc.), and also before one-byte string commands (CMPSB, INSB, LODSB, MOVSB, OUTSB, SCASB, STOSB). These combinations of codes may be interpreted by modern processors as SSE commands.
  3. Operand size override prefix can't be specified before commands with operands in segment registers, since these are 16-bit registers in both 16-bit and 32-bit processors. However, this restriction doesn't relate to commands, which read segment address for access to a particular memory cell.
  4. When a program is tested with commands "Proceed" (6.05-14) or "Trace" (6.05-17), then DEBUG.EXE doesn't show as the next machine command that one, which follows prefix byte 66h. Nevertheless 32-bit CPUs always accept prefix byte 66h together with the following machine command and execute both at one step.
  5. Operand size override prefix always forces the non-default size of operands. When bit 6 in byte 06h of code segment descriptor[Note 5 to A.12-2] specifies default 32-bit size of operands, then prefix byte 66h forces 16-bit operand's size. Here and further in this book default 16-bit operand's size is implied, just as it is automatically set by CPU's "shadow" registers, when CPU begins to work in real mode after being switched on.

7.02-07 Address size override prefix

For testing memory and for several other tasks it is necessary to get access to the whole address space without those restrictions, which are inherent to 32-bit addressing in protected mode. In real mode access to the whole address space is possible, but it needs the utmost 4-Gb segment size to be set (example in article 9.10-01) and, besides that, non-default 32-bit addressing to be allowed. Address size override prefix byte 67h allows non-default 32-bit addressing to a single nearest following command.

Prefix byte 67h is not "known" to DEBUG.EXE. Therefore it has to be introduced as data by DB instruction (7.01-01). When a command is preceded by several prefixes, then prefix byte 67h is specified before operand size override prefix (7.02-06), but after segment override prefix (7.02-01). Naturally, there is no sense in combination of prefix byte 67h with those commands, which have no deal with memory cells.

Prefix byte 67h affects code length of many machine commands and changes interpretation of all indirection expressions, listed in notes [3] and [4] of table 7.00. Only commands with implicit indirection remain unchanged : CMPSB, CMPSW, LODSB, LODSW, MOVSB, MOVSW, SCASB, SCASW, STOSB, STOSW. For assembling all other commands with prefix byte 67h capabilities of DEBUG.EXE are insufficient.

The table below shows original interpretation of indirection expressions (in the left column) in comparison with interpretation, affected by address size override prefix byte 67h (in the right column). The table lists only those interpretations, which correspond to machine commands of the same length, performing the same operation(s). Hence the shown interpretations can be exchanged, and thus DEBUG.EXE can be "deceived". You are free to specify to DEBUG.EXE that indirection expression from the left column, which corresponds to the desirable CPU's interpretation, chosen in the right column.

Original formAffected by prefix 67h
[BP+DI][EBX]
[BX][EDI]
[BP+DI±7f][EBX±7f]
[BX±7f][EDI±7f]
[BP±7f][ESI±7f]
[DI±7f][EBP±7f]
Notes
  1. Programs using address size override prefix can't be executed by 16-bit CPUs.
  2. When a program is tested with commands "Proceed" (6.05-14) or "Trace" (6.05-17), then DEBUG.EXE doesn't show as the next machine command that one, which follows prefix byte 67h. Nevertheless 32-bit processors always accept prefix byte 67h together with the following machine command and execute both at one step.
  3. Address size override prefix always forces the non-default address size. When bit 6 in byte 06h of code segment descriptor[Note 5 to A.12-2] specifies default 32-bit address size, then prefix byte 67h forces 16-bit address size for the nearest following command. Here and further in this book default 16-bit address size is implied, just as it is automatically set by CPU's "shadow" registers, when CPU begins to work in real mode after being switched on.

7.02-08 Operation codes extension prefixes

Operation codes of x86 platform CPUs have been formed as a result of long evolution. At each stage of evolution a set of machine commands has been supplemented with new commands. Therefore several bytes have been used as operation code extension prefixes, affecting interpretation of operation codes in CPU's instruction decoder. These prefixes have no common specific function, except that each such prefix is able to introduce a separate group of various machine commands.

In the past, mostly on mainframe computers, byte FFh has been charged with mission of operation code extension prefix. Now it is regarded as first byte in operation codes of many different machine commands, described in part 7.03. Later bytes D8h–DFh were devoted to introduce arithmetic coprocessor's commands, described in part 7.04. In 1990s new commands for Pentium processor were introduced by prefix byte 0Fh ; several of these commands are mentioned in table 6.05-18.

Nowadays there is no more free bytes for being charged with prefix mission. For modern processors the SSE group of new commands was introduced by those combinations of operation codes with prefixes 66h (7.02-06), F2h (7.02-03), F3h (7.02-04), which previously were regarded invalid. New 64-bit processors transfer into prefix class the bytes 40h–4Fh, which are interpreted by all other x86 platform processors as commands DEC (7.03-20) and INC (7.03-27). Such changes can't be ignored, even if the goal of this book is limited to acquaintance with 16-bit programming under DOS. Recommendations for ensuring compatibility of 16-bit codes with modern processors are given in notes to descriptions of affected machine commands.

7.03 Commands for CPU

7.03-01 AAA – decimal correction of unpacked sum

AAA name stands for Adjust After Addition. The AAA command transforms a binary sum, obtained in AX register after addition of unpacked decimal digits, into a proper unpacked decimal word, containing one decimal digit per byte (for correction of a sum in packed decimal format see 7.03-18). The AAA command checks whether a binary sum in AX violates decimal overflow condition. Violation is expressed in that either flag AF is set into AC state, or a number in four least significant bits of AL (junior nimble) exceeds 9. If decimal overflow hasn't happened, then AAA command does nothing but clears CF flag (resets it into NC state). Otherwise AAA command adds AL =(AL + 6), AH =(AH + 1), and sets flags AF and CF into states AC and CY respectively. In any case four most significant bits in AL (senior nimble) are cleared to zero. Flags OF, SF, ZF and PF acquire indefinite state.

CodeExample
37AAA

7.03-02 AAD – decimal preparation to division

The AAD command (AAD = Adjust AX for Division) implies that AX register contains an unpacked decimal word, i.e. one decimal digit per byte. This decimal word is transformed by AAD command into binary form, so that it may be subjected to binary division (7.03-21). The AAD command calculates AL = AL+(10•AH), and then clears AH to zero. Flags SF, ZF, PF are set according to the result. Flags OF, AF, CF acquire indefinite state.

CodeExample
D5 0AAAD
Notes
  1. Machine codes "D5 (1-F)(0-9,B-F)" are improperly unassembled by DEBUG.EXE as "AAD ff" command.
  2. Numbers in packed decimal format can't be processed by AAD command, these must be unpacked beforehand.

7.03-03 AAM – decimal correction of a product

AAM stands for Adjustment After Multiplication. It is implied that AX register contains a binary product of two bytes, each representing an unpacked decimal digit and having four most significant bits (senior nimble) zeroed. The AAM command divides the product in AX by 10, writes the quotient into AH, and the remainder into AL, the result being a proper unpacked decimal word, containing one decimal digit per byte. Flags SF, ZF, PF are set according to the result. Flags OF, AF, CF acquire indefinite state. In a similar way the AAM command is able to transform any binary number (up to 63h) into an unpacked decimal word.

CodeExample
D4 0AAAM
Notes
  1. Machine codes "D4 (1-F)(0-9,B-F)" are improperly unassembled by DEBUG.EXE as "AAM ff" command.
  2. A product of packed decimal bytes can't be corrected by AAM command. Packed decimal numbers must be unpacked before multiplication.

7.03-04 AAS – decimal correction of unpacked remainder

The AAS command (AAS = Adjust After Subtraction) transforms a binary remainder, obtained in AX register after subtraction of unpacked decimal digits, into a proper unpacked decimal word, containing one decimal digit per byte (for correction of a remainder in packed decimal format see 7.03-19).

The AAS command checks whether a binary remainder in AX violates decimal overflow condition. Violation is expressed in that either the AF flag is set into AC state, or a number in four least significant bits of AL (junior nimble) exceeds 9. If decimal overflow hasn't happened, then AAS command does nothing but clears CF flag (resets it into NC state). Otherwise AAS command subtracts AL =(AL 6), AH = AH 1, and sets flags AF and CF into states AC and CY respectively. In any case four most significant bits in AL (senior nimble) are cleared to zero. Flags OF, SF, ZF and PF acquire indefinite state.

CodeExample
3FAAS

7.03-05 ADC – binary addition with carry

The ADC command performs addition of specified integers, taking into account a carry in the least significant bit. Carry reflects the result of previous operation, and since then it must be preserved, being represented by a state of CF flag. After addition flags OF, SF, ZF, AF, PF, CF acquire new states according to the sum, which replaces the first operand of ADC command.

ADC is a binary operation, but there are two exceptions. If the first operand is in AX register, then ADC command may be applied to unpacked decimal numbers, and the obtained binary sum in AX should be transformed into proper unpacked decimal number by AAA command (7.03-01). If the first operand is in AL register, then ADC command may be applied to packed decimal bytes, and the obtained binary sum in AL should be transformed into proper packed decimal byte by DAA command (7.03-18).

First
byte
Second byte Data
bytes
Example
10(0-B)(0-F)0-2ADC [bp+si+ffff],bl
10(C-F)(0-F) ADC bl,bl
11(0-B)(0-F)0-2ADC [bp+si+ffff],bx
11(C-F)(0-F) ADC bx,bx
12(0-B)(0-F)0-2ADC bl,[bp+si+ffff]
13(0-B)(0-F)0-2ADC bx,[bp+si+ffff]
14 1ADC AL,ff
15 2ADC AX,ffff
80(1,5,9)(0-7)1-3ADC byte ptr [bp+si+ffff],ff
80D(1-7)1ADC bl,ff
81(1,5,9)(0-7)2-4ADC word ptr [bp+si+ffff],ffff
81D(1-7)2ADC bx,ffff
83(1,5,9)(0-7)1-3ADC word ptr [bp+si+ffff],±7f
83D(1-7)1ADC bx,±7f
Notes
  1. Machine codes "1(2,3) (C-F)(0-F)" and "82 (1,5,9,D)(0-7)" are also unassembled by DEBUG.EXE as ADC command.

7.03-06 ADD – binary addition

The ADD command performs addition of specified integers, ignoring carry in the least significant bit, represented by a state of CF flag. After addition flags OF, SF, ZF, AF, PF, CF acquire new states according to the sum, which replaces the first operand of ADD command.

ADD is a binary operation, but there are two exceptions. If the first operand is in AX register, then ADD command may be applied to unpacked decimal numbers, and the obtained binary sum in AX should be transformed into a proper unpacked decimal number by AAA command (7.03-01). If the first operand is in AL register, then ADD command may be applied to packed decimal bytes, and the obtained binary sum in AL should be transformed into proper packed decimal byte by DAA command (7.03-18).

First
byte
Second byte Data
bytes
Example
00(0-B)(0-F)0-2ADD [bp+si+ffff],bl
00(C-F)(0-F) ADD bl,bl
01(0-B)(0-F)0-2ADD [bp+si+ffff],bx
01(C-F)(0-F) ADD bx,bx
02(0-B)(0-F)0-2ADD bl,[bp+si+ffff]
03(0-B)(0-F)0-2ADD bx,[bp+si+ffff]
04 1ADD AL,ff
05 2ADD AX,ffff
80(0,4,8)(0-7)1-3ADD byte ptr [bp+si+ffff],ff
80C(1-7)1ADD bl,ff
81(0,4,8)(0-7)2-4ADD word ptr [bp+si+ffff],ffff
81C(1-7)2ADD bx,ffff
83(0,4,8)(0-7)1-3ADD word ptr [bp+si+ffff],±7f
83C(1-7)1ADD bx,±7f
Notes
  1. Machine codes "0(2,3) (C-F)(0-F)" and "82 (0,4,8,C)(0-7)" are also unassembled by DEBUG.EXE as ADD command.

7.03-07 AND – logical "AND" operation

The AND command analyses pairs of corresponding bits in two operands. If FALSE (zero) state is found in at least one bit in a pair, then corresponding bit of the result is cleared to FALSE (zero). If both bits in a pair are in TRUE state, then corresponding bit of the result is set to TRUE state too. Result replaces the first operand. Flags SF, ZF, PF acquire new states according to the result. Flags CF and OF are cleared to states NC (No Carry) and NV (No oVerflow) respectively.

First
byte
Second byte Data
bytes
Example
20(0-B)(0-F)0-2AND [bp+si+ffff],bl
20(C-F)(0-F) AND bl,bl
21(0-B)(0-F)0-2AND [bp+si+ffff],bx
21(C-F)(0-F) AND bx,bx
22(0-B)(0-F)0-2AND bl,[bp+si+ffff]
23(0-B)(0-F)0-2AND bx,[bp+si+ffff]
24 1AND AL,ff
25 2AND AX,ffff
80(2,6,A)(0-7)1-3AND byte ptr [bp+si+ffff],ff
80E(1-7)1AND bl,ff
81(2,6,A)(0-7)2-4AND word ptr [bp+si+ffff],ffff
81E(1-7)2AND bx,ffff
83(2,6,A)(0-7)1-3AND word ptr [bp+si+ffff],±7f
83E(1-7)1AND bx,±7f
Notes
  1. Machine codes "2(2-3) (C-F)(0-F)" and "82 (2,6,A,E)(0-7)" are also unassembled by DEBUG.EXE as AND command.

7.03-08 CALL – call for a subroutine

The CALL command saves return address in stack and then performs a jump to subroutine according to specified target address. States of flags are not altered by CALL command.

Several forms of CALL command should be distinguished. A call to subroutine outside code segment of caller program is a CALL FAR, it operates with full 4-byte target address (segment : offset). A call to subroutine within code segment of caller program is a "near" CALL, it doesn't change current code segment and operates with 2-byte target offset only.

There are two different forms of "near" CALL command with machine codes FFh and E8h.

The "near" CALL command with machine code FFh pushes current contents of IP (Instruction Pointer) register into stack and then overwrites IP register with target offset, read either from memory or from a general-purpose register.

The "near" CALL command with machine code E8h, followed by a data word, acts otherwise: after saving IP's contents in stack, it adds this data word to offset in IP. Having found explicit target offset in command line after the CALL command, DEBUG.EXE automatically calculates difference between the given target offset and offset of the next machine command, which is currently present in IP register. This difference constitutes just that data word, which is written after E8h byte into assembled executable code.

Since both forms of "near" CALL command execute jumps inside current code segment, return back to the caller program from a subroutine, called with a "near" CALL command, must be performed by RET command (7.03-73), which restores from stack the contents of IP register only.

The CALL FAR command pushes into stack contents of both CS (Code Segment) and IP registers. Double-word operand of CALL FAR command replaces former contents in both CS and IP registers. Thus a jump to other segment is performed. Therefore a return back to the caller program from a subroutine, called with CALL FAR command, must be performed by RETF command (7.03-74), which restores from stack the contents of both CS and IP registers.

First
byte
Second byte Data
bytes
ExampleComments
9A 4CALL FAR ffff:ffff[Note 1]
E8 2CALL ffff
FF(1,5,9)(0-7)0-2CALL [bp+si+ffff][Note 2]
FF(1,5,9)(8-F)0-2CALL FAR [bp+si+ffff][Note 2]
FFD(0-7) CALL bx[Note 3]
Notes
  1. ^ In the shown example the first number is target segment address, the second number – target offset. Specification of marker FAR in this line is allowed, but isn't necessary : in any case a call FAR is performed.
  2. ^ a b When target address is read from memory, operation depends on presence (or absence) of marker FAR. If it is present, a four-byte target address is read, and CALL FAR is performed. If marker FAR is not specified, then a 2-byte target offset is read, and a "near" call is performed.
  3. ^ If operand of CALL command is in a register, then target offset must be written into this register beforehand. An appeal of CALL command to a 16-bit register always causes a "near" call.
  4. Machine code "FF D(8-F)" is unassembled by DEBUG.EXE as "CALL far bx".
  5. Repetition prefixes F2h (7.02-03), F3h (7.02-04) can't be applied to the CALL command.

7.03-09 CBW – byte to word conversion

The CBW command (CBW = Convert Byte to Word) converts a signed byte in AL register into a signed word (2 bytes) in AX register by filling the AH part of AX with sign bit of original signed byte. States of flags are not altered by CBW command.

CodeExample
98CBW

7.03-10 CLC – carry flag reset

The CLC command clears carry flag CF to the default "NC" (No Carry) state, which is often referred to as CF=0.

CodeExample
F8CLC

7.03-11 CLD – direction flag reset

The CLD command (CLD = CLear Direction) resets direction flag DF into its default state "UP". This causes ascending direction of offset count in index registers (DI and/or SI) during execution of string operations (CMPSB, LODSB, MOVSB, SCASB, STOSB, etc.).

CodeExample
FCCLD

7.03-12 CLI – interrupt flag reset

The CLI command (CLI = Clear Interrupt Flag) resets interrupt flag IF to the "DI" (= Disable Interrupts) state. The CLI command forces CPU to ignore external interrupts, except the non-maskable interrupt INT 02 (8.01-03).

CodeExample
FACLI
Notes
  1. Programmable interrupts are performed by INT command (7.03-28) in any case, regardless of the IF flag state.
  2. The CLI command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).

7.03-13 CMC – reversion of carry flag state

The CMC command (CMC = CompleMentary Carry) changes any current state of carry flag CF to the reverse state: NC (No Carry) to CY (CarrY) or vice versa.

CodeExample
F5CMC

7.03-14 CMP – comparison of operands

The CMP command sets flags OF, SF, ZF, AF, PF, CF according to difference between the first operand (the minuend) and the second (the subtrahend). The difference itself is not saved. Both operands are preserved intact.

Interpretation of flag's states, left by CMP command, depends on whether the operands were signed or unsigned numbers. Conditional jump commands JA, JB, JBE, JNB should be used after comparison of unsigned numbers. Other conditional jump commands JG, JGE, JL, JLE should be used after comparison of signed numbers. Full names of all conditional jump and loop commands reflect status relation of the first (left) operand of CMP command to the second (right) operand. For example, JA = "jump if above" means that the left operand of CMP command must be above, or greater than the right operand.

First
byte
Second byte Data
bytes
Example
38(0-B)(0-F)0-2CMP [bp+si+ffff],bl
38(C-F)(0-F) CMP bl,bl
39(0-B)(0-F)0-2CMP [bp+si+ffff],bx
39(C-F)(0-F) CMP bx,bx
3A(0-B)(0-F)0-2CMP bl,[bp+si+ffff]
3B(0-B)(0-F)0-2CMP bx,[bp+si+ffff]
3C 1CMP AL,ff
3D 2CMP AX,ffff
80(3,7,B)(8-F)1-3CMP byte ptr [bp+si+ffff],ff
80F(9-F)1CMP bl,ff
81(3,7,B)(8-F)2-4CMP word ptr [bp+si+ffff],ffff
81F(9-F)2CMP bx,ffff
83(3,7,B)(8-F)1-3CMP word ptr [bp+si+ffff],±7f
83F(9-F)1CMP bx,±7f
Notes
  1. machine codes "3(A,B) (C-F)(0-F)" and "82 (3,7,B,F)(8-F)" are also unassembled by DEBUG.EXE as CMP command.

7.03-15 CMPSB – serial comparison of byte pairs

Though the name CMPSB stands for "CoMPare Strings of Bytes", the CMPSB command in fact compares one pair of bytes. Addresses of bytes to compare must be loaded beforehand into DS:SI and ES:DI pairs of registers. If bytes are equal, CF (carry flag) is cleared to NC (No Carry) state, ZF (zero flag) is set to ZR state. If bytes are not equal, CF flag is set to CY state, ZF flag is cleared to NZ (No Zero) state. Flags OF, SF, AF, PF acquire the states corresponding to the difference between compared bytes, but this difference itself is not saved.

After comparison both offsets — in SI (source index) register and in DI (destination index) register — are incremented by 1 or decremented by 1, depending on the state ("UP" or "DN") of direction flag DF. The state of DF flag can be altered by CLD (7.03-11) and STD (7.03-85) commands. Automatic change of index register(s) contents prepares conditions for comparison of the next bytes pair.

The CMPSB command is often preceded by repetition prefixes F2h (7.02-03) or F3h (7.02-04), which enable to execute it cyclically and thus compare strings of bytes. The CMPSB command also may be preceded by a segment override prefix (2Eh or 26h or 36h, see 7.02-01); it enables to refer to other segment register instead of segment register DS for one of compared bytes. For the other compared byte the default segment register ES cannot be overridden by prefix.

CodeExample
A6CMPSB
Notes
  1. When CMPSB command is preceded by repetition prefixes F2h or F3h, the order of operations within the cycle includes assignment of flags states, then incrementation (or decrementation) of index register's contents, and after that cycle termination condition check. Therefore, the offsets in index registers at the moment of cycle termination are pointing not to those bytes, which have caused cycle termination, but rather to the next pair of bytes.

7.03-16 CMPSW – serial comparison of word pairs

The CMPSW command (CMPSW = CoMPare Strings of Words) compares a pair of words and then increments (or decrements) contents of SI and DI index registers by 2, thus preparing addresses to comparison of the next words pair. The operand size override prefix 66h (7.02-06) forces CMPSW command to compare pairs of four-byte operands (of DWORD type) and to increment (or decrement) contents of index registers by 4. All other peculiarities of CMPSW command execution are the same as those for CMPSB command (7.03-15).

CodeExample
A7CMPSW

7.03-17 CWD – word to double word conversion.

The CWD command (CWD = Convert Word into Double word) converts a signed word in AX register into a four-byte signed number (of DWORD type). The DX register is dedicated for the two most significant bytes of dword operand. Conversion is performed by filling the DX register with the sign bit of original signed word. States of flags are not altered by CWD command.

CodeExample
99CWD

7.03-18 DAA – decimal correction of packed sum

The DAA command (DAA = Decimal Adjustment after Addition) transforms a binary sum of packed decimal bytes in AL register into a proper packed decimal byte, representing 2 decimal digits of the sum (for decimal correction of unpacked sum, see 7.03-01).

Binary sum of packed decimal bytes may violate decimal overflow condition in both junior and senior 4-bit parts (nimbles) of AL register. The junior part is checked first: if the value there exceeds 9 or flag AF is set into AC state, then DAA command adds AL = (AL + 6). After that similar check is applied to senior 4-bit part (nimble) of AL register: if the value there exceeds 9Fh or CF flag is set into CY state, then DAA command adds AL = (AL + 60h). Flags AF, CF, SF, ZF, PF acquire new states according to the result. The OF flag is left in indefinite state.

CodeExample
27DAA

7.03-19 DAS – decimal correction of packed remainder

The DAS command (DAS = Decimal Adjustment after Subtraction) transforms a binary difference of packed decimal bytes in AL register into a proper packed decimal byte, representing 2 decimal digits of the remainder (for decimal correction of unpacked difference see 7.03-04).

Binary difference of packed decimal bytes may violate decimal overflow condition in both junior and senior 4-bit parts (nimbles) of AL register. The junior part is checked first : if the value there exceeds 9 or flag AF is set into AC state, then DAS command subtracts AL = (AL – 6). After that a similar check is applied to senior 4-bit part (nimble) of AL register : if the value there exceeds 9Fh or CF flag is set into CY state, then DAS command subtracts AL = (AL – 60h). Flags AF, CF, SF, ZF, PF acquire new states according to the result. The OF flag is left in indefinite state.

CodeExample
2FDAS

7.03-20 DEC – a unity decrement

The DEC command decrements its operand by 1. Flags OF, SF, ZF, AF, PF acquire new states according to the result. The CF flag preserves its former state.

First
byte
Second byte Data
bytes
Example
4(8-F)  DEC bx
FE(0,4,8)(8-E)0-2DEC byte ptr [bp+si+ffff]
FEC(8-F) DEC bl
FF(0,4,8)(8-E)0-2DEC word ptr [bp+si+ffff]
Notes
  1. Bytes 48h–4Fh can be interpreted by 64-bit processors as prefixes. Therefore 2-byte codes "FF C(8-F)" should be given preference over 1-byte codes 4(8-F). Codes "FF C(8-F)" are interpreted as "DEC bx" command by all x86 platform

processors and are properly unassembled by DEBUG.EXE, but during assembling these codes should be presented to DEBUG.EXE as data by DB instruction (7.01-01).

7.03-21 DIV – division of unsigned integers

The DIV command performs division of unsigned binary integers (for division of signed integers see 7.03-24). Explicit operand is the divisor. If divisor is a byte, dividend is implied to exist in AX register, quotient is left in AL, and the remainder is placed in AH. If divisor is a word, dividend is implied to exist in DX register (most significant 2 bytes) and in AX register (less significant 2 bytes), quotient is left in AX, the remainder is placed in DX. Flags OF, SF, ZF, AF, PF, CF acquire indefinite state.

Though DIV is a binary operation, unpacked decimal words may be subjected to binary division, if they are transformed in advance into acceptable quasi-binary form by AAD command (7.03-02).

First
byte
Second byte Data
bytes
Example
F6(3,7,B)(0-7)0–2DIV byte ptr [bp+si+ffff]
F6F(0-7) DIV bl
F7(3,7,B)(0-7)0–2DIV word ptr [bp+si+ffff]
F7F(0-7) DIV bx
Notes
  1. If division operation causes overflow in quotient register, CPU automatically generates an exception : a call for INT 00 handler (8.01-01). Outcome depends on that handler.

7.03-22 ESC – code transfer to asynchronous coprocessor

Originally the ESC (= ESCape) command has been used to send data and commands from CPU to external asynchronous coprocessor. Each ESC command has been preceded by WAIT prefix (7.02-05), forcing CPU to wait for arrival of readiness confirmation signal from coprocessor to CPU's pin "BUSY". Later arithmetic coprocessor's commands have got their specific names (7.04), but the rest machine codes, starting with bytes D8h–DFh, are still unassembled by DEBUG.EXE as ESC command :

D9 (0,4,8)(8-F), D9 D(1-7), DA (C-F)(0-F), DB (0,4,8,C,D,F)(8-F),
DB (2,3,6,7,A-D,F)(0-7), DB E(4 – F), DD (0,2,6)(8-F),
DD (E,F)(0-F), DE D(8,A-F), DF (0,4,8)(8-F), DF (E,F)(0-F).

Some of the mentioned codes are assigned yet to new commands of modern arithmetic coprocessors. As all coprocessor's commands, starting with bytes D8h–DFh, the ESC command is executed by CPU, if in control register CR0 (A.11-4) its bit 02h ("Coprocessor emulation") is cleared to zero. But if bit 02h is set, then CPU responds to each such command with a call to INT 07 handler (8.01-08). It is implied that a special INT 07 handler should be loaded, which is able to emulate functions of arithmetic coprocessor or other asynchronous device(s).

Potentially the ESC command can be used to send data and commands to external asynchronous device(s), but for modern processors this opportunity is not documented. The first operand of ESC command is a hexadecimal number, the second is read from the specified source. Interpretation of both operands is a prerogative of each particular target device. States of flags are not altered by ESC command.

First
byte
Second byteExamples
DAC(0-7)ESC 10,bl
DAC(8-F)ESC 11,bl
DAD(0-7)ESC 12,bl
DAD(8-F)ESC 13,bl
DAE(0-7)ESC 14,bl
DAE(8-F)ESC 15,bl
DAF(0-7)ESC 16,bl
DAF(8-F)ESC 17,bl

7.03-23 HLT – set CPU to a standstill

The HLT command (= HaLT) forces processor to stop. Being stopped, processor preserves contents of CS:IP registers and states of flags, so that an opportunity of proper activation is secured. Processor can be turned back to normal functioning either by a reboot or by external interrupt signal, received either via NMI pin (8.01-03) or via interrupt controller (8.01-09).

CodeExample
F4HLT
Notes
  1. The HLT command can be used in programs, which are to be executed at the highest privilege level. Beyond the highest privilege level the HLT command is ignored.
  2. A way to determine that particular external interrupt, which has turned CPU out of a standstill state, is shown in article 8.01-09.

7.03-24 IDIV – division of signed integers

The IDIV (= Integer DIVision) command performs division of signed binary integers (for division of unsigned integers see 7.03-21). Explicit operand is the divisor. If divisor is a byte, dividend is implied to exist in AX register, quotient is left in AL, the remainder is placed in AH. If divisor is a word, dividend is implied to exist in DX register (more significant 2 bytes) and in AX register (less significant 2 bytes), quotient is left in AX, the remainder is placed in DX. Sign of the remainder is always the same as that of dividend. Flags OF, SF, ZF, AF, PF, CF acquire indefinite state. Though IDIV is a binary operation, unpacked decimal words may be subjected to binary division, if they are transformed in advance into acceptable quasi-binary form by AAD command (7.03-02).

First
byte
Second byte Data
bytes
Example
F6(3,7,B)(8-F)0-2IDIV byte ptr [bp+si+ffff]
F6F(8-F) IDIV bl
F7(3,7,B)(8-F)0-2IDIV word ptr [bp+si+ffff]
F7F(8-F) IDIV bx
Notes
  1. If division operation causes overflow in quotient register, CPU automatically generates an exception: a call for INT 00 handler (8.01-01). Outcome depends on that handler.

7.03-25 IMUL – multiplication of signed integers

The IMUL (Integer MULtiplcation) command multiplies signed integers (for unsigned integers see 7.03-61). Explicit operand of IMUL command represents multiplier. If this operand is a byte, then the other operand is implied to exist in AL register ; after multiplication the product is left in AX register. If explicit operand is a word, then the other operand must exist in AX register; after multiplication the less significant 2 bytes of product are left in AX register, the most significant 2 bytes of product in DX register.

If most significant part of product in AH or in DX register represents non-zero values, then flags OF and CF are set by IMUL command to OV and CY states correspondingly. On the contrary, clear states NV and NC of these flags indicate that most significant part of product is filled with sign bits only. Flags SF, ZF, AF, PF acquire indefinite state.

The IMUL command can be applied to binary integers and to unpacked decimal numbers. Packed decimal operands must be unpacked beforehand. Product of unpacked decimal numbers needs to be transformed into unpacked decimal format by AAM command (7.03-03).

First
byte
Second byte Data
bytes
Example
F6(2,6,A)(8-F)0-2IMUL byte ptr [bp+si+ffff]
F6E(8-F) IMUL bl
F7(2,6,A)(8-F)0-2IMUL word ptr [bp+si+ffff]
F7E(8-F) IMUL bx
Notes
  1. Other forms of IMUL command with 2 explicit operands (codes 69h and 6Bh) are not supported by DEBUG.EXE.

7.03-26 IN – data input from port

While performing the IN command, CPU generates a signal, which switches CPU's buses from memory to I/O ports and enables asynchronous data transfer. First operand of IN command specifies the register, where the received data should be written. This register must be chosen according to format of received data: a byte register AL, if a byte is to be received, or a double-byte register AX, if a word is to be received. The second operand of IN command defines port address. The latter may be specified either explicitly as a double-digit hexadecimal number or indirectly – as contents of DX register. States of CPU's flags are not altered by IN command.

First
byte
Second byte Data
bytes
Example
E4 1IN AL,ff
E5 1IN AX,ff
EC  IN AL,DX
ED  IN AX,DX
Notes
  1. Selected port addresses are shown in appendix A.14-1. Direct forms of IN command don't allow port addresses above FFh. Indirect addressing via DX register is not subjected to this restriction.
  2. The IN command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).

7.03-27 INC – a unity increment

The INC command increments its operand by 1. Flags OF, SF, ZF, AF, PF acquire new states according to the result. The CF flag preserves its former state.

First
byte
Second byte Data
bytes
Example
4(0-7)  INC bx
FE(0,4,8)(0-7)0-2INC byte ptr [bp+si+ffff]
FEC(0-7) INC bl
FF(0,4,8)(0-7)0-2INC word ptr [bp+si+ffff]
Notes
  1. Bytes 40h–47h can be interpreted by 64-bit processors as prefixes. Therefore 2-byte codes "FF C(0-7)" should be given preference over 1-byte codes 4(0-7). Codes "FF C(0-7)" are interpreted as "INC bx" command by all x86 platform processors and are properly unassembled by DEBUG.EXE, but during assembling these codes should be presented to DEBUG.EXE as data by DB instruction (7.01-01).

7.03-28 INT – a call for interrupt handler

The INT (= Interrupt) command transfers control to that interrupt handler, which number is defined by operand of INT command. But before control is transferred, the INT command prepares conditions for further return back to the current program after termination of interrupt handler's job. Therefore the following actions are undertaken by INT command :

  • current state of flags register is saved in stack ;
  • current state of CS register (segment address) is saved in stack ;
  • offset of the next command is calculated and saved in stack in order to enable further restoration of IP register state ;
  • the IF flag is cleared to DI state, so that intake of interrupt requests via interrupt controller is blocked ;
  • queue of prefetched commands in CPU is reset ;
  • multiplication of interrupt number by 4 gives address of that memory cell, where the interrupt handler's address is stored ;
  • copying of interrupt handler's address (segment and offset) from memory cell into registers CS:IP transfers control to the handler.

The order of data, left in stack by INT command, enables a return back to current program by means of IRET command (7.03-30), which must be the last command, performed by each interrupt handler. Resumed execution of interrupted program will start from that command, which follows the INT command.

Almost each interrupt handler can't perform its mission unless some specific conditions are met or unless some required data are present in CPU's registers or in memory. These conditions and data must be prepared in advance, before execution of INT command. Relevant requirements of selected interrupt handlers are described in chapter 8 of this book.

First
byte
Second byte Data
bytes
Example
CC  INT 3
CD 1INT ff
Notes
  1. Original state of interrupt flag IF doesn't affect execution of INT command. Flag IF affects only those external interrupt requests, which are received via interrupt controller.
  2. A unique feature of INT 3 command (code CCh) is that it doesn't depend on privilege level: at any privilege level it is executed just as in real mode.
  3. Offset of the next command is stored in stack by commands INT and INTO (7.03-29) only. All other internal interrupts (exceptions) leave in stack the current command's offset.
  4. Data in stack are available to interrupt handlers. If the state of stack pointer (SP) is saved in BP register just after control transfer, then [BP+00] address points at return offset, [BP+02] address points at return segment, [BP+04] address points at flag's states of interrupted program.

7.03-29 INTO – a call for overflow handler

Immediate response to overflow via INT 00 (8.01-01) sometimes isn't expedient. More flexible and retarded response to overflows can be provided by INTO command (INTO = INTerrupt if Overflow). If OV (= OVerflow) state of OF flag indicates fact of overflow, then INTO command calls for interrupt INT 04 handler (8.01-05), which must be designed for handling overflow errors. A call for INT 04 handler by INTO command includes all those precautions, which are undertaken by INT command (7.03-28).

CodeExample
CEINTO
Notes
  1. The default INT 04 handler does nothing but returns control to the caller program. In order to obtain a desirable response to overflows the user has to prepare another INT 04 handler instead of the default one. New handler becomes active since its address is written into interrupt table (8.02-18).

7.03-30 IRET – return from interrupt handler

The IRET (= Interrupt RETurn) command restores from stack all the data, providing a return to execution of the caller program: its segment address in CS register, the former states of flags, and prepared offset of the next command in IP register. The IRET command must be the last executed by every interrupt handler.

CodeExample
CFIRET
Notes
  1. Restoration of flag's states by IRET command is not subjected to those restrictions, which are imposed on POPF command (7.03-68). Thus IRET command gives a chance to bypass these restrictions.
  2. The IRET commands resets a queue of prefetched commands in CPU. This is done because commands decoding rules for interrupt handler may differ from those accepted for the caller program.

7.03-31 JA – Jump if above

The JA command adds its data byte to contents of IP register, if both flags CF and ZF are cleared to states NC (No Carry) and NZ (No Zero) correspondingly. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

Most often the JA command is used after operations with unsigned integers, in particular, after commands CMP, SBB, SUB (after operations with signed integers the same condition "above" is checked by JG command, 7.03-35).

DEBUG.EXE accepts one more name for JA command: JNBE – "jump if not below or equal", but code 77h is always unassembled as JA.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
77 1JA aaaa

7.03-32 JB – Jump if below

The JB command adds its data byte to contents of IP register, if flag CF is set to CY (CarrY) state. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JB command is used for performing jumps after various failures, marked by setting CF flag into CY state. JB command is also used after operations with unsigned integers, in particular, after commands CMP, SBB, SUB (after operations with signed integers the same condition "below" is checked by JL command, 7.03-37).

DEBUG.EXE accepts two more names for JB command: JNAE –"jump if not above or equal" and JC – "jump if carry", but code 72h is always unassembled as JB.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
72 1JB aaaa

7.03-33 JBE – Jump if below or equal

The JBE command adds its data byte to contents of IP register, if either flag CF is set to CY (CarrY) state or flag ZF is set into ZR (ZeRo) state. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JBE command is used after operations with unsigned integers, in particular, after commands CMP, SBB, SUB (after operations with signed integers the same condition "below or equal" is checked by JLE command, 7.03-38).

DEBUG.EXE accepts one more name of this command: JNA – "jump if not above", but code 76h is always unassembled as JBE.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
76 1JBE aaaa

7.03-34 JCXZ – Jump if CX is Zero

The JCXZ command adds its data byte to contents of IP register, if value in CX register is zero. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

As far as CX register is often used as iterations counter, the JCXZ command enables to bypass loops, if necessary condition is not met before entering the loop.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
E3 1JCXZ aaaa

7.03-35 JG – Jump if Greater

The JG command adds its data byte to contents of IP register, if flag ZF is cleared to NZ (No Zero) state and at the same time flags SF and OF have the same state, i.e. either both are cleared or both are set. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JG command is used after operations with signed integers, in particular, after commands CMP, SBB, SUB (after operations with unsigned integers the same condition "greater" is checked by JA command, 7.03-31).

DEBUG.EXE accepts one more name for JG command: JNLE – "jump if not lower or equal", but code 7Fh is always unassembled as JG.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
7F 1JG aaaa

7.03-36 JGE – Jump if Greater or Equal

The JGE command adds its data byte to contents of IP register, if flags SF and OF are in the same state, i.e. either both are cleared or both are set. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JGE command is used after operations with signed integers, in particular, after commands CMP, SBB, SUB (after operations with unsigned integers the same condition "greater or equal" is checked by JNB command, 7.03-40).

DEBUG.EXE accepts one more name of this command: JNL – "jump if not lower", but code 7Dh is always unassembled as JGE.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
7D 1JGE aaaa

7.03-37 JL – Jump if Lower

The JL command adds its data byte to contents of IP register, if flags SF and OF are in different states, i.e. when any of them is cleared, while the other is set. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JL command is used after operations with signed integers, in particular, after commands CMP, SBB, SUB (after operations with unsigned integers the same condition "lower" is checked by JB command, 7.03-32).

DEBUG.EXE accepts one more name of this command: JNGE – "jump if not greater or equal", but code 7Ch is always unassembled as JL.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
7C 1JL aaaa

7.03-38 JLE – Jump if Lower or Equal

The JLE command adds its data byte to contents of IP register, if either flag ZF is set to ZR (ZeRo) state or flags SF and OF are in different states, i.e. when any of them is cleared, while the other is set. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JLE command is used after operations with signed integers, in particular, after commands CMP, SBB, SUB (after operations with unsigned integers the same condition "lower or equal" is checked by JBE command, 7.03-33).

DEBUG.EXE accepts one more name for JLE command: JNG – "jump if not greater", but code 7Eh is always unassembled as JLE.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
7E 1JLE aaaa

7.03-39 JMP – unconditional jump

The JMP command performs a transition (jump) to execution of another machine command by changing former contents either in IP (instruction pointer) register only or simultaneously in both CS (code segment) and IP registers. If JMP command is given a double-word operand, it is executed as "JMP FAR": the first word replaces former segment address in CS register, the second word replaces former offset in IP register. The JMP command with one-word operand replaces offset in IP register only, thus performing a "near" jump within the same segment. The JMP command with a single byte of data performs a "short" jump otherwise: it adds its data byte to current offset in IP register.

While CPU operates in real mode, the JMP command doesn't affect flags.

First
byte
Second byte Data
bytes
ExampleComments
E9 2JMP ffffnote 1
EA 4JMP ffff:ffffnote 2
EB 1JMP aaaanote 1
FF(2,6,A)(0-7)0-2JMP [bp+si+ffff]note 3
FF(2,6,A)(8-F)0-2JMP FAR [bp+si+ffff]note 3
FFE(0-7) JMP bxnote 4
Notes
  1. ^ a b Having been given a target offset in an assembler command line, DEBUG.EXE automatically calculates difference between specified target offset and offset of the next command. If this difference doesn't exceed ±7fh, JMP command is translated into machine code EBh ("short" jump), otherwise it is translated into machine code E9h ("near" jump).
  2. ^ In the shown example the first number is segment address, the second number – the target offset. Marker FAR in such command lines is allowed, but isn't necessary : a FAR jump will be performed in any case.
  3. ^ a b When JMP command gets target address via indirection, then the type of jump depends on presence of marker FAR : if it is specified, a 4-byte full address will be read from memory, and a far jump will be performed. If marker FAR is absent, then a 2-byte word will be read from memory. This word will be interpreted as target offset, and a "near" jump will be performed.
  4. ^ If JMP command appeals to a register, then target offset must be prepared in this register beforehand. An appeal of JMP command to a 16-bit register always causes a "near" jump.
  5. Almost each switching of CPU from real mode and back is followed by a JMP FAR command, transferring control to the next command in the same code segment. This JMP command is needed not for a jump, but for other purposes. First, it brings status of a word in CS register (segment address or selector) in accordance with CPU's mode. Second, this JMP command resets a queue of prefetched commands in CPU, because these commands were decoded according to the rules of former CPU's mode.
  6. Codes "FF E(8-F)" are unassembled by DEBUG.EXE as command "JMP far bx".

7.03-40 JNB – Jump if Not Below

The JNB command adds its data byte to contents of IP register, if flag CF is cleared to NC (No CarrY) state. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

The JNB command is used for performing jumps after successful terminations, marked by clearing CF flag to NC state. JNB command is also used after operations with unsigned integers, in particular, after commands CMP, SBB, SUB (after operations with signed integers the same condition "greater or equal" is checked by JGE command, 7.03-36).

DEBUG.EXE accepts two more names for JNB command: JAE – "jump if above or equal" and JNC – "jump if not carry", but code 73h is always unassembled as JNB.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
73 1JNB aaaa

7.03-41 JNO – Jump if No Overflow

The JNO command adds its data byte to contents of IP register, if OF flag is cleared to NV (No oVerflow) state. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
71 1JNO aaaa

7.03-42 JNS – Jump if No Sign

The JNS command adds its data byte to contents of IP register, if SF flag is cleared to PL state, which indicates positive integer result of previous operation. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
79 1JNS aaaa

7.03-43 JNZ – Jump if No Zero

The JNZ command adds its data byte to contents of IP register, if ZF flag is cleared to NZ (No Zero) state, which indicates inequality or non-zero result of previous operation. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

DEBUG.EXE accepts one more name for JNZ command: JNE – "jump if not equal", but code 75h is always unassembled as JNZ.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
75 1JNZ aaaa

7.03-44 JO – Jump if Overflow

The JO command adds its data byte to contents of IP register, if OF flag is set to OV (OVerflow) state. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
70 1JO aaaa

7.03-45 JPE – Jump if Parity Even

The JPE command adds its data byte to contents of IP register, if PF flag is set to PE (Parity Even) state, which indicates an even sum of bits in the least significant byte of previous operation result (other bytes of the result are not taken into account). As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

DEBUG.EXE accepts one more name for JPE command: JP – "jump if parity", but code 7Ah is always unassembled as JPE.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
7A 1JPE aaaa

7.03-46 JPO – Jump if Parity Odd

The JPO command adds its data byte to contents of IP register, if PF flag is cleared to PO (Parity Odd) state, which indicates an odd sum of bits in the least significant byte of previous operation result (other bytes of the result are not taken into account). As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

DEBUG.EXE accepts one more name for JPO command: JNP – "jump if not parity", but code 7Bh is always unassembled as JPO.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
7B 1JPO aaaa

7.03-47 JS – Jump if Sign

The JS command adds its data byte to contents of IP register, if SF flag is set to NG state, which indicates negative integer result of previous operation. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
78 1JS aaaa

7.03-48 JZ – Jump if Zero

The JZ command adds its data byte to contents of IP register, if ZF flag is set to ZR (ZeRo) state, which indicates equality or zero result of previous operation. As far as data byte represents difference between target and current offsets, its addition causes a "short" transition (jump) to appointed target offset within ±7Fh vicinity of the nearest next command.

DEBUG.EXE accepts one more name for JZ command: JE – "jump if equal", but code 74h is always unassembled as JZ.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
74 1JZ aaaa

7.03-49 LAHF – copying flags into AH register

The LAHF command (LAHF = Load AH with Flags) copies into AH register the states of flags from the lower byte of flags register. Bit 0 in AH corresponds to CF (carry flag), bit 2 – to PF (parity flag), bit 4 – to AF (auxiliary flag), bit 6 – to ZF (zero flag), bit 7 – to SF (sign flag). Bits 5, 3, 1 have no corresponding flags. Bit 1 is always set to binary unity, bits 5 and 3 are always cleared to zero.

CodeExample
9FLAHF

7.03-50 LDS – Loading of DS register

The LDS command regards its second operand as address of a double word. Bytes 1 and 2 in this double word are interpreted as offset, bytes 3 and 4 as segment address. LDS command copies this segment address into DS segment register, and offset into the register which is specified as the first operand of LDS command. Thus this register together with segment register DS become ready to be referenced as segment: offset pair. States of flags are not altered by LDS command.

First
byte
Second byte Data
bytes
Example
C5(1,5,9)(8-F)0-2LDS bx,[bp+si+ffff]
Notes
  1. Codes "C5 (C-F)(0-F)" are also unassembled by DEBUG.EXE as LDS command.
  2. By default the DS:SI pair of registers represents source address; therefore SI register is the most frequent substitution for "bx" in the shown example of LDS command.
  3. Both segment in DS register and offset in specified register may be used for addressing and be reassigned in the same operation; for example, command DS: LDS SI,[SI] is valid.

7.03-51 LEA – offset calculation

The LEA command (LEA = Load Effective Address) calculates expression in square brackets, given as the second operand. Result of calculation represents a certain offset. This offset is written into that register, which is specified as the first operand of LEA command. States of flags are not altered by LEA command.

First
byte
Second byte Data
bytes
Example
8D(0-B)(0-F)0-2LEA bx,[bp+si+ffff]

7.03-52 LES – Loading of ES register

The LES command regards its second operand as address of a double word. Bytes 1 and 2 in this double word are interpreted as offset, bytes 3 and 4 – as segment address. LES command copies this segment address into ES segment register, and offset into the register which is specified as the first operand of LES command. Thus this register together with segment register ES become ready to be referenced as segment: offset pair. States of flags are not altered by LES command.

First
byte
Second byte Data
bytes
Example
C4(1,5,9)(8-F)0-2LES bx,[bp+si+ffff]
Notes
  1. Codes "C4 (C-F)(0-F)" are also unassembled by DEBUG.EXE as LES command.
  2. By default the ES:DI pair of registers represents destination address; therefore DI register is the most frequent substitution for "bx" in the shown example of LES command.
  3. Both segment in ES register and offset in specified register may be used for addressing and be reassigned in the same operation; for example, command ES: LES DI,[DI] is valid.

7.03-53 LODSB – serial copying of bytes

Though the name LODSB stands for "LOaD String of Bytes", the LODSB command in fact copies into AL register a single byte, read out of memory according to address, which is written beforehand into DS:SI pair of registers. After copying offset in SI (source index) register is incremented by 1 or decremented by 1: it depends on state ("UP" or "DN") of direction flag DF. The state of DF flag can be altered by CLD (7.03-11) and STD (7.03-85) commands. Automatic change of SI register contents prepares conditions for copying of the next byte. States of flags are not altered by LODSB command.

The LODSB command may be preceded by a segment override prefix (7.02-01) ; it enables to refer to other segment register instead of default source segment register DS.

CodeExample
ACLODSB

7.03-54 LODSW – serial copying of words

The LODSW command (LODSW = LOaD a String of Words) copies into AX register a single word and then increments (or decrements) contents of SI index register by 2, thus preparing offset to copying of the next word. The operand size override prefix 66h (7.02-06) forces LODSW command to copy a four-byte operands (of DWORD type) and to increment (or decrement) contents of SI register by 4. All other peculiarities of LODSW command execution are the same as those for LODSB command (7.03-53).

CodeExample
ADLODSW

7.03-55 LOOP – arrangement of a cycle

The LOOP command first decrements an integer in CX register by 1, and then checks whether the remainder is zero. Until the remainder is not zero, the LOOP command adds its data byte to current offset in IP register. Thus a "short" jump is performed within ±7fh vicinity of the nearest next command. But when the remainder in CX register becomes zero, then LOOP command does nothing, so that CPU exits the cycle and just proceeds to execution of the nearest following command beyond the cycle's body. States of flags are not altered by LOOP command.

Count of iterations in CX register is based on the supposition that the LOOP command follows the cycle's body. In this case cycle's body is executed once before cycle entering condition is checked by LOOP command for the first time. In order to prevent uncontrolled execution the cycle's body should be preceded by JCXZ command (7.03-34). The same result can be obtained by cycles with exported body, but in the latter case the preset integer in CX register must be by a unity greater, than required number of iterations.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
E2 1LOOP aaaa

7.03-56 LOOPNZ – cycle with ZF = ZR exit condition

The LOOPNZ command (LOOPNZ = Loop if Not Zero) first decrements an integer in CX register by 1, not affecting flags, and then checks two conditions : whether the remainder in CX register is zero and whether ZF flag is set into ZR (ZeRo) state. Until both conditions are met, LOOPNZ command adds its data byte to current offset in IP register. Thus a "short" jump is performed within ±7fh vicinity of the nearest next command. But when either of the mentioned conditions is met, then LOOPNZ command does nothing, so that CPU exits the cycle and just proceeds to execution of the nearest following command beyond the cycle's body. Other peculiarities of cycle's arrangement with LOOPNZ command are the same as for LOOP command (7.03-55).

DEBUG.EXE accepts one more name for LOOPNZ command: LOOPNE (= loop, if not equal), but code E0h is always unassembled as LOOPNZ.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
E0 1LOOPNZ aaaa

7.03-57 LOOPZ – cycle with ZF = NZ exit condition

The LOOPZ command (LOOPZ = Loop if Zero) first decrements an integer in CX register by 1, not affecting flags, and then checks two conditions : whether the remainder in CX register is zero and whether ZF flag is cleared to NZ (No Zero) state. Until both conditions are met, LOOPZ command adds its data byte to current offset in IP register. Thus a "short" jump is performed within ±7fh vicinity of the nearest next command. But when either of the mentioned conditions is met, then LOOPZ command does nothing, so that CPU exits the cycle and just proceeds to execution of the nearest following command beyond the cycle's body. Other peculiarities of cycle's arrangement with LOOPZ command are the same as for LOOP command (7.03-55).

DEBUG.EXE accepts one more name for LOOPZ command: LOOPE (loop, if equal), but code E1h is always unassembled as LOOPZ.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
E1 1LOOPZ aaaa

7.03-58 MOV – data copying command

The MOV command copies a byte or a word, specified directly or indirectly by the second operand, into register or memory cell, specified by the first operand. Explicit specification of data size to be copied (byte or word) is not needed, when it can be determined by the size of involved register. States of flags are not altered by ordinary forms of MOV command, except forms appealing to control, debugging and test CPU's registers. These forms, shown in [note 1] below, may leave flags OF, SF, ZF, AF, PF, CF in indefinite state.

First
byte
Second byte Data
bytes
Example
88(0-B)(0-5, 7-F)0-2MOV [bp+si+ffff],bl
88(C-F)(0-F) MOV bl,bl
89(0-B)(0-5, 7-F)0-2MOV [bp+si+ffff],bx
89(C-F)(0-F) MOV bx,bx
8A(0-B)(0-5, 7-F)0-2MOV bl,[bp+si+ffff]
8B(0-B)(0-5, 7-F)0-2MOV bx,[bp+si+ffff]
8C(0,1,4,5,8,9)(0-F)0-2MOV [bp+si+ffff],ss
8C(C,D,E)(0-F) MOV bx,ss
8E(0,1,4,5,8,9)(0-F)0-2MOV ss,[bp+si+ffff]
8E(C,D,E)(0-F) MOV ss,bx
A0 2MOV AL,[ffff]
A1 2MOV AX,[ffff]
A2 2MOV [ffff],AL
A3 2MOV [ffff],AX
B(0-7) 1MOV bl,ff
B(8-F) 2MOV bx,ffff
C6(0,4,8)(0-7)1-3MOV byte ptr [bp+si+ffff],ff
C7(0,4,8)(0-7)2-4MOV word ptr [bp+si+ffff],ffff
Notes
  1. ^ a b DEBUG.EXE doesn't "know" those forms of MOV command, which appeal to debugging and control registers of 32-bit CPUs, but codes of these commands may be entered as data by DB instruction (7.01-01). Codes of these commands are 3 bytes long, commencing with OFh byte. The second byte defines direction of copying :
    20h – from control register (CR0, CR2–CR4)
    21h – from debugging register (DR0–DR3, DR6, DR7)
    22h – into control register (CR0, CR2–CR4)
    23h – into debugging register (DR0–DR3, DR6, DR7)

    The third byte in such codes defines particular register :

    C0h – CR0 or DR0, for example,0F 20 C0 = MOV EAX,CR0
    C8h – DR1, for example,0F 23 C8 = MOV DR1,EAX
    D0h – CR2 or DR2, for example,0F 20 D0 = MOV EAX,CR2
    D8h – CR3 or DR3, for example,0F 20 D8 = MOV EAX,CR3
    E0h – CR4, for example,0F 22 E0 = MOV CR4,EAX
    F0h – DR6, for example,0F 21 F0 = MOV EAX,DR6
    F8h – DR7, for example,0F 23 F8 = MOV DR7,EAX

    In order to use other register instead of EAX, you have to add to the third byte the number of that register (from 00h to 07h) in the following list :

    EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, for example :0F 20 C3 = MOV EBX,CR0
    DEBUG.EXE can't unassemble these codes, but doesn't hamper debugging of programs with these codes, if programs are executed by 32-bit CPU. Operand size override prefix (7.02-06) before these commands isn't needed.
  2. DEBUG.EXE doesn't "know" commands appealing to segment registers GS and FS of 32-bit CPUs, but codes of these commands can be entered as data by DB instruction (7.01-01). Codes of these commands are 2 bytes long :
    8C E0= MOV AX,FS
    8C E8= MOV AX,GS
    8E E0= MOV FS,AX
    8E E8= MOV GS,AX

    In order to use other register instead of AX, you have to add to the second byte the number of that register (from 00h to 07h) in a list, given in second line of table 7.00, for example :

    8E E3= MOV FS,BX
    DEBUG.EXE improperly unassembles codes of these commands as related to ES and CS segment registers, but this doesn't hamper debugging of programs with these codes, if programs are executed by a 32-bit CPU.
  3. The MOV command can't copy data into CS segment register; this can be done by control transfer commands only (CALL, JMP, RETF, etc.).
  4. The MOV command copying data into SS register induces hardware blocking of external interrupts for the time of execution of one next command. It is implied, that the next command must write new offset into SP register. Only this order of commands excludes failures, caused by external interrupts, at the moment of transition to other stack.
  5. Codes 8(A,B) (C-F)(0-F), 8(C,E)(2,3,6,7,A,B,F)(0-F) and C(6,7) (C-F)(0-F) are also unassembled by DEBUG.EXE as MOV command.

7.03-59 MOVSB – serial copying of bytes

Though the name MOVSB stands for "MOVe String of Bytes", the MOVSB command in fact copies a single byte. The source byte address must be loaded beforehand into the DS:SI pair of registers ; the destination address, into the ES:DI registers. After copying, both offsets — in SI (source index) register and in DI (destination index) register — are incremented by 1 or decremented by 1: it depends on the state ("UP" or "DN") of direction flag DF. The state of DF flag can be altered by CLD (7.03-11) and STD (7.03-85) commands. Automatic change of index registers contents prepares conditions for copying of the next byte in the next memory cell. States of flags are not altered by MOVSB command.

The MOVSB command is often preceded by repetition prefixes F2h (7.02-03) or F3h (7.02-04), which enable to execute it cyclically and thus copy a string of bytes. The MOVSB command also may be preceded by a segment override prefix (7.02-01) ; it enables to refer to other segment register instead of default source segment register DS. Destination segment register ES can't be changed by prefix.

CodeExample
A4MOVSB

7.03-60 MOVSW – serial copying of words

The MOVSW command (MOVSW = Move String of Words) copies a word and then increments (or decrements) contents of SI and DI index registers by 2, thus preparing source and destination offsets to copying the next word into the next pair of memory cells. Operand size override prefix 66h (7.02-06) forces MOVSW command to copy four-byte operands (of DWORD type) and to increment (or decrement) contents of index registers by 4. All other peculiarities of MOVSW command execution are the same as those for MOVSB command (7.03-59).

CodeExample
A5MOVSW

7.03-61 MUL – multiplication of unsigned integers

The MUL command (MUL = MULtiplcation) multiplies unsigned integers (for multiplication of signed integers see 7.03-25). Explicit operand of MUL command represents a multiplier. If this operand is a byte, then the other operand is implied to exist in AL register; after multiplication the product is left in AX register. If explicit operand is a word, then the other operand is implied to exist in AX register; after multiplication the less significant 2 bytes of product are left in AX register, and the most significant 2 bytes of product, in the DX register.

If the most significant part of product in AH or in DX register represents non-zero values, then flags OF and CF are set by MUL command to OV and CY states correspondingly. On the contrary, cleared states NV and NC of these flags indicate that the most significant part of product is filled with zeros. Flags SF, ZF, AF, PF acquire indefinite state.

The MUL command can be applied to binary integers and to unpacked decimal bytes. Packed decimal operands must be unpacked beforehand. Product of unpacked decimal bytes needs to be transformed into unpacked decimal format by AAM command (7.03-03).

First
byte
Second byte Data
bytes
Example
F6(2,6,A)(0-7)0-2 MUL byte ptr [bp+si+ffff]
F6E(0-7) MUL bl
F7(2,6,A)(0-7)0-2 MUL word ptr [bp+si+ffff]
F7E(0-7) MUL bx

7.03-62 NEG – operand's sign reversal

The NEG command (NEG = NEGate) subtracts its operand from zero. Thus the sign of non-zero operands is reversed, but zero operands are left unchanged. Flags (OF, SF, ZF, AF, PF, CF) acquire new states according to the result.

First
byte
Second byte Data
bytes
Example
F6(1,5,9)(8-F)0-2NEG byte ptr [bp+si+ffff]
F6D(8-F) NEG bl
F7(1,5,9)(8-F)0-2NEG word ptr [bp+si+ffff]
F7D(8-F) NEG bx

7.03-63 NOP – a void operation

Though the NOP command (NOP = No operation) is known to do nothing, it in fact increments IP (instruction pointer) by 1, so since that IP points at the next command.

CodeExample
90NOP

7.03-64 NOT – inversion of operand's bits

The NOT command subjects to logical NOT operation every bit in its operand. States of flags are not altered by NOT command.

First
byte
Second byte Data
bytes
Example ("aaaa" – target offset)
F6(1,5,9)(0-7)0-2NOT byte ptr [bp+si+ffff]
F6D(0-7) NOT bl
F7(1,5,9)(0-7)0-2NOT word ptr [bp+si+ffff]
F7D(0-7) NOT bx

7.03-65 OR – logical OR operation

The OR command analyses pairs of corresponding bits in two operands. If TRUE state is set in at least one bit in a pair, then corresponding bit of the result is set to TRUE state too. If both bits in a pair are cleared to FALSE state, then corresponding bit of the result is also cleared to FALSE state. Result replaces the first operand. Flags SF, ZF, PF acquire new states according to the result. Flags CF and OF are cleared to states NC (No Carry) and NV (No oVerflow) respectively. Flag AF acquires indefinite state.

First
byte
Second byte Data
bytes
Example
08(0-B)(0-F)0-2OR [bp+si+ffff],bl
08(C-F)(0-F) OR bl,bl
09(0-B)(0-F)0-2OR [bp+si+ffff],bx
09(C-F)(0-F) OR bx,bx
0A(0-B)(0-F)0-2OR bl,[bp+si+ffff]
0B(0-B)(0-F)0-2OR bx,[bp+si+ffff]
0C 1OR AL,ff
0D 2OR AX,ffff
80(0,4,8)(8-F)1-3OR byte ptr [bp+si+ffff],ff
80C(9-F)1OR bl,ff
81(0,4,8)(8-F)2-4OR word ptr [bp+si+ffff],ffff
81C(9-F)2OR bx,ffff
83(0,4,8)(8-F)1-3OR word ptr [bp+si+ffff],±7f
83C(9-F)1OR bx,±7f
Notes
  1. Codes "0(A,B) (C-F)(0-F)" and "82 (0,4,8,C)(8-F)" are also unassembled by DEBUG.EXE as OR command.
  2. When OR command is applied to equal operands, these operands wouldn't be changed. For example, OR AX,AX command is often used just to set flags.

7.03-66 OUT – data output to port

While performing the OUT command, CPU generates a signal, which switches CPU's buses from memory to I/O ports. First operand of OUT command specifies target port address either explicitly as a double-digit hexadecimal number, or indirectly, as contents of DX register. The second operand of OUT command defines data source register: a byte register AL, if a byte is to be sent, or a double-byte register AX, if a word is to be sent. States of CPU's flags are not altered by OUT command.

First
byte
Second byte Data
bytes
Example
E6 1OUT ff,AL
E7 1OUT ff,AX
EE  OUT DX,AL
EF  OUT DX,AX
Notes
  1. Selected port addresses are shown in appendix A.14-1. Direct forms of OUT command don't allow port addresses above FFh. Indirect addressing via DX register is not subjected to this restriction.
  2. The OUT command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).

7.03-67 POP – data ejection out of stack

The POP command copies a data word (2 bytes) from stack's top into specified register or memory cell, and then shifts stack's top by incrementing offset in SP register (stack pointer) by 2. States of flags are not altered by POP command.

First
byte
Second byte Data
bytes
ExampleComments
07  POP ES
0FA1 DB 0F A1= POP FS
0FA9 DB 0F A9= POP GS
17  POP SS
1F  POP DS
5(8-F)  POP bx
8F(0,8)(0-7)0-2POP [bp+si+ffff]
Notes
  1. Commands popping data from stack into FS and GS segment registers are not "known" to DEBUG.EXE, but may be entered by DB instruction (7.01-01). DEBUG.EXE can't unassemble codes of these commands. Nevertheless DEBUG.EXE enables to debug programs with such codes, if programs are executed by a 32-bit processor.
  2. Codes "8F (C-F)(0-F)" are unassembled by DEBUG.EXE as "POP bx".

7.03-68 POPF – restoration of flag's states out of stack

The POPF command copies data word (2 bytes) from stack's top into flags register, and then shifts stack's top by incrementing offset in SP register (stack pointer) by 2. Flags acquire new states, defined by bits of the ejected data word.

CodeExample
9DPOPF
Notes
  1. The POPF command can't alter states in I/O privilege level field (bits 0Ch and 0Dh) of flags register (A.11-4), if current program is executed at any non-highest privilege level.
  2. The POPF command can't alter state of IF flag, if privilege level of current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).
  3. Being preceded by operand size override prefix 66h (7.02-06), the POPF command pops 4 bytes from stack into extended 32-bit flags register. However, this way of access to V86 mode flag is blocked by hardware (more about that in notes 4 and 5 to A.11-4).

7.03-69 PUSH – copying of a data word into stack.

The PUSH command decrements SP register (stack pointer) by 2, thus extending stack by two memory cells for new data. Then data are copied into these memory cells from that source, which is defined by operand of PUSH command. States of flags are not altered by PUSH command.

First
byte
Second byte Data
bytes
ExampleComments
06  PUSH ES
0E  PUSH CS
0FA0 DB 0F A0= PUSH FS
0FA8 DB 0F A8= PUSH GS
16  PUSH SS
1E  PUSH DS
5(0-7)  PUSH bx
68 2DB 68 ff ff= PUSH ffff
6A 1DB 6A ff= PUSH 00ff
FF(3,7,B)(0-7)0-2PUSH [bp+si+ffff]
Notes
  1. Commands pushing explicit integers and segment addresses from FS and GS registers are not "known" to DEBUG.EXE, but may be entered as data by DB instruction (7.01-01). DEBUG.EXE can't unassemble codes of these commands. Nevertheless DEBUG.EXE enables to debug programs with such codes, if programs are executed by a 32-bit processor.
  2. It is recommended to avoid PUSH SP operation. Obsolete CPUs first decrement SP, and then copy its value. Most modern CPUs store original SP contents. Therefore in some computers PUSH SP operation may cause unpredictable program's behavior.
  3. Code "FF F(0-7)" is also unassembled by DEBUG.EXE as "PUSH bx".

7.03-70 PUSHF – copying of flag's states into stack

The PUSHF command (PUSHF = PUSH Flags) copies two bytes from flags register into stack, just as PUSH command (7.03-69) copies a data word. All peculiarities of execution are the same.

CodeExample
9CPUSHF
Notes
  1. Being preceded by operand size override prefix 66h (7.02-06), the PUSHF command copies into stack 4 bytes from extended 32-bit flags register.[Note 4 to A.11-4] However, copying of the V86 mode flag state by PUSHF command is blocked by hardware.

7.03-71 RCL – leftward shift through carry flag

The RCL command (RCL = Rotate through Carry Leftward) arranges a circular shift of its first operand through carry flag to the left, towards more significant bit positions. At each step the most significant bit becomes the state of CF flag, while the least significant bit of operand acquires previous state of CF flag. State of OV flag may be altered too, but other flags preserve their former states.

The second operand (1 or CL) defines the number of shift steps to the left. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(1,5,9)(0-7)0-2RCL byte ptr [bp+si+ffff],1
D0D(0-7) RCL bl,1
D1(1,5,9)(0-7)0-2RCL word ptr [bp+si+ffff],1
D1D(0-7) RCL bx,1
D2(1,5,9)(0-7)0-2RCL byte ptr [bp+si+ffff],CL
D2D(0-7) RCL bl,CL
D3(1,5,9)(0-7)0-2RCL word ptr [bp+si+ffff],CL
D3D(0-7) RCL bx,CL

7.03-72 RCR – shift through carry flag to the right

The RCR command (RCR = Rotate through Carry to the Right) arranges a circular shift of its first operand through carry flag to the right, towards less significant bit positions. At each step the least significant bit becomes the state of CF flag, while the most significant bit of operand acquires previous state of CF flag. State of OV flag may be altered too, but other flags preserve their former states.

The second operand (1 or CL) defines the number of shift steps to the right. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(1,5,9)(8-F)0-2RCR byte ptr [bp+si+ffff],1
D0D(8-F) RCR bl,1
D1(1,5,9)(8-F)0-2RCR word ptr [bp+si+ffff],1
D1D(8-F) RCR bx,1
D2(1,5,9)(8-F)0-2RCR byte ptr [bp+si+ffff],CL
D2D(8-F) RCR bl,CL
D3(1,5,9)(8-F)0-2RCR word ptr [bp+si+ffff],CL
D3D(8-F) RCR bx,CL

7.03-73 RET – return within the same segment

The RET command performs a return to the caller program from subroutines, which are present inside the same code segment and are called by CALL command with doublebyte target address (for those called by CALL FAR command with 4-byte target address the RETF command must be used, 7.03-74).

The RET command implies that the top stack register contains return offset, i.e. offset of the next command in the caller program. Operand for RET command is not needed, if terminating subroutine leaves nothing in stack. However, subroutines may accept parameters via stack, and at the moment of termination these parameters must be deleted. Therefore operand of RET command defines the number of bytes, which are to be deleted from stack. The RET command pops return offset from stack into IP (instruction pointer) register, and then adds its operand to contents of SP (stack pointer) register. Thus a return is executed to the caller program, and original position of stack's top is restored. States of flags are not altered by RET command.

First
byte
Second byte Data
bytes
Example
C2 2RET ffff
C3  RET
Notes
  1. Stack's top position can be preserved at return offset, if return is executed by RET FFFE command.
  2. If code, assembled by DEBUG.EXE, is to be executed inside debugger's environment, then the RET command may be used to terminate execution of this code (example in 9.02-03).

7.03-74 RETF – return from another segment

The RETF command (RETF = RETurn Far) performs all operations of RET command (7.03-73) and, besides that, restores segment address in CS register from stack. Therefore a return to the caller program from other code segment is implemented.

The RETF command is used as exit in those subroutines and drivers, which are called from other code segments by CALL FAR command (7.02-08) with full 4-byte address, so that original segment address of the caller program is saved in stack.

First
byte
Second byte Data
bytes
Example
CA 2RETF ffff
CB  RETF

7.03-75 ROL – circular shift leftward

The ROL command (ROL = ROtate Leftward) arranges a circular shift of its first operand to the left, towards more significant bit positions. At each step the least significant bit acquires the "ejected" former state of the most significant bit. States of CF and OV flags are altered according to result, but all other flags preserve their former states.

The second operand (1 or CL) defines the number of shift steps to the left. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(0,4,8)(0-7)0-2ROL byte ptr [bp+si+ffff],1
D0C(0-7) ROL bl,1
D1(0,4,8)(0-7)0-2ROL word ptr [bp+si+ffff],1
D1C(0-7) ROL bx,1
D2(0,4,8)(0-7)0-2ROL byte ptr [bp+si+ffff],CL
D2C(0-7) ROL bl,CL
D3(0,4,8)(0-7)0-2ROL word ptr [bp+si+ffff],CL
D3C(0-7) ROL bx,CL

7.03-76 ROR – circular shift to the right

The ROR command (ROR = ROtate to the Right) arranges a circular shift of its first operand to the right, towards less significant bit positions. At each step the most significant bit acquires the "ejected" former state of the least significant bit. States of CF and OV flags are altered according to result, but all other flags preserve their former states.

The second operand (1 or CL) defines the number of shift steps to the right. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(0,4,8)(8-F)0-2ROR byte ptr [bp+si+ffff],1
D0C(8-F) ROR bl,1
D1(0,4,8)(8-F)0-2ROR word ptr [bp+si+ffff],1
D1C(8-F) ROR bx,1
D2(0,4,8)(8-F)0-2ROR byte ptr [bp+si+ffff],CL
D2C(8-F) ROR bl,CL
D3(0,4,8)(8-F)0-2ROR word ptr [bp+si+ffff],CL
D3C(8-F) ROR bx,CL

7.03-77 SAHF – copying AH into flags register

The SAHF command (SAHF = Store AH in Flags) copies a byte from AH register into lower part of flags register. Bit 7 will define the state of SF (sign flag), bit 6 – the state of ZF (zero flag), bit 4 – the state of AF (auxiliary flag), bit 2 – the state of PF (parity flag) and bit 0 – the state of CF (carry flag). Though SAHF command doesn't define the state of overflow flag OF, nevertheless the latter may acquire indefinite state. Bits 5, 3, 1 in AH register don't correspond to real flags, their states are ignored.

CodeExample
9ESAHF

7.03-78 SAR – signed integer shift to the right

The SAR command (SAR = Shift Arithmetic to the Right) shifts its signed integer operand to the right, towards less significant bit positions. At each shift's step the state of the rightmost bit becomes lost, and the leftmost bit acquires state of sign bit. Flags ZF, PF, CF acquire new states according to the result. Flags OF and AF acquire indefinite state.

The second operand (1 or CL) defines the number of shift steps to the right. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(3,7,B)(8-F)0-2SAR byte ptr [bp+si+ffff],1
D0F(8-F) SAR bl,1
D1(3,7,B)(8-F)0-2SAR word ptr [bp+si+ffff],1
D1F(8-F) SAR bx,1
D2(3,7,B)(8-F)0-2SAR byte ptr [bp+si+ffff],CL
D2F(8-F) SAR bl,CL
D3(3,7,B)(8-F)0-2SAR word ptr [bp+si+ffff],CL
D3F(8-F) SAR bx,CL

7.03-79 SBB – binary integers subtraction with borrow

The SBB command subtracts its second operand (the substrahend) from the first operand (the minuend), taking into account the borrow, left after previous operation and represented by state of CF (carry flag). Remainder replaces the first operand. Flags OF, SF, ZF, AF, PF, CF acquire new states according to the result.

Interpretation of flag's states, left by SBB command, depends on whether the operands were signed or unsigned numbers. Conditional jump commands JA, JB, JBE, JNB should be used after subtraction of unsigned numbers. Other conditional jump commands JG, JGE, JL, JLE should be used after subtraction of signed numbers. Full names of all conditional jump and loop commands reflect status relation of the first (left) operand of SBB command to the second (right) operand. For example, JA = "jump if above" means that the left operand of SBB command (the minuend) must be above, or greater than the right operand (the subtrahend).

SBB is a binary operation, but there are two exceptions. If the first operand is in AX register, then SBB command may be applied to unpacked decimal words: binary difference of unpacked decimal words in AX register can be transformed into valid unpacked decimal word by AAS command (7.03-04). If the first operand is in AL register, then SBB command may be applied to packed decimal bytes: binary difference of packed decimal bytes in AL register can be transformed into valid packed decimal byte by DAS command (7.03-19).

First
byte
Second byte Data
bytes
Example
18(0-B)(0-F)0-2SBB [bp+si+ffff],bl
18(C-F)(0-F) SBB bl,bl
19(0-B)(0-F)0-2SBB [bp+si+ffff],bx
19(C-F)(0-F) SBB bx,bx
1A(0-B)(0-F)0-2SBB bl,[bp+si+ffff]
1B(0-B)(0-F)0-2SBB bx,[bp+si+ffff]
1C 1SBB AL,ff
1D 2SBB AX,ffff
80(1,5,9)(8-F)1-3SBB byte ptr [bp+si+ffff],ff
80D(9-F)1SBB bl,ff
81(1,5,9)(8-F)2-4SBB word ptr [bp+si+ffff],ffff
81D(9-F)2SBB bx,ffff
83(1,5,9)(8-F)1-3SBB word ptr [bp+si+ffff],±7f
83D(9-F)1SBB bx,±7f
Notes
  1. Codes "1(A,B) (C-F)(0-F)" and "82 (1,5,9,D)(8-F)" are also unassembled by DEBUG.EXE as SBB command.

7.03-80 SCASB – search for a particular byte

Though the name SCASB stands for "SCAn a String of Bytes", the SCASB command in fact compares a byte in AL register with another byte, read out of memory. Address of that another byte must be loaded beforehand into ES:DI pair of registers. If bytes are equal, ZF (zero flag) is set to ZR state. If bytes are not equal, ZF flag is cleared to NZ (No Zero) state. Flags OF, SF, AF, PF, CF acquire the states according to difference between compared bytes, but this difference itself is not saved.

After comparison offset in DI (destination index) register is incremented by 1 or decremented by 1: it depends on the state ("UP" or "DN") of direction flag DF. The state of DF flag can be altered by CLD (7.03-11) and STD (7.03-85) commands. Automatic change of index register contents prepares conditions for comparison of AL contents with a byte in the next memory cell.

The SCASB command is often preceded by repetition prefixes F2h (7.02-03) or F3h (7.02-04), which enable to execute it cyclically and thus search for a particular byte in a string of bytes. Default segment register ES for that string of bytes can't be altered by segment override prefixes.

CodeExample
AESCASB
Notes
  1. When SCASB command is preceded by repetition prefixes F2h or F3h, the order of operations within the cycle includes assignment of flags states, then incrementation (or decrementation) of index register's contents, and after that cycle termination condition check. Therefore, the offset in DI register at the moment of cycle termination is pointing not to that data byte, which has caused cycle termination, but rather to the next byte.

7.03-81 SCASW – search for a particular word

The SCASW command (SCASW = SCAn a String of Words) compares a word in AX register with another word, read out of memory, and then increments (or decrements) offset of that another word in DI index register by 2, thus preparing comparison of AX contents with a word in next memory cells. The operand size override prefix 66h (7.02-06) forces SCASW command to compare a four-byte operand in EAX register with other operand of the same DWORD type, and to increment (or decrement) offset in DI index register by 4. All other peculiarities of SCASW command execution are the same as those for SCASB command (7.03-80).

CodeExample
AFSCASW

7.03-82 SHL – Shift to the left

The SHL command shifts its first operand step-by-step to the left, towards more significant bit positions. At each step the state of the most significant bit becomes shifted into carry flag CF, and the least significant bit acquires zero (cleared) state. Flags SF, ZF, PF acquire new states according to the result. Flags AF and OV acquire indefinite state.

The second operand (1 or CL) defines the number of shift steps to the left. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(2,6,A)(0-7)0-2SHL byte ptr [bp+si+ffff],1
D0E(0-7) SHL bl,1
D1(2,6,A)(0-7)0-2SHL word ptr [bp+si+ffff],1
D1E(0-7) SHL bx,1
D2(2,6,A)(0-7)0-2SHL byte ptr [bp+si+ffff],CL
D2E(0-7) SHL bl,CL
D3(2,6,A)(0-7)0-2SHL word ptr [bp+si+ffff],CL
D3E(0-7) SHL bx,CL
C0E(0-7)1see note 2
C1E(0-7)1see note 2
Notes
  1. SHL command is an exact equivalent of SAL command, accepted by other assemblers, but DEBUG.EXE doesn't accept the SAL name.
  2. ^ a b Since CPU model 80286, processors execute SHL command with explicit specification of shift steps. This form of SHL command is not known to DEBUG.EXE, but may be entered as data by DB instruction (7.01-01). For example, machine codes of 4-step shift to the left may look as
    C0 E0 04 = SHL AL,4
    C1 E0 04 = SHL AX,4
    
    The last byte defines the number of shift steps. In order to apply shift to other register you have to add to the second byte (E0h) the number (00h–07h) of the desired register in lists, presented in first and second lines of table 7.00.

7.03-83 SHR – shift to the right

The SHR command shifts its first operand step-by-step to the right, towards less significant bit positions. At each step the state of the least significant bit becomes shifted into carry flag CF, and the most significant bit acquires zero (cleared) state. Flags SF, ZF, PF acquire new states according to the result. Flags AF and OV acquire indefinite state.

The second operand (1 or CL) defines the number of shift steps to the left. When the number of shift steps is read from CL register, only 5 less significant bits are taken into account; hence maximum number of shift steps is 31. The preset number of shift steps in CL register is preserved intact.

First
byte
Second byte Data
bytes
Example
D0(2,6,A)(8-F)0-2SHR byte ptr [bp+si+ffff],1
D0E(8-F) SHR bl,1
D1(2,6,A)(8-F)0-2SHR word ptr [bp+si+ffff],1
D1E(8-F) SHR bx,1
D2(2,6,A)(8-F)0-2SHR byte ptr [bp+si+ffff],CL
D2E(8-F) SHR bl,CL
D3(2,6,A)(8-F)0-2SHR word ptr [bp+si+ffff],CL
D3E(8-F) SHR bx,CL
C0E(8-F)1see note 1
C1E(8-F)1see note 1
Notes
  1. ^ a b Since CPU model 80286, processors execute SHR command with explicit specification of shift steps. This form of SHL command is not known to DEBUG.EXE, but may be entered as data by DB instruction (7.01-01). For example, machine codes of 4-step shift to the right may look as
    C0 E8 04 = SHR AL,4
    C1 E8 04 = SHR AX,4
    
    The last byte defines the number of shift steps. In order to apply shift to other register you have to add to the second byte (E0h) the number (00h–07h) of the desired register in lists, presented in first and second lines of table 7.00.

7.03-84 STC – set carry flag

The STC command sets carry flag CF to the "CY" (CarrY) state, which is often referred to as CF=1.

CodeExample
F9STC

7.03-85 STD – set direction flag

The STD command sets direction flag DF into its non-default state "DN". This means descending direction of offset count in index registers (DI and/or SI) during execution of string operations (CMPSB, LODSB, MOVSB, SCASB, STOSB, etc.).

CodeExample
FDSTD

7.03-86 STI – set interrupt flag

The STI command sets interrupt flag IF into its default "EI" (= Enable Interrupts) state, thus enabling intake of interrupt requests via interrupt controller.

CodeExample
FBSTI
Notes
  1. The STI command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).

7.03-87 STOSB – filling memory with a byte

Though the name STOSB stands for "STOre String of Bytes", the STOSB command in fact copies a byte from AL register into a memory cell. Address of that memory cell must be loaded beforehand into ES:DI pair of registers. States of flags are not altered by STOSB command.

After copying offset in DI (destination index) register is incremented by 1 or decremented by 1: it depends on the state ("UP" or "DN") of direction flag DF. The state of DF flag can be altered by CLD (7.03-11) and STD (7.03-85) commands. Automatic change of index register contents prepares conditions for copying a byte from AL register into the next memory cell. The STOSB command is often preceded by repetition prefixes F2h (7.02-03) or F3h (7.02-04), which enable to execute it cyclically and thus fill a succession of memory cells with copies of the same byte. Default segment register ES for these memory cells can't be altered by segment override prefixes.

CodeExample
AASTOSB

7.03-88 STOSW – filling memory with a word

The STOSW command (STOSW = STOre String of Words) copies a word from AX register into memory according to address in ES:DI pair of registers and then increments (or decrements) offset in DI index register by 2, thus preparing copying of AX contents in next memory cells. Operand size override prefix 66h (7.02-06) forces STOSW command to copy a four-byte operand of DWORD type from EAX register and to increment (or decrement) offset in DI index register by 4. All other peculiarities of STOSW command execution are the same as those for STOSB command (7.03-87).

CodeExample
ABSTOSW

7.03-89 SUB – binary integers subtraction

The SUB command subtracts its second operand (the subtrahend) from the first operand (the minuend), ignoring the state of CF flag (the borrow). Remainder replaces the first operand. Flags OF, SF, ZF, AF, PF, CF acquire new states according to the result.

Interpretation of flag's states, left by SUB command, depends on whether the operands were signed or unsigned numbers. Conditional jump commands JA, JB, JBE, JNB should be used after subtraction of unsigned numbers. Other conditional jump commands JG, JGE, JL, JLE should be used after subtraction of signed numbers. Full names of all conditional jump and loop commands reflect status relation of the first (left) operand of SUB command to the second (right) operand. For example, JA = "jump if above" means that the left operand of SUB command (the minuend) must be above, or greater than the right operand (the subtrahend).

SUB is a binary operation, but there are two exceptions. If the first operand is in AX register, then SUB command may be applied to unpacked decimal words: binary difference of unpacked decimal words in AX register can be transformed into valid unpacked decimal word by AAS command (7.03-04). If the first operand is in AL register, then SUB command may be applied to packed decimal bytes: binary difference of packed decimal bytes in AL register can be transformed into valid packed decimal byte by DAS command (7.03-19).

First
byte
Second byte Data
bytes
Example
28(0-B)(0-F)0-2SUB [bp+si+ffff],bl
28(C-F)(0-F) SUB bl,bl
29(0-B)(0-F)0-2SUB [bp+si+ffff],bx
29(C-F)(0-F) SUB bx,bx
2A(0-B)(0-F)0-2SUB bl,[bp+si+ffff]
2B(0-B)(0-F)0-2SUB bx,[bp+si+ffff]
2C 1SUB AL,ff
2D 2SUB AX,ffff
80(2,6,A)(8-F)1-3SUB byte ptr [bp+si+ffff],ff
80E(9-F)1SUB bl,ff
81(2,6,A)(8-F)2-4SUB word ptr [bp+si+ffff],ffff
81E(9-F)2SUB bx,ffff
83(2,6,A)(8-F)1-3SUB word ptr [bp+si+ffff],±7f
83E(9-F)1SUB bx,±7f
Notes
  1. Codes "2(A,B) (C-F)(0-F)" and "82 (2,6,A,E)(8-F)" are also unassembled by DEBUG.EXE as SUB command.

7.03-90 TEST – logical test of bit's states

The TEST command sets flags SF (sign flag), ZF (zero flag) and PF (parity flag) according to result of logical AND bit-to-bit operation upon operands, but this result itself is not saved. Both operands of TEST command remain intact. Carry flag CF and overflow flag OF are cleared by TEST command to states NC (No Carry) and NV (No Overflow) correspondingly. AF flag acquires indefinite state.

First
byte
Second byte Data
bytes
Example
84(0-B)(0-F)0-2TEST [bp+si+ffff],bl
84(C-F)(0-F) TEST bl,bl
85(0-B)(0-F)0-2TEST [bp+si+ffff],bx
85(C-F)(0-F) TEST bx,bx
A8 1TEST AL,ff
A9 2TEST AX,ffff
F6(0,4,8)(0-7)1-3TEST byte ptr [bp+si+ffff],ff
F6C(1-7)1TEST bl,ff
F7(0,4,8)(0-7)2-4TEST word ptr [bp+si+ffff],ffff
F7C(1-7)2TEST bx,ffff

7.03-91 XCHG – operands exchange

The XCHG command exchanges contents between specified registers or between a memory cell and a register. States of flags are not altered by XCHG command.

First
byte
Second byte Data
bytes
Example
86(0-B)(0-F)0-2XCHG [bp+si+ffff],bl
86(C-F)(1-7,9-F) XCHG bl,bl
87(0-B)(0-F)0-2XCHG [bp+si+ffff],bx
87(C-F)(1-7,9-F) XCHG bx,bx
 9(1-7) XCHG bx,AX

7.03-92 XLAT – tabular translation

The XLAT command calculates a sum (AL + BX) and then copies a byte from DS:(AL + BX) address into AL register, replacing its former contents. States of flags and contents of BX register are not altered by XLAT command.

XLAT command is used for translation of codes via a code table (up to 256 bytes long), which must be loaded beforehand starting at DS:BX address and on. Another segment register may be referred instead of the default segment register DS, if XLAT command is preceded by an appropriate segment override prefix (7.02-01).

CodeExample
D7XLAT

7.03-93 XOR – exclusive OR logical operation

The XOR command analyses pairs of corresponding bits in two operands. If states of both bits in a pair are identical (both set or both cleared), then corresponding bit of the result is cleared to FALSE (zero). If states of bits in an analyzed pair are different, then corresponding bit of the result is set to TRUE state. Result replaces the first operand.

Flags SF, ZF, PF acquire new states according to the result. Flags CF and OF are cleared to states NC (No Carry) and NV (No oVerflow) respectively. Flag AF acquires indefinite state.

First
byte
Second byte Data
bytes
Example
30(0-B)(0-F)0-2XOR [bp+si+ffff],bl
30(C-F)(0-F) XOR bl,bl
31(0-B)(0-F)0-2XOR [bp+si+ffff],bx
31(C-F)(0-F) XOR bx,bx
32(0-B)(0-F)0-2XOR bl,[bp+si+ffff]
33(0-B)(0-F)0-2XOR bx,[bp+si+ffff]
34 1XOR AL,ff
35 2XOR AX,ffff
80(3,7,B)(0-7)1-3XOR byte ptr [bp+si+ffff],ff
80F(1-7)1XOR bl,ff
81(3,7,B)(0-7)2-4XOR word ptr [bp+si+ffff],ffff
81F(1-7)2XOR bx,ffff
83(3,7,B)(0-7)1-3XOR word ptr [bp+si+ffff],±7f
83F(1-7)1XOR bx,±7f
Notes
  1. The XOR command with specifications of the same source as each of two operands is often used in order to clear that source to zero.
  2. Codes "3(2,3) (C-F)(0-F)" and "82 (3,7,B,F)(0-7)" are also unassembled by DEBUG.EXE as XOR command.

7.04 Commands for arithmetic coprocessor

Those assembler's commands, whose name begins with letter "F" (float), are transferred for execution to math coprocessor. All modern computers are able to perform these commands, because their coprocessor is integrated in the main CPU.

Computers with obsolete processors, including some 486 models, may have no math coprocessor. Then execution of coprocessor's commands may be performed by software emulation, but for that two conditions must be met :

  • first, generation of a call for INT 07 handler (8.01-08) must be ensured in response to each coprocessor's command. This is achieved by setting bit 02h ("coprocessor emulation") in control register CR0 (A.11-4).
  • second, an appropriate INT 07 handler must be loaded, which is able to emulate execution of coprocessor's commands.

In some old computers both these conditions are met automatically due to their BIOS system, in some others the user has to bother about that. In any case presence or absence of arithmetical coprocessor is reported by INT 11 handler (8.01-36, A.11-1).

If there is a chance that your program will be executed by old CPUs with a separate coprocessor chip, then each coprocessor's command should be preceded by WAIT prefix (7.02-05), which synchronizes command's transfer from CPU to coprocessor. Modern CPUs have an integrated coprocessor with hardware synchronizing means. Therefore for modern CPUs the WAIT prefix is not needed, its presence is allowed, but most probably is ignored.

7.04-01 F2XM1 – approximation for fractional power of 2

The F2XM1 command calculates a sum of series, used for fractional power of 2 function approximation within limits of power index from 1 to +1. The power index is implied to be prepared in coprocessor's top stack register ST(0). Calculated sum of series replaces power index in ST(0) register. Final result can be expressed by formula ST(0) = 1+2^ST(0)

CodeExample
D9 F0F2XM1

7.04-02 FABS – absolute value

The FABS command clears sign bit in coprocessor's top stack register ST(0) to zero, thus making the operand in ST(0) a positive value.

CodeExample
D9 E1FABS

7.04-03 FADD – addition of real values

The FADD command adds a real value being read out of memory or from any coprocessor's register, if it is specified as the second operand, to another real value in coprocessor's top stack register ST(0) or in any other register ST(1-7), if the latter is specified as the first operand. The sum replaces former value in ST(0) or in ST(1-7), if it is specified as the first operand.

First
byte
Second byte Data
bytes
Example
D8(0,4,8)(0-7)0-2FADD dword ptr [bp+si+ffff]
D8C(0-7) FADD ST,ST(0-7)
DC(0,4,8)(0-7)0-2FADD qword ptr [bp+si+ffff]
DCC(0-7) FADD ST(1-7),ST

7.04-04 FADDP – addition and stack shift up

The FADDP command (FADDP = "ADD and Pop") adds its second operand in coprocessor's top stack register ST(0) to the first operand in any other specified stack register ST(1-7). The sum replaces former value in specified ST(1-7) stack register, and then coprocessor's stack pointer is incremented by 1, so that access to former ST(0) is lost, and all other stack registers ST(1-7), including the one where the sum has been stored, become renamed into ST(0-6), as though their number is decremented by 1.

CodeExample
DE C(0-7)FADDP ST(1-7),ST

7.04-05 FBLD – loading with binary transform

The FBLD command (FBLD = "Binary LoaD") reads from memory, starting at specified address, a 10-byte packed decimal integer, containing two decimal digits per byte. This decimal integer is transformed to real binary value and is loaded into coprocessor's register ST(7), which must be empty at that moment. Then FBLD command decrements coprocessor's stack pointer by 1 ; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded value is found in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DF(2,6,A)(0-7)0-2FBLD tbyte ptr [bp+si+ffff]
Notes
  1. The FBLD command doesn't check whether its operand is really a packed decimal number or not. If not, the result of performed binary transform is invalid.
  2. The default 10-byte binary real format includes mantissa (bits 0–63), power index (bits 64–78) and sign bit 79. By changing contents of precision control field in CWR register (note 2 to 7.04-35) coprocessor may be turned to 8-byte double precision format (52 bits – mantissa, 11 bits – power index) or to 4-byte single precision format (23 bits – mantissa, 8 bits – power index).

7.04-06 FBSTP – store with decimal transform

The FBSTP command (FBSTP = Binary STore and Pop) transforms a real binary value in coprocessor's top stack register ST(0) into a 10-byte packed decimal integer, containing two decimal digits per byte. Transform includes rounding of fractional part. Transformed integer is written into memory, starting from specified offset and on. Then FBSTP command increments coprocessor's stack pointer by 1, so that access to former ST(0) is lost, and registers ST(1-7) become renamed into ST(0-6).

First
byte
Second byte Data
bytes
Example
DF(3,7,B)(0-7)0-2FBSTP tbyte ptr [bp+si+ffff]

7.04-07 FCHS – change sign

The FCHS command reverses sign of real binary value in coprocessor's top stack register ST(0).

CodeExample
D9 E0FCHS

7.04-08 FCLEX – clear exceptions flags

The FCLEX command clears those bits in coprocessor's status word register SWR, which are used as flags to register coprocessor's states and exceptions. In particular, the following bits are cleared :

bit 0– flag of invalid operation
bit 1– flag of denormalized operand
bit 2– flag of division by zero
bit 3– coprocessor's overflow flag
bit 4– antioverflow (lost result) flag
bit 5– flag of lost precision
blt 7– interrupt request flag
bit 15– "coprocessor busy" flag
CodeExample
DB E2FCLEX

7.04-09 FCOM – comparison of real values

The FCOM command compares a real value in coprocessor's top stack register ST(0) with contents of specified register or memory cells. Flags C0, C2 and C3 in coprocessor's register SWR acquire new states according to the result. First the state of C2 flag should be checked: C2 = 1 marks uncomparable operands, so that there is no sense in further checks. If operands are equal, then C3 = 1. If value in ST(0) is less than the other operand, then C0 = 1. The way of performing checks for flags C0, C2 and C3 in coprocessor's SWR register is described in article 7.04-64.

First
byte
Second byte Data
bytes
Example
D8(1,5,9)(0-7)0-2FCOM dword ptr [bp+si+ffff]
D8D(0-7) FCOM ST(0-7)
DC(1,5,9)(0-7)0-2FCOM qword ptr [bp+si+ffff]
Notes
  1. Code "DC D(0-7)" is also unassembled by DEBUG.EXE as FCOM ST(0-7).

7.04-10 FCOMP – compare and shift stack up

The FCOMP command performs comparison just as FCOM operation does (7.04-09), but then increments coprocessor's stack pointer by 1, so that access to former ST(0) is lost, and registers ST(1-7) become renamed into ST(0-6).

First
byte
Second byte Data
bytes
Example
D8(1,5,9)(8-F)0-2FCOMP dword ptr [bp+si+ffff]
D8D(8-F) FCOMP ST(0-7)
DC(1,5,9)(8-F)0-2FCOMP qword ptr [bp+si+ffff]
Notes
  1. Codes "DC D(8-F)" and "DE D(0-7)" are also unassembled by DEBUG.EXE as FCOMP command.

7.04-11 FCOMPP – compare and twice shift stack up

The FCOMPP command compares operands in coprocessor's stack registers ST(0) and ST(1) and then increments coprocessor's stack pointer by 2, so that access to former operands in both ST(0) and ST(1) is lost. Registers ST(2-7) become renamed into ST(0-5). The result of comparison affects states of flags C0, C2 and C3 in coprocessor's SWR register, just as it is done after FCOM command (7.04-09).

CodeExample
DE D9FCOMPP
Notes
  1. DEBUG.EXE unassembles code "DE D9" as "FCOMPP ST(1)", but while assembling doesn't accept the "ST(1)".

7.04-12 FDECSTP – DECrement Stack Top Pointer

The FDECSTP command decrements coprocessor's stack pointer by 1, so that registers ST(0-6) are renamed into ST(1-7). The last stack register ST(7) is renamed into ST(0). All stack register's contents remain accessible and are not altered.

CodeExample
D9 F6FDECSTP
Notes
  1. Coprocessor's stack pointer is a tree-stage reversible counter, involving bits 11, 12 and 13 of status word register SWR.
  2. Most commands pushing data into coprocessor's stack are performed by copying data into ST(7) register and decrementing coprocessor's stack pointer by 1, so that ST(7) register becomes renamed into ST(0). All such commands can't be performed, if ST(7) register originally isn't empty.

7.04-13 FDISI – disable interrupts

The FDISI command disables interrupts for obsolete 8087 arithmetical coprocessor. Since model 80287 coprocessors don't need this command and ignore it.

CodeExample
DB E1FDISI

7.04-14 FDIV – division of real values

The FDIV command divides a real value in the coprocessor's top stack register ST(0), or in any non-top register ST(1-7), if specified as the first operand, by a real divisor from specified memory cell or other coprocessor's register, if specified as the second operand. The quotient replaces the dividend in ST(0) or in other stack register ST(1-7), if it is specified as the first operand.

First
byte
Second byte Data
bytes
Example
D8(3,7,B)(0-7)0-2FDIV dword ptr [bp+si+ffff]
D8F(0-7) FDIV ST,ST(0-7)
DC(3,7,B)(0-7)0-2FDIV qword ptr [bp+si+ffff]
DCF(8-F) FDIV ST(1-7),ST

7.04-15 FDIVP – divide and shift stack up

The FDIVP – divide a real value in any coprocessor's non-top stack register ST(1-7) with divisor in top stack register ST(0). The quotient replaces the dividend in non-top stack register ST(1-7). Then FDIVP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6), including that one, where the quotient has been written. The ST(0) register is renamed into ST(7) and is announced free. Access to its former contents (the divisor) is lost.

CodeExample
DE F(8-F)FDIVP ST(1-7),ST

7.04-16 FDIVR – divide in reverse order

The FDIVR command divides a real value read out of a memory cell, or from the coprocessor's stack register, if specified as the second operand, by a real divisor in coprocessor's top stack register ST(0), or in any non-top stack register ST(1-7), if specified as the first operand. The quotient replaces divisor in ST(0) register or in non-top stack register ST(1-7), if it is specified as the first operand.

First
byte
Second byte Data
bytes
Example
D8(3,7,B)(8-F)0-2FDIVR dword ptr [bp+si+ffff]
D8F(8-F) FDIVR ST,ST(0-7)
DC(3,7,B)(8-F)0-2 FDIVR qword ptr [bp+si+ffff]
DCF(0-7) FDIVR ST(1-7),ST

7.04-17 FDIVRP – divide in reverse order and shift stack up

The FDIVRP command divides a real value in coprocessor's top stack register ST(0) with divisor in any other stack register ST(1-7), replaces the divisor by the quotient in non-top stack register ST(1-7) and then increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6), including that one, where the quotient has been written. The ST(0) register is renamed into ST(7) and is announced free. Access to its former contents (the dividend) is lost.

CodeExample
DE F(0-7)FDIVRP ST(1-7),ST

7.04-18 FENI – enable interrupts

The FENI command enables interrupts for obsolete 8087 arithmetical coprocessor. Since model 80287 coprocessors don't need this command and ignore it.

CodeExample
DB E0FENI

7.04-19 FFREE – announce register as free

The FFREE command marks specified coprocessor's stack register as free by writing "11" binary value into corresponding bits of coprocessor's tag register TWR.

CodeExample
DD C(0-7)FFREE ST(0-7)
Notes
  1. Code "DF C(0-7)" is also unassembled by DEBUG.EXE as FFREE command.

7.04-20 FIADD – addition with an integer

The FIADD command adds an integer from specified memory cell to a real value in coprocessor's top stack register ST(0). The sum is a real value, replacing the former contents in ST(0) register.

First
byte
Second byte Data
bytes
Example
DA(0,4,8)(0-7)0-2FIADD dword ptr [bp+si+ffff]
DE(0,4,8)(0-7)0-2FIADD word ptr [bp+si+ffff]

7.04-21 FICOM – comparison with an integer

The FICOM command reads an integer from specified memory cell, transforms it into a real value and compares the result with a real value in coprocessor's top stack register ST(0). The comparison itself is performed just as it is done by FCOM command (7.04-09).

First
byte
Second byte Data
bytes
Example
DA(1,5,9)(0-7)0-2FICOM dword ptr [bp+si+ffff]
DE(1,5,9)(0-7)0-2FICOM word ptr [bp+si+ffff]

7.04-22 FICOMP – compare with an integer and shift stack up

The FICOMP command reads an integer from specified memory cell, transforms it into a real value, and compares the result with a real value in coprocessor's top stack register ST(0). The comparison itself is performed just as it is done by FCOM command (7.04-09), but then FICOMP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6). The ST(0) register is renamed into ST(7) and is announced free. Access to its former contents is lost.

First
byte
Second byte Data
bytes
Example
DA(1,5,9)(8-F)0-2FICOMP dword ptr [bp+si+ffff]
DE(1,5,9)(8-F)0-2FICOMP word ptr [bp+si+ffff]

7.04-23 FIDIV – division by an integer

The FIDIV command divides a real value in coprocessor's top stack register ST(0) by an integer divisor, read from specified memory cell. The quotient replaces the dividend in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DA(3,7,B)(0-7)0-2FIDIV dword ptr [bp+si+ffff]
DE(3,7,B)(0-7)0-2FIDIV word ptr [bp+si+ffff]

7.04-24 FIDIVR – integer division in reverse order

The FIDIVR command performs division of integer dividend, read from specified memory cell, by a divisor in coprocessor's top stack register ST(0). The quotient replaces the divisor in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DA(3,7,B)(8-F)0-2FIDIVR dword ptr [bp+si+ffff]
DE(3,7,B)(8-F)0-2FIDIVR word ptr [bp+si+ffff]

7.04-25 FILD – loading of an integer

The FILD command transforms an integer, read from specified memory cell, into a real value and loads this value into coprocessor's stack register ST(7), which must be empty at that moment. Then FILD command decrements coprocessor's stack pointer by 1; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded value is found in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DB(0,4,8)(0-7)0-2FILD dword ptr [bp+si+ffff]
DF(0,4,8)(0-7)0-2FILD word ptr [bp+si+ffff]
DF(2,6,A)(8-F)0-2FILD qword ptr [bp+si+ffff]

7.04-26 FIMUL – multiplication by an integer

The FIMUL command multiplies a real value in coprocessor's top stack register ST(0) by an integer value, read from specified memory cell. The product replaces former value in coprocessor's top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DA(0,4,8)(8-F)0-2FIMUL dword ptr [bp+si+ffff]
DE(0,4,8)(8-F)0-2FIMUL word ptr [bp+si+ffff]

7.04-27 FINCSTP – increment stack top pointer

The FINCSTP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6). The top stack register ST(0) is renamed into ST(7). All stack register's contents remain accessible and are not altered.

CodeExample
D9 F7FINCSTP
Notes
  1. The coprocessor's stack pointer is a tree-stage reversible counter, involving bits 11, 12 and 13 of status word register SWR.
  2. When incrementing of coprocessor's stack pointer is performed by other commands, then top stack register ST(0) after being renamed into ST(7) acquires status of empty register (tag 11b). Therefore access to former contents of ST(0) is lost. This operation is often referred to as popping ST(0) contents out of stack.

7.04-28 FINIT – setting coprocessor's initial state

The FINIT command writes initial states into CWR, SWR, TWR, IPR and DPR registers of arithmetical coprocessor. Control word register CWR acquires the state 037Fh: it defines 80-bit format of operands, masking of all exceptions and rounding to nearest integer. Tags register TWR is set to FFFFh state, which means that all coprocessor's stack registers are free. Other coprocessor's registers (SWR, IPR and DPR) are cleared to 0000h.

CodeExample
DB E3FINIT

7.04-29 FIST – storing of an integer

The FIST command (FIST = Integer STore) reads a real value from coprocessor's top stack register ST(0), translates it into an integer, rounds it according to specified format, and writes the result into specified memory address.

First
byte
Second byte Data
bytes
Example
DB(1,5,9)(0-7)0-2FIST dword ptr [bp+si+ffff]
DF(1,5,9)(0-7)0-2FIST word ptr [bp+si+ffff]

7.04-30 FISTP – store an integer and shift stack up

The FISTP command stores in memory a translated value from ST(0) register, just as FIST command does (7.04-29). Besides that, FISTP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6). Access to former value in top stack register ST(0) becomes lost.

First
byte
Second byte Data
bytes
Example
DB(1,5,9)(8-F)0-2FISTP dword ptr [bp+si+ffff]
DF(1,5,9)(8-F) FISTP word ptr [bp+si+ffff]
DF(3,7,B)(8-F)0-2FISTP qword ptr [bp+si+ffff]

7.04-31 FISUB – subtraction of an integer

The FISUB command subtracts an integer subtrahend, stored in specified memory cell, from a real value — the minuend — in the coprocessor's top stack register ST(0). The remainder replaces minuend in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DA(2,6,A)(0-7)0-2FISUB dword ptr [bp+si+ffff]
DE(2,6,A)(0-7)0-2FISUB word ptr [bp+si+ffff]

7.04-32 FISUBR – subtract in reverse order

The FISUBR command performs reverse order subtraction of a real subtrahend in coprocessor's top stack register ST(0) from an integer minuend, stored in specified memory cell. The remainder replaces former subtrahend in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
DA(2,6,A)(8-F)0-2FISUBR dword ptr [bp+si+ffff]
DE(2,6,A)(8-F)0-2FISUBR word ptr [bp+si+ffff]

7.04-33 FLD – loading of a real value

The FLD command reads a real value from specified register or from specified memory cell and loads this value into coprocessor's stack register ST(7), which must be empty at that moment. Then FLD command decrements coprocessor's stack pointer by 1 ; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded real value is found in top stack register ST(0).

First
byte
Second byte Data
bytes
Example
D9(0,4,8)(0-7)0-2FLD dword ptr [bp+si+ffff]
D9C(0-7) FLD ST(0-7)
DB(2,6,A)(8-F)0-2FLD tbyte ptr [bp+si+ffff]
DD(0,4,8)(0-7)0-2FLD qword ptr [bp+si+ffff]

7.04-34 FLD1 – loading of a unity constant

The FLD1 command loads a unity constant into coprocessor's stack register ST(7), which must be empty at that moment. Then FLD1 command decrements coprocessor's stack pointer by 1; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 E8FLD1

7.04-35 FLDCW – loading of CWR register

The FLDCW command (FLDCW = LoaD Control Word) copies a data word, saved by FSTCW command (7.04-55), from specified memory cell into coprocessor's control word register CWR. If selected bits in this data word have been intentionally altered, then after loading with FLDCW command the changes will come into effect.

First
byte
Second byte Data
bytes
Example
D9(2,6,A)(8-F)0-2FLDCW word ptr [bp+si+ffff]
Notes
  1. Bits 5–0 in CWR register represent masks for exception flags in corresponding bits 5–0 of SWR register (7.04-08). By default masks in CWR register are set, but if a mask is cleared, then occurrence of an exception invokes a request IRQ 13 for interrupt handler INT 75 (8.03-75), which must be able to cope with the problem.
  2. ^ Bits 9 and 8 in CWR register represent PC (= precision control) field. Default state 11b of PC field defines 10-byte format of operands. State 10b of PC field defines rounding to 8-byte format, state 00b – rounding to 4-byte format (7.04-05). However, reduction of precision doesn't make calculations faster.

7.04-36 FLDENV – loading into service registers

The FLDENV (= LoaD ENVironment) restores states of coprocessor's service registers (CWR, SWR, TWR, IPR, DPR) according to a record, which is read from memory starting at specified address. This record must be formed and stored beforehand by FSTENV command (7.04-56).

First
byte
Second byte Data
bytes
Example
D9(2,6,A)(0-7)0-2FLDENV word ptr [bp+si+ffff]

7.04-37 FLDL2E – loading of "log e" constant

The FLDL2E command loads a log e = 1.44269... constant (i.e. base 2 logarithm of e = 2.71828...) into coprocessor's stack register ST(7), which must be empty at that moment. Then FLDL2E command decrements coprocessor's stack pointer by 1 ; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 EAFLDL2E

7.04-38 FLDL2T – loading of "log10" constant

The FLDL2T command loads log10 = 3.32192... constant (i.e. base 2 logarithm of 10) into coprocessor's stack register ST(7), which must be empty at that moment. Then FLDL2T command decrements coprocessor's stack pointer by 1 ; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 E9FLDL2T

7.04-39 FLDLG2 – loading of "lg2" constant

The FLDLG2 command loads lg2 = 0.301029... constant (i.e. base 10 logarithm of 2) into coprocessor's stack register ST(7), which must be empty at that moment. Then FLDLG2 command decrements coprocessor's stack pointer by 1 ; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 ECFLDLG2

7.04-40 FLDLN2 – loading of "ln2" constant

The FLDLN2 command loads ln2 = 0.693147... constant (i.e. base e = 2.71828... logarithm of 2) into coprocessor's stack register ST(7), which must be empty at that moment. Then FLDLN2 command decrements coprocessor's stack pointer by 1 ; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 EDFLDLN2

7.04-41 FLDPI – loading of "PI" constant

The FLDPI command loads PI = 3.14159... constant into coprocessor's stack register ST(7), which must be empty at that moment. Then FLDPI command decrements coprocessor's stack pointer by 1; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 EBFLDPI

7.04-42 FLDZ – loading of zero constant

The FLDZ command loads 0 (i.e. zero) constant into coprocessor's stack register ST(7), which must be empty at that moment. Then FLDZ command decrements coprocessor's stack pointer by 1; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last the loaded constant becomes stored in top stack register ST(0).

CodeExample
D9 EEFLDZ

7.04-43 FMUL – multiplication of real values

The FMUL command multiplies a real value in coprocessor's top stack register ST(0) or in any non-top register ST(1-7), if it specified as the first operand, by another real value – the multiplier, which is read from memory or from other stack register, if it is specified as the second operand. The product replaces former contents in top stack register ST(0) or in other stack register ST(1-7), if it is specified as the first operand.

First
byte
Second byte Data
bytes
Example
D8(0,4,8)(8-F)0-2FMUL dword ptr [bp+si+ffff]
D8C(8-F) FMUL ST,ST(0-7)
DC(0,4,8)(8-F)0-2 FMUL qword ptr [bp+si+ffff]
DCC(8-F) FMUL ST(1-7),ST

7.04-44 FMULP – multiply and shift stack up

The FMULP command multiplies a real value in specified coprocessor's non-top stack register ST(1-7) by a real multiplier in top stack register ST(0). The product overwrites former value in specified non-top stack register ST(1-7). Then FMULP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6), including that one, where the product has been written. The ST(0) register is renamed into ST(7) and is announced free. Access to its former contents (the multiplier) is lost.

CodeExample
DE С(8-F)FMULP ST(1-7),ST

7.04-45 FNOP – a void operation

Though the FNOP command is known to do nothing, it in fact increments IP (instruction pointer) by 2, because machine code of FNOP command itself takes 2 bytes, so since that IP points at the next command.

CodeExample
D9 D0FNOP

7.04-46 FPATAN – Partial arctangent.

The FPATAN command divides a positive real dividend in coprocessor's top stack register ST(0) by a positive real divisor in stack register ST(1). The divisor must be equal or greater, than the dividend. The quotient is used to calculate approximation of Arctg(ST(0)/ST(1)) function in radians. Result replaces divisor in stack register ST(1), and then FPATAN command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6). Former ST(1) register, where the result has been written, becomes top stack register ST(0). Access to former contents of ST(0) register (the divisor) is lost.

CodeExample
D9 F3FPATAN

7.04-47 FPREM – Partial remainder

The main purpose of FPREM command is reduction of periodic trigonometric function's arguments into limits of their main interval. FPREM command divides a real dividend in coprocessor's top stack register ST(0) by a real divisor in stack register ST(1). The remainder replaces dividend in top stack register ST(0). If this remainder is greater than divisor in ST(1) register, it is considered as a partial remainder and is marked by setting flag C2 = 1 in SWR register. In this case, the FPREM command should be executed repeatedly until cleared state of flag C2 in SWR register indicates acquisition of final remainder.

When final remainder is obtained, the states of flags C3, C1 and C0 in SWR register point at that circle's sector, which corresponds to final value of trigonometric argument. The way of performing checks for flags C3, C2, C1 and C0 in coprocessor's SWR register is described in article 7.04-64.

CodeExample
D9 F8FPREM

7.04-48 FPTAN – Partial tangent

The FPTAN command accepts in coprocessor's top stack register ST(0) angular argument in radians for calculation of Tg(ST(0)) value approximation. FPTAN command decrements coprocessor's stack pointer by 1; therefore registers ST(0-6) are renamed into ST(1-7), and angular argument occurs in register ST(1). Calculated value of tangent function replaces argument in register ST(1), and a unity constant is written in register ST(0). In case of success bit C2 in SWR register is cleared to 0, otherwise it is set to 1.

CodeExample
D9 F2FPTAN
Notes
  1. If the coprocessor's stack register ST(7) isn't empty, stack pointer can't be decremented, and then FPTAN command wouldn't be executed.
  2. Obsolete arithmetic coprocessors (up to model 80287) require argument of FPTAN command to be within limits from 0 to PI/4.

7.04-49 FRNDINT – round to integer

The FRNDINT command transforms a real value in coprocessor's top stack register ST(0) into an integer by rounding operation. The way rounding is performed depends on the state of RC (Rounding Control) field in CWR register: if RC=00 – round to the nearest integer, if RC=01 – round to the nearest lower integer, if RC=10 – round to the nearest greater integer, if RC=11 – round by omitting fractional part of original real value.

CodeExample
D9 FCFRNDINT
Notes
  1. Bits 11 and 10 in coprocessor's CWR register constitute the RC (Rounding Control) field. States of all bits in CWR register can be written into memory by FSTCW command (7.04-55), and then states of desired bits can be intentionally changed. Changed states come into effect after loading back into CWR register by FLDCW command (7.04-35).

7.04-50 FRSTOR – restoration of coprocessor's state

The FRSTOR command (FRSTOR = ReSTORe) loads states of all coprocessor's registers, including control registers and stack, from a record of 96 or 108 bytes long, starting at specified memory address. This record must be stored in memory beforehand by FSAVE command (7.04-51). Actual length of this record depends on CPU's mode: real mode or protected mode. Therefore it is important to perform restoration of coprocessor's state in exactly that CPU's mode, under which this record has been stored.

First
byte
Second byte Data
bytes
Example
DD(2,6,A)(0-7)0-2FRSTOR [bp+si+ffff]

7.04-51 FSAVE – save coprocessor's state

The FSAVE command stores in computer's memory, starting at specified address, states of all stack registers and control registers in arithmetical coprocessor, and then resets coprocessor's registers CWR, SWR, TWR, IPR, DPR just as FINIT command does (7.04-28). The whole record, formed by FSAVE command, is either 96 or 108 bytes long — that depends on CPU's mode: real mode or protected mode. Later this record may be read by FRSTOR command (7.04-50), which enables to restore coprocessor's former state.

First
byte
Second byte Data
bytes
Example
DD(3,7,B)(0-7)0-2FSAVE [bp+si+ffff]

7.04-52 FSCALE – multiplication by power of 2

The FSCALE command multiplies a real value in coprocessor's top stack register ST(0) by a power of 2 with integer power index, either positive or negative. Power index must be prepared in ST(1) register as a real value. If it is not an integer, it will be rounded to the nearest lower integer. Final product replaces the first multiplier in top stack register ST(0).

CodeExample
D9 FDFSCALE

7.04-53 FSQRT – square root

The FSQRT calculates square root of a real positive value in coprocessor's top stack register ST(0). Square root value replaces operand in top stack register ST(0).

CodeExample
D9 FAFSQRT

7.04-54 FST – store a real value

The FST command copies a real value from coprocessor's top stack register ST(0) into any other stack register ST(1-7) or into memory according to specified address and format. If specified format is shorter, than original 10-byte format in coprocessor's stack registers, then the stored value is rounded according to specified format.

First
byte
Second byte Data
bytes
Example
D9(1,5,9)(0-7)0-2FST dword ptr [bp+si+ffff]
DD(1,5,9)(0-7)0-2FST qword ptr [bp+si+ffff]
DDD(0-7) FST ST(1-7)
Notes
  1. Code "DF D(0-7)" is also unassembled by DEBUG.EXE as FST command.
  2. FST command enables to copy new contents into a stack register, which is not free. Former contents of this register will be overwritten.

7.04-55 FSTCW – store a state of CWR register

The FSTCW command copies current state of coprocessor's control register CWR into a data word, stored in memory according to specified address.

First
byte
Second byte Data
bytes
Example
D9(3,7,B)(8-F)0-2FSTCW [bp+si+ffff]
Notes
  1. Later the stored state of CWR register can be restored by FLDCW command (7.04-35).

7.04-56 FSTENV – store states of service registers

The FSTENV (= Store environment) command writes into memory, starting from specified address, states of all coprocessor's service registers: CWR (Control Word Register), SWR (Status Word Register), TWR (Tags Word Register), IPR (Instruction Pointer Register), DPR (Data Pointer Register). Contrary to FSAVE command (7.04-51), FSTENV command doesn't reset coprocessor's service registers and doesn't save contents of stack registers. Data from the record, formed by FSTENV command, may be later used to restore former states of service registers by FLDENV command (7.04-36).

First
byte
Second byte Data
bytes
Example
D9(3,7,B)(0-7)0-2FSTENV [bp+si+ffff]

7.04-57 FSTP – store and shift stack up

The FSTP command copies a real value from coprocessor's top stack register ST(0) into any other stack register ST(1-7), which must not necessarily be free, or into memory according to specified address and format. If specified format is shorter, than original 10-byte format in coprocessor's stack registers, then the stored value is rounded according to specified format. Then FSTP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6). The ST(0) register is renamed into ST(7) and is announced free. Access to former contents of ST(0) register is lost.

First
byte
Second byte Data
bytes
Example
D9(1,5,9)(8-F)0-2FSTP dword ptr [bp+si+ffff]
DB(3,7,B)(8-F)0-2FSTP tbyte ptr [bp+si+ffff]
DD(1,5,9)(8-F)0-2FSTP qword ptr [bp+si+ffff]
DDD(8-F) FSTP ST(1-7)
Notes
  1. Codes "D9 D(8-F)" and "DF D(8-F)" are also unassembled by DEBUG.EXE as FSTP command.

7.04-58 FSTSW – store status word

The FSTSW command copies into specified address the state of coprocessor's status word register SWR, chiefly for analyzing results after comparisons. The role of several bits in SWR register is described in articles 7.04-08 and 7.04-64.

First
byte
Second byte Data
bytes
Example
DD(3,7,B)(8-F)0-2FSTSW [bp+si+ffff]
DFE0 ESC 3C,AL[Note 1]
Notes
  1. ^ CPU models 80486 and higher perform FSTSW AX operation (code "DF E0"), copying coprocessor's status word into CPU's AX register. This operation is not "known" to DEBUG.EXE, but DEBUG.EXE accepts it under its former name ESC 3C,AL.

7.04-59 FSUB – subtraction of real values

The FSUB command (FSUB = SUBtract) subtracts a real value – the subtrahend, stored in a memory cell or in coprocessor's stack register ST(0-7), if it is specified as the second operand, from a real minuend in coprocessor's top stack register ST(0) or in other stack register ST(1-7), if it is specified as the first operand. The remainder replaces minuend in the coprocessor's top stack register ST(0) or in other stack register ST(1-7), if it is specified as the first operand.

First
byte
Second byte Data
bytes
Example
D8(2,6,A)(0-7)0-2FSUB dword ptr [bp+si+ffff]
D8E(8-F) FSUB ST,ST(0-7)
DC(2,6,A)(0-7)0-2FSUB qword ptr [bp+si+ffff]
DCE(8-F) FSUB ST(1-7),ST

7.04-60 FSUBP – subtract and shift stack up

The FSUBP command subtracts a real value – the subtrahend, stored in coprocessor's top stack register ST(0), from another real value – the minuend, stored in specified coprocessor's non-top stack register ST(1-7). The remainder overwrites the minuend in specified non-top stack register ST(1-7). Then FSUBP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6), including that one, where the remainder has been written. The ST(0) register is renamed into ST(7) and is announced free. Access to its former contents (the subtrahend) is lost.

CodeExample
DE E(8-F)FSUBP ST(1-7),ST

7.04-61 FSUBR – subtract in reverse order

The FSUBR command subtracts a real value – the subtrahend, stored in coprocessor's top stack register ST(0) or in any non-top stack register ST(1-7), if it is specified as the first operand, from a real minuend, stored in a specified memory cell or in another coprocessor's stack register, specified as the second operand. The remainder replaces subtrahend in coprocessor's top stack register ST(0) or in other stack register ST(1-7), if it is specified as the first operand.

First
byte
Second byte Data
bytes
Example
D8(2,6,A)(8-F)0-2FSUBR dword ptr [bp+si+ffff]
D8E(0-7) FSUBR ST,ST(0-7)
DC(2,6,A)(8-F)0-2FSUBR qword ptr [bp+si+ffff]
DCE(0-7) FSUBR ST(1-7),ST

7.04-62 FSUBRP – subtract in reverse order and shift stack up

The FSUBRP command subtracts a real value – the subtrahend, stored in any coprocessor's non-top stack's register ST(1-7), from real minuend in top stack register ST(0). Remainder replaces subtrahend in coprocessor's non-top stack's register ST(1-7). Then FSUBRP command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6), including that one, where the remainder has been written. The ST(0) register is renamed into ST(7) and is announced free. Access to its former contents (the minuend) is lost.

CodeExample
DE E(0-7)FSUBRP ST(1-7),ST

7.04-63 FTST – comparison with zero

The FTST (= TeST) command compares a real value in coprocessor's top stack register ST(0) with zero constant. States of flags C3, C2 and C0 in coprocessor's status word register SWR are altered according to the result. State of flag C2 should be checked first : if C2 = 1, the operands are incomparable, so there is no sense in further checks. Flag C3 = 1 indicates equality, i.e. zero value in top stack register ST(0). Flag C0 = 1 indicates negative value in top stack register ST(0). If top stack register ST(0) contains a positive real value, then all three flags C3, C2, C0 are cleared to zero. The way of performing checks for flags C0, C2, C3 in coprocessor's SWR register is described in article 7.04-64.

CodeExample
D9 E4FTST

7.04-64 FXAM – operand's type check

The FXAM (= eXAMine) command determines type of operand in coprocessor's top stack register ST(0). States of flags C3, C2, C1 and C0 in coprocessor's status word register SWR indicate the result. Flag C1 reflects the sign of operand. Interpretation for states of flags C3, C2 and C0 is given in the following table:

C3C2C0Operand type
000- unknown format
001- any non-numeric format
010- correct real number
011- infinity (tag = 10)
100- zero (tag = 01)
101- empty ST(0) register (tag = 11).
110- any denormalized number

In order to analyze the result, status word should be copied by FSTSW command (7.04-58) from coprocessor's SWR register preferably into CPU's AX register. Then states may be either tested by TEST command (7.03-90) or loaded into CPU's flags by SAHF command (7.03-77). As far as flags C0, C1, C2, C3 are represented in SWR register (and in AX register as well) by bits 08, 09, 10 and 14 correspondingly, in AH register the same flags are represented by bits 0, 1, 2, 6. After loading into CPU's flags by SAHF command the state C3 flag is represented by CPU's zero flag ZF, state of C2 flag – by CPU's parity flag PF, state of C0 flag – by CPU's carry flag CF.

CodeExample
D9 E5FXAM

7.04-65 FXCH – register's contents exchange

The FXCH (= eXCHange) command exchanges contents between coprocessor's top stack register ST(0) and any other specified stack register ST(1-7).

CodeExample
D9 C(8-F)FXCH ST(1-7)
Notes
  1. Codes "DD C(8-F)" and "DF C(8-F)" are also unassembled by DEBUG.EXE as FXCH command.

7.04-66 FXTRACT – separation of mantissa and exponent

The FXTRACT (= eXTRACT) command decomposes a real value in coprocessor's top stack register ST(0) into its mantissa (i.e. significand) and binary exponent (i.e. binary power index). Mantissa is written into stack register ST(7), which must be empty at that moment. Exponent replaces original value in top stack register ST(0). Then FXTRACT command decrements coprocessor's stack pointer by 1; therefore registers ST(0-6) are renamed into ST(1-7), and register ST(7) is renamed into ST(0), so that at last mantissa occurs in coprocessor's top stack register ST(0), and exponent in register ST(1).

CodeExample
D9 F4FXTRACT

7.04-67 FYL2X – logarithm of arbitrary base

The FYL2X command calculates a base 2 logarithm of a positive real value in coprocessor's top stack register ST(0), and then multiplies logarithm by a real multiplier in register ST(1). Multiplication enables to transform a base 2 logarithm to any arbitrary base. Product overwrites multiplier in register ST(1). Then FYL2X command increments coprocessor's stack pointer by 1, so that registers ST(1-7) are renamed into ST(0-6). That register, where the product has been written, becomes top stack register ST(0). The former ST(0) register is renamed into ST(7) and is announced free, access to its contents is lost. Final result of FYL2X command is expressed by formula ST(0)=ST(1)•log(ST(0)).

CodeExample
D9 F1FYL2X

7.04-68 FYL2XP1 – sum of series logarithm

The FYL2XP1 command calculates a logarithm of arbitrary base, just as FYL2X command does (7.04-67), but FYL2XP1 command implies that its argument in coprocessor's top stack register ST(0) is a sum of series, calculated by F2XM1 command (7.04-01). For obtaining high precision of calculations this sum of series must be within limits from (1 + 1/SQRT2) to (1 + SQRT2), which correspond to base 2 logarithm values from 1/2 to +1/2. Further multiplication of base 2 logarithm value by a multiplier in stack register ST(1) and incrementation of coprocessor's stack pointer is performed just as it is done by FYL2X command (7.04-67). Calculated logarithm is left in top stack register ST(0). Final result of FYL2XP1 command is expressed by formula ST(0)=ST(1)•log(1+ST(0)).

CodeExample
D9 F9FYL2XP1
This article is issued from Wikibooks. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.