Assembly language array multiplication bug using bit shift - arrays

I have a bug in one of my loops and I can't fix it. It is part of my HW assignment for school.
I have an array, with 20 elements, and I need to multiply every element by 2, using bit shift.
It kind of works, but every time I have a carry, it is adding 2 to the previous element in the array, instead of one. I can't propagate the carry through the array properly.
This is my first semester with assembly, so I appreciate your help. Also, please keep it simple if you can. Thank you.
This is what I want:
0000000009 ==> 0000000018
0000000099 ==> 0000000198
This is what I am getting.
0000000009 ==> 0000000028
0000000099 ==> 00000002108
Here is the code.
ARR1 DB 20 DUP (0)
MULTIPLYING PROC
MOV AX, 0
MOV CX, 19
.WHILE CX != 0
MOV DI, CX
MOV AL, [DIGIT_ARR1+DI]
;MOV BL, 2
;MUL BL
SHL AX, 1
.IF AX > 9 ; IF THE NEW DIGIT IS LARGER THAN 9
SUB AX, 10
MOV AH, 0
MOV [DIGIT_ARR1+DI], AL
DEC DI
ADD [DIGIT_ARR1+DI], 1
.ELSEIF
MOV [DIGIT_ARR1+DI], AL ; IF IT IS LESS THAN 9, THEN JUST INSERT IT BACK INTO THE ARRAY
.ENDIF
DEC CX
.ENDW
RET
MULTIPLYING ENDP

So it turned that Phil was correct. I was rewriting my own data. The trick was to read the value, do the multiplication using bit shift, add the carry (if any) and only then write back to the array. This way I can multiply each element, by two and not corrupt my data.
NOTE FOR BEGINNERS LIKE ME: bit shift will only multiply by two, so if you need to multiply by something else, use mul or imul. Also, bit shift to the right will divide by two.
The loop below will multiply BCD in array by two. Division works the same way, only make sure you process the array the other way and add 10 to each next digit when you have a carry. You will also have to make sure you don't add the carry once you reach the end of the array. Assembly language doesn't check if you are out of bounds of the array.
MULTIPLYING PROC
PUSH CX
PUSH AX
MOV CARRY, 0 ; START WITH EMPTY CARRY FLAG
MOV CX, 19 ; ARRAY SIZE
.WHILE CX > 0
MOV DI, CX ; GET ELEMENT ADDRESS
MOV AL, [ARR1+DI] ; READ THE ELEMENT
SHL AL, 1 ; DOUBLING THE DIGIT
ADD AL, CARRY ; ADD THE CARRY FLAG
MOV CARRY, 0 ; CLEAR THE CARRY FLAG
.IF AL > 9 ; IF THE NEW DIGIT IS LARGER THAN 9
SUB AL, 10
MOV CARRY, 1 ; SET CARRY FLAG
MOV [ARR1+DI], AL ; INSERTING THE DOUBLED DIGIT BACK TO THE ARRAY
.ELSEIF
MOV [DIGIT_ARR1+DI], AL ; IF IT IS LESS THAN 9, INSERT IT BACK INTO THE ARRAY
MOV CARRY, 0
.ENDIF
DEC CX
.ENDW ; END OF MULTIPLICATION PROC
POP AX
POP CX
RET
MULTIPLYING ENDP

I think the problem is that you're writing back to the same array of addresses that you're reading from. When you carry, it is corrupting the calculation.
e.g.
From: 99
Positon 19 = 9
9*2 = 18 (set position 19 to 8, increment position 18 to 10)
Position 18 = 10
10*2 = 20 (set position 18 to 10, increment position 17 to 1)
Position 17 = 1
1*2 = 2 (set position 17 to 2)
Result: 2108
But you still need to do more work because even with empty destination addresses you get
Positon 19 = 9
9*2 = 18 (set position 19 to 8, increment position 18 to 1)
Position 18 = 9
9*2 = 18 (set position 18 to 8, increment position 17 to 1)
Position 17 = 1
1*2 = 2 (set position 17 to 2)
Result: 288
You need to add the 8 to the 1 at position 18 so you get 9. And you dont do a 3rd multiplication because position 17 is empty in the source address array. I hope this makes sense.
You shouldn't get any digit overflow errors when multiplying by 2 like this, but you may need to handle it when multiplying by larger numbers.

Related

Sorting only odd/even numbers in assembly, how to copy an array without using already allocated memory?

TL;DR: How to copy elements from one array to another (not all but some)?
I want to perform a sorting algorithm on some array, but so that I only sort odd or even numbers in it, depending on a flag. So my idea was to set a flag on either 0 or 1 and then I'd extract all the numbers that have to be sorted into another array and put -1 (as a marker) on the positions where those numbers were. After that I'd just insert the sorted numbers back into the original array, for example:
arr1: 5 8 2 4 13 9 14 10 9
arr2: 8 2 4 14 10
arr2 (sorted): 2 4 8 10 14
arr1 (without even numbers): 5 -1 -1 -1 13 9 -1 -1 9
arr1 (final): 5 2 4 8 13 9 10 14 9
Now while writing this in a higher language is rather trivial for me, doing it in assembly which I'm learning for a class isn't. The part where I sort the numbers works just fine, but the part that causes me problems is the one where I have to copy the numbers into another array. My main method looks like this:
section .data
arr dd 6,8,1,7,4,2,9,107,10,8,12,16,14,5
len equ ($-arr) / 4 - 1
section .bss
section .text
extern sort
global _start
_start:
mov r8, len
mov esi, arr ; esi points to the start of the array/top of it
call sort
mov rax, 60
mov rdi, 0
syscall
And the subroutine which will do the job (showing only the problematic part):
mov r9d, esi
.export:
mov eax, [r9d]
cdq
div dword [divider]
cmp edx, [flag]
je .add
.continue:
add r9d, 4 ; point to the next number
inc dword [i]
cmp r8d, [i]
jg .export
.add:
mov edi, [r9d]
mov dword [r9d], -1 ; put the number in the old array to -1
sub edi, 4
inc dword [lenNewArray] ; inc the total number of elements in new array
jmp .continue
Anyway what I want to accomplish is that I add an element that matches the condition to some location and then point rdi to that location, when I want to add another element I subtract -4 from rdi so it points to the lower location and put that element there, in the end I'd have rdi pointing to the last element which was extracted, is that possible to be done, and is it a bad way?

Assembly x86 putting values into array with loop

I wanted to put numbers in an array of length 10, but each number is 1 bigger than the last number.
It means: myArray = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
I have tried this:
IDEAL
MODEL small
STACK 100h
DATASEG
intArray db 10 dup (0)
index db 1
CODESEG
start:
mov ax, #DATA
mov ds, ax
loopArray:
mov al, [index]
add [intArray+index], al ; here is the problem
inc [index]
cmp [index], 11
jb loopArray
exit:
mov ax, 4c00h
int 21h
END start
But I can't add the index to [intArray + index], so I tried to to add it to [intArray+al], and that doesn't work either.
How can I add the index to the next array's value each time?
myArray = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
These are the numbers that you want your array to contain. But since you initialized the index variable (that you will be using for both indexing and storing) as 1 (using index db 1) this will lead to another result.
Just setup the index with:
index db 0
There's another reason to set it up this way!
In the notation [intArray+index] the index part is meant to be an offset in the array. Offsets are always zero based quantities. The way you wrote your program, it will write the 10th value behind the array.
add [intArray+index], al ; here is the problem
You're right that this is the problem. Some assemblers won't compile this, and others will just add the addresses of both these variables. Neither suits your purpose. What you need is putting the content of the index variable in a register and use that combination of operands.
intArray db 10 dup (0)
index db 0
...
loopArray:
movzx bx, [index]
mov [intArray+bx], bl ;Give the BX-th array element the value BL
inc [index]
cmp [index], 10
jb loopArray
With this code the index will start at 0 and then the loop will continu for as long as the index is smaller than 10.
Of course you could write this program without using an index variable at all.
intArray db 10 dup (0)
...
xor bx, bx ;This make the 'index' = 0
loopArray:
mov [intArray+bx], bl ;Give the BX-th array element the value BL
inc bx
cmp bx, 10
jb loopArray
Given that the array was initially filled with zeroes, you can replace the:
mov [intArray+bx], bl ;Give the BX-th array element the value BL
with:
add [intArray+bx], bl ;Give the BX-th array element the value BL
Do remember this can only work if the array was filled with zeroes beforehand!

Display integers in aligned columns in MASM Assembly

I have been struggling to get this to work for days and have had no luck getting it to work. I would really appreciate some help figuring this out.
(This is in MASM Assembly and the only library allowed is the Irvine Library)
The program is a basic Fibonacci number generator. It takes user input from 1-46 (n)and then prints the Fibonacci sequence up to the nth term.
The output should be 5 terms per row with at least 5 spaces in between. This part works.
Edit* The part I am having issues with is getting the output to line up in columns when the integer length is greater than 7 digits.
I attempted to implement this by passing a TAB to the console which works but only up to 7 digit long integers. I understand why this is but I can't find a way around it. I tried printing spaces of varying length to no avail. My guess is that I either need to count the length of the integer and print the correct number of spaces based on the length after row 7 or use Gotoxy() from the Irvine library but I am having trouble wrapping my head around exactly how.
Here is the output-
1 1 2 3 5
8 13 21 34 55
89 144 233 377 610
987 1597 2584 4181 6765
10946 17711 28657 46368 75025
121393 196418 317811 514229 832040
1346269 2178309 3524578 5702887 9227465
14930352 24157817 39088169 63245986 102334155
165580141 267914296 433494437 701408733 1134903170
1836311903 Press any key to continue . . .
Here is the relevant code...
mov rowPosition,1 ;assign initial row positon
mov currentTerm, 0 ;makes first two terms 1
mov previousTerm, 1
mov ecx, nthTerm ;set counter to nth term
printFib:
mov eax,currentTerm
mov ebx,currentTerm ;mov current term to ebx to hold value
add eax,previousTerm ; = currentTerm + previousTerm
call WriteDec ;writes new term
mov currentTerm,eax ;set newly calculated term to currentTerm
mov previousTerm,ebx ;set previousTerm to equal current Term
mov al, 9 ; ASCII CHAR 9 = TAB
call WriteChar ;writes tab to align text
call WriteChar
cmp rowPosition, 5 ;checks postion in row
jge newRow ;jump if rowPostion greater than or equal to 5
inc rowPosition ;else increment rowPosition
loop printFib ;If ECX!=0 Loop to printFib
jmp goodbye
newRow: call crlf ; move to next row
mov rowPosition, 1 ;reset row postion to 1
loop printFib ;If ECX!=0 Loop to printFib
Can anyone please point me in the right direction?

Assembly Language (Order Taker)

I'm trying hard to study Assembly language.
I really need help to store different 10 items in an array
I want to build a program that will accept items from 10 choices and it will store it.
Those 10 items have different value.
Example
Item 1 = 10$
Item 2 = 4$
So that when the User tries to choose Item1 and Item2 it will show the
sum of both items.
I'll be gladly be happy if someone can share his/her own code that can store 10 items with the sum of all items. Thanks
Here's my code:
_start:
mov eax,3 ;number bytes to be summed
mov ebx,0 ;EBX will store the sum
mov ecx, x ;ECX will point to the current element to be summed
top: add ebx, [ecx]
add ecx,1 ;move pointer to next element
dec eax ;decrement counter
jnz top ;if counter not 0, then loop again
done:
add ebx, '0'
mov [sum], ebx ;done, store result in "sum"
display:
mov edx,1 ;message length
mov ecx, sum ;message to write
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
global x
x:
db 2
db 4
db 3
sum:
db 0
My code here does have problem.
x:
db 2
db 4
db 3
sum:
db 0
Since you've defined all of your variables to be of byte type, you must process them as bytes! You're reading and writing dwords in your program.
This could have been OK:
top: add bl, [ecx]
add ecx, 1 ;move pointer to next element
dec eax ;decrement counter
jnz top ;if counter not 0, then loop again
done:
add bl, '0'
mov [sum], bl ;done, store result in "sum"

nested loop in 8086 assembly language

I am having problem on how exactly should i use loop to get the desire output in this program,
What i want to do is to take input any number from the user and then sort that number in descending order,
I tried my best here to explain every step of the code in the comment.
here is my code,
STSEG SEGMENT
DB 64 DUP(?)
STSEG ENDS
DTSEG SEGMENT
SNAME DB 24 DUP("$")
DTSEG ENDS
CDSEG SEGMENT
MAIN PROC
ASSUME CS:CDSEG, DS:DTSEG, SS:STSEG
MOV AX,DTSEG
MOV DS,AX
MOV ES, AX ;ES:DI
MOV DX, OFFSET STRNG1
MOV AH,09
INT 21H
XOR DX,DX
MOV BYTE PTR SNAME, 40
MOV DX, OFFSET SNAME
MOV AH, 0AH
INT 21H
PUSH DX ;Hold the input number in a stack until we clear the screen and set the cursor
; The clear screen and cursor position code is here which i didn't really mention.
;What we need to do now is to compare first number to each other number and store the greatest
of two on first position.
MOV BX,DX ;Copy un-sorted number to BX,
MOV AX,BX[1] ;the length of the number which is stored on the first position of the string
XOR AH,AH ;Empty AH
MOV CL,AL ;MOVE AL into CL for looping 6 times
SUB CL,1
MOV SI,02H ;the number is stored in string array from the 2nd position
;Suppose the input number is the following,
;[6][3][9][1][8][2][6]
;this is how it should work,
; Loop 6 times , CX = 6
[7][6][3][9][1][8][2][6] ; 7 is length of the number which is already copied in CX above.
; but we need 6 iterations this is why we subtract 1 from CL above.
; 6 > 3 ?
; Yes, then BX[SI] = 6 and BX[SI+1] = 3
; 6 > 9 ?
; NO, then BX[SI] = 9 and BX[SI+2] = 6
; 9 > 1
; Yes, then BX[SI] = 9 and BX[SI+3] = 1
; 9 > 8
; Yes, then BX[SI] = 9 and BX[SI+4] = 8
; 9 > 2
; Yes, then BX[SI] = 9 and BX[SI+5] = 2
; 9 > 6
; Yes, then BX[SI] = 9 and BX[SI+6] = 6
; After first iteration the incomplete sorted number is,
;[9][3][6][1][8][2][6]
;Similarly here i need to loop 5 times now by comparing the 2nd number which is a 3 with all ;the number after it and then loop 4 times then 3 times then two times and then 1 time.
L1:
;Loop 1 must iterate 6 time for the supposed input number,
;but i couldn't be able to write the proper code as i always get out of registers. kindly help me out
L2:
LOOP L2
Loop L1
Kindly help me with the nested loop where i've stucked.
The loop instruction uses the cx register.
So you must either preserve cx for the outer loop with e.g. push cx (before L2:) and pop cx (after loop L2):
mov cx,5
L1:
push cx
mov cx,6
L2:
. . . do stuff inner
loop L2
pop cx
. . . do stuff outer
loop L1
or remember that loop is roughly equal to dec cx jnz, so do e.g.
mov dx,5
L1:
mov cx,6
L2:
... do stuf inner
loop L2
.. do stuff outer
dec dx
jnz L1
Possible off-by-one errors are intended and meant as an exercise for the reader :-)

Resources