Assembly 8086 loops issue [duplicate] - loops

This question already has an answer here:
Problems with IDIV Assembly Language
(1 answer)
Closed 1 year ago.
The pseudocode is the following:
read c //a double digit number
for(i=1,n,i++)
{ if (n%i==0)
print i;}
In assembly I have written it as:
mov bx,ax ; ax was the number ex.0020, storing a copy in bx.
mov cx,1 ; the start of the for loop
.forloop:
mov ax,bx ; resetting ax to be the number(needed for the next iterations)
div cx
cmp ah,0 ; checking if the remainder is 0
jne .ifinstr
add cl 48 ;adding so my number would be displayed later as decimal
mov dl,cl ;printing the remainder
mov ah,2
int 21h
sub cl,48 ;converting it back to hexa
.ifinstr:
inc cx ;the loop goes on
cmp cx,bx
jle .forloop
I've checked by tracing its steps. The first iteration goes well, then, at the second one, it makes ax=the initial number and cx=2 as it should, but at 'div cx' it jumps somwhere unknown to me and it doesn't stop anywhere. It does:
push ax
mov al,12
nop
push 9
.
.
Any idea why it does that?

try to do mov dx,0 just before div instruction.
Basically every time you come after jump, there may be some data in dx register, so you can just move zero in dx or XOR dx,dx.
This is to be done, because otherwise division will be considered differently.
See this:
Unsigned divide.
Algorithm:
when operand is a byte:
AL = AX / operand
AH = remainder (modulus)
when operand is a word:
AX = (DX AX) / operand
DX = remainder (modulus)
Example:
MOV AX, 203 ; AX = 00CBh
MOV BL, 4
DIV BL ; AL = 50 (32h), AH = 3
RET

Related

how to check if 2 numbers have reversed bits in assembly?

My code should get 2 arr with the same length(k) and to check how much pairs of numbers have the same index (1 in arr 1 and the other in arr2) and are opposite, which means the first bit in 1 should be the last in the other and the second to first would be the second to first and go on...
Overall, this is my code:
IDEAL
MODEL small
STACK 100h
DATASEG
k dw 2 ;length of arr1 and arr2
ARR1 dw 2 dup (?)
ARR2 dw 2 dup (?)
CODESEG
start:
mov ax,#data
mov ds,ax
lea si,[ARR1]
lea di,[ARR2]
mov cx,k ; cx=length of arr
xor dx,dx
mov [ARR1],1000000000000001b
mov [ARR2],1000000000000001b
mov [ARR1+1],0033h
mov [ARR2+1],0033h
xor dx,dx
L1: ; loops through every index in both arr1, and arr2
mov bx,k
sub bx,cx
push cx
mov cx,16
L2:
xor ax,ax
shr [si+bx],1
jnc nc1
inc al
nc1:
clc
shl [di+bx],1
jnc nc2
inc ah
nc2:
clc
cmp al,ah
jne end_loop2
dec cx
jnz L2
inc dx
end_loop2:
pop cx
dec cx
jnz L1
exit:
mov ax, 4c00h
int 21h
END start
My debugger doesn't give me any errors but when I run the code it doesn't work, when I shift left the number in arr 2 it doesn't change the CF though it should.
Do you know why is that happening?
mov [ARR1],1000000000000001b
mov [ARR2],1000000000000001b
mov [ARR1+1],0033h
mov [ARR2+1],0033h ; ARR1/ARR2 contain 1, 51, 0, ?
Your program defines 2 arrays that have each 2 word-sized elements. Because a word occupies 2 bytes in memory, assigning a value to the 2nd element must use an offset of +2. Assigning a value to the 3rd element would have to use an offset of +4, and so on.
mov [ARR1], 8001h
mov [ARR2], 8001h
mov [ARR1+2], 0033h
mov [ARR2+2], 0033h ; ARR1/ARR2 contain 1, 128, 51, 0
L1:
mov bx,k
sub bx,cx
The inner loop (L2) is processing words but the outer loop (L1) advances through the array per byte. On the 1st outer iteration CX is 2 so BX = k - CX becomes 0, and on the 2nd outer iteration CX=1 so BX = k - CX becomes 1 which then will begin processing a word composed of the high byte from the 1st array element together with the low byte from the 2nd array element.
The good news is that you don't need that convoluted way (using BX) to walk through these arrays. Just add 2 to SI and DI on every iteration of the outer loop.
Your program contains a number of redundant instructions like xor dx, dx and unnecessary instructions like clc. For clarity you should remove these unproductive lines.
to check how much pairs of numbers have the same index (1 in arr 1 and the other in arr2) and are opposite
Knowing that the arrays hold each 2 elements, this means that the final result of your program will have to be a number in the range [0,2].
Without the forementioned errors your program would have worked fine, except that a solution that wipes out the arrays is not something I would have chosen.
Below is my implementation. Read the tail comments carefully!
lea si, [ARR1] ; SI is address of 1st array
mov [si], 8001h ; Assign 1st element
mov [si+2], 0033h ; Assign 2nd element
lea di, [ARR2] ; DI is address of 2nd array
mov [di], 8001h ; Assign 1st element
mov [di+2], 0033h ; Assign 2nd element
mov bp, k ; Number of array elements
xor dx, dx ; Final result (will be 1 based on the fixed data)
L1:
mov cx, [di] ; CX is current element from 2nd array
mov bx, [si] ; BX is current element from 1st array
xor ax, ax ; AL is status byte, AH is a convenient 0
L2:
shr bx, 1 ; The bit that comes out of BX
adc al, ah ; is added to AL (that was zeroed beforehand)
shl cx, 1 ; The bit that comes out of CX (at the opposite side)
sbb al, ah ; is subtracted from AL
jnz NOK ; If both bits matched then AL would still be zero
test bx, bx ; Has BX more ON bits ?
jnz L2 ; Yes
; Early exiting if BX has no more ON bits
; If, at the same time, CX too has no more ON bits
; then an OK pair was found
test cx, cx
jnz NOK
OK:
inc dx ; Found an OK pair
NOK:
add si, 2 ; Next array element is 2 bytes higher in memory
add di, 2
dec bp ; Repeat for every element in the array(s)
jnz L1
If implemented serially, the inner loop can be squeezed at least to
again:
add ax,ax ; shift ax, while moving MSB to carry
sbb bx, 0 ; subtract carry (i.e 0 or 1) from bx
; here the LSB of bx will be 0 afterwards,
; iff the top bit of ax == lsb of bx
; in this case the top 15 bits of bx will
; be preserved for further iterations
shr bx, 1 ; now we shift out the LSB, setting CF on failure
jc not_ok
jnz again ; there are further bits on bx to check
;
test ax,ax ; we need to check that ax == 0, when bx == 0
jnz not_ok ;
ok: ; there was full match
I think a separate early exit on AX would not make much sense, but one could sort the inputs, placing BX <= AX, so BX would run out of bits sooner.
Using the parity flag as in how-to-exchange-between-2-bits-in-a-1-byte-number, one can simplify the inner loop greatly:
ok = (bitreverse(al) == bh) && (bitreverse(bl) == ah)
With the following implementation one does not even need any other registers, than ax = input0, bx = input1.
test al, 0x81
jpe 1f // parity even IFF al == 0x81 OR al == 0
xor al, 0x81 // toggle bits 0x80 and 0x01, if there was only 1 bit
1: test al, 0x42
jpe 2f
2: xor al, 0x42
test al, 0x24
jpe 3f
3: xor al, 0x24
test al, 0x18
jpe 4f
xor al, 0x18
4: cmp al, bh
jnz NOT_OK
// and same for the pair bl, ah

Assembly Language code to count different characters but when any counter exceeds 9, it prints different symbols instead of that specific number [duplicate]

I've written a pretty simple code in asm x8086 and I'm facing an error. If anyone could help me with a brief explanation I would greatly appreciate it.
IDEAL
MODEL small
STACK 100h
DATASEG
; --------------------------
array db 10h, 04h, 04h, 04h, 04h, 04h, 04h, 04h, 04h, 04h
sum db 0
; --------------------------
CODESEG
start:
mov ax, #data
mov ds, ax
; --------------------------
xor cx, cx
mov al, 0
mov bx, offset array
StartLoop:
cmp cx, 10
jge EndLoop
add al, [bx]
add [sum],al
inc cx
inc bx
jmp StartLoop
EndLoop:
mov ah, 09h
int 21h
; --------------------------
exit:
mov ax, 4c00h
int 21h
END start
With the correction for the add to be replaced by mov as noted in your comment (Note that the line: add al, [bx] is actually mov al, [bx]) there's just the function call at the label EndLoop that's wrong!
You want to display the sum, and are using the DOS print function. This function 09h expects a pointer in DS:DX that you are not providing!
Even if you did, you would still have to convert the sum number in its text representation.
A quick solution here would be to content yourself and just display the result in the form of a single ASCII character. The hardcoded sum is 52 and so it is a displayable character:
EndLoop:
mov dl, [sum]
mov ah, 02h ;Single character output
int 21h
; --------------------------
exit:
mov ax, 4c00h
int 21h
One step further and we can display "52":
mov al,[sum]
mov ah,0
mov dl,10
div dl ---> AL=5 AH=2
add ax,3030h ---> AL="5" AH="2"
mov dh,ah ;preserve AH
mov dl,al
mov ah,02h
int 21h
mov dl,dh ;restore
int 21h
I don't see any error at all, the code will sum the array, display some random sh*t, and exit.
You probably want to display result of sum?
int 21h, ah=9 will display '$' terminated string from memory pointed to by dx.
So you need two things, convert the number in [sum] to string terminated by'$' at end, and then set dx to the converted string ahead of that int 21h.
You may try to extract number2string procedure from here: https://stackoverflow.com/a/29826819/4271923
I would personally change it to take the address of target buffer in si as another call argument (ie. just remove the mov si,offset str from the procedure body). Like this:
PROC number2string
; arguments:
; ax = unsigned number to convert
; si = pointer to string buffer (must have 6+ bytes)
; modifies: ax, bx, cx, dx, si
mov bx, 10 ; radix 10 (decimal number formatting)
xor cx, cx ; counter of extracted digits set to zero
number2string_divide_by_radix:
; calculate single digit
xor dx, dx ; dx = 0 (dx:ax = 32b number to divide)
div bx ; divide dx:ax by radix, remainder will be in dx
; store the remainder in stack
push dx
inc cx
; loop till number is zero
test ax, ax
jnz number2string_divide_by_radix
; now convert stored digits in stack into string
number2string_write_string:
pop dx
add dl, '0' ; convert 0-9 value into '0'-'9' ASCII character encoding
; store character at end of string
mov [si], dl
inc si
; loop till all digits are written
dec cx
jnz number2string_write_string
; store '$' terminator at end
mov BYTE PTR [si],'$'
ret
ENDP
Then to call this at your EndLoop you need to add into data segment numberStr DB 8 DUP (0) to have some memory buffer allocated for string and add into code:
; load sum as 16b unsigned value into ax
xor ax,ax ; ax = 0
mov al,[sum] ; ax = sum (16b zero extended)
; convert it to string
mov si,OFFSET numberStr
call number2string
; display the '$' terminated string
mov dx,OFFSET numberStr
mov ah,9
int 21h
; ... exit ...

Transform string into number (array size), input of that array and output it back. ASM

In this programm i struggle to convert string into number. I have spent around 10 hours with this code already (yeah, I'm a novice) and I have a strong feeling I'm so close to the point when it's gonna work... Please, prove me right or prove me wrong! =) I tried to add as many comments as possible in the most complicated (as for me) parts of the code so you can save your time in case you have a mood to help me out a little. I'll be glad to explaine code even more if neccessary. Sorry for my English and let's see the code.
model tiny
.code
org 0100h
start:
mov ax,cs
mov ds,ax
mov dx,offset text
mov ah,09h
int 21h
mov dx,offset x
mov ah,3fh
int 21h
mov dh,byte ptr[x+di] ; Loading first digit into elem.
sub dh,48
mov ah,2
CH2INT:mov elem,dh
_TEST: push di
OFFST1:mov cl,ah
dec cl
add di,di
loop OFFST1
mov dh,byte ptr[x+di] ; Loading next (x+di*2) digit into dh.
pop di ; Inc ah for the next iteration.
inc ah ; Here we see if next char is a digit or not,
cmp dh,"0" ; if it is (meaning that current digit isn't in the lowest position) -
jb INVAL ; multiplying current digit by 10 (our base),
cmp dh,"9" ; if not - jump to INVAL where we go on to the next digit (if there is any).
ja INVAL ; Push-pop ax just for convinience, I'm almost out of free-to-use registers.
push ax
mov al,elem
mov ten,10
imul ten
mov elem,al
pop ax
jmp _TEST
INVAL: mov al,elem ; Adding number we got to the sum which in the end (after jump to NEXT)
add sum,al ; will containe inputt size of array as a number, not a char.
push di
OFFST2:mov cl,ah
dec cl
add di,di
loop OFFST2
mov dh,byte ptr[x+di] ; Move next digit into dh (x+ah(wich was incremented earlier)+di).
pop di ; See if there is a next digit in x or previous one was the last one
cmp dh,"0" ; (I just hope that next value in memory after the inputt will not be a digit...
jb NEXT ; I understand that it's not nice and a luck game, but I do not yet come up with smthn better).
cmp dh,"9"
ja NEXT
push di ; Next few lines write next char into dh (but with index less by 1 than in previous
; few lines, where we checked is there any more digits in the inputt value left.
OFFST3:mov cl,ah
sub cl,2
add di,di
loop OFFST3
mov dh,byte ptr[x+di]
pop di
sub dh,48
jmp CH2INT ; Jump to next iteration to work with next digit in the inputt value
NEXT: mov dx,offset array
mov ah,3fh
int 21h
mov cl,sum
L: mov dh,byte ptr[array+di]
inc di
mov dl,dh
mov ah,02h
int 21h
loop L
mov ah,4Ch ; Service 4Ch - Terminate with Error Code
mov al,0 ; Error code
int 21h ; Interrupt 21h - DOS General Interrupts
ret
array db 256 dup (?) ; Array
x db ? ; Size of the array
ten db ?
text db "Enter number of elements in array: $"
elem db ? ; A single digit from the inputt value
sum db 0 ; Inputt value transformed into number
end start
Code written in TASM as a .com executable. It has some irrational things here and there but it's just for the clarity and due to the fact it's just a draft. Hugely appreciate your help! Thanks!
P.S. I'll be modifying the code over time in case you'll give me some advise.

Assembly Array and loop

I've a problem in assembly language that I want to make loop for sum element of an array. Suppose an array contains 10,20,30,40,50,60,70,80,90,100 I have to sum all elements of the array by loop... How can I do this?
I'm trying this:
.MODEL SMALL
.STACK 100H
.DATA
W DW 10,20,30,40,50,60,70,80,90,100
.CODE
MAIN PROC
MOV AX, #data
MOV DS, AX
XOR AX, AX
XOR BX, BX
MOV CX, 10
ADDNOS:
ADD AX, W [BX]
ADD BX, 2
LOOP ADDNOS
;this for display
MOV DX, AX
MOV AH,2
INT 21H
MOV AH, 4CH
INT 21H
MAIN ENDP
END MAIN
but something wrong in display that print from ascii (&).
EDIT: Updated answer since the code in the question has been changed:
INT 21h / AH=2 prints a single character (note that the integer 1 and the character '1' are different values).
The sum of the elements in your array is 550, which requires 3 characters to print. The way to solve that is to write a routine that converts the value 550 to the string "550", and then use INT 21h / AH=9 to print that string. How you'd go about doing that has been asked several times before on StackOverflow; see e.g. this question and the answers to it.
This is my answer for the original question
For future questions, note that "but something wrong" is a terrible problem description. You should explain precisely in what way the code isn't behaving the way you intended.
That said, there are a number of problems with your code:
Here you're initializing CX to the first value in x. Actually, since the elements in x are bytes (because you used DB) and CX is a word (two bytes) you'll get CX = 301h (which is 769 in decimal):
MOV CX, x
Here you're simply moving the first element of x into BX over and over, instead of doing an addition. And again, x contains bytes while BX is a word register.
top: MOV BX, [x]
The loop instruction decrements CX by 1 and jumps to the given label if CX != 0. By incrementing CX before the loop you're creating an infinite loop. Also, the CMP is useless (and I'm not sure why you're comparing against 7 since x only has 5 elements):
INC CX
CMP CX, 7
loop top
This will only work for values in the range 0-9. If the sum is >=10 it will require multiple characters. See e.g. this answer for an example of how to convert a multi-digit number to a string that can be printed. Also, you're writing a word-sized register to a byte variable:
ADD BX, '0'
MOV [sum], BX
Here I'm a bit lost at what you're trying to do. If you wanted to write a single character to STDOUT you should use INT 21h / AH = 2 / DL = character. Note that MOV AX,4 sets AH=0 and AL=4. Also, you should end your program with INT 21h / AX = 4C00h:
display_:
MOV DX,1
MOV CX, sum
MOV BX, 1
MOV AX,4
INT 21h
MOV AX, 1
INT 21h
I suspect that there is an error in the code following the top label.
You do MOV BX, [x] but I think there you should sum the item pointed by CX with what currently is in BX (that seems to store the sum). So substitute the move instruction with:
ADD BX, [CX]

Wrong result in multiplying in 8086 assembly

The program must calculate the following:
in VP array , each index will have the value of Multiplying V1[INDEX] WITH V2[INDEX]
in VPROD array , the program ADDS all the Values of Array VP
Example:
VP = { V1[0]*V2[0] , V1[1]*V2[1] , V1[2]*V2[2] , V1[3]*V2[3] ,V1[4]*V2[4] }
VPROD = { VP[0] + VP[1] + VP[2] + VP[3] + VP[4] }
The problem is that when the result is bigger than 16 bits (a word), it does not give the right results. But when the result is smaller than 16 bits, it gives the right result.
I thought about Carry and fixed my code, but still it gives me bad results.
My code:
dseg segment
v1 DW 255,5,255,9,21
v2 DW 4,4,255,13,5
vprod DW 10 DUP (0)
vp DW 10 DUP (?)
N DB 5
dseg ends
sseg segment stack
dw 100h dup(?)
sseg ends
cseg segment
assume ds:dseg,cs:cseg,ss:sseg
start: mov ax,dseg
MOV DS,AX
MOV SI,0
MOV CX,0
MOV CL,N
MOV DI, 0
LOVP: MOV AX,v1[si]
MUL v2[si]
MOV vp[di],AX
MOV vp[di+2],DX
ADD SI,2
ADD DI,4
LOOP LOVP
MOV CX,0
MOV CL,N
MOV SI,0
MOV DI,0
LOVPROD: MOV AX,vp[SI]
ADD vprod[DI],AX
ADC VPROD[DI+2],0
ADD SI,2
LOOP LOVPROD
SOF:
mov ah,4ch
int 21h
cseg ends
end start
Since you are getting a 32 bit result from the multiply, you should use 4 bytes for every element of the vp array. You will need to use a separate index for that, and of course store the high word at offset +2 instead of +1. For the summation part, you should add up both low and high words.
Fixed code for the multiplication could look like:
MOV DI, 0
LOVP: MOV AX,v1[si]
MUL v2[si]
MOV vp[di],AX
MOV vp[di+2],DX
ADD SI,2
ADD DI,4
LOOP LOVP
I trust you can do the summation yourself, using ADD and ADC.
UPDATE:
Since your post says you want to sum the vp array, I don't see why you expect the result in another array (vprod). The sum is a scalar value, do something like:
MOV SI, 0
MOV AX, 0
MOV DX, 0
LOVPROD: ADD AX,vp[SI]
ADC DX,vp[SI+2]
ADD SI,4
LOOP LOVPROD
The result will be in DX:AX
Also, for the LOOP to work properly, you should put the number of iterations into CX, not 0 as you are doing.

Resources