80x86 Assembly with Masm: Tutorial IV
80x86 Assembly with Masm 5.0: Tutorial IV
ferguson_travis@hotmail.com
Previous tutorials can be found at http://www25.brinkster.com/cheeseball as can code examples.
This tutorial will cover the definition of Arrays as well as File input/output using Microsoft Macro Assembler 5.00. Tutorial II has a link as to where to find the assembler and linker, Google.com is also another good reference.
Arrays in MASM are quite simple. Inside the data segment (The part between the .data and .code) simply put “<varname> <type> <number> DUP(X)”. Where varname is the variable name you wish to declare, type is the type of variable it will be, number is the number of the types in the array, DUP means we are duplicating what is inside the brackets. And a simple code example:
http://www25.brinkster.com/cheeseball/tut04-1.asm
Code:
.model small
.stack
.data
array1 dw 50 DUP("A") ;Set array1 = 50 A's.
.code
start:
mov ax, SEG array1 ;Put address of data segment into ax
mov ds, ax ;then into DS (we can't do this directly)
lea di, array1 ;put address of array1 into DI
add di, 50 ;move to position 50
mov al, "$" ;put $ into al
mov [di], al ;put the $ into DI at position 50
;we now have 49 letter A's ending with a $
mov ax, SEG array1 ;Put segment address of array1 into AX
mov ds, ax ;Then into DS
mov dx, OFFSET array1 ;text at DS:DX
mov ah, 09h ;print text to screen
int 21h ;Function 9h of Interrupt 21h
mov ax, 4c00h ;Function 4c00h of Int 21h
int 21h ;Return to OS
end start ;End program (it starts at Start Label)
As you can see in the above example, arrays open your programming style and let you do things that would otherwise be extremely difficult. We could have easily used any data type before the DUP command (i.e. DW, DB, BYTE, or DWORD). We can create any type of array (I used a character array because it will be the most common).
Now we get to learn about a new register, the FLAGS register. This register is 16 bits and inside there are a series of flags which give the status of certain things the CPU has been doing. The CF flag is the first (and only for this tutorial) we will be working with. The CF flag is stored at the 0 bit of the register, in order to access this we will be working with a new instruction, LAHF, which loads the low byte of the FLAGS register into the high byte of the AX register, thus making AH = first 8 bits of FLAGS. Now that we know how to access FLAGS we need to find out how to only read one flag from it. This part is trivial using the SHL and SHR opcodes. SHL shifts everything in a register one place to the left so “0101” would become “1010” and “0001” would become “0010”. So now for a very wonderful example of how this will work:
Code:
LAHF ;put FLAGS into AH
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
;AH now holds CF flag value 0 or 1
After this we can simply test AH for either 00h or 01h, TRUE or FALSE. There are far better ways to do this, mainly using the AND opcode. The MUCH easier way of doing this would be:
The SHL (and SHR) instructions both occupy 2 bytes of space. The AND instruction only takes up 3 bytes, thus we can either take up 14 bytes clearing the other flags out of AH or we can take up 3 bytes. Now for what we have all been waiting for, we are going to open, close, read, and write to a file. (to keep the size of this tutorial down we are not going to be doing this all in the same program, we will split it up into multiple programs).
First of all, opening a file is quite a bit easier than it sounds (actually I think it is easier than in C). First of all, just like almost everything in assembly (at least x86 DOS programming) we are going to be using an interrupt. Most file IO operations are accomplished using INT 21h. To open a file we first need to know what function in INT 21h it is, and what values we must pass along with it. Here is that information
Code:
AH = 3dh
AL = bit 0-2 is the Access mode
000 = Read Only
001 = Write Only
010 = Read/Write Mode
bits 4-6 is the Sharing mode (DOS 3.5 and up). (I never use these)
000 Compatibility mode
001 Deny all
010 deny write
011 deny read
100 deny none
DS:DX = SEGMENT:OFFSET of ASCIIZ pathname.
The output is as follows:
CF = 0 if it worked
AX = Handle if it worked
CF = 1 if we got an error
AX = Error Code
01h = missing file sharing program
02h = file not found
03h = path not found or no file exist at path
04h = no file handle available.
05h = access denied
0ch = access mode not permitted
As you can see, there is not that much more needed to open a file than is needed to print text to the screen, and there is also built in handling of errors we simply have the test the end results of opening the file and jump to the correct fix if there is an error. Now to learn how to close files.
Code:
Closing a File:
AX = 3e00h
BX = File Handle
The output of this will be:
CF = 0 if it worked
AX = nothing, it will be random
CF = 1 if function did not work.
AX = error code – 06h if file not opened or incorrect file handle
Warning: Do not call this with a 00h file handle as handle 00h is the STDIN and will lock your access to the machine.
Reading from a file or device:
AH = 3fh
BX = Handle
CX = Number of bytes to read from file
DS:DX = SEGMENT:OFFSET of buffer to hold info that is in the file
The output will be:
CF = 0 if successful
AX = Number of bytes read
CF = 1 if an error occurred
05h = Access denied
06h = incorrect handle or file not opened
CF + AX = 0 then EOF (End of File)
CF = 0 AND AX < CX EOF or error occurred.
Creating Files:
AH = 3ch
CX = File Attrib
Bit 0 = read only
Bit 1 = hidden file
Bit 2 = system file
Bit 3 = volume (ignore this one)
Bit 4 = directory (ignore this one too for now)
Bit 5 = archive bit
Bit 6-15 = reserved (do not touch)
DS:DX = ASCIIZ pathname
The output is as follows:
CF = 0 if successful
AX = File Handle
CF = 1 if an error occurred
03h = path not found
04h = no handle available.
05h = access denied
Writing to files:
AH = 40h
BX = File Handle
CX = number of bytes to write
DS:DX = SEGMENT:OFFSET of information to write
Now we have all the information needed to do anything with a file. All we need now is to learn how to use it. For doing this we have a very great and informative (slightly non optimized as I am using shl and shr instead of AND in the code) code example:
http://www25.brinkster.com/cheeseball/tut04-2.asm
Code:
.model small
.stack
.data
failedcf db "Failed to create file!$"
generr db "General error!$"
readf db "Read Failed!$"
message db "Message to put into blank file!$",0
filename db "test.tst",0
buffer db 50 DUP ("$")
handle dw ?
.code
start:
mov ax, @data ;Put data segment into AX
mov ds, ax ;Then put it into DS
mov ah, 3dh ;function 3dh of int 21h (open file)
mov al, 02h ;for read/write mode
mov dx, OFFSET filename ;filename ASCIIZ at DS:DX
int 21h ;Call interrupt to finish open
mov handle, ax ;put handle or error into handle
LAHF ;put FLAGS into AH
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
;AH now holds CF flag value 0 or 1
cmp ah, 00h ;if successful open
je fileopen ;then goto fileopen
cmp ah, 01h ;if not successful open
je filenoopen ;then goto filenoopen
filenoopen:
cmp handle, 01h ;if no file sharing software
je generror ;goto generror
cmp handle, 02h ;if file not found
je filenotfound ;goto filenotfound
cmp handle, 03h ;if path not found
je filenotfound ;goto filenotfound
cmp handle, 04h ;if no handle aval
je generror ;goto generror
cmp handle, 05h ;if access denied
je generror ;goto generror
cmp handle, 0ch ;if access mode not permitted
je generror ;goto generror
jne generror ;else goto generror
generror:
mov dx, OFFSET generr ;print generr to screen
mov ah, 09h ;function 09h of int 21h (print text)
int 21h ;call int 21h
mov ax, 4c00h ;function 4ch of int 21h (return to OS)
int 21h ;call int 21h
filenotfound:
mov ah, 3ch ;function 3c (create file) int 21h
mov cx, 00h ;no special attribs
mov dx, OFFSET filename ;ASCIIZ offset of filename
int 21h ;create the file
mov handle, ax ;save handle into handle
mov dx, OFFSET message ;put offset of message into DX
mov bx, handle ;handle into BX
mov cx, SIZEOF message ;send into file message size
mov ah, 40h ;function 40h (write to file)
int 21h ;call int 21h
LAHF
mov dh, ah
mov ax, 3e00h ;closing file (function 3eh of int 21)
mov bx, handle ;file with handle
int 21h ;call int 21h
mov ah, dh ;put old EFLAGS into AH
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
;AH now holds CF flag value 0 or 1
cmp ah, 00h ;if successful open
je start ;then goto fileopen
cmp ah, 01h ;if not successful open
je failedcreatefile ;goto failedcreatefile
failedcreatefile:
mov dx, OFFSET failedcf ;offset of failedcf
mov ah, 09h
int 21h
mov ax, 4c00h ;return to DOS
int 21h ;call int 21h
fileopen:
mov ah, 3fh ;read file
mov bx, handle ;file with handle in handle
mov cx, 50 ;num of chars to read
mov dx, OFFSET buffer ;offset to store at DS:DX
int 21h ;call int to finish off
LAHF ;put FLAGS into AH
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shl ah, 1 ;shift ah left one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
shr ah, 1 ;shift ah right one
;AH now holds CF flag value 0 or 1
cmp ah, 00h ;if read worked
je readsuccess ;goto readsuccess
cmp ah, 01h ;if it did not work
je readfailed ;goto readfailed
readfailed:
mov dx, OFFSET readf ;print text to screen func
mov ah, 09h ;function 09, int 21h
int 21h ;call int 21h
mov ax, 4c00h ;RETURN TO OS
int 21h
readsuccess:
mov ax, 3e00h ;closing file (function 3eh of int 21)
mov bx, handle ;file with handle
int 21h ;call int 21h
mov dx, OFFSET buffer ;print message at buffer
mov ah, 09h ;function 09h of int 21h
int 21h ;call int 21h
mov ax, 4c00h ;return to OS int 21h end start
int 21h
end start
Yeah, that is a LOT longer than the rest of the code used in all my tutorials. Sorry I think I got a little carried away with that one (not really, everything there is needed to keep the program flow logical). This code completes this tutorial and hopefully now we understand file handling and arrays in assembly. If anyone has any questions you can reach me at the email address above.
8086 Assembly language programming
Hey guys, i'm writing a code in assembly language using Turbo Assembler 2.0. If anybody out there is using it too then maybe you can help me with my question. ( I just hope cheeseball can read this.)
I'm creating in assembly language a game. To provide a good user interface i have a module which creates a window (with shadow). the width and height of the window depends on the coordinates passed into the stack.
Now, because assembly language has no built-in functions, I have to write one which creates and destroys a window.
Let's say I'll create a window with blue background and white foreground with these parameters: row1 = 10, col1=20, row2=20, col2=60.
In assembly, I'll only invoke function 06 of int 10 like this:
mov ah, 06
mov al, 00
mov bh, 17h
mov cx, 0a14h
mov dx, 143dh
int 10h
now, my problem is when i try to remove the window, the underlying screen data is removed too.
how do i display this window so that when i remove it, the underlying data is also preserved?
And, how do I allocate memory dynamically in assembly?