Copyright 1984 by ABComputing May 15, 1984 ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Introduction to Assembly Language º º º º by º º º º Bill Salkin º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Introduction ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This month's column discusses BIOS and DOS routines, and introduces the DEBUG program. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ DOS Function Calls ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ DOS function calls are similar to subroutine calls in other languages, and perform many useful services such as: display a string on the screen, accept input from the keyboard, perform file I/O, delete files, rename files, determine the time and date stamp on a file, read the directory of a disk, and others. In FORTRAN, the subroutine SUB is invoked by the statement "CALL SUB", and the code in this subroutine is then executed. DOS function calls are invoked by the statement "INT 21H" (interrupt 21 hex), but INT 21H is a subroutine with many entry points (entrances). It has numerous options, with each option corresponding to a different function or service. For example, the program DISPLAY, presented in last month's sample program section, displays a string on the screen by using service #9: MOV AH,9 ;REQUEST "PRINT STRING" SERVICE MOV DX,OFFSET STRINGY ;DX HAS THE ADDRESS OF STRINGY INT 21H ;INVOKE "PRINT STRING" SERVICE The service number is placed in the AH register, and INT 21H is invoked to begin execution of the requested service. DOS function calls (often referred to as "INT 21H" calls) are listed in the appendix "DOS Interrupts and Function Calls" of the DOS manual. Study that section and familiarize yourself with the many services that are available. In particular, verify that #9 is the proper value for printing a string. You may not understand the explanations, but note how the statement of service #9 was translated and used in DISPLAY. Think: DOS function call = INT 21H ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ BIOS Calls ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The BIOS is a collection of important services that deal directly with the hardware, such as the 6845 CRT controller. The BIOS is stored on a ROM chip in the PC. Some BIOS services that programmers can access are: ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ 1. equipment determination (how many disk ³ ³ drives are attached to the system?, is a ³ ³ color monitor present?,etc.) ³ ³ ³ ³ 2. read characters from the keyboard ³ ³ ³ ³ 3. handle printer interfacing ³ ³ ³ ³ 4. handle asynchronous communications ³ ³ ³ ³ 5. scroll "windows" on the screen (used to ³ ³ clear the screen) ³ ³ ³ ³ 6. determine the amount of RAM in the PC. ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The source code of the BIOS is included in the IBM Technical Reference Manual, and should probably not be read by beginning programmers. Later though, it will prove invaluable. In the routine CLEAR, also presented in last month's sample program section, the screen is cleared by using the "scroll screen" service #6. Like a DOS function call, the service number is moved into the AH register: MOV AH,6 ;REQUEST "SCROLL SCREEN" SERVICE INT 10H ;INVOKE "SCROLL SCREEN" SERVICE The actual operation that clears the screen is invoked by INT 10H. You will not find this interrupt listed in the DOS manual, for it is a BIOS call. It is found in the BIOS listing in the Technical Reference Manual, under the heading "Display (VIDEO) I/O." INT 10H is the BIOS call for general screen handling and can be used to change the cursor size, print characters at the cursor position, set medium or high resolution mode, create windows that are smaller than the display screen, and a variety of other functions. Unlike DOS, which centralizes all services under INT 21H, each of the many BIOS services (screen, diskette, etc.) has its own interrupt number. Screen I/O uses INT 10H, while diskette I/O uses INT 13H. Each BIOS service has many sub-services. It may be a pleasant pastime for some to leisurely read the BIOS and extract the information it contains, but this work has already been done. The BIOS calls are presented in any decent assembly language book, and were also presented in the first issue of PCFL/PCUG. (See "The Interrupt PhoneBook," in the DOS column.) So, you need not purchase the Technical Reference Manual to obtain a copy of these calls, but it is to your advantage to own this manual. When you gain experience in assembly language, it will prove a valuable reference - you will be amazed at the information omitted from available texts. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Comparison of BIOS and DOS Calls ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ In many respects, BIOS and DOS calls are complementary, each addressing deficiencies in the other. It is necessary to master the fundamentals of using both types of calls if you wish to use your PC to its fullest advantage. BIOS calls perform "utility" type services, and are not truly a part of PC-DOS operating system. Portability of programs, to other MS-DOS machines, may be compromised by using BIOS calls. Programs employing only standard DOS calls are guaranteed a large degree of portability and should run on other computers using DOS. Many programmers, the "good" guys, insist that BIOS calls should not be used - only DOS calls should be used. Other programmers, the "bad" guys, use the BIOS and do nasty tricks that hamper program portability. This is generally done because a desired function is not available through DOS. (Incidentally, DOS 2.0 has rectified many of the omissions that forced programmers to use BIOS services under DOS 1.1, instead of DOS services.) In truth, I am one of the "bad" guys - but, for good reasons. BIOS and DOS calls are relatively slow, and faster screen response (among other items) can be obtained by writing custom-tailored routines that duplicate BIOS and DOS functions. (The main-menu handler for PCFL/PCUG avoids some of the available BIOS and DOS calls, to increase the speed of the panel display.) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ DEBUG ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ New topic time! Focus on DEBUG. DEBUG is not used only to debug programs. It can be used to understand how instructions actually work when the reference manual is unclear, or to view memory, or to single-step through your program and check for errors. DEBUG provides a controlled environment in which assembly code can be tested. The routine DISPLAY is used as our introduction to DEBUG. ;COPYRIGHT 1984 BY ABComputing ; ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ ³ ; ³ THIS ROUTINE DISPLAYS THE STRING "I have the world on a ³ ; ³ string", AND RETURNS TO DOS. ³ ; ³ ³ ; ³ PLACE THIS ROUTINE IN FILE : DISPLAY.ASM ³ ; ³ ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ;------------------------------------------------------------------------ DATA SEGMENT PARA STRINGY DB 'I have the world on a string','$' DATA ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ STACK SEGMENT PARA STACK 'STACK' DB 40H DUP('STCK') STACK ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ CODE SEGMENT PARA ASSUME CS:CODE, DS:DATA, SS:STACK ;--STANDARD LINKAGE TO DOS DISPLAY PROC FAR ;DO PUSH DS ; SUB AX,AX ; PUSH AX ; NOT MOV AX,DATA ; MOV DS,AX ; ALTER! ;--PRINT THE STRING "STRINGY" MOV AH,9 ;REQUEST "PRINT STRING" SERVICE MOV DX,OFFSET STRINGY ;DX HAS THE ADDRESS OF STRINGY INT 21H ;INVOKE "PRINT STRING" SERVICE ;-- RETURN TO DOS RET ;RETurn to DOS DISPLAY ENDP ;END of Procedure CODE ENDS ;END of code Segment ;------------------------------------------------------------------------ END DISPLAY ;END of routine DISPLAY Assuming you have assembled and linked this program, the executable module is in the file DISPLAY.EXE. I will use drive D, which contains the routines CLEAR.EXE, DISPLAY.EXE, and the DEBUG program (DEBUG.EXE), for the remainder of this article. To invoke DEBUG, type DEBUG after the DOS prompt, followed by the name of the executable module to be "debugged," and press the ENTER key. This loads DEBUG and the module into memory, and DEBUG displays its prompt, a minus sign (-). Type "U" for Unassemble and DEBUG will decode the contents of the module, and present an assembly-like listing. D>DEBUG DISPLAY.EXE -U 098E:0000 1E PUSH DS 098E:0001 2BC0 SUB AX,AX 098E:0003 50 PUSH AX 098E:0004 B89009 MOV AX,0990 098E:0007 8ED8 MOV DS,AX 098E:0009 B409 MOV AH,09 098E:000B BA0000 MOV DX,0000 098E:000E CD21 INT 21 098E:0010 CB RETF <ÄÄÄÄ End of routine DISPLAY 098E:0011 0000 ADD [BX+SI],AL 098E:0013 0000 ADD [BX+SI],AL 098E:0015 0000 ADD [BX+SI],AL 098E:0017 0000 ADD [BX+SI],AL 098E:0019 0000 ADD [BX+SI],AL 098E:001B 0000 ADD [BX+SI],AL 098E:001D 0000 ADD [BX+SI],AL 098E:001F 004920 ADD [BX+DI+20],CL Only the code segment of DISPLAY is shown, but notice how compressed it is in contrast to the code segment in the source file! The unassembled version does not contain comments (as the assembler removes them), and labels in the source code, such as DATA and STRINGY, have been replaced by numbers (addresses). DISPLAY ends with the RETF statement shown above, but DEBUG obligingly unassembles "code" beyond the end of our program (which is of no value to us.) A sample line from the unassembly is interpreted as follows: address machine lang assembler equivalent 098E:0000 1E PUSH DS We will not be concerned with the meaning of the address field yet, or the machine language equivalent of the instruction. Notice that "PUSH DS" in the unassembly is the first statement in the code segment of the original source code. Let's single-step or walk-through our program. Typing "T" for trace yields: -T AX=0000 BX=0000 CX=0180 DX=0000 SP=00FE BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0992 CS=098E IP=0001 NV UP DI PL NZ NA PO NC 098E:0001 2BC0 SUB AX,AX This is the second instruction in our program! We are getting warmer. The "T" command traced, or completed, the first instruction, and DEBUG displays the next instruction to be executed. The contents of all registers are displayed on two lines, and the instruction to be executed on the bottom line. We have not discussed many of these registers, but at this time you should recognize AX, BX, CX, and DX. The AX, BX, and DX registers contain a hex 0, while CX contains 180 hex = 384 decimal. Typing "T" will execute the instruction listed above; we are executing our program one instruction at a time. -T AX=0000 BX=0000 CX=0180 DX=0000 SP=00FE BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0992 CS=098E IP=0003 NV UP DI PL ZR NA PE NC 098E:0003 50 PUSH AX -T ÚÄÄ¿ AX=0000 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0992 CS=098E IP=0004 NV UP DI PL ZR NA PE NC 098E:0004 B89009 MOV AX,0990 ÀÂÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ AX contains zero before this instruction is executed, ³ and will contain 990 after it is executed. ³ -T ÚÁÄ¿ AX=0990 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0992 CS=098E IP=0007 NV UP DI PL ZR NA PE NC W98E:0007 8ED8 MOV DS,AX Recall from my first column, the DS is a pointer to the data segment. DISPLAY's data segment consists of the string "I have the world on a string." We verify this by "dumping" the contents of the data segment using the "D" or dump command. To dump the data segment from its start type: -D DS:0000 0990:0000 49 20 68 61 76 65 20 74-68 65 20 77 6F 72 6C 64 I have the world 0990:0010 20 6F 6E 20 61 20 73 74-72 69 6E 67 24 00 00 00 on a string$... 0990:0020 53 54 43 4B 53 54 43 4B-53 54 43 4B 53 54 43 4B STCKSTCKSTCKSTCK 0990:0030 53 54 43 4B 53 54 43 4B-53 54 43 4B 53 54 43 4B STCKSTCKSTCKSTCK 0990:0040 53 54 43 4B 53 54 43 4B-53 54 43 4B 53 54 43 4B STCKSTCKSTCKSTCK 0990:0050 53 54 43 4B 53 54 43 4B-53 54 43 4B 53 54 43 4B STCKSTCKSTCKSTCK 0990:0060 53 54 43 4B 53 54 43 4B-53 54 43 4B 53 54 43 4B STCKSTCKSTCKSTCK 0990:0070 53 54 43 4B 53 54 43 4B-53 54 43 4B 53 54 43 4B STCKSTCKSTCKSTCK There's our string and the "STCK" pattern in the far right side of the display. If you study the source code for DISPLAY, you will note that the stack segment immediately follows the data segment, and this is precisely what DEBUG shows us. DEBUG gives one a good feel for the relationship of various segments in a program. Attentive readers will note that while the stack segment immediately follows the data segment in the source code, there are three periods separating the $ from the word STCK in the DEBUG display. We will discuss this in a later column under "segment alignment." -T AX=0990 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0992 CS=098E IP=0009 NV UP DI PL ZR NA PE NC 098E:0009 B409 MOV AH,09 The number 9 is moved into the AH register to indicate the "print string" service. -T AX=0990 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0992 CS=098E IP=000B NV UP DI PL ZR NA PE NC 098E:000B BA0000 MOV DX,0000 DX has the address of the string to be printed, as measured relative to the start of the data segment. Moving 0 into DX means that our string has an "offset" of 0 bytes into the data segment. That is, the first byte of our string starts at the beginning of the data segment. Finally typing "G" for Go will cause the program to be executed. The "Program terminated normally" message was issued by DEBUG; it is not part of DISPLAY. -G I have the world on a string Program terminated normally To exit DEBUG and return to DOS, type "Q", for quit. -Q D> ------------------------------------------------------------------- ;COPYRIGHT 1984 BY ABComputing ; ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ ³ ; ³ THIS ROUTINE CLEARS THE SCREEN BY MAKING A BIOS CALL. ³ ; ³ ³ ; ³ PLACE THIS ROUTINE IN FILE : CLEAR.ASM ³ ; ³ ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ;------------------------------------------------------------------------ DATA SEGMENT PARA DATA ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ STACK SEGMENT PARA STACK 'STACK' DB 40H DUP('STCK') STACK ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ CODE SEGMENT PARA ASSUME CS:CODE, DS:DATA, SS:STACK ;--STANDARD LINKAGE TO DOS CLEAR PROC FAR ;DO PUSH DS ; SUB AX,AX ; PUSH AX ; NOT MOV AX,DATA ; MOV DS,AX ; ALTER! ;--NOTE 24 DECIMAL = 18H, AND 79 DECIMAL = 4FH MOV AH,6 ;REQUEST SCROLL SCREEN SERVICE MOV AL,0 ;SCROLL ENTIRE WINDOW MOV BH,7 ;WHITE FOREGROUND, BLACK BACKGROUND MOV CX,0000H ;UPPER LEFT CORNER (0,0) MOV DX,184FH ;LOWER RIGHT (24,79) INT 10H ;INVOKE SCROLL SCREEN SERVICE ;-- RETURN TO DOS RET ;RETurn to DOS CLEAR ENDP ;END of Procedure CODE ENDS ;END of code Segment ;------------------------------------------------------------------------ END CLEAR ;END of routine CLEAR D>DEBUG CLEAR.EXE -U 098E:0000 1E PUSH DS 098E:0001 2BC0 SUB AX,AX 098E:0003 50 PUSH AX 098E:0004 B89009 MOV AX,0990 098E:0007 8ED8 MOV DS,AX 098E:0009 B406 MOV AH,06 098E:000B B000 MOV AL,00 098E:000D B707 MOV BH,07 098E:000F B90000 MOV CX,0000 098E:0012 BA4F18 MOV DX,184F 098E:0015 CD10 INT 10 098E:0017 CB RETF <ÄÄÄÄ End of routine CLEAR 098E:0018 0000 ADD [BX+SI],AL 098E:001A 0000 ADD [BX+SI],AL 098E:001C 0000 ADD [BX+SI],AL 098E:001E 0000 ADD [BX+SI],AL -T AX=0000 BX=0000 CX=0180 DX=0000 SP=00FE BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0990 CS=098E IP=0001 NV UP DI PL NZ NA PO NC 098E:0001 2BC0 SUB AX,AX -T AX=0000 BX=0000 CX=0180 DX=0000 SP=00FE BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0990 CS=098E IP=0003 NV UP DI PL ZR NA PE NC 098E:0003 50 PUSH AX -T AX=0000 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0990 CS=098E IP=0004 NV UP DI PL ZR NA PE NC 098E:0004 B89009 MOV AX,0990 -t AX=0990 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=097E ES=097E SS=0990 CS=098E IP=0007 NV UP DI PL ZR NA PE NC 098E:0007 8ED8 MOV DS,AX -T Ú¿ AX=0990 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0990 CS=098E IP=0009 NV UP DI PL ZR NA PE NC 098E:0009 B406 MOV AH,06 ÀÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ AH, which is the upper byte of the AX register, ³ contains "09" before this instruction is executed, and will ³ contain "06" after it is executed. ³ -T Ú¿ AX=0690 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0990 CS=098E IP=000B NV UP DI PL ZR NA PE NC 098E:000B B000 MOV AL,00 ÀÂÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Ú¿ AX=0600 BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0990 CS=098E IP=000D NV UP DI PL ZR NA PE NC 098E:000D B707 MOV BH,07 -T BH contains "00", and will contain "07" after this instruction is executed. Ú¿ AX=0600 BX=0700 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0990 CS=098E IP=000F NV UP DI PL ZR NA PE NC 098E:000F B90000 MOV CX,0000 -T ÚÄÄ¿ AX=0600 BX=0700 CX=0000 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=0990 ES=097E SS=0990 CS=098E IP=0012 NV UP DI PL ZR NA PE NC 098E:0012 BA4F18 MOV DX,184F -G Program terminated normally -Q What can not be shown is that the screen cleared after the "G" command was typed. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Conclusion ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ We covered a lot of information this time. Study the DOS function calls, BIOS calls, and experiment. Try DEBUGging any programs you write. Next month, we will discuss BIOS/DOS calls further, and I hope to introduce the topic of subroutine calls. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ File Name: ÛÛ asm1.txt ÛÛ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ