I want to get a number (i.e 5) from the user and then print starting from 1 to < input (i.e 1 2 3 4)
But my code does not stop in "4" rather than the loop runs till "d"
I know that loop runs CX times
and as in 8086 MOVZX does not work that is why at first I moved AL to CL then zeroed the CH.
As someone mentioned that the problem is as I am moving AL to CX I'm not moving the value 4, I'm moving 34(ASCII value of 4) and so my loop runs 34 times.
Now how do I convert my user input value to decimal and move that to CX. Is there any way to take user input that will be stored in AL as decimal value?
org 100h
MOV AH, 1 ; Get user input
INT 21H
DEC AL ; Dec AL to satisfy the condition that it will print till < input
MOV BL,31H ; Initialize BL so that the output starts printing from 1
MOV CL,Al ; set counter register CX
MOV CH,00
Print:
MOV AH, 2 ; for output printing
MOV DL,0DH ; for output printing
INT 21H ; for output printing
MOV DL,0AH ; for output printing
INT 21H ; for output printing
MOV AH,2
MOV DL,BL ; print what is in BL
INT 21H
INC BL ; then increment BL
LOOP Print ; supposed to run the loop on Print what is the value in CL times
hlt
MOV AH, 1 ; Get user input
INT 21H
If you input 5 then the AL register will hold the number 35h which is the ASCII code of that key. You clearly want what that key represents which is 5. You need to subtract 30h (48).
mov ah, 01h ; DOS.GetKey
int 21h
sub al, '0'
dec al
mov cl, al
mov ch, 0
The rest of the program is fine for printing starting from 1 to < input.
Now how do I convert my user input value to decimal and move that to CX.
You've fallen into the trap of forgetting that loop conditions other than }while(--cx) are possible, using instructions other than loop.
loop is just a peephole optimization for dec cx / jnz (without affecting FLAGS). Only use it when that's actually the most efficient way to loop. (Or just never use it at all, because you need to understand conditional branches anyway so omitting loop is one fewer instruction to learn about / remember. Also, on most modern x86 CPUs, loop is much slower than dec/jnz. It's good if tuning for real 8086, or optimizing for code-size over speed, though. But only necessary as an optimization.
The easiest and most logically clear way to write this loop is:
MOV AH, 1 ; read a char from stdin into AL
INT 21H
mov cl, al ; ending character
mov bl, '1' ; b = current character, starting with '1'
.top: ; do {
... print CR/LF (not shown)
mov dl, bl
int 21h ; With AH=2 from printing CR/LF
inc bl ; b++
cmp bl, cl
jbe .top ; }while(b <= end_char);
Notice that I increment after printing. If you increment before printing, you'd use jb for }while(b < end_char).
On a real 8086, where loop is efficient, this does have more instructions and more code bytes inside the loop, and thus could be slower (if we consider a case where loop overhead matters, not with 3x slow int 21h system calls inside the loop).
But that trades off against smaller total code size (from the trivial loop setup). So it's a tradeoff between static code size vs. dynamic instruction count (and amount of code bytes that need to be fetched, which was the real issue on 8086).
Related
.data
.code
main proc
mov cx,5
user_input:
mov ah,1h
int 21h
push dx
loop user_input
mov cx,5
system_output:
mov ah,2h
int 21h
pop dx
loop system_output
endp
I want to write a code that gets 5 numbers from the User and print all five of them in reversed order
my code
the program gets 5 numbers but does nothing and dies.
and I still cant find my numbers in Stack,
and I put the screen shot below.
The AH=1 function gives you a character in the AL register. You should use push AX in the first loop! (instead of push dx)
In the second loop you should use pop DX before doing function AH=2.
And best end your program with:
mov ax, 4C00h
int 21h
I am trying to find the smallest value in a given array, and the code I used is this
MOV SI,500
MOV CL,[SI]
MOV CH,00
INC SI
MOV AL,[SI]
DEC CL
INC SI
L1:CMP AL,[SI]
JNC SKIP
MOV AL,[SI]
SKIP:INC SI
LOOP L1
MOV [600],AL
HLT
It compiles fine and runs fine, then I go to "aux" and then to "memory" to type in my input values. I set the address as 0100:0500 and then I give input "01" "02" "03" "04" "05" then against the first row, like this -- https://i.imgur.com/Lrg23B2.png and i click update, and then "RUN" and then go to aux-->memory and check the address 0600, which is where i guess the output would be, and i get just zeroes, like this --- https://i.imgur.com/z2CCtBA.png what is wrong with my code? why am i not getting the minimum value and just zeroes in 0600? I am a total beginner to the 8086 programming, please help.
Don't you need setting the DS segment register to 0100h on top of your program?
You clearly expect to address memory at 0100h:0500h.
Don't you have to use the hexadecimal suffix?
MOV SI,500 uses a decimal 500; you need MOV SI,0500h for hexadecimal!
Note: If you're using a debugger then the hexadecimal notation could well be the default. If you're using a normal assembler then using the correct prefix or suffix is essential.
The loop runs for much too long!
MOV SI,500
MOV CL,[SI] <<<< If THIS reads 01 as is expected...
MOV CH,00
INC SI
MOV AL,[SI]
DEC CL <<<< then THIS will produce 0
INC SI
L1:CMP AL,[SI]
JNC SKIP
MOV AL,[SI]
SKIP:INC SI
LOOP L1 <<<< So THIS runs 65536 times.
MOV [600],AL
HLT
In order to find the minimum you will have to change the jnc skip instruction to jb skip. Currently you're looking for the maximum.
This is a version that you could try. As always: don't just copy but try to understand how it works.
mov ax, 0100h
mov ds, ax
mov si, 0500h
mov cx, 5 ;The number of values in the array
mov al, 255 ;This will become the mininum
L1:
cmp al, [si]
jb SKIP
mov al, [si]
SKIP:
inc si
loop L1 ;This now runs 5 times.
mov [0600h], al
hlt
It would be a good idea to try the code with data that is a bit more random. Perhaps use 3, 2, 5, 1, 4.
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.
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]
I've got a problem looping in assembly language.
When we want to use the counter register for looping in nested loop,
what we first do is move the value of counter register into the stack for outer loop and then fetch it back when we're done with the inner loop, this way we're capable of using one counter register for looping into the nested loop with different number of iteration on each loop.
But what about nested loop inside a nested loop?
I want to print a pyramid made of of the character S. What I am getting is,
SSSSSSSSSS
SSSSSSSSS
SSSSSSSS
SSSSSSS
SSSSSS
SSSSS
SSSS
SSS
SS
S
What I actually want is,
SSSSSSSSSS
SSSSSSSS
SSSSSS
SSSS
SS
S
Here is my code for the program
MOV BX,10 ; HOLD 10 IN BX FOR INNER LOOP
MOV AX,0 ; START ITERATIONS FROM 0
MOV CX,10 ; MAX NUMBER OF ITERATIONS
L2:
PUSH CX ;PUSH CX IN A STACK
MOV CX,BX ;STORE NEW VALUE IN CX FOR INNER LOOP ITERATION
L1:
MOV DX, [SI] ; MOVE THE STRING INTO DX
MOV AH,02H ; DISPLAY EVERYTHING FROM DX
INT 21H
LOOP L1
MOV DX,0AH ;PRINT LINE FEED AFTER PRINTING EACH LINE OF ASTERIKS
MOV AH,02H
INT 21H
SUB BX,01 ;DECREASE THE VALUE OF BX BY 1
POP CX ;RESTORE ORIGINAL VALUE OF CX FOR OUTER LOOP
ADD AX,01 ;INCREMENT VALUE OF AX BY 1
LOOP L2
MOV AH, 4CH ;RETURN CONTROL TO DOS
INT 21H
In order to achieve what I want, i need to add another loop inside the nested loop that prints space characters (i.e 020H). But for this I need another counter register and I am not able to do it. How can I solve this problem?
You are already doing what needs to be done in the given ASM. You can push the current value of CX to the stack (save it) and pop it later on to restore it. You would need to do this when you require additional nesting.
In the code JohnB provided he simply added in a loop to print out the spaces before printing the asterisks. No additional nesting is required, meaning it is fairly straight forward.
It's a bit like this:
For each line
Print an incrementing number of spaces
Print a decrementing number of asterisks
Repeat
Which is exactly what JohnB has shown you.
About like that?
L2:
PUSH CX ;PUSH CX IN A STACK
; insert this
MOV CX, 10 ; width of your tree = 10
SUB CX, BX ; subtract length of "s" string
SHR CX, 1 ; divide CX by 2 => number of spaces at the beginning
JCXZ endL3 ; no spaces? don't do anything
L3:
MOV DX, 20H ; space character
MOV AH,02H ; print space
INT 21H
LOOP L3
endL3:
MOV CX,BX ;STORE NEW VALUE IN CX FOR INNER LOOP ITERATION
L1:
MOV DX, [SI] ; MOVE THE STRING INTO DX
MOV AH,02H ; DISPLAY EVERYTHING FROM DX
INT 21H
LOOP L1
MOV DX,0AH ;PRINT LINE FEED AFTER PRINTING EACH LINE OF ASTERIKS
MOV AH,02H
INT 21H
SUB BX,01 ;DECREASE THE VALUE OF BX BY 1
POP CX ;RESTORE ORIGINAL VALUE OF CX FOR OUTER LOOP
ADD AX,01 ;INCREMENT VALUE OF AX BY 1
LOOP L2
Btw, for what purpose are you initializing and incrementing AX? You overwrite it anyway when moving data to AH/AL.