Appendix
"The Jump Program"
Jeff Duntemann
John.Wiley.And.Sons
Chapter 6: p137-138
An Uneasy Alliance:
The x86 CPU and Its
Segmented Memory System
Segmented Memory System
Transferring Control to Machine Instructions in Read-Only Memory
So far we've looked at locations in memory as containers for data. All well and good-but memory contains machine instructions as well. A very effective illustration of a machine instruction at a particular address is also provided by the ROM BIOS-and right next door to the BIOS revision date, at that. The machine instruction in question is located at address 0FFFF:0. Recall that, by convention, the next machine instruction to be executed is the one whose address is stored in CS:IP. Run DEBUG. Load the value 0FFFFH into code segment register CS, and 0 into instruction pointer IP. Then dump memory at 0FFFF:0.
- r cs
CS 1980
:ffff
- r ip
IP 0100
:0
- r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1980 ES=1980 SS=1980 CS=FFFF IP=0000 NV UP EI PL NZ NA PO NC
FFFF:0000 EA5BE000F0 JMP F000:E05B
- d cs:0
FFFF:0000 EA 5B E0 00 F0 30 34 2F-33 30 2F 38 37 00 FC B8 .[...04/30/87...
FFFF:0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
FFFF:0020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
FFFF:0030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
FFFF:0040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
FFFF:0050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
FFFF:0060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
FFFF:0070 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
Look at the third line of the register display, which we've been ignoring up until now. To the right of the address display FFFF:0000 is this series of five bytes: EA5BE000F0.
These five bytes make up the machine instruction we want. Notice that the first line of the memory dump begins with the same address, and, sure enough, shows us the same five bytes.
Trying to remember what machine instruction EA5BE000F0 is would try anyone's intellect, so DEBUG is a good sport and translates the five bytes into a more readable representation of the machine instruction. This translation is placed to the right of the binary machine code EA5BE000F0. We call this process of translating binary machine codes back into human-readable assembly language mnemonics unassembly or, more commonly, disassembly:
Trying to remember what machine instruction EA5BE000F0 is would try anyone's intellect, so DEBUG is a good sport and translates the five bytes into a more readable representation of the machine instruction. This translation is placed to the right of the binary machine code EA5BE000F0. We call this process of translating binary machine codes back into human-readable assembly language mnemonics unassembly or, more commonly, disassembly:
JMP F000:E05B. What this instruction does, quite simply, is tell the CPU to jump to the address 0F000:0E05B and begin executing the machine instructions located there. If we execute the machine instruction at CS:IP, that's what will happen: The CPU will jump to the address 0F000:0E05B and begin executing whatever machine instructions it finds there.
All IBM-compatible PCs have a JMP instruction at address 0FFFF:0. The address to which that JMP instruction jumps will be different for different makes and models of PC. This is why on your machine you won't necessarily see the exact five bytes EA5BE000F0, but whatever five bytes you find at 0FFFF:0, they will always begin with 0EAH. The 0EAH byte specifies that this instruction will be a JMP instruction. The remainder of the machine instruction is the address to which the CPU must jump. If that address as given in the machine instruction looks a little scrambled, well, it is...but that's the way the x86 CPUs do things. We return to the issue of funny-looking addresses a little later.
DEBUG has a command, G (for Go), that begins execution at the address stored in CS:IP. If you enter the G command and press Enter, the CPU will jump to the address built into the JMP instruction and begin executing machine instructions. What happens then? If you're running under DOS, your machine will go into a cold boot, just as it would if you powered down and powered up again. (So make sure you're ready for a reboot before you try it!)
This may seem odd. But consider: The CPU chip has to begin execution somewhere. When the CPU "wakes up" after being off all night with the power removed, it must get its first machine instruction from somewhere and start executing. Built into the silicon of the x86 CPU chips is the assumption that a legal machine instruction will exist at address 0FFFF:0. When power is applied to the CPU chip, the first thing it does is place 0FFFH in CS, and 0 in IP. Then it starts fetching instructions from the address in CS:IP and executing them, one at a time, in the manner that CPUs must. This is why all PCs have a JMP instruction at 0FFFF:0, and why this JMP instruction always jumps to the routines that bring the PC up from stone cold dead to fully operational.
Unfortunately, if you're running in a DOS window under Windows 9x or NT, jumping to 0FFFF:0 won't initiate a cold boot. Under Windows 9x, the JMP will close your DOS window. Under NT, it won't even do that...It'll just exit DEBUG. You see, Windows lives in protected mode, and it's...um...protected from little
tricks like idle jumps to 0FFFF:0.
tricks like idle jumps to 0FFFF:0.
But if you're running DOS-what the heck, go ahead: Load 0FFFFH into CS and 0 into IP, and press G.
Feel good?
It's what we call the feeling of power.
----------------------------------------------
Chapter 10: p233-234
Bits, Flags, Branches, and Tables:
Easing into Mainstream Assembly Programming
Bits, Flags, Branches, and Tables:
Easing into Mainstream Assembly Programming
A jump is just that: an abrupt change in the flow of instruction execution. Ordinarily, instructions are executed one after the other, in order, moving from low memory toward high memory. Jump instructions alter the address of the next instruction to be executed. Execute a jump instruction, and zap! All of a sudden you're somewhere else in the code segment. A jump instruction can move execution forward in memory or backward.
It can bend execution back into a loop. (And it can tie your program logic in knots . . .)
There are two kinds of jumps: conditional and unconditional. An unconditional jump is a jump that always happens. It takes this form:
JMP <label>
JMP <label>
When this instruction executes, the sequence of execution moves to the instruction located at the label specified by <label>. It's just that simple. The unconditional JMP instruction is of limited use by itself. It almost always works in conjunction with the conditional jump instructions that test the state of the various x86 flags...
Conditional Jumps
A conditional jump instruction is one of those fabled tests I introduced in Chapter 1. When executed, a conditional jump tests something, usually one of the flags in the Flags register. If the flag being tested happens to be in a particular state, execution may jump to a label somewhere else in the code segment, or it may simply fall through to the next instruction in sequence. This two-way nature is important. A conditional jump instruction either jumps, or it falls through. Jump, or no jump. It can't jump to one of two places, or three. Whether it jumps or not depends on the current value of one single bit within the CPU.
For example, there is a flag that is set to 1 by certain instructions when the result of that instruction is zero: the Zero flag ZF. The DEC (DECrement) instruction is a good example. DEC subtracts one from its operand. If by that subtraction the operand becomes zero, ZF is set to 1. One of the conditional jump instructions, JZ (Jump if Zero) tests ZF. If ZF is found set to 1, a jump occurs, and execution transfers to a label. If ZF is found to be 0, execution falls through to the next instruction in line.
Here's a simple (and nonoptimal) example, using instructions you should already understand:
Conditional Jumps
A conditional jump instruction is one of those fabled tests I introduced in Chapter 1. When executed, a conditional jump tests something, usually one of the flags in the Flags register. If the flag being tested happens to be in a particular state, execution may jump to a label somewhere else in the code segment, or it may simply fall through to the next instruction in sequence. This two-way nature is important. A conditional jump instruction either jumps, or it falls through. Jump, or no jump. It can't jump to one of two places, or three. Whether it jumps or not depends on the current value of one single bit within the CPU.
For example, there is a flag that is set to 1 by certain instructions when the result of that instruction is zero: the Zero flag ZF. The DEC (DECrement) instruction is a good example. DEC subtracts one from its operand. If by that subtraction the operand becomes zero, ZF is set to 1. One of the conditional jump instructions, JZ (Jump if Zero) tests ZF. If ZF is found set to 1, a jump occurs, and execution transfers to a label. If ZF is found to be 0, execution falls through to the next instruction in line.
Here's a simple (and nonoptimal) example, using instructions you should already understand:
mov byte [Counter],17 | ; We're going to do this 17 times |
WorkLoop: call DoWork | ; Process the data |
dec byte [Counter] | ; Subtract 1 from the counter |
jz AllDone | ; If the counter is zero, we're done! |
jmp WorkLoop | ; Otherwise, go back and execute the loop again |
The label AllDone isn't shown in the example because it's somewhere else in the program, maybe a long way off. The important thing is that the JZ instruction is a two-way switch. If ZF is equal to 1, execution moves to the location marked by the label AllDone. If ZF is equal to 0, execution falls through to the next instruction in sequence. Here, that would be the unconditional jump instruction JMP WorkLoop.
This simple loop is one way to perform a call to a procedure some set number of times. A count value is stored in a variable named Counter. The procedure is called. After control returns from the procedure,
Counter is decremented by one. If that drops the counter to 0, the procedure has been called the full number of times, and the loop sends execution elsewhere. If the counter still has some count in it, execution loops back to the procedure call and begins the loop again.
Note the use of an unconditional jump instruction to close the loop.
This simple loop is one way to perform a call to a procedure some set number of times. A count value is stored in a variable named Counter. The procedure is called. After control returns from the procedure,
Counter is decremented by one. If that drops the counter to 0, the procedure has been called the full number of times, and the loop sends execution elsewhere. If the counter still has some count in it, execution loops back to the procedure call and begins the loop again.
Note the use of an unconditional jump instruction to close the loop.
[al] Any genuine course will find one confronted by the "sorcerer's curriculum", whereby one tends towards repose in concentrated learning of a specific subject matter involving required reading of certain books; he may obtain these on his own, but most often they will find the student when time for that knowledge becomes relevant. "Assembly Language Step by Step" is not necessarily one of those books, though may be required reading for some according to what their path dictates. It is added here as reference guide rather than a course of study per se.
No comments:
Post a Comment