Problem in reversing a string in assembly 8086 - string

I need to reverse a given string in assembly 8086. I wrote a code that should work but I get the output
'MAGSM$HIMI
instead of
MAGSHIMIM
I can't figure out what is the problem with the code. I suspect the "$" is causing the problem but I have no idea.
My code:
org 100h
jmp main
chrs db 'M','I','M','I','H','S','G','A','M', '$'
main:
mov bp, sp
mov ax,offset chrs
call print_ax_str
mov ax, offset chrs
push ax
push 9
call reverse
PRINTN ;new line
mov ax,offset chrs
call print_ax_str
jmp stop
reverse proc
; First save the old BP
push bp
; Now establish new BP
mov bp, sp
;make space for 2 local variables
sub sp, 4
mov dx, [bp+6]
mov bx, [bp+4] ;bx = 9
dec bx ;bx = 8
add dx, bx ; dx = dx + 8
mov di, dx
mov SI, [bp+6]
mov cx, 4
L1:
mov dx, [si]
xchg ax, [di]
mov [si], ax
mov [di], dx
inc si ;si--
dec di ;di++
loop L1
mov sp, bp
; Restore OLD BP
pop bp
retn 4
reverse endp
stop:
mov ah, 0
int 16h
ret
include magshimim.inc

chrs db 'M','I','M','I','H','S','G','A','M', '$'
...
L1:
mov dx, [si]
xchg ax, [di]
mov [si], ax
mov [di], dx
inc si ;si--
dec di ;di++
loop L1
The biggest problem here is that your chrs string contains bytes but your reversing procedure works with words (2 bytes).
Looking at this loop I suspect that you've mixed 2 solutions to the reversing problem. The xchg ax, [di] instruction tells this.
Solution 1 using MOV
L1:
mov dl, [si]
mov al, [di]
mov [si], al
mov [di], dl
inc si ;si++
dec di ;di--
loop L1
Solution 2 using XCHG
L1:
mov dl, [si]
xchg dl, [di]
mov [si], dl
inc si ;si++
dec di ;di--
loop L1
Please notice that the comments in your code were wrong. Incrementing SI corresponds to "si++". Similarly decrementing DI corresponds to "di--".
Since you pass the length of the string to the procedure and have the pointers based on it, you should also base your loop counter on it and not use a fixed count of 4 via mov cx, 4.
mov cx, [bp+4] ;SLen
shr cx, 1 ;SLen/2
L1:
With some additional clean-up:
reverse proc
push bp
mov bp, sp
mov cx, [bp+4] ;SLen
mov si, [bp+6]
mov di, si
add di, cx
dec di
shr cx, 1 ;SLen/2
L1:
mov dl, [si]
mov al, [di]
mov [si], al
mov [di], dl
inc si ;si++
dec di ;di--
loop L1
pop bp
retn 4
reverse endp

Related

Substring in a string display

I am a beginner in assembly and I am trying to make a program where I should input 2 strings from the keyboard. The first string should be the main string and the second input is the substring which I need to look for in the main string. If I find it, I should display that it was found, and if not, I should display that it wasn't found.
I tried to compare the lengths of the strings so that if the first one has less characters than the second, the message "Invalid" would be displayed. Then I tried to compare the substring with the string until the substring is found in the string and the message "string found" gets displayed, if not, the message : "string not found" gets displayed. No matter what words I input, it will always say "Invalid". How can I change that?
Here is my code:
.model small
.stack 200h
.data
prompt1 db "Input String: $"
prompt2 db 10,10, 13, "Input Word: $"
prompt3 db 10,10, 13, "Output: $"
found db "Word Found. $"
notfound db "Word Not Found. $"
invalid db 10,10, 13, "Invalid. $"
InputString db 21,?,21 dup("$")
InputWord db 21,?,21 dup("$")
actlen db ?
.code
start:
mov ax, #data
mov ds, ax
mov es, ax
;Getting input string
mov ah,09h
lea dx, prompt1
int 21h
lea si, InputString
mov ah, 0Ah
mov dx, si
int 21h
;Getting input word
mov ah,09h
lea dx, prompt2
int 21h
lea di, InputWord
mov ah, 0Ah
mov dx, di
int 21h
;To check if the length of substring is shorter than the main string
mov cl, [si+1]
mov ch, 0
add si, 2
add di, 2
mov bl, [di+1]
mov bh, 0
cmp bx, cx
ja invalid_length
je valid
jb matching
valid:
cld
repe cmpsb
je found_display
jne notfound_display
mov bp, cx ;CX is length string (long)
sub bp, bx ;BX is length word (short)
inc bp
cld
lea si, [InputString + 2]
lea di, [InputWord + 2]
matching:
mov al, [si] ;Next character from the string
cmp al, [di] ;Always the first character from the word
je check
continue:
inc si ;DI remains at start of the word
dec bp
jnz matching ;More tries to do
jmp notfound_display
check:
push si
push di
mov cx, bx ;BX is length of word
repe cmpsb
pop di
pop si
jne continue
jmp found_display
again:
mov si, ax
dec dx
lea di, InputWord
jmp matching
invalid_length:
mov ah, 09h
lea dx, invalid
int 21h
jmp done
found_display:
mov dx, offset found
mov ah, 09h
int 21h
jmp done
notfound_display:
mov dx, offset notfound
mov ah, 09h
int 21h
;fallthrough is intentional
done:
mov ax,4C00h
int 21h ;exit program and return to DOS
end start
I see that you have tried to apply some of the advice I gave in the answer at Finding the substring in an input string.
But it's gone wrong mostly because you decided to special-case where the inputted string has the same length as the inputted word. That's not a special case at all! If it so happens, my calculation of the number of possible finds will remain valid and yield a 1 in the BP register. In short, your problems originate from having inserted that valid part and not having edited the program accordingly.
add si, 2
add di, 2
je valid
jb matching
valid:
cld
repe cmpsb
je found_display
jne notfound_display
You don't need all of the above once you drop the redundant valid part.
again:
mov si, ax
dec dx
lea di, InputWord
jmp matching
And don't forget to remove any code that you don't actually need in your program, especially when you use code that you found on the internet.
Solution
...
; To check if the length of substring is shorter than the main string
mov cl, [si+1]
mov ch, 0
mov bl, [di+1]
mov bh, 0
mov bp, cx ; CX is length string (long)
sub bp, bx ; BX is length word (short)
jb notfound_display
inc bp ; -> BP is number of possible finds 1+
cld
lea si, [InputString + 2]
lea di, [InputWord + 2]
matching:
mov al, [si] ; Next character from the string
cmp al, [di] ; Always the first character from the word
je check
continue:
inc si ; DI remains at start of the word
dec bp
jnz matching ; More tries to do
jmp notfound_display
check:
push si
push di
mov cx, bx ; BX is length of word
repe cmpsb
pop di
pop si
jne continue
jmp found_display
...
Some optimization
I have absorbed the instructions cmp bx, cx ja invalid_length in the calculation of the number of possible finds (shaving off 2 bytes). If the subtraction produces a borrow, you know the word is longer than the string and so you can branch away. Whether you jump to invalid_length or notfound_display is up to you...
You can shorten this program by 2 bytes if you replace lea si, [InputString + 2] lea di, [InputWord + 2] by add si, 2 add di, 2.
This should work:
.model small
.stack 100h
print macro p
lea dx,p
mov ah,09h
int 21h
endm
.data
cn db 0
pn db 0
space db 10,13, " $"
msg db 10,13, "hjut$"
msg1 db "Introduceti primul sir:$"
msg2 db "Introduceti al doilea sir:$"
msg3 db "Al doilea sir nu se gaseste in primul.$"
msg4 db "Al doilea sir se gaseste in primul. $"
ar db 20 dup("$")
br db 20 dup("$")
.code
start:
mov ax,#data
mov ds,ax
mov si,01h
mov di,00h
mov cn,00h
print msg1
read1:mov ah,01h
int 21h
mov ar[si],al
inc si
cmp al,0dh
jnz read1
mov si,00h
print msg2
read2:mov ah,01h
int 21h
mov br[si],al
inc si
cmp al,0dh
jnz read2
mov si,00h
mov di,00h
jmp lop1
lop1: mov di,00h
inc si
mov bh,ar[si]
cmp bh,0dh
jz disp
mov bh,br[di]
cmp ar[si],bh
jnz lop1
jz lop2
lop2:inc si
inc di
mov bh,br[di]
cmp bh,0dh
jz l1
mov bh,br[di]
cmp ar[si],bh
jz lop2
jmp lop1
l1:
add cn,01h
dec si
jmp lop1
disp:
cmp cn,00h
jz disp1
print msg4
add cn,30h
mov dl,cn
mov ah,02h
int 21h
jmp exit
disp1:print msg3
exit:mov ah,4ch
int 21h
end start

Assembly 8086 TASM - 0Ah 21h remembers last entry

So I'm writing a uni assignment - a program that subtracts two entered decimals (max 10 characters ea). The first iteration works as intended. However, when I restart the program, for some reason the second operand is remembered.
The prompt to enter it does come up, but is then skipped as if I've entered something already - the same thing I entered the first iteration, in fact.
The question is: why does it happen and how do I fix it? The first prompt works correctly.
The prompt is under INPUT_2:
.model small
.386
stack 100h
dataseg
inputMsg1 db 0Ah, 0Dh, 'Enter first operand', 0Ah, 0Dh, '$'
inputMsg2 db 0Ah, 0Dh, 'Enter second operand', 0Ah, 0Dh, '$'
inputMax1 db 11
inputLen1 db ?
input1 db 12 dup(?)
input1Packd db 5 dup(0)
inputMax2 db 11
inputLen2 db ?
input2 db 12 dup(?)
input2Packd db 5 dup(0)
packMode db 0 ;Режим упаковки: 1 - первая цифра, 2 - вторая
resMsg db 0Ah, 0Dh, 'Result: $'
res db 9 dup(' '),'$'
retryMsg db 0Ah, 0Dh
db 'Press Any Key to continue, ESC to quit'
db '$'
errorMsg db 0Ah, 0Dh, 'Something went wrong. Try again$'
codeseg
START:
startupcode
jmp INPUT_1
INPUT_1_ERROR:
lea DX, errorMsg
mov AH, 09h
int 21h
INPUT_1:
lea DX, inputMsg1
mov AH, 09h
int 21h
lea DX, inputMax1
mov AH, 0Ah
int 21h
cmp inputLen1, 0
jz INPUT_1_ERROR
INPUT_1_PROCESS:
lea BX, input1
lea DX, input1Packd
xor CX, CX
mov CL, inputLen1
mov SI, CX
dec SI
mov DI, 4
INPUT_1_LOOP:
mov AL, [BX][SI]
cmp AL, '0'
jb INPUT_1_ERROR
cmp AL, '9'
ja INPUT_1_ERROR
and AL, 0Fh
mov AH, packMode
cmp AH, 0
jnz INPUT_1_PACK_SECOND
INPUT_1_PACK_FIRST:
inc AH
push BX
mov BX, DX
mov [BX][DI], AL
pop BX
jmp INPUT_1_PACK_FINISHED
INPUT_1_PACK_SECOND:
dec AH
shl AL, 4
push BX
mov BX, DX
or [BX][DI], AL
pop BX
dec DI
INPUT_1_PACK_FINISHED:
mov packMode, AH
dec SI
loop INPUT_1_LOOP
mov packMode, 0
jmp INPUT_2
INPUT_2_ERROR:
lea DX, errorMsg
mov AH, 09h
int 21h
INPUT_2:
lea DX, inputMsg2
mov AH, 09h
int 21h
lea DX, inputMax2
mov AH, 0Ah
int 21h
cmp inputLen2, 0
jz INPUT_2_ERROR
INPUT_2_PROCESS:
lea BX, input2
lea DX, input2Packd
xor CX, CX
mov CL, inputLen2
mov SI, CX
dec SI
mov DI, 4
INPUT_2_LOOP:
mov AL, [BX][SI]
cmp AL, '0'
jb INPUT_2_ERROR
cmp AL, '9'
ja INPUT_2_ERROR
and AL, 0Fh
mov AH, packMode
cmp AH, 0
jnz INPUT_2_PACK_SECOND
INPUT_2_PACK_FIRST:
inc AH
push BX
mov BX, DX
mov [BX][DI], AL
pop BX
jmp INPUT_2_PACK_FINISHED
INPUT_2_PACK_SECOND:
dec AH
shl AL, 4
push BX
mov BX, DX
or [BX][DI], AL
pop BX
dec DI
INPUT_2_PACK_FINISHED:
mov packMode, AH
dec SI
loop INPUT_2_LOOP
MATH_SETUP:
mov SI, 4
mov CX, 4
mov DI, 7
MATH:
lea BX, input1Packd
mov AL, [BX][SI]
lea BX, input2Packd
mov AH, [BX][SI]
sbb AL, AH
pushf
das
dec SI
mov AH, AL
lea BX, res
and AL, 0Fh
or AL, 30h
mov [BX][DI], AL
dec DI
shr AH, 4
or AH, 30h
mov [BX][DI], AH
dec DI
popf
loop MATH
lea BX, res
mov CX, 7
mov SI, 0
SHORTEN:
mov AL, [BX][SI]
cmp AL, '0'
jnz WRAPUP
inc SI
loop SHORTEN
WRAPUP:
push CX
lea DX, resMsg
mov AH, 09h
int 21h
lea DX, res
pop CX
cmp CX, 0
jz SKIP_SHORTEN
PRINT_SHORTEN:
add DX, 7
sub DX, CX
jmp FINISH_SHORTEN
SKIP_SHORTEN:
add DX, 6
FINISH_SHORTEN:
mov AH, 09h
int 21h
lea DX, retryMsg
mov AH, 09h
int 21h
mov AH, 01h
int 21h
cmp AL, 1Bh
jz QUIT
lea BX, input1Packd
mov DX, 1
BCD_CLEANUP:
mov DI, 0
mov CX, 5
BCD_CLEANUP_LOOP:
mov [BX][DI], 0
inc DI
loop BCD_CLEANUP_LOOP
lea BX, input2Packd
cmp DX, 1
mov DX, 0
jz BCD_CLEANUP
jmp START
QUIT:
exitcode 0
end START
Any better code suggestions welcome as well, but not needed if you don't have an answer for the question.
The DOS buffered input function 0Ah allows you to have a preset text in the storage space of the input buffer that you provide. For a complete explanation of this DOS function see how buffered input works.
The first time that your program runs the inputLen1 and inputLen2 fields are empty because of how you defined them in the source using db ? which translates to zero.
But when you re-run the code this is no longer the case! The lengths still show what you got in the previous run. You need to zero both these fields before invoking function 0Ah again on the same input buffers.
mov DX, 0
jz BCD_CLEANUP
mov inputLen1, DL ;DL=0
mov inputLen2, DL ;DL=0
jmp START
The MATH loop has a couple of problems regarding the CF.
The sbb al, ah instruction depends on the value in the carry flag, but you neglect to make sure it is off on the first iteration of this loop. Just add clc:
clc
MATH:
lea BX, input1Packd
mov AL, [BX][SI]
lea BX, input2Packd
mov AH, [BX][SI]
sbb AL, AH
The das instruction consumes the carry flag that you get from the sbb instruction, but it's the carry flag that you get from the das instruction that you need to preserve/restore to have it propagate through the loop.
sbb AL, AH
das
pushf
...a program that subtracts two entered decimals (max 10 characters ea)...
If ever you enter 9 or 10 characters, those most significant digits will not be taken into account because MATH_SETUP really limits you to 8 characters (which in turn is a good thing since the res buffer only has room to show 8 characters)!
MATH_SETUP:
mov SI, 4 <-- Could permit 10 packed BCD digits
mov CX, 4 <-- Max 8 characters
mov DI, 7 <-- Max 8 characters

x86 assembly searching word in text file

.data
word db 64 dup(?)
wordLen dw ?
Theres following code:
MOV ah, 3Ch
MOV cx, 0
MOV dx, offset rez
INT 21h
JC writeWrror
MOV rFail, ax
MOV counter, 0
mov tcounter, 0
;*****************************************************
;Reading from file
;*****************************************************
read:
MOV bx, dFail
CALL readBuf
CMP ax, 0
JE closeWrite
;*****************************************************
;Compare tasks
;*****************************************************
MOV cx, ax
MOV si, offset skBuf
MOV di, offset wrBuf
compare:
MOV dl, [si]
mov temp, si
mov si, counter
mov dh, [zodis + si]
mov si, temp
CMP dl, dh
JE isEqual
mov counter, 0
tesk:
INC si
LOOP compare
Word is the word read from program parameters. I want to compare first symbol of my word with symbol read from file and continue reading by increasing counter if there is a match.
Is this compare legal operation to find a specified string in file? Or should i use other tactics?

how to check compare if palindrome or not?

I don't know much of assembly language but I tried making this palindrome and it's quite hard. First I have to enter a string then show its original and reversed string then show if its a palindrome or not.
I already figured out how to show the reverse string by pushing and popping it through a loop from what I have read in another forum and figured it out
now the only problem for me is to compare the reverse string and original string to check if its a palindrome or not.
call clearscreen
mov dh, 0
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str1
int 21h
palin db 40 dup(?)
mov ah, 0ah
mov palin, 40
mov dx, offset palin
int 21h
mov dh, 1
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str2
int 21h
mov si, 2
forward:
mov al, palin + si
cmp al, 13
je palindrome
mov ah, 02h
mov dl, al
push ax 'push the letter to reverse
int 21h
inc si
jmp forward
palindrome:
mov dh, 2
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str3
int 21h
mov cx, 40 'pop each letter through a loop to show its reverse
reverse:
mov ah, 02h
pop ax
mov dl, al
int 21h
loop reverse
int 20h
clearscreen:
mov ax, 0600h
mov bh, 0Eh
mov cx, 0
mov dx, 8025
int 10h
ret
cursor:
mov ah, 02h
mov bh, 0
int 10h
ret
str1: db "Enter A String : $"
str2: db "Forward : $"
str3: db "Backward : $"
str4: db "Its a Palindrome! $"
str5: db "Not a Palindrome!$"
You have a string the user typed in, what you need to do is compare the first byte with the last byte, do the same for the 2nd one and the 2nd to last one. Keep doing this for the whole string. You also need the length of the string. To make life easier, you should convert the string to all UPPER case letters or all lowercase letters to make comparison easier.
It is unfortunate they are still teaching 16bit DOS code. This is a sample with the word defined in the data section. You will have to modify it to receive input and work on that string
.data
pal db "racecar"
pal_len equ $ - pal - 1
szYes db "yes$"
szNo db "no$"
.code
start:
mov ax,#data
mov ds,ax
call IsPalindrome
mov ah,4ch
int 21h
IsPalindrome:
lea si, pal
lea di, pal
add di, pal_len
mov cx, 0
CheckIt:
mov al, byte ptr [si]
mov dl, byte ptr [di]
cmp al, dl
jne No
inc si
dec di
inc cx
cmp cx, pal_len
jne CheckIt
mov ah,9
lea dx,szYes
int 21h
ret
No:
mov ah,9
lea dx,szNo
int 21h
ret
end start
For completeness and to bring us into the 21st century, 32bit NASM code:
section .data
fmt db "%s", 0
szPal db "RACECAR"
Pal_len equ $ - szPal - 1
szYes db "Yes", 10, 0
szNo db "No", 10, 0
extern printf, exit
global _start
section .text
_start:
call IsPalindrome
call exit
IsPalindrome:
mov ecx, 0
mov ebx, Pal_len
mov esi, szPal
.CheckIt:
mov al, byte [esi + ecx]
mov dl, byte [esi + ebx]
cmp al, dl
jne .No
inc ecx
dec ebx
jns .CheckIt
push szYes
push fmt
call printf
add esp, 4 * 2
mov eax, 1
jmp Done
.No:
push szNo
push fmt
call printf
add esp, 4 * 2
xor eax, eax
Done:
ret

String comparison is not working

I have a small problem.
This is my code:
cmpstr:
pusha
xor cx, cx
mov ax, ds
push ax
mov ax, si
push ax
call strlen
mov dx, ax
mov ax, es
mov ds, ax
mov si, bx
call strlen
cmp al, dl
jnz .fail
pop ax
mov si, ax
pop ax
mov ds, ax
.loop:
push bx
mov al, byte [es:bx]
mov bl, byte [ds:si]
cmp al, bl
jne .fail
cmp bl, 0
jz .suc
pop bx
add bx, 1
inc si
inc cx
jmp .loop
.fail:
mov al, 'C';
mov ah, 0Eh
int 10h
popa
mov ax, 0
jmp .end
.suc:
mov al, 'D';
mov ah, 0Eh
int 10h
popa
mov ax, 1
.end:
ret
This procedure should compare two strings and return (mov to ax) 1, if strings (first on es:bx, second ds:si) are same or 0 if they're different. My problem is that command before procedure call is executed, letter 'D' is being printed (comparasion was successful) but command after procedure call is not working. I think that problem must be somewhere in this procedure. Does anybody know what is wrong here?
When you leave the loop with one of the conditional jumps
.loop:
push bx
mov al, byte [es:bx]
mov bl, byte [ds:si]
cmp al, bl
jne .fail
cmp bl, 0
jz .suc
You have pushed BX, but you never pop it. That will make the next RET go to strange places.

Resources