Display Negative numbers in TASM - tasm

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

Related

New to x64 Assembly, not sure why user input is still 0

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:

How to print signed integer in x86 assembly (NASM) on Mac

I found an implementation of unsigned integer conversion in x86 assembly, and I tried plugging it in but being new to assembly and not having a debugging env there yet, it's difficult to understand why it's not working. I would also like it to work with signed integers so it can capture error messages from syscalls.
Wondering if one could show how to fix this code to get the signed integer to print, without using printf but using strprn provided by this answer.
%define a rdi
%define b rsi
%define c rdx
%define d r10
%define e r8
%define f r9
%define i rax
%define EXIT 0x2000001
%define EXIT_STATUS 0
%define READ 0x2000003 ; read
%define WRITE 0x2000004 ; write
%define OPEN 0x2000005 ; open(path, oflag)
%define CLOSE 0x2000006 ; CLOSE
%define MMAP 0x2000197 ; mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset)
; szstr computes the lenght of a string.
; rdi - string address
; rdx - contains string length (returned)
strsz:
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1 (uses bitwise id: ~x = -x-1)
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative -> absolute value
dec rcx ; -1 to skip the null-term, rcx contains length
mov rdx, rcx ; size returned in rdx, ready to call write
ret
; strprn writes a string to the file descriptor.
; rdi - string address
; rdx - contains string length
strprn:
push rdi ; push string address onto stack
call strsz ; call strsz to get length
pop rsi ; pop string to rsi (source index)
mov rax, WRITE ; put write/stdout number in rax (both 1)
mov rdi, 1 ; set destination index to rax (stdout)
syscall ; call kernel
ret
; mov ebx, 0xCCCCCCCD
itoa:
xor rdi, rdi
call itoal
ret
; itoa loop
itoal:
mov ecx, eax ; save original number
mul ebx ; divide by 10 using agner fog's 'magic number'
shr edx, 3 ;
mov eax, edx ; store quotient for next loop
lea edx, [edx*4 + edx] ; multiply by 10
shl rdi, 8 ; make room for byte
lea edx, [edx*2 - '0'] ; finish *10 and convert to ascii
sub ecx, edx ; subtract from original number to get remainder
lea rdi, [rdi + rcx] ; store next byte
test eax, eax
jnz itoal
exit:
mov a, EXIT_STATUS ; exit status
mov i, EXIT ; exit
syscall
_main:
mov rdi, msg
call strprn
mov ebx, -0xCCCCCCCD
call itoa
call strprn
jmp exit
section .text
msg: db 0xa, " Hello StackOverflow!!!", 0xa, 0xa, 0
With this working it will be possible to properly print signed integers to STDOUT, so you can log the registers values.
https://codereview.stackexchange.com/questions/142842/integer-to-ascii-algorithm-x86-assembly
How to print a string to the terminal in x86-64 assembly (NASM) without syscall?
How do I print an integer in Assembly Level Programming without printf from the c library?
https://baptiste-wicht.com/posts/2011/11/print-strings-integers-intel-assembly.html
How to get length of long strings in x86 assembly to print on assertion
My answer on How do I print an integer in Assembly Level Programming without printf from the c library? which you already linked shows that serializing an integer into memory as ASCII decimal gives you a length, so you have no use for (a custom version of) strlen here.
(Your msg has an assemble-time constant length, so it's silly not to use that.)
To print a signed integer, implement this logic:
if (x < 0) {
print('-'); // or just was_negative = 1
x = -x;
}
unsigned_intprint(x);
Unsigned covers the abs(most_negative_integer) case, e.g. in 8-bit - (-128) overflows to -128 signed. But if you treat the result of that conditional neg as unsigned, it's correct with no overflow for all inputs.
Instead of actually printing a - by itself, just save the fact that the starting number was negative and stick the - in front of the other digits after generating the last one. For bases that aren't powers of 2, the normal algorithm can only generate digits in reverse order of printing,
My x86-64 print integer with syscall answer treats the input as unsigned, so you should simply use that with some sign-handling code around it. It was written for Linux, but replacing the write system call number will make it work on Mac. They have the same calling convention and ABI.
And BTW, xor al,al is strictly worse than xor eax,eax unless you specifically want to preserve the upper 7 bytes of RAX. Only xor-zeroing of full registers is handled efficiently as a zeroing idiom.
Also, repnz scasb is not fast; about 1 compare per clock for large strings.
For strings up to 16 bytes, you can use a single XMM vector with pcmpeqb / pmovmskb / bsf to find the first zero byte, with no loop. (SSE2 is baseline for x86-64).

Converting quaternary to octal. ASM 8086

I have to prepare program for 8086 processor which converts quaternary to octal number.
My idea:
Multiplying every digit by exponents of 4 and add to register. Later check the highest exponent of 8 not higher than sum from first step.
Divide by exponents of 8 until remainder equals 0. Every result of dividing is one digit in octal.
But for 16-digits number last exponent of 4 is 4^15. I suppose It isn't optimal algorithm.
Is there any other way? Maybe to binary and group by 3 digits.
Turns out you can indeed process values 3 digits at a time. Done this way, you can process strings of arbitrary length, without being limited by the size of a register. Not sure why you might need to, unless aliens try to communicate with us using ascii strings of quaternary digits with huge lengths. Might happen.
It's possible to do the translation either way (Right to Left or Left to Right). However, there's a bit of a challenge to either:
If you are processing RtL, you need to know the length of the output string before you start (so that you know where to write the digits as you compute them). This is do-able, but a bit tricky. In simplest terms, the length is ((strlen(Q) + 2) / 3) * 2. That almost gets it. However, you can end up with a blank space at the beginning for a number of cases. "1" as well as "10" will give the blank space. "20" won't. The correct value can be computed, but it's annoying.
Likewise, processing LtR has a similar problem. You don't have the problem of figuring out where to write digits, but consider: If the string to convert it "123", then the conversion is simple (33 octal). But what if you start processing, and the complete string is "1231" (155 octal)? In that case what you need to process it like "001231" (01 55). IOW, digits can be processed in groups of 3, but you need to handle the initial case where the number of digits doesn't evenly divide by 3.
Posting solutions to homework is usually something I avoid. However I doubt you are going to turn this in as your 'solution,' and it's (barely) possible that google might send someone here who needs something similar.
A few things to note:
This code is intended to be called from C using Microsoft's fastcall (it made testing easier) and compiled with masm.
While it is written in 32bit (my environment), there's nothing that particularly requires 32bit in it. Since you said you were targeting 8086, I've tried to avoid any 'advanced' instructions. Converting to 16bit or even 64bit should not present much of a challenge.
It processes from left to right.
As with any well-written routine, it validates its parameters. It outputs a zero length string on error, such as invalid digits in the input string.
It will crash if the output buffer is NULL. I suppose I could return a bool on error (currently returns void), but, well, I didn't.
I'm sure the code could be tighter (couldn't it always?), but for "homework project quality," it seems reasonable.
Other than that, that comments should explain the code.
.386
.model flat
.code
; Call from C via:
; extern "C" void __fastcall PrintOct(const char *pQuat, char *pOct);
; On Entry:
; ecx: pQuat
; edx: pOct
; On Exit:
; eax, ecx, edx clobbered
; all others preserved
; If pOct is zero bytes long, an error occurred (probably invalid digits)
#PrintOct#8 PROC
; -----------------------
; If pOct is NULL, there's nothing we can do
test edx, edx
jz Failed
; -----------------------
; Save the registers we modify (except for
; eax, edx and ecx which we treat as scratch).
push esi
push ebx
push edi
mov esi, ecx
mov edi, edx
xor ebx, ebx
; -----------------------
; esi: pQuat
; edi: pOct
; ebx: zero (because we use lea)
; ecx: temp pointer to pQuat
; Reject NULL pQuat
test esi, esi
jz WriteNull
; -----------------------
; Reject 0 length pQuat
mov bl, BYTE PTR [esi]
test bl, bl
jz WriteNull
; -----------------------
; How many chars in pQuat?
mov dl, bl ; bl is first digit as ascii. Preserve it.
CountLoop:
inc ecx ; One more valid char
; While we're counting, check for invalid digits
cmp dl, '0'
jl WriteNull
cmp dl, '3'
jg WriteNull
mov dl, BYTE PTR [ecx] ; Read the next char
test dl, dl ; End of string?
jnz CountLoop
sub ecx, esi
; -----------------------
; At this point, there is at least 1 valid digit, and
; ecx contains # digits
; bl still contains first digit as ascii
; Normally we process 3 digits at a time. But the number of
; digits to process might not be an even multiple of 3.
; This code finds the 'remainder' when dividing ecx by 3.
; It might seem like you could just use 'div' (and you can),
; but 'div' is so insanely expensive, that doing all these
; lines is *still* cheaper than a single div.
mov eax, ecx
mov edx, 0AAAAAAABh
mul edx
shr edx, 1
lea edx, [edx+edx*2]
sub ecx, edx ; This gives us the remainder (0-2).
; If the remainder is zero, use the normal 3 digit load
jz LoadTriplet
; -----------------------
; Build a triplet from however many leading 'odd' digits
; there are (1 or 2). Result is in al.
lea eax, DWORD PTR [ebx-48] ; This get us the first digit
; If there was only 1 digit, don't try to load 2
cmp cl, 1
je OneDigit
; Load the other digit
shl al, 2
mov bl, BYTE PTR [esi+1]
sub bl, 48
or al, bl
OneDigit:
add esi, ecx ; Update our pQuat pointer
jmp ProcessDigits
; -----------------------
; Build a triplet from the next 3 digits.
; Result is in al.
; bl contains the first digit as ascii
LoadTriplet:
lea eax, DWORD PTR [ebx-48]
shl al, 4 ; Make room for the other 2 digits.
; Second digit
mov cl, BYTE PTR [esi+1]
sub cl, '0'
shl cl, 2
or al, cl
; Third digit
mov bl, BYTE PTR [esi+2]
sub bl, '0'
or al, bl
add esi, 3 ; Update our pQuat pointer
; -----------------------
; At this point
; al: Triplet
; ch: DigitWritten (initially zeroed when computing remainder)
ProcessDigits:
mov dl, al
shr al, 3 ; left digit
and dl, 7 ; right digit
; If we haven't written any digits, and we are
; about to write a zero, skip it. This deals
; with both "000123" and "2" (due to OneDigit,
; the 'left digit' might be zero).
; If we haven't written any digits yet (ch == 0), and the
; value we are are about to write is zero (al == 0), skip
; the write.
or ch, al
jz Skip1
add al, '0' ; Convert to ascii
mov BYTE PTR [edi], al ; Write a digit
inc edi ; Update pointer to output buffer
jmp Skip1a ; No need to check again
Skip1:
or ch, dl ; Both check and update DigitWritten
jz Skip2
Skip1a:
add dl, '0' ; Convert to ascii
mov BYTE PTR [edi], dl ; Write a digit
inc edi ; Update pointer to output buffer
Skip2:
; Load the next digit.
mov bl, BYTE PTR [esi]
test bl, bl
jnz LoadTriplet
; -----------------------
; All digits processed. We know there is at least 1 valid digit
; (checked on entry), so if we never wrote anything, the value
; must have been zero. Since we skipped it to avoid
; unnecessary preceding zeros, deal with it now.
test ch, ch
jne WriteNull
mov BYTE PTR [edi], '0'
inc edi
; -----------------------
; Write the trailing NULL. Note that if the returned string is
; 0 bytes long, an error occurred (probably invalid digits).
WriteNull:
mov BYTE PTR [edi], 0
; -----------------------
; Cleanup
pop edi
pop ebx
pop esi
Failed:
ret
#PrintOct#8 ENDP
end
I've run a string with 1,000,000,000 quaternary digit thru it as well as all the values from 0-4,294,967,295. Seems to work.
I for one welcome our new 4-digited alien overlords.

why if the number I enter gets to high it returns the wrong number [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I've got the following code but I can't work out why if the number I enter is too high does it return the wrong number. It might be because of the data types and dividing and multiplying but I can't work out exactly why. if you know of why I would be grateful for the help.
.586
.model flat, stdcall
option casemap :none
.stack 4096
extrn ExitProcess#4: proc
GetStdHandle proto :dword
ReadConsoleA proto :dword, :dword, :dword, :dword, :dword
WriteConsoleA proto :dword, :dword, :dword, :dword, :dword
STD_INPUT_HANDLE equ -10
STD_OUTPUT_HANDLE equ -11
.data
bufSize = 80
inputHandle DWORD ?
buffer db bufSize dup(?)
bytes_read DWORD ?
sum_string db "The number was ",0
outputHandle DWORD ?
bytes_written dd ?
actualNumber dw 0
asciiBuf db 4 dup (0)
.code
main:
invoke GetStdHandle, STD_INPUT_HANDLE
mov inputHandle, eax
invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr bytes_read,0
sub bytes_read, 2 ; -2 to remove cr,lf
mov ebx,0
mov al, byte ptr buffer+[ebx]
sub al,30h
add [actualNumber],ax
getNext:
inc bx
cmp ebx,bytes_read
jz cont
mov ax,10
mul [actualNumber]
mov actualNumber,ax
mov al, byte ptr buffer+[ebx]
sub al,30h
add actualNumber,ax
jmp getNext
cont:
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov outputHandle, eax
mov eax,LENGTHOF sum_string ;length of sum_string
invoke WriteConsoleA, outputHandle, addr sum_string, eax, addr bytes_written, 0
mov ax,[actualNumber]
mov cl,10
mov bl,3
nextNum:
xor edx, edx
div cl
add ah,30h
mov byte ptr asciiBuf+[ebx],ah
dec ebx
mov ah,0
cmp al,0
ja nextNum
mov eax,4
invoke WriteConsoleA, outputHandle, addr asciiBuf, eax, addr bytes_written, 0
mov eax,0
mov eax,bytes_written
push 0
call ExitProcess#4
end main
Yes, it is plausible that your return value is capped by a maximum value. This maximum is either the BYTE boundary of 255 or the WORD boundary of 65536. Let me explain why, part by part:
mov inputHandle, eax
invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr bytes_read,0
sub bytes_read, 2 ; -2 to remove cr,lf
mov ebx,0
mov al, byte ptr buffer+[ebx]
sub al,30h
add [actualNumber],ax
In this part you are calling a Win32 API function, which always returns the return value in the register EAX. After it has returned, you assign the lower 8-bits of the 32-bit return value to byte ptr buffer+[ebx], subtract 30h from it. Then you MOV the 8-bit you just modified in AL and the 8-bit from the return-value preserved in AH as a block AX to a WORD variable by add [actualNumber],ax. So AH stems from the EAX return value and is quite of undefined. You may be lucky if it's 0, but that should not be assumed.
The next problem is the following sub-routine:
getNext:
inc bx
cmp ebx,bytes_read
jz cont
mov ax,10
mul [actualNumber]
mov actualNumber,ax
mov al, byte ptr buffer+[ebx]
sub al,30h
add actualNumber,ax
jmp getNext
You are moving the decimal base 10 to the WORD register AX and multiply it by the WORD variable [actualNumber]. So far, so good. But the result of a 16-bit*16-bit MUL is returned in the register pair AX:DX(lower:higher). So your mov actualNumber,ax solely MOVs the lower 16-bits to your variable (DX is ignored, limiting your result to result % 65536). So your maximum possible result is MAX_WORD = 65535. Everything else would just give you the modulo in AX.
After your mov al, byte ptr buffer+[ebx] your overwrite the lower 8-bits of this result with the BYTE pointed to by buffer[ebx] and then subtract 30h from it. Remember: the higher 8-bits of the result still remain in AH, the higher 8-bits of AX.
Then you (re)add this value to the variable actualNumber with add actualNumber,ax. Let me condense these last two paragraphs:
Operation | AX |
| AL AH |
mov actualNumber,ax | ................ |
mov al, byte ptr buffer+[ebx] | ........ AH |
sub al,30h | ....-30h AH |
add actualNumber,ax | ................ |
So, you are modifying the lower 8-bits of AX through AL and then add the higher 8-bits of actualNumber/AH to itself - effectively doubling AH and then adding this to actualNumber like this:
actualNumber = 256 * (2 * AH) + (byte ptr buffer[ebx]-30h) ; I doubt you want that ;-)
These problems may cause several deviations from the desired result.

Printing values of arrays in MASM assembly

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.

Resources