I have code that will add nodes to a tree properly in 16 bit Assembler. I can display any number of left trees up to max without an issue. Everytime I try to get the right child to print it crashes the system. I have tried to see what is happening in turbo debugger and assume I am not getting something saved to or popped off the stack properly.
I have run this code through turbo debugger more times than I can remember and not able to see where the issue(s) are.
.MODEL small
.STACK 100h
.DATA
menuchoice DB 10, 10, 13, 'Please enter your choice from the menu. $'
message DB 10, 10, 13, 'Please enter the character you would like to add (enter # to exit): $'
menu DB 10, 10, 13, 'Binary Search Tree Menu'
DB 10, 10, 13, '1 - Add a character to the tree'
DB 10, 10, 13, '2 - Search the tree for a character'
DB 10, 10, 13, '3 - Display the contents of the tree'
DB 10, 10, 13, '4 - Exit the program $'
empty DB 10, 10, 13, ' The tree is empty. $'
crlf DB 10, 13, ' $'
bintree DB 60 DUP(0) ; create a list with a size of 20 characters
node DB 0 ; node starts at zero become 1 once value is added
root DB 1 ; next available slot
next DB 1 ; next is the pointer to the next slot for the character
.CODE
tree PROC
; first two lines in main in all assembler
mov ax, #data
mov ds, ax
DISPLAYMENU: ; set up my label
; display menu
mov dx, OFFSET menu
mov ah, 09h
int 21h
; display menu choice prompt
mov dx, OFFSET menuchoice
mov ah, 09h
int 21h
; get the character from the keyboard with echo
mov ah, 01h
int 21h
MAINLOOP: ; set up my label
; compare data entry to 4 to exit program
cmp al, 34h
je exitprog
; compare data entry to 1 to add node
cmp al, 31h
je starttree
; set root to 1
mov root, 1
; determine if tree is empty, if empty display message
cmp node, 0
je emptymessage
; compare data entry to 2 to search tree
cmp al, 32h
jne check3
call searchtree
CHECK3: ; set up my label
; compare date entry to 3 to display data
cmp al, 33h
jne mainloop
call disptree
jmp mainloop
; set si to zero to represent empty
; mov si, 0
; set di to zero to represent empty
; mov di, 0
EXITPROG: ; set up my label
; exit to DOS
mov al, 0
mov ah, 04ch
int 21h
EMPTYMESSAGE: ; set up my label
; display empty message
mov dx, OFFSET empty
mov ah, 09h
int 21h
; move back to main
jmp displaymenu
STARTTREE: ; set up my label
; display message
mov dx, OFFSET message
mov ah, 09h
int 21h
; get character from keyboard with echo
mov ah, 01h
int 21h
mov ah, 0
; check to see if the character entered is #
cmp al, 23h
; if equal jump to exit program
je exitprog
mov root, 1
; compare next to 60 to determine if there is still space in the tree if not exit
cmp next, 60
jnl exitprog
CHECKTREE: ; set up my label
; check to see if tree is empty and jump to addnode if equal
cmp node, 0
je addnode
; compare root to zero and jump to addnode if equal
cmp root, 0
je addnode
; if not equal then jump to evaltree
jne evaltree
ADDNODE: ; set up my label
; change node value to 1
mov node, 1
; move the array position value in next to si
mov bl, next
mov bh, 0
mov si, bx
; move the value into tree
mov [bintree + si], al
; increment next by 3 to move position to next character slot
add next, 3
; jump back to menu
jmp displaymenu
EVALTREE: ; set up my label
; move root to di to compare position
mov bl, root
mov bh, 0
mov di, bx
; compare bintree to value
cmp al, [bintree + di]
; if less than jump to left child
jl leftchild
; if greater than jump to right child
jg rightchild
LEFTCHILD: ; set up my label
; subtract 1 from di
sub di, 1
; move the position in bintree + di to root
mov bl, [bintree + di]
mov root, bl
; compare root to zero and if equal jump to change pointer and not equal to checktree
cmp root, 0
je changeptr
jne checktree
RIGHTCHILD: ; set up my label
; add 1 from di
add di, 1
; move the position in bintree + di to root
mov bl, [bintree + di]
mov root, bl
; compare root to zero and if equal jump to change pointer and not equal to checktree
cmp root, 0
je changeptr
jne checktree
CHANGEPTR: ; set up my label
; move next to bintree + di
mov bl, next
mov [bintree + di], bl
; jump to checktree
jmp checktree
tree ENDP
searchtree PROC
searchtree ENDP
disptree PROC
; save the value in root to the stack
mov bl, root
mov bh, 0
push bx
; mov value in root to si
mov si, bx
; decrement si to check the left child of character
sub si, 1
; compare the value in bintree to 0
cmp [bintree + si], 0
je ldispret
; move the value in bintree + si to root
mov bl, [bintree + si]
mov root, bl
; return to disptree
call disptree
; get root value from the stack
mov root, bl
; move root to si
mov bl, root
mov bh, 0
DISPRIGHT: ; set up my label
mov si, bx
; display the character
mov dl, [bintree + si]
mov ah, 02h
int 21h
; increment si to check the right child of the character
add si, 1
; compare the value in bintree + si to 0
cmp [bintree + si], 0
jne goright
jmp rdispret
GORIGHT: ; set up my label
; move the value in bintree + si to root
mov bl, [bintree + si]
mov root, bl
; go back to disptree
call disptree
ldispret: ; set up my label
pop bx
pop ax
push bx
push ax
ret
rdispret: ; set up my label
; mov cl, root
; add cl, 3
; cmp next, cl
;je tomain
pop bx
pop bx
jmp dispright
pop bx
pop bx
ret
disptree ENDP
TOMAIN: ; set up my label
;jmp mainloop
end tree
When it displays the contents of the binary tree it should print the characters in alphabetical order. If my input is m, b, a, c it should display abcm and then return to the menu. The program crashes when it evaluates the right tree and tries to display it.
It wasn't easy to follow your program flow but I think I managed to find a number of (important) reasons why the code crashes.
; return to disptree
call disptree
; get root value from the stack
mov root, bl
The disptree procedure leaves a word on the stack using the stackmanipulation in ldispret:, but contrary to your comment above, you don't get anything off the stack! Use pop bx mov root, bl.
Please note that the first call to disptree (the one that comes from the menu) will also have to discard this word from the stack.
rdispret: ; set up my label
pop bx
pop bx
jmp dispright
and
DISPRIGHT: ; set up my label
mov si, bx
; display the character
mov dl, [bintree + si]
The rdispret: code first pops a root value and then pops a return address. It's the return address that you then mistakenly use as an index in the bintree array. Use pop bx pop ax jmp dispright.
For any program that uses recursion it is best to allocate a big stack. 256 bytes is a bit cheap.
CHECK3: ; set up my label
; compare date entry to 3 to display data
cmp al, 33h
jne mainloop
Your program enters an endless loop if the user fails to type in a number in the range "1" to "4".
If this happens you need to ask for another character from the user. Move the mainloop label 2 lines higher up.
Related
New to assembly.
I'm trying to write an assembly code, that will do the following operation:
x/8 + 32 - y IF (x+y)/2>0
OR
2Y - 60 IF (x+y)/2<=0
While I was able to get the first operation to work ( even though, only when putting even numbers as X and Y, because otherwise I get results like ~+8000 ), I can't manage to make the second operation to work. When inserting in console something like : x= - 6, y=2, it should prind me out -56, but it prints me +30.
What did I do wrong there and how could I correct it?
INCLUDE Irvine32.inc
.data
mes1 byte "X:",0
mes2 byte "Y:",0
mes3 byte "Result:",0
vrx dword 0
vry dword 0
rez dword 0
doi dword 2
vxr dword 0
vyr dword 0
.code
main PROC
mov edx,OFFSET mes1
call WriteString ;
call ReadDec ;
mov vrx,eax ;
mov edx,OFFSET mes2
call WriteString ;
call ReadDec ;
mov vry,eax ;
;
xor eax,eax
mov edx,0
mov eax, vrx ;x
add eax, vry ;x+y
div doi ;(x+y)/2
cmp eax, 0 ; comparing
jb con1 ;
mov eax,vrx ; X/8+32-Y
mov ebx,8
div ebx
add eax,32
sub eax,vry
mov rez,eax
jmp ex ; jump to ex
con1: mov eax,vry ; 2Y-60
mov ebx,2
mul ebx
sub eax,60
mov rez,eax
ex: mov edx,OFFSET mes3
call WriteString ;
call WriteInt ;
call Crlf ;
exit
main ENDP
END main
I am trying to implement Fibonacci sequence in assembly by using recursion. This is my first time of trying to implement recursion in x86 Assembly.
The code compiles fine but it gives wrong outputs. The output for 1 is 1, output for 2 is 0, output for 3 is 1, output for 4 is 2, output for 5 is 3.
Only output it gives correct when you plug 5 in.
Is there something wrong with the algorithm?
.DATA
n1 DWORD ?
prompt1 BYTE "Please enter the first value", 0
prompt3 BYTE "No negative numbers!",0
string BYTE 40 DUP (?)
resultLbl BYTE "The Fib is: ", 0
fib BYTE 40 DUP (?), 0
.CODE
_MainProc PROC
input prompt1, string, 40
atod string
test eax, eax
js signed
mov n1, eax
jmp procName
signed:
output prompt3, string
jmp end1
procName:
mov eax, n1
push n1
call fib1
add esp,4
dtoa fib, eax
output resultLbl, fib
end1:
mov eax, 0
ret
_MainProc ENDP
Fib1 proc
PUSH EBP ; save previous frame pointer
MOV EBP, ESP ; set current frame pointer
MOV EAX, [EBP+8] ; get argument N
CMP EAX, 1 ; N<=1?
JA Recurse ; no, compute it recursively
MOV ECX, 1 ; yes, Fib(1)--> 1
JMP exit
Recurse:
DEC EAX ; = N-1
MOV EDX, EAX ; = N-1
PUSH EDX ; save N-1
PUSH EAX ; set argument = N-1
CALL Fib1 ; compute Fib(N-1) to ECX
POP EAX ; pop N-1
DEC EAX ; = N-2
PUSH ECX ; save Fib(N-1)
PUSH EAX ; set argument = N-2
CALL Fib1 ; compute Fib(N-2) to ECX
POP EAX ; = Fib(N-1)
ADD ECX, EAX ; = Fib(N-1)+FIB(N-2)
exit:
MOV ESP,EBP ; reset stack to value at function entry
POP EBP ; restore caller's frame pointer
RET
Fib1 endp
END
In my 80x86 assembly program, I am trying to calculate the equation of
(((((2^0 + 2^1) * 2^2) + 2^3) * 2^4) + 2^5)...(2^n), where each even exponent is preceded by a multiplication and each odd exponent is preceded by a plus. I have code, but my result is continuously off from the desired result. When 5 is put in for n, the result should be 354, however I get 330.
Any and all advice will be appreciated.
.586
.model flat
include io.h
.stack 4096
.data
number dword ?
prompt byte "enter the power", 0
string byte 40 dup (?), 0
result byte 11 dup (?), 0
lbl_msg byte "answer", 0
bool dword ?
runtot dword ?
.code
_MainProc proc
input prompt, string, 40
atod string
push eax
call power
add esp, 4
dtoa result, eax
output lbl_msg, result
mov eax, 0
ret
_MainProc endp
power proc
push ebp
mov ebp, esp
push ecx
mov bool, 1 ;initial boolean value
mov eax, 1
mov runtot, 2 ;to keep a running total
mov ecx, [ebp + 8]
jecxz done
loop1:
add eax, eax ;power of 2
test bool, ecx ;test case for whether exp is odd/even
jnz oddexp ;if boolean is 1
add runtot, eax ;if boolean is 0
loop loop1
oddexp:
mov ebx, eax ;move eax to seperate register for multiplication
mov eax, runtot ;move existing total for multiplication
mul ebx ;multiplication of old eax to new eax/running total
loop loop1
done:
mov eax, runtot ;move final runtotal for print
pop ecx
pop ebp
ret
power endp
end
You're overcomplicating your code with static variables and branching.
These are powers of 2, you can (and should) just left-shift by n instead of actually constructing 2^n and using a mul instruction.
add eax,eax is the best way to multiply by 2 (aka left shift by 1), but it's not clear why you're doing that to the value in EAX at that point. It's either the multiply result (which you probably should have stored back into runtot after mul), or it's that left-shifted by 1 after an even iteration.
If you were trying to make a 2^i variable (with a strength reduction optimization to shift by 1 every iteration instead of shifting by i), then your bug is that you clobber EAX with mul, and its setup, in the oddexp block.
As Jester points out, if the first loop loop1 falls through, it will fall through into oddexp:. When you're doing loop tail duplication, make sure you consider where fall-through will go from each tail if the loop does end there.
There's also no point in having a static variable called bool which holds a 1, which you only use as an operand for test. That implies to human readers that the mask sometimes needs to change; test ecx,1 is a lot clearer as a way to check the low bit for zero / non-zero.
You also don't need static storage for runtot, just use a register (like EAX where you want the result eventually anyway). 32-bit x86 has 7 registers (not including the stack pointer).
This is how I'd do it. Untested, but I simplified a lot by unrolling by 2. Then the test for odd/even goes away because that alternating pattern is hard-coded into the loop structure.
We increment and compare/branch twice in the loop, so unrolling didn't get rid of the loop overhead, just changed one of the loop branches into an an if() break that can leave the loop from the middle.
This is not the most efficient way to write this; the increment and early-exit check in the middle of the loop could be optimized away by counting another counter down from n, and leaving the loop if there are less than 2 steps left. (Then sort it out in the epilogue)
;; UNTESTED
power proc ; fastcall calling convention: arg: ECX = unsigned int n
; clobbers: ECX, EDX
; returns: EAX
push ebx ; save a call-preserved register for scratch space
mov eax, 1 ; EAX = 2^0 running total / return value
test ecx,ecx
jz done
mov edx, ecx ; EDX = n
mov ecx, 1 ; ECX = i=1..n loop counter and shift count
loop1: ; do{ // unrolled by 2
; add 2^odd power
mov ebx, 1
shl ebx, cl ; 2^i ; xor ebx, ebx; bts ebx, ecx
add eax, ebx ; total += 2^i
inc ecx
cmp ecx, edx
jae done ; if (++i >= n) break;
; multiply by 2^even power
shl eax, cl ; total <<= i; // same as total *= (1<<i)
inc ecx ; ++i
cmp ecx, edx
jb loop1 ; }while(i<n);
done:
pop ebx
ret
I didn't check if the adding-odd-power step ever produces a carry into another bit. I think it doesn't, so it could be safe to implement it as bts eax, ecx (setting bit i). Effectively an OR instead of an ADD, but those are equivalent as long as the bit was previously cleared.
To make the asm look more like the source and avoid obscure instructions, I implemented 1<<i with shl to generate 2^i for total += 2^i, instead of a more-efficient-on-Intel xor ebx,ebx / bts ebx, ecx. (Variable-count shifts are 3 uops on Intel Sandybridge-family because of x86 flag-handling legacy baggage: flags have to be untouched if count=0). But that's worse on AMD Ryzen, where bts reg,reg is 2 uops but shl reg,cl is 1.
Update: i=3 does produce a carry when adding, so we can't OR or BTS the bit for that case. But optimizations are possible with more branching.
Using calc:
; define shiftadd_power(n) { local res=1; local i; for(i=1;i<=n;i++){ res+=1<<i; i++; if(i>n)break; res<<=i;} return res;}
shiftadd_power(n) defined
; base2(2)
; shiftadd_power(0)
1 /* 1 */
...
The first few outputs are:
n shiftadd(n) (base2)
0 1
1 11
2 1100
3 10100 ; 1100 + 1000 carries
4 101000000
5 101100000 ; 101000000 + 100000 set a bit that was previously 0
6 101100000000000
7 101100010000000 ; increasing amounts of trailing zero around the bit being flipped by ADD
Peeling the first 3 iterations would enable the BTS optimization, where you just set the bit instead of actually creating 2^n and adding.
Instead of just peeling them, we can just hard-code the starting point for i=3 for larger n, and optimize the code that figures out a return value for the n<3 case. I came up with a branchless formula for that based on right-shifting the 0b1100 bit-pattern by 3, 2, or 0.
Also note that for n>=18, the last shift count is strictly greater than half the width of the register, and the 2^i from odd i has no low bits. So only the last 1 or 2 iterations can affect the result. It boils down to either 1<<n for odd n, or 0 for even n. This simplifies to (n&1) << n.
For n=14..17, there are at most 2 bits set. Starting with result=0 and doing the last 3 or 4 iterations should be enough to get the correct total. In fact, for any n, we only need to do the last k iterations, where k is enough that the total shift count from even i is >= 32. Any bits set by earlier iterations are shifted out. (I didn't add a branch for this special case.)
;; UNTESTED
;; special cases for n<3, and for n>=18
;; enabling an optimization in the main loop (BTS instead of add)
;; funky overflow behaviour for n>31: large odd n gives 1<<(n%32) instead of 0
power_optimized proc
; fastcall calling convention: arg: ECX = unsigned int n <= 31
; clobbers: ECX, EDX
; returns: EAX
mov eax, 14h ; 0b10100 = power(3)
cmp ecx, 3
ja n_gt_3 ; goto main loop or fall through to hard-coded low n
je early_ret
;; n=0, 1, or 2 => 1, 3, 12 (0b1, 0b11, 0b1100)
mov eax, 0ch ; 0b1100 to be right-shifted by 3, 2, or 0
cmp ecx, 1 ; count=0,1,2 => CF,ZF,neither flag set
setbe cl ; count=0,1,2 => cl=1,1,0
adc cl, cl ; 3,2,0 (cl = cl+cl + (count<1) )
shr eax, cl
early_ret:
ret
large_n: ; odd n: result = 1<<n. even n: result = 0
mov eax, ecx
and eax, 1 ; n&1
shl eax, cl ; n>31 will wrap the shift count so this "fails"
ret ; if you need to return 0 for all n>31, add another check
n_gt_3:
;; eax = running total for i=3 already
cmp ecx, 18
jae large_n
mov edx, ecx ; EDX = n
mov ecx, 4 ; ECX = i=4..n loop counter and shift count
loop1: ; do{ // unrolled by 2
; multiply by 2^even power
shl eax, cl ; total <<= i; // same as total *= (1<<i)
inc edx
cmp ecx, edx
jae done ; if (++i >= n) break;
; add 2^odd power. i>3 so it won't already be set (thus no carry)
bts eax, edx ; total |= 1<<i;
inc ecx ; ++i
cmp ecx, edx
jb loop1 ; }while(i<n);
done:
ret
By using BTS to set a bit in EAX avoids needing an extra scratch register to construct 1<<i in, so we don't have to save/restore EBX. So that's a minor bonus saving.
Notice that this time the main loop is entered with i=4, which is even, instead of i=1. So I swapped the add vs. shift.
I still didn't get around to pulling the cmp/jae out of the middle of the loop. Something like lea edx, [ecx-2] instead of mov would set the loop-exit condition, but would require a check to not run the loop at all for i=4 or 5. For large-count throughput, many CPUs can sustain 1 taken + 1 not-taken branch every 2 clocks, not creating a worse bottleneck than the loop-carried dep chains (through eax and ecx). But branch-prediction will be different, and it uses more branch-order-buffer entries to record more possible roll-back / fast-recovery points.
My code assembles correctly, however it is not returning the volume of a cone. I tried a few other ways to get it to give me the volume of a cone, and it doesn't give me the correct answer. Is there something I am missing?
Edit: I fixed my code to output a number, however its not giving me the right number.
This is the formula I am using for this code:
V= (22)(r)(r)(h)/21
diviser DWORD 21
height WORD 0
radius BYTE 0 ;*** Declare an unsigned, integer variable for the value
product WORD 0
askRadius BYTE "What is the radius?", 0ah, 0dh, 0 ;*** Declare the prompt and message parts
askHeight BYTE "What is the height? ", 0
volumeOutput BYTE "The volume of the cone is: ", 0
lineBreak BYTE " ",0dh, 0ah, 0
.code
main PROC
mov ebx, diviser ;Initializes the diviser
mov edx,OFFSET askRadius ;Asks for Radius
call WriteString
call ReadDec
mov radius, al ;moves radius into al (8-bit)
mul radius ;Multiplies radius * radius(16-bit)
mov edx,OFFSET askHeight ;asks for Height
call writestring
call ReadDec
mov height, dx ;moves height into AX
mov WORD PTR product, ax ;convert 16-bit into 32-bit
mov WORD PTR product+2, dx ;converts 16-bit into 32-bit
mul eax ;multiplies by 22
mov edx, 22
mul edx
div ebx ;divides by 21
mov edx, OFFSET volumeOutput
call WriteString
call WriteDec
call WaitMsg
exit
main ENDP
END main
Something like this should work better. Note you should choose where you want to widen the calculation to 64 bit.
askRadius BYTE "What is the radius?", 0ah, 0dh, 0 ;*** Declare the prompt and message parts
askHeight BYTE "What is the height? ", 0
volumeOutput BYTE "The volume of the cone is: ", 0
lineBreak BYTE " ",0dh, 0ah, 0
.code
main PROC
mov edx,OFFSET askRadius ;Asks for Radius
call WriteString
call ReadDec
imul eax, eax ; radius * radius
mov ecx, eax ; save for later
mov edx,OFFSET askHeight ;asks for Height
call writestring
call ReadDec
imul eax, eax, 22
mul ecx ;multiplies by radius * radius
mov ecx, 21
div ecx ;divides by 21
mov edx, OFFSET volumeOutput
call WriteString
call WriteDec
call WaitMsg
exit
main ENDP
END main
The following is my code. The block in hex2dec works successfully for converting a single hexadecimal number to decimal number. It would be really helpful if someone could point out where I was going wrong in the use of array. Thanks.
DATA SEGMENT
NUM DW 1234H,9H,15H
RES DB 3*10 DUP ('$','$','$')
SIZE DB 3
DATA ENDS
CODE SEGMENT
ASSUME DS:DATA, CS:CODE
START:
MOV AX, DATA
MOV DS,AX
MOV DI,0
LOOP3:
MOV AX,NUM[DI]
LEA SI,RES[DI]
CALL HEX2DEC
LEA DX,RES[DI]
MOV AH,9
INT 21H
INC DI
CMP DI,3
JL LOOP3
MOV AH,4CH ; end program
INT 21H
CODE ENDS
HEX2DEC PROC NEAR
MOV CX,0
MOV BX,10
LOOP1:
MOV DX,0
DIV BX
ADD DL,30H
PUSH DX
INC CX
CMP AX,9
JG LOOP1
ADD AL,30H
MOV [SI],AL
LOOP2:
POP AX
INC SI
MOV [SI],AL
LOOP LOOP2
RET
HEX2DEC ENDP
END START
MOV AX,NUM[DI]
LEA SI,RES[DI]
LEA DX,RES[DI]
You are treating DI as an array index like we use in any of the high level languages. In assembly programming we only use displacements aka offsets in the array.
In your program, since the NUM array is composed of words, you need to give the DI register successively the values 0, 2, and 4.
ADD DI, 2
CMP DI, 6
JB LOOP3
Also it would be best to not treat the RES as an array. Just consider it a buffer and always use it from the start.
RES DB 10 DUP (0)
...
LEA SI, RES
CALL HEX2DEC
LEA DX, RES
A better version of HEX2DEC avoids the ugly prefixed "0" on the single digit numbers:
HEX2DEC PROC NEAR
XOR CX, CX <--- Same as MOV CX,0
MOV BX,10
LOOP1:
XOR DX, DX <--- Same as MOV DX,0
DIV BX
ADD DL, 30H
PUSH DX
INC CX
TEST AX, AX
JNZ LOOP1
LOOP2:
POP AX
MOV [SI], AL
INC SI
LOOP LOOP2
MOV AL, "$" <--- Add this to use DOS function 09h
MOV [SI], AL
RET
HEX2DEC ENDP