My program is supposed to read a bunch of signed integers and display the largest and smallest entered. When zero is entered after the input, it will print but if the first value entered is 0, the program must end right away without displaying anything. Been stuck on figuring out how to store all of the numbers the user enters as it is just 0 when I test print, so I assume the values are getting destroyed. Haven't figured out the loop to compare the numbers yet because I've been banging my brain on that part for a good while and figuring out a botched way to get the sentinel working, but I'll get to it. Can someone hint to me what I'm doing wrong?
Output:
Enter a series of numbers:
100
250
500
0
Largest: 0
Smallest: 0
Source Code:
extrn DisplayString: PROTO
extrn DisplayNewline: PROTO
extrn ReadInt: PROTO
extrn DisplayInt: PROTO
extrn ExitProcess: PROTO
.data
msg1 byte 'Enter a series of numbers: ', 0
largestMsg byte 'Largest: ',0
smallestMsg byte 'Smallest: ',0
.data?
input byte ?
smallest word ?
largest word ?
numsEntered byte ?
.code
main PROC
;Prepare for stacks
sub rsp, 32 ;reserve shadow space
push rbp ;save non-volatile base pointer
mov rbp, rsp ;make frame pointer for direct access
mov rcx, 100 ;get address of request
lea rbx, numsEntered ;get address of array
;Print prompt
lea rcx, msg1 ;store in address the prompt
call DisplayString ;print message
call DisplayNewline ;print new line
;Get First input and compare to sentinel
lea rcx, input ;point input to address
call readInt ;read user input
push rcx
mov r12b, input ;store input in register
cmp r12b, 0 ;compare input to sentinal
je Continue ;if input = to sentinal exit code
jg asknum ;if input greater than sentinal jp to loop
asknum: ;Read and compare user input until they enter 0
lea rcx, input ;point input to address
call readInt ;read user input
mov r12b, input ;store input in register
cmp input, 0 ;compare input to sentinal
je PrintLargest ;if user types sentinal after inputs, jp to print largest num
loop asknum ;loop for user input
PrintLargest:
lea rcx, largestMsg ;point to prompt address
call DisplayString ;print prompt largestMsg
lea rcx, largest ;point largest integer to address
call DisplayInt ;print largest integer
call DisplayNewline ;print new line
jp PrintSmallest ;jump to calculate smallest integer
PrintSmallest:
;after printing largest, print the smallest
lea rcx, smallestMsg ;point to prompt address
call DisplayString ;print prompt smallestMsg
lea rcx, smallest ;point smallest integer to address
call DisplayInt ;print smallest integer
jp Continue ;jump to exit code
Continue:
pop rbp ;clear stack
add rsp, 32 ;restore shadow space
call ExitProcess ;exit code
main ENDP
END
Output:
Related
How can I print a negative number in tasm? Please anyone help me.
For e.g If I do a subtraction 2-7=(-5). How can I print -5?
You will need to perform the number conversion yourself (e.g. such as with your own function). I threw this TASM5 compatible Win32 app together as an example although I'm sure you can find countless other number->string conversion samples online.
This might seem like a lot of code for such a simple goal however if you place "common" functions that are designed to be generic enough to be used in any number of programs in an include file or library, the real program is only the appMain() function at the bottom which is only about 10 lines of code. I threw everything in one file for purposes of being a complete working example.
You are interested in the numToString() function shown below which converts a DWORD to a number within a buffer passed to the function. The rest of the code is just a framework to call the function along with some common WriteConsole wrappers. numToString() supports converting numbers to any base (such as hex and binary). In this example, I pass arguments to specify base-10 with the negative flag enabled (indicating to treat the DWORD number as signed).
The algorithm for numToString() is as follows:
-If number is to be interpreted as signed and IS negative, convert it to positive and set bHasSign flag
-Repeatedly divide positive (in loop) number by base (radix) and convert remainder to ASCII char; this gives us each digit of the resulting string starting with the least significant digit
-Append negative "-" to end based on bHasSign flag
-NULL terminate destination buffer
-Because the algorithm actually computes digits from least significant to most significant, we need to reverse the resultant string (including negative sign) before returning. In English, we read digits starting with most significant first.
The reverse algorithm swaps the outer bytes, then the next inner bytes, and so on until it reaches the middle of the string. This swapping allows us to avoid setting up a temporary buffer for the character reversal.
Program Output:
The number is: -352
Program Listing:
.386
.model flat
GetStdHandle PROTO STDCALL :DWORD
WriteConsoleA PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
ExitProcess PROTO STDCALL :DWORD
getStringLength PROTO STDCALL :DWORD
outputString PROTO STDCALL :DWORD, :DWORD
numToString PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
NULL equ 0
STD_OUTPUT_HANDLE equ -11
MAXLEN_BINARYNUM_BUFFER equ 33 ;max string length for number->string buffer assuming smallest base (32 bits) + NULL accounts for largest possible binary number
MAXLEN_BINARYNUM_BUFFER_AS_DWORD equ 9 ;36-byte hack for MAXLEN_BINARYNUM_BUFFER used below so TASM doesn't mess up Win32 stack alignment
CODE_ERROR_BUFLEN equ -10
.data
szMsg db 'The number is: ',0
.code
;
; getStringLength() - returns zero terminated string length in eax
;
getStringLength PROC STDCALL pszString:DWORD
;count the characters in the string and store result in ecx
lea esi,pszString ;make esi point to first character in string
mov esi,[esi]
mov edi,esi ;edi pointer to first character also
cld
##countloop:
lodsb
cmp al,0
jne SHORT ##countloop
dec esi ;we're now pointing past NULL, so back up one
sub esi,edi ;subtract beginning pointer from pointer at NULL so that esi now contains string count
;return string length in returned in eax
mov eax,esi
ret
getStringLength ENDP
;
; outputString(pszString) - outputs a zero terminated string
; returns 0 on failure or final character count upon success (excluding NULL terminator)
;
outputString PROC STDCALL
LOCALS
ARG hConsole:DWORD
ARG pszString:DWORD
LOCAL dwCharsWrote:DWORD
;get string length (in eax)
lea esi,pszString
mov esi,[esi]
call getStringLength ,esi
;load string pointer into esi
lea esi,pszString
mov esi,[esi]
;load dwCharsWrote pointer into edx
lea edx,dwCharsWrote
;write string to console
call WriteConsoleA ,hConsole,edi,eax,edx,NULL
;if the function fails, return 0, otherwise return the characters written
cmp eax,0
jz ##funcdone
mov eax,[dwCharsWrote]
##funcdone:
ret
outputString ENDP ;outputString()
;
; numToString() - converts unsigned DWORD to digit string
;
numToString PROC STDCALL
ARG uNum:DWORD
ARG uBase:DWORD
ARG pDest:DWORD
ARG uDestLen:DWORD
ARG bSigned:DWORD
LOCAL pEnd:DWORD
LOCAL bHasSign:DWORD
;default to number not signed
mov [bHasSign],0
;if we're interpreting number as signed, see if 32nd bit (sign flag) is set
cmp bSigned,0
jz SHORT ##setup_div_loop
test DWORD PTR [uNum],080000000h
jz SHORT ##setup_div_loop
mov [bHasSign],1 ;number is signed: set sign flag, then remove the sign
not [uNum] ; from the bits VIA: bitwise NOT, then adding 1
inc [uNum] ; this will give us an unsigned representation of the same number
; for which we will add a negative sign character at end
;setup divide/remainder loop
##setup_div_loop:
mov edi,[pDest] ;edi points to beginning of dest buffer
mov ebx,edi ;pEnd variable will point 1 past end of buffer
add ebx,[uDestLen]
mov [pEnd],ebx
mov ebx,[uBase] ;ebx will hold the base to convert to
mov eax,uNum ;eax is our number to convert
##divloop:
;range check buffer
cmp edi,[pEnd] ;if we are we outside of buffer?
jae SHORT ##errorbufoverflow ; we've overflowed
;setup 32-bit divide - divide eax by radix (ebx)
xor edx,edx
div ebx
;assume remainder can always fit in byte (should range check radix)
; and convert value to ASCII (values past 9 get hex digits
cmp dl,9
ja SHORT ##convletters
add dl,48 ;convert 0-9 to ASCII digits
jmp SHORT ##chardone
##convletters:
add dl,55 ;convert A-F (letter A starts at 65, and we want to back it up by 10 to 55 such that 65 is for the value 10)
##chardone:
mov BYTE PTR [edi],dl ;remainder goes in buffer
inc edi ;point at next character
cmp eax,0 ;if quotient nonzero, keep dividing
jnz SHORT ##divloop
;
; loop above complete
;
;do we need to add sign?
cmp [bHasSign],0
jz SHORT ##nullTerminate
; yes, range check that we have room
cmp edi,[pEnd]
jae SHORT ##errorbufoverflow
; we have room, add sign character to string
mov BYTE PTR [edi],'-'
inc edi
##nullTerminate:
;range check buffer that we can NULL terminate the string
cmp edi,[pEnd]
jae SHORT ##errorbufoverflow
mov BYTE PTR [edi],0 ;SUCCESS - NULL terminate
;return character count in eax (not counting null)
mov eax,edi ;subtract start of buffer
sub eax,[pDest] ; from ending position of buffer to obtain count
;
; we now have correct numeric string, but with least significant digits first
; we must reverse the string to make it human-readible
;
mov esi,[pDest] ;esi is left pointer
dec edi ;edi is right pointer
##reverseloop:
cmp esi,edi
jae SHORT ##procdone ;if esi ever meets or goes past edi, we're done!
mov dl,BYTE PTR [esi] ;swap esi and edi bytes
xchg dl,BYTE PTR [edi]
mov BYTE PTR [esi],dl
dec edi
inc esi
jmp SHORT ##reverseloop
##errorbufoverflow:
;ERROR: buffer length too small
mov eax,CODE_ERROR_BUFLEN
##procdone:
ret
numToString ENDP
;
; appMain()
;
appMain PROC STDCALL
LOCAL hConsole:DWORD
LOCAL szCode[MAXLEN_BINARYNUM_BUFFER_AS_DWORD]:DWORD
;get handle to console
call GetStdHandle ,STD_OUTPUT_HANDLE
mov hConsole,eax
;output message
call outputString ,hConsole,OFFSET szMsg
;this is your negative value
mov eax,-352
;convert value to string
lea esi,[szCode]
call numToString ,eax,10,esi,MAXLEN_BINARYNUM_BUFFER,1
;output number buffer
lea esi,[szCode]
call outputString ,hConsole,esi
ret
appMain ENDP
;
; Program Entry Point
;
_start:
call appMain
call ExitProcess ,eax
end _start
I am writing a NASM Assembly program on Windows to get the user to enter in two single digit numbers, add these together and then output the result. I am trying to use the Windows API for input and output.
Unfortunately, whilst I can get it to read in one number as soon as the program loops round to get the second the program ends rather than asking for the second value.
The output of the program shown below:
What is interesting is that if I input 1 then the value displayed is one larger so it is adding to something!
This holds for other single digits (2-9) entered as well.
I am pretty sure it is related to how I am using the ReadConsoleA function but I have hit a bit of a wall attempting to find a solution. I have installed gdb to debug the program and assembled it as follows:
nasm -f win64 -g -o task9.obj task9.asm
GoLink /console /entry _main task9.obj kernel32.dll
gdb task9
But I just get the following error:
"C:\Users\Administrator\Desktop/task9.exe": not in executable format: File format not recognized
I have since read that NASM doesn't output the debug information needed for the Win64 format but I am not 100% sure about that. I am fairly sure I have the 64-bit version of GDB installed:
My program is as follows:
extern ExitProcess ;windows API function to exit process
extern WriteConsoleA ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA ;windows API function to read from the console window (ANSI version)
extern GetStdHandle ;windows API to get the for the console handle for input/output
section .data ;the .data section is where variables and constants are defined
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
digits db '0123456789' ;list of digits
input_message db 'Please enter your next number: '
length equ $-input_message
section .bss ;the .bss section is where space is reserved for additional variables
input_buffer: resb 2 ;reserve 64 bits for user input
char_written: resb 4
chars: resb 1 ;reversed for use with write operation
section .text ;the .text section is where the program code goes
global _main ;tells the machine which label to start program execution from
_num_to_str:
cmp rax, 0 ;compare value in rax to 0
jne .convert ;if not equal then jump to label
jmp .output
.convert:
;get next digit value
inc r15 ;increment the counter for next digit
mov rcx, 10
xor rdx, rdx ;clear previous remainder result
div rcx ;divide value in rax by value in rcx
;quotient (result) stored in rax
;remainder stored in rdx
push rdx ;store remainder on the stack
jmp _num_to_str
.output:
pop rdx ;get the last digit from the stack
;convert digit value to ascii character
mov r10, digits ;load the address of the digits into rsi
add r10, rdx ;get the character of the digits string to display
mov rdx, r10 ;digit to print
mov r8, 1 ;one byte to be output
call _print
;decide whether to loop
dec r15 ;reduce remaining digits (having printed one)
cmp r15, 0 ;are there digits left to print?
jne .output ;if not equal then jump to label output
ret
_print:
;get the output handle
mov rcx, STD_OUTPUT_HANDLE ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
mov rcx, rax
mov r9, char_written
call WriteConsoleA
ret
_read:
;get the input handle
mov rcx, STD_INPUT_HANDLE ;specifies that the input handle is required
call GetStdHandle
;get value from keyboard
mov rcx, rax ;place the handle for operation
mov rdx, input_buffer ;set name to receive input from keyboard
mov r8, 2 ;max number of characters to read
mov r9, chars ;stores the number of characters actually read
call ReadConsoleA
movzx r12, byte[input_buffer]
ret
_get_value:
mov rdx, input_message ;move the input message into rdx for function call
mov r8, length ;load the length of the message for function call
call _print
xor r8, r8
xor r9, r9
call _read
.end:
ret
_main:
mov r13, 0 ;counter for values input
mov r14, 0 ;total for calculation
.loop:
xor r12, r12
call _get_value ;get value from user
sub r12, '0' ;convert char to integer
add r14, r12 ;add value to total
;decide whether to loop for another character or not
inc r13
cmp r13, 2
jne .loop
;convert total to ASCII value
mov rax, r14 ;num_to_str expects total in rax
mov r15, 0 ;num_to_str uses r15 as a counter - must be initialised
call _num_to_str
;exit the program
mov rcx, 0 ;exit code
call ExitProcess
I would really appreciate any assistance you can offer either with resolving the issue or how to resolve the issue with gdb.
I found the following issues with your code:
Microsoft x86-64 convention mandates rsp be 16 byte aligned.
You must reserve space for the arguments on the stack, even if you pass them in registers.
Your chars variable needs 4 bytes not 1.
ReadConsole expects 5 arguments.
You should read 3 bytes because ReadConsole returns CR LF. Or you could just ignore leading whitespace.
Your _num_to_str is broken if the input is 0.
Based on Jester's suggestions this is the final program:
extern ExitProcess ;windows API function to exit process
extern WriteConsoleA ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA ;windows API function to read from the console window (ANSI version)
extern GetStdHandle ;windows API to get the for the console handle for input/output
section .data ;the .data section is where variables and constants are defined
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
digits db '0123456789' ;list of digits
input_message db 'Please enter your next number: '
length equ $-input_message
NULL equ 0
section .bss ;the .bss section is where space is reserved for additional variables
input_buffer: resb 3 ;reserve 64 bits for user input
char_written: resb 4
chars: resb 4 ;reversed for use with write operation
section .text ;the .text section is where the program code goes
global _main ;tells the machine which label to start program execution from
_num_to_str:
sub rsp, 32
cmp rax, 0
jne .next_digit
push rax
inc r15
jmp .output
.next_digit:
cmp rax, 0 ;compare value in rax to 0
jne .convert ;if not equal then jump to label
jmp .output
.convert:
;get next digit value
inc r15 ;increment the counter for next digit
mov rcx, 10
xor rdx, rdx ;clear previous remainder result
div rcx ;divide value in rax by value in rcx
;quotient (result) stored in rax
;remainder stored in rdx
sub rsp, 8 ;add space on stack for value
push rdx ;store remainder on the stack
jmp .next_digit
.output:
pop rdx ;get the last digit from the stack
add rsp, 8 ;remove space from stack for popped value
;convert digit value to ascii character
mov r10, digits ;load the address of the digits into rsi
add r10, rdx ;get the character of the digits string to display
mov rdx, r10 ;digit to print
mov r8, 1 ;one byte to be output
call _print
;decide whether to loop
dec r15 ;reduce remaining digits (having printed one)
cmp r15, 0 ;are there digits left to print?
jne .output ;if not equal then jump to label output
add rsp, 32
ret
_print:
sub rsp, 40
;get the output handle
mov rcx, STD_OUTPUT_HANDLE ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
mov rcx, rax
mov r9, char_written
mov rax, qword 0 ;fifth argument
mov qword [rsp+0x20], rax
call WriteConsoleA
add rsp, 40
ret
_read:
sub rsp, 40
;get the input handle
mov rcx, STD_INPUT_HANDLE ;specifies that the input handle is required
call GetStdHandle
;get value from keyboard
mov rcx, rax ;place the handle for operation
xor rdx, rdx
mov rdx, input_buffer ;set name to receive input from keyboard
mov r8, 3 ;max number of characters to read
mov r9, chars ;stores the number of characters actually read
mov rax, qword 0 ;fifth argument
mov qword [rsp+0x20], rax
call ReadConsoleA
movzx r12, byte[input_buffer]
add rsp, 40
ret
_get_value:
sub rsp, 40
mov rdx, input_message ;move the input message into rdx for function call
mov r8, length ;load the length of the message for function call
call _print
call _read
.end:
add rsp, 40
ret
_main:
sub rsp, 40
mov r13, 0 ;counter for values input
mov r14, 0 ;total for calculation
.loop:
call _get_value ;get value from user
sub r12, '0' ;convert char to integer
add r14, r12 ;add value to total
;decide whether to loop for another character or not
inc r13
cmp r13, 2
jne .loop
;convert total to ASCII value
mov rax, r14 ;num_to_str expects total in rax
mov r15, 0 ;num_to_str uses r15 as a counter - must be initialised
call _num_to_str
;exit the program
mov rcx, 0 ;exit code
call ExitProcess
add rsp, 40
ret
As it turned out I was actually missing a 5th argument in the WriteConsole function as well.
I am trying to write a simple assembly program to add two numbers together. I want the user to be able to enter the values. The problem I am encountering is that when I display a string message and then read a value in, the next time the string is required the first x characters of the string have been overwritten by the data that was entered by the user.
My assumption is that this is related to the use of LEA to load the string into the register. I have been doing this because Macho64 complains if a regular MOV instruction is used in this situation (something to do with addressing space in 64-bits on the Mac).
My code is as follows:
section .data ;this is where constants go
input_message db 'Please enter your next number: '
length equ $-input_message
section .text ;declaring our .text segment
global _main ;telling where program execution should start
_main: ;this is where code starts getting executed
mov r8, 0
_loop_values:
call _get_value
call _write
inc r8 ;increment the loop counter
cmp r8, 2 ;compare loop counter to zero
jne _loop_values
call _exit
_get_value:
lea rcx, [rel input_message] ;move the input message into rcx for function call
mov rdx, length ;load the length of the message for function call
call _write
call _read
ret
_read:
mov rdx, 255 ;set buffer size for input
mov rdi, 0 ;stdout
mov rax, SYSCALL_READ
syscall
mov rdx, rax ;move the length from rax to rdx
dec rdx ;remove new line character from input length
mov rcx, rsi ;move the value input from rsi to rcx
ret
_write:
mov rsi, rcx ;load the output message
;mov rdx, rax
mov rax, SYSCALL_WRITE
syscall
ret
_exit:
mov rax, SYSCALL_EXIT
mov rdi, 0
syscall
The program loops twice as it should. The first time I get the following prompt:
Please enter your next number:
I would the enter something like 5 (followed by the return key)
The next prompt would be:
5
ease enter your next number:
Any assistance would be much appreciated.
I think all 64-bit code on Mac is required to be rip relative.
Absolute addresses are not supported. in this type of addressing you address your symbol relative to rip.
NASM documentation says:
default abs
mov eax,[foo] ; 32−bit absolute disp, sign−extended
mov eax,[a32 foo] ; 32−bit absolute disp, zero−extended
mov eax,[qword foo] ; 64−bit absolute disp
default rel
mov eax,[foo] ; 32−bit relative disp
mov eax,[a32 foo] ; d:o, address truncated to 32 bits(!)
mov eax,[qword foo] ; error
mov eax,[abs qword foo] ; 64−bit absolute disp
and you can also see this question.
This is my first assembler program in masm32. Using vis studio 2012. And this is just one procedure in a program to convert input into an ascii chart with decimal, hex and ascii output. I've been trying to figure this out for 8 hours, and I know it's going to be something really simple.
It gets through all the computation, but during the pop and return phase it crashes into an unhandled exception when accessing the EIP(i think). Also, all my registers are set to 0 except ebx and I don't know why, but it may have something to do with it.
This is just the procedure to convert from the input string to a decimal value.*
My inputStr is:
inputStr db 16 DUP(0)
.code
main proc
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
lea esi, outputStr1
call PrintString
lea esi, inputStr
call GetString
call StrtoNum
invoke ExitProcess, 0 ;****This is the next line when it crashes***
main endp
StrtoNum proc ;going to hex first
pushad
pushfd
mov bl, 1 ;mov 1 into bl for later multiplying
whilemorechar:
mov al,byte ptr [esi]
cmp al, 0 ;stuff
je ConvertDone ;if null then we are done here
;esi is pointing to the string to be converted
;cmp bl,0
;jnz decrement
cmp al, 0h
je ConvertDec
sub al, 30h ;get first string byte to dec number 0-9
push ax ;push last digit to stack
inc esi ;gets to next string byte
inc cl ;make note of decimal position in string
jmp whilemorechar ;jump for next place in string
ConvertDec: ;reverse is done, now turn into decimal number
cmp cl, 0 ;compare counter to 0
jz ConvertDone ;if counter is 0, start comparing numbers
pop ax ;pop last on stack
mul bl ;multiply place value by input byte
add dx, ax ;add decimal value into dl
mov al, 10d ;move 10 into al
mul bx ;multiply 10 and bl value to get next
mov bx, ax ;mov decimal place into bl for next loop
dec cl ;decrement counter
jmp ConvertDec ;loop through again for next decimal place
ConvertDone:
mov ebx, 0
popfd ;pop flags
popad ;pop registers
ret ;return to caller
StrtoNum endp
I'm new to writing assembly code and I'm having trouble printing out the values of my array using a loop. The code I have prints out the value of the counter and the not the values in the array, can someone please explain what I'm doing wrong, also how do I point to the top of the array? I have tried using different registers, but nothing seems to be working. My professor asks that I do it this way (if it seems inefficient):
.386
.model flat
ExitProcess PROTO NEAR32 stdcall, dwExitCode:dword
Include io.h
cr equ 0DH
Lf equ 0AH
.stack 4096
.data
newline byte CR, LF, 0
whitespace byte 32,32,0
arr dword 10 dup(?)
n dword 2
string byte 40 dup(?)
prompt byte "Please enter a value: ", 0
origArr byte "Original Array", 0
.code
_start:
mov ecx,n ; number of values in the array
lea ebx,arr ; address of the array
sub edi, edi
top: cmp ecx, 0
je done
output prompt
input string, 40
atod string
mov [arr+edi], ecx
add edi, 4
loop top
done: output origArr
mov ecx, n
call myproc
INVOKE ExitProcess, 0
PUBLIC _start
myproc proc near32
.data
val_str byte 11 dup(?), 0
.code
push eax
push edi
push ecx
sub edi,edi ; index register
top2: mov eax, [ebx+edi]
dtoa val_str, eax
output val_str
add edi,4 ; modify esi rather than ebx
loop top2
pop ecx
pop edi
pop eax
ret
myproc endp
END
Any suggestions are appreciated.
mov [arr+edi], ecx
You're storing the loop counter, rather than the return value of atod.