NASM - The Netwide Assembler
NASM Forum => Using NASM => Topic started by: jambli on July 26, 2010, 08:49:07 PM
-
Afternoon everyone,
NM - realized my problem, I wasn't popping them back off in the correct order. =)
For some reason I assumed each register had its own stack, my bad.
I'm running into an infinite loop after calling one of my functions from within another function.
This occurs in 16bit mode, its for a boot loader.
;This is the function were I hit an infinite loop.
Function_1:
mov cd, 10 ; number of times to loop
.loop_section
mov ax, cx ; Moving number to print into ax
mov bx, 10 ; Number base of output
call Print_Num
loop .loop_section
ret
; Here is the offending function that is causing the loop.
; register ax must contain the number to print
; register bx the number base to print.
Print_Num:
; Setup
push ax ; numerator
push bx ; number base.
push cx ; counter
push dx ; result
xor cx, cx ; Clear counter
.Push_Digits:
xor dx,dx ; clear dx.
div bx ; ax/bx -> ax quotient, dx remainder
push dx ; save dx
inc cx ; increment counter
or ax,ax ; check for a quotient of 0
jnz .Push_Digits ; if quotient is not 0 continue looping.
mov ah, 14
.Pop_Digits:
pop dx
mov al, dl
add al, 48 ; nasm has some special op for this I know.
int 16 ; video interrupt to display the value in al to screen.
loop .Pop_Digits
.Done:
pop ax ; numerator
pop bx ; denominator
pop cx ; counter
pop dx ; result
ret
Now when I call Function_1 it repeatedly displays 9. I can't tell if it displays 10, it moves too quickly and I haven't figured out a way to debug my bootloader. =/
Now my understanding is that in Print_Num when I push the values at the beginning and pop them at the end it'll restore the registers to their original state. however it doesn't seem like this is what is occuring. If I modify function_1 in the following way :
function_1:
mov cx, 10
.loop_section
push cx
mov ax,cx
mov bx, 10
call Print_Num
pop cx
loop .loop_section
ret
it works just fine. However I shouldn't have to do this since Print_Num was suppose to restore the values itself.
Basically I'm wondering why this is occurring, I checked the manual couldn't find anything that helped. The descriptions of the
instructions for nasm are practically worthless from what I saw, in appendix b, if there are better descriptions in the manual somewhere please let me know. =)
-
The "old" Nasm manual had descriptions of the instructions - the "new" manual is just a list.
http://home.myfairpoint.net/fbkotler/nasmdocr.html
Note the disclaimer!
This is derived from the old, obsolete, and potentially buggy instruction set reference from the old Nasm Manual. The Nasm development team explicitly disclaims any responsibility for errors and omissions in this document!
In spite of that, it may be useful to you. Doesn't mention the effect on flags. There are other, potentially better, instruction set references floating around - perhaps best to go straight to the horse's mouth, the Intel/AMD manuals... but I "rescued" this one... 'cause I'm used to it...
Right, you need to pop registers in the opposite order from which you pushed 'em. And to complete the question in the topic, "call" puts the return address on the stack, and "ret" removes it, so you do need to pop everything you pushed before you hit the "ret"!
But... do you really need to preserve all those registers? You're reloading ax and bx before each call to Print_Num, and not really using dx (in the caller), so the only thing you need to preserve is cx. Doesn't do any harm, but in a bootsector, you may want to save every byte you can, since space is limited.
Another potential "savings":
Pop_Digits:
; pop dx
; mov al, dl
pop ax
add al, 48 ; nasm has some special op for this I know.
int 16 ; video interrupt to display the value in al to screen.
loop .Pop_Digits
.Done:
There's only one stack, and you need to pop everything you pushed, but you *can* pop it into a different register than you pushed...
Nasm doesn't have any "special op" that I know of, but you could write it different ways:
add al, 48
add al, 30h
add al, '0'
... all do the same thing. Also, speaking of writing things different ways, you've called "int 10h", "int 16". This will work fine - it's the same number (bit pattern) - but it's more "usual" to use hex for interrupt numbers. They're usually documented that way. For example:
http://www.ctyme.com/rbrown.htm
(if you haven't discovered Ralf Brown yet, he's your new best friend!) :)
You mention that it "moves too fast"... You can "hold" the display so you've got time to read it by waiting for a key-press:
mov ah, 0
int 16h ; note hex!
I understand that you can debug a bootsector by running it in an emulator - Bochs, or some other. I don't "trust" emulators to be exactly the same as "real hardware" (which differs from bios to bios), so I like to run my bootsectors on "real hardware"... so I can't advise you on emulators. :)
Since a bootsector is hard to debug, and for other reasons, it isn't an easy place for a beginner to start. I'd advise doing a few .com files first (if your OS supports it), but a lot of beginners like to start with a bootsector - go for it, if you wanna, but it isn't the easiest thing to do!
Best,
Frank