Im currently writing an assembler program that runs in real mode. I compile it using NASM. My problem is that if I try to call one function from an other it only executes if I subtract a verry high value from esp (70) or if I dont have any arguments pushed. I think there is something wrong with the way I call functions but I cant figure out what.
The comlete code:
; boot.asm
[ORG 0x7c00] ;set base addresse
;mov ax, 0x7c0
xor ax, ax ; set zero
mov ds, ax ; set data pointer
mov ss, ax ; set stack start ptr
mov sp, 0x2000
add sp, ax
call main
hang:
jmp hang
main:
push ebp
mov ebp, esp
push 0x13
call setVideoMode_mode
add esp, 2
push 2
push 320*200
call clearScreen_char_n
add esp, 4
push 100
call drawVerticalLine
add esp, 2
nop
mov esp, ebp
pop ebp
ret
setVideoMode_mode:
push ebp
mov ebp, esp
mov ah, 0x00 ;change mode command
mov al, [ebp+6] ;video mode
int 0x10 ;execute command
mov esp, ebp
pop ebp
ret
putPixel_pos_char:
push ebp
mov ebp, esp
sub esp, 16 ; 3 local vars
mov eax, DWORD [ebp+8] ;POS
mov WORD [ebp-12], ax
mov eax, DWORD [ebp+6] ;CHAR
mov WORD [ebp-8], ax
mov DWORD [ebp-4], 0xA0000 ;STD VIDEO POINTER
mov eax, [ebp-12]
add eax, DWORD [ebp-4]
movzx edx, WORD [ebp-8]
mov BYTE [eax], dl
nop
; leave
mov esp, ebp
pop ebp
ret
;----------------------------
The problematic function:
drawVerticalLine: ;doing debugging stuf at the moment
push ebp
mov ebp, esp
sub esp, 16 ;the function call only works with 70 or higher / no arguments for this function
mov eax, DWORD [ebp+6]
mov WORD [ebp-4], ax
push 4
push 320*200
call clearScreen_char_n ;not working (should make the screen red)
add esp, 4
nop
mov esp, ebp
push ebp
ret
clearScreen_char_n:
push ebp
mov ebp, esp
sub esp, 32
mov eax, DWORD [ebp+8]
mov WORD [ebp-12], ax ; CHAR
mov eax, DWORD [ebp+6]
mov WORD [ebp-8], ax ; N
mov DWORD [ebp-4], 0 ; COUNTER
jmp .LoopCompare
.LoopBody:
push WORD [ebp-4] ;COUNTER
push WORD [ebp-12] ;CHAR
call putPixel_pos_char
add esp, 4
inc DWORD[ebp-4] ;COUNTER ++
.LoopCompare:
mov ax, WORD [ebp-8] ;N
cmp ax, WORD [ebp-4] ;COUNTER
jne .LoopBody ;NOT EQUAL
nop
mov esp, ebp
pop ebp
ret
times 510-($-$$) db 0
db 0x55 ;mark bootsector
db 0xAA
Thanks for your help.
EDIT: I just noticed an even more strange thing: If I debug with gdb and logg sp after each instruction (breakpoints + x/x $sp)(except the loop) it magicaly works how it should. If I run with gdb without any breakpoints it again doesnt work.
mov DWORD [ebp-4], 0xA0000 ;STD VIDEO POINTER mov eax, [ebp-12] add eax, DWORD [ebp-4] movzx edx, WORD [ebp-8] mov BYTE [eax], dl
The troubles in your program come from the fact that the above instruction mov BYTE [eax], dl
overwrites memory erroneously!
Because your entire program runs in the real address mode, you can't (*) access the video memory through its linear address at 0xA0000. You need to setup a segment register with 0xA000 and use a 16-bit offset within that segment.
putPixel_pos_char:
push bp
mov bp, sp
push ds
mov ax, 0xA000 ; Segment of graphical video memory
mov ds, ax
mov al, [bp+4] ; 1st arg : Color
mov bp, [bp+6] ; 2nd arg : Position in video memory 0-63999
mov [ds:bp], al
pop ds
pop bp
ret
Tip: Don't setup local variables if the code doesn't really require it!
Also because this program runs in the real address mode, you should stop using ESP
and EBP
the way you do. Only use the 16-bit portions sp
and bp
.
(*) You can, but setting up the unreal address mode might not be worth the trouble for the short time spent in this bootloader.