- •Table of Contents
- •Foreword
- •Do Not Pass GO
- •Counting in Martian
- •Octal: How the Grinch Stole Eight and Nine
- •Hexadecimal: Solving the Digit Shortage
- •From Hex to Decimal and from Decimal to Hex
- •Arithmetic in Hex
- •Binary
- •Hexadecimal as Shorthand for Binary
- •Switches, Transistors, and Memory
- •The Shop Foreman and the Assembly Line
- •The Box That Follows a Plan
- •DOS and DOS files
- •Compilers and Assemblers
- •The Assembly Language Development Process
- •DEBUG and How to Use It
- •Chapter 5: NASM-IDE: A Place to Stand Give me a lever long enough, and a place to stand, and I will move the Earth.
- •NASM-IDE's Place to Stand
- •Using NASM-IDE's Tools
- •NASM-IDE's Editor in Detail
- •Other NASM-IDE Features
- •The Nature of Segments
- •16-Bit and 32-Bit Registers
- •The Three Major Assembly Programming Models
- •Reading and Changing Registers with DEBUG
- •Assembling and Executing Machine Instructions with DEBUG
- •Machine Instructions and Their Operands
- •Reading and Using an Assembly Language Reference
- •Rally Round the Flags, Boys!
- •Using Type Specifiers
- •The Bones of an Assembly Language Program
- •Assembling and Running EAT.ASM
- •One Program, Three Segments
- •Last In, First Out via the Stack
- •Using DOS Services through INT
- •Boxes within Boxes
- •Using BIOS Services
- •Building External Libraries of Procedures
- •Creating and Using Macros
- •Bits Is Bits (and Bytes Is Bits)
- •Shifting Bits
- •Flags, Tests, and Branches
- •Assembly Odds 'n Ends
- •The Notion of an Assembly Language String
- •REP STOSW, the Software Machine Gun
- •The Semiautomatic Weapon: STOSW without REP
- •Storing Data to Discontinuous Strings
- •Chapter 12: The Programmer's View of Linux Tools and Skills to Help You Write Assembly Code under a True 32-Bit OS
- •Prerequisites-Yukkh!
- •NASM for Linux
- •What's GNU?
- •The make Utility and Dependencies
- •Using the GNU Debugger
- •Your Work Strategy
- •Genuflecting to the C Culture
- •A Framework to Build On
- •The Perks of Protected Mode
- •Characters Out
- •Characters In
- •Be a Time Lord
- •Generating Random Numbers
- •Accessing Command-Line Arguments
- •Simple File I/O
- •Conclusion: Not the End, But Only the Beginning
- •Where to Now?
- •Stepping off Square One
- •Notes on the Instruction Set Reference
- •AAA Adjust AL after BCD Addition
- •ADC Arithmetic Addition with Carry
- •ADD Arithmetic Addition
- •AND Logical AND
- •BT Bit Test (386+)
- •CALL Call Procedure
- •CLC Clear Carry Flag (CF)
- •CLD Clear Direction Flag (DF)
- •CMP Arithmetic Comparison
- •DEC Decrement Operand
- •IMUL Signed Integer Multiplication
- •INC Increment Operand
- •INT Software Interrupt
- •IRET Return from Interrupt
- •J? Jump on Condition
- •JMP Unconditional Jump
- •LEA Load Effective Address
- •MOV Move (Copy) Right Operand into Left Operand
- •NOP No Operation
- •NOT Logical NOT (One's Complement)
- •OR Logical OR
- •POP Pop Top of Stack into Operand
- •POPA Pop All 16-Bit Registers (286+)
- •POPF Pop Top of Stack into Flags
- •POPFD Pop Top of Stack into EFlags (386+)
- •PUSH Push Operand onto Top of Stack
- •PUSHA Push All 16-Bit GP Registers (286+)
- •PUSHAD Push All 32-Bit GP Registers (386+)
- •PUSHF Push 16-Bit Flags onto Stack
- •PUSHFD Push 32-Bit EFlags onto Stack (386+)
- •RET Return from Procedure
- •ROL Rotate Left
- •ROR Rotate Right
- •SBB Arithmetic Subtraction with Borrow
- •SHL Shift Left
- •SHR Shift Right
- •STC Set Carry Flag (CF)
- •STD Set Direction Flag (DF)
- •STOS Store String
- •SUB Arithmetic Subtraction
- •XCHG Exchange Operands
- •XOR Exclusive Or
- •Appendix C: Web URLs for Assembly Programmers
- •Appendix D: Segment Register Assumptions
- •Appendix E: What's on the CD-ROM?
- •Index
- •List of Figures
- •List of Tables
Arithmetic in Hex
As you become more and more skilled in assembly language, you'll be doing more and more arithmetic in base 16. You may even (good grief) come to do it in your head. Still, it takes some practice.
Addition and subtraction are nothing more than what we know in decimal, with a few extra digits tossed in for flavor. The trick is nothing more than knowing your addition tables to 0FH. This is best done not by thinking to yourself, "Now, if C is 12 and F is 15, then C + F is 12 + 15, which is 27 decimal but 1BH." Instead, you should simply say inside your head, "C + F is 1BH."
Yes, that's asking a lot. But I ask you now, as I will ask you again on this journey, Do you wanna hack assembly…or do you just wanna fool around? It takes practice to learn the piano, and it takes practice to get really greased up on the foundation concepts of assembly language programming.
So let me sound like an old schoolmarm and tell you to memorize the following. Make flash cards if you must:
If nothing else, this exercise should make you glad computers don't work in base 64.
Columns and Carries
With all of the single-column additions committed (more or less) to memory, you can tackle multicolumn addition. It works pretty much the same way it does with decimal. Add each column starting from the right, and carry into the next column anytime a single column's sum exceeds 0FH.
For example:
11
2F 3 1 A DH
+9 6 B A 0 7H C 5 E B B 4H
Carefully work this one through, column by column. The sum of the first column (that is, the rightmost) is 14H, which cannot fit in a single column, so we must carry the one into the next column to the left. Even with the additional 1, however, the sum of the second column is 0BH, which fits in a single column and no carry is required.
Keep on adding toward the left. The second-to-last column will again overflow, and you will need to carry the one into the last column. As long as you have your single-digit sums memorized, it's a snap.
Well, more or less.
Now, here's something you should take note of:
The most you can ever carry out of a single-column addition of two numbers is 1.
It doesn't matter what base: 16, 10, fooby, or 2. You will either carry a 1 (in Martian, foo) out of a column, or carry nothing at all. This fact surprises people for some reason, so ask yourself: What two single digits in old familiar base 10 can you add that will force you to carry a 2? The largest digit is 9, and 9 + 9 = 18. Put down the 8 and carry the 1. Even if you have to add in a carry from a previous column, that will bring you up (at most) to 19. Again, you carry a 1 and no more. This is important when you add numbers on paper, or within the silicon of your CPU, as we'll learn a few chapters on.
Subtraction and Borrows
If you have your single-column sums memorized, you can usually grind your way through subtraction with a shift into a sort of mental reverse: "If E + 6 equals 14H, then 14H - E must equal 6." The alternative is memorizing an even larger number of tables, and since I haven't memorized them, I won't ask you to.
But over time, that's what tends to happen. In hex subtraction, you should be able to dope out any given single-column subtraction by turning a familiar hexadecimal sum inside-out. And just as with base 10, multicolumn subtractions are done column by column, one column at a time:
F76CH
- A05BH
5711H
During your inspection of each column, you should be asking yourself: "What number added to the bottom number yields the top number?" Here, you should know from your tables that B + 1 = C, so the difference between B and C is 1. The leftmost column is actually more challenging: What number added to A gives you F? Chin up; even I have to think about it on an off-day.
The problems show up, of course, when the top number in a column is smaller than its corresponding bottom number. Then (like the federal government on a bomber binge) you have no recourse but to borrow.
Borrowing is one of those grade-school rote-learned processes that very few people really understand. (To understand it is tacit admittance that something of New Math actually stuck, horrors.) From a height, what happens in a borrow is that one count is taken from a column and applied to the column on its right. I say applied rather than added to because in moving from one column to the column on its right, that single count is multiplied by 10, where 10 represents the number base. (Remember that 10 in octal has a value of 8, while 10 in hexadecimal has a value of 16.)
It sounds worse than it is. Let's look at a borrow in action, and you'll get the idea.
92H
-4 FH
Here, the subtraction in the rightmost column can't happen as-is, because F is larger than 2. So, we borrow from the next column to the left.
Nearly 30 years out of the past, I can still hear old Sister Marie Bernard toughing it out on the blackboard, albeit in base 10: "Cross out the 9; make it an 8. Make the 2 a 12. And 12 minus F is what, class?" It's 3, Sister. And that's how a borrow works. (I hope the poor dear will forgive me for putting hex bytes in her mouth…)
Think about what happened there, functionally. We subtracted 1 from the 9 and added 10H to the 2. One obvious mistake is to subtract 1 from the 9 and add 1 to the 2, which (need I say it?) won't work. Think of it this way: We're moving part of one column's surplus value over to its right, where some extra value is needed. The overall value of the upper number doesn't change (which is why we call it a borrow and not a steal), but the recipient of the loan is increased by 10, not 1.
After the borrow, what we have looks something like this:
812H
- 4 FH
(On Sister Marie Bernard's blackboard, we crossed out the 9 and made it an 8. I just made it an 8. Silicon has advantages over chalk-except that the 8's earlier life as a 9 is not so obvious.)
And of course, once we're here, the columnar subtractions all work out, and we discover that the difference is 43H.
People sometimes ask if you ever have to borrow more than 1. The answer, plainly, is no. If you borrow 2, for example, you would add 20 to the recipient column, and 20 minus any single digit remains a twodigit number. That is, the difference won't fit into a single column. Subtraction contains an important symmetry with addition:
The most you ever need to borrow in any single-column subtraction of two numbers is 1.
Borrows across Multiple Columns
Understanding that much about borrows gets you most of the way there. But, as life is wont, you will frequently come across a subtraction similar to this:
F 0 0 0H
- 3 B 6 CH
Column 1 needs to borrow, but neither column 2 nor column 3 have anything at all to lend. Back in grade school, Sister Marie Bernard would have rattled out with machine-gun efficiency: "Cross out the F, make it an E. Make the 0 a 10. Then cross it out, make it an F. Make the next 0 a 10; cross it out, make it an F. Then make the last 0 a 10." Got that? (I got it. In Catholic school, the consequences of not getting it are too terrible to consider.)
What happens is that the middle two 0s act as loan brokers between the F and the rightmost 0, keeping their commission in the form of enough value to allow their own columns' subtractions to take place. Each column to the right of the last column borrows 10 from its neighbor to the left, and loans 1 to the neighbor on its right. After all the borrows trickle through the upper number, what we have looks like this (minus all of Sister's cross-outs):
EF F10H
-3 B 6 CH
At this point, each columnar subtraction can take place, and the difference is B494H.
In remembering your grade-school machinations, don't fall into the old decimal rut of thinking, "cross out the 10, make it a 9." In the world of hexadecimal, 10H - 1 = F. Cross out the 10, make it an F.
What's the Point?
… if you have a hex calculator, or a hex-capable screen calculator? The point is practice. Hexadecimal is the lingua franca of assemblers, to multiply-mangle a metaphor. The more you burn a gut-level understanding of hex into your reflexes, the easier assembly language will be. Furthermore, understanding the internal structure of the machine itself will be much easier if you have that intuitive grasp of hex values. We're laying important groundwork here. Take it seriously now and you'll lose less hair later on.