First steps towards system programming under MS-DOS 7/Debugger's assembler commands

Clipboard

To do:

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
Designators Explanations and allowed alternatives
bl any of 8-bit registers: AL, CL, DL, BL, AH, CH, DH, BH.
bx any of 16-bit registers: AX, CX, DX, BX, SP, BP, SI, DI.
ss any of segment registers: CS, DS, ES, SS.
ST coprocessor's top stack register ST(0) [Note 1]
ST(0-7) any coprocessor's stack register from ST(0) to ST(7)
far marker of a 4-byte address (segment + offset).
byte ptr marker of a one-byte operand (ptr = "pointer")
word ptr marker of a 2-bytes operand (a word)
dword ptr marker of a 4-bytes operand (a double word)
qword ptr marker of a 8-bytes operand (a quadruple word)
tbyte ptr marker of a 10-bytes operand
f any single hexadecimal digit from "0" to "F"
±7f any signed hexadecimal number from –7Fh to +7Fh
ff any hexadecimal number from 00h to FFh
ffff any hexadecimal number from 0000h to FFFFh
aaaa address 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 instructionsEdit

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 instructionEdit

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 instructionEdit

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 instructionEdit

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:ffff Set explicitly both segment address and offset
ORG fff Leave segment address intact, set offset 0fffh
ORG ss:ffff Refer to segment register "ss:", set offset ffffh
ORG ss: Refer to segment register "ss:", offset 0000h is implied

7.01-04 Instruction "Empty line"Edit

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 instructionEdit

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 PrefixesEdit

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 prefixesEdit

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.

Code Example Comments
2E CS:
3E DS:
26 ES:
36 SS:
64 DB 64 relative to FS: segment register
65 DB 65 relative 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 prefixEdit

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).

Code Example
F0 LOCK
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 REPNZEdit

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.

Code Example
F2 REPNE
F2 REPNZ
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 REPZEdit

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.

Code Example
F3 REP
F3 REPE
F3 REPZ
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 FWAITEdit

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.

Code Example
9B FWAIT
9B WAIT

7.02-06 Operand's size override prefixEdit

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 prefixEdit

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 form Affected 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 prefixesEdit

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 CPUEdit

7.03-01 AAA – decimal correction of unpacked sumEdit

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.

Code Example
37 AAA

7.03-02 AAD – decimal preparation to divisionEdit

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.

Code Example
D5 0A AAD
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 productEdit

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.

Code Example
D4 0A AAM
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 remainderEdit

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.

Code Example
3F AAS

7.03-05 ADC – binary addition with carryEdit

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-2 ADC [bp+si+ffff],bl
10 (C-F)(0-F)   ADC bl,bl
11 (0-B)(0-F) 0-2 ADC [bp+si+ffff],bx
11 (C-F)(0-F)   ADC bx,bx
12 (0-B)(0-F) 0-2 ADC bl,[bp+si+ffff]
13 (0-B)(0-F) 0-2 ADC bx,[bp+si+ffff]
14   1 ADC AL,ff
15   2 ADC AX,ffff
80 (1,5,9)(0-7) 1-3 ADC byte ptr [bp+si+ffff],ff
80 D(1-7) 1 ADC bl,ff
81 (1,5,9)(0-7) 2-4 ADC word ptr [bp+si+ffff],ffff
81 D(1-7) 2 ADC bx,ffff
83 (1,5,9)(0-7) 1-3 ADC word ptr [bp+si+ffff],±7f
83 D(1-7) 1 ADC 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 additionEdit

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-2 ADD [bp+si+ffff],bl
00 (C-F)(0-F)   ADD bl,bl
01 (0-B)(0-F) 0-2 ADD [bp+si+ffff],bx
01 (C-F)(0-F)   ADD bx,bx
02 (0-B)(0-F) 0-2 ADD bl,[bp+si+ffff]
03 (0-B)(0-F) 0-2 ADD bx,[bp+si+ffff]
04   1 ADD AL,ff
05   2 ADD AX,ffff
80 (0,4,8)(0-7) 1-3 ADD byte ptr [bp+si+ffff],ff
80 C(1-7) 1 ADD bl,ff
81 (0,4,8)(0-7) 2-4 ADD word ptr [bp+si+ffff],ffff
81 C(1-7) 2 ADD bx,ffff
83 (0,4,8)(0-7) 1-3 ADD word ptr [bp+si+ffff],±7f
83 C(1-7) 1 ADD 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" operationEdit

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-2 AND [bp+si+ffff],bl
20 (C-F)(0-F)   AND bl,bl
21 (0-B)(0-F) 0-2 AND [bp+si+ffff],bx
21 (C-F)(0-F)   AND bx,bx
22 (0-B)(0-F) 0-2 AND bl,[bp+si+ffff]
23 (0-B)(0-F) 0-2 AND bx,[bp+si+ffff]
24   1 AND AL,ff
25   2 AND AX,ffff
80 (2,6,A)(0-7) 1-3 AND byte ptr [bp+si+ffff],ff
80 E(1-7) 1 AND bl,ff
81 (2,6,A)(0-7) 2-4 AND word ptr [bp+si+ffff],ffff
81 E(1-7) 2 AND bx,ffff
83 (2,6,A)(0-7) 1-3 AND word ptr [bp+si+ffff],±7f
83 E(1-7) 1 AND 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 subroutineEdit

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
Example Comments
9A   4 CALL FAR ffff:ffff [Note 1]
E8   2 CALL ffff
FF (1,5,9)(0-7) 0-2 CALL [bp+si+ffff] [Note 2]
FF (1,5,9)(8-F) 0-2 CALL FAR [bp+si+ffff] [Note 2]
FF D(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 conversionEdit

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.

Code Example
98 CBW

7.03-10 CLC – carry flag resetEdit

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

Code Example
F8 CLC

7.03-11 CLD – direction flag resetEdit

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.).

Code Example
FC CLD

7.03-12 CLI – interrupt flag resetEdit

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).

Code Example
FA CLI
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 stateEdit

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.

Code Example
F5 CMC

7.03-14 CMP – comparison of operandsEdit

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-2 CMP [bp+si+ffff],bl
38 (C-F)(0-F)   CMP bl,bl
39 (0-B)(0-F) 0-2 CMP [bp+si+ffff],bx
39 (C-F)(0-F)   CMP bx,bx
3A (0-B)(0-F) 0-2 CMP bl,[bp+si+ffff]
3B (0-B)(0-F) 0-2 CMP bx,[bp+si+ffff]
3C   1 CMP AL,ff
3D   2 CMP AX,ffff
80 (3,7,B)(8-F) 1-3 CMP byte ptr [bp+si+ffff],ff
80 F(9-F) 1 CMP bl,ff
81 (3,7,B)(8-F) 2-4 CMP word ptr [bp+si+ffff],ffff
81 F(9-F) 2 CMP bx,ffff
83 (3,7,B)(8-F) 1-3 CMP word ptr [bp+si+ffff],±7f
83 F(9-F) 1 CMP 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 pairsEdit

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.

Code Example
A6 CMPSB
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 pairsEdit

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).

Code Example
A7 CMPSW

7.03-17 CWD – word to double word conversion.Edit

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.

Code Example
99 CWD

7.03-18 DAA – decimal correction of packed sumEdit

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.

Code Example
27 DAA

7.03-19 DAS – decimal correction of packed remainderEdit

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.

Code Example
2F DAS

7.03-20 DEC – a unity decrementEdit

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-2 DEC byte ptr [bp+si+ffff]
FE C(8-F)   DEC bl
FF (0,4,8)(8-E) 0-2 DEC 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 integersEdit

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–2 DIV byte ptr [bp+si+ffff]
F6 F(0-7)   DIV bl
F7 (3,7,B)(0-7) 0–2 DIV word ptr [bp+si+ffff]
F7 F(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 coprocessorEdit

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 byte Examples
DA C(0-7) ESC 10,bl
DA C(8-F) ESC 11,bl
DA D(0-7) ESC 12,bl
DA D(8-F) ESC 13,bl
DA E(0-7) ESC 14,bl
DA E(8-F) ESC 15,bl
DA F(0-7) ESC 16,bl
DA F(8-F) ESC 17,bl

7.03-23 HLT – set CPU to a standstillEdit

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).

Code Example
F4 HLT
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 integersEdit

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-2 IDIV byte ptr [bp+si+ffff]
F6 F(8-F)   IDIV bl
F7 (3,7,B)(8-F) 0-2 IDIV word ptr [bp+si+ffff]
F7 F(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 integersEdit

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-2 IMUL byte ptr [bp+si+ffff]
F6 E(8-F)   IMUL bl
F7 (2,6,A)(8-F) 0-2 IMUL word ptr [bp+si+ffff]
F7 E(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 portEdit

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   1 IN AL,ff
E5   1 IN 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 incrementEdit

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-2 INC byte ptr [bp+si+ffff]
FE C(0-7)   INC bl
FF (0,4,8)(0-7) 0-2 INC 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 handlerEdit

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   1 INT 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 handlerEdit

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).

Code Example
CE INTO
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 handlerEdit

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.

Code Example
CF IRET
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 aboveEdit

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   1 JA aaaa

7.03-32 JB – Jump if belowEdit

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   1 JB aaaa

7.03-33 JBE – Jump if below or equalEdit

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   1 JBE aaaa

7.03-34 JCXZ – Jump if CX is ZeroEdit

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   1 JCXZ aaaa

7.03-35 JG – Jump if GreaterEdit

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   1 JG aaaa

7.03-36 JGE – Jump if Greater or EqualEdit

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   1 JGE aaaa

7.03-37 JL – Jump if LowerEdit

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   1 JL aaaa

7.03-38 JLE – Jump if Lower or EqualEdit

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   1 JLE aaaa

7.03-39 JMP – unconditional jumpEdit

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
Example Comments
E9   2 JMP ffff note 1
EA   4 JMP ffff:ffff note 2
EB   1 JMP aaaa note 1
FF (2,6,A)(0-7) 0-2 JMP [bp+si+ffff] note 3
FF (2,6,A)(8-F) 0-2 JMP FAR [bp+si+ffff] note 3
FF E(0-7)   JMP bx note 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 BelowEdit

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   1 JNB aaaa

7.03-41 JNO – Jump if No OverflowEdit

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   1 JNO aaaa

7.03-42 JNS – Jump if No SignEdit

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   1 JNS aaaa

7.03-43 JNZ – Jump if No ZeroEdit

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   1 JNZ aaaa

7.03-44 JO – Jump if OverflowEdit

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   1 JO aaaa

7.03-45 JPE – Jump if Parity EvenEdit

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   1 JPE aaaa

7.03-46 JPO – Jump if Parity OddEdit

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   1 JPO aaaa

7.03-47 JS – Jump if SignEdit

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   1 JS aaaa

7.03-48 JZ – Jump if ZeroEdit

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   1 JZ aaaa

7.03-49 LAHF – copying flags into AH registerEdit

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.

Code Example
9F LAHF

7.03-50 LDS – Loading of DS registerEdit

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-2 LDS 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 calculationEdit

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-2 LEA bx,[bp+si+ffff]

7.03-52 LES – Loading of ES registerEdit

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-2 LES 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 bytesEdit

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.

Code Example
AC LODSB

7.03-54 LODSW – serial copying of wordsEdit

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).

Code Example
AD LODSW

7.03-55 LOOP – arrangement of a cycleEdit

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   1 LOOP aaaa

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

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   1 LOOPNZ aaaa

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

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   1 LOOPZ aaaa

7.03-58 MOV – data copying commandEdit

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-2 MOV [bp+si+ffff],bl
88 (C-F)(0-F)   MOV bl,bl
89 (0-B)(0-5, 7-F) 0-2 MOV [bp+si+ffff],bx
89 (C-F)(0-F)   MOV bx,bx
8A (0-B)(0-5, 7-F) 0-2 MOV bl,[bp+si+ffff]
8B (0-B)(0-5, 7-F) 0-2 MOV bx,[bp+si+ffff]
8C (0,1,4,5,8,9)(0-F) 0-2 MOV [bp+si+ffff],ss
8C (C,D,E)(0-F)   MOV bx,ss
8E (0,1,4,5,8,9)(0-F) 0-2 MOV ss,[bp+si+ffff]
8E (C,D,E)(0-F)   MOV ss,bx
A0   2 MOV AL,[ffff]
A1   2 MOV AX,[ffff]
A2   2 MOV [ffff],AL
A3   2 MOV [ffff],AX
B(0-7)   1 MOV bl,ff
B(8-F)   2 MOV bx,ffff
C6 (0,4,8)(0-7) 1-3 MOV byte ptr [bp+si+ffff],ff
C7 (0,4,8)(0-7) 2-4 MOV 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 bytesEdit

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.

Code Example
A4 MOVSB

7.03-60 MOVSW – serial copying of wordsEdit

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).

Code Example
A5 MOVSW

7.03-61 MUL – multiplication of unsigned integersEdit

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]
F6 E(0-7)   MUL bl
F7 (2,6,A)(0-7) 0-2 MUL word ptr [bp+si+ffff]
F7 E(0-7)   MUL bx

7.03-62 NEG – operand's sign reversalEdit

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-2 NEG byte ptr [bp+si+ffff]
F6 D(8-F)   NEG bl
F7 (1,5,9)(8-F) 0-2 NEG word ptr [bp+si+ffff]
F7 D(8-F)   NEG bx

7.03-63 NOP – a void operationEdit

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.

Code Example
90 NOP

7.03-64 NOT – inversion of operand's bitsEdit

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-2 NOT byte ptr [bp+si+ffff]
F6 D(0-7)   NOT bl
F7 (1,5,9)(0-7) 0-2 NOT word ptr [bp+si+ffff]
F7 D(0-7)   NOT bx

7.03-65 OR – logical OR operationEdit

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-2 OR [bp+si+ffff],bl
08 (C-F)(0-F)   OR bl,bl
09 (0-B)(0-F) 0-2 OR [bp+si+ffff],bx
09 (C-F)(0-F)   OR bx,bx
0A (0-B)(0-F) 0-2 OR bl,[bp+si+ffff]
0B (0-B)(0-F) 0-2 OR bx,[bp+si+ffff]
0C   1 OR AL,ff
0D   2 OR AX,ffff
80 (0,4,8)(8-F) 1-3 OR byte ptr [bp+si+ffff],ff
80 C(9-F) 1 OR bl,ff
81 (0,4,8)(8-F) 2-4 OR word ptr [bp+si+ffff],ffff
81 C(9-F) 2 OR bx,ffff
83 (0,4,8)(8-F) 1-3 OR word ptr [bp+si+ffff],±7f
83 C(9-F) 1 OR 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 portEdit

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   1 OUT ff,AL
E7   1 OUT 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 stackEdit

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
Example Comments
07     POP ES
0F A1   DB 0F A1 = POP FS
0F A9   DB 0F A9 = POP GS
17     POP SS
1F     POP DS
5(8-F)     POP bx
8F (0,8)(0-7) 0-2 POP [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 stackEdit

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.

Code Example
9D POPF
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.Edit

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
Example Comments
06     PUSH ES
0E     PUSH CS
0F A0   DB 0F A0 = PUSH FS
0F A8   DB 0F A8 = PUSH GS
16     PUSH SS
1E     PUSH DS
5(0-7)     PUSH bx
68   2 DB 68 ff ff = PUSH ffff
6A   1 DB 6A ff = PUSH 00ff
FF (3,7,B)(0-7) 0-2 PUSH [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 stackEdit

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.

Code Example
9C PUSHF
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 flagEdit

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-2 RCL byte ptr [bp+si+ffff],1
D0 D(0-7)   RCL bl,1
D1 (1,5,9)(0-7) 0-2 RCL word ptr [bp+si+ffff],1
D1 D(0-7)   RCL bx,1
D2 (1,5,9)(0-7) 0-2 RCL byte ptr [bp+si+ffff],CL
D2 D(0-7)   RCL bl,CL
D3 (1,5,9)(0-7) 0-2 RCL word ptr [bp+si+ffff],CL
D3 D(0-7)   RCL bx,CL

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

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-2 RCR byte ptr [bp+si+ffff],1
D0 D(8-F)   RCR bl,1
D1 (1,5,9)(8-F) 0-2 RCR word ptr [bp+si+ffff],1
D1 D(8-F)   RCR bx,1
D2 (1,5,9)(8-F) 0-2 RCR byte ptr [bp+si+ffff],CL
D2 D(8-F)   RCR bl,CL
D3 (1,5,9)(8-F) 0-2 RCR word ptr [bp+si+ffff],CL
D3 D(8-F)   RCR bx,CL

7.03-73 RET – return within the same segmentEdit

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   2 RET 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 segmentEdit

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   2 RETF ffff
CB     RETF

7.03-75 ROL – circular shift leftwardEdit

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-2 ROL byte ptr [bp+si+ffff],1
D0 C(0-7)   ROL bl,1
D1 (0,4,8)(0-7) 0-2 ROL word ptr [bp+si+ffff],1
D1 C(0-7)   ROL bx,1
D2 (0,4,8)(0-7) 0-2 ROL byte ptr [bp+si+ffff],CL
D2 C(0-7)   ROL bl,CL
D3 (0,4,8)(0-7) 0-2 ROL word ptr [bp+si+ffff],CL
D3 C(0-7)   ROL bx,CL

7.03-76 ROR – circular shift to the rightEdit

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-2 ROR byte ptr [bp+si+ffff],1
D0 C(8-F)   ROR bl,1
D1 (0,4,8)(8-F) 0-2 ROR word ptr [bp+si+ffff],1
D1 C(8-F)   ROR bx,1
D2 (0,4,8)(8-F) 0-2 ROR byte ptr [bp+si+ffff],CL
D2 C(8-F)   ROR bl,CL
D3 (0,4,8)(8-F) 0-2 ROR word ptr [bp+si+ffff],CL
D3 C(8-F)   ROR bx,CL

7.03-77 SAHF – copying AH into flags registerEdit

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.

Code Example
9E SAHF

7.03-78 SAR – signed integer shift to the rightEdit

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-2 SAR byte ptr [bp+si+ffff],1
D0 F(8-F)   SAR bl,1
D1 (3,7,B)(8-F) 0-2 SAR word ptr [bp+si+ffff],1
D1 F(8-F)   SAR bx,1
D2 (3,7,B)(8-F) 0-2 SAR byte ptr [bp+si+ffff],CL
D2 F(8-F)   SAR bl,CL
D3 (3,7,B)(8-F) 0-2 SAR word ptr [bp+si+ffff],CL
D3 F(8-F)   SAR bx,CL

7.03-79 SBB – binary integers subtraction with borrowEdit

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-2 SBB [bp+si+ffff],bl
18 (C-F)(0-F)   SBB bl,bl
19 (0-B)(0-F) 0-2 SBB [bp+si+ffff],bx
19 (C-F)(0-F)   SBB bx,bx
1A (0-B)(0-F) 0-2 SBB bl,[bp+si+ffff]
1B (0-B)(0-F) 0-2 SBB bx,[bp+si+ffff]
1C   1 SBB AL,ff
1D   2 SBB AX,ffff
80 (1,5,9)(8-F) 1-3 SBB byte ptr [bp+si+ffff],ff
80 D(9-F) 1 SBB bl,ff
81 (1,5,9)(8-F) 2-4 SBB word ptr [bp+si+ffff],ffff
81 D(9-F) 2 SBB bx,ffff
83 (1,5,9)(8-F) 1-3 SBB word ptr [bp+si+ffff],±7f
83 D(9-F) 1 SBB 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 byteEdit

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.

Code Example
AE SCASB
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 wordEdit

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).

Code Example
AF SCASW

7.03-82 SHL – Shift to the leftEdit

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-2 SHL byte ptr [bp+si+ffff],1
D0 E(0-7)   SHL bl,1
D1 (2,6,A)(0-7) 0-2 SHL word ptr [bp+si+ffff],1
D1 E(0-7)   SHL bx,1
D2 (2,6,A)(0-7) 0-2 SHL byte ptr [bp+si+ffff],CL
D2 E(0-7)   SHL bl,CL
D3 (2,6,A)(0-7) 0-2 SHL word ptr [bp+si+ffff],CL
D3 E(0-7)   SHL bx,CL
C0 E(0-7) 1 see note 2
C1 E(0-7) 1 see 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 rightEdit

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-2 SHR byte ptr [bp+si+ffff],1
D0 E(8-F)   SHR bl,1
D1 (2,6,A)(8-F) 0-2 SHR word ptr [bp+si+ffff],1
D1 E(8-F)   SHR bx,1
D2 (2,6,A)(8-F) 0-2 SHR byte ptr [bp+si+ffff],CL
D2 E(8-F)   SHR bl,CL
D3 (2,6,A)(8-F) 0-2 SHR word ptr [bp+si+ffff],CL
D3 E(8-F)   SHR bx,CL
C0 E(8-F) 1 see note 1
C1 E(8-F) 1 see 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 flagEdit

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

Code Example
F9 STC

7.03-85 STD – set direction flagEdit

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.).

Code Example
FD STD

7.03-86 STI – set interrupt flagEdit

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

Code Example
FB STI
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 byteEdit

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.

Code Example
AA STOSB

7.03-88 STOSW – filling memory with a wordEdit

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).

Code Example
AB STOSW

7.03-89 SUB – binary integers subtractionEdit

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-2 SUB [bp+si+ffff],bl
28 (C-F)(0-F)   SUB bl,bl
29 (0-B)(0-F) 0-2 SUB [bp+si+ffff],bx
29 (C-F)(0-F)   SUB bx,bx
2A (0-B)(0-F) 0-2 SUB bl,[bp+si+ffff]
2B (0-B)(0-F) 0-2 SUB bx,[bp+si+ffff]
2C   1 SUB AL,ff
2D   2 SUB AX,ffff
80 (2,6,A)(8-F) 1-3 SUB byte ptr [bp+si+ffff],ff
80 E(9-F) 1 SUB bl,ff
81 (2,6,A)(8-F) 2-4 SUB word ptr [bp+si+ffff],ffff
81 E(9-F) 2 SUB bx,ffff
83 (2,6,A)(8-F) 1-3 SUB word ptr [bp+si+ffff],±7f
83 E(9-F) 1 SUB 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 statesEdit

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-2 TEST [bp+si+ffff],bl
84 (C-F)(0-F)   TEST bl,bl
85 (0-B)(0-F) 0-2 TEST [bp+si+ffff],bx
85 (C-F)(0-F)   TEST bx,bx
A8   1 TEST AL,ff
A9   2 TEST AX,ffff
F6 (0,4,8)(0-7) 1-3 TEST byte ptr [bp+si+ffff],ff
F6 C(1-7) 1 TEST bl,ff
F7 (0,4,8)(0-7) 2-4 TEST word ptr [bp+si+ffff],ffff
F7 C(1-7) 2 TEST bx,ffff

7.03-91 XCHG – operands exchangeEdit

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-2 XCHG [bp+si+ffff],bl
86 (C-F)(1-7,9-F)   XCHG bl,bl
87 (0-B)(0-F) 0-2 XCHG [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 translationEdit

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).

Code Example
D7 XLAT

7.03-93 XOR – exclusive OR logical operationEdit

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-2 XOR [bp+si+ffff],bl
30 (C-F)(0-F)   XOR bl,bl
31 (0-B)(0-F) 0-2 XOR [bp+si+ffff],bx
31 (C-F)(0-F)   XOR bx,bx
32 (0-B)(0-F) 0-2 XOR bl,[bp+si+ffff]
33 (0-B)(0-F) 0-2 XOR bx,[bp+si+ffff]
34   1 XOR AL,ff
35   2 XOR AX,ffff
80 (3,7,B)(0-7) 1-3 XOR byte ptr [bp+si+ffff],ff
80 F(1-7) 1 XOR bl,ff
81 (3,7,B)(0-7) 2-4 XOR word ptr [bp+si+ffff],ffff
81 F(1-7) 2 XOR bx,ffff
83 (3,7,B)(0-7) 1-3 XOR word ptr [bp+si+ffff],±7f
83 F(1-7) 1 XOR 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 coprocessorEdit

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 2Edit

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)

Code Example
D9 F0 F2XM1

7.04-02 FABS – absolute valueEdit

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.

Code Example
D9 E1 FABS

7.04-03 FADD – addition of real valuesEdit

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-2 FADD dword ptr [bp+si+ffff]
D8 C(0-7)   FADD ST,ST(0-7)
DC (0,4,8)(0-7) 0-2 FADD qword ptr [bp+si+ffff]
DC C(0-7)   FADD ST(1-7),ST

7.04-04 FADDP – addition and stack shift upEdit

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.

Code Example
DE C(0-7) FADDP ST(1-7),ST

7.04-05 FBLD – loading with binary transformEdit

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-2 FBLD 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 transformEdit

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-2 FBSTP tbyte ptr [bp+si+ffff]

7.04-07 FCHS – change signEdit

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

Code Example
D9 E0 FCHS

7.04-08 FCLEX – clear exceptions flagsEdit

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
Code Example
DB E2 FCLEX

7.04-09 FCOM – comparison of real valuesEdit

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-2 FCOM dword ptr [bp+si+ffff]
D8 D(0-7)   FCOM ST(0-7)
DC (1,5,9)(0-7) 0-2 FCOM 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 upEdit

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-2 FCOMP dword ptr [bp+si+ffff]
D8 D(8-F)   FCOMP ST(0-7)
DC (1,5,9)(8-F) 0-2 FCOMP 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 upEdit

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).

Code Example
DE D9 FCOMPP
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 PointerEdit

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.

Code Example
D9 F6 FDECSTP
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 interruptsEdit

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

Code Example
DB E1 FDISI

7.04-14 FDIV – division of real valuesEdit

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-2 FDIV dword ptr [bp+si+ffff]
D8 F(0-7)   FDIV ST,ST(0-7)
DC (3,7,B)(0-7) 0-2 FDIV qword ptr [bp+si+ffff]
DC F(8-F)   FDIV ST(1-7),ST

7.04-15 FDIVP – divide and shift stack upEdit

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.

Code Example
DE F(8-F) FDIVP ST(1-7),ST

7.04-16 FDIVR – divide in reverse orderEdit

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-2 FDIVR dword ptr [bp+si+ffff]
D8 F(8-F)   FDIVR ST,ST(0-7)
DC (3,7,B)(8-F) 0-2 FDIVR qword ptr [bp+si+ffff]
DC F(0-7)   FDIVR ST(1-7),ST

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

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.

Code Example
DE F(0-7) FDIVRP ST(1-7),ST

7.04-18 FENI – enable interruptsEdit

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

Code Example
DB E0 FENI

7.04-19 FFREE – announce register as freeEdit

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.

Code Example
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 integerEdit

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-2 FIADD dword ptr [bp+si+ffff]
DE (0,4,8)(0-7) 0-2 FIADD word ptr [bp+si+ffff]

7.04-21 FICOM – comparison with an integerEdit

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-2 FICOM dword ptr [bp+si+ffff]
DE (1,5,9)(0-7) 0-2 FICOM word ptr [bp+si+ffff]

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

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-2 FICOMP dword ptr [bp+si+ffff]
DE (1,5,9)(8-F) 0-2 FICOMP word ptr [bp+si+ffff]

7.04-23 FIDIV – division by an integerEdit

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-2 FIDIV dword ptr [bp+si+ffff]
DE (3,7,B)(0-7) 0-2 FIDIV word ptr [bp+si+ffff]

7.04-24 FIDIVR – integer division in reverse orderEdit

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-2 FIDIVR dword ptr [bp+si+ffff]
DE (3,7,B)(8-F) 0-2 FIDIVR word ptr [bp+si+ffff]

7.04-25 FILD – loading of an integerEdit

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-2 FILD dword ptr [bp+si+ffff]
DF (0,4,8)(0-7) 0-2 FILD word ptr [bp+si+ffff]
DF (2,6,A)(8-F) 0-2 FILD qword ptr [bp+si+ffff]

7.04-26 FIMUL – multiplication by an integerEdit

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-2 FIMUL dword ptr [bp+si+ffff]
DE (0,4,8)(8-F) 0-2 FIMUL word ptr [bp+si+ffff]

7.04-27 FINCSTP – increment stack top pointerEdit

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.

Code Example
D9 F7 FINCSTP
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 stateEdit

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.

Code Example
DB E3 FINIT

7.04-29 FIST – storing of an integerEdit

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-2 FIST dword ptr [bp+si+ffff]
DF (1,5,9)(0-7) 0-2 FIST word ptr [bp+si+ffff]

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

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-2 FISTP dword ptr [bp+si+ffff]
DF (1,5,9)(8-F)   FISTP word ptr [bp+si+ffff]
DF (3,7,B)(8-F) 0-2 FISTP qword ptr [bp+si+ffff]

7.04-31 FISUB – subtraction of an integerEdit

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-2 FISUB dword ptr [bp+si+ffff]
DE (2,6,A)(0-7) 0-2 FISUB word ptr [bp+si+ffff]

7.04-32 FISUBR – subtract in reverse orderEdit

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-2 FISUBR dword ptr [bp+si+ffff]
DE (2,6,A)(8-F) 0-2 FISUBR word ptr [bp+si+ffff]

7.04-33 FLD – loading of a real valueEdit

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-2 FLD dword ptr [bp+si+ffff]
D9 C(0-7)   FLD ST(0-7)
DB (2,6,A)(8-F) 0-2 FLD tbyte ptr [bp+si+ffff]
DD (0,4,8)(0-7) 0-2 FLD qword ptr [bp+si+ffff]

7.04-34 FLD1 – loading of a unity constantEdit

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).

Code Example
D9 E8 FLD1

7.04-35 FLDCW – loading of CWR registerEdit

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-2 FLDCW 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 registersEdit

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-2 FLDENV word ptr [bp+si+ffff]

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

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).

Code Example
D9 EA FLDL2E

7.04-38 FLDL2T – loading of "log10" constantEdit

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).

Code Example
D9 E9 FLDL2T

7.04-39 FLDLG2 – loading of "lg2" constantEdit

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).

Code Example
D9 EC FLDLG2

7.04-40 FLDLN2 – loading of "ln2" constantEdit

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).

Code Example
D9 ED FLDLN2

7.04-41 FLDPI – loading of "PI" constantEdit

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).

Code Example
D9 EB FLDPI

7.04-42 FLDZ – loading of zero constantEdit

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).

Code Example
D9 EE FLDZ

7.04-43 FMUL – multiplication of real valuesEdit

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-2 FMUL dword ptr [bp+si+ffff]
D8 C(8-F)   FMUL ST,ST(0-7)
DC (0,4,8)(8-F) 0-2 FMUL qword ptr [bp+si+ffff]
DC C(8-F)   FMUL ST(1-7),ST

7.04-44 FMULP – multiply and shift stack upEdit

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.

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

7.04-45 FNOP – a void operationEdit

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.

Code Example
D9 D0 FNOP

7.04-46 FPATAN – Partial arctangent.Edit

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.

Code Example
D9 F3 FPATAN

7.04-47 FPREM – Partial remainderEdit

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.

Code Example
D9 F8 FPREM

7.04-48 FPTAN – Partial tangentEdit

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.

Code Example
D9 F2 FPTAN
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 integerEdit

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.

Code Example
D9 FC FRNDINT
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 stateEdit

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-2 FRSTOR [bp+si+ffff]

7.04-51 FSAVE – save coprocessor's stateEdit

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-2 FSAVE [bp+si+ffff]

7.04-52 FSCALE – multiplication by power of 2Edit

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).

Code Example
D9 FD FSCALE

7.04-53 FSQRT – square rootEdit

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).

Code Example
D9 FA FSQRT

7.04-54 FST – store a real valueEdit

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-2 FST dword ptr [bp+si+ffff]
DD (1,5,9)(0-7) 0-2 FST qword ptr [bp+si+ffff]
DD D(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 registerEdit

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-2 FSTCW [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 registersEdit

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-2 FSTENV [bp+si+ffff]

7.04-57 FSTP – store and shift stack upEdit

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-2 FSTP dword ptr [bp+si+ffff]
DB (3,7,B)(8-F) 0-2 FSTP tbyte ptr [bp+si+ffff]
DD (1,5,9)(8-F) 0-2 FSTP qword ptr [bp+si+ffff]
DD D(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 wordEdit

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-2 FSTSW [bp+si+ffff]
DF E0   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 valuesEdit

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-2 FSUB dword ptr [bp+si+ffff]
D8 E(8-F)   FSUB ST,ST(0-7)
DC (2,6,A)(0-7) 0-2 FSUB qword ptr [bp+si+ffff]
DC E(8-F)   FSUB ST(1-7),ST

7.04-60 FSUBP – subtract and shift stack upEdit

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.

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

7.04-61 FSUBR – subtract in reverse orderEdit

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-2 FSUBR dword ptr [bp+si+ffff]
D8 E(0-7)   FSUBR ST,ST(0-7)
DC (2,6,A)(8-F) 0-2 FSUBR qword ptr [bp+si+ffff]
DC E(0-7)   FSUBR ST(1-7),ST

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

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.

Code Example
DE E(0-7) FSUBRP ST(1-7),ST

7.04-63 FTST – comparison with zeroEdit

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.

Code Example
D9 E4 FTST

7.04-64 FXAM – operand's type checkEdit

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:

C3 C2 C0 Operand type
0 0 0 - unknown format
0 0 1 - any non-numeric format
0 1 0 - correct real number
0 1 1 - infinity (tag = 10)
1 0 0 - zero (tag = 01)
1 0 1 - empty ST(0) register (tag = 11).
1 1 0 - 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.

Code Example
D9 E5 FXAM

7.04-65 FXCH – register's contents exchangeEdit

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

Code Example
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 exponentEdit

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).

Code Example
D9 F4 FXTRACT

7.04-67 FYL2X – logarithm of arbitrary baseEdit

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)).

Code Example
D9 F1 FYL2X

7.04-68 FYL2XP1 – sum of series logarithmEdit

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)).

Code Example
D9 F9 FYL2XP1