I'm trying to figure out how to take a null-terminated normal IP address as a string and convert it to a 32-bit integer. In this example, my string is located at address ES:DI+Qapktdat. Qapktdat is a member of a struct defined as a string stored at ES:DI.
I have no problem reading from or writing to strings in the struct, but I have a problem with calculating the IP.
For example, If I use the string "192.168.7.2" then EDX should equal Hex value C0A80702 but I'm getting nothing close to it.
What could I be doing wrong?
ipconvmath:
mov BX,Qapktdat-1 ;BX=data pointer. Set it to beginning of data -1
mov CX,11h ;Look for null within the first 17 bytes of data
ipend:
inc BX
mov AL,[ES:DI+BX]
cmp AL,0
je ipok
loop ipend
mov DX,-1 ;cant find null char so exit
ret
ipok:
mov BP,10 ;BP = multiplier of multiplier
xor EDX,EDX ;EDX = eventual IP address
;We scan IP address starting at LAST digit
nextipseg:
mov CX,1 ;CX= immediate multiplier
xor DL,DL ;lower 8 bits of EDX = 0
nxb:
xor AX,AX
dec BX
mov AL,[ES:DI+BX]
cmp AL,'.'
je nodot
;Input isnt DOT so see if its number 0-9
cmp AL,30h ;#0
jb badipcnv
cmp AL,39h ;#9
ja badipcnv
sub AL,30h
mul CX ;multiply by 1, 10, or 100 depending on number position
add DL,AL ;add it to our numeric IP
mov AX,CX ;temporarily make AX=CX so multiply can work
mul BP ;multiply the immediate multiplier by 10
mov CX,AX
cmp BX,Qapktdat ;see if were at beginning of data
jne nxb
;we are so skip beginning
nodot:
rol EDX,8 ;shift existing contents over by 1 byte
cmp BX,Qapktdat ;see if were at beginning
jne nextipseg
;here we are at beginning so were done
;EDX = ip address converted
ret
;Reach here if a character is not 0-9 or a dot
badipcnv:
mov AX,1
ret
Related
I am working on a program in Assembly Language right now where the user can input any word of their preference and then the program will reverse it. So, if the user inputs HELLO and the program will output OLLEH.
The algorithm I decided to implement was by first getting the length of the string and storing it in length. I will then use the length to traverse backward in the given string and store each character in a new memory location which is REVERSE. I tested my program several times but it only outputs one character. I tried several words and noticed that the character is always output is the second character of the string so I am unsure if it is able to reverse the word or not.
Did I miss anything or I implemented a code block incorrectly?
Note: The GET_STRING[end] 5 is used when I run the code in .exe format.
Below is the code I am currently working on:
%include "io.inc"
section .data
string dd 0x00
end dd 0x00
REVERSE dd 0x00
length db 0
section .text
global CMAIN
CMAIN:
;write your code here
PRINT_STRING "Please enter a string: "
GET_STRING [string], 10
lea esi, [string]
lea edi, [REVERSE]
lea ecx, [length]
L1:
mov al, [esi]
cmp al, 0
JE FINISH
JNE INCREMENT
inc esi
jmp L1
INCREMENT:
inc byte[length]
inc esi
jmp L1
FINISH:
L2:
;points to the current index of the string (i.e. if Hello,it will first point at 'o' which is 5
mov al, [ecx]
cmp cl, 0 ;checks if length == 0
JE DONE
JNE DECREMENT
dec ecx
jmp L2
DECREMENT:
mov byte[edi], al ;adds a character from [string]
dec ecx
jmp L2
DONE:
PRINT_STRING REVERSE
GET_STRING[end], 5
xor eax, eax
ret
If GET_STRING gives you a zero-terminated string then your L1 part will find the length of that string.
This is what remains after removing the redundant code:
lea esi, [string]
L1:
mov al, [esi]
cmp al, 0
je FINISH
inc byte [length]
inc esi
jmp L1
FINISH:
lea ecx, [length]
In the L2 part however
you are using the address of the length variable as if it were an address into the string itself!
you are checking for length == 0 from a register that you did not load with the length
At the start of part L2, the ESI register still points at the terminating zero. So you'll have to decrement first and then fetch and store the character.
Of course you can't copy any characters if the length variable contains 0. So check this condition first.
lea edi, [REVERSE]
mov cl, [length]
test cl, cl
jz DONE
L2:
dec esi ; Decrement
mov al, [esi] ; Fetch
mov [edi], al ; Store
inc edi
dec cl ; 1 more character done
jnz L2
DONE:
mov [edi], cl ; CL=0 at this point, now zero-terminating the result
If you keep your current data definitions, then don't input more than 3 characters. A dword dd allows for just 3 characters and 1 terminating zero.
Alternatively widen your buffers:
string db 11 dup (0) ; GET_STRING [string], 10
end db 6 dup (0) ; GET_STRING [end], 5
REVERSE db 11 dup (0)
length db 0
Below is a version that doesn't use a memory-based length variable. The length of the string is stored in ECX.
lea edi, [REVERSE]
lea esi, [string]
L1:
mov al, [esi]
inc esi
cmp al, 0
jne L1
lea ecx, [esi - 1 - String]
jecxz DONE
; ESI points behind the terminating zero, the fetch uses a -2 offset (avoiding address stall)
L2:
mov al, [esi - 2] ; Fetch
mov [edi], al ; Store
dec esi
inc edi
dec ecx ; 1 more character done
jnz L2
DONE:
mov [edi], cl ; CL=0 at this point, now zero-terminating the result
Input is random number of integers(does not exceed 1 byte), separated with spaces like this : -3 4 5 7 8 -1
There is ReadInt function in Irvine32 library, as far as I know, one integer as its input function. But since there is no clear information about the number of inputs, I had to declare the space in BYTE string form like this :
keyListInString BYTE 31 DUP(0)
and get integer string like this :
mov edx,OFFSET keyListInString
mov ecx,SIZEOF keyListInString
call ReadString
and I tried to separate each integers by recognizing spaces,'-' symbol and manually inserting them to new space I have created.
keyListInInteger BYTE 31 DUP(0) ;new space for integers
And here is the code I used.
mov esi,OFFSET keyListInString
mov edi,OFFSET keyListInInteger
mov ecx,eax ;eax = length of string = number of cycles
L1:
mov al,[esi]
cmp al,45 ;if current component of keyListInString is '-'
je ifMinus
mov al,[esi]
cmp al,0 ;if this is null(space)
je ifNull
mov al,[esi] ;if its none of above(normal number)
sub al,48 ;sub 48 for ascii --> integer
mov [edi],al ;put integer to where edi points(keyListInInteger)
inc edi
jmp final
ifMinus:
inc esi ;points next index on keyListInString(number part)
mov al,[esi]
sub al,48
neg al ;have to reverse the sign, because it is originally a minus
mov [edi],al
inc edi ;manually increase edi
jmp final
ifNull:
inc esi ;it is space anyway, so point next.
jmp L1 ;and repeat from first.
final:
inc esi
loop L1
I tried to check if this is the right code in this way :
mov edx,OFFSET keyListInInteger
mov al,[edx] ;see [edx+2],[edx+4],...
movzx eax,al
call DumpRegs
and see al's value.
I think I somehow approached to the result I wanted, but not exactly.
I wanted to make each integers like elements in an array, but I have no idea how to do so.
I have an assignment asking me to find the hamming distance of two user-input strings that are not necessarily equal in length.
So, I made the following algorithm:
Read both strings
check the length of each string
compare the length of the strings
if(str1 is shorter)
set counter to be the length of str1
END IF
if(str1 is longer)
set counter to be the length of str2
END IF
if(str1 == str2)
set counter to be length of str1
END IF
loop through each digit of the strings
if(str1[digitNo] XOR str2[digitNo] == 1)
inc al
END IF
the final al value is the hamming distance of the strings, print it.
But I'm stuck at step 3 and I don't seem to get it working. any help?
I tried playing around with the registers to save the values in, but none of that worked, I still didn't get it working.
; THIS IS THE CODE I GOT
.model small
.data
str1 db 255
db ?
db 255 dup(?)
msg1 db 13,10,"Enter first string:$"
str2 db 255
db ?
db 255 dup(?)
msg2 db 13,10,"Enter second string:$"
one db "1"
count db ?
.code
.startup
mov ax,#data
mov ds,ax
; printing first message
mov ah, 9
mov dx, offset msg1
int 21h
; reading first string
mov ah, 10
mov dx, offset str1
int 21h
; printing second message
mov ah, 9
mov dx, offset msg2
int 21h
; reading second string
mov ah, 10
mov dx, offset str2
int 21h
; setting the values of the registers to zero
mov si, 0
mov di, 0
mov cx, 0
mov bx, 0
; checking the length of the first string
mov bl, str1+1
add bl, 30h
mov ah, 02h
mov dl, bl
int 21h
; checking the length of the second string
mov bl, str2+1
add bl, 30h
mov ah, 02h
mov dh, bl
int 21h
; comparing the length of the strings
cmp dl,dh
je equal
jg str1Greater
jl str1NotGreater
; if the strings are equal we jump here
equal:
mov cl, dl
call theLoop
; if the first string is greater than the second, we jump here and set counter of str1
str1Greater:
; if the second string is greater than the first, we jump here and set counter to length of str2
Str1NotGreater:
; this is the loop that finds and prints the hamming distance
;we find it by looping over the strings and taking the xor for each 2, then incrementing counter of ones for each xor == 1
theLoop:
end
So, in the code I provided, it's supposed to print the length of each string (it prints the lengths next to each other), but it seems to always keep printing the length of the first string, twice. The register used to store the length of the first string is dl, and the register used to store the length of the second is dh, if I change it back to dl, it would then print the correct length, but I want to compare the lengths, and I think it won't be possible to do so if I save it in dl both times.
but it seems to always keep printing the length of the first string, twice.
When outputting a character with the DOS function 02h you don't get to choose which register to use to supply the character! It's always DL.
Since after printing both lengths you still want to work with these lengths it will be better to not destroy them in the first place. Put the 1st length in BL and the second length in BH. For outputting you copy these in turn to DL where you do the conversion to a character. This of course can only work for strings of at most 9 characters.
; checking the length of the first string
mov BL, str1+1
mov dl, BL
add dl, 30h
mov ah, 02h
int 21h
; checking the length of the second string
mov BH, str2+1
mov dl, BH
add dl, 30h
mov ah, 02h
int 21h
; comparing the length of the strings
cmp BL, BH
ja str1LONGER
jb str1SHORTER
; if the strings are equal we ** FALL THROUGH ** here
equal:
mov cl, BL
mov ch, 0
call theLoop
!!!! You need some way out at this point. Don't fall through here !!!!
; if the first string is greater than the second, we set counter of str1
str1LONGER:
; if the second string is greater than the first, we set counter to length of str2
Str1SHORTER:
; this is the loop that finds and prints the hamming distance
;we find it by looping over the strings and taking the xor for each 2, then incrementing counter of ones for each xor == 1
theLoop:
Additional notes
Lengths are unsigned numbers. Use the unsigned conditions above and below.
Talking about longer and shorter makes more sense for strings.
Don't use 3 jumps if a mere fall through in the code can do the job.
Your code in theLoop will probably use CX as a counter. Don't forget to zero CH. Either using 2 instructions like I did above or else use movzx cx, BL if you're allowed to use instructions that surpass the original 8086.
Bonus
mov si, offset str1+2
mov di, offset str2+2
mov al, 0
MORE:
mov dl, [si]
cmp dl, [di]
je EQ
inc al
EQ:
inc si
inc di
loop MORE
My program receives a string representing a hex value, say "324" and I want to convert it to an actual value in decimal (aka get the integer 804)
So far, I've read char by char, and for each one, I would convert it to value (move it to AL, and sub 48), then multiply by 16, and only at the end divide one time by 16.
The problem: If I input a hex string more than "99" in hex, my registers reset out to 0, but I need to be able to support much larger values.
My sketch:
initialize ecx register with 0 that will hold our overall decimal integer value
for each step:
1. mov al, byte [ebx] (ebx holds our string)
2. sub, al 48
3. add eax, ecx
4. multiply eax by 16, then move content of eax to ecx
Is there another way?
If I understand correctly you are trying to parse hex decimal string to value in register. It would be easier to see the problem if you posted actual code.
I would do something like this:
; accepts null terminated string with characters '0'-'9', 'A'-'F'
; return value in eax
xor eax, eax
.loop:
mov cl, byte [ebx]
test cl, cl
jz .end
cmp cl, 65
jl .numeric
sub cl, 7 ; 'A' code is 65, need to subtract 7 more so 'A' = 10
.numeric:
sub cl, 48
shl eax, 4
or al, cl
inc ebx
jmp .loop
.end:
ret
So I am taking an assembly language course and I am stuck on this problem from the book:
Using the windows 32 console (so I have an io.h to use), I am supposed to take a valid hex value inputted by the user and then display the actual hex value in the register EAX. So if the user entered "AB CD E2 18", then after the procedure EAX would hold the value: ABCDE218.
The parts that I am stuck on are the A-F values. If I use A for example, I can get the bits to read 00000010, but I don't know how to change that into its hex value A. Here is what I have so far:
.586
.MODEL FLAT
.CODE
hexToInt PROC
push ebp ; save base pointer
mov ebp, esp ; establish stack frame
sub esp, 4 ; local space for sign
push ebx ; Save registers
push edx
push esi
pushfd ; save flags
mov esi,[ebp+8] ; get parameter (source addr)
WhileBlankD:
cmp BYTE PTR [esi],' ' ; space?
jne EndWhileBlankD ; exit if not
inc esi ; increment character pointer
jmp WhileBlankD ; and try again
EndWhileBlankD:
mov eax,1 ; default sign multiplier
IfPlusD:cmp BYTE PTR [esi],'+' ; leading + ?
je SkipSignD ; if so, skip over
IfMinusD:
cmp BYTE PTR [esi],'-' ; leading - ?
jne EndIfSignD ; if not, save default +
mov eax,-1 ; -1 for minus sign
SkipSignD:
inc esi ; move past sign
EndIfSignD:
mov [ebp-4],eax ; save sign multiplier
mov eax,0 ; number being accumulated
WhileDigitD:
cmp BYTE PTR [esi],'0' ; compare next character to '0'
jb EndWhileDigitD ; not a digit if smaller than '0'
cmp BYTE PTR [esi],'9' ; compare to '9'
ja TestForHexD
mov bl,[esi] ; ASCII character to BL
and ebx,0000000Fh ; convert to single-digit integer
and eax, ebx
shl eax, 4
inc esi
jmp WhileDigitD
TestForHexD:
cmp BYTE PTR [esi], 'F'
ja EndWhileDigitD
mov bl, [esi]
sub bl, 31h
and ebx, 000000FFh
or al, bl
shl eax, 4
inc esi ; increment character pointer
jmp WhileDigitD ; go try next character
EndWhileDigitD:
; if value is < 80000000h, multiply by sign
cmp eax,80000000h ; 80000000h?
jnb endIfMaxD ; skip if not
imul DWORD PTR [ebp-4] ; make signed number
endIfMaxD:
popfd ; restore flags
pop esi ; restore registers
pop edx
pop ebx
mov esp, ebp ; delete local variable space
pop ebp
ret ; exit
hexToInt ENDP
END
The TestForHex label is where I am trying to convert the ASCII string to hex. I was looking around and read that I could accomplish my goal by shifting and masking, but I can't figure it out and I can't find any examples. At this point I am sure its something really small that I am just over looking, but I am stuck.
There are some bugs in your code.
First, in 0 ... 9 string to integer conversion code, you don't do ASCII to binary conversion as you should do, but instead you do and ebx,0Fh, which is incorrect. You need to subtract '0' (30h) from each ASCII character, like this:
mov bl,[esi]
sub bl,'0' ; sub bl,30h
Then, also in 0 ... 9 string to integer conversion code:
and eax, ebx
If the number consists of only 0...9 digits, and eax, ebx will produce always 0. It should be:
or al,bl
Then, you do shl eax,4, even if you don't know if there will be more digits. That means that the number will be 16 times bigger than it should.
Then, you give the example input with spaces, but your code does not handle spaces (20h) properly, it ends reading input for any value below '0' (30h), it seems to accept only leading spaces (skip this if you don't want to accept spaces in between).
So, the entire code block above should be:
WhileDigitD:
cmp byte ptr [esi], ' ' ; delete this if you don't want spaces in between.
je next_char ; delete this if you don't want spaces in between.
cmp BYTE PTR [esi],'0' ; compare next character to '0'
jb EndWhileDigitD ; not a digit if smaller than '0'
cmp BYTE PTR [esi],'9' ; compare to '9'
ja TestForHexD
mov bl,[esi] ; ASCII character to BL
sub bl,'0' ; sub bl,30h -> convert ASCII to binary.
shift_eax_by_4_and_add_bl:
shl eax,4 ; shift the current value 4 bits to left.
or al,bl ; add the value of the current digit.
next_char:
inc esi
jmp WhileDigitD
I also added labels next_char and shift_eax_by_4_and_add_bl. The reason for next_char should be evident, shift_eax_by_4_and_add_bl is to minimize duplicate code of 0...9 and A...F code blocks, see below.
You don't check that that the hexadecimal A...F digit is within range A ... F, only that it's below or equal to F. Otherwise it has same bug with shl eax,4. And as usually duplicate code should be avoided, I added shift_eax_by_4_and_add_bl label to minimize duplicate code.
So I think it should be:
Edit: corrected sub bl,31h -> sub bl,('A'-0Ah).
TestForHexD:
cmp BYTE PTR [esi], 'A'
jb EndWhileDigitD
cmp BYTE PTR [esi], 'F'
ja EndWhileDigitD
mov bl,[esi]
sub bl,('A'-0Ah) ; sub bl,55 -> convert ASCII to binary.
jmp shift_eax_by_4_and_add_bl
If you need to convert a character (for simplicity, say, in upper case) representing a hex digit into the value of that digit you need to do this:
IF char >= 'A'
value = char - 'A' + 10
ELSE
value = char - '0'
ENDIF
If you need to do the reverse, you do the reverse:
IF value >= 10
char = value - 10 + 'A'
ELSE
char = value + '0'
ENDIF
Here you exploit the fact that the ASCII characters 0 through 9 have consecutive ASCII codes and so do the ASCII characters A through F.