How to find largest number in an array using NASM - arrays

i was doing a program in NASM(x86 assembly), in which user is asked to enter three 32 bit hex numbers(8 digit), which are further stored in an array and the program shows the number which is largest of them all. The program works fine, i.e. it shows the largest of the three numbers. But the problem is, that it shows only 16 bit (4 digit number) as output. For example, if i give three numbers as 11111111h,22222222h and 10000000h, the output comes out to be only 2222. This is the code.
section .data
msg db "Enter the number : ",10d,13d
msglen equ $-msg
show db "The greatest number is : ",10d,13d
showlen equ $-show
%macro display 2
mov eax,4
mov ebx,1
mov ecx,%1
mov edx,%2
int 80h
%endmacro
%macro input 2
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,%2
int 80h
%endmacro
section .bss
large resd 12
num resd 3
section .text
global _start
_start:
mov esi,num
mov edi,3
; Now taking input
nxt_num:
display msg,msglen
input esi,12
add esi,12
dec edi
jnz nxt_num
mov esi,num
mov edi,3
add: mov eax,[esi]
jmp check
next: add esi,12
mov ebx,[esi]
CMP ebx,eax
jg add
check: dec edi
jnz next
mov [large],eax
display show,showlen
display large,12
;exit
mov eax,1
mov ebx,0
int 80h
I even tried changing reserved size of array from doubly byte to quad byte. But the result remains the same.
Also, when i execute the same code in NASM x86_64 assembly, only with the registers and the system calls changed (i.e. eax to rax, ebx to rcx, int 80h to syscall, etc) the output comes out to of 32 bits(8 digits). Why so?
I need help. Thank you. :)

In you little program , you're trying to move the Qword into a 32-bit register which can hold just 4bytes (DWord). Based on your response to Gunner I guess you're misunderstanding this concept.
Actually each byte is represented by 8bits.
a word is 2 bytes (16 bits)
a dword is 4 bytes (32 bits) which is the size of a register in a x86 arch.
So whenever you take a byte , its binary equivalent has always an 8bits size.
So the binary equivalent of "FF" in hex is 00001111.
In your program just try to print your number as a string instead of printing it through a register, you can simply do that by using the pointer to the memory address where you number is stored or simply by printing the input using printf.
P.S : the string should be in ASCII , so to display 11111111 it should be in memory as following 3131313131313131 .

The output 2222 is correct for a 32 bit register. Each number is 8 bits, 4 numbers = 8 * 4 = 32, the max a 32 bit register can hold. This is why if you change to 64 bit registers, the full number is printed. You will need to change the displayed number into a string to display the full number.

Related

Assembly: store a string in register

Let's say we work on architecture x86_64 and let's say we have the following string, "123456". In ASCII characters, it becomes 31 32 33 34 35 36 00.
Now, which assembly instructions should I use to move the entire (even if fragmented) content of this string somewhere in a way that %rdi stores the address of that string (points to that)?
Because I am not simply able to move the hex representation of the string into a register, like one can do with unsigned values, how do I do it?
There are a couple of ways to do so.
If you want to move the entire string to another offset first, you would have to do so with a loop.
mov rbx, 0
loop:
mov al, [string+rbx]
mov [copyoffset+rbx], al
inc rbx
cmp al, 0x0
jne loop
... Insert other code here
Then you can use the Lea instruction described below to move it into rdi.
If you just want to load the address of the string and don't care about moving it you can just use lea
lea rdi, [stringoffset]
Edit: Changed rax to al so we only move one byte at a time

File size in assembly

I have a following code written in TASM assembly for reading from a file and printing out the file content using a buffer.
Buffer declaration:
buffer db 100 dup (?), '$' ;regarding to comment, buffer is db 101 dup (?), '$'
EDIT
The structure of my program is:
Task 1 is asking me for a file name (string) which I want to read.
After I input file name, the procedure task1 opens the file.
mov ah, 3dh
xor al, al
lea dx, fname
int 21h ;open file
jc openError
mov bx, ax
Not sure, if opening the file is correct, because I have seen similar ways of opening the file but I do not have a handler here, or?
Here is the reading part task2:
task2 proc
pam 10,13 ;pam is macro for printing out
read:
mov ah, 3fh
lea dx, buffer
mov cx, 100
int 21h
jc readError ;read error , jump
mov si, ax
mov buffer[si], '$'
mov ah, 09h
int 21h ;print out
cmp si, 100
je read
jmp stop ;end
openError:
pam error1
jmp stop
readError:
pam error2
stop: ret
task2 endp
My question is, how can I get file length using this code? I have read that there are some ways of getting file size but they all look very complicated and I was thinking that when I read file, I should be able to calculate file size by storing number of characters I read in a register but I am not so sure about it and if it is possible, then I have no idea how to do that in tasm. Also in data segment, what variable do I need for storing file size? Maybe a code snippet would help me understand the process with some helpful comments how does it work. Thanks for help.
UPDATE regarding to the answer:
So I tried to convert hexa to decimal, it kinda works but I must have some bug in there because it works for small file, lets say I tried 1kB file and it worked, I got size in Bytes printed out on screen but when I tried bigger file like 128kB, decimal numbers were not correct - printed size was wrong, file is big exactly 130,862 bytes and my conversion gave me -- MENU653261 = Enter file name.
... code from the answer ...
lea di,[buffer] ; hexa number will be written to buffer
mov word ptr [di],('0' + 'x'*256) ; with C-like "0x" prefix
add di,2 ; "0x" written at start of buffer
mov ax,dx
call AxTo04Hex ; upper word converted to hexa string
mov ax,cx
call AxTo04Hex ; lower word converted to hexa string
mov byte ptr [di],'$' ; string terminator
;HEX TO DECIMAL = my code starts here
mov cx,0
mov bx,10
loop1: mov dx,0
div bx
add dl,30h
push dx
inc cx
cmp ax,9
jg loop1
add al,30h
mov [si],al
loop2: pop ax
inc si
mov [si],al
loop loop2
; output final string to screen
mov ah,9
lea dx,[buffer]
int 21h
Here is a screen how it looks when the decimal value gets printed out. It is mixed with the next line. I tried to move it to the next line but did not help.
screenshot
A simple code to display hexa-formatted length of DOS file (file name is hardcoded in source, edit it to existing file):
.model small
.stack 100h
.data
fname DB "somefile.ext", 0
buffer DB 100 dup (?), '$'
.code
start:
; set up "ds" to point to data segment
mov ax,#data
mov ds,ax
; open file first, to get "file handle"
mov ax,3D00h ; ah = 3Dh (open file), al = 0 (read only mode)
lea dx,[fname] ; ds:dx = pointer to zero terminated file name string
int 21h ; call DOS service
jc fileError
; ax = file handle (16b number)
; now set the DOS internal "file pointer" to the end of opened file
mov bx,ax ; store "file handle" into bx
mov ax,4202h ; ah = 42h, al = 2 (END + cx:dx offset)
xor cx,cx ; cx = 0
xor dx,dx ; dx = 0 (cx:dx = +0 offset)
int 21h ; will set the file pointer to end of file, returns dx:ax
jc fileError ; something went wrong, just exit
; here dx:ax contains length of file (32b number)
; close the file, as we will not need it any more
mov cx,ax ; store lower word of length into cx for the moment
mov ah,3Eh ; ah = 3E (close file), bx is still file handle
int 21h ; close the file
; ignoring any error during closing, so not testing CF here
; BTW, int 21h modifies only the registers specified in documentation
; that's why keeping length in dx:cx registers is enough, avoiding memory/stack
; display dx:cx file length in hexa formatting to screen
; (note: yes, I used dx:cx for storage, not cx:dx as offset for 42h service)
; (note2: hexa formatting, because it's much easier to implement than decimal)
lea di,[buffer] ; hexa number will be written to buffer
mov word ptr [di],('0' + 'x'*256) ; with C-like "0x" prefix
add di,2 ; "0x" written at start of buffer
mov ax,dx
call AxTo04Hex ; upper word converted to hexa string
mov ax,cx
call AxTo04Hex ; lower word converted to hexa string
mov byte ptr [di],'$' ; string terminator
; output final string to screen
mov ah,9
lea dx,[buffer]
int 21h
; exit to DOS with exit code 0 (OK)
mov ax,4C00h
int 21h
fileError:
mov ax,4C01h ; exit with code 1 (error happened)
int 21h
AxTo04Hex: ; subroutine to convert ax into four ASCII hexadecimal digits
; input: ax = 16b value to convert, ds:di = buffer to write characters into
; modifies: di += 4 (points beyond the converted four chars)
push cx ; save original cx to preserve it's value
mov cx,4
AxTo04Hex_singleDigitLoop:
rol ax,4 ; rotate whole ax content by 4 bits "up" (ABCD -> BCDA)
push ax
and al,0Fh ; keep only lowest nibble (4 bits) value (0-15)
add al,'0' ; convert it to ASCII: '0' to '9' and 6 following chars
cmp al,'9' ; if result is '0' to '9', just store it, otherwise fix
jbe AxTo04Hex_notLetter
add al,'A'-(10+'0') ; fix value 10+'0' into 10+'A'-10 (10-15 => 'A' to 'F')
AxTo04Hex_notLetter:
mov [di],al ; write ASCII hexa digit (0-F) to buffer
inc di
pop ax ; restore other bits of ax back for next loop
dec cx ; repeat for all four nibbles
jnz AxTo04Hex_singleDigitLoop
pop cx ; restore original cx value back
ret ; ax is actually back to it's input value here :)
end start
I tried to comment the code extensively, and to use "more straightforward" implementation of this stuff, avoiding some less common instructions, and keep the logic simple, so actually you should be able to comprehend how it works fully.
Again I strongly advise you to use debugger and go instruction by instruction slowly over it, watching how CPU state is changing, and how it correlates with my comments (note I'm trying to comment not what the instruction exactly does, as that can be found in instruction reference guide, but I'm trying to comment my human intention, why I wrote it there - in case of some mistake this gives you idea what should have been the correct output of the wrong code, and how to fix it. If comments just say what the instruction does, then you can't tell how it should be fixed).
Now if you would implement 32b_number_to_decimal_ascii formatting function, you can replace the last part of this example to get length in decimal, but that's too tricky for me to write from head, without proper debugging and testing.
Probably the simplest way which is reasonably to implement by somebody new to asm is to have table with 32b divisors for each 32b decimal digit and then do nested loop for each digits (probably skipping storage of leading zeroes, or just incrementing the pointer before printing to skip over them, that's even less complex logic of code).
Something like (pseudo code similar to C, hopefully showing the idea):
divisors dd 1000000000, 100000000, 10000000, ... 10, 1
for (i = 0; i < divisors.length; ++i) {
buffer[i] = '0';
while (divisors[i] <= number) {
number -= divisors[i];
++digit[i];
}
}
digit[i] = '$';
// then printing as
ptr_to_print = buffer;
// eat leading zeroes
while ( '0' == ptr_to_print[0] ) ++ptr_to_print;
// but keep at least one zero, if the number itself was zero
if ('$' == ptr_to_print[0] ) --ptr_to_print;
print_it // dx = ptr_to_print, ah = 9, int 21h
And if you wonder, how do you subtract 32 bit numbers in 16 bit assembly, that's actually not that difficult (as 32b division):
; dx:ax = 32b number
; ds:si = pointer to memory to other 32b number (mov si,offset divisors)
sub ax,[si] ; subtract lower word, CF works as "borrow" flag
sbb dx,[si+2] ; subtract high word, using the "borrow" of SUB
; optionally: jc overflow
; you can do that "while (divisors[i] <= number)" above
; by subtracting first, and when overflow -> exit while plus
; add the divisor back (add + adc) (to restore "number")
Points to question update:
You don't convert hex to decimal (hex string is stored in buffer, you don't load anything from there). You convert value in ax to decimal. The ax contains low word of file length from previous hex conversion call. So for files of length up to 65535 (0xFFFF = maximum 16b unsigned integer) it may work. For longer files it will not, as upper word is in dx, which you just destroy by mov dx,0.
If you would actually keep dx as is, you would divide file length by 10, but for file with 655360+ length it would crash on divide error (overflow of quotient). As I wrote in my answer above, doing 32b / 16b division on 8086 is not trivial, and I'm not even sure what is the efficient way. I gave you hint about using table of 32b divisors, and doing the division by subtraction, but you went for DIV instead. That would need some sophisticated split of the original 32b value into smaller parts up to a point where you can use div bx=10 to extract particular digits. Like doing filelength/1e5 first, then calculate 32b remainder (0..99999) value, which can be actually divided by 10 even in 16b (99999/10 = 9999 (fits 16b), remainder 9).
Looks like you didn't understand why 128k file length needs 32 bits to store, and what are the effective ranges of various types of variables. 216 = 65536 (= 64ki) ... that how big your integers can get, before you run into problems. 128ki is two times over that => 16 bit is problem.
Funny thing... as you wrote "converting from hex to decimal", at first I though: what, you convert that hexa string into decimal string??? But actually that sounds doable with 16b math, to go through whole hexa number first picking up only 100 values (extracted from particular k*16n value), then in next iteration doing 101 counting, etc...
But that division by subtracting 32bit numbers from my previous answer should be much easier to do, and especially to comprehend, how it works.
You write the decimal string at address si, but I don't see how you set si, so it's probably pointing into your MENU string by accident, and you overwrite that memory (again using debugger, checking ds:si values to see what address is used, and using memory view to watch the memory content written would give you hint, what is the problem).
Basically you wasted many hours by not following my advices (learning debugging and understanding what I meant by 32b - 32b loop doing division), trying to copy some finished code from Internet. At least it looks like you can somewhat better connect it to your own code, but you are still missing obvious problems, like not setting si to point to destination for decimal string.
Maybe try to first to print all numbers from the file, and keep the size in hexa (at least try to figure out, why conversion to hexa is easy, and to decimal not). So you will have most of the task done, then you can play with the hardest part (32b to decimal in 16b asm).
BTW, just a day ago or so somebody had problem with doing addition/subtraction over 64b numbers in 16b assembly, so this answer may give you further hints, why doing those conversion by sub/add loops is not that bad idea, it's quite "simple" code if you get the idea how it works: https://stackoverflow.com/a/42645266/4271923

Two different values at the same memory address - assembly

I have some code in assembly which behaves a little bit strange. I have a C extern function that calls with asm another function from an .asm file. This C function puts on the stack three addresses used by my function from .asm file. All went well untill this appeared:
; Let's say we take from the stack first parameter from my C function.
; This parameter is a string of bytes that respect this format:
; - first 4 bytes are the sign representation of a big number
; - second 4 bytes are the length representation of a big number
; - following bytes are the actual big number
section .data
operand1 dd 0
section .text
global main
main:
push ebp
mov ebp, esp
mov eax, [ebp + 8] ; Here eax will contain the address where my big number begins.
lea eax, [eax + 8] ; Here eax will contain the address where
; my actual big number begins.
mov [operand1], eax
PRINT_STRING "[eax] is: "
PRINT_HEX 1, [eax] ; a SASM macro which prints a byte as HEX
NEWLINE
PRINT_STRING "[operand1] is: "
PRINT_HEX 1, [operand1]
NEWLINE
leave
ret
When running this code, I get at the terminal the correct output for [eax], and for [operand1] it keeps printing a number which will not change if I modify that first parameter of my C function. What am I doing wrong here?
I made an understandable mistake. When doing:
mov [operand1], eax
PRINT_STRING "[operand1] is: "
PRINT_HEX 1, [operand1]
NEWLINE
This code prints the first byte of the content (which is the address where my actual big number begins) contained at the address where this local variable (operand1) resides. In order to get the actual value which resides at [operand1] I had to do this:
mov ebx, [operand1]
PRINT_STRING "[operand1] is: "
PRINT_HEX 1, [ebx]
NEWLINE

Printing an array using MASM with Irvine32 library

My program accepts 4 integers and is suppose to display them back to the user. When printing the values I don't get the expected results. I'm using MASM with Kip's Irvine32 library
My code is:
include irvine32.inc
.data
msg byte "Enter a number",0
arr dword 4 dup(?)
len=($-arr)/4
.code
main PROC
mov edx,offset msg
call writestring
call crlf
mov eax,0
mov ecx,0
.while(ecx<len)
call readdec
mov arr[ecx],eax
inc ecx
.endw
mov ebx,0
mov ecx,0
.while(ecx<len)
mov ebx,arr[ecx]
call writedec
call crlf
inc ecx
.endw
exit
main ENDP
END main
A sample run of my program:
Enter a number
1
2
3
4
4
4
4
4
After entering the numbers 1,2,3, and 4 the program should have displayed those numbers back to the user. The output I expected is:
Enter a number
1
2
3
4
1
2
3
4
If I modify the loop that prints the numbers so that I place the value to print in EAX instead of EBX with this code:
mov eax,arr[ecx]
call writedec
I end up with nonsensical output values like this:
Enter a number
1
2
3
4
67305985
262914
1027
4
Why is my program behaving this way, and how can I modify it to get the expected results?
There is only one real issue in the code that appears in different places. When you do something like:
.while(ecx<len)
call readdec
mov arr[ecx],eax
inc ecx
.endw
You must realize that arr[ecx] is the same as arr+ecx. ECX is the offset in bytes added to arr . The issue is that each element of arr is 32-bit (4 bytes) since you declared the array as:
arr dword 4 dup(?)
What you need to do is multiply ECX by the length of each element (in this case 4 bytes). You can do that by multiplying ECX by the size of the elements in the array arr[ecx*4] = arr+(ecx*4) . This form of scaled addressing only supports multiplying by the value of 1,2,4, and 8 in 32-bit code. Your code should have looked like this:
.while(ecx<len)
call readdec
mov arr[ecx*4],eax
inc ecx
.endw
There is a similar issue with the code that calls writedec. As well writedec takes the number to print in EAX, not EBX. This code:
.while(ecx<len)
mov ebx,arr[ecx]
call writedec
call crlf
inc ecx
.endw
Should be something like:
.while(ecx<len)
mov eax,arr[ecx*4]
call writedec
call crlf
inc ecx
.endw
One other trick to getting the length of an array in MASM is to use the lengthof pseudo-opcode. Where you wrote:
len=($-arr)/4
You could have used:
len=lengthof arr
lengthof will return the number of elements in arr . MASM takes into account the size of each element size (in this case 4 since you declared arr with DWORD elements). There was nothing wrong with the way you did it, I am offering up another mechanism.
I highly recommend learning to use a debugger to step through your code. A debugger can be an invaluable tool that can allow you to see how things are progressing in the code and see what is in the registers and memory at any given time.

Displaying hexadecimal contents in assembly language

Hey guys I'm not sure if I'm going about all this the right way. I need the first 12 numbers of Fibonacci sequence to calculate which its doing already I'm pretty sure. But now I need to display the hexadecimal contents of (Fibonacci) in my program using dumpMem. I need to be getting a print out of : 01 01 02 03 05 08 0D 15 22 37 59 90
But I'm only getting: 01 01 00 00 00 00 00 00 00 00 00 00
Any tips or help is much much appreciated.
INCLUDE Irvine32.inc
.data
reg DWORD -1,1,0 ; Initializes a DOUBLEWORD array, giving it the values of -1, 1, and 0
array DWORD 48 DUP(?)
Fibonacci BYTE 1, 1, 10 DUP (?)
.code
main PROC
mov array, 1
mov esi,OFFSET array ; or should this be Fibonacci?
mov ecx,12
add esi, 4
L1:
mov edx, [reg]
mov ebx, [reg+4]
mov [reg+8], edx
add [reg+8], ebx ; Adds the value of the EBX and 'temp(8)' together and stores it as temp(8)
mov eax, [reg+8] ; Moves the value of 'temp(8)' into the EAX register
mov [esi], eax ; Moves the value of EAX into the offset of array
mov [reg], ebx ; Moves the value of the EBX register to 'temp(0)'
mov [reg+4], eax ; Moves the value of the EAX register to 'temp(4)
add esi, 4
; call DumpRegs
call WriteInt
loop L1
;mov ebx, offset array
;mov ecx, 12
;L2:
;mov eax, [esi]
;add esi, 4
;call WriteInt
;loop L2
;Below will show hexadecimal contents of string target-----------------
mov esi, OFFSET Fibonacci ; offset the variables
mov ebx,1 ; byte format
mov ecx, SIZEOF Fibonacci ; counter
call dumpMem
exit
main ENDP
END main
It seems to me that the problem here is with computing the Fibonacci sequence. Your code for that leaves me somewhat...puzzled. You have a bunch of "stuff" there, that seems to have nothing to do with computing Fibonacci numbers (e.g., reg), and others that could, but it seems you don't really know what you're trying to do with them.
Looking at your loop to compute the sequence, the first thing that practically jumps out at me is that you're using memory a lot. One of the first (and most important) things when you're writing assembly language is to maximize your use of registers and minimize your use of memory.
As a hint, I think if you read anything from memory in the course if computing the sequence, you're probably making a mistake. You should be able to do all the computation in registers, so the only memory references will be writing results. Since you're (apparently) producing only byte-sized results, you should need only one array of the proper number of bytes to hold the results (i.e., one byte per number you're going to generate).
I'm tempted to write a little routine showing how neatly this can be adapted to assembly language, but I suppose I probably shouldn't do that...
Your call to dumpMem is correct, but your program is not storing the results of your calculations at the correct location: the region you call "Fibonacci" remains initialized to 1, 1, and ten zeros. You need to make sure that your loop starts writing at the offset of Fibonacci plus 2, and moves ten times in one-byte increments (ten, not twelve, because you provided the two initial items in the initializer).
I'm sorry, I cannot be any more specific, as any question containing the word "Fibonacci" inevitably turns out to be someone's homework :-)

Resources