xv

CHAPTER 0.2 - BASES 2 AND 16

I'm making the assumption that if you are along for the ride you
review only.

BASE 2 AND BASE 16

Base 2 (binary) allows only 0s and 1s. Base 16 (hexadecimal)
allows 0 - 9, and then makes up the next six numbers by using the
letters A - F. A = 10, B=11, C=12, D=13, E=14 and F=15. You can
directly translate a hex number to a binary number and a binary
number to a hex number. A group of four digits in binary is the
same as a single digit in hex. We'll get to that in a moment.

The binary digits (BITS) are the powers of 2. The values of the
digits (in increasing order) are 1, 2, 4, 8, 16, 32, 64, 128, 256
and so on. 1 + 2 + 4 + 8 = 15, so the first four digits can
represent a hex number. This repeats itself every four binary
digits. Here are some numbers in binary, hex, and decimal

BINARY HEX DECIMAL

0100 4 4
1111 F 15
1010 A 10
0011 3 3

Let's go from binary to hex. Here's a binary number.

0110011010101101

To go from binary to hex, first divide the binary number up into
groups of four starting from the right.

0110 0110 1010 1101

Now simply change each group into a hex number.

0110 -> 4 + 2 -> 6
0110 -> 4 + 2 -> 6
1010 -> 8 + 2 -> A
1101 -> 8 + 4 + 1 -> D

and we have 66AD as the result. Similarly, to go from hex to
binary:

D39F

change each hex digit into a set of four binary digits:

D = 13 -> 8 + 4 + 1 -> 1101

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson

The PC Assembler Tutor xvi
______________________

3 -> 2 + 1 -> 0011
9 -> 8 + 1 -> 1001
F = 15 -> 8+4+2+1 -> 1111

and then put them all together:

1101001110011111

Of course, having 16 digits strung out like that makes it totally
unreadable, so in this book, if we are talking about a binary
number, it will always be separated every 4 digits for
clarity.{1}

All computers operate on binary data, so why do we use hex
numbers? Take a test. Copy these two binary numbers:

1011 1000 0110 1010 1001 0101 0111 1010
0111 1100 0100 1100 0101 0110 1111 0011

Now copy these two hex numbers:

B86A957A
7C4C56F3

As you can see, you recognize hex numbers faster and you make
fewer mistakes in transcription with hex numbers.

The rules for binary addition are easy:

0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0 (carry 1 to the next digit left)

similarly for binary subtraction:

0 - 0 = 0
0 - 1 = 1 (borrow 1 from the next digit left)
1 - 0 = 1
1 - 1 = 0

On the 8086, you can have a 16 bit (binary digit) number
represent a number from 0 - 65535. 65535 + 1 = 0 (65536). For
binary numbers, the boundary is 65535/0. You count up or down
through that boundary. The 8086 is a mod 65536 machine. That
means the things that are equivalent to 35631 mod 65536 are:{2}

____________________

1. This will not be true of the actual assembler code, since
the assembler demands an unseparated number.

2. 35631 + 29905 = 65536. -29905 = 35631 (mod 65536)

Chapter 0.2 - Binary and Hex Numbers xvii
____________________________________

(3*65536 + 35631) (3*65536 - 29905)
(2*65536 + 35631) (2*65536 - 29905)
(1*65536 + 35631) (1*65536 - 29905)
( 0 + 35631) ( 0 - 29905)
(-1*65536 + 35631) (-1*65536 - 29905)
(-2*65536 + 35631) (-2*65536 - 29905)
(-3*65536 + 35631) (-3*65536 - 29905)

The unsigned number 35631 and the signed number -29905 look the
same. They ARE the same. In all addition, they will operate in
the same fashion. The unsigned number will use CF (the carry
flag) and the signed number will use OF (the overflow flag).

On all 16 bit computers, 0-32767 is positive and 32768 - 65535 is
negative. Here's 32767 and 32768.

32767 0111 1111 1111 1111
32768 1000 0000 0000 0000

32768 and all numbers above it have the left bit 1. 32767 and all
numbers below it have the left bit 0. This is how to tell the
sign of a signed number. If the left bit is 0 it's positive and
if the left bit is 1 it's negative.

TWO'S COMPLEMENT

In base 10 we had 10s complement to help us with negative
numbers. In base 2, we have 2s complememt.

0 = 65536 = 65535 + 1

so we have:

1 0000 0000 0000 0000 = 1111 1111 1111 1111 + 1

To get the negative of a number, we subtract:

-49 = 0 - 49 = 65536 - 49 = 65535 - 49 + 1

(65536) 1111 1111 1111 1111 + 1
(49) 0000 0000 0011 0001
result 1111 1111 1100 1110 + 1 -> 1111 1111 1100 1111 (-49)
; - - - - -

-21874
(65536) 1111 1111 1111 1111 + 1
(21874) 0101 0101 0101 0111
result 1010 1010 1010 1000 + 1 -> 1010 1010 1010 1001 (-21847)
; - - - - -

-11628
(65536) 1111 1111 1111 1111 + 1
(11628) 0010 1101 0110 1100
result 1101 0010 1001 0011 + 1 -> 1101 0010 1001 0100 (-11628)
; - - - - -

The PC Assembler Tutor xviii
______________________

-1764
(65536) 1111 1111 1111 1111 + 1
(1764) 0000 0110 1110 0100
result 1111 1001 0001 1011 + 1 -> 1111 1001 0001 1100 (-1764)
; - - - - -

Notice that since:

1 - 0 = 1
1 - 1 = 0

when you subtract from 1, you are simply switching the value of
the subtrahend (that's the number that you subtract).

1 -> 0
0 -> 1

1 becomes 0 and 0 becomes 1. You don't even have to think about
it. Just switch the 1s to 0s and switch the 0s to 1s, and then
add 1 at the end. Well do one more:

-348
(65536) 1111 1111 1111 1111 + 1
(348) 0000 0001 0101 1100
result 1111 1110 1010 0011 + 1 -> 1111 1110 1010 0100 (-348)

Now two more, this time without the crutch of having the top
number visible. Remember, even though you are subtracting, all
you really need to do is switch 1s to 0s and switch 0s to 1s, and
then add 1 at the end.

-658

(658) 0000 0010 1001 0010
result 1111 1101 0110 1101 + 1 -> 1111 1101 0110 1110 (-658)
; - - - - -

-31403

(34103) 0111 1010 0100 0111
result 1000 0101 1011 1000 + 1 -> 1000 0101 1011 1001 (-31403)

SIGN EXTENSION

If you want to use larger numbers, it is possible to use multiple
words to represent them.{3} The arithmetic will be done 16 bits
at a time, but by using the method described in Chapter 0.1, it
is possible to add and subtract numbers of any length. One normal
length is 32 bits. How do you convert a 16 bit to a 32 bit
number? If it is unsigned, simply put 0s to the left:

0100 1100 1010 0111 -> 0000 0000 0000 0000 0100 1100 1010 0111
____________________

3. On the 8086, a word is 16 bits.

Chapter 0.2 - Binary and Hex Numbers xix
____________________________________

What if it is a signed number? The first thing we need to know
about signed numbers is what is positive and what is negative.
Once again, for reasons of symmetry, we choose positive to be
from 0000 0000 0000 0000 0000 0000 0000 0000
to 0111 1111 1111 1111 1111 1111 1111 1111
(hex 00000000 to 7FFFFFFF)
and we choose negative to be
from 1000 0000 0000 0000 0000 0000 0000 0000
to 1111 1111 1111 1111 1111 1111 1111 1111
(hex 10000000 to FFFFFFFF).{4}
This longer number system cycles
from 1111 1111 1111 1111 1111 1111 1111 1111
to 0000 0000 0000 0000 0000 0000 0000 0000
(hex FFFFFFFF to 00000000).
Notice that by using binary numbers we are innundating ourselves
with 1s and 0s.

If it is a positive signed number, it is still no problem (recall
that in our 16 bit system, a positive number is between 0000 0000
0000 0000 and 0111 1111 1111 1111, a negative signed number is
between 1000 0000 0000 0000 and 1111 1111 1111 1111). Just put 0s
to the left. Here are some positive signed numbers and their
conversions:

(1974)
0000 0111 1011 0110 -> 0000 0000 0000 0000 0000 0111 1011 0110
(1)
0000 0000 0000 0001 -> 0000 0000 0000 0000 0000 0000 0000 0001
(3909)
0000 1111 0100 0101 -> 0000 0000 0000 0000 0000 1111 0100 0101

If it is a negative number, where did its representation come
from in our 16 bit system? -x -> 1111 1111 1111 1111 + 1 -x =
1111 1111 1111 1111 - x + 1. This time it won't be FFFFh + 1 but
FFFFFFFFh + 1. Let's have some examples. (Here we have 8 bits to
the group because there is not enough space on the line to
accomodate 4 bits to the group).

16 BIT SYSTEM 32 BIT SYSTEM

-1964
11111111 11111111 + 1 11111111 11111111 11111111 11111111 + 1
00000111 10101100 00000000 00000000 00000111 10101100

11111000 01010011 + 1 11111111 11111111 11111000 01010011 + 1

11111000 01010100 11111111 11111111 11111000 01010100
____________________

4. Once again, the sign will be decided by the left hand
digit. If it is 0 it is a positive number; if it is 1 it is a
negative number.

The PC Assembler Tutor xx
______________________

-2867
11111111 11111111 + 1 11111111 11111111 11111111 11111111 + 1
00001011 00110011 00000000 00000000 00001011 00110011

11110100 11001100 + 1 11111111 11111111 11110100 11001100 + 1

11110100 11001101 11111111 11111111 11110100 11001101

-182
11111111 11111111 + 1 11111111 11111111 11111111 11111111 + 1
00000000 10110110 00000000 00000000 00000000 10110110

11111111 01001001 + 1 11111111 11111111 11111111 01001001 + 1

11111111 01001010 11111111 11111111 11111111 01001010

As you can see, all you need to do to sign extend a negative
number is to put 1s to the left.

Can't those 1s on the left become 0s when we add that 1 at the
end? No. In order for that to happen, the right 16 bits must be
1111 1111 1111 1111. But that can only happen if the number to be
negated is 0:

1111 1111 1111 1111 1111 1111 1111 1111 + 1
-0000 0000 0000 0000
1111 1111 1111 1111 1111 1111 1111 1111 + 1 ->

0000 0000 0000 0000 0000 0000 0000 0000

In all other cases, adding 1 does not carry anything out of the
right 16 bits.

It is impossible to truncate one of these 32 bit numbers to a 16
bit number without making the results unreliable. Here are two
examples:

+1,687,451
00000000 00011001 10111111 10011011 -> 10111111 10011011 (-16485)

-3,524,830
11111111 11001010 00110111 00100010 -> 00110111 00100010 (+14114)

Truncating has changed both the sign and the absolute value of
the number.

xxi

CHAPTER 0.3 - LOGIC

Programs use numbers a lot. But they also ask questions that
require a yes/no answer. Is there an 8087 chip in the computer?
Is there a color monitor; how about a monochrome monitor? Is
there keyboard input waiting to be processed? Are you going to
get lucky on your date on Friday? Or, since you are a computer
programmer, are you going to have a date this month? Did the file
open correctly? Have we reached end of file?

In order to combine these logical questions to our heart's
content, we need a few operations: AND, OR, XOR (exclusive or),
and NOT.

AND

If we have two sentences "A" and "B", then ("A" AND "B") is true
if (and only if) both "A" and "B" are true. "It is raining and I
am wet" is true only if both "It is raining" and "I am wet" are
true. If one or both are false, then 'A and B' is false. A
shortcut for writing this is to use a truth table. A truth table
tells under what conditions an expression is true or false. All
we need to know is whether each component expression is true or
false. T stands for true, F for false.

"A" "B" "A" AND "B"

T T T
T F F
F T F
F F F

Notice that the truth table does NOT say anything about whether
the expression makes sense. The sentence:

"It's hot and I am sweating."

is a reasonable expression which may or may not be true. It will
be true if both "It is hot" and "I am sweating" are true. But the
sentence:

"The trees are green and Quito is the Capital of Ecuador."

is pure garbage. It does not satisfy our idea of what a sensible
expression should be, and should NEVER be evaluated by means of a
truth table. The warranty on a truth table is, if the expression
makes sense, then the truth table will tell you under what
conditions it is true or false. If the expression does not make
sense, then the truth table tells you nothing.

Fortunately, this problem really belongs to philisophical logic.
When you use logical operators in your program, there will be a

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson

The PC Assembler Tutor xxii
______________________

well defined reason for each use. If you start doing screwy
things, your program probably won't run.

OR

There are two different types of OR alive and kicking in the
English language - the exclusive OR (A or B but not both) and the
inclusive OR (A or B or both).

A mother tells her child "You can have a piece of cake or a piece
of candy." Does this mean that he can have both if he wants? Of
course not. He can have one or the other, but not both. This is
XOR, the exclusive or. The truth table for this is:

"A" "B" "A" XOR "B"

T T F
T F T
F T T
F F F

'A XOR B' is true if exactly one of them is true. If they both
are true 'A XOR B' is false. If neither is true, 'A XOR B' is
false. Examples of XOR are:

1) We will either go to Italy or to Japan on our vacation.
3) He'll either buy a Lamborghini or a BMW.

Consider this sentence: "To go to Harvard, you need to have
connections or to be very smart." Do we want this to mean that if
you have connections but are very smart, you are automatically
excluded from going to Harvard? No. We want this to mean one or
the other or perhaps both. Sometimes you write this as 'and/or'
if you want to be absolutely clear. This is the inclusive OR. The
truth table for OR is:

"A" "B" "A" OR "B"

T T T
T F T
F T T
F F F

'A OR B' is true if one or both of them are true. If both are
false, then it is false. Examples of OR are:

1) They'll either go to Italy or to Austria on their
vacation.
2) I'll have either steak or shrimp at The Sizzler.
3) He'll buy either a paisley tie or a rep tie.

The three sentences for XOR and OR mimic each other on purpose.
In the English language, you know which type of OR is being used
by what is being talked about. You know intuitively which one

Chapter 0.3 - Logic xxiii
___________________

applies. If someone buys two different ties you are not suprised.
If someone buys two expensive cars at the same time you are quite
surprised.{1} With very few exceptions, if you confuse the two
you are doing it on purpose. If your father says "You can have
the car on Friday night or on Saturday night." and you don't
understand which OR applies, it's not his fault.

NOT

The final logical operation is NOT. The sentence: "It is not
raining." is false if it is raining, and true otherwise. The
truth table is:

"A" NOT "A"

T F
F T

This is self-explanatory.

Amazingly enough, this is all you need to know about logic to be
a quality programmer. Trying to make very complex combinations of
these logical operations may be fun for philosophy, but it is
death to a program. KISS is the operative word in programming.{2}

____________________

1. Especially if his job is working the cash register at
Sears.

2. Which, if you don't know, is Keep It Simple, Stupid.

xxiv

CHAPTER 0.4 - MEMORY

The basic unit of memory on 8086 machines is the byte.{1} This
means that in every memory cell we can store a number from 0 to
255. Each memory cell has an address. The first one in memory has

The registers on the 8086 are one word (two bytes) long. This
means that any register can store and operate on a number from 0
to 65,535. (It also has some registers which can operate on bytes
and can store and operate on numbers from 0 to 255.)

Memory is physically external to the 8086. Registers are
physically internal to the 8086; they are actually on the chip.

One of the ways that we can access memory on the 8086 is by
putting the address of a memory cell in a register and then
telling the 8086 that we want to use the data at that memory

Since each byte has its own address, and since we can't have a
number larger that 65,535 in any one register, it is impossible
to address more than 65,535 bytes with a single register. Back in
the dark ages, that might have been enough memory, but it sure
isn't enough nowdays.

Intel solved the problem by creating SEGMENTS. Each segment is
tell the 8086 where you want to go in memory by telling it which
segment you are in and what the address is within that segment.
Segments are numbered from 0 to 65,535. That is, there are 65,536
of them.

As a design decision, Intel decided that a segment should start
every 16 bytes. This decision was made in the late 70's and is
cast in stone. On the 8086, a segment starts every 16 bytes. Here
is a list of some segments, with the segment number and the
segment starting address in both decimal and hex.

0d 0h 0d 00h
1d 1h 16d 10h
2d 2h 32d 20h
3d 3h 48d 30h
4d 4h 64d 40h
____________________

1. As it is on all computers.

2. The last segments are actually less that 65,535, as will be
explained later.

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson

Chapter 0.4 - Memory xxv
____________________

200d C8h 3,200d C80h
21694d 54BEh 347,104d 54BE0h
51377d C8B1h 822,032d C8B10h

One thing you should note is that in hex, the segment number is
the same as the starting address, except that the starting
address has an extra 0 digit on the right.

These segments overlap. A new segment starts every 16 bytes, and
they are all 65,535 bytes long. This means that with the
exception of the first 16 bytes, any address in memory is in more
that one segment. Here are some addresses. The word "offset"
means the number of bytes from the beginning of the segment. (It
is possible for a memory cell to have an offset 0). The offset is
shown in both decimal (d) and hex (h):

Seg # Offset Offset
0 55d 37h
1 39d 27h
2 23d 17h
3 7d 7h

Thus the address 55 is in 4 different segments, and can be
addressed relative to any one of them.

Seg # Offset Offset
0 17,946d 461Ah
1 17,930d 460Ah
2 17,914d 45FAh
... ...
1120 26d 1Ah
1121 10d 0Ah

The address 17,946 is in 1122 different segments, and can be
addressed relative to any of them. Notice that as the segment
number goes up one segment, the offset address goes down 16 bytes
(10h).

4,096 different segments.

Because of the way the addresses are generated on the 8086, the
maximum memory address possible is 1,048,575 (FFFFF hex.) This
means that the final segments are less than 65,536 bytes long. In
the following numbers are decimal:

65,535d 1,048,560d 15d
65,534d 1,048,544d 31d
65,533d 1,048,528d 47d
... ... ...

The PC Assembler Tutor xxvi
______________________

64,000d 1,024,000d 24,575d

Let's look at these same numbers in hex:

FFFFh FFFF0h Fh
FFFEh FFFE0h 1Fh
FFFDh FFFD0h 2Fh
... ... ...
FA00h FA000h 5FFFh

The maximum addressable number is FFFFFh, which is why these
segments are cut short. There are 4,095 segments at the top which
are less than 65,536 bytes long. Don't worry, though, because
this top section of memory belongs to the operating system, and
your programs will never be put there. It will never affect you.

Back in the late 70s, a million bytes of memory seemed like a
lot. In the 60s, large mainframe computers had only a
half-million bytes of memory. In the 70s memory was still
exhorbitantly expensive. Nowdays, however, you practically need a
megabyte just to blow your nose. But this segmentation system is
cast in stone, so there is no way to get more memory on the
8086.{3}

In the beginning, when we make a program, we will use one segment
for the machine code, one segment for permanant data, and one
segment for temporary data. If we need it, this gives us 196,608
bytes of usable memory right off the bat. As you will see by the
time we are finished, ALL memory is addressable - we just need to
do more work to get to it all.

All this talk about segments and offsets may have you concerned.
If you have to keep track of all these offsets, programming is
going to be very difficult.{4} Not to worry. It is the
assembler's job to keep track of the offsets of every variable

____________________

3. This megabyte rule is unalterable. EMS (extended memory) is
actually a memory swapping program. On the 28086 and 80386 you
can have more than one megabyte of memory but the program can
only access one megabyte. The program reserves a section of its
one megabyte for a transfer area. It then calls EMS which
transfers the data from this extended memory to the transfer
area. It is in effect a RAM disk. It is like using a hard disk
but is much faster. If Intel had bitten the bullet with the 80286
and said that a segment would start every 256 bytes instead of
every 16 bytes, we would have 16 megabytes of directly accessible
memory instead of 1 megabyte. Hindsight is such a wonderful
thing.

4. Remember, an offset is just how many bytes a memory cell is
from the beginning of the segment.

Chapter 0.4 - Memory xxvii
____________________

and every label in your program.{5}

Which segments your program uses is decided by the operating
system when it loads your program into memory. It puts some of
this information into the 8086. At the start of the program, you
put the rest of the information into the 8086, and then you can

NUMBERS IN MEMORY

The largest number you can store in a single byte is 255. If you
are calculating the distance from the sun to Alpha Centauri in
inches, obviously one byte is not enough. Unfortunately, the 8086
can't really handle large numbers like that.{6} It can handle
numbers which are 16 bits (2 bytes) long. However, with
subprograms supplied with all compilers, we can handle large
numbers, though rather slowly if we don't use an 8087. All these
different programs need a standard way to write numbers in
memory, and this standard is supplied by Intel. The standard is :

(1) integers can be 1, 2, or 4 bytes long. This corresponds
to -128 to +127 , -32,768 to +32,767, and -2,147,483,648 to
+2,147,483,647.

(2) scientific floating point numbers which have an exponent
and can be very large. They come as 4 byte and 8 byte numbers.
We will not deal with them at all, but we need to know how
they are stored.

(3) Commercial or BCD numbers which occupy 1/2 byte per
digit. Since some of the 8086 instructions are concerned with
these we will cover them, but if you are not curious about
them, you can skip that section. The standard is a 10 byte
number.

Let's look at a number. For the rest of this section, all numbers
will be hex, and if a number is longer than one byte, we will
display it with a blank space between each byte. If it is a one
byte number - e.g. 3C, we know exactly where we are going to put
it. But what if a number is 4 bytes long - e.g. 2D F5 33 0A - and
we want to put it in memory starting at offset 264. We have two
choices:

2D F5 33 0A
267 2D 0A
266 F5 33
265 33 F5
____________________

5. As in other languages, a label is a name that marks a place
in the code. In BASIC, labels are actually numbers (such as 500
in the instruction GOTO 500). Labels are frowned on in Pascal and
C, but are the lifeblood of assembler language.

6. But fortunately, the 8087 can.

The PC Assembler Tutor xxviii
______________________

264 0A 2D

Neither choice is better than the other. Choice 1 puts the
right-most byte in low memory, choice 2 puts the right-most byte
in high memory.{7} The right-most byte is called the LEAST
SIGNIFICANT BYTE because it has the least effect on a number,
while the left-most byte is called the MOST SIGNIFICANT BYTE
because it has the most effect on a number. In fact, Intel picked
choice #1 for the 8086 (which has the least significant byte in
low memory), and Motorola picked choice #2 for the 68000 (which
has the most significant byte in low memory).

This is consistent for both the 8086 and the 8087: THE LEAST
SIGNIFICANT BYTE IS ALWAYS IN LOW MEMORY: EACH NUMBER IN MEMORY
STARTS WITH THE LEAST SIGNIFICANT BYTE. Remember that, and you'll
save yourself some trouble.

One problem you will run up against is that when we draw pictures
of memory, we often draw from left to right, that is:

When we do that, things start looking wierd. For 2D F5 33 0A we
have:

VALUE 0A 33 F5 2D

This is exactly backwards. Remember, memory doesn't go from left
to right, it goes UP from 0, and THE LEAST SIGNIFICANT BYTE IS
ALWAYS IN LOW MEMORY. You will certainly make some mistakes till
of a number is always in low memory. If you think of memory as
being VERTICAL:

1E A3 07 B5
1E 4782
A3 4781
07 4780
B5 4779

rather than being LEFT TO RIGHT:

Value B5 07 A3 1E

you will be much better off.

____________________

7. Low memory always means the low addresses and high memory

xxix

CHAPTER 0.5 - STYLE

Finally, it is time to say a word about style. Assembler code is
by its nature difficult to read. It divides any concept into a
number of sequential steps. If you have the BASIC statement:

MINUTES = DAYS * 1440

You get the idea because you can scan the line to see what is
wanted. The assembler code for the above line is: {1}

mov ax, days
mov bx, 1440
mul bx
mov minutes, ax

In BASIC, the concept was imbedded in the expression. In
assembler it was lost. This means two things. First, you must be
religious about documenting every step. If you come back to
something two or three months later and you haven't documented
what you are doing, it may take you longer to figure out what you
did than it would to completely rewrite what you did.

Secondly, if you are a person who likes code like this:

x = (y + k) / (z - 4)

you are headed for big trouble. At the assembler level it is
CRITICAL that you give every variable a name that signifies
exactly what it is. If you are counting the number of correct
answers, then the variable should be:

not 'K'. If you are looking at the remainder from a division,
then the variable should be:

remainder

not 'R'. If you try to use short names like 'x', 'k' and 'y', I
will guarantee that for every minute you save by not having to
type in long variable names, you will lose 10 minutes by not
being able to figure out what is going on when you reread the
code. In this tutorial we will use multiple words connected by
underscores:

first_positive_prime
median_age
oldest_student
____________________

1. Don't worry about what this code does. You will learn soon
enough.

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson

The PC Assembler Tutor xxx
______________________

The Microsoft assembler allows 31 significant characters in a
name. Even though there are several other characters allowed, we
will use only letters, the underscore, and numbers (where
appropriate):

approximation1
approximation2
approximation3

This should make your code similar to well written C or Pascal
code, and greatly increase the readability of the code.