First steps towards system programming under MS-DOS 7/Debugger's assembler commands
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.
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
- ^ 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.
- ^ 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.
- ^ 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]
, ora single register reference : [BX]
,[DI]
,[SI]
, ora sum of 2 registers : [BX+DI]
,[BX+SI]
, ora 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]
. - ^ 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]
. - Register names in expressions may be separated by minus sign, but this doesn't affect the result: sum is counted in any case.
- The default segment register may be changed by explicit specification of a prefix byte (7.02-01).
- When operand's type marker ("byte ptr", "word ptr"...) isn't present in assembler command, DEBUG.EXE determines type of operand according to the register, containing the other operand: 16-bit register (AX, BX...) sets word type, 8-bit register (AL,AH,BL...) sets byte type. If a command doesn't address to a register, then operand's type marker becomes a required item. In any case these markers may be truncated to 2 characters: "by", "wo", etc.
7.01 Control instructions
[edit | edit source]Having been switched to translation of assembler commands, DEBUG.EXE accepts commands and also control instructions. The latter are not translated into machine code, but rather affect the process of translation and may play a very important role.
7.01-01 DB – data bytes insertion instruction
[edit | edit source]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
- While unassembling machine codes there may be encountered byte(s), which can't be identified as machine commands, "known" to DEBUG.EXE. Then "DB" is displayed as a prefix to each such byte.
7.01-02 DW – data words insertion instruction
[edit | edit source]The DW (= Data Word) instruction informs DEBUG.EXE that the following string must be treated not as an assembler command, but rather as separate words of data, which should be written into assembled code "as they are". Each word consists of up to 4 hexadecimal digits. If there are less than 4 digits in a word, it will be automatically supplemented to 4 digits with higher order zeros. In assembled machine code the least significant 2 digits of each word constitute the first byte, and the most significant 2 digits constitute the following byte. String of words after the "DW" instruction may include group(s) of ASCII characters, enclosed in quotes or in double quotes. These characters (except the enclosing quotes) are translated into hexadecimal representation one byte per character, just as after the "DB" instruction (7.01-01). Comments after the DW instruction are not allowed. Here is a DW instruction usage example :
DW 71A0 F01 06D5 "other key" 0FFF
7.01-03 ORG – target address change instruction
[edit | edit source]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 | edit source]Empty line, containing no commands, no instructions, no data and no comments, is itself regarded by DEBUG.EXE as an instruction, forcing to return from translation of assembler commands, described in chapter 7, to normal control with those commands, described in articles 6.05-02 – 6.05-23. In order to input this instruction from keyboard you have to leave the last presented line empty and just press ENTER. When assembler commands are received from a command file via input redirection, return to normal control is induced by the first encountered empty line in this command file. Therefore a care should be taken about absence of empty lines inside any block of assembler commands in command file(s), and equally about presence of an empty line, marking the end of each block of assembler commands.
7.01-05 Semicolon – comments insertion instruction
[edit | edit source]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
- 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.
- 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.
- Semicolon can't be used to append commentaries to those assembler command lines, which begin with control instruction DB or DW, and also when DEBUG.EXE is not switched to translation of assembler commands.
7.02 Prefixes
[edit | edit source]In machine code of x86 platform CPU's several particular bytes are given a special prefix status. Each such byte is not a separate machine command. But a prefix byte, being encountered preceding a machine command, forces CPU to change the manner of its interpretation or execution. Effect of a prefix byte isn't spread beyond the nearest following command.
DEBUG.EXE allows to specify a prefix byte in a separate line before that assembler command, which is to be affected by prefix, for example :
CS: ADD byte ptr [BX],0F
Specification of a prefix byte just before the affected command in the same line is equally allowable :
CS: ADD byte ptr [BX],0F
Machine command's code may be preceded by up to four prefixes, if their effects don't contradict to each other. All prefixes in one command must be different, duplicate prefixes are not allowed.
7.02-01 Segment override prefixes
[edit | edit source]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
- Segment override prefixes can't affect default assignment of ES: register, in particular, for string commands CMPSB, CMPSW, INSB, INSW, MOVSB, MOVSB, SCASB, SCASW, STOSB, STOSW.
7.02-02 LOCK – system bus lock prefix
[edit | edit source]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
- Intel's CPUs don't allow combinations of LOCK prefix with any of repetition prefixes (7.02-03, 7.02-04) in one command.
- Modern processors, having more than 8 control registers, allow prefix byte F0h for access to control registers with MOV command.[Note 1 to 7.03-58] In this case F0h byte is interpreted not as LOCK prefix, but as a prefix for access to registers CR8–CR15.
7.02-03 Repetition prefix REPNZ
[edit | edit source]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 :
- check the CX==0 condition. If met, terminate the cycle. If not, decrement by a unit the number in CX register.
- reset zero flag into NZ state.
- perform that command, which is preceded by repetition prefix.
- 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
- 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.
- 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.
- When repetition prefix is used in combination with operand size override prefix (7.02-06), the prescribed number of repetitions is read not from 16-bit CX register, but from 32-bit ECX register. For such cases it's important to remind about preparation of a proper value in upper part (bits 31–16) of ECX register.
7.02-04 Repetition prefix REPZ
[edit | edit source]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
- Prefix REPZ is often used with commands CMPSB and CMPSW for comparison between two strings of characters – names, paths, signatures. When comparison cycle terminates, the result is expressed by state of ZR flag: the set state (ZR) proves identity, the reset state (NZ) is evidence of the difference.
7.02-05 Synchronizing prefixes WAIT and FWAIT
[edit | edit source]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 prefix
[edit | edit source]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
- Programs using operand size override prefix can't be executed by 16-bit CPUs.
- 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.
- 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.
- 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.
- Operand size override prefix always forces the non-default size of operands. When bit 6 in byte 06h of code segment descriptor[Note 5 to A.12-2] specifies default 32-bit size of operands, then prefix byte 66h forces 16-bit operand's size. Here and further in this book default 16-bit operand's size is implied, just as it is automatically set by CPU's "shadow" registers, when CPU begins to work in real mode after being switched on.
7.02-07 Address size override prefix
[edit | edit source]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
- Programs using address size override prefix can't be executed by 16-bit CPUs.
- 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.
- Address size override prefix always forces the non-default address size. When bit 6 in byte 06h of code segment descriptor[Note 5 to A.12-2] specifies default 32-bit address size, then prefix byte 67h forces 16-bit address size for the nearest following command. Here and further in this book default 16-bit address size is implied, just as it is automatically set by CPU's "shadow" registers, when CPU begins to work in real mode after being switched on.
7.02-08 Operation codes extension prefixes
[edit | edit source]Operation codes of x86 platform CPUs have been formed as a result of long evolution. At each stage of evolution a set of machine commands has been supplemented with new commands. Therefore several bytes have been used as operation code extension prefixes, affecting interpretation of operation codes in CPU's instruction decoder. These prefixes have no common specific function, except that each such prefix is able to introduce a separate group of various machine commands.
In the past, mostly on mainframe computers, byte FFh has been charged with mission of operation code extension prefix. Now it is regarded as first byte in operation codes of many different machine commands, described in part 7.03. Later bytes D8h–DFh were devoted to introduce arithmetic coprocessor's commands, described in part 7.04. In 1990s new commands for Pentium processor were introduced by prefix byte 0Fh ; several of these commands are mentioned in table 6.05-18.
Nowadays there is no more free bytes for being charged with prefix mission. For modern processors the SSE group of new commands was introduced by those combinations of operation codes with prefixes 66h (7.02-06), F2h (7.02-03), F3h (7.02-04), which previously were regarded invalid. New 64-bit processors transfer into prefix class the bytes 40h–4Fh, which are interpreted by all other x86 platform processors as commands DEC (7.03-20) and INC (7.03-27). Such changes can't be ignored, even if the goal of this book is limited to acquaintance with 16-bit programming under DOS. Recommendations for ensuring compatibility of 16-bit codes with modern processors are given in notes to descriptions of affected machine commands.
7.03 Commands for CPU
[edit | edit source]7.03-01 AAA – decimal correction of unpacked sum
[edit | edit source]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 division
[edit | edit source]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
- Machine codes "D5 (1-F)(0-9,B-F)" are improperly unassembled by DEBUG.EXE as "AAD ff" command.
- Numbers in packed decimal format can't be processed by AAD command, these must be unpacked beforehand.
7.03-03 AAM – decimal correction of a product
[edit | edit source]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
- Machine codes "D4 (1-F)(0-9,B-F)" are improperly unassembled by DEBUG.EXE as "AAM ff" command.
- A product of packed decimal bytes can't be corrected by AAM command. Packed decimal numbers must be unpacked before multiplication.
7.03-04 AAS – decimal correction of unpacked remainder
[edit | edit source]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 carry
[edit | edit source]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
- Machine codes "1(2,3) (C-F)(0-F)" and "82 (1,5,9,D)(0-7)" are also unassembled by DEBUG.EXE as ADC command.
7.03-06 ADD – binary addition
[edit | edit source]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
- Machine codes "0(2,3) (C-F)(0-F)" and "82 (0,4,8,C)(0-7)" are also unassembled by DEBUG.EXE as ADD command.
7.03-07 AND – logical "AND" operation
[edit | edit source]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
- Machine codes "2(2-3) (C-F)(0-F)" and "82 (2,6,A,E)(0-7)" are also unassembled by DEBUG.EXE as AND command.
7.03-08 CALL – call for a subroutine
[edit | edit source]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
- ^ 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.
- ^ 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.
- ^ 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.
- Machine code "FF D(8-F)" is unassembled by DEBUG.EXE as "CALL far bx".
- Repetition prefixes F2h (7.02-03), F3h (7.02-04) can't be applied to the CALL command.
7.03-09 CBW – byte to word conversion
[edit | edit source]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 reset
[edit | edit source]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 reset
[edit | edit source]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 reset
[edit | edit source]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
- Programmable interrupts are performed by INT command (7.03-28) in any case, regardless of the IF flag state.
- The CLI command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).
7.03-13 CMC – reversion of carry flag state
[edit | edit source]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 operands
[edit | edit source]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
- machine codes "3(A,B) (C-F)(0-F)" and "82 (3,7,B,F)(8-F)" are also unassembled by DEBUG.EXE as CMP command.
7.03-15 CMPSB – serial comparison of byte pairs
[edit | edit source]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
- When CMPSB command is preceded by repetition prefixes F2h or F3h, the order of operations within the cycle includes assignment of flags states, then incrementation (or decrementation) of index register's contents, and after that cycle termination condition check. Therefore, the offsets in index registers at the moment of cycle termination are pointing not to those bytes, which have caused cycle termination, but rather to the next pair of bytes.
7.03-16 CMPSW – serial comparison of word pairs
[edit | edit source]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 | edit source]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 sum
[edit | edit source]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 remainder
[edit | edit source]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 decrement
[edit | edit source]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
- Bytes 48h–4Fh can be interpreted by 64-bit processors as prefixes. Therefore 2-byte codes "FF C(8-F)" should be given preference over 1-byte codes 4(8-F). Codes "FF C(8-F)" are interpreted as "DEC bx" command by all x86 platform
processors and are properly unassembled by DEBUG.EXE, but during assembling these codes should be presented to DEBUG.EXE as data by DB instruction (7.01-01).
7.03-21 DIV – division of unsigned integers
[edit | edit source]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
- If division operation causes overflow in quotient register, CPU automatically generates an exception : a call for INT 00 handler (8.01-01). Outcome depends on that handler.
7.03-22 ESC – code transfer to asynchronous coprocessor
[edit | edit source]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 standstill
[edit | edit source]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
- 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.
- A way to determine that particular external interrupt, which has turned CPU out of a standstill state, is shown in article 8.01-09.
7.03-24 IDIV – division of signed integers
[edit | edit source]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
- If division operation causes overflow in quotient register, CPU automatically generates an exception: a call for INT 00 handler (8.01-01). Outcome depends on that handler.
7.03-25 IMUL – multiplication of signed integers
[edit | edit source]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
- Other forms of IMUL command with 2 explicit operands (codes 69h and 6Bh) are not supported by DEBUG.EXE.
7.03-26 IN – data input from port
[edit | edit source]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
- 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.
- The IN command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).
7.03-27 INC – a unity increment
[edit | edit source]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
- Bytes 40h–47h can be interpreted by 64-bit processors as prefixes. Therefore 2-byte codes "FF C(0-7)" should be given preference over 1-byte codes 4(0-7). Codes "FF C(0-7)" are interpreted as "INC bx" command by all x86 platform processors and are properly unassembled by DEBUG.EXE, but during assembling these codes should be presented to DEBUG.EXE as data by DB instruction (7.01-01).
7.03-28 INT – a call for interrupt handler
[edit | edit source]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
- 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.
- 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.
- 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.
- Data in stack are available to interrupt handlers. If the state of stack pointer (SP) is saved in BP register just after control transfer, then [BP+00] address points at return offset, [BP+02] address points at return segment, [BP+04] address points at flag's states of interrupted program.
7.03-29 INTO – a call for overflow handler
[edit | edit source]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
- The default INT 04 handler does nothing but returns control to the caller program. In order to obtain a desirable response to overflows the user has to prepare another INT 04 handler instead of the default one. New handler becomes active since its address is written into interrupt table (8.02-18).
7.03-30 IRET – return from interrupt handler
[edit | edit source]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
- 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.
- The IRET commands resets a queue of prefetched commands in CPU. This is done because commands decoding rules for interrupt handler may differ from those accepted for the caller program.
7.03-31 JA – Jump if above
[edit | edit source]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 below
[edit | edit source]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 equal
[edit | edit source]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 Zero
[edit | edit source]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 Greater
[edit | edit source]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 Equal
[edit | edit source]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 Lower
[edit | edit source]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 Equal
[edit | edit source]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 jump
[edit | edit source]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
- ^ 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).
- ^ 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.
- ^ 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.
- ^ 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.
- 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.
- Codes "FF E(8-F)" are unassembled by DEBUG.EXE as command "JMP far bx".
7.03-40 JNB – Jump if Not Below
[edit | edit source]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 Overflow
[edit | edit source]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 Sign
[edit | edit source]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 Zero
[edit | edit source]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 Overflow
[edit | edit source]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 Even
[edit | edit source]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 Odd
[edit | edit source]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 Sign
[edit | edit source]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 Zero
[edit | edit source]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 register
[edit | edit source]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 register
[edit | edit source]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
- Codes "C5 (C-F)(0-F)" are also unassembled by DEBUG.EXE as LDS command.
- 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.
- Both segment in DS register and offset in specified register may be used for addressing and be reassigned in the same operation; for example, command DS: LDS SI,[SI] is valid.
7.03-51 LEA – offset calculation
[edit | edit source]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 register
[edit | edit source]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
- Codes "C4 (C-F)(0-F)" are also unassembled by DEBUG.EXE as LES command.
- 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.
- Both segment in ES register and offset in specified register may be used for addressing and be reassigned in the same operation; for example, command
ES: LES DI,[DI]
is valid.
7.03-53 LODSB – serial copying of bytes
[edit | edit source]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 words
[edit | edit source]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 cycle
[edit | edit source]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 condition
[edit | edit source]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 condition
[edit | edit source]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 command
[edit | edit source]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
- ^ 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)
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 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 - The MOV command can't copy data into CS segment register; this can be done by control transfer commands only (CALL, JMP, RETF, etc.).
- 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.
- Codes 8(A,B) (C-F)(0-F), 8(C,E)(2,3,6,7,A,B,F)(0-F) and C(6,7) (C-F)(0-F) are also unassembled by DEBUG.EXE as MOV command.
7.03-59 MOVSB – serial copying of bytes
[edit | edit source]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 words
[edit | edit source]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 integers
[edit | edit source]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 reversal
[edit | edit source]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 operation
[edit | edit source]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 bits
[edit | edit source]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 operation
[edit | edit source]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
- 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.
- When OR command is applied to equal operands, these operands wouldn't be changed. For example, OR AX,AX command is often used just to set flags.
7.03-66 OUT – data output to port
[edit | edit source]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
- 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.
- The OUT command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).
7.03-67 POP – data ejection out of stack
[edit | edit source]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
- 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.
- Codes "8F (C-F)(0-F)" are unassembled by DEBUG.EXE as "POP bx".
7.03-68 POPF – restoration of flag's states out of stack
[edit | edit source]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
- 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.
- 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).
- 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 | edit source]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
- 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.
- 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.
- Code "FF F(0-7)" is also unassembled by DEBUG.EXE as "PUSH bx".
7.03-70 PUSHF – copying of flag's states into stack
[edit | edit source]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
- Being preceded by operand size override prefix 66h (7.02-06), the PUSHF command copies into stack 4 bytes from extended 32-bit flags register.[Note 4 to A.11-4] However, copying of the V86 mode flag state by PUSHF command is blocked by hardware.
7.03-71 RCL – leftward shift through carry flag
[edit | edit source]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 right
[edit | edit source]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 segment
[edit | edit source]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
- Stack's top position can be preserved at return offset, if return is executed by RET FFFE command.
- If code, assembled by DEBUG.EXE, is to be executed inside debugger's environment, then the RET command may be used to terminate execution of this code (example in 9.02-03).
7.03-74 RETF – return from another segment
[edit | edit source]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 leftward
[edit | edit source]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 right
[edit | edit source]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 register
[edit | edit source]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 right
[edit | edit source]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 borrow
[edit | edit source]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
- Codes "1(A,B) (C-F)(0-F)" and "82 (1,5,9,D)(8-F)" are also unassembled by DEBUG.EXE as SBB command.
7.03-80 SCASB – search for a particular byte
[edit | edit source]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
- When SCASB command is preceded by repetition prefixes F2h or F3h, the order of operations within the cycle includes assignment of flags states, then incrementation (or decrementation) of index register's contents, and after that cycle termination condition check. Therefore, the offset in DI register at the moment of cycle termination is pointing not to that data byte, which has caused cycle termination, but rather to the next byte.
7.03-81 SCASW – search for a particular word
[edit | edit source]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 left
[edit | edit source]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
- SHL command is an exact equivalent of SAL command, accepted by other assemblers, but DEBUG.EXE doesn't accept the SAL name.
- ^ 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
7.03-83 SHR – shift to the right
[edit | edit source]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
- ^ 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
7.03-84 STC – set carry flag
[edit | edit source]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 flag
[edit | edit source]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 flag
[edit | edit source]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
- The STI command wouldn't be executed, if privilege level of the current program is lower than privilege level for I/O operations, defined by bits 0Ch and 0Dh in flags register (A.11-4).
7.03-87 STOSB – filling memory with a byte
[edit | edit source]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 word
[edit | edit source]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 subtraction
[edit | edit source]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
- Codes "2(A,B) (C-F)(0-F)" and "82 (2,6,A,E)(8-F)" are also unassembled by DEBUG.EXE as SUB command.
7.03-90 TEST – logical test of bit's states
[edit | edit source]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 exchange
[edit | edit source]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 translation
[edit | edit source]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 operation
[edit | edit source]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
- 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.
- Codes "3(2,3) (C-F)(0-F)" and "82 (3,7,B,F)(0-7)" are also unassembled by DEBUG.EXE as XOR command.
7.04 Commands for arithmetic coprocessor
[edit | edit source]Those assembler's commands, whose name begins with letter "F" (float), are transferred for execution to math coprocessor. All modern computers are able to perform these commands, because their coprocessor is integrated in the main CPU.
Computers with obsolete processors, including some 486 models, may have no math coprocessor. Then execution of coprocessor's commands may be performed by software emulation, but for that two conditions must be met :
- first, generation of a call for INT 07 handler (8.01-08) must be ensured in response to each coprocessor's command. This is achieved by setting bit 02h ("coprocessor emulation") in control register CR0 (A.11-4).
- second, an appropriate INT 07 handler must be loaded, which is able to emulate execution of coprocessor's commands.
In some old computers both these conditions are met automatically due to their BIOS system, in some others the user has to bother about that. In any case presence or absence of arithmetical coprocessor is reported by INT 11 handler (8.01-36, A.11-1).
If there is a chance that your program will be executed by old CPUs with a separate coprocessor chip, then each coprocessor's command should be preceded by WAIT prefix (7.02-05), which synchronizes command's transfer from CPU to coprocessor. Modern CPUs have an integrated coprocessor with hardware synchronizing means. Therefore for modern CPUs the WAIT prefix is not needed, its presence is allowed, but most probably is ignored.
7.04-01 F2XM1 – approximation for fractional power of 2
[edit | edit source]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 value
[edit | edit source]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 values
[edit | edit source]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 up
[edit | edit source]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 transform
[edit | edit source]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
- 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.
- The default 10-byte binary real format includes mantissa (bits 0–63), power index (bits 64–78) and sign bit 79. By changing contents of precision control field in CWR register (note 2 to 7.04-35) coprocessor may be turned to 8-byte double precision format (52 bits – mantissa, 11 bits – power index) or to 4-byte single precision format (23 bits – mantissa, 8 bits – power index).
7.04-06 FBSTP – store with decimal transform
[edit | edit source]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 sign
[edit | edit source]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 flags
[edit | edit source]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 values
[edit | edit source]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
- Code "DC D(0-7)" is also unassembled by DEBUG.EXE as FCOM ST(0-7).
7.04-10 FCOMP – compare and shift stack up
[edit | edit source]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
- Codes "DC D(8-F)" and "DE D(0-7)" are also unassembled by DEBUG.EXE as FCOMP command.
7.04-11 FCOMPP – compare and twice shift stack up
[edit | edit source]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
- DEBUG.EXE unassembles code "DE D9" as "FCOMPP ST(1)", but while assembling doesn't accept the "ST(1)".
7.04-12 FDECSTP – DECrement Stack Top Pointer
[edit | edit source]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
- Coprocessor's stack pointer is a tree-stage reversible counter, involving bits 11, 12 and 13 of status word register SWR.
- Most commands pushing data into coprocessor's stack are performed by copying data into ST(7) register and decrementing coprocessor's stack pointer by 1, so that ST(7) register becomes renamed into ST(0). All such commands can't be performed, if ST(7) register originally isn't empty.
7.04-13 FDISI – disable interrupts
[edit | edit source]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 values
[edit | edit source]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 up
[edit | edit source]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 order
[edit | edit source]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 up
[edit | edit source]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 interrupts
[edit | edit source]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 free
[edit | edit source]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
- Code "DF C(0-7)" is also unassembled by DEBUG.EXE as FFREE command.
7.04-20 FIADD – addition with an integer
[edit | edit source]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 integer
[edit | edit source]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 up
[edit | edit source]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 integer
[edit | edit source]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 order
[edit | edit source]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 integer
[edit | edit source]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 integer
[edit | edit source]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 pointer
[edit | edit source]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
- The coprocessor's stack pointer is a tree-stage reversible counter, involving bits 11, 12 and 13 of status word register SWR.
- When incrementing of coprocessor's stack pointer is performed by other commands, then top stack register ST(0) after being renamed into ST(7) acquires status of empty register (tag 11b). Therefore access to former contents of ST(0) is lost. This operation is often referred to as popping ST(0) contents out of stack.
7.04-28 FINIT – setting coprocessor's initial state
[edit | edit source]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 integer
[edit | edit source]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 up
[edit | edit source]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 integer
[edit | edit source]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 order
[edit | edit source]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 value
[edit | edit source]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 constant
[edit | edit source]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 register
[edit | edit source]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
- 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.
- ^ Bits 9 and 8 in CWR register represent PC (= precision control) field. Default state 11b of PC field defines 10-byte format of operands. State 10b of PC field defines rounding to 8-byte format, state 00b – rounding to 4-byte format (7.04-05). However, reduction of precision doesn't make calculations faster.
7.04-36 FLDENV – loading into service registers
[edit | edit source]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" constant
[edit | edit source]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" constant
[edit | edit source]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" constant
[edit | edit source]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" constant
[edit | edit source]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" constant
[edit | edit source]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 constant
[edit | edit source]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 values
[edit | edit source]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 up
[edit | edit source]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 operation
[edit | edit source]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 | edit source]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 remainder
[edit | edit source]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 tangent
[edit | edit source]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
- 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.
- Obsolete arithmetic coprocessors (up to model 80287) require argument of FPTAN command to be within limits from 0 to PI/4.
7.04-49 FRNDINT – round to integer
[edit | edit source]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
- Bits 11 and 10 in coprocessor's CWR register constitute the RC (Rounding Control) field. States of all bits in CWR register can be written into memory by FSTCW command (7.04-55), and then states of desired bits can be intentionally changed. Changed states come into effect after loading back into CWR register by FLDCW command (7.04-35).
7.04-50 FRSTOR – restoration of coprocessor's state
[edit | edit source]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 state
[edit | edit source]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 2
[edit | edit source]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 root
[edit | edit source]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 value
[edit | edit source]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
- Code "DF D(0-7)" is also unassembled by DEBUG.EXE as FST command.
- FST command enables to copy new contents into a stack register, which is not free. Former contents of this register will be overwritten.
7.04-55 FSTCW – store a state of CWR register
[edit | edit source]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
- Later the stored state of CWR register can be restored by FLDCW command (7.04-35).
7.04-56 FSTENV – store states of service registers
[edit | edit source]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 up
[edit | edit source]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
- Codes "D9 D(8-F)" and "DF D(8-F)" are also unassembled by DEBUG.EXE as FSTP command.
7.04-58 FSTSW – store status word
[edit | edit source]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
- ^ CPU models 80486 and higher perform FSTSW AX operation (code "DF E0"), copying coprocessor's status word into CPU's AX register. This operation is not "known" to DEBUG.EXE, but DEBUG.EXE accepts it under its former name
ESC 3C,AL
.
7.04-59 FSUB – subtraction of real values
[edit | edit source]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 up
[edit | edit source]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 order
[edit | edit source]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 up
[edit | edit source]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 zero
[edit | edit source]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 check
[edit | edit source]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 exchange
[edit | edit source]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
- Codes "DD C(8-F)" and "DF C(8-F)" are also unassembled by DEBUG.EXE as FXCH command.
7.04-66 FXTRACT – separation of mantissa and exponent
[edit | edit source]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 base
[edit | edit source]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 logarithm
[edit | edit source]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 |