Related
So i made this code with the knowledge i gather in various sites, i think there is an optimazed way to do it without pushing and poping the registers on the stack memory, but i don't know how to do it.
Here is my Code
comparing proc
MOV CX, SIZEOF vec2 ;The size of the substring
DEC CX
MOV DX, SIZEOF vec1 ; The size of the String
LEA SI, vec1 ; The String
LEA DI, vec2 ; The substring
FIND_FIRST:
MOV AL, [SI]; storing the ascii value of current character of mainstring
MOV AH, [DI]; storing the ascii value of current character of substring
CMP AL,AH; comparing both character
JE FITTING; if we find it we try to find the whole substring
JNE NEXT
NEXT:
INC SI; We go to the next char
DEC DX; And the size of the string decreased
JE N_FINDED
JMP FIND_FIRST
FITTING:
CLD
PUSH CX ; I push this register because in the instruction REPE CMPSB
PUSH SI ; They change.
PUSH DI
REPE CMPSB
JNE N_FITTING
JE FINDED
N_FITTING:
POP DI
POP SI
POP CX
JMP NEXT ; if the complete substring doesn't fit we go to the next char
FINDED:
POP DI
POP SI
POP CX
MOV AL, 0001H; substring found
JMP RETURN
N_FINDED:
MOV AL, 0000H; substring not found
RETURN:
ret
comparing endp
If the substring happens to have more than one character, which is very likely, then your code will start comparing bytes that are beyond the string to search through.
With the string referred to by DI and its length DX, and the substring referred to by SI and its length CX, you first need to make sure that neither string is empty, and then you need to limit the number of possible finds. Next 4 lines of code do just that:
jcxz NotFound ; Substring is empty
sub dx, cx
jb NotFound ; Substring is longer than String
; Happens also when String is empty
inc dx
As an example consider the string "overflow" (DX=8) and the substring "basic" (CX=5):
sub dx, cx ; 8 - 5 = 3
inc dx ; 3 + 1 = 4 Number of possible finds is 4
overflow
basic possible find number 1
basic possible find number 2
basic possible find number 3
basic possible find number 4
You can write your proc without having to preserve those registers on the stack (or elsewhere) all the time. Just introduce another register so you don't have to clobber the CX, SI, and DI registers:
jcxz NotFound
sub dx, cx
jb NotFound
inc dx
mov al, [si] ; Permanent load of first char of the Substring
FirstCharLoop:
cmp [di], al
je FirstCharMatch
NextFirstChar:
inc di
dec dx ; More tries?
jnz FirstCharLoop ; Yes
NotFound:
xor ax, ax
ret
FirstCharMatch:
mov bx, cx
dec bx
jz Found ; Substring had only 1 character
OtherCharsLoop:
mov ah, [si+bx]
cmp [di+bx], ah
jne NextFirstChar
dec bx
jnz OtherCharsLoop
Found:
mov ax, 1
ret
Do note that this code now does not compare the first character again like the repe cmpsb in your program did.
AX being the result, the only registers that are clobbered (and that you maybe could want to preserve) are BX, DX, and DI.
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
i am currently stuck with getting the answer from a combination problem. My basecase works just fine. I think the problem is at evaluating combination(n-1,k) and then evaluating combination(n-1,k-1).
Here's my code: n and k are inputs from user.
sub esp, 2
push word[n]
push word[k]
call combi
pop word[ans] ;store yung combi value sa ans
;convert to ascii string value
add word[ans], 30h
;print answer
mov eax, 4
mov ebx, 1
mov ecx, ans
mov edx, 2
int 80h
jmp exit
combi:
mov ebp, esp
mov ax, word[ebp+4] ;ax = k
cmp [ebp+6], ax ;if (n==k)
je basecase
cmp word[ebp+4],0 ;cmp k = 0
je basecase
;combi(n-1,k)
mov ax, [ebp+6] ; ax = n
mov bx, [ebp+4] ; bx = k
dec ax ;n-1
;execute again
sub esp, 2
push ax
push bx
call combi
pop cx ;stores to cx popped value combi(n-1,k)
mov ebp, esp ;update pointers
;combi(n-1,k-1)
push ax
dec bx
push bx
call combi
pop dx ;stores to dx popped value combi(n-1,k-1)
mov ebp, esp ;update pointers
add cx, dx ;combi(n-1,k) + combi(n-1,k-1)
mov word[ebp+8], cx
jmp combi_exit
basecase:
mov word[ebp+8], 1
combi_exit:
ret 4
Hoping for your kind responses and brilliant ideas! Thank you!
To fix your recursion, the middle part of combi: has a problem:
...
call combi
pop cx ;stores to cx popped value combi(n-1,k)
;* ^ this freed the allocated space for result
mov ebp, esp ;update pointers
;* not needed, as you will not use EBP right now, and next call destroys it
;combi(n-1,k-1)
push ax
;* ^ pushing first argument, but no stack space reserved for result
dec bx
push bx
call combi
...
To fix you can adjust that part to:
EDIT: this will not work correctly for 2+ deep recursion, as you don't preserve registers as needed, the whole recursion architecture requires more care and I would opt simply to rewrite it with simpler design in the first place, than fixing all these minor problems. This "fix" will at least stop the segfaulting.
...
call combi
mov cx,[esp] ;stores to cx value combi(n-1,k)
;* ^ keeps the stack space reserved (not popping)
;combi(n-1,k-1)
push ax
...
There's of course the other problem with your output being correct only for single digit numbers, but just search stack overflow and the tag [x86] info for those, not going to repeat it here.
BTW, this IMO stems from the unfortunate overcomplicated usage stack, do you have some particular reason why you follow such complex calling convention? How about some custom fastcall-like giving arguments and results in registers? It's also much more performant, but for me personally it's also easier to keep track of things and process stack correctly. I may add my own variant later to this answer, if I will try...
EDIT: full working example with register calling convention:
file: so_32b_pascal_triangle.asm
section .text
global _start
_start:
mov ecx,5 ; n
mov edx,2 ; k
call combi
; return to linux with sys_exit(result)
mov ebx,eax
mov eax,1
int 80h
; ECX = n, EDX = k, returns result in EAX, no other register modified
; (_msfastcall-like convention, but extended to preserve ECX+EDX)
combi: ; c(n, k) = c(n-1, k-1) + c(n-1, k), c(i, 0) = c(i, i) = 1
test edx,edx ; k == 0
je .basecases ; c(i, 0) = 1
cmp edx,ecx ; k == n
je .basecases ; c(i, i) = 1
; 0 < k < n case:
dec ecx ; n-1
call combi ; EAX = c(n-1, k)
push esi
mov esi,eax ; keep c(n-1, k) in ESI
dec edx ; k-1
call combi ; EAX = c(n-1, k-1)
add eax,esi ; EAX = c(n-1, k-1) + c(n-1, k)
; restore all modified registers
pop esi
inc ecx
inc edx
ret ; return result in EAX
.basecases:
mov eax,1
ret
compilation + run + result displayed:
...$ nasm -f elf32 -F dwarf -g so_32b_pascal_triangle.asm -l so_32b_pascal_triangle.lst -w+all
...$ ld -m elf_i386 -o so_32b_pascal_triangle so_32b_pascal_triangle.o
...$ ./so_32b_pascal_triangle ; echo $?
10
...$
EDIT:
And for my own curiosity, tried to call it from C-ish C++ code (to verify the fastcall convention is working as expected even when interoperability with C/C++ is required):
The so_32b_pascal_triangle.asm file has same combi: code, but the beginning is modified (added global, removed _start):
section .text
global combi
; ECX = n, EDX = k, returns result in EAX, no other register modified
; (fastcall-like convention, but extended to preserve ECX+EDX)
combi: ; c(n, k) = c(n-1, k-1) + c(n-1, k), c(i, 0) = c(i, i) = 1
...
file so_32b_pascal_triangle_Cpp.cpp:
#include <cstdio>
#include <cstdint>
extern "C" __attribute__ ((fastcall)) uint32_t combi(uint32_t n, uint32_t k);
int main()
{
for (uint32_t n = 0; n < 10; ++n) {
printf("%*c", 1+2*(10-n), ' ');
for (uint32_t k = 0; k <= n; ++k) {
printf("%4d", combi(n, k));
// 4-char width formatting - works for 3 digit results max.
}
printf("\n");
}
}
Build + test:
$ nasm -f elf32 -F dwarf -g so_32b_pascal_triangle.asm -l so_32b_pascal_triangle.lst -w+all
$ g++ -std=c++17 -c -m32 -O3 -Wall -Wpedantic -Wextra so_32b_pascal_triangle_Cpp.cpp
$ g++ -m32 so_32b_pascal_triangle*.o -o so_32b_pascal_triangle
$ ./so_32b_pascal_triangle
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
my program is nearing completion and is due tommrow. However i have identified the source of an issue. i am cycling through a few functions which roll a dice 1-6 and add that result to a buffer to keep score.Then when i want to print, i am only able to print single digit integers and anything with two digits such as 10 or greater loops through my write_number function infinetly until a seg fault results. How can i alter this algorithm to accomodate for up to three digit integers as the score will need to progress to 100 before the progam ends? unless anyone has a better previously coded algorithm since my assignment was to use a working function found online. please, any insight is greatly appreciated!
section .bss
userScore: resd 4
pcScore: resb 4
number: resd 4
digit: resd 4
count: resd 4
. . . . . . . . . . .
cmp edx, 6 ;dice roll result
call write_number
jmp printReadOption
write_number:
add dword[userScore], edx
mov edx, dword[userScore]
mov dword[number], edx
mov eax, dword[number]
mov ecx, 0Ah ;hex for 10
cdq
div ecx ;eax=number/10,edx=number%10
mov dword[number], eax ;number= number/10
add edx, 30h ;add 48 (30h) to make a printable character
push edx ;push edx in to the stack and
inc dword[count] ;increment count of numbers in the stack
cmp dword[number], 0 ;if number != 0, loop again
jne write_number
loop2:
pop dword[digit] ;pop the digit from the stack and
call write_digit ;write it
dec dword[count] ;decrement the count
cmp dword[count], 0 ; if count != 0, loop again
jne loop2
ret
write_digit: ;Print score
mov eax, 4
mov ebx, 1
mov ecx, userScoreIs
mov edx, userScoreIsLen
int 80h
mov eax, 4
mov ebx, 1
mov ecx, digit ;score to print
mov edx, 4
int 80h
ret
You keep adding to the number you're diving at the beginning of each loop iteration
(add dword[userScore], edx),
so the quotient probably never reaches zero, which would eventually lead to you pushing too many digits onto the stack. complements of Michael as listed in the comment below.
Learning Assembly with NASM, Ubuntu, 32 bits.
My array in .data:
ary db 1,2,2,4,5 ; Five elements of one byte each
And some number:
tmp db 2 ; Holds the number 2
Let's say I want to print the element at index 4 in the array (so it would be 5).
I know I could do this:
mov EAX,4
mov EBX,0
mov ECX,ary ; Put the array's address in ECX
add ECX,4 ; Move address four bytes to the right
add byte [ECX],'0' ; The value at this address to ASCII
mov EDX,1
int 0x80
However, for whatever reasons, I decided that instead of writing the constant number 4, I want to do it by multiplying my variable (which is 2) by 2.
This is the updated code:
mov EAX,[tmp] ; Put the number 2 in EAX
mov ECX,ary ; Put the array's address in ECX
add ECX,EAX * 2 ; Move (2 * 2) = 4 bytes to the right
add byte [ECX],'0' ; Decimal to ASCII
mov EAX,4
mov EBX,0
mov EDX,1
int 0x80
This doesn't work at add ECX,EAX * 2:
invalid operand type
But why? Doesn't ECX evaluate to 2? Being equivalent to
add ECX,2 * 2
Curiously, these do work:
add ECX,EAX * 1 ; Moves by 2
add ECX,EAX * 0 ; Moves by 0
The above suggests me that the answer is no. And the reason that multiplying by 1 or 0 works is because the assembler doesn't actually need to do any multiplication to know the answer in the first place.
Does this mean that to achieve what I want, I do have to use the mul instruction?
You CAN do multiplication and adding in one instruction if you use lea:
lea ECX,[ECX+EAX*2]
In x86, although lea supports multiplication by a constant, the add instruction doesn't support an operand that multiples a register by a constant. It supports additive offsets, but not multiplication. I assume, as you noted, that the assembler is being somewhat forgiving in this case in the accepted syntax of add ECX,EAX*0 and add ECX,EAX*1 as being equivalent to add ECX,0 and add ECX,EAX, respectively.
You would instead need do something like this:
mov ECX,ary ; Put the array's address in ECX
mov EAX,[tmp] ; Put the number 2 in EAX
shl EAX,1 ; (instead of mul EAX,2)
add ECX,EAX ; Move (2 * 2) = 4 bytes to the right
add byte [ECX],'0' ; Decimal to ASCII
mov EAX,4
mov EBX,0
mov EDX,1
int 0x80
The instruction LEA can be used to provide two additions and one limited multiplication at once. The common syntax is:
lea reg, [offset+reg+const*reg]
Here, reg is any register, offset is some constant number and const is one of 1, 2, 4 or 8 constant.
This way, this instruction is very powerful is order to compute some pretty complex equations:
The equation from the question:
add ECX,EAX * 2
can be computed this way:
lea ecx, [ecx+2*eax]
There are many other uses:
lea eax, [ebx+2*ebx] ; eax = 3*ebx
lea eax, [eax+4*eax] ; eax = 5*eax
lea eax, [ecx+8*ecx] ; eax = 9*ecx
lea eax, [1234+ebx+8*ecx]
Note, that FASM allows shorter syntax for the above examples:
lea eax, [3*ebx]
lea eax, [5*eax]
lea eax, [9*ecx]
Additional advantage of lea instruction is that it does not affects the flags. The execution speed of this instruction is very fast on all x86 CPU.