Assembly Language StepbyStep Programming with DOS and Linux 2nd Ed [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Assembly Language StepbyStep Programming with DOS and Linux 2nd Ed [Electronic resources] - نسخه متنی

Jeff Duntemann

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید









Assembling and Executing Machine Instructions with DEBUG


The most obvious way to experiment with machine instructions is to build a short program out of them and watch it go. This can easily be done (and we'll be doing it a lot in later chapters), but it's far from the fastest way to do things. Editing source code and assembling it (and linking, when you must link) all take time, and when you only want to look at one machine instruction in action (rather than a crew of them working together), the full development cycle is overkill.

Once more, we turn to DEBUG.

At the close of the last chapter we got a taste of a DEBUG feature called unassembly, which is a peculiar way of saying what most of us call disassembly. This is the reverse of the assembly process we looked at in detail in Chapter 3. Disassembly is the process of taking a binary machine instruction such as 42H and converting it into its more readable assembly language equivalent, INC DX.

In addition to all its other tools, DEBUG also contains a simple assembler, suitable for taking assembly language mnemonics such as INC DX and converting them to their binary machine code form. (That is, taking "INC DX" from you, and translating it to 42H.) Later on we'll use a stand-alone assembler program called NASM to assemble complete assembly language programs. For the time being, we can use DEBUG to do things one or two instructions at a time and get a feel for the process.


Assembling a MOV Instruction


The single most common activity in assembly language work is getting data from here to there. There are several specialized ways to do this, but only one truly general way: The MOV instruction. MOV can move a byte, word (16 bits), or double word (32 bits) of data from one register to another, from a register into memory, or from memory into a register. What MOV cannot do is move data directly from one address in memory to a different address in memory. (To do that, you need two separate MOV instructions—one from memory to a register, and second from the register back out to memory.)

The name MOV is a bit of a misnomer, since what is actually happening is that data is copied from a source to a destination. Once copied to the destination, however, the data does not vanish from the source, but continues to exist in both places. This conflicts a little with our intuitive notion of moving something, which usually means that something disappears from a source and reappears at a destination.

Because MOV is so general and obvious in its action, it's a good place to start in working with DEBUG's assembler.

Bring up DEBUG and use the R command to display the current state of the registers. You should see something like this:


- r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1980 ES=1980 SS=1980 CS=1980 IP=0100 NV UP EI PL NZ NA PO NC
1980:0100 701D JO 011F

We more or less ignored the third line of the register display in the previous chapter. Now let's think a little bit more about what it means.

When DEBUG is loaded without a specific file to debug, it simply takes the empty region of memory where a file would have been loaded (had a file been loaded when DEBUG was invoked) and treats it as though a program file were really there. The registers all get default values, most of which are zero. IP, however, starts out with a value of 0100H, and the code segment register CS gets the segment address of DEBUG's workspace, which is theoretically empty.

But ... memory is never really empty. A byte of memory always contains some value, whether true garbage that happened to reside in memory at power-up time, or else a leftover value remaining from the last time that byte of memory was used in some computing operation. In the preceding register dump, memory at CS:IP contains a JO instruction. This rather obscure instruction (Jump on Overflow) was not placed there deliberately, but is simply DEBUG's interpretation of the two bytes 701DH that happen to reside at CS:IP. Most likely, the 701D value was part of some data table belonging to the last program to use that area of memory. It could have been part of a word processor file, or a spreadsheet, or anything else. Just don't think that some program necessarily put a JO instruction in memory. Machine instructions are just numbers, after all, and what numbers in memory do depends completely on how you interpret them—and what utility program you feed them to.

DEBUG's internal assembler assembles directly into memory, and places instructions one at a time—as you enter them at the keyboard—into memory at CS:IP. Each time you enter an instruction, IP is incremented to the next free location in memory. So, by continuing to enter instructions, you can actually type an assembly language program directly into memory.

Try it. Type the A command (for Assemble) and press Enter. DEBUG responds by displaying the current value of CS:IP and then waits for you to enter an assembly language instruction. Type MOV AX,1. Press Enter. DEBUG again displays CS:IP and waits for a second instruction. It will continue waiting for instructions until you press Enter without typing anything. Then you'll see DEBUG's dash prompt again.

Now, use the R command again to display the registers. You should see something like this:


- r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1980 ES=1980 SS=1980 CS=1980 IP=0100 NV UP EI PL NZ NA PO NC
1980:0100 B80100 MOV AX,0001

The registers haven't changed—but now the third line shows that the JO instruction is gone, and that the MOV instruction you entered has taken its place. Notice once again that CS contains 1980H, and IP contains 0100H. The address of the MOV instruction is shown as 1980:0100; in other words, at CS:IP.


Executing a MOV Instruction with the Trace Command


Note that you haven't executed anything. You've simply used DEBUG's Assemble command to write a machine instruction into a particular location in memory.

There are two ways to execute machine instructions from within DEBUG. One way is to execute a program in memory, starting at CS:IP. This means that DEBUG will simply start the CPU executing whatever sequence of instructions begins at CS:IP. We looked at the G command very briefly at the end of the last chapter, when we found the JMP instruction that reboots your PC on power-up, and used G to execute that instruction. The command is quite evocative: Go. But don't type G just yet ...

Here's the reason: You haven't entered a program. You've entered one instruction, and one instruction does not a program make. The instruction after your MOV instruction could be anything at all, recalling that DEBUG is simply interpreting garbage values in memory as random machine instructions. A series of random machine instructions could easily go berserk, locking your system into an endless loop or writing zeroes over an entire segment of memory that may contain part of DOS or Windows, or of DEBUG itself. We'll use DEBUG's G command a little later, once we've constructed a complete program in memory.

Go executes programs in memory starting at CS:IP; Trace executes the single instruction at CS:IP.

For now, let's consider the mechanism DEBUG has for executing one machine instruction at a time. It's called Trace, and you invoke it by typing T. The Trace command will execute the machine instruction at CS:IP, then give control of the machine back to DEBUG. Trace is generally used to single-step a machine-code program one instruction at a time, in order to watch what it's up to every step of the way. For now, it's a fine way to execute a single instruction and examine that instruction's effects.

So type T. DEBUG will execute the MOV instruction you entered at CS:IP, and then immediately display the registers before returning to the dash prompt. You'll see this:


AX=0001 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1980 ES=1980 SS=1980 CS=1980 IP=0103 NV UP EI PL NZ NA PO NC
1980:0103 6E DB 6E

Look at the first line. DEBUG says AX is now equal to 0001. It held the default value 0000 before; obviously, your MOV instruction worked.

And there's something else to look at here: The third line shows an instruction called DB at CS:IP. Not quite true—DB is not a machine instruction, but an assembly language directive that means Define Byte. DB has other uses, but in this case it's simply DEBUG's way of saying that the number 6EH does not correspond to any machine instruction. It is truly a garbage byte sitting in memory, doing nothing. Executing a 6EH byte as though it were an instruction, however, could cause your machine to do unpredictably peculiar things, up to and including locking up hard.

Remember, of course, that the 6EH was what happened to lie in memory one address up from the MOV AX,1 instruction on my machine at that particular time. You almost certainly encountered something else when you tried the experiment just now. In fact, I just rebooted my machine and tried it again and found an XCHG BP,[8001] instruction there instead. There's nothing meaningful about the instructions you find in memory with DEBUG this way. DEBUG is interpreting random values in memory as instructions, so almost any instruction may turn up—and if the random values do not represent a legal machine instruction, you'll see a DB directive instead.


/ 166