The program will ask for 2 strings (with a maximum size of 150 characters, for example).
INPUT
String 1: "Hello this is the first string"
String 2: "Woops this string is different and longer!"
OUTPUT
Number of equal characters = 26
Number of different characters = 15
I have in mind that we don't have to "calculate" the different characters as we can get the size of the longest string and subtract the number of equal characters but I don't know how to do this first comparison (the number of equal characters).
How can I do this? Could I do a macro for that comparison?
UPDATE
I am using EMU8086, MASM. I don't want to use string instructions.
This is my current code, following the instructions in the comments, but I still can't make it work.
.model small
.stack 100h
.data
Text1 DB "Please enter the first phrase: ",13,10,'$'
Text2 DB "Please enter the second phrase: ",13,10,'$'
max1 DB 151 ;We add one to 150 for the ENTER
charRead1 DB 0
max2 DB 151 ;We add one to 150 for the ENTER
charRead2 DB 0
TextoEquals DB "Number of equal characters: ",13,10,'$'
TextoDiffer DB "Number of different characters: ",13,10,'$'
linefeed DB 13, 10, "$"
size1 dw 0000h
size2 dw 0000h
.code
AllMacros:
MagicFunction MACRO
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, OFFSET max1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, offset max2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
repne
scasb
jne NotFound
inc bx ; Increment counter
mov byte ptr [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
ENDM
PrintText MACRO str
MOV AH, 09h
LEA DX, str
INT 21h
ENDM
PrintCls MACRO
mov ah, 09h
mov dx, offset linefeed
int 21h
ENDM
Inicio:
mov ax, @data
mov ds, ax
PrintText Text1
mov ah, 0Ah
lea dx, max1
mov size1, offset max1
int 21h
PrintCls
PrintText Text2
mov ah, 0Ah
lea dx, max2
mov size2, offset max2
int 21h
PrintCls
MagicFunction
PrintText TextoEquals
mov ah, 9
lea dx, bx
int 21h
PrintCls
PrintText TextoDiffer
mov ah, 9
lea dx, dx
int 21h
END Inicio
Thank you for including code in your question and tagging it appropriately. It allows me to write a full review this time. Mind you, the other answer is not a half answer as you seem to imply. That answer is a par with your question at the time.
How can I do this? Could I do a macro for that comparison?
The code that I already provided in my previous answer, and that you have adopted, is not really meant to be used as a macro. If anything, it could become a subroutine, but I see no benefit from doing that in your program. Just inline the code.
Inicio: mov ax, @data mov ds, ax
As I wrote in my previous answer, the scasb
instruction uses the ES
segment register. You should set it up the same as the DS
segment register:
Inicio:
mov ax, @data
mov ds, ax
mov es, ax
How buffered input works explains in great detail how the DOS.BufferedInput function 0Ah works.
Where you wrote:
max1 DB 151 ;We add one to 150 for the ENTER charRead1 DB 0
you forgot to reserve room for the actual characters that you would like to receive from the user. Next is how it needs to be:
max1 DB 151
charRead1 DB 0
chars1 DB 151 dup(0)
When you invoke this DOS function 0Ah, you will also receive in the charRead1 byte the length of the actual input. That is the value that you need to store in your size1 variable (not the address of the input structure like your code is doing now!):
mov ah, 0Ah lea dx, max1 mov size1, offset max1 <<<< is wrong int 21h
And of course, you can only do this after the DOS call was made:
mov dx, offset max1 ; Address of the input structure
mov ah, 0Ah
int 21h
mov al, charRead1 ; Length of the string
mov ah, 0 ; Extending is needed because size1 is a word-sized variable
mov size1, ax
Same goes for the second string.
Displaying numbers with DOS explains in great detail how you can print the value from the 16-bit register AX
.
mov ah, 9 lea dx, bx int 21h
When you use the DOS.PrintString function 09h, DOS expects in DX
a pointer to a string of characters. The BX
register in your code holds a number and you still need to convert that into a string of digits. The 'method 2' snippet from the link does that and also displays the characters immediately using the DOS.PrintCharacter
function 02h. Displaying characters with DOS or BIOS has info about many output functions. You only need to insert the code snippet at the ellipses.
mov ax, bx
push bx ;(0)
...
pop bx ;(0)
And since this is my original code, I'll do the copying for you...
mov ax,bx
push bx ;(0)
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
pop bx ;(0)
(0) Preserving BX
is necessary because you need the value a second time to display the number of different characters.
You knew that you didn't have to "calculate" the different characters as you could get the size of the longest string and subtract the number of equal characters. But why didn't you do that subtraction in your program?
mov ax,size2 ;SizeLongestString
sub ax,bx ; minus EqualCharacters
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
So it seems that we have inserted twice the code to convert and display a number. That's a good reason to put it in a subroutine and then just call
it twice. See below.
.model small
.stack 100h
.data
Text1 DB "Please enter the shorter phrase: ",13,10,'$'
Text2 DB "Please enter the longer phrase: ",13,10,'$'
max1 DB 151
charRead1 DB 0
chars1 DB 151 dup(0)
max2 DB 151
charRead2 DB 0
chars2 DB 151 dup(0)
TextoEquals DB "Number of equal characters: ",13,10,'$'
TextoDiffer DB "Number of different characters: ",13,10,'$'
linefeed DB 13, 10, "$"
size1 DW 0
size2 DW 0
.code
AllMacros:
PrintText MACRO str
MOV AH, 09h
LEA DX, str
INT 21h
ENDM
PrintCls MACRO
mov ah, 09h
mov dx, offset linefeed
int 21h
ENDM
Inicio:
mov ax, @data
mov ds, ax
mov es, ax
PrintText Text1
mov dx, offset max1
mov ah, 0Ah
int 21h
mov al, charRead1
mov ah, 0
mov size1, ax
PrintCls
PrintText Text2
mov dx, offset max2
mov ah, 0Ah
int 21h
mov al, charRead2
mov ah, 0
mov size2, ax
PrintCls
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, offset chars1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, offset chars2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
repne scasb
jne NotFound
inc bx ; Increment counter
mov byte ptr [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
PrintText TextoEquals
mov ax, bx
call DisplayAX
PrintCls
PrintText TextoDiffer
mov ax, size2
sub ax, bx
call DisplayAX
mov ax, 4C00h ; DOS.Terminate
int 21h
DisplayAX:
push bx ;(0)
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
pop bx ;(0)
ret
END Inicio
Never forget to exit your program properly. For DOS, the preferred way is to use the DOS.Terminate function 4Ch.
[Recent addition to the question]
I don't want to use string instructions
That's ok for me. Simply replacing the lodsb
and repne scasb
string instructions with equivalent code:
xor bx, bx ; Reset counter
mov si, offset chars1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
mov al, [si] ; Fetch next character from shorter string
inc si
mov di, offset chars2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
Scan:
cmp al, [di]
je Found
inc di
loop Scan
jmp NotFound
Found:
inc bx ; Increment counter
mov byte ptr [di], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
Strictly speaking, it's no longer necessary to set up ES
, but it won't harm the program if you leave it in.