Copyright 1984 by ABComputing July 15, 1984 ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Introduction to Assembly Language º º º º by º º º º Bill Salkin º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Overview ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This month's column shows how to use the BIOS equipment interrupt to determine the equipment attached to your PC, how to use DOS interrupts to read the directory of a diskette, and closes with a discussion of subroutine calling. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Equipment Determination ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ On page A-1 of my Technical Reference Manual is an index to the ROM-BIOS listing. The index shows that the "Equipment Determination" routine is listed on page A-67. Turning to this page we find that the equipment- determination routine, interrupt 11H, has no programmer input, and returns a bit-significant value in the AX register indicating the equipment attached to the PC. The meaning of the bit-pattern returned in the AX register is mapped below: 1 1 1 1 1 1 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ÉÍÑÍÑÍÑÍÑÍÑÍÑÍÑÍËÍÑÍÑÍÑÍÑÍÑÍÑÍÑÍ» ºP P³ ³J³S S S³ ºD D³V V³R R³C³Iº ÈÍÏÍÏÍÏÍÏÍÏÍÏÍÏÍÊÍÏÍÏÍÏÍÏÍÏÍÏÍÏͼ ÀÂÙ ³ ³ À¿ ÚÙ ³ ÀÂÙ ÀÂÙ ÀÂÙ ³ ³ ³ ³ ³ ÀÂÙ ³ ³ ³ ³ ³ ÀÄ Disk flag. 0=no drives ³ ³ ³ ³ ³ ³ ³ ³ ÀÄÄÄ 8087 chip (1=found; 0=not found) ³ ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄ motherboard RAM (00=16k...11=64k) ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄ Video (11=mono, else=color/graphics) ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Drives (00=1, 01=2, 10=3, 11=4) ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ not used ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Serial ports (up to 7) ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Joystick flag. ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ not used ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Printers (0,1,2, or 3) The program EQUIP.ASM, shown below, uses interrupt 11H (INT 11H) to determine the equipment attached to my PC. Recall that an interrupt is similar to a subroutine. As EQUIP.ASM does not write to the screen, DEBUG is used to trace EQUIP.EXE and study the value in the AX register. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Program EQUIP ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ;Copyright 1984 by ABComputing ; ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ ³ ; ³ DETERMINE THE EQUIPMENT ATTACHED TO THE IBM-PC. ³ ; ³ ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ;------------------------------------------------------------------------ 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 EQUIP PROC FAR ;DO PUSH DS ; SUB AX,AX ; PUSH AX ; NOT MOV AX,DATA ; MOV DS,AX ; ALTER! ;--RETURNS EQUIPMENT FLAG SETTINGS IN AX REGISTER. INT 11H ;-- RETURN TO DOS RET ;RETurn to DOS EQUIP ENDP ;END of PROCedure CODE ENDS ;END of CODE Segment ;------------------------------------------------------------------------ END EQUIP ;END of routine EQUIP Assuming DEBUG and EQUIP.EXE are on drive D, DEBUG is invoked as follows: D>DEBUG EQUIP.EXE -U 0959:0000 1E PUSH DS 0959:0001 2BC0 SUB AX,AX 0959:0003 50 PUSH AX 0959:0004 B85A09 MOV AX,095A 0959:0007 8ED8 MOV DS,AX 0959:0009 CD11 INT 11 0959:000B CB RETF <ÄÄÄ End of routine 0959:000C 0000 ADD [BX+SI],AL 0959:000E 0000 ADD [BX+SI],AL I always unassemble the code immediately to insure that I have loaded the proper module successfully. We can single-step or trace through this routine using the DEBUG "T" (Trace) command, but I will show you a faster method. In the above unassembly, notice the number 959:000B in: 0959:0009 CD11 INT 11 This is the address of the instruction. The DEBUG "G" (Go) command, followed by an address such as G 959:9, causes DEBUG to execute all instructions from its current location to the instruction prior to the address given. As we are at the beginning of the EQUIP program, the command "G 959:9" causes DEBUG to execute all instructions from the beginning of the program to, but not including, the INT 11H. -G 959:9 AX=095A BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=095A ES=0949 SS=095A CS=0959 IP=0009 NV UP DI PL ZR NA PE NC 0959:0009 CD11 INT 11 Sure enough, we arrived at the proper location. We can single-step through INT 11H, but as the source code of INT 11H is provided in the ROM-BIOS listing, there is no reason to do this. Instead, we execute the entire interrupt by going (using "G") to the first instruction past INT 11H, which is a RETF with address 959:000B. 0959:0009 CD11 INT 11 0959:000B CB RETF <ÄÄÄ End of routine -G 959:B AX=44BF BX=0000 CX=0180 DX=0000 SP=00FC BP=0000 SI=0000 DI=0000 DS=095A ES=0949 SS=095A CS=0959 IP=000B NV UP DI PL ZR NA PE NC 0959:000B CB RETF Prior to the interrupt the AX register contained the value 095A, and after the interrupt it contains the value 44BF. The value 44BF is represented in binary as: 0100 0100 1011 1111 With bit 0 being the rightmost bit, the output of INT 11H is interpreted as follows: BIT 1 = 1 => 8087 Math coprocessor found BIT 2,3 = 1 => motherboard has 64K of RAM BIT 4,5 = 11 => 80x25 BW VIDEO MODE using BW card BIT 6,7 = 10 => 3 diskette drives. BIT 14,15 = 1 => one printer in system This is correct. My system has a monochrome monitor, 3 disk drives (2 floppies plus a non-existent hard disk switch setting), one printer, and an 8087 chip. The FBI couldn't find more information, even with fingerprints. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Reading a Diskette Directory ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This program DIRR (shown below) reads and displays the directory of the diskette in drive A. Now is the time to re-read last month's article on "DOS 1.1, 2.0 Sequential File-handling" if you are unfamiliar with terms such as FCB (File Control Block) and DTA (Disk Transfer Area.) In a nutshell, though, an FCB is a 37-byte data structure that is the liaison or link between a file, as stored on the diskette, and as viewed from the program. Routine DIRR uses an FCB containing question marks in the filename and file-extension fields. FCB DB 01,'????????','???',25 DUP(0) The file "????????.??? is preceded by a "01" indicating that drive A is to be used for any operation on the file "????????.???". DOS function-call 11H is used to search the directory on drive A for the first filespec matching that given in the FCB. As the FCB filespec is filled with wildcard characters, meaning "anything goes," any file found in the directory matches, and INT 11H finds the first member in the directory. (Do not be confused by the number 11H. The BIOS interrupt 11H, mentioned earlier, is used for equipment determination; the DOS function-call 11H is used to find the first directory member. The sharing of the number 11H is coincidental.) Using the same FCB, DOS function-call 12H is invoked to find the next member matching the filespec "A:????????.???" in the FCB. The next member in the directory is returned, and this process continues until all members have been found. This approach is preferable to assuming that the directory members are located at track x, sector y, and doing an "absolute" read of these tracks and sectors. In fact, DOS 2.x directory entries need not be at the same locations as DOS 1.x entries, so "absolute" reading is not the proper method for obtaining a disk directory under any release of DOS. When function-calls 11H and 12H are invoked, they use the FCB pointed to by DS:DX and fill in the DTA area with an unopened FCB for the file. (Recall that the DTA is a buffer through which the data from disk read/writes passes. In DIRR, the DTA is called IO_AREA.) DIRR appends a carriage return, linefeed, and a $ (print string terminator) after the filespec portion of the FCB, and then prints the filespec; the remainder of the FCB is disregarded. A typical run of DIRR produces a listing similar to: COMMAND.COM BADLUCK.TXT .... MOREJUN.K ... The smiling faces preceding each filename are a list of our satisfied subscribers! Actually, it is the ASCII character with decimal code 1. (The "1" was used to indicate that drive A should be searched.) ;Copyright 1984 by ABComputing ; ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ ³ ; ³ THIS ROUTINE READS THE DIRECTORY OF A DISK IN DRIVE "A," ³ ; ³ USING DOS FUNCTION-CALLS 11H AND 12H. ³ ; ³ ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ;------------------------------------------------------------------------ CR EQU 10 ;CARRIAGE RETURN = ASCII 10 LF EQU 13 ;LINEFEED = ASCII 13 ;------------------------------------------------------------------------ DATA SEGMENT PARA FCB DB 01,'????????','???',25 DUP(0) IO_AREA DB 40 DUP(0) ;40 BYTE BUFFER FOR FCBs. DATA ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ STACK SEGMENT PARA STACK 'STACK' DB 40H DUP('STCK') STACK ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ CODE SEGMENT PARA ASSUME CS:CODE, DS:DATA ;--STANDARD LINKAGE TO DOS DIRR PROC FAR ;DO PUSH DS ; SUB AX,AX ; PUSH AX ; NOT MOV AX,DATA ; MOV DS,AX ; ALTER! ;--SET DTA TO IO_AREA (USED TO HOLD FCB OF FILES FOUND IN DIRECTORY) MOV DX,OFFSET IO_AREA ;DS:DX -> IO_AREA MOV AH,1AH ;REQUEST "SET DTA" SERVICE INT 21H ;INVOKE "SET DTA" SERVICE ;--MOVE FCB OF FIRST MEMBER IN DIRECTORY TO IO_AREA. MOV DX,OFFSET FCB ;DS:DX ->FCB MOV AH,11H ;REQUEST "FIRST MEMBER" SERVICE INT 21H ;INVOKE "FIRST MEMBER" SERVICE ;--STARTING AT BYTE 12 IN IO_AREA (THE DTA), MOVE A CARRIAGE RETURN, ; LINEFEED, AND FINALLY A "$". (THESE CHARACTERS ARE PLACED AFTER THE ; FILESPEC. MOV IO_AREA[12],CR MOV IO_AREA[13],LF MOV IO_AREA[14],'$' ;------------------------------------------------------------------------ ; THE FIRST 12 BYTES OF THE FCB IN IO_AREA CONTAIN THE FILESPEC. ; ; RECALL FROM LAST MONTH'S SAMPLE PROGRAM "REVERSE" THAT: ; ; IO_AREA[0] POINTS TO IO_AREA ; IO_AREA[1] POINTS ONE BYTE PAST THE START OF IO_AREA ; IO_AREA[2] POINTS TWO BYTES PAST THE START OF IO_AREA ; ETC. ; ;------------------------------------------------------------------------ ;--DISPLAY FIRST MEMBER ;--PRINTING THE CONTENTS OF IO_AREA PRINTS THE FILESPEC AND MOVES THE ; CURSOR TO THE START OF THE NEXT LINE. MOV DX,OFFSET IO_AREA ;DS:DX -> IO_AREA MOV AH,9 ;REQUEST "PRINT STRING" SERVICE INT 21H ;INVOKE "PRINT STRING" SERVICE ;--GET NEXT MEMBER IN DIRECTORY NXT_MEM: MOV DX,OFFSET FCB MOV AH,12H ;REQUEST "NEXT MEMBER" SERVICE INT 21H ;INVOKE "NEXT MEMBER" SERVICE PUSH AX ;STATUS OF "NEXT MEMBER" RETURNED IN AX REG. ; SAVE THIS VALUE ON STACK ;--ADD CARRIAGE RETURN, LINEFEED, AND PRINT STRING TERMINATOR AFTER FILESPEC. MOV IO_AREA[12],CR MOV IO_AREA[13],LF MOV IO_AREA[14],'$' ;--DISPLAY THIS MEMBER MOV DX,OFFSET IO_AREA MOV AH,9 INT 21H ;--CONTINUE UNTIL LAST MEMBER FOUND POP AX ;PLACE STATUS OF "NEXT MEMBER" SERVICE IN ; THE AX REGISTER. CMP AL,0 ;AL = 0 MEANS MEMBER FOUND JE NXT_MEM ;IF AL = 0 THEN FIND NEXT DIRECTORY MEMBER ; (JUMP TO NXT_MEM IF AL = 0 ) ;-- RETURN TO DOS RET DIRR ENDP ;END of PROCedure CODE ENDS ;END of CODE Segment ;------------------------------------------------------------------------ END DIRR ;END of routine DIRR ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Subroutine Calls ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The topic of subroutine calls is discussed next. A few more installments and you will have seen many of the basic elements of assembly language. The time will then be ripe to circle back and explain what we have been doing in these columns! ;COPYRIGHT 1984 BY ABComputing ; ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ ³ ; ³ THIS ROUTINE CALLS AN INTERNAL SUBROUTINE (SUB). ³ ; ³ ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ;------------------------------------------------------------------------ CR EQU 10 ;CARRIAGE RETURN = ASCII 10 LF EQU 13 ;LINEFEED = ASCII 13 ;------------------------------------------------------------------------ DATA SEGMENT PARA MSG1 DB 'POSITIVELY, PAUCITY','$' MSG2 DB CR,LF,'OF THOUGHT','$' DATA ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ STACK SEGMENT PARA STACK 'STACK' DB 40H DUP('STCK') STACK ENDS ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ CODE SEGMENT PARA ASSUME CS:CODE, DS:DATA, SS:STACK CALLS PROC FAR ;STANDARD LINKAGE TO DOS PUSH DS ; DO SUB AX,AX ; PUSH AX ; NOT MOV AX,DATA ; MOV DS,AX ; ALTER! ;-- SUBROUTINE SUB IS CALLED AND DISPLAYS MSG1. ROUTINE SUB RETURNS ; TO ROUTINE CALLS, AND THE NEXT INSTRUCTION (MOV DX,OFFSET MSG2) ; IS EXECUTED. CALL SUB ;DISPLAY "POSITIVELY, PAUCITY" ;-- DISPLAY MSG2 ("OF THOUGHT") MOV DX,OFFSET MSG2 ;DS:DX-> MSG2 MOV AH,9 ;REQUEST "PRINT STRING" OPTION INT 21H ;INVOKE "PRINT STRING" SERVICE ;-- RETURN TO DOS RET ;RETURN TO DOS ;--------------------------- START OF SUBROUTINE "SUB" ---------------------; ; ; SUB PROC NEAR ; ; MOV DX,OFFSET MSG1 ;DS:DX -> MSG1. ; MOV AH,9 ;REQUEST "PRINT STRING" SERVICE ; INT 21H ;INVOKE "PRINT STRING" SERVICE ; ; ; ;-- RETURN TO "CALLS"- THE ROUTINE THAT CALLED "SUB" ; ; RET ;RETURN TO CALLING ROUTINE ; ; ; SUB ENDP ;END of subroutine SUB. ; ; ;--------------------------- END OF SUBROUTINE `SUB' -----------------------; CALLS ENDP ;END OF PROCedure CALLS CODE ENDS ;END of CODE Segment ;------------------------------------------------------------------------ END CALLS ;END of routine CALLS. Using DEBUG to trace the sequence of events: D>DEBUG CALLS.EXE -U 0959:0000 1E PUSH DS <ÄÄ¿ 0959:0001 2BC0 SUB AX,AX ³ 0959:0003 50 PUSH AX ³ 0959:0004 B85B09 MOV AX,095B ³ 0959:0007 8ED8 MOV DS,AX ³ "CALLS" 0959:0009 E80800 CALL 0014 ³ 0959:000C BA0800 MOV DX,0008 ³ 0959:000F B409 MOV AH,09 ³ 0959:0011 CD21 INT 21 ³ 0959:0013 CB RETF <ÄÄÙ 0959:0014 BA0000 MOV DX,0000 <ÄÄ¿ 0959:0017 B409 MOV AH,09 ³ "SUB" 0959:0019 CD21 INT 21 ³ 0959:001B C3 RET <ÄÄÙ 0959:001C 0000 ADD [BX+SI],AL 0959:001E 0000 ADD [BX+SI],AL Notice the portion labeled CALLS and the portion labeled SUB. In an earlier column, I mentioned that DEBUG obligingly unassembles information past the end of our routine, and this information is of little use to us. This statement must be qualified, for we now see that some of the "code" unassembled beyond the end of our routine may be subroutines called by our routine. Let's single-step or trace through the code, to insure that it acts as we expect: -T AX=0000 BX=0000 CX=0280 DX=0000 SP=01FE BP=0000 SI=0000 DI=0000 DS=0949 ES=0949 SS=095D CS=0959 IP=0001 NV UP DI PL NZ NA PO NC 0959:0001 2BC0 SUB AX,AX -T AX=0000 BX=0000 CX=0280 DX=0000 SP=01FE BP=0000 SI=0000 DI=0000 DS=0949 ES=0949 SS=095D CS=0959 IP=0003 NV UP DI PL ZR NA PE NC 0959:0003 50 PUSH AX -T AX=0000 BX=0000 CX=0280 DX=0000 SP=01FC BP=0000 SI=0000 DI=0000 DS=0949 ES=0949 SS=095D CS=0959 IP=0004 NV UP DI PL ZR NA PE NC 0959:0004 B85B09 MOV AX,095B -T AX=095B BX=0000 CX=0280 DX=0000 SP=01FC BP=0000 SI=0000 DI=0000 DS=0949 ES=0949 SS=095D CS=0959 IP=0007 NV UP DI PL ZR NA PE NC 0959:0007 8ED8 MOV DS,AX Here comes the big moment. We are about to execute that call instruction. -T AX=095B BX=0000 CX=0280 DX=0000 SP=01FC BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=0009 NV UP DI PL ZR NA PE NC 0959:0009 E80800 CALL 0014 Notice that the name "SUB" in the source code has been replaced by the number "0014". -T AX=095B BX=0000 CX=0280 DX=0000 SP=01FA BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=0014 NV UP DI PL ZR NA PE NC 0959:0014 BA0000 MOV DX,0000 This instruction is the first instruction of SUB. (Verify this.) Congratulations, we had done something right. Also, note that the address at the far left has the magic number "14". This will all be tied together in a few columns. The purpose of this instruction is to prepare for the print string operation. -T AX=095B BX=0000 CX=0280 DX=0000 SP=01FA BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=0017 NV UP DI PL ZR NA PE NC 0959:0017 B409 MOV AH,09 Indicate that the print string option is desired. -T AX=095B BX=0000 CX=0280 DX=0000 SP=01FA BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=0019 NV UP DI PL ZR NA PE NC 0959:0019 CD21 INT 21 Uh-oh. We do not want to trace through INT 21H. INT 21H forms the spine of DOS function calls and is quite lengthy. The best way to proceed is to execute the entire interrupt by going to the instruction following it. But what is the address of the instruction following this INT? Actually, I do not know, but typing "U" we find -U 0959:0019 CD21 INT 21 0959:001B C3 RET 0959:00^C that the address is 959:1B, a RET instruction. As I needed only the address of the RET instruction, I pressed the Ctrl-Break keys to terminate the unassembly. In review, the address of the first instruction past the INT 21H is 959:1B, and going to this instruction: -G 959:1B POSITIVELY, PAUCITY AX=0924 BX=0000 CX=0280 DX=0000 SP=01FA BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=001B NV UP DI PL ZR NA PE NC 0959:001B C3 RET MSG1 was printed from subroutine SUB. When RET is encountered, we return to the subroutine which called SUB. This is, of course, CALLS. -T AX=0924 BX=0000 CX=0280 DX=0000 SP=01FC BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=000C NV UP DI PL ZR NA PE NC 0959:000C BA0800 MOV DX,0008 We are now at the instruction following the CALL statement in routine CALLS. Notice the address 959:C, and locate the instruction with this address in the original unassembly, above. -T AX=0924 BX=0000 CX=0280 DX=0008 SP=01FC BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=000F NV UP DI PL ZR NA PE NC 0959:000F B409 MOV AH,09 Typing G prints MSG2, and the program is terminated. -G AX=0924 BX=0000 CX=0280 DX=0008 SP=01FC BP=0000 SI=0000 DI=0000 DS=095B ES=0949 SS=095D CS=0959 IP=0011 NV UP DI PL ZR NA PE NC 0959:0011 CD21 INT 21 OF THOUGHT Program terminated normally -Q There you have it. Assembly-language subroutines behave as expected from our experience with high-level languages. Incidentally, we could not have named the above routine "CALL", as this is a reserved keyword used by the assembler to invoke subroutines. For this reason our program was named "CALLS." (I always like to be close to, but never in, trouble.) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The Future ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ I hope to be the first human being to write a useful assembly-language program requiring zero (0) bytes. (Assembly-language modules are known to use few bytes.) Imagine the number of programs that will fit on a disk! Next issue, I plan to delve deeply into segments, near and far calls, and writing to video RAM. In fact, I hope to cover everything known about assembly language. On second thought, that may require two articles at least! ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ File Name: ÛÛ asm1.txt ÛÛ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ