Jumping to random code when using IDIV - debugging

I am relatively new to assembler, but when creating code what works with arrays and calculates the average of each row, I encountered a problem that suggests I don't know how division really works. This is my code:
.model tiny
.code
.startup
Org 100h
Jmp Short Start
N Equ 2 ;columns
M Equ 3 ;rows
Matrix DW 2, 2, 3 ; elements
DW 4, 6, 6 ; elements]
Vector DW M Dup (?)
S Equ Type Matrix
Start:
Mov Cx, M;20
Lea Di, Vector
Xor Si, Si
Cols: Push Cx
Mov Cx, N
Xor Bx, Bx
Xor Ax, Ax
Rows:
Add Ax, Matrix[Bx][Si]
Next:
Add Bx, S*M
Loop Rows
Add Si, S
Mov [Di], Ax
Add Di, S
Pop Cx
Loop Cols
Xor Bx, Bx
Mov Cx, M
Mov DX, 2
Print: Mov Ax, Vector[Bx]
IDiv Dx; div/idiv error here
Add Bx, S
Loop Print
.exit 0
There are no errors when compiling. Elements are counted correctly, but when division happens the debugger shows the program jumping to apparently random code. Why is this happening and how can I resolve it?

If you use x86 architecture, IDiv with 16-bit operand will also take Dx as a part of the integer to be divided and throw an exception (interrupt) if the quotient is too large to fit in 16bits.
Try something like this:
Mov Di, 2
Print: Mov Ax, Vector[Bx]
Cwd ; sign extend Ax to Dx:Ax
IDiv Di

Related

Incorrect value of the variable ans that stores the LCM of two numbers (8086 Program)

Following is the code I wrote to find LCM of two numbers in EMU8086. When I ran it, I am getting value 0 in the Ans variable.
.MODEL SMALL
.DATA
Num1 DW 250
Num2 DW 100
Ans DW ?
.CODE
MOV AX,#DATA
MOV DS, AX
MOV AX, Num1
MOV BX, Num2
MOV DX, 0000h
NEXT: PUSH AX
PUSH DX
DIV BX
CMP DX, 0000h
JZ LAST
POP DX
POP AX
ADD AX, Num1
JNC NEXT
INC DX
JMP NEXT
LAST: POP Ans+2
POP Ans
MOV AH, 4Ch
INT 21h
END
LCM(a, b) = a * b / GCD(a, b)
Due to this equation, you can find GCD using Euclid's algorithm and then calculate LCM. Assuming numbers a and b are in al and dl, this code calculate LCM.
; Save multiplication value
MOV AL, DL ; This 2 lines is AL * DH
MUL DH
PUSH AX ; Store result in stack
FINDBCD: ; General idea is : LCM(a, b) = a*b/BCD(a,b)
; We calculate BCD with euclidean algorithm
CMP DH, DL
JNS CALCULATE ; Jump if DL < DH else swap them
MOV CL, DL ; This 3 line swap DL and DH
MOV DL, DH
MOV DH, CL
CALCULATE:
MOV AL, DH ; Move greater number in AL
XOR AH, AH ; Zero AX 8 second bits
DIV DL ; This is AL/DL
CMP AH, 0 ; If remainder is zero so we found BCD
JE AFTERFINDBCD
SUB DH, DL ; Else substract smaller number from greater number
JMP FINDBCD ; Do this until find BCD
AFTERFINDBCD:
CMP DH, DL
JNS FINDLCM ; Jump if DL < DH
MOV CL, DL ; This 3 line swap DL and DH
MOV DL, DH
MOV DH, CL
FINDLCM:
POP AX ; Retreive multiplication result
DIV DL ; This is AX/DL
AND AX, 0000000011111111B ; Ignore remainder

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.

Effective addressing in Real Mode - accessing array

I am working in real mode of x86 and say I need to access a element from the array people; the index of which is in the register BX.
MOV BX, 2
struc person
.name resb 11
.age resb 1
endstruc
people: times 10 db person_size
The effective addressing in real mode is limited to base + offset. So code like
mov [people + bx * person_size + person.age],byte 20
does not work; however the assembler can do the calculation if no BX register is used -
mov [people + 2 * person_size + person.age],byte 20
I can do multiplication or shift lefts a few times and make it work, but is there a way to do access any element in an array, without assuming that the size of the structure will remain the same in future?
Is there any other way than multiplying like below (cannot do shifts if the structure size changes, code will also change)?
push ax
mov ax, person_size
mul bx
mov bx, ax
pop ax
add bx, person.age
mov [people + bx], byte 20
The effective addressing in real mode is limited to base + offset.
Only on 8086 but not on x86-16 in general.
It's true that in Real Mode you can use Scaled Index addressing like in Fifoernik's answer, but in your program it won't help much since the Scale values are limited to either {1, 2, 4, or 8} and your structure has 12 bytes.
You must do the multiplication yourself especially since you want to leave it open what the size of the structure will be in future.
push ax
mov ax, person_size
mul bx
mov bx, ax
pop ax
add bx, person.age
mov [people + bx], byte 20
What the Real Mode on x86-16 does offer is an extra imul variant that simplifies your calculation:
imul bx, person_size
mov [people + bx + person.age], byte 20
There was no need to add person.age in a separate instruction. The assembler will add people and person.age to become a 16-bit offset.
Your version with the mul bx instruction also modified the DX register. you didn't preserve that one like you did with AX!
For a true 8086 your code was (almost) fine:
push ax
push dx
mov ax, person_size
mul bx
mov bx, ax
pop dx
pop ax
mov [people + bx + person.age], byte 20
One optimization would pad the 12-byte structure to 16 bytes.
struc person
.name resb 11
.age resb 1
.pad resb 4
endstruc
This replaces multiplication by simple shifting to the left in order to access the elements:
For x86-16 (array index in ebx):
shl ebx, 1
mov [people + ebx * 8 + person.age], byte 20
or for 8086 (array index in bx):
push cx
mov cl, 4
shl bx, cl
pop cx
mov [people + bx + person.age], byte 20
Another solution uses a lookup table to avoid multiplication and padding.
LUT dw 0, 12, 24, 36, 48, 60, 72, 84, 96, 108 ; 10 elements
...
shl bx, 1 ; Lookup table holds words
mov bx, [LUT + bx] ; Fetch array element's offset
mov [people + bx + person.age], byte 20

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.

TASM SIMPLE LOOP implemention

I just want too write simple .asm code for TASM that work as for in C++
int t=2;
for(int i=0;i<2;i++)
t=t+(i-1)*7*t;
How can I implement it with TASM?
This will loop from 1 to 100 in 8086 TASM:
.MODEL SMALL
.STACK 100h
.DATA
Finished DB 10, 13, 'Loop x 100 finished. Congratulations! $', 10, 13
.CODE
MAIN PROC
MOV AX, #data ; Required at the start of every program (inside your main procedure, from what I've seen)
MOV DS, AX
MOV CX, 100 ; Set CX to 100
MOV BX, 0 ; Counter (for double-verification, I guess...lol)
StrtLoop: ; When a loop starts, it does CX-- (subtracts 1 from CX)
INC BX ; This does BX++, which increments BX by 1
LOOP StrtLoop ; Go back to StrtLoop label
CMP BX, 100 ; Compare BX to 100...
JE DispMsg ; Jump-if-Equal...CMP BX, 100 sets flags, and if they are set,
; JE will Jump you to DispMsg (to get "congratulations" message).
JMP SkipMsg ; Jump to the SkipMsg label (so you don't see the "congratulations" message).
DispMsg: ; If BX = 100, you JE here.
MOV AH, 09H ; Displays the message stored in the defined byte "Finished"
MOV DX, OFFSET Finished
INT 21H
SkipMsg: ; If BX != 100, you JMP here.
MOV AL, 0h ; Op code to exit to DOS from the assembler.
MOV AH, 4CH
INT 21H
MAIN ENDP
END MAIN
I hope it helps. I did the basic loop, so you can do the other bits of your code (and I don't know C++, lol). Good luck! It's hard, but kind of fun at the same time (at least for me).

Resources