Search code examples
assemblyx86gdbnasmreal-mode

assembler function not executing when esp is not exetremely low


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.


Solution

  • 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.