I am trying to learn TASM Assembly and i need your help.
I made this code which introduces a vector from the keyboard and than it displays it on the screen with the elements sum. The problem is that when it displays it displays some weird characters but the sum works.
Hope you can help me
TITLE vectors
.model small
.stack 100H
.data
msg1 db 10,13,"Enter the lenght of the vector$"
msg2 db 10,13,"Enter the vector elements $"
msg4 db 10,13,"The sum is $"
msg3 db 10,13,"The entered vector is $"
msg5 db " $"
vector db 0
sum db 0
x db 0
.code
main PROC
MOV ax,#data
MOV ds,ax
MOV ah,9h
LEA dx, msg1
int 21h
MOV ah,1h
int 21h
LEA Si,vector
MOV cl , al
MOV x,al
SUB cl , 30h
MOV sum , 0
Introducere:
MOV ah, 9h
LEA dx, msg2
int 21h
MOV ah,1h
int 21h
SUB al,30h
MOV [Si] , al
ADD Si, 1
ADD suma,al
DEC cl
JNZ Introducere
JZ Afisare1
Afisare1:
MOV ah,9h
LEA dx, msg3
int 21h
MOV cl,x
SUB cl,30h
LEA Si,vector
JMP Afisare2
Afisare2:
MOV dx,[Si]
ADD dx,30h
MOV ah,2h
int 21h
LEA dx,msg5
int 21h
INC Si
DEC cl
JNZ Afisare2
JZ Afisare3
Afisare3:
MOV ah,9h
LEA dx,msg4
int 21h
MOV dl,sum
ADD dl,'0'
MOV ah,2h
int 21h
MOV ah,04ch
int 21h
main ENDP
END main
The problem is that when it displays it displays some weird characters but the sum works.
These weird characters come from the ommission of the required function number for displaying your msg5. Currently, instead of showing a nice separating space, you get output of the low byte from msg5's address.
MOV ah,2h
int 21h
LEA dx,msg5
<<<<< Here is missing `mov ah, 09h`
int 21h
vector db 0
sum db 0
x db 0
With this definition of vector you reserve just 1 byte to store your inputs. That's not enough! Since your entire program works with single digit numbers, the length of the vector could range from 1 to 9. Therefore you need to make this change:
vector db 9 dup (0) ;This reserves 9 bytes
sum db 0
x db 0
Since your entire program works with single digit numbers and that you output the sum as a single digit number also, you can not be too generous with the values for the inputted numbers. An example that will work follows:
Enter the lenght of the vector3 <<<<< You're missing a space character here!
Enter the vector elements 2
Enter the vector elements 5
Enter the vector elements 1
The entered vector is 2 5 1
The sum is 8
Related
I was trying to find the nth Fibonacci number e.x n=3, output = 1
so my logic was this
a = 0
b = 1
input n
si 0
n>2
loop
temp = b
b = a+b
a = b
loop if si/=cx
print b
This is my pseudo code logic. When I tried to implement this I am stuck in an infinite loop
.MODEL SMALL
.STACK 100h
.DATA
STRING0 DB 'Enter INDEX $'
.CODE
MAIN PROC
MOV AX,#DATA
MOV DS,AX
LEA DX, STRING0
MOV AH,9
INT 21H
MOV AH, 2
MOV DL,0AH ;NEW LINE
INT 21H
MOV DL,0DH
INT 21H
MOV AH,1
INT 21H
SUB CX,CX
MOV CL,AL
MOV SI,0
MOV AX,0
MOV BX,1
LOOP1:
PUSH BX
ADD BX,AX
POP AX
INC SI
LOOP LOOP1
MOV DX,BX
MOV AH,9
INT 21H
MAIN ENDP
END MAIN
I use EMU 4.08. The code us stuck at an infinite loop. I have no idea why
I did SUB cx,cx to move the AL value to CL and use CL as counter otherwise it gives me error that the code failed to send 8bit data to 16bit
I was trying to find the nth Fibonacci number e.x n=3, output = 1
From your example I understand that you consider the Fibonacci sequence to begin with 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
Fibonacci himself started his sequence from 1, 2, 3, 5, 8, ...
See the Wikipedia article https://en.wikipedia.org/wiki/Fibonacci_number
I didn't follow your pseudo code too much as it has flaws of its own!
Why your assembly program fails
You say your "code is stuck at an infinite loop", but that's not really the case. It is just that your loop executes an extra 50 iterations. The reason is that the DOS.GetCharacter function 01h gives you an ASCII code, that you have to convert into the digit that the pressed key represents. eg. If you press 3, DOS gives you AL=51, and you need to subtract 48 to obtain the inputted digit which is 3.
But wait, don't use this number 3 as your loop counter already! Since the 1st and 2nd Fibonacci numbers are known from the start, calculating the 3rd Fibonacci number requires just 1 iteration of the loop. Account for this and subtract 2 beforehand.
Once your program has found the answer you simply move the result from BX to DX, and expect the DOS.PrintString function 09h to output the number. It can't do that. It's a function that outputs a series of characters (so a string beginning at the address in DX), however your result is still a number in a register. You have to convert it into its textual representation. Displaying numbers with DOS has all the fine details about this conversion!
Next code allows the user to input a single-digit from 1 to 9
...
mov ah, 01h ; DOS.GetCharacter
int 21h ; -> AL = ["1","9"]
sub al, 48 ; -> AL = [1,9]
cbw ; -> AH = 0
mov cx, ax ; -> CX = [1,9]
xor ax, ax ; -> AX = 0
dec cx
jz PrintIt ; 1st Fib is 0
inc ax ; -> AX = 1
dec cx
jz PrintIt ; 2nd Fib is 1
cwd ; -> DX = 0
CalcIt: ; 3rd Fib and others
xchg ax, dx
add ax, dx
loop CalcIt
PrintIt: ; AX is at most 21 (because of the limited input)
aam
add ax, 3030h ; Conversion into text
xchg al, ah
cmp al, '0'
mov dh, 02h ; DOS.PrintCharacter
xchg ax, dx
je Ones
int 21h
Ones:
mov dl, dh
int 21h
Because in your program the output is very limited, I used a special code to display at most 2 digits. For the general case of outputting numbers see this Q/A.
I think this should be good:
.MODEL SMALL
.STACK 100h
.DATA
STRING0 DB 'Enter INDEX $'
STRING1 DB 'OUTPUT: $'
.CODE
MAIN PROC
MOV AX,#DATA
MOV DS,AX
LEA DX, STRING0
MOV AH,9
INT 21H
MOV AH, 2
MOV DL,0AH ;NEW LINE
INT 21H
MOV DL,0DH
INT 21H
MOV AH,1
INT 21H
SUB CX,CX
SUB AL,30H ;To convert char into digit value
MOV CL,AL
MOV SI,0
MOV AX,0
MOV BX,1
LOOP1:
PUSH BX
ADD BX,AX
POP AX
INC SI
LOOP LOOP1
MOV AH, 2
MOV DL,0AH ;NEW LINE
INT 21H
MOV DL,0DH
INT 21H
LEA DX, STRING1
MOV AH,9
INT 21H
;Print the result
MOV AX,BX
MOV SI,0
;Push digits from right to left into stack
LOOP2:
MOV DL,10
DIV DL
PUSH AX
MOV AH,0
INC SI
CMP AL,0
JNE LOOP2
;Pop digits from stack and print them
LOOP3:
POP DX
MOV DL,DH
MOV DH,0
ADD DL,30H ;To convert digit to char
MOV AH,2
INT 21H
DEC SI
CMP SI,0
JNE LOOP3
HLT
MAIN ENDP
END MAIN
I'm experimenting with my program by trying to build different kinds of pyramids and converting the values into different values. I managed to build one with asterisk sign and now I'm trying to find a way on how to change it into running numbers like "0123456789" and the next line is "012345678" and so on. Is there a way to do that without fully/less changes of my code?
.MODEL SMALL
.STACK 100H
.DATA
STAR DB ?
BLANK DB ?
.CODE
MAIN PROC
MOV AX,#DATA
MOV DS,AX
MOV CX,10
MOV BH,10
MOV BL,0
MOV STAR,BH
MOV BLANK,BL
L1:
CMP BLANK,0
JE L2
MOV AH,2
MOV DL,32
INT 21H
DEC BLANK
JMP L1
L2:
MOV AH,2
MOV DL,'*'
INT 21H
DEC STAR
CMP STAR,0
JNE L2
MOV AH,2
MOV DL,0AH
INT 21H
MOV DL,0DH
INT 21H
DEC BH
MOV STAR,BH
INC BL
MOV BLANK,BL
LOOP L1
EXIT:
MOV AH,4CH
INT 21H
MAIN ENDP
END MAIN
I'm trying to find a way on how to change it into running numbers like "0123456789" and the next line is "012345678" and so on. Is there a way to do that without fully/less changes of my code?
While the other answer provides an alternative solution, my solution keeps most of your program intact as per request, only adding the digit sequence:
L2:
mov dl, '0' <--- Setup 1st digit
L3: <--- Extra label
MOV AH,2
INT 21H
inc dl <--- To next digit
DEC STAR
CMP STAR,0 ; Tip: you can remove this instruction
JNE L3 <--- To extra label
Expected output (similar to your asterisks):
0123456789
012345678
01234567
0123456
012345
01234
0123
012
01
0
Consider a simplier algorithm which uses DOS function Int 21h/AH=09h (which displays $-terminated strings). You may shorten the string by overwriting characters at its end with '$' in each loop cycle:
.MODEL SMALL
.STACK 100H
.DATA
EOL DB 13,10,'$' ; End of line string.
TXT DB '0123456789$' ; The initial string.
.CODE
MAIN PROC
MOV AX,#DATA
MOV DS,AX ; Initialize DS to .DATA segment.
MOV AH,9 ; Use DOS function WRITE STRING TO STANDARD OUTPUT.
MOV BX,10 ; Initialize the number of iteration (address index).
L1: MOV DX,OFFSET EOL
INT 21H ; Write EOL first.
MOV [TXT+BX],'$' ; Terminate TXT at index BX.
MOV DX,OFFSET TXT
INT 21H ; Write TXT.
DEC BX ; Let BX index the previous character.
JNZ L1 ; Loop while BX > 0.
EXIT:MOV AH,4CH
INT 21H
MAIN ENDP
END MAIN
Result should be this:
0123456789
012345678
01234567
0123456
012345
01234
0123
012
01
0
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 ...
I'm having a hard time with this homework assignment. I need to print my array showing that the numbers I pulled from ReadInt are in it. I'm not sure how to do that.
Here is my code so far.
.data
intarray DWORD ?
finish BYTE "Please enter a EVEN number less than 50. ", 0dh, 0ah, 0
finish2 BYTE "The entered array is: ", 0dh, 0ah, 0
finish3 BYTE "The sum of the first half of the array is: ", 0dh, 0ah, 0
finish4 BYTE "Please enter your numbers: ", 0dh, 0ah, 0
.code
main proc
mov edx, OFFSET finish
call WriteString
call crlf
call ReadInt
add intarray, eax
call crlf
mov edx, OFFSET finish4
call WriteString
call crlf
mov ecx,intarray
L1:
call ReadInt
mov intarray, eax
inc intarray
loop L1
mov edx, OFFSET finish2
call WriteString
call crlf
invoke ExitProcess,0
main endp
end main
I need to print my intarray to show that the number's entered by ReadInt are in it. I'm not sure how to do this.
Next code does what you need. The code was made with EMU8086 : it reads 10 numbers as strings (because data comes from keyboard), convert every string to number, insert them into the array, clears the screen, and displays the numbers in the array, converting every number to string (to display them). It's plenty of comments to help you understand (hope this helps you) :
.stack 100h
;------------------------------------------
.data
;------------------------------------------
array dw 10 dup(?)
msj db 13,10,'Enter a number: $'
str db 6 ;MAX NUMBER OF CHARACTERS ALLOWED (5).
db ? ;LENGTH (NUMBER OF CHARACTERS ENTERED BY USER).
db 6 dup (?) ;CHARACTERS ENTERED BY USER.
buf db 6 dup('$') ;WILL HOLD NUMBERS WITH 5 DIGITS OR LESS.
crlf db 13,10,'$'
count db ? ;JUST A COUNTER FOR LOOPS.
;------------------------------------------
.code
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;------------------------------------------
;CAPTURE 10 NUMBERS FROM USER.
mov di, offset array ;POINTER TO ARRAY.
mov count, 10 ;WE WILL CAPTURE 10 NUMBERS.
ten_numbers:
;DISPLAY MESSAGE.
mov ah, 9
mov dx, offset msj
int 21h
;CAPTURE NUMBER AS STRING.
mov ah, 0Ah
mov dx, offset str
int 21h
;CONVERT CAPTURED NUMBER FROM STRING TO NUMERIC.
mov si, offset str ;PARAMETER FOR STRING2NUMBER.
call string2number ;NUMBER RETURNS IN BX.
;INSERT NUMBER INTO ARRAY.
mov [ di ], bx ;NUMBER GOES IN CURRENT POSITION (DI).
add di, 2 ;ARRAY POSITION FOR THE NEXT NUMBER. MUST BE
;2 BECAUSE IT'S AN ARRAY OF DW (DW = TWO BYTES).
;REPEAT PROCESS.
dec count
jnz ten_numbers ;IF ( COUNT != 0 ) JUMP.
;------------------------------------------
;DISPLAY THE NUMBERS IN ARRAY.
call clear_screen
mov di, offset array
mov count, 10 ;ARRAY HAS 10 NUMBERS.
print_array:
;CONVERT NUMBER TO STRING TO DISPLAY IT.
call dollars ;FILLS BUF WITH DOLLARS (REQUIRED TO DISPLAY).
mov ax, [ di ] ;MOVE CURRENT NUMBER TO AX.
call number2string ;TAKES AX AS PARAMETER.
;STRING RETURNS IN VARIABLE BUF.
;DISPLAY STRING.
mov ah, 9
mov dx, offset buf
int 21h
;DISPLAY LINE BREAK.
mov ah, 9
mov dx, offset crlf
int 21h
;REPEAT PROCESS.
add di, 2 ;NEXT NUMBER IN ARRAY TO BE DISPLAYED. MUST BE
;2 BECAUSE IT'S AN ARRAY OF DW (DW = TWO BYTES).
dec count
jnz print_array ;IF ( COUNT != 0 ) JUMP.
;------------------------------------------
;WAIT FOR USER TO PRESS ANY KEY.
mov ah,7
int 21h
;------------------------------------------
;FINISH THE PROGRAM.
mov ax, 4c00h
int 21h
;==========================================
;NUMBER TO CONVERT MUST ENTER IN AX.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (BUF).
proc number2string
mov bx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov dx, 0 ;NECESSARY TO DIVIDE BY BX.
div bx ;DX:AX / 10 = AX:QUOTIENT DX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp ax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
mov si, offset buf
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
endp
;------------------------------------------
;CONVERT STRING TO NUMBER IN BX.
;SI MUST ENTER POINTING TO THE STRING.
proc string2number
;MAKE SI TO POINT TO THE LEAST SIGNIFICANT DIGIT.
inc si ;POINTS TO THE NUMBER OF CHARACTERS ENTERED.
mov cl, [ si ] ;NUMBER OF CHARACTERS ENTERED.
mov ch, 0 ;CLEAR CH, NOW CX==CL.
add si, cx ;NOW SI POINTS TO LEAST SIGNIFICANT DIGIT.
;CONVERT STRING.
mov bx, 0
mov bp, 1 ;MULTIPLE OF 10 TO MULTIPLY EVERY DIGIT.
repeat:
;CONVERT CHARACTER.
mov al, [ si ] ;CHARACTER TO PROCESS.
sub al, 48 ;CONVERT ASCII CHARACTER TO DIGIT.
mov ah, 0 ;CLEAR AH, NOW AX==AL.
mul bp ;AX*BP = DX:AX.
add bx,ax ;ADD RESULT TO BX.
;INCREASE MULTIPLE OF 10 (1, 10, 100...).
mov ax, bp
mov bp, 10
mul bp ;AX*10 = DX:AX.
mov bp, ax ;NEW MULTIPLE OF 10.
;CHECK IF WE HAVE FINISHED.
dec si ;NEXT DIGIT TO PROCESS.
loop repeat ;COUNTER CX-1, IF NOT ZERO, REPEAT.
ret
endp
;------------------------------------------
;FILLS VARIABLE BUF WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THESE STRINGS WILL BE DISPLAYED.
proc dollars
mov si, offset buf
mov cx, 6
six_dollars:
mov al, '$'
mov [ si ], al
inc si
loop six_dollars
ret
endp
;------------------------------------------
proc clear_screen
mov ah,0
mov al,3
int 10H
ret
endp
Maybe it's important to explain a little the variable str, notice the three level format, it's using 3 DBs because ah=0Ah (capture string) requires it : the first DB must indicate the max number of characters allowed, a second DB is necessary to store the length, and the third DB are the characters themselves. The string ends with ENTER (character 13), that's why the first DB is 6 (if user enters 5 characters, the sixth is ENTER).
I'm new at programming assembly. Now I'm trying to write a program that converts number from decimal to binary. But I got stuck with one program while trying to input. After i output msg2 and get into loop, program doesn't turn off. I can input a lot of numbers and program doesn't turn off. I guess problem is in convertnumber: cmp si,cx (si is how many numbers I have to input, cx- how many numbers I have already written), but I am not sure about that. Where have I made a mistake and how could I correct it?
.MODEL small
.Stack 100h
.DATA
msg0 db 'how many numbers will include your input number(example. 123 is 3 numbers)? $'
msg1 db 'Now input number from 0 to 65535: $'
number db 255, ?, 256 dup ('$')
numberinAscii db 255, ?, 256 dup ('$')
enterbutton db 13,10,'$'
.CODE
start:
mov ax, #data
mov ds,ax
mov ah,09h
mov dx, offset msg0 ; first message output
int 21h
xor ah,ah ; function 00h of
int 16h ; int 16h gets a character (ASCII translation in AL)
int 3
mov bl,al
mov dl,al
mov ah,02h ; function 02h - display character
int 21h ; call DOS service
mov ah,09h
mov dx, offset enterbutton
int 21h
mov ah, 09h
mov dx, offset msg1 ; output second message
int 21h
jmp covertHowMany ; converting number that we entered
next:
xor si,si
mov si, ax ; number that we entered now is in si
xor cx,cx
mov cx,0 ;cx=0
enterfirstnumber: ;entering first number (example 123, first number is 1)
xor ah,ah
int 16h ; int 16h gets a one character
int 3
mov bl,al
mov dl,al
mov ah,02h ; function 02h - display character
int 21h ;
jmp convertnumber ; converting this number
input: ;converting number from ascii char to ascii integer
mov ax,bx
mov dx,10
mul dx ; ax:=ax*10
mov bx,ax ; number that I try to convert is in bx now
xor ah,ah
int 16h ; int 16h gets a character (ASCII translation in AL)
int 3
mov bl,al
mov dl,al
mov ah,02h ; function 02h - display character
int 21h
jmp convertnumber
convertHowMany:
sub al,30h ; convert from ascii character to ascii number
jmp next
convertnumber:
sub al,30h
add bx,ax
inc cx
cmp cx, si
jne input
jmp ending
ending:
mov ax,04C00h
int 21h
end start
I see at least two problems with your code:
The first is that when you reach convertHowMany you assume that AL still contains the character that the user typed in. That will not be the case, since both INT 21h/AH=02h and INT 21h/AH=09h modify AL. You'll have to save and restore the value of AL somehow (e.g. by pushing and popping AX).
The second problem is how you initialize SI before the loop. You're moving the value of AX into SI, which means both AL and AH. AH is not zero at that point, because you've just used INT 21h/AH=09h.
You could change the sequence xor si,si / mov si,ax into something like mov si,ax / and si,0FFh.