How to print an integer stored in a variable - x86-16

So I have a program in 8086 assembly that allows the user to enter 2 digits, store them in a variable and then print out the number:
data segment
broj db ?
ends
stack segment
dw 128 dup(0)
ends
code segment
mov ax, data
mov ds, ax
mov es, ax
mov ah, 1h
int 21h
sub al, 48d
mov bl, 10d
mul bl
mov broj, al
mov ah, 1h
int 21h
sub al, 48d
add broj, al
mov dl, broj
sub dl, 48d
mov ah, 2h
int 21h
mov ax, 4c00h
int 21h
ends
However whenever I enter a number for example 21 it doesn't give me the number instead it gives me ASCII Code for that value.
Can anyone help?!

However whenever I enter a number for example 21 it doesn't give me the number instead it gives me ASCII Code for that value.
If you feed (input) your program a number that consists of 2 digits, then you'll have to print also 2 digits! Currently your code contains just the one character output function.
First divide the number in broj by 10 giving a quotient (in AL) and a remainder (in AH).
Convert the quotient to character (Add 48) and print it.
Convert the remainder to character (Add 48) and print it.
Example:
mov al, broj
mov ah, 0
mov bl, 10
div bl
add ax, "00"
mov dx, ax
mov ah, 02h
int 21h
mov dl, dh
mov ah, 02h
int 21h

Related

Sum of two positive numbers is a negative

So I'm creating a simple 8086 program that calculates the sum of two two digit numbers:
.model small
.data
first db 0
second db 0
sum db 0
.stack
.code
mov ax, data
mov ds, ax
mov es, ax
mov ah, 1
int 21h
sub al, 48d
mov bl, 10
mul bl
mov first, al
mov ah, 1
int 21h
sub al, 48d
add first, al
mov ah, 1
int 21h
sub al, 48d
mov bl, 10
mul bl
mov second, al
mov ah, 1
int 21h
sub al, 48d
add second, al
mov al, first
add al, second
mov sum, al
However when I check the value of the numbers specifically the sum it gives me a negative number as it's signed value. For example if the first number is 99 and the second one is 98 the sum is -59.
Does anyone have an answers that might fix this problem?
Since you used a byte for each number, every number can have 2^8 = 256 different values. So signed numbers must be between -128 and 127. When addition result get larger than 127, it represent it as corresponding negative number. You should use word(16-bit) or more to solve this problem.

Printing individual characters from a string to standard output in ms-dos/assembly x86-16

I am new to x86-16bit programming. I am very stumped by how I call function 2 properly to read characters individually from a string. If anybody has any ideas it would be greatly appreciated. The code below shows my current attempt (one of many).
.model small
.data
message db "Hello, DOS Here!", 0dh, 0ah
.code
main proc
mov ax, #data
mov ds, ax
L1:
mov ah, 2
mov bx, 1
int 21h
loop L1
.EXIT
main endp
end main
I'm also supposed to use push and pop to be able to print the string in the same method but backwards. I'm sure I'm missing something obvious. All I get when it prints is the British pound symbol for a few lines.
(Dec: 156; Hex: 9C)
With this definition message db "Hello, DOS Here!", 0dh, 0ah, next code will print the complete message:
mov ah, 02h
mov dl, message ;Get the H character
int 21h
mov dl, message+1 ;Get the e character
int 21h
mov dl, message+2
int 21h
mov dl, message+3
int 21h
mov dl, message+4
int 21h
mov dl, message+5
int 21h
mov dl, message+6
int 21h
mov dl, message+7
int 21h
mov dl, message+8
int 21h
mov dl, message+9
int 21h
mov dl, message+10
int 21h
mov dl, message+11
int 21h
mov dl, message+12
int 21h
mov dl, message+13
int 21h
mov dl, message+14
int 21h
mov dl, message+15
int 21h
mov dl, message+16 ;Get the 0Dh carriage return
int 21h
mov dl, message+17 ;Get the 0Ah linefeed
int 21h
Rather stupid, wouldn't you say?
In order to use a loop and obtain much compacter code we need to:
Put the address of your message in an address register. You can choose from SI, DI, BX and BP. I've picked BX in below code.
Read one byte at this address.
Output the byte with DOS.
Increment the pointer in the address register.
Repeat steps 2, 3, 4, 5 for all the text by checking if it was the linefeed character that you just outputted. Since in your text that's the very last character.
A version of this loop:
mov bx, OFFSET message ;1.
Again:
mov dl, [bx] ;2.
mov ah, 02h ;3.
int 21h
inc bx ;4.
cmp dl, 0Ah ;5.
jne Again
I'm also supposed to use push and pop to be able to print the string in the same method but backwards.
To accomplish this next task of yours, you would put a push dx in between steps 2. and 3. Then write an additional loop to display the reversed string:
Again2:
pop dx
mov ah, 02h
int 21h
cmp dl, "H"
jne Again2
The pitfalls here will be that
you shouldn't output the linefeed and carriage return before the other (real) characters ==> Hint: do 2 pops before starting the 2nd loop
you can't readily assume that "H" will only appear as the text's first character ==> Hint: count the number of pushs
Caveat: I haven't done this in decades and I don't have a compiler in front of me that can do this. This looks like an assignment so I'm not going to write the code, but I'll point you in the right direction.
Int 21h function 2 requires you to set dl equal to the character you want to output. No pushes and pops required here since DOS doesn't use the stack for parameter passing. First, outside of your loop, you want to point a register to the address of the message, something like: MOV si, message. Then, you need to dereference a single byte from the message and put it into dl. This will require using indirect address notation. Then you can call interrupt 21h and write the character.
As for the loop, there are a few ways to approach it. You could use a counter, with cx being the typical register for that, and use a LOOP statement or DEC and JNZ statements. Or you could put a marker at the end of the string, and terminate the loop when you hit that marker. A null character is commonly used for this. You will also need to move to the next character in the string. You can do that by adding an index, or by incrementing the value of the si register each iteration of the loop. Something like INC si
With all that said, there is a much easier way that avoids the loop but maybe it is cheating. Take a look at interrupt 21h function 9
Yeah, using function 9 would be 'cheating' as it is one of the other exercises (which was the easy one). I have revised my code and got it to start with the beginning character. I just need to figure out how to get it to keep looping so that it continues to read the string and not stop arbitrarily. Here's what I've got so far (It prints H and the 15 e's):
.model small
.data
message db "Hello, DOS Here!"
.code
main proc
mov ax, #data
mov ds, ax
mov ah, 2
mov cx, 16
mov dl, message
L1:
int 21h
mov dl, message + 1
loop L1
.EXIT
main endp
end main

Why does the following 8086 assembly code only display numbers up to 2559?

What am I trying to do ?
I want to get a 16-bit number from the user and print It on the console.
What Is the problem ?
Well my code works fine If the number entered Is less than 2600 but the moment I enter 2600 It displays "40" and for 2601 "41" and so on. Shouldn't It display numbers up to 65535 ? Because I am storing the value In the bx register which Is 16-bit and which can store at most 65535. Then why only 2559 ?
My code:
.model small
.data
msg db 10,13,'Enter a 16bit number: $'
newline db 10,13,'$'
.code
main:
mov ax, #data
mov ds, ax
mov cl, 10
mov bx, 0
mov ah, 09h
lea dx, msg
int 21h
call get16bitNum
mov ah, 09h
lea dx, newline
int 21h
mov ax, '$'
push ax
mov ax, bx
store:
div cl
cmp al, 0
mov bh, 0
mov bl, ah
push bx
je continue
mov ah, 0
jmp store
continue:
pop ax
cmp ax, '$'
je ext
mov bx, ax
mov ah, 02h
mov dx, bx
add dx, 48
int 21h
jmp continue
ext:
mov ah, 04ch
int 21h
proc get16bitNum
aggregate:
mov ah, 01h
int 21h
cmp al, 13
je return
mov ah, 0
sub al, 48
mov dx, bx
mov bx, ax
mov ax, dx
mul cl
add bx,ax
jmp aggregate
return:
ret
endp
end main
You don't actually retrieve a 16-bit number!
You keep the desired input in BX, and so you need to multiply the whole of BX by 10. You do this using a word sized multiplication.
proc get16bitNum
aggregate:
mov ah, 01h
int 21h
cmp al, 13
je return
mov ah, 0
sub al, 48 ;AX is 0 to 9
xchg ax, bx ;New digit temporarily in BX
mov cx, 10
mul cx ;Product is in DX:AX, but we won't use DX!
add bx ,ax ;Add new digit
jmp aggregate
return:
ret
You don't display the 16-bit number
The procedure to convert the number into text will definitely need to use the word sized division.
For an excellent explanation on how to do this see this recent post Displaying numbers with DOS. It explains in great detail everything you need to know about converting numbers. It even uses the same technique of pushing some value in the stack (You used a $ character for this) to know where the number ends.
ps. If you find the info in the linked post useful don't hesitate to upvote it. (Of course I hope you find my answer useful too!)
8 bit div produces 8 bit quotient and remainder.
When you divide 2600 by 10 you get an 8 bit quotient, losing higher bits.
You should use 16 bit division.

Algorithm to assembly x86?

I have been for some days trying to translate an algorithm to assembly x86, and I did it. However I would like to print the final result that it is saved in "tmp", what instruction can I use? (I'm Spanish so I'm sorry if I say something wrong in English).
This is my algorithm:
tmp = NOT(L0)
tmp = tmp AND L1
tmp = NOT(NOT(tmp) OR NOT(L2))
tmp = NOT(tmp OR NOT(L3))
tmp = NOT(tmp + NOT(L4))
if (tmp == L5)
licence = correct
else
licence = incorrect
And this is it in assembly:
LicenceCorrect PROC
push ebp
mov ebp,esp
push ebx
push ecx
push edx
mov ebx, [ebp+8]
mov edx,[ebx]
mov ecx,edx
not ecx
mov edx,[ebx+4]
and ecx,edx
mov edx,[ebx+8]
not edx
not ecx
or ecx,edx
not ecx
mov edx,[ebx+16]
not edx
or ecx,edx
not ecx
;if
mov edx,[ebx]
cmp ecx,edx
jne cons
mov al,0
jmp next
cons:
mov al,1
next:
pop edx
pop ecx
pop ebx
pop ebp
ret
LicenceCorrect ENDP
END
Next code displays a number in AX (made with EMU8086). What MissPaper must do now is insert your procedure (LicenseCorrect) at the end of next code, and call it after "call dollars", then assign the value to AX (remove "12345").
Here it is for 32 bits:
.model small
.stack 100h
.data
buffer db 6 dup(?)
.code
start:
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;FIRST, FILL BUFFER WITH '$' (NECESSARY TO DISPLAY).
mov si, offset buffer
call dollars
;SECOND, CONVERT NUMBER TO STRING.
mov ax, 12345
mov si, offset buffer
call number2string
;THIRD, DISPLAY STRING.
mov dx, offset buffer
call printf
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;-----------------------------------------
;PARAMETER : DX POINTING TO '$' FINISHED STRING.
printf proc
mov ah, 9
int 21h
ret
printf endp
;------------------------------------------
;FILLS VARIABLE WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
;PARAMETER : SI = POINTING TO STRING TO FILL.
dollars proc
mov cx, 6
six_dollars:
mov bl, '$'
mov [ si ], bl
inc si
loop six_dollars
ret
dollars endp
;------------------------------------------
;CONVERT A NUMBER IN STRING.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (STR).
;PARAMETERS : AX = NUMBER TO CONVERT.
; SI = POINTING WHERE TO STORE STRING.
number2string proc
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.
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
number2string endp
end start
Now the 64 bits version (for much bigger numbers in EAX), made with GUI Turbo Assembler x64 (http://sourceforge.net/projects/guitasm8086/):
.model small
.586
.stack 100h
.data
buffer db 11 dup(?)
.code
start:
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;FIRST, FILL BUFFER WITH '$' (NECESSARY TO DISPLAY).
mov esi, offset buffer
call dollars
;SECOND, CONVERT NUMBER TO STRING.
mov eax, 1234567890
mov esi, offset buffer
call number2string
;THIRD, DISPLAY STRING.
mov dx, offset buffer
call printf
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;-----------------------------------------
;PARAMETER : DX POINTING TO '$' FINISHED STRING.
printf proc
mov ah, 9
int 21h
ret
printf endp
;------------------------------------------
;FILLS VARIABLE WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
;PARAMETER : ESI = POINTING TO STRING TO FILL.
dollars proc
mov cx, 11
six_dollars:
mov bl, '$'
mov [ esi ], bl
inc esi
loop six_dollars
ret
dollars endp
;------------------------------------------
;CONVERT A NUMBER IN STRING.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (STR).
;PARAMETERS : EAX = NUMBER TO CONVERT.
; ESI = POINTING WHERE TO STORE STRING.
number2string proc
mov ebx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov edx, 0 ;NECESSARY TO DIVIDE BY EBX.
div ebx ;EDX:EAX / 10 = EAX:QUOTIENT EDX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED (DL) FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp eax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ esi ], dl
inc esi
loop cycle2
ret
number2string endp
end start
I didn't add your procedure because it uses the stack and I don't know what values to push before calling it.

Assembler (TASM x64) arrays and elements

I have an array of nine names:
.model tiny
.data
vardas1 db "Rokas",0ah,'$'
vardas2 db "Tomas",0ah,'$'
vardas3 db "Matas",0ah,'$'
vardas4 db "Domas",0ah,'$'
vardas5 db "Augis",0ah,'$'
vardas6 db "Vofka",0ah,'$'
vardas7 db "Marka",0ah,'$'
vardas8 db "Auris",0ah,'$'
vardas9 db "Edvis",0ah,'$'
vardai dw offset vardas1, offset vardas2, offset vardas3, offset vardas4, offset vardas5, offset vardas6, offset vardas7, offset vardas8, offset vardas9
.code
org 100h
I need to read a digit from keyboard, and then I need to print that name. For example I will push 5, and console should write "Augis". BTW, second code block aren't all code, just loop that doesn't work
paieska:
mov dx, offset _comment1 ; Just string name asking user to input digit
mov ah, 9
int 21h
mov j, 00h ; Trying to input the digit from keyboard
mov ah, 01h
mov dl, 0ah
int 21h
mov bx, offset vardai ; Add array "names" to bx register
add bx, cx ; Add cx for indexing
mov dx, [bx] ; Add first array element to dx register
add cx, 2 ; Increasing cx by 2, because I'm using data word not data byte
mov ah, 9 ; Try to print it
int 21h
cmp cx, j ; Try to compare cx (index of array) to mine inputed digit "j"
jne paieska
je end
mov ah, 01h
mov dl, 0ah ;NO NEED FOR THIS - INT21/01 DOES NOT USE DL
int 21h
MOV AH, '1' ; MIN INPUT CHAR
mov bx, offset vardai ; Add array "names" to bx register WELL, ASSIGN ACTUALLY
MOV CX,2 ;NUMBER OF BYTES TO ADD (WORDS, NOT BYTES)
LOOPN:
mov dx, [bx] ; name-pointer array element to dx register
CMP AH,AL ; MATCHING char?
JE PNAME ; YES, PRINT NAME
add bx, cx ; Add cx=2 for next name
inc AH ; next possible character input
CMP AH,'9'+1 ; allowed is '1'..'9'
jne loopn ; in allowed range
; input not 1..9
mov dx, offset errormessage
PNAME:
mov ah, 9 ; Try to print it
int 21h
jmp end
Well, I tried to edit your approach with CAPS, but it became too complicated.
Essentially, you are reading a character from the keyboard using function 01. This character arrives in AL. If all goes well, it should be '1'..'9'. Notice these are the ASCII characters '1'..'9', that is hex 31..39
Next step is to set BX to the start of the table, AH to the minimum character you anticipate and CX to 2 because the table contains words, not bytes.
Now we have a loop. Load X from the table, and check whether AL is equal to AH. If the user input 1, these will be equal, so go print the string.
Otherwise, add 2 to BX to point to the next entry in the table (this could have been done by ADD BX,2 or INC BX INC BX which would mean the MOV CX,2 would be unnecessary - just the way I wrote it...) and increment the '1' in AH to '2'.
The end-condition for the loop is when AH gets incremented from '9' to - well, ':' or '9'+1. If it hasn't reached that end-condition, then run around the loop until all of the values '1'..'9' have been tested. If you haven't got to PNAME yet, then there's an error because the character input wasn't allowed, so point to an error message and then print it.
Now jumping to the end - probably you'd want to terminate the program, so you'd execute
MOV AH,4CH
INT 21H

Resources