Display integers in aligned columns in MASM Assembly - masm

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?

Related

Assembly Language x86 - Registers Set and Arithmetic and Loop

I am trying to solve this problem about loops. I am using a push and pop method instead of using a separate register to store data.
.model small
.stack
.code
m proc
mov ax,0b800h
mov es,ax
mov di,7d0h
mov ah,7 ; normal attribute
mov al,'A'
mov cx,5
x: stosw
push ax ;mov dl,al ; dl='A'
push di
mov al,'1'
stosw
pop di
add di,158
pop ax ;mov al,dl
inc al
loop x
mov ah,4ch
int 21h
m endp
end m
I am unable to loop the mov al, '1'.
The output should be like this:
A1
B2
C3
D4
E5
Can anyone show the correct code? Thank you.
Consider the ASCII codes involved:
Letter Digit Difference
A1 65 49 16
B2 66 50 16
C3 67 51 16
D4 68 52 16
E5 69 53 16
See how the difference is always 16 ? That's what next solution exploits:
...
mov ax, 0700h + 'A' ; WhiteOnBlack 'A'
x: stosw ; Stores one letter from {A, B, C, D, E}
sub al, 'A' - '1' ; Convert from letter to digit
stosw ; Stores one digit from {1, 2, 3, 4, 5}
add al, 'A' - '1' + 1 ; Restore AL and at the same time increment
add di, 160 - 4 ; Move down on the screen
cmp al, 'E'
jbe x
...
You don't always need to use CX and the LOOP instruction to work with a loop. Anyway the LOOP instruction is to be avoided for speed reasons!

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 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"

Assembly language array multiplication bug using bit shift

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.

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