I am in the process of writing a byte sized poly derivative app in MASM x8086 and it has to be able to receive negative coefficients.
I understand that binary can be represented in signed and unsigned form. But I am looking for a way to receive a signed integer so that I can avoid another array. Or is there a way to define my variable as a signed integer?
Below is my integer input procedure.
TEN db 10 ;;; constant
num db ? ;;; coefficient
;;; bh is the degree
get_int PROC
lea si, string ; replaces the ? in the string with the degree
add si, 13h
mov dl, bh
add dl, 30h
mov [si], dl
mov ah, 09h
lea dx, string ; prompt
int 21h
mov ah, 0Ah
lea dx, buffString ; user input
int 21h
lea si, buffString ; point to count byte
inc si
mov ch, 00h ; cx = count
mov cl, [si]
add si, cx ; si points to end of string
mov dl, 00h ; hold result(dl)
mov bl, 01h ; hold 10^x (bl)
loop1:
mov al, [si] ; prep for char ---> number conversion
cmp al, '-'
je negativeSign
sub al, 30h ; convert
mul bl ; ax = al*bl
add dl, al ; sum of results
mov al, bl ; preload for instruction
mul TEN ; TEN is variable predefined as 10
mov bl, al
jmp overNegative
negativeSign:
mov dh, 00h
mov [si], dh
overNegative:
dec si
loop loop1 ; loop instruction uses cx as index counter once zero breaks
mov num, dl
ret
get_int ENDP
; output is num
When, while interpreting the input, you stumble upon the "-" character it is a save assumption that you reached the start of the number. Therefore you should bail out of the loop. I don't see any point in replacing the "-" character by a zero!
What you do need to do is negate the number to obtain the correct signed result:
loop1:
mov al, [si] ; prep for char ---> number conversion
cmp al, '-'
je negativeSign
sub al, 30h ; convert
mul bl ; ax = al*bl
add dl, al ; sum of results
mov al, bl ; preload for instruction
mul TEN ; TEN is variable predefined as 10
mov bl, al
dec si
loop loop1 ; loop instruction uses cx as index counter once zero breaks
mov num, dl ;Positive number [0,127]
ret
negativeSign:
mov dh, 00h <<<<<<< Need this as a flag?
neg dl
mov num, dl ;Negative number [-128,-1]
ret
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 have already written the code to add TWO arrays and Store the result in 3rd array. But the problem occurs while handling the NEGATIVE SIGN Numbers to display with (-) sign. Follow are the code listed below while subtracting the 6th element of array1 with array 2, result is GARBAGE value Need assistance immediately. After running executing's the code, all signed values are not displaying correctly.
org 100h
Array1 db 1,3,2,2,2,2,2,2,2,2
Array2 db 4,5,6,7,8,9,0,1,2,3
Array3 db 10 dup (?)
lea dx, msg1
mov ah, 9
int 21h
mov cx, 10
mov bx, 0
L1001:
mov al, Array1 [bx]
; Extend (unsigned) AL to AX (to print)
mov ah, 0
call printd
mov ah, 2
mov dl, 09 ;TAB Character
int 21h
inc bx
loop L1001 ;End Loop1
mov ah,2
mov dl,10
int 21h
mov dl,13
int 21h
; print msg2
lea dx, msg2
mov ah, 9
int 21h
; Use loop to print values of Array2
mov cx, 10
mov bx, 0
L1002:
mov al, Array2 [bx]
; Extend (unsigned) AL to AX (to print)
mov ah, 0
call printd
mov ah, 2
mov dl, 09 ;TAB Character
int 21h
inc bx
loop L1002 End Loop2
mov ah,2
mov dl,10
int 21h
mov dl,13
int 21h
; print msg3
lea dx, msg3
mov ah, 9
int 21h
mov cx,10
mov bx, 0
L1003: ; Main Addition
mov al, Array1 [bx]
sub al, Array2 [bx]
mov Array3 [bx], al
; Extend (unsigned) AL to AX (to print)
mov ah, 0
call printd
mov ah, 2
mov dl, 09 ;TAB Character
int 21h
inc bx
loop L1003 ; End of lOOP3
lea dx, pkey
mov ah, 9
int 21h ; output string at ds:dx
; wait for any key....
mov ah, 1
int 21h
mov ax, 4c00h ; exit to operating system.
int 21h
printd proc
; preserve used registers
push ax
push bx
push cx
push dx
; if negative value, print - and call again with -value
cmp ax, 0
jge L1
mov bx, ax
; print -
mov dl, '-'
mov ah, 2
int 21h
; call with -AX
mov ax, bx
neg ax
call printd
jmp L3
L1:
; divide ax by 10
; ( (dx=0:)ax / cx(= 10) )
mov dx, 0
mov cx, 10
div cx
; if quotient is zero, then print remainder
cmp ax, 0
jne L2
add dl, '0'
mov ah, 2
int 21h
jmp L3
L2:
; if the quotient is not zero, we first call
; printd again for the quotient, and then we
; print the remainder.
; call printd for quotient:
call printd
; print the remainder
add dl, '0'
mov ah, 2
int 21h
L3:
; recover used registers
pop dx
pop cx
pop bx
pop ax
ret
printd endp
printud proc ;Print Undecimal Numbers
push ax
push bx
push cx
push dx
mov dx, 0
mov cx, 10
div cx
cmp ax, 0
jne L4
add dl, '0'
mov ah, 2
int 21h
jmp L5
L4:
call printud
add dl, '0'
mov ah, 2
int 21h
L5:
pop dx
pop cx
pop bx
pop ax
ret
printud endp ;
ret
msg1 db "Array 1 = $"
msg2 db "Array 2 = $"
msg3 db "Array 3 = : $"
pkey db "press any key...$"
mov al, Array1 [bx]
sub al, Array2 [bx]
mov Array3 [bx], al
; Extend (unsigned) AL to AX (to print)
mov ah, 0
call printd
You say that your program is having trouble displaying the negative numbers, but your code is never feeding any negative number to the printd routine! Whatever the signed result of the subtraction in AL may be, the mov ah, 0 that follows will produce a positive number in AX and it is AX that printd processes...
You should replace mov ah, 0 by cbw.
But, what is terribly wrong is the placement of the 3 arrays. They can't be at the top of the program like that. The cpu is executing their bytes (data) as if it were instructions (code)!
Move those 3 lines towards the bottom of the source.
It surprised me to see these recursive solutions to display a decimal number. I believe they are correct with one exception though! If you feed printd the negative number -32768 then the program will fall into an infinite loop. This happens because the negation of that particular value is again -32768.
You might want to investigate Displaying numbers with DOS for info about the iterative solution that is surely faster (and could be improved further by outputting the digits all at once).
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 am working on a project for class, and it works as required by the rubric, though I am wondering if there is a slightly better way to implement a few things. I was docked a few points for an unnecessary 'mov' in another project. Here is problem 1.
"If—else (34 points): Write a program that asks the user to enter a single digit. If that digit is less 5, you will state so and you will add 5 to it and store it in a variable; if the digit is greater than 5, you will state so and then subtract 5 from it and store it in a variable; if the digit is 5, you will add 3 to it and state so and store it in a variable."
org 100h
mov dx, offset start ;move start of string address 'start' into dx
mov ah, 09h
int 21h ;print the string stored at DS:DX
mov ah, 01h ;function for getting keyboard input
int 21h
sub al, 30h ;subtract 30h to store our number as hexadecimal
mov bl, al ;copying data to BL as the following commands manipulate the data
;at AL.
cmp bl, 5 ;BL = AL
jz ifZero ;jump to ifZero if BL = 5
jl ifLess ;jump to isLess if BL < 5
jg ifGreater ;jump to ifGreater if BL > 5
ifZero: ;direct console output function
mov ah, 06h
mov dl, 0Ah
int 21h
mov dl, 0Dh
int 21h ;print newline and character return
add bl, 03h ;add 3 to BL, BL = 8
mov temp, bl
mov dx, offset eq ;move start of string address 'eq' into dx
mov ah, 09h
int 21h ;print string
jmp exit ;unconditional jump to end program
ifLess: ;direct console output function
mov ah, 06h
mov dl, 0aH
int 21h
mov dl, 0Dh
int 21h ;print newline and character return
add bl, 05h ;add 5 to BL
mov temp, bl
mov dx, offset less ;move start of string address 'less' into dx
mov ah, 09h
int 21h ;print string
jmp exit ;unconditional jump to end program
ifGreater:
mov ah, 06h
mov dl, 0ah
int 21h
mov dl, 0dh
int 21h ;print newline and character return
sub bl, 05h ;subtract 5 from BL
mov temp, bl
mov dx, offset great ;move starting address of string 'great' into dx
mov ah, 09h
int 21h ;print string
jmp exit ;unconditional jump to end program
exit:
ret
temp db ?
start db "Please enter a number: $"
less db "Less than 5... adding 5 $"
great db "Greater than 5... subtracting 5 $"
eq db "Equal to 5... adding 3 $"
in this case, would 'mov bl, al' be not needed? Running through the disassembler shows that the data in AL changes after most of these commands. Is this supposed to happen? Is there a better way to do so?
Problem 3:
Counter-controlled loop. The program will ask the user to enter a character and then it will display
that character with a label five times
For example:
Enter a character: A
You entered: A
org 100h
mov cx, 05h ;counter controlled loop, start as 5
LabelLoop:
mov dx, offset prompt ;move string offset to dx
mov ah, 09h ;function for printing string from dx
int 21h
mov ah, 01h ;function to read character from keyboard
int 21h
mov bl, al ;preserving character read by copying to BL
;as register data for AL will be changing
;due to various functions
mov ah, 06h ;function for direct console output
mov dl, 0ah
int 21h
mov dl, 0dh ;these just make the text appear on a new
;line
int 21h
mov dx, offset output ;move the memory offset of output to dx
mov ah, 09h ;printing another string
int 21h
mov ah, 02h ;function to write a character to console
;gets the value from DL
mov dl, bl ;so we copy BL to DL and print it
int 21h
jmp newLine ;we unconditionally jump to the newLine
;label and print a new line for the program
;to run again
newLine:
mov ah, 06h
mov dl, 0ah
int 21h
mov dl, 0dh
int 21h
loop LabelLoop ;we jump to LabelLoop and CX = CX - 1
mov dx, offset goodbye
mov ah, 09h
int 21h
ret
prompt db 'Enter a character: $'
output db 'You entered: $'
goodbye db 'Good bye!$'
So, my question for these problems, is there a better way to do this? keyboard input is stored in AL, but the register value changes for each time i do a mov function into AH, whether string printing or character printing. in order to avoid a variable (since it wasn't a part of the requirements) or allocating it to memory (which we haven't learned), I moved the data to a different register. Is this an unnecessary 'mov' for either program?
edit: i realize that AL = DL after
mov ah, 06h
mov dl, 0ah
int 21h ;AL = DL after execution
cmp bl, 5 ;BL = AL
jz ifZero ;jump to ifZero if BL = 5
jl ifLess ;jump to isLess if BL < 5
jg ifGreater ;jump to ifGreater if BL > 5
ifZero:
The first improvement you should do is making use of the possibility to fall-through in the code. Don't use jz ifZero but rather fall through as equality is the only state that remains after jl and jg. Also ifEqual would be a more correct name for this state.
cmp bl, 5 ;BL = AL
jl ifLess ;jump to isLess if BL < 5
jg ifGreater ;jump to ifGreater if BL > 5
ifEqual:
A second optimization will be to get rid of all of those direct console outputs for CR and LF. You should incorporate these in the messages that you will print. Doing so will also remove the need to copy AL to BL using mov bl, al (you specifically asked this):
less db 13,10,"Less than 5... adding 5 $"
great db 13,10,"Greater than 5... subtracting 5 $"
eq db 13,10,"Equal to 5... adding 3 $"
Here's another opportunity to fall through:
jmp exit ;unconditional jump to end program
exit:
Your second program can benefit from these advices too.
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.