[완료] 질문_어셈블리어 call에 관한 질문 입니다.
================boot.asm================
[org 0]
jmp 0x07C0:start
start:
mov ax, cs
mov ds, ax
mov ax, 0xB800
mov es, ax
mov di, 0
mov ax, word [msgBack]
mov cx, 0x7FF
; lea esi, [msgBootloader]
; call print
;print:
; push eax
;print_loop:
; mov al, byte [esi]
; mov byte [es:di], al
; inc di
; or al, al
; jz print_end
; mov byte [es:di], 0x07
; inc di
; inc esi
; jmp print_loop
;print_end:
; pop eax
; ret
paint:
mov word [es:di], ax
add di, 2
dec cx
jnz paint
read:
mov ax, 0x1000
mov es, ax
mov bx, 0
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 0x13
jc read
jmp 0x1000:0000
;msgBootloader db "succeed into the Bootloader", 0
msgBack db '.', 0x07
times 510-($-$$) db 0
dw 0xAA55
=====kernel.asm==================================
[org 0]
[bits 16]
start:
mov ax, cs
mov ds, ax
xor ax, ax
mov ss, ax
mov ax, 0xB800
mov es, ax
mov di, 80*2*10+10*2
lea esi, [msgKernel]
call print
jmp $
print:
push eax
print_loop:
mov al, byte [esi]
mov byte [es:di], al
inc di
or al, al
jz print_end
mov byte [es:di], 0x06
inc di
inc esi
jmp print_loop
print_end:
pop eax
ret
msgKernel db "From now on, We are in Kernel", 0
처음에는 boot.asm에서 메시지 1개, kernel.asm에서 메시지 1개를 출력하려고,
boot.asm에 print:레이블을 써서 출력 하려고 했는데 무슨 탓인지 저위치에서 call을 하니까 [주석처리한 부분..]kernel쪽 메시지가 올라오지 않습니다....
원래는 boot.asm에 call명령을 쓰지않고 위 코드의 paint: 레이블 같이 점으로 화면은 채우는 코드가 있었습니다.
여러번 코드 수정하고, 별에별짓 다해보다가 책에 있는 구조대로 print: 레이블 빼고 paint:로 바꾸니까 되는군요...
제목을 call 명령에 관한 질문이라고 했지만 사실 정확히 뭣 때문에 안되는지 모르겠습니다. 단지 call을 빼니까 안뜨던 메시지가 떠서 call때문인가? 라고 생각한 것입니다.
왜 저기서 call명령어를 넣은 온래 source는 kernel을 불러오지 못할까요??
안되는것이 당연합니다.:) ; lea esi,
안되는것이 당연합니다.:)
; lea esi, [msgBootloader]
; call print
이를 이용해서 print를 call했습니다.
그리고 바로 아래에 있는 print 레이블로 점프후 call이 다 끝나고 ret 명령어가
call 할 때 스택에 푸시한 메모리 주소의 다음 주소를 eip에 넘겨 줍니다.
그러면 cpu는 그 다음 명령어를 수행합니다. 그 명령어가 바로
;print:
;push eax
이 코드에서 두번째 코드가 되는 것입니다.
즉, 똑같은 코드가 다시 call을 안했지만 실행되고 맨 마지막에 또 ret으로 인해서 스택에서
원래로 돌아갈 메모리 주소를 eip에 넣지만 이는 엉뚱한 메모리 주소가 되는 것입니다.
그래서 cpu는 엉뚱한 메모리 주소로 점프를 하게 되고 그대로 crash하는 것입니다.
해결 방법은 절대로 실행이 되지 않는 위치에 print 코드를 넣어주시면 됩니다.
jmp 0x1000:0000 코드 다음으로 넣어주시면 되겠네요.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.
감사합니다
답 주신분 알림글을 보니 콜 때문에 일어난 일이 아니라 콜을 잘못 쓴 저때문이군요..
이해하고 넘어갔다고 생각했지만.. 어째 하면 할 수록 새롭습니다. ㅎ
정확한 답변 감사합니다 ㅎ
아., 그리고 전부터 궁금한게 있었는데, 왜 push eax를 해주나요?..
eax는 연산할 때 필요에 따라 값을 넣었다 뺏다 하던데...
굳이 값을 보존하는 이유를 모르겠네요...
...
많은 어셈블리어 명령어 중 명령어 연산 결과를
많은 어셈블리어 명령어 중 명령어 연산 결과를 eax에 자동으로 넣어주는 명령어가 많습니다.
그리고 eax 레지스터는 어떤 명령어를 실행하기 위해서 필요한 파라메터 등을 넣는데 많이 쓰입니다.
이렇게 때문에 어떤 함수가 call되어서 수행중에 다른 함수를 call 할 경우 caller의 상태를 백업 해야만
합니다.
필요때마다 push-pop을 해도 되지만 어차피 쓸것이므로 그냥 미리 스택에 push해서 백업해놓는 편이
편하기 때문에 그렇게 씁니다. 물론 eax를 전혀 안쓰는 경우에는 스택에 백업 할 필요가 없구요.
또한 eax뿐만 아니라 callee에서 다른 레지스터를 건든다면 건드는 레지스터도 백업해야합니다.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.
감사합니다...오늘도 많은 지식 얻어감니다.
감사합니다...오늘도 많은 지식 얻어감니다.
...
댓글 달기