7.0 Names, Instructions, Operators, and Operands
Although program features like good comments, proper spacing of statements,
and good modularization can help yield programs that are more readable;
ultimately, a programmer must read the instructions in a program to understand
what it does. Therefore, do not underestimate the importance of making your
statements as readable as possible. This section deals with this issue.
7.1 Names
According to studies done at IBM, the use of high-quality identifiers in a
program contributes more to the readability of that program than any other
single factor, including high-quality comments. The quality of your identifiers
can make or break your program; program with high-quality identifiers can be
very easy to read, programs with poor quality identifiers will be very difficult
to read. There are very few "tricks" to developing high-quality names; most of
the rules are nothing more than plain old-fashion common sense. Unfortunately,
programmers (especially C/C++ programmers) have developed many arcane naming
conventions that ignore common sense. The biggest obstacle most programmers have
to learning how to create good names is an unwillingness to abandon existing
conventions. Yet their only defense when quizzed on why they adhere to
(existing) bad conventions seems to be "because that's the way I've always done
it and that's the way everybody else does it."
The aforementioned researchers at IBM developed several programs with the
following set of attributes:
- Bad comments, bad names
- Bad comments, good names
- Good comments, bad names
- Good comments, good names
As should be obvious, the programs that had bad comments and names were the
hardest to read; likewise, those programs with good comments and names were the
easiest to read. The surprising results concerned the other two cases. Most
people assume good comments are more important than good names in a program. Not
only did IBM find this to be false, they found it to be really false.
As it turns out, good names are even more important that good comments in a
program. This is not to say that comments are unimportant, they are extremely
important; however, it is worth pointing out that if you spend the time to write
good comments and then choose poor names for your program's identifiers, you've
damaged the readability of your program despite the work you've put into your
comments. Quickly read over the following code:
mov ax, SignedValue
cwd
add ax, -1
rcl dx, 1
mov AbsoluteValue, dx
Question: What does this code compute and store in the AbsoluteValue
variable?
- The sign extension of SignedValue.
- The negation of SignedValue.
- The absolute value of SignedValue.
- A boolean value indicating that the result is positive or negative.
- Signum(SignedValue) (-1, 0, +1 if neg, zero, pos).
- Ceil(SignedValue)
- Floor(SignedValue)
The obvious answer is the absolute value of SignedValue. This is also
incorrect. The correct answer is signum:
mov ax, SignedValue ;Get value to check.
cwd ;DX = FFFF if neg, 0000 otherwise.
add ax, 0ffffh ;Carry=0 if ax is zero, one
otherwise.
rcl dx, 1 ;DX = FFFF if AX is neg, 0 if
ax=0,
mov Signum, dx ; 1 if ax>0.
Granted, this is a tricky piece of code[16].
Nonetheless, even without the comments you can probably figure out what the code
sequence does even if you can't figure out how it does it:
mov ax, SignedValue
cwd
add ax, 0ffffh
rcl dx, 1
mov Signum, dx
Based on the names alone you can probably figure out that this code computes
the signum function. This is the "understanding 80% of the code" referred to
earlier. Note that you don't need misleading names to make this code
unphathomable. Consider the following code that doesn't trick you by using
misleading names:
mov ax, x
cwd
add ax, 0ffffh
rcl dx, 1
mov y, dx
This is a very simple example. Now imagine a large program that has many
names. As the number of names increase in a program, it becomes harder to keep
track of them all. If the names themselves do not provide a good clue to the
meaning of the name, understanding the program becomes very difficult.
- Enforced Rule:
- All identifiers appearing in an assembly language program must be
descriptive names whose meaning and use are clear.
Since labels (i.e., identifiers) are the target of jump and call
instructions, a typical assembly language program will have a large number of
identifiers. Therefore, it is tempting to begin using names like "label1,
label2, label3, ..." Avoid this temptation! There is always a reason you are
jumping to some spot in your code. Try to describe that reason and use that
description for your label name.
- Rule:
- Never use names like "Lbl0, Lbl1, Lbl2, ..." in your program.
-
7.1.1 Naming Conventions
Naming conventions represent one area in Computer Science where there are far
too many divergent views (program layout is the other principle area). The
primary purpose of an object's name in a programming language is to describe the
use and/or contents of that object. A secondary consideration may be to describe
the type of the object. Programmers use different mechanisms to handle these
objectives. Unfortunately, there are far too many "conventions" in place, it
would be asking too much to expect any one programmer to follow several
different standards. Therefore, this standard will apply across all languages as
much as possible.
The vast majority of programmers know only one language - English. Some
programmers know English as a second language and may not be familiar with a
common non-English phrase that is not in their own language (e.g., rendezvous).
Since English is the common language of most programmers, all identifiers should
use easily recognizable English words and phrases.
- Rule:
- All identifiers that represent words or phrases must be English words or
phrases.
7.1.2 Alphabetic Case Considerations
A case-neutral identifier will work properly whether you compile it with a
compiler that has case sensitive identifiers or case insensitive identifiers. In
practice, this means that all uses of the identifiers must be spelled exactly
the same way (including case) and that no other identifier exists whose only
difference is the case of the letters in the identifier. For example, if you
declare an identifier "ProfitsThisYear" in Pascal (a case-insensitive language),
you could legally refer to this variable as "profitsThisYear" and
"PROFITSTHISYEAR". However, this is not a case-neutral usage since a case
sensitive language would treat these three identifiers as different names.
Conversely, in case-sensitive languages like C/C++, it is possible to create two
different identifiers with names like "PROFITS" and "profits" in the program.
This is not case-neutral since attempting to use these two identifiers in a case
insensitive language (like Pascal) would produce an error since the
case-insensitive language would think they were the same name.
- Enforced Rule:
- All identifiers must be "case-neutral."
Different programmers (especially in different languages) use alphabetic case
to denote different objects. For example, a common C/C++ coding convention is to
use all upper case to denote a constant, macro, or type definition and to use
all lower case to denote variable names or reserved words. Prolog programmers
use an initial lower case alphabetic to denote a variable. Other comparable
coding conventions exist. Unfortunately, there are so many different conventions
that make use of alphabetic case, they are nearly worthless, hence the following
rule:
- Rule:
- You should never use alphabetic case to denote the type, classification,
or any other program-related attribute of an identifier (unless the language's
syntax specifically requires this).
There are going to be some obvious exceptions to the above rule, this
document will cover those exceptions a little later. Alphabetic case does have
one very useful purpose in identifiers - it is useful for separating words in a
multi-word identifier; more on that subject in a moment.
To produce readable identifiers often requires a multi-word phrase. Natural
languages typically use spaces to separate words; we can not, however, use this
technique in identifiers. Unfortunatelywritingmultiwordidentifiers
makesthemalmostimpossibletoreadifyoudonotdosomethingtodistiguishtheindividualwords
(Unfortunately writing multiword identifiers makes them almost impossible to
read if you do not do something to distinguish the individual words). There are
a couple of good conventions in place to solve this problem. This standard's
convention is to capitalize the first alphabetic character of each word in the
middle of an identifier.
- Rule:
- Capitalize the first letter of interior words in all multi-word
identifiers.
Note that the rule above does not specify whether the first letter of an
identifier is upper or lower case. Subject to the other rules governing case,
you can elect to use upper or lower case for the first symbol, although you
should be consistent throughout your program.
Lower case characters are easier to read than upper case. Identifiers written
completely in upper case take almost twice as long to recognize and, therefore,
impair the readability of a program. Yes, all upper case does make an identifier
stand out. Such emphasis is rarely necessary in real programs. Yes, common C/C++
coding conventions dictate the use of all upper case identifiers. Forget them.
They not only make your programs harder to read, they also violate the first
rule above.
- Rule:
- Avoid using all upper case characters in an identifier.
7.1.3 Abbreviations
The primary purpose of an identifier is to describe the use of, or value
associated with, that identifier. The best way to create an identifier for an
object is to describe that object in English and then create a variable name
from that description. Variable names should be meaningful, concise, and
non-ambiguous to an average programmer fluent in the English language. Avoid
short names. Some research has shown that programs using identifiers whose
average length is 10-20 characters are generally easier to debug than programs
with substantially shorter or longer identifiers.
Avoid abbreviations as much as possible. What may seem like a perfectly
reasonable abbreviation to you may totally confound someone else. Consider the
following variable names that have actually appeared in commercial software:
NoEmployees, NoAccounts, pend
The "NoEmployees" and "NoAccounts" variables seem to be boolean variables
indicating the presence or absence of employees and accounts. In fact, this
particular programmer was using the (perfectly reasonable in the real world)
abbreviation of "number" to indicate the number of employees and the number of
accounts. The "pend" name referred to a procedure's end rather than any pending
operation.
Programmers often use abbreviations in two situations: they're poor typists
and they want to reduce the typing effort, or a good descriptive name for an
object is simply too long. The former case is an unacceptable reason for using
abbreviations. The second case, especially if care is taken, may warrant the
occasional use of an abbreviation.
- Guideline:
- Avoid all identifier abbreviations in your programs. When necessary, use
standardized abbreviations or ask someone to review your abbreviations.
Whenever you use abbreviations in your programs, create a "data dictionary" in
the comments near the names' definition that provides a full name and
description for your abbreviation.
The variable names you create should be pronounceable. "NumFiles" is a much
better identifier than "NmFls". The first can be spoken, the second you must
generally spell out. Avoid homonyms and long names that are identical except for
a few syllables. If you choose good names for your identifiers, you should be
able to read a program listing over the telephone to a peer without overly
confusing that person.
- Rule:
- All identifiers should be pronounceable (in English) without having to
spell out more than one letter.
7.1.4 The Position of Components Within an Identifier
When scanning through a listing, most programmers only read the first few
characters of an identifier. It is important, therefore, to place the most
important information (that defines and makes this identifier unique) in the
first few characters of the identifier. So, you should avoid creating several
identifiers that all begin with the same phrase or sequence of characters since
this will force the programmer to mentally process additional characters in the
identifier while reading the listing. Since this slows the reader down, it makes
the program harder to read.
- Guideline:
- Try to make most identifiers unique in the first few character positions
of the identifier. This makes the program easier to read.
- Corollary:
- Never use a numeric suffix to differentiate two names.
Many C/C++ Programmers, especially Microsoft Windows programmers, have
adopted a formal naming convention known as "Hungarian Notation." To quote Steve
McConnell from Code Complete: "The term 'Hungarian' refers both to the fact that
names that follow the convention look like words in a foreign language and to
the fact that the creator of the convention, Charles Simonyi, is originally from
Hungary." One of the first rules given concerning identifiers stated that all
identifiers are to be English names. Do we really want to create "artificially
foreign" identifiers? Hungarian notation actually violates another rule as well:
names using the Hungarian notation generally have very common prefixes, thus
making them harder to read.
Hungarian notation does have a few minor advantages, but the disadvantages
far outweigh the advantages. The following list from Code Complete and other
sources describes what's wrong with Hungarian notation:
- Hungarian notation generally defines objects in terms of basic machine
types rather than in terms of abstract data types.
- Hungarian notation combines meaning with representation. One of the
primary purposes of high level language is to abstract representation away.
For example, if you declare a variable to be of type integer, you shouldn't
have to change the variable's name just because you changed its type to real.
- Hungarian notation encourages lazy, uninformative variable names. Indeed,
it is common to find variable names in Windows programs that contain only type
prefix characters, without an descriptive name attached.
- Hungarian notation prefixes the descriptive name with some type
information, thus making it harder for the programming to find the descriptive
portion of the name.
- Guideline:
- Avoid using Hungarian notation and any other formal naming convention that
attaches low-level type information to the identifier.
Although attaching machine type information to an identifier is generally a
bad idea, a well thought-out name can successfully associate some high-level
type information with the identifier, especially if the name implies the type or
the type information appears as a suffix. For example, names like "PencilCount"
and "BytesAvailable" suggest integer values. Likewise, names like "IsReady" and
"Busy" indicate boolean values. "KeyCode" and "MiddleInitial" suggest character
variables. A name like "StopWatchTime" probably indicates a real value.
Likewise, "CustomerName" is probably a string variable. Unfortunately, it isn't
always possible to choose a great name that describes both the content and type
of an object; this is particularly true when the object is an instance (or
definition of) some abstract data type. In such instances, some additional text
can improve the identifier. Hungarian notation is a raw attempt at this that,
unfortunately, fails for a variety of reasons.
A better solution is to use a suffix phrase to denote the type or class of an
identifier. A common UNIX/C convention, for example, is to apply a "_t" suffix
to denote a type name (e.g., size_t, key_t, etc.). This convention succeeds over
Hungarian notation for several reasons including (1) the "type phrase" is a
suffix and doesn't interfere with reading the name, (2) this particular
convention specifies the class of the object (const, var, type, function, etc.)
rather than a low level type, and (3) It certainly makes sense to change the
identifier if it's classification changes.
- Guideline:
- If you want to differentiate identifiers that are constants, type
definitions, and variable names, use the suffixes "_c", "_t", and "_v",
respectively.
- Rule:
- The classification suffix should not be the only component that
differentiates two identifiers.
Can we apply this suffix idea to variables and avoid the pitfalls? Sometimes.
Consider a high level data type "button" corresponding to a button on a Visual
BASIC or Delphi form. A variable name like "CancelButton" makes perfect sense.
Likewise, labels appearing on a form could use names like "ETWWLabel" and
"EditPageLabel". Note that these suffixes still suffer from the fact that a
change in type will require that you change the variable's name. However,
changes in high level types are far less common than changes in low-level types,
so this shouldn't present a big problem.
7.1.5 Names to Avoid
Avoid using symbols in an identifier that are easily mistaken for other
symbols. This includes the sets {"1" (one), "I" (upper case "I"), and "l" (lower
case "L")}, {"0" (zero) and "O" (upper case "O")}, {"2" (two) and "Z" (upper
case "Z")}, {"5" (five) and "S" (upper case "S")}, and ("6" (six) and "G" (upper
case "G")}.
- Guideline:
- Avoid using symbols in identifiers that are easily mistaken for other
symbols (see the list above).
Avoid misleading abbreviations and names. For example, FALSE shouldn't be an
identifier that stands for "Failed As a Legitimate Software Engineer." Likewise,
you shouldn't compute the amount of free memory available to a program and stuff
it into the variable "Profits".
- Rule:
- Avoid misleading abbreviations and names.
You should avoid names with similar meanings. For example, if you have two
variables "InputLine" and "InputLn" that you use for two separate purposes, you
will undoubtedly confuse the two when writing or reading the code. If you can
swap the names of the two objects and the program still makes sense, you should
rename those identifiers. Note that the names do not have to be similar, only
their meanings. "InputLine" and "LineBuffer" are obviously different but you can
still easily confuse them in a program.
- Rule:
- Do not use names with similar meanings for different objects in your
programs.
In a similar vein, you should avoid using two or more variables that have
different meanings but similar names. For example, if you are writing a
teacher's grading program you probably wouldn't want to use the name
"NumStudents" to indicate the number of students in the class along with the
variable "StudentNum" to hold an individual student's ID number. "NumStudents"
and "StudentNum" are too similar.
- Rule:
- Do not use similar names that have different meanings.
Avoid names that sound similar when read aloud, especially out of context.
This would include names like "hard" and "heart", "Knew" and "new", etc.
Remember the discussion in the section above on abbreviations, you should be
able to discuss your problem listing over the telephone with a peer. Names that
sound alike make such discussions difficult.
- Guideline:
- Avoid homonyms in identifiers.
Avoid misspelled words in names and avoid names that are commonly misspelled.
Most programmers are notoriously bad spellers (look at some of the comments in
our own code!). Spelling words correctly is hard enough, remembering how to
spell an identifier incorrectly is even more difficult. Likewise, if a word is
often spelled incorrectly, requiring a programer to spell it correctly on each
use is probably asking too much.
- Guideline:
- Avoid misspelled words and names that are often misspelled in identifiers.
If you redefine the name of some library routine in your code, another
program will surely confuse your name with the library's version. This is
especially true when dealing with standard library routines and APIs.
- Enforced Rule:
- Do not reuse existing standard library routine names in your program
unless you are specifically replacing that routine with one that has similar
semantics (i.e., don't reuse the name for a different purpose).
7.2 Instructions, Directives, and Pseudo-Opcodes
Your choice of assembly language sequences, the instructions themselves, and
your choice of directives and pseudo-opcodes can have a big impact on the
readability of your programs. The following subsections discuss these
problems.
7.2.1 Choosing the Best Instruction Sequence
Like any language, you can solve a given problem using a wide variety of
solutions involving different instruction sequences. As a continuing example,
consider (again) the following code sequence:
mov ax, SignedValue ;Get value to check.
cwd ;DX = FFFF if neg, 0000 otherwise.
add ax, 0ffffh ;Carry=0 if ax is zero.
rcl dx, 1 ;DX = FFFF if AX is neg, 0 if AX=0,
mov Signum, dx ; 1 if AX>0.
Now consider the following code sequence that also computes the signum
function:
mov ax, SignedValue ;Get value to check.
cmp ax, 0 ;Check the sign.
je GotSignum ;We're done if it's zero.
mov ax, 1 ;Assume it was positive.
jns GotSignum ;Branch if it was positive.
neg ax ;Else return -1 for negative
values.
GotSignum: mov Signum, ax
Yes, the second version is longer and slower. However, an average person can
read the instruction sequence and figure out what it's doing; hence the second
version is much easier to read than the first. Which sequence is best? Unless
speed or space is an extremely critical factor and you can show that this
routine is in the critical execution path, then the second version is obviously
better. There is a time and a place for tricky assembly code; however, it's rare
that you would need to pull tricks like this throughout your code.
So how does one choose appropriate instruction sequences when there are many
possible ways to accomplish the same task? The best way is to ensure that you
have a choice. Although there are many different ways to accomplish an
operation, few people bother to consider any instruction sequence other than the
first one that comes to mind. Unfortunatley, the "best" instruction sequence is
rarely the first instruction sequence that comes to most people's minds[17]. In order to make a choice, you have to have a
choice to make. That means you should create at least two different code
sequences for a given operation if there is ever a question concerning the
readability of your code. Once you have at least two versions, you can choose
between them based on your needs at hand. While it is impractical to "write your
program twice" so that you'll have a choice for every sequence of instructions
in the program, you should apply this technique to particularly bothersome code
sequences.
- Guideline:
- For particularly difficult to understand sections of code, try solving the
problem several different ways. Then choose the most easily understood
solution for actual incorporation into your program.
One problem with the above suggestion is that you're often too close to your
own work to make decisions like "this code isn't too hard to understand, I don't
have to worry about it." It is often a good idea to have someone else review
your code and point out those sections they find hard to understand[18].
- Guideline:
- Take advantage of reviews to determine those sections of code in your
program that may need to be rewritten to make them easier to understand.
7.2.2 Control Structures
Ralph Griswold[19] once said (roughly) the
following about C, Pascal, and Icon: "C makes it easy to write hard to read
programs[20], Pascal makes it hard to write hard to
read programs, and Icon makes it easy to write easy to read programs." Assembly
language can be summed up like this: "Assembly language makes it hard to write
easy to read programs and easy to write hard to read programs." It takes
considerable discipline to write readable assembly language programs; but it can
be done. Sadly, most assembly code you find today is extremely poorly written.
Indeed, that state of affairs is the whole reason for this document. Once you
get past issues like comments and naming conventions, issues like program
control flow and data structure design have among the largest impacts on program
readability. Since most assembly languages lack structured control flow
constructs, this is one area where undisciplined programmers can really show how
poorly they can write their code. One need look no farther than the public
domain code on the Internet, or at Microsoft's sample code for that matter[21], to see abundant examples of poorly written
assembly language code.
Fortunately, with a little discipline it is possible to write readable
assembly language programs. How you design your control structures can have a
big impact on the readability of your programs. The best way to do this can be
summed up in two words: avoid spaghetti.
Spaghetti code is the name given to a program that has a large number of
intertwined branches and branch targets within a code sequence. Consider the
following example:
jmp L1
L1: mov ax, 0
jmp L2
L3: mov ax, 1
jmp L2
L4: mov ax, -1
jmp L2
L0: mov ax, x
cmp ax, 0
je L1
jns L3
jmp L4
L2: mov y, ax
This code sequence, by the way, is our good friend the Signum function. It
takes a few moments to figure this out because as you manually trace through the
code you find yourself spending more time following jumps around than you do
looking at code that computes useful results. Now this is a rather extreme
example, but it is also fairly short. A longer code sequence code become just as
obfuscated with even fewer branches all over the place.
Spaghetti code is given this name because it resembles a bowl of spaghetti.
That is, if we consider a control path in the program a spaghetti noodle,
spaghetti code contains lots of intertwined branches into and out of different
sections of the program. Needless to say, most spaghetti programs are difficult
to understand, generally contain lots of bugs, and are often inefficient (don't
forget that branches are among the slowest executing instructions on most modern
processors).
So how to we resolve this? Easy by physically adopting structured programming
techniques in assembly language code. Of course, 80x86 assembly language doesn't
provide if..then..else..endif, while..endwhile, repeat..until, and other such
statements[22], but we can certainly simulate them.
Consider the following high level language code sequence:
if(expression) then
<< statements to execute if expression is true
>>
else
<< statements to execute if expression is false
>>
endif
Almost any high level language program can figure out what this type of
statement will do. Assembly languge programmers should leverage this knowledge
by attempting to organize their code so it takes this same form. Specifically,
the assembly language version should look something like the following:
<< Assembly code to compute value of expression
>>
JNxx ElsePart ;xx is the opposite condition we want to
check.
<< Assembly code corresponding to the then portion
>>
jmp AroundElsePart
ElsePart:
<< Assembly code corresponding to the else portion
>>
AroundElsePart:
For an concrete example, consider the following:
if ( x=y ) then
write( 'x = y' );
else
write( 'x <> y' );
endif;
; Corresponding Assembly Code:
mov ax, x
cmp ax, y
jne ElsePart
print "x=y",nl
jmp IfDone
ElsePart: print "x<>y",nl
IfDone:
While this may seem like the obvious way to organize an if..then.else..endif
statement, it is suprising how many people would naturally assume they've got to
place the else part somewhere else in the program as follows:
mov ax, x
cmp ax, y
jne ElsePart
print "x=y",nl
IfDone:
.
.
.
ElsePart: print "x<>y",nl
jmp IfDone
This code organization makes the program more difficult to follow. Most
programmers have a HLL background and despite a current assignment, they still
work mostly in HLLs. Assembly language programs will be more readable if they
mimic the HLL control constructs[23].
For similar reasons, you should attempt to organize your assembly code that
simulates while loops, repeat..until loops, for loops, etc., so that the code
resembles the HLL code (for example, a while loop should physically test the
condition at the beginning of the loop with a jump at the bottom of the
loop).
- Rule:
- Attempt to design your programs using HLL control structures. The
organization of the assembly code that you write should physically resemble
the organization of some corresponding HLL program.
Assembly language offers you the flexibility to design arbitrary control
structures. This flexibility is one of the reasons good assembly language
programmers can write better code than that produced by a compiler (that can
only work with high level control structures). However, keep in mind that a fast
program doesn't have to contain the tightest possible code in every sequence.
Execution speed is nearly irrelevant in most parts of the program. Sacrificing
readability for speed isn't a big win in most of the program.
- Guideline:
- Avoid control structures that don't easily map to well-known high level
language control structures in your assembly language programs. Deviant
control structures should only appear in small sections of code when
efficiency demands their use.
7.2.3 Instruction Synonyms
MASM defines several synonyms for common instructions. This is especially
true for the conditional jump and "set on condition code" instructions. For
example, JA and JNBE are synonyms for one another. Logically, one could use
either instruction in the same context. However, the choice of synonym can have
an impact on the readability of a code sequence. To see why, consider the
following:
if( x <= y ) then
<< true statements>>
else
<< false statements>>
endif
; Assembly code:
mov ax, x
cmp ax, y
ja ElsePart
<< true code >>
jmp IfDone
ElsePart: << false code >>
IfDone:
When someone reads this program, the "JA" statement skips over the true
portion. Unfortunately, the "JA" instruction gives the illusion we're checking
to see if something is greater than something else; in actuality, we're testing
to see if some condition is less than or equal, not greater than. As such, this
code sequence hides some of the original intent of high level algorithm. One
solution is to swap the false and true portions of the code:
mov ax, x
cmp ax, y
jbe ThenPart
<< false code >>
jmp IfDone
ThenPart: << true code >>
IfDone:
This code sequence uses the conditional jump that matches the high level
algorithm's test (less than or equal). However, this code is now organized in a
non-standard fashion (it's an if..else..then..endif statement). This hurts the
readability more than using the proper jump helped it. Now consider the
following solution:
mov ax, x
cmp ax, y
jnbe ElsePart
<< true code >>
jmp IfDone
ElsePart: << false code >>
IfDone:
This code is organized in the traditional if..then..else..endif fashion.
Instead of using JA to skip over the then portion, it uses JNBE to do so. This
helps indicate, in a more readable fashion, that the code falls through on below
or equal and branches if it is not below or equal. Since the instruction (JNBE)
is easier to relate to the original test (<=) than JA, this makes this
section of code a little more readable.
- Rule:
- When skipping over some code because some condition has failed (e.g., you
fall into the code because the condition is successful), always use a
conditional jump of the form "JNxx" to skip over the code section. For
example, to fall through to a section of code if one value is less than
another, use the JNL or JNB instruction to skip over the code. Of course, if
you are testing a negative condition (e.g., testing for equality) then use an
instruction of the form Jx to skip over the code.
|