assembly language step by step programming with dos and linux PHẦN 6 pptx

47 301 0
assembly language step by step programming with dos and linux PHẦN 6 pptx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm character (ASCII 3FH) into every byte of the buffer. The DUP directive can also be used to store repeated patterns into larger variables and buffers. This can make the buffers and variables easy to spot when you perform a hex dump from DEBUG : Marked DB 10 DUP ('Zorro!!!') Table1 DB 5 DUP (02H,04H.06H,08H) DB 64 DUP ('STACK!!!') The last example allocates space for the stack segment in EAT.ASM. Although this statement illustrates the fact that you don't have to name a buffer that simply exists to take up space (as in allocating space for the stack segment), I recommend that you name all variables and buffers. Setting Up the Code Segment Like any segment, the code segment must have a name, and the name must be given at the start and end of the segment definition, before the SEGMENT and ENDS directives. Although the name is unimportant and probably won't be referenced anywhere in the code, it must be there, or you will receive an assembler error. An ASSUME directive must be included in the program. Its purpose is to tell the assembler which of the segments you have defined is to be used for the code segment, and which segment is to be used for the data segment. Unlike the stack segment, which has the directive STACK to tell the assembler what sort of segment it is, nothing in the code or data segments specifies which sort of segment they are. It isn't enough that there are variables defined in the data segment or machine instructions in the code segment. The assembler will allow you put variable definitions in the code segment and machine instructions in the data segment, regardless of whether that makes sense or not. (It may, in certain extremely advanced techniques.) In EAT.ASM, the ASSUME directive tells the assembler that the code segment will be the segment named MyCode, and that the data segment will be named MyData. EAT.ASM has its machine instructions grouped together in a procedure named Main with the PROC directive. This is not strictly necessary unless you have broken down your program into procedures or modules, and EAT.ASM will assemble and run correctly without the Main PROC and Main ENDP statements. I would advise you to get in the habit of placing the main program portion of any assembly-language program into a procedure called Main to help make the program more readable. What is essential, however, is to provide a label that marks the place where program file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm (35 of 38) [9/25/2002 8:07:31 PM] file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm execution is to begin. I recommend the label Start: as a convention, but the label can be any legal identifier. Whatever label you choose, mark the main program's starting point with the label and a colon. Then, place the same label minus the colon after the END directive, which marks the end of the source-code file. Placing the "start" label after the END directive tells the assembler that there is no more source code, and that the label is the point at which execution is to begin. What EAT.ASM's Machine Instructions Do ; From the top: mov AX,MyData ; Set up our own data segment address in DS mov DS,AX ; Can't load seg-reg. directly from memory Before your program can access any of its variables in the data segment, it must have the segment address of the data segment in the DS register. The ASSUME directive tells the assembler to assemble any instruction referencing an identifier in the MyData segment under the assumption (hence the name of the directive) that MyData is to be a data segment. ASSUME, however, does not load the data segment address into DS! You must do that yourself, which is the purpose of the two instructions shown above. This seemingly simple operation takes two instructions rather than one because MOV cannot move memory data directly into a segment register like DS. To load the address of memory data into a segment register, you must first load the address into one of the general-purpose registers and then load the general-purpose register into the segment register: lea DX , Eat1 ; Load offset of Eat1 message string into DX mov AH,09H ; Select DOS service 09H: Print String int 21H ; Call DOS Here's where the first real work of EAT.ASM gets done. The load effective address instruction (LEA) puts the offset address of variable Eat1 into the DX register. Keep in mind that the segment address of Eat1 is already in DS— loaded by the first two instructions in the program. MOV AH, 09H loads the number of DOS service O9H (Print String) into register half AH. The term "Print String" is a misnomer inherited from file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm (36 of 38) [9/25/2002 8:07:31 PM] file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm an ancient age when video terminals were considered exotic, and strings could only be printed—on (kerchunk-kerchunkity-chunk) Teletype terminals! Finally, INT 21H transfers control to the DOS services dispatcher by way of software interrupt 21H. The dispatcher looks in DS:DX for the address of the string variable to be displayed, and then hands control over to the Print String service routine somewhere deep within DOS. When the string is displayed, execution returns to the instruction following the INT 21H instruction, which is possible because the INT 21H instruction pushed the address of the next instruction onto the stack before it passed execution to the DOS services dispatcher. The dispatcher simply popped that return address of the stack and resumed execution at that address. Again, here is an explanation of how interrupts work: the previous block of instructions were enough to display the string "Eat at Joe's!" on your video display. DOS leaves the hardware cursor on the character following the last character of the string, however, and any subsequent display output would follow "Eat at Joe's!" immediately. You may want this, and you may not—and if you don't, it would be a good idea to return the cursor to the left margin and bump it down to the next screen line. This is what's going on here: lea DX , CRLF ; Load offset of CRLF string into DX mov AH, 09H ; Select DOS service 09H: Print String int 21H ; Call DOS The CRLF variable contains the EOL marker, which includes the ASCII carriage return characters. EAT.ASM passes the string containing these two "invisible" characters to DOS in exactly the same way it passed the string "Eat at Joe's!", by loading CRLF's address into DS:DX and selecting DOS service O9H before handing control to the DOS services dispatcher through software interrupt 21H. Finally, the job is done. Joe's has been properly advertised, and it's time to let DOS have the machine back: mov AH,4CH ; Terminate process DOS service mov AL,0 ; Pass this value back to ERRORLEVEL int 21H ; Control returns to DOS Another DOS service, 4CH (Terminate Process) handles the mechanics of courteously disentangling the machine from EAT.ASM's clutches. The Terminate Process service doesn't need the address of anything, but it will take whatever value it finds in the AL register and place it in the DOS ERRORLEVEL variable. DOS batch programs can test the value of the ERRORLEVEL variable and branch on it, as I'll demonstrate in the next file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm (37 of 38) [9/25/2002 8:07:31 PM] file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm chapter. EAT.ASM doesn't do anything worth testing in a batch program, but if ERRORLEVEL will be set anyway, it's a good idea to provide some reliable and harmless value for ERRORLEVEL to take. This is why 0 is loaded into AL prior to ending it all by the final INT 21 instruction. If you were to test ERRORLEVEL after running EAT.EXE, you would find it set to 0 in every case. file:///D|/Agent%20Folders/Chapter%207%20Our%20Object%20All%20Sublime.htm (38 of 38) [9/25/2002 8:07:31 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm Dividing and Conquering Using Procedures and Macros to Battle Complexity 8.1 Programming in Martian >• 216 8.2 Boxes Within Boxes >• 216 8.3 Using BIOS Services >• 224 8.4 Building External Libraries of Procedures >• 235 8.5 Creating and Using Macros >• 248 8.1 Programming in Martian There is a computer language called APL (an acronym for "A Programming Language," how clever) that has more than a little Martian in it. APL was the first computer language I learned, (on a major IBM mainframe) and when I learned it I learned a little more than just APL. APL uses a very compact notation, with dozens of odd little symbols, each of which is capable of some astonishing power like matrix inversion. You can do more in one line of APL than you can in one line of anything else I have learned since. The combination of the strange symbol set and the compact notation make it very hard to read and remember what a line of code in APL actually does. So it was in 1977. Having mastered (or so I thought) the whole library of symbols, I set out to write a text formatter program. The program would justify right and left, center headers, and do a few other things that we take for granted today, but which were very file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm (1 of 50) [9/26/2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm exotic in the Seventies. The program grew over a period of a week to about 600 lines of squirmy little APL symbols. I got it to work, and it worked fine—as long as I didn't try to format a column that was more than 64 characters wide. Then everything came out scrambled. Whoops. I printed the whole thing out and sat down to do some serious debugging. Then I realized with a feeling of sinking horror that, having finished the last part of the program, I had no idea how the first part worked. The APL symbol set was only part of the problem. I soon came to realize that the most important mistake I had made was writing the whole thing as one 600-line monolithic block of code lines. There were no functional divisions, nothing to indicate what any 10- line portion of the code was trying to accomplish. The Martians had won. I did the only thing possible: I scrapped it. And I settled for ragged margins in my text. 8.2 Boxes Within Boxes This sounds like Eastern mysticism, but it's just an observation from life: Within any action is a host of smaller actions. Look inside your common activities. When you "brush your teeth," what you're actually doing is: • Picking up your toothpaste tube • Unscrewing the cap • Placing the cap on the sink counter • Picking up your toothbrush • Squeezing toothpaste onto the brush from the middle of the tube • Putting your toothbrush into your mouth • Working the brush back and forth vigorously and so on. The original list went the entire page. When you brush your teeth, you perform every one of those actions. However, when you think about brushing your teeth, you don't consciously run through each action on the list. You bring to mind the simple concept "brushing teeth." Furthermore, when you think about what's behind the action we call "getting up in the morning," you might assemble a list of activities like this: • Shut off the clock radio • Climb out of bed • Put on your robe • Let the dogs out file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm (2 of 50) [9/26/2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm • Make breakfast • Brush your teeth • Shave • Get dressed Brushing your teeth is on the list, but within the "brushing your teeth" activity a whole list of smaller actions exist. The same can be said for most of the activities collectively called "getting up in the morning." How many individual actions, for example, does it take to put a reasonable breakfast together? And yet in one small, if sweeping, phrase, "getting up in the morning," you embrace that whole host of small and even smaller actions without having to laboriously trace through each one. What I'm describing is the "Chinese boxes" method of fighting complexity. Getting up in the morning involves hundreds of little actions, so we divide the mass up into coherent chunks and set the chunks into little conceptual boxes. "Making breakfast" is in one box, "brushing teeth" is in another, and so on. Closer inspection of any box shows that its contents can also be divided into numerous boxes, and those smaller boxes into even smaller boxes. This process doesn't (and can't) go on forever, but it should go on as long as it needs to in order to satisfy this criterion: the contents of any one box should be understandable with only a little scrutiny. No single box should contain anything so subtle or large and involved that it takes hours of hair pulling to figure it out. Procedures as Boxes for Code The mistake I made in writing my APL text formatter is that I threw the whole collection of 600 lines of APL code into one huge box marked "text formatter." While I was writing it, I should have been keeping my eyes open for sequences of code statements that worked together at some identifiable task. When I spotted such sequences, I should have set them off as procedures. Each sequence would then have a name that would provide a memory-tag for the sequence's function. If it took ten statements to justify a line of text, those ten statements should have been named JustifyLine, and so on. Xerox's legendary APL programmer, Jim Dunn, later told me that I shouldn't ever write a procedure that wouldn't fit on a single 25-line terminal screen "More than 25 lines and you're doing too much in one procedure. Split it up, " he said. Whenever I worked in APL after that, I adhered to that rather sage rule of thumb. The Martians still struck from time to time, but when they did, it was no longer a total loss. All computer languages have procedures of one sort or another, and assembly language file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm (3 of 50) [9/26/2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm is no exception. You may recall from the previous chapter that the main program is in fact a procedure, and the only thing setting it apart as the main program is the fact that its name is specified after the END directive. Your assembly-language program may have numerous procedures. There's no limit to the number of procedures, as long as the total number of bytes of code does not exceed 65,536 (one segment). Other complications arise at that point, but nothing that can't be worked around. But that's a lot of code. You needn't worry for awhile, and certainly not while you're just learning assembly language. (I won't be treating the creation of multiple code segments in this book.) In the meantime, let's take a look at the "Eat at Joe's" program, expanded a little to include a couple of procedures: file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm (4 of 50) [9/26/2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm (5 of 50) [9/26/2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm EAT2.ASM does about the same thing as EAT.ASM. It prints a two-line slogan, and that's all. The way the two lines of the slogan are displayed, however, bears examination: lea DX , Eat1 call Writeln Here's a new instruction: CALL. The label Writeln refers to a procedure. As you might have gathered, (especially if you've programmed in an older language like BASIC or FORTRAN) CALL Writeln simply tells the CPU to go off and execute a procedure named Writeln. The means by which CALL operates may sound familiar: CALL first pushes the address of the next instruction after itself onto the stack. Then CALL transfers execution to the address represented by the name of the procedure. The instructions contained in the procedure execute. Finally, the procedure is terminated by CALL'S alter ego: RET (for RETurn.) The RET instruction pops the address off the top of the stack and transfers execution to that address. Since the address pushed was the address of the first instruction after the CALL instruction, execution continues as though CALL had not changed the flow of instruction execution at all. See Figure 8.1. This should remind you strongly of how software interrupts work. The main difference is that the caller does know the exact address of the routine it wishes to call. Apart from that, it's very close to being the same process. (Also note that RET and IRET are not interchangeable. CALL works with RET just as INT works with IRET. Don't get those return instructions confused!) file:///D|/Agent%20Folders/Chapter%208%20Dividing%20and%20Conquering.htm (6 of 50) [9/26/2002 12:42:58 PM] [...]... file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (7 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm Calls Within Calls file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (8 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm Within a procedure you can do anything that you can do within the... file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (10 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm call VerifyRec call WriteRec loop Input call CloseFile call Cleanup call ReturnToDOS This is clean and readable, and provides a necessary "view from a height" when you begin to approach a thousand-line assembly- language program Remember that... screen with GotoXY, it makes sense to start with a completely clear screen so the remains of earlier file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (12 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm programs and DOS commands don't clutter up the view There's another VIDEO service that can do the job VIDEO Service 6 is... This entry point starts executing the code after CX and DX are set to the corners of the full screen This means that the caller must load CX and DX with the upper-left and lower-right corners of the screen region to be cleared Calling ClrWin without setting CX and DX at all will execute service 6 with whatever leftover garbage values happen to be in CX and DX Something will happen, for certain Whether... file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (15 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm mov AL , 1 call ScrlWin ; Set to scroll by one line ; Call the window-scroll procedure As you can see, more and more of the work is being done by caller and less and less within the procedure How you arrange the entry points... in any and all of your assembly- language programs When this is the case, break the utility procedures out into an external library that you can assemble only once, and then link into every program that uses its procedures without assembling the library every time you assemble the program This is called modular programming, and it is an extremely effective tool for programming efficiently in any language, ... file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm ( 26 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (27 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm : file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (28 of 50) [9/ 26/ 2002... example, are always going to be 0,0, and nothing will change that The lower-right corner, however, is not necessarily always 79,24 The original 1981-vintage IBM MDA and CGA graphics adapters are indeed capable of displaying only an 80 by 25 text screen and no more However, with an EGA it is possible to have an 80 by either 25 or 43 text screen, and the VGA, introduced in 1987 with the PS/2 line, can display... procedures that most high-level language vendors supply with their compilers just don't exist with assembly language By and large, you create your own Keeping such a list of routines straight is no easy task, when you've written them all yourself You must document the essential facts about each individual procedure or you'll forget them, or, worse yet, remember them incorrectly and act on bad information... there is only one stack in the system, and while your program is running, DOS and the BIOS and any active TSRs may well be using the same stack If they fill it, you'll go down with the system—so leave room! file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm (9 of 50) [9/ 26/ 2002 12:42:58 PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm When to Make Something . PM] file:///D|/Agent%20Folders/Chapter%208%20Dividing%2 0and% 20Conquering.htm Dividing and Conquering Using Procedures and Macros to Battle Complexity 8.1 Programming in Martian >• 2 16 8.2 Boxes Within Boxes >• 2 16 8.3 Using BIOS. caller must load CX and DX with the upper-left and lower-right corners of the screen region to be cleared. Calling ClrWin without setting CX and DX at all will execute service 6 with whatever leftover. modules, and EAT.ASM will assemble and run correctly without the Main PROC and Main ENDP statements. I would advise you to get in the habit of placing the main program portion of any assembly- language

Ngày đăng: 12/08/2014, 08:23

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan