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

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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









REP STOSW, the Software Machine Gun


The best way to cement all that string background information in your mind is to see a string instruction at work. In this section I lay out a very useful video display tool that makes use of the simplest string instruction, STOSW. (Think: STOre String by Word.) The discussion involves something called a prefix, which I haven't gone into yet. Bear with me for now. We'll discuss prefixes in a little while.


Machine-Gunning the Video Display Buffer


The ClrScr procedure we discussed earlier relied on BIOS to handle the actual clearing of the screen. BIOS is very much a black box, and we're not expected to know how it works. The trouble with BIOS is that it only knows how to clear the screen to blanks. Some programs (such as the most recent releases of Borland/Turbo Pascal) give themselves a stylish, sculpted look by clearing the screen to one of the PC's halftone characters, which are character codes 176 to 178. BIOS can't do this. If you want the halftone look, you'll have to do it yourself. It doesn't involve anything more complex than replicating a single word value (two bytes) into every position in your video refresh buffer.

Such things should always be done in tight loops. The obvious way is to put the video refresh buffer segment into the extra segment register ES, the refresh buffer offset into DI, the number of words in your refresh buffer into CX, the word value to clear the buffer to into AX, and then code up a tight loop this way:


Clear: mov ES:[DI],AX ; Copy AX to ES:DI
inc DI ; Bump DI to next *word* in buffer,
inc DI ; which means incrementing by 1 twice
dec CX ; Decrement CX by one position
jnz Clear ; And loop again until CX is 0

This will work. It's even tolerably fast, especially on newer CPUs. But all of the preceding code is equivalent to this one single instruction:


rep stosw

Really. Really.

There are two parts to this instruction, actually. As I said, REP is a new type of critter, called a prefix. We'll get back to it. Right now, let's look at STOSW. The mnemonic means STOre String by Word. Like all the string instructions, STOSW makes certain assumptions about some CPU registers. It works only on the destination string, so DS and SI are not involved. However, these assumptions must be respected and dealt with:



ES must be loaded with the segment address of the destination string (that is, the string into which data will be stored). This is automatically the case in flat model and does not have to be set up by you.



DI must be loaded with the offset address of the destination string. (Think: DI, the destination index.)



CX (think: the Count register) must be loaded with the number of times the copy of AX is to be stored into the string. Note that this does not mean the size of the string in bytes!



AX must be loaded with the word value to be stored into the string.




Executing the STOSW Instruction


Once you set up these four registers, you can safely execute a STOSW instruction. When you do, this is what happens:



The word value in AX is copied to the word at ES:DI.



DI is incremented by 2, such that ES:DI now points to the next word in memory following the one just written to.



Note that we're not machine-gunning here. One copy of AX gets copied to one word in memory. The DI register is adjusted so that it'll be ready for the next time STOSW is executed.

One very important point to remember is that CX is not decremented by STOSW. CX is decremented automatically only if you put the REP prefix in front of STOSW. Lacking the REP prefix, you have to do the decrementing yourself, either explicitly through DEC or through the LOOP instruction, as I explain a little later in this chapter.

So, you can't make STOSW run automatically without REP. However, you can, if you like, execute other instructions before executing another STOSW. As long as you don't disturb ES, DI, or CX, you can do whatever you wish. Then when you execute STOSW again, another copy of AX will go out to the location pointed to by ES:DI, and DI will be adjusted yet again. (You have to remember to decrement CX somehow.) Note that you can change AX if you like, but the changed value will be Chapter 10. It needs to be passed a far pointer (which is nothing more than a full 32-bit address consisting of a segment and an offset laid end to end) to the video refresh buffer, the word value to be blasted into the buffer, and the size of the buffer in bytes.


;---------------------------------------------------------------
; CLEAR -- Clears the entire visible screen buffer
; Last update 9/20/99
;
; Caller must pass:
; In VidAddress: The address of the video refresh buffer
; In ClearAtom: The character/attribute pair to fill the
; buffer with. The high byte contains the
; attribute and the low byte the character.
; In BufLength: The number of *characters* in the visible
; display buffer, *not* the number of bytes!
; This is typically 2000 for a 25-line screen
; or 4000 for a 50-line screen.
; Action: Clears the screen by machine-gunning the
; character/attribute pair in AX into the
; display buffer beginning at VidAddress.
;---------------------------------------------------------------
%macro Clear 3 ;VidAddress,ClearAtom,BufLength
les DI,[%1] ; Load ES and DI from FAR pointer
mov AX,%2 ; Load AX with word to blast into memory
mov CX,%3 ; Load CX with length of buffer in bytes
shr CX,1 ; Divide size of buffer by 2 for word count
cld ; Set direction flag so we blast up-memory
rep stosw ; Blast away!
GotoXY 0,0 ; Move hardware cursor to UL corner of screen
%endmacro

Don't let the notion of a far pointer throw you. It's jargon you're going to hear again and again, and this was a good point at which to introduce it. A pointer is an address, quite simply. A near pointer is an offset address only, used in conjunction with some value in some segment register that presumably doesn't change. All pointers to objects inside a real mode flat model program are by definition near pointers.

A far pointer is a pointer that consists of both a segment value and an offset value, both of which may be changed at any time, working together. The video refresh buffer is not usually part of your data segment, so if you're going to work with it, you're probably going to have to access it with a far pointer, as we're doing here. Any time you need to reference something that exists outside the 64K boundaries of a real mode flat model program (such as your system's text video buffer), you're going to have to work with a far pointer.

Note that most of Clear is setup work. The LES instruction loads both ES and DI with the address of the destination string. The screen atom (display character plus attribute value) is loaded into AX.

The handling of CX deserves a little explanation. The value in BufLength is the size in bytes of the video refresh buffer. Remember, however, that CX is assumed to contain the number of times that AX is to be machine-gunned into memory. AX is a word, and a word is 2 bytes long. So, each time STOSW fires, 2 bytes of the video refresh buffer will be written to. Therefore, in order to tell CX how many times to fire the gun, we have to divide the size of the refresh buffer (which is given in bytes) by 2, in order to express the size of the refresh buffer in words.

As I explained in Chapter 10, dividing a value in a register by 2 is easy. All you have to do is shift the value of the register to the right by one bit. This what the SHR CX,1 instruction does: divides CX by 2.


STOSW and the Direction Flag DF


Note the CLD instruction in the Clear macro. I've avoided mentioning it until now to avoid confusing you. Most of the time you'll be using STOSW, you'll want to run it "uphill" in memory; that is, from a lower memory address to a higher memory address. In Clear, you put the address of the start of the video refresh buffer into ES and DI, and then blast character/attribute pairs into memory at successively higher memory addresses. Each time STOSW fires a word into memory, DI is incremented twice to point to the next higher word in memory.

This is the logical way to work it, but it doesn't have to be done that way. STOSW can just as easily begin at a high address and move downward in memory. On each store into memory, DI can be decremented by 2 instead.

Which way STOSW fires—uphill toward successively higher addresses, or downhill toward successively lower addresses—is governed by one of the flags in the Flags register. This is the Direction flag DF. DF's sole job in life is to control the direction of certain instructions that, like STOSW, can move in one of two directions in memory. Most of these (like STOSW) are string instructions.

The sense of DF is this: When DF is set (that is, when DF has the value 1), STOSW and its fellow string instructions work downhill, from higher to lower addresses. When DF is cleared (that is, when it has the value 0), STOSW and its siblings work uphill from lower to higher addresses. This in turn is simply the direction in which the DI register is adjusted: When DF is set, DI is decremented. When DF is cleared, DI is incremented.

The Direction flag defaults to 0 (uphill) when the CPU is reset. It is generally changed in one of two ways: with the CLD instruction, or with the STD instruction. CLD clears DF, and STD sets DF. (You should keep in mind when debugging that the POPF instruction can also change DF, by popping an entire new set of flags from the stack into the Flags register.) It's always a good idea to place the appropriate one of CLD or STD right before a string instruction to make sure that your machine gun fires in the right direction!

People sometimes get confused and think that DF also governs whether CX is incremented or decremented by the string instructions. Not so! Nothing in a string instruction ever increments CX! You place a count in CX and it counts down, period. DF has nothing to say about it.

The Clear macro is part of the MYLIB.MAC macro library on the CD-ROM for this book. As you build new macro tools, you might place them in MYLIB.MAC as well.


/ 166