Welcome to Xander Obrzut's tutorial on 32-bit Assembly Language. In this tutorial Xander will guide you through as much knowledge of assembly language as the books he possess contain.
Xander owns a 16-bit reference manual for Assembly Language and a 32-bit reference manual for Assembly Language. Using these books, Xander will produce a easy to understand guide, with example programs at the end of each chapter for you, the programmer, to compile using MASM.
For your first program, at the end of this introduction, will be the Instructions to compile a 'Hello World' example program. These compiler instructions will be used through out this tutorial - so it is a good idea to learn right here and now how to use MASM and LINK to compile assembly code.
In the table below is your first taste of assembly code. Take a good review of the code - it will be explained in details after the program code listing. Also, there will be details on how to compile the code using MASM - the Microsoft compiler.
; --------------------------(Start of Program Listing)-------------------------------------------
| title Hello World Program | (hello.asm) | |
| ; this program displays "Hello, World!" | ||
| .model small | ||
| .stack 100h | ||
| .data | ||
| message db "Hello, World!",0dh,0ah,'$' | ||
| .code | ||
| main proc | ||
| mov | ax,@data | |
| mov | ds,ax | |
| mov | ah,9 | |
| mov | dx, offset message | |
| int 21h | ||
| mov | ax,4C00h | |
| int 21h | ||
| main endp | ||
| end main |
; --------------------------(END OF PROGRAM)-----------------------------------------
The above code starts with a title directive. This is where you can name the program you are working on and also provide the filename at the end of the title for future reference. Next comes the actual beginning of a program - the .model and .stack directives. The model dictates to the compiler several things, such as the program calling definitions to use such as far or near, to size of over all program code in machine code.
The .stack directive dictates how much space to allocate on the stack (the place where data is stored - we come to the stack at a later point in this tutorial). Here we allocate 100h (hex) space for the program. Following the .stack directive comes the first segment of the program.
Assembly programs are divided in to segments. You can have multiply .code segments that are linked together and also multiple .data segments. This, how ever, is only for .exe programs where as .com programs (virus etc...) do not have a .data segment but allocate variables at the end of the program code.
The message db "Hello, World!" section is a variable being declared called 'message' which uses db (define byte) as storage space. It is stored in bytes. You can define bytes,words,double words, and quad words if not more in the data segment.
Finally, we have the juice of the program. The .code segment. This is divided yet again in to procedures. This program starts with 'main proc' - just a name which can be any name but we decided up on 'main' to name the first procedure. At the end of the code is 'main endp' which tells the compiler this procedure has finished.
Now, let us look at the actual code. The first two lines initialise the data segment to ds. It is not possible to move @data directly in to ds (data segment) so it is first passed to the register AX in the first line of code, and ax is passed in to ds in the second line of code making ds point to the start of the .data segment (message db "Hello, World!" etc...
Next, we call DOS with the function in ah number 9 (out put data) - then call the interrupt 21h (this is calling DOS) to display the data segment. We move the offset of the message to display in to dx - this is so DX now points to message - and at the end of message we terminate it with 0dh,0ah,'$' telling DOS the end of the string. Remove these lines and you'll out put too much data and possibly run in to physical ram.
The last two lines of the code move 4C00h (exit Program / return to DOS) in to AX can call DOS with the INT 21h command. This safely returns the program to the command prompt - otherwise only God knows what would happen!
The last two lines of the actual program listing are main endp (discussed above) and also 'end main', now the latter is the actual end of the program. This tells the compiler not to process any more of the file hello.asm
Try the following line:
ML /Zi /Zm /Fm /Fl hello.asm /link /co C:\Irvine.lib
This is one way to compile hello.asm - the /Zi means include debugging info, /Fl means produce a listing file, (hello.lst)
/Fm means produce a map file (hello.map), and /Zm means use MASM 5.12 compatibility mode.
Another way - a much easier way - is to simply state the following...
masm hello
link hello
This, will also produce a compiled .exe to run from the command line. I just thought you would like to see a few
options for ML :)
Now, when you run that code - your command prompt will prolly say they can not find masm or link - this is because you need to declare the PATH= directive on the command line before doing that. I included in the download a file called autoexec.bat - run that file by typing its name from the command line and you will automatically be included the right PATH= statements to the 'bin' and 'binr' folders for MASM. Examine the autoexec.bat file by right clicking it and selecting 'Edit' to see what all the buff is about with this file...
One final point of worth noting is the CV command. This command runs the program in a debugger mode - pressing F8 I believe forwards you through the program command by command execution and you can watch the registers change as well as observe other things. It is pretty neat and can be invoked by typing CV hello
This will load hello.exe in to CV for examination. Cool, eh?
Using the Assembler Library| Title Link Library Demo Program | (lnkdemo.asm) | ||
| ; This program calls various i/o procedures | |||
| ; in the link library | |||
| .model small | |||
| .stack 100h | |||
| WhiteOnBlue = 1Fh | |||
| GreetingLoc = 0400h | |||
| .data | |||
| greeting | db "Link Library Demo Program" | ||
| db 0dh,0ah,0dh,0ah | |||
| db "What is your name? ",0 | |||
| numberPrompt | db 0dh,0ah | ||
| db "Please enter a 16-bit integer: ",0 | |||
| userName | db 50 dup(0) | ||
| pressAnyKey | db 0dh,0ah,0dh,0ah | ||
| db "Press any key...",0 | |||
| .code | |||
| extrn Clrscr:proc,Crlf:proc,Gotoxy:proc | |||
| extrn Readint:proc,Readstring:proc,Scroll:proc | |||
| extrn Readkey:proc,Writeint:proc,Writestring:proc | |||
| main proc | |||
| mov | ax,@data | ||
| mov | ds,ax | ||
| ; Clear the screen, scroll a blue window. | |||
| call near ptr Clrscr | |||
| mov | cx,0400h | ; upper left corner | |
| mov | dx,0B28h | ; lower right corner | |
| mov | bh,WhiteOnBlue | ||
| call near ptr Scroll | |||
| ; Display a greeting and ask for the | |||
| ; user's name. | |||
| mov | dx,GreetingLoc | ||
| call near ptr Gotoxy | |||
| mov | dx,offset greeting | ||
| call near ptr Writestring | |||
| mov | dx,offset userName | ||
| call near ptr Readstring | |||
| ; Ask the user to enter a signed decimal integer. | |||
| ; Redisplay the number in hex and binary. | |||
| mov | dx,offset numberPrompt | ||
| call near ptr Writestring | |||
| call near ptr Readint | ; in put an integer | ||
| call near ptr Crlf | |||
| mov | bx,16 | display in hexidecimal | |
| call near ptr Writeint | |||
| call near ptr Crlf | |||
| mov | bx,2 | ; display in binary | |
| call near ptr Writeint | |||
| mov | dx,offset pressAnyKey | ||
| call near ptr Writestring | |||
| call near ptr Readkey | |||
| call near ptr Clrscr | |||
| mov | ax,4C00h | ||
| int 21h | |||
| main endp | |||
| end main |
; ----------------------(END OF PROGRAM LISTING)--------------------------------
With this program, you are required to link to the irvine.lib after compiling to an .obj with MASM. There fore, use link /co lnkdemo and when it asks you for the .lib to link to - enter 'c:\irvine.lib' or where ever the irvine.lib was unzipped to enter.
This is an excellent example of using an external library for simplifying complex assembly operations.
After the title, .stack, and .model directives come two constants (WhiteOnBlue & GreetingLoc). These constants are declared out side the .data segment because the compiler does not include them as data in the program but rather replaces their labels when used in side the code segment with the value of the constant.
Next, comes the .data segment of the program. This is where we declare four strings, with terminators at the end of the strings. These strings will be loaded at the start of the .code segment with the first mov commands. The mov command will load ax with the @data location and then point ds to ax thus initialising the .data segment.
Now, the first few lines after the .code declaration start with the label 'extrn' followed by some procedure names. These procedures are 'external' to the program .code segment but are used in side it. The extrn command tells the compiler to look in a different segment to this one to find these procedures.
The rest of the code is simply loading registers with the correct values for the external procedures to use. For example, the Writestring procedure requires the offset of the message (terminated, obviously) to display to be loaded in to the dx register before calling Writestring. There are many examples of calls through out this program listing. If you also notice, the call statement has a 'near ptr' in between the call and procedure being called.
This 'near ptr' declaration tells the compiler not to look too far for the procedure in side the code block. That means, to use a memory model that is probably 16-bits wide rather than 32-bits wide for a far ptr. This, in turn, decreases the amount of RAM used for calling the procedures. Further more, 'near ptr' is required by MASM because with out it, MASM craps out with an error message if it is not included. Quicks of the job, I guess.
Procedures and InterruptsNow that we have covered some of the fundamentals of assembly programming, we can look to increase our knowledge base with writing our own procedures and also take a look at some other INT (interrupts.)
The next program has no out put, there fore we suggest you run it with CV to see the state of the registers when the program is ran. This is because the program asks for in put in one sub-routine, and another sub-routine processes two arrays in an addition of integers in side registers to observe via CV.
Compile as usual, with masm and link - but this time you are not required to use the irvine.lib with this assembly.
;---------------------------------(START OF PROGRAM LISTING)-------------------------------------| title Subroutine Demonstration | (SUBS.ASM) | ||
| ; This program calls two subroutines: one for | |||
| ; keyboard input, another to add the elements | |||
| ; in an array of integers. | |||
| .model small | |||
| .stack 100h | |||
| .data | |||
| char db ? | |||
| sum dw ? | |||
| array dw 100h,200h,300h,400h,500h | |||
| array_size = 5 | |||
| .code | |||
| main proc | |||
| mov | ax,@data | ; set up the DS register | |
| mov ds,ax | |||
| call near ptr inputChar | ; input keyboard into AL | ||
| mov char,AL | ; store in a variable | ||
| ; prepare to call the calcSum procedure. | |||
| mov | bx,offset array | ; BX points to array | |
| mov | cx,array_size | ; CX = array count | |
| call near ptr calcSum | ; calculate sum | ||
| mov | sum,ax | ; store in a variable | |
| mov | ax,4C00h | ; return to DOS | |
| int 21h | |||
| main endp | |||
| inputChar proc | |||
| mov | ah,1 | ; input character from keyboard | |
| int 21h | |||
| ret | |||
| inputChar endp | |||
| ;----------------------------------------------- | |||
| ; Calculate the sum of an array of integers. Input: | |||
| ; BX points to the array and CX contains the | |||
| ; array size. Returns the SUM in AX. | |||
| ;----------------------------------------------- | |||
| calcSum proc | |||
| push bx | ; save BX, CX | ||
| push cx | |||
| mov | ax,0 | ||
| CS1: | |||
| add | ax,[bx] | ||
| add | bx,2 | ; point to next integer | |
| loop CS1 | |||
| pop cx | |||
| pop bx | |||
| ret | |||
| calcSum endp | |||
| end main |
This is a relatively short program to illustrate what makes assembly programs powerful (and some times bloated!) The sub-routines, or procedures, can extend a programs functionality and make it modular. Eventually, when you have written a collection of sub-routines or procedures (the two names are interchangeable) then you will want to include them in a library, just like K.R. Irvine did in his 32-bit assembly book.
As you can see in this program - it starts out with a title and .data segment and moving the @data segment in to AX and then AX in to DS so DS points to the start of the data segment. But, now directly after that we have a call to a procedure what is written after the main proc as a proc of its own called inputChar proc. This inputChar procedure receives a character from key board in put.
We again use 'near ptr' because MASM can not handle any thing other wise. Perhaps Turbo Assembler by Borland would do better?
Now, following this call to inputChar, we loads the registers with the correct values for the call to the procedure calcSum. This procedure, adds up the numbers stored in the array of integers (100h,200h,300h...) This routine eventually returns with the SUM in AX. This AX word pointer has the data pointed to put in to a variable called 'sum' with the mov sum,ax command.
A point worth noting of the calcSum procedure - is it introduces some new commands called push and pop. These commands relate to the stack declared at the start of the program with the .stack 100h directive. This stack grows downwards in memory. There fore, what ever is put on the stack last, is the first to be taken off! This is why the push commands are a mirror image of the pop commands. DX,CX is pushed where as CX,DX is popped from the stack. Try and visualise it in your head - memory locations starting at 0000:0100H - moving downwards to 0000:0090h to 0000:0080h as each value is put on to the stack. The stack pointer decreases or increases its memory location with each push and pop.
Well, we will winky take a look at the stack more in subsequent chapters of this tutorial.
Also, in the calcSum procedure you see the 'add' command. This creates a simple addition via one register and a data. The data could be another register [bx] or a variable offset var1.
Video Page Swapping Example| title Video Page Swapping Example | (page.asm) | ||
| ; This program switches back and forth between | |||
| ; text pages 0 and 1 on a colour display. | |||
| .model small | |||
| .stack 100h | |||
| .data | |||
| page0 db 'This is video page zero.$' | |||
| page1 db 'This is video page one.$' | |||
| .code | |||
| main proc | |||
| mov | ax,@data | ; Initialise the DS register | |
| mov | ds,ax | ||
| mov | ah,9 | ; Display a message | |
| mov | dx, offset page0 | ||
| int 21h | |||
| to_page_1: | |||
| mov | ah,5 | ; set video page | |
| mov | al,1 | ; to page 1 | |
| int 10h | |||
| mov | ah,9 | ; display a message | |
| mov | dx,offset page1 | ||
| int 21h | |||
| mov | ah,1 | ; get keystroke | |
| int 21h | |||
| to_page_0: | |||
| mov | ah,5 | ; set video page | |
| mov | al,0 | ; to page 0 | |
| int 10h | |||
| mov | ax,4c00h | ; return to DOS | |
| int 21h | |||
| main endp | |||
| end main |
The program starts with the usual suspects of title,.model, and .stack. There is the .data and .code segements as per usual. At the start, as with all .data segment programs, we initialise the .data segment via AX to DS. Then, we start calling DOS INT 21h (DOS Interrupt) to display a message (mov ah,9) via dx with dx loaded with the offset of the page0 message.
Then, we turn to INT 10h, the video Interrupt - with ah,5 loaded (set video page) and al,0 (to this page number) and call INT 10h. Then we repeat calling INT 21h with ah,9 loaded to display message in dx with offset page1.
Using labels to_page_0: and to_page_1: etc... we can navigate between program segments. Eventually, as per usual, at the end of the program, we call DOS INT 21h with ax,4c00h loaded (return to DOS.)
For a little trial of your own - try using the jmp to_page_1: command to loop back to the initial to_page_1: segment for a continuous looping program. You'll have to press Ctrl+C or Ctrl+D to exit this program - or just simply exit DOS command.com. IF you exit DOS - remember to run autoexec.bat to set the PATH=%PATH% variables again!
Conditional Jumps| title Test Alphabetic Input | (ISALPHA.ASM) | ||
| ; This program reads and displays characters | |||
| ; until a non-alphabetic character is entered. | |||
| .model small | |||
| .stack 100h | |||
| .code | |||
| main proc | |||
| mov | ax,@data | ||
| mov | ds,ax | ||
| L1: | |||
| mov | ah,1 | ; input a character | |
| int 21h | ; AL = character | ||
| call near ptr Isalpha | ; test value in AL | ||
| jnz | exit | ; exit if not alphabetic | |
| jmp | L1 | ||
| exit: | |||
| mov | ax,4c00h | ; return to DOS | |
| int 21h | |||
| main endp | |||
| ; Isalpha sets ZF = 1 if the character | |||
| ; in AL is alphabetic. | |||
| Isalpha proc | |||
| push | ax | ; save AX | |
| and | al,11011111b | ; convert to uppercase | |
| cmp | al,'A' | ; check A to Z range | |
| jb | B1 | ; jump if below the ASCII code for A | |
| cmp | al,'Z' | ; check A to Z range | |
| ja | B1 | ; jump if above the ASCII code for Z | |
| test | ax,0 | ; ZF = 1 | |
| B1: | |||
| pop | ax | ; restore AX | |
| ret | |||
| Isalpha endp | |||
| end main |
| Title Encryption Program | (encrypt.asm) | ||
| .model small | |||
| .stack 100h | |||
| XORVAL = 239 | ; any value between 0-255 | ||
| .code | |||
| main proc | |||
| mov | ax,@data | ||
| mov | ds,ax | ||
| L1: | |||
| mov | ah,6 | ; direct console input | |
| mov | dl,0FFh | ; do not wait for character | |
| int 21h | ; AL = character | ||
| jz | L2 | ; quit if ZF = 1 (EOF) | |
| xor | al,XORVAL | ; encrypt the char | |
| mov | ah,2 | ; write to output | |
| mov | dl,al | ||
| int 21h | |||
| jmp L1 | ; repeat the loop | ||
| L2: | |||
| mov | ax,4c00h | ; return to DOS | |
| int 21h | |||
| main endp | |||
| end main |
encrypt < plain.txt > coded
encrypt < coded > plain.txt
This is a simple two way system for encryption. First, input the plain text file in to encrypt with out put going to coded file. Then to decrypt coded, in put coded file in to encyrpt with out put going to a plain text file.
Interger Arithmetic| title Display ASCII Binary | (BIN.ASM) | ||
| ; This program displays a number in binary. | |||
| .model small | |||
| .stack 100h | |||
| .data | |||
| prompt db "Enter a decimal integer: ",0 | |||
| .code | |||
| extrn Clrscr:proc,Crlf:proc | |||
| extrn Readint:proc,Writestring:proc | |||
| main proc | |||
| mov | ax,@data | ||
| mov | ds,ax | ||
| ; prompt for an integer: | |||
| call near ptr Clrscr | |||
| mov | dx,offset prompt | ||
| call near ptr Writestring | |||
| call near ptr Readint | ; read integer into AX | ||
| call near ptr Crlf | |||
| mov | cx,16 | ; number of bits in AX | |
| L1: | |||
| shl | ax,1 | ; shift AX left into Carry Flag | |
| mov | dl,'0' | ; choose '0' as default digit | |
| jnc L2 | ; if no carry, jump to L2 | ||
| mov | dl,'1' | ; else move '1' to dl | |
| L2: | |||
| push ax | ; save AX | ||
| mov | ah,2 | ; display DL | |
| int 21h | |||
| pop ax | ; restore AX | ||
| loop L1 | ; shift another bit to left | ||
| mov | ax,4C00h | ; exit program | |
| int 21h | |||
| main endp | |||
| end main |
The following program is an example of structures. It uses a student data base for a premise on how to use structures. Structures are basically data blocks that can be initialised by other variables either with their own unique data or as a standard data model. This will be shown in greater detail in the program that follows this paragraph.
| Title Structure Input Example | (STRUC.ASM) | ||
| .model small | |||
| .stack 100h | |||
| STUNUMBER_SIZE = 7 | |||
| LASTNAME_SIZE = 20 | |||
| ACTIVE_STATUS = 1 | |||
| STUDENT_COUNT = 5 | |||
| typStudent struc | |||
| IdNum db STUNUMBER_SIZE + 1 dup(?) | |||
| Lastname db LASTNAME_SIZE + 1 dup(?) | |||
| Credits dw ? | |||
| Status db ? | |||
| typStudent ends | |||
| .data | |||
| progTitle db "Student Structure Demonstration",0 | |||
| srec typStudent <> | ; create a blank student | ||
| ; Initialise and declare a structure variable: | |||
| rec2 typStudent <"1234","Baker",32,ACTIVE_STATUS> | |||
| ; Declare an array of students: | |||
| allStudents typStudent STUDENT_COUNT dup( <> ) | |||
| .code | |||
| extrn Clrscr:proc,Writestring:proc,Readstring:proc | |||
| extrn Crlf:proc,Readint:proc | |||
| main proc | |||
| mov | ax,@data | ||
| mov | ds,ax | ||
| call near ptr Clrscr | |||
| mov | dx,offset progTitle | ||
| call near ptr Writestring | |||
| call near ptr Crlf | |||
| ; Use a loop to input an array of students. | |||
| mov | si,offset allStudents | ||
| mov | cx,STUDENT_COUNT | ||
| L1: | |||
| call near ptr InputStudent | |||
| add | si,SIZE typStudent | ||
| Loop L1 | |||
| mov | ax,4c00h | ||
| int 21h | |||
| main endp | |||
| ; Input the fields for a single student from | |||
| ; the console. Input parameter: SI points to the | |||
| ; typStudent object. | |||
| InputStudent proc | |||
| push | ax | ||
| push | cx | ||
| push | dx | ||
| mov | dx,si | ; point to the structure | |
| add | dx,IdNum | ; point to the IdNum field | |
| mov | cx,STUNUMBER_SIZE | ||
| call near ptr Readstring | ; get the student ID | ||
| call near ptr Crlf | |||
| mov | dx,si | ||
| add | dx,LastName | ||
| mov | cx,LASTNAME_SIZE | ||
| call near ptr Readstring | ; get the last name | ||
| call near ptr Crlf | |||
| call near ptr Readint | ; get the credits | ||
| mov | (typStudent PTR [si]).Credits,ax | ||
| call near ptr Crlf | |||
| mov | (typStudent PTR [si]).Status, ACTIVE_STATUS | ||
| pop | dx | ||
| pop | cx | ||
| pop | ax | ||
| ret | |||
| InputStudent endp | |||
| end main |
; ---------------------(END OF PROGRAM LISTING)-----------------------
In this above code program example, you see your first use of struc (structures.) These structures are much like objects in the Assembly world. They can be created at the start of the program, to be used through out the program via initialising variables to these structures. They can contain new data, or original data on structure declare - even empty data.
At the end of the program main procedure is the InputStudent procedure. This procedure is called in a loop with CX holding the student number count (5) and this will loop until CX is zero calling the InputStudent procedure on each loop. This is where you enter data at the console (with out out put telling you what data needs in putting - check the program code listing on what data needs in putting at what time...) and this in put data is stored in the student structures declared through out the program.
Numeric Conversions and Libraries| title Character Filtering | (XLAT.ASM) | ||
| ; This program filters input from the console | |||
| ; by screening out all ASCII codes less than | |||
| ; 32 or greater than 127. Uses INT 16h for | |||
| ; direct keyboard input. | |||
| .model small | |||
| .stack 100h | |||
| INPUT_LENGTH = 20 | |||
| .data | |||
| validchars label byte | |||
| db 32 dup (0) | ; invalid chars: 0-31 | ||
| db 96 dup (0FFh) | ; valid chars: 32-127 | ||
| db 128 dup (0) | ; invalid chars: 128-255 | ||
| .code | |||
| main proc | |||
| mov | ax,@data | ||
| mov | ds,ax | ||
| mov | bx,offset validchars | ||
| mov | cx,INPUT_LENGTH | ||
| getchar: | |||
| mov | ah,0 | ; keyboard input | |
| int 16h | ; CHAR is in AL | ||
| mov | dl,al | ; save copy in DL | |
| xlat validchars | ; look up char in AL | ||
| or | al,al | ; invalid char? | |
| jz getchar | ; yes: get another | ||
| mov | ah,2 | ; no: output the char | |
| int 21h | |||
| loop getchar | |||
| mov | ax,4c00h | ||
| int 21h | |||
| main endp | |||
| end main |
One of the best uses of XLAT is to filter out unwanted characters from a stream of text. Suppose we want to input a string of characters from the keyboard and echo only those with ASCII values from 32 to 127. We can set up a translate table, place a zero in each table position corresponding to an invalid character, and place 0FFh in each valid position:
validchars db 32 dup (0) ; invalid chars 0-31If XLAT returns a value of zero in AL, we skip the character and jump back to the top of the loop. (When AL is ORed with itself, the Zero Flag is set if AL equals 0). If the character is valid, 0FFh is returned in AL , and we use INT 21h to display the character in DL.
Now we are slowly shifting along to the beat of assembly language. Let us take a look back at the irvine.lib library. This library stores a collection of routines to be used in programs through out this tutorial. If there is an extrn reference in a source code, you know you must include the irvine.lib during the link compiling section. But, there are more tricks of the trade when it comes to examing existing libraries and making your own libraries!
lib irvine.lib,con
This command, the lib command, with con after the comma at the end of the command, views a listing of the library contents. This is useful for dissecting the library. Yet, there is more...type lib /help for further details.
| C:\>lib /help | |
| Microsoft (R) Library Manager Version 3.20.010 | |
| Copyright (C) Microsoft Corp 1983-1992. All rights reserved. | |
| USAGE: LIB library [options] [commands] [,listfile [,newlibrary]] | |
| Options: | |
| /? | : display LIB options |
| /HELP | : display help on LIB |
| /IGNORECASE | : ignore case on names |
| /NOEXTDICTIONARY | : do not build extended dictionary |
| /NOIGNORECASE | : do not ignore case on names |
| /NOLOGO | : do not display signon banner |
| /PAGESIZE:n | : set library page size to n |
| Commands: | |
| +name | : add object file |
| -name | : delete object file |
| -+name | : replace object file |
| *name | : copy (extract) object file |
| -*name | move (delete and extract) object file |
To create a lbrary use this command: lib string;
Then add individual object files to the created library as follows: lib string +strwrite
Then as stated earlier, to view the listing of the library type: lib string,con
Disk Storage| mov ah,0Eh | ; set default drive | ||
| mov dl,0 | ; select drive A | ||
| int 21h | |||
| mov numberOfDrives,al |
To set the default disk drive, call function 0Eh and pass it a number in DL corresponding to the disk drive (A = 0, B = 1, C = 2, etc)
| mov al,19h | ; get default drive | ||
| int 21h | |||
| mov current_drive,al |
To find out which drive is currently the default, call DOS function 19h. DOS returns the number of the logged drive in AL (A = 0,B=1,C=2, etc)
This may be redundant now with much larger disks, but it is worth a try on a floppy I guess...
| mov ah,36h | ; get disk space | ||
| mov dl,3 | ; select drive C | ||
| int 21h | |||
| ; Return values: AX = sectors per cluster | |||
| BX = available clusters | |||
| CX = bytes per sector | |||
| DX = clusters per drive | |||
| ; (calculate disk free space) | |||
| push dx | ; clusters per drive | ||
| push cx | ; AX = AX * CX = bytes per cluster | ||
| push ax | AX = cluster size | ||
| push bx | DX:AX = available bytes | ||
| ; (get total disk capacity) | |||
| pop ax | ; cluster size | ||
| pop dx | ; clusters per drive | ||
| pop dx | ; DX:AX = total disk capacity |
To get the current directory, call DOS function 47h with INT 21h. Pass a drive code in DL (default = 0, A=1,B=2,C=3,etc) and point DS:SI to a 64-byte buffer. In this buffer, DOS places a null terminated string with the full pathname from the root directory to the current directory (the drive letter and leading backslash are omitted)
| .data | |||
| pathname db 64 dup(0) | ; path stored here | ||
| .ocde | |||
| mov ah,47h | ; get current directory path | ||
| mov dl,0 | ; on default drive | ||
| mov si,offset pathname | |||
| int 21h | |||
| jc display_error |
| .data | |||
| pathname db 'C:\ASM\PROGS',0 | |||
| .code | |||
| mov ah,3Bh | ; set current directory | ||
| mov dx, offset pathname | |||
| int 21h | |||
| jc display_error |
| .data | |||
| pathname db '\ASM',0 | |||
| .code | |||
| mov ah,39h | ; create subdirectory | ||
| mov dx,offset pathname | |||
| int 21h | |||
| jc display_error |
Well, so far, we have covered quite a lot of ground. You will find now and in the previous chapter that we are not including full program listings but rather program excerpts - this is because you should now be talented enough in asm programming to write your own .data segment and initialise it to ds as well as call DOS with ax loaded with 4c00h to return to DOS safely.
In the following sections are some file commands, namely DOS related, that alter the file system to some degree.
| .data | |||
| filespec db "C:\Sample.obj",0 | |||
| .code | |||
| mov ah,41h | ; delete file | ||
| mov dx,offset filespec | |||
| int 21h | |||
| jc display_error |
If DOS fails and the carry flag is set, the possible error codes are 2 (file not found), 3 (path not found), and 5 (access denied because file is read only). To delete a file that is read only, you must first call Function 43h (change file mode) to change its attribute.
| .data | ||
| oldname db "prog1.asm",0 | ||
| newname db "prog2.asm",0 | ||
| .code | ||
| mov ah,56h | ; rename file | |
| mov dx,offset oldname | ||
| mov di,offset newname | ||
| int 21h | ||
| jc display_error |
The error codes are the same as Delete File (41h) but with one exception, 11h (not same device) which is where the filenames refer to files on different disks.
The following statements move prog1.asm from the current directory to the \asm\progs directory...
| .data | ||
| oldname db "prog1.asm",0 | ||
| newname db "\asm\progs\prog1.asm",0 | ||
| .code | ||
| mov ah,56h | ; rename file | |
| mov dx,offset oldname | ||
| mov di,offset newname | ||
| int 21h | ||
| jc display_error |
| .data | ||
| newfile db "NEWFILE.DOC",0 | ||
| handle dw ? | ||
| .code | ||
| mov dx,offset newfile | ||
| mov ah,3ch | ; function: create file | |
| mov cx,0 | ; normal file attrib. | |
| int 21h | ||
| jc display_error | ||
| mov handle,ax |
As you can see there is a moment during this program when a 0 gets moved into CX and this is the 'normal attribute'. There are other attributes you can move in to CX:
00h Normal FileWell, that might be all well and good if you want to DESTROY an existing file, but what if you want to preserve an already existing file? Calling 3Ch will just zero the existing file to the beginning, so it is not a good idea to call that routine on an already existing file - another means follows - which you should use before calling 3Ch to check if a file is already existing before writing or reading it...
| .data | ||
| filename db "FILE1.DOC",0 | ||
| .code | ||
| mov ah,5Bh | ; create new file | |
| mov cx,0 | ; normal attrib. | |
| mov dx,offset filename | ||
| int 21h | ||
| jc error_routine |
Error codes: If DOS sets the carry flag - the errors are as follows;
3 - PATH NOT FOUND
4 - TOO MANY FILES OPEN
5 - ACCESS DENIED
You can, instead of using 5Bh, use 3Dh (open file). Using 3Dh will set the carry flag if the file does not exist and return AX = 2 (file not found). Then you can safely use 3Ch to create your file. Sorted.
3Dh has several attributes - one of which is the 'open state' of the function call. This is basically if you want to open the file for reading,writing, or both.
AL Mode
0 Input (read only)
1 Output (write only)
2 (INPUT-OUTPUT)
| .data | ||
| filename db 'C:\FILE1.DOC',0 | ||
| infilehandle dw ? | ||
| .code | ||
| mov ah,3Dh | ; function: open file | |
| mov al,0 | ; choose the input mode | |
| mov dx,offset filename | ||
| int 21h | ; call DOS | |
| jc display_error | ; ERRROR? Display error | |
| mov infilehandle,ax | ; no error: save the file handle |
To close a file, call function 3Eh and place the file handle in BX. It also sets the date and time of a file when closed...
| .data | ||
| infile db 'B:\FILE1.DOC',0 | ||
| infilehandle dw ? | ||
| .code | ||
| mov ah,3Eh | ; close the file handle | |
| mov bx,infilehandle | ||
| int 21h | ||
| jc display_error |
| .data | ||
| buffersize = 512 | ||
| filehandle dw ? | ||
| buffer db buffersize dup(0) | ||
| .code | ||
| mov ah,3Fh | ; read from file or device | |
| mov bx,filehandle | BX = file handle | |
| mov cx,buffersize | ; number of bytes to read | |
| mov dx,offset buffer | ; point to buffer | |
| int 21h | ||
| jc Display_Error | ; error if CF = 1 | |
| cmp ax,cx | compare the bytes requested | |
| jbe exit | yes: quit reading |
| .data | ||
| buffer db 100h dup(?) | ; output buffer | |
| handle dw ? | ; file handle | |
| .code | ||
| write_to_file: | ||
| mov ah,40h | ; write to file or device | |
| mov bx,handle | ; file handle returned by open | |
| mov cx,100h | ; number of bytes to write | |
| mov dx,offset buffer | ; DX points to the buffer | |
| int 21h | ; write bytes | |
| jc display_Error | ; ERROR? Display error | |
| cmp ax,100h | ; all bytes written? | |
| jne close_file | ; no? Disk is full |
Well, we've come to the end of the tutorial. There are some parts I missed out on purpose, other parts I kept in for prosperities sake. But, overall, I believe with this tutorial you can start on your way to becoming an advanced assembly programmer. Use the Internet to search for more code snippets - hell - buy a book! But, most of all, keep reading, studying, and learning about assembly language. Because, high-level languages have their place, but nothing beats some raw machine code!