Introduction
This tutorial looks at how we deal with arrays in assembler. we are going to extend our console application by reading in some user text from the console, and print it back in reverse.
New Instructions
We are introducing four new instruction to our repetoire in this tutorial
- lea - load the effective address of a memory location into a register
- dec - decrement a register or a memory location
- or - perform a logical or on a register with memory, memory with a register, or a register with a register
- jne - jump if not equal
The code
; This is a console application that reads some text from the standard input
; device and prints the string back to the output device, but in reverse
global _mainCRTStartup ; This is the main program entry point
extern _ExitProcess@4 ; Windows API call to exit the process
extern _GetStdHandle@4 ; Windows API call to get the standard output handle
extern _ReadFile@20 ; Windows API call to read from a handle
extern _WriteFile@20 ; Windows API call to write to a handle
section .data ; Start of the data segment
prompt db 'Please enter some text', 10, 10
terminator db 0 ; The terminating character
input_string times 256 db 0 ; Space for input message << This is our char array
bytes_read dd 0 ; Return 32-bit word from ReadFile
bytes_written dd 0 ; Return 32-bit word from WriteFile
input_handle dd 0 ; The standard input handle
output_handle dd 0 ; The standard output handle
new_lines db 10, 10 ; The new line characters
section .code ; Start of the code segment
; We need to get hold of the standard input and output handle so we can write
; our text to it. This is provided by the GetStdHandle windows API call
_mainCRTStartup: push -10 ; We want the standard input handle
call _GetStdHandle@4 ; Call the Windows API GetStdHandle to retrieve it
mov [input_handle], eax ; Save the handle for later use
push -11 ; We want the standard output handle
call _GetStdHandle@4 ; Call the Windows API GetStdHandle to retrieve it
mov [output_handle], eax ; Save the handle for later use
; Output our request for some user text to the standard output stream
push 0 ; We do not want overlapped I/O
push dword bytes_written ; The address of the number of bytes written
push 24 ; The length of the text we are writing
push prompt ; The address of the text we are writing
push dword [output_handle] ; The handle returned from GetStdHandle call
call _WriteFile@20 ; Write the text to the standard output handle
; Read in the text from the user into our input_string area
push 0 ; We do not want overlapped I/O
push bytes_read ; The address of the number of bytes read
push 256 ; The length of the text we are reading
push input_string ; The address of the buffer for reading
push dword [input_handle] ; The handle returned from GetStdHandle call
call _ReadFile@20 ; Read the text to the standard input handle
mov edi, [bytes_read] ; Load the number of bytes read into input_string
; The start of our loop, and the position we return to if we want to
; print out the next character
start_loop:
push 0 ; We do not want overlapped I/O
push dword bytes_written ; The address of the number of bytes written
push 1 ; The length of the text we are writing
lea eax, [esi+edi] ; Load the address of the byte to output
push eax ; Stack the address of the char we are writing
push dword [output_handle] ; The handle returned from GetStdHandle call
call _WriteFile@20 ; Write the text to the standard output handle
dec edi ; Decrement the index field
mov al, [esi+edi] ; Load this previous byte
or al, al ; Set the condition code flags
jne start_loop ; Jump back to the start of the loop if not end
push 0 ; We do not want overlapped I/O
push bytes_written ; The address of the number of bytes written
push 2 ; The length of the text we are writing
push new_lines ; The address of the text we are writing
push dword [output_handle] ; The handle returned from GetStdHandle call
call _WriteFile@20 ; Write the text to the standard output handle
; The text has been written, so all we need to do now is exit the
; process and return control back to the console
push 0 ; Stack the exit code
call _ExitProcess@4 ; Exit the process
We should be familiar now with the code upto the start_loop, as it is no different from the code of the previous tutorial except I have thrown in an extra call to WriteFile to output the user prompt message and a ReadFile, to read some user input into the input_string buffer.
The three lines from the start_loop label are as they have been as well. Now we have the first of our new instructions - the lea instruction which means load the effective address. In this code, we are saying load the effective address of the esi register plus the edi register and place into the eax register. Then push that address onto the stack. What we have done, is to compute the offset address of the current character in the array of characters input by the user. Here in this code, the esi register is being used as a base register as it wil remain constant and the edi register is being used as an offset value from that base address.
NOTE: The esi register and the edi register are the only registers that are 'guaranteed' to contain the same values after a Windows API call that they had before the call.
lea eax, [esi+edi] ; Load the address of the byte to output
push eax ; Stack the address of the char we are writing
The line of code immediately after the WriteFile call is
dec edi ; Decrement the index field
which decrements the offset value being used to calculate the address of the next character to be output.
The next three lines we have to discuss contain the last two new instructions, namely or and jne. What the code is doing, is loading the byte that is to be output on the next loop into the al register. The or al, al instruction is basically setting the condition code flags based upon the contents of that byte. What we are looking for here is a NULL (binary zero) byte which we placed just before the start of the input_string buffer. The jne instruction is testing to see if the condition code flags have been set to indicate a status of not zero - in other words the al register is not zero. If the result is not zero, then the next instruction to be executed is back at the start_loop label. If we have readed the end of the input, then the next instruction to be executed is the instruction following the jne.
mov al, [esi+edi] ; Load this previous byte
or al, al ; Set the condition code flags
jne start_loop ; Jump back to the start of the loop if not end
The final lines of code simply output two newline characters to the console and the program exits.
Results
If you compile and run the code, these are the kind of results you should get
Please enter some text This is some text to work with htiw krow ot txet emos si sihT
The next tutorial may be found here
This post has been edited by Martyn.Rae: 06 April 2010 - 09:56 PM




MultiQuote


|