2016-10-07 4 views
0

Я пишу подпрограмму, чтобы просто перепечатывать десятичные числа в виде строк, используя стек, но не получая ожидаемых значений. Когда я запускаю его через отладчик, я вижу, что я не могу получить значение из esi в al. Я подозреваю, что мне не разрешено использовать esi так, как я, но я не уверен по-другому, что могу это сделать. Кроме того, мне не разрешено вводить элементы, которые я храню в edx, в стек.Сборка NASM x86 - Простой проект стека

код подпрограммы:

%define STDIN 0 
%define STDOUT 1 
%define SYSCALL_EXIT 1 
%define SYSCALL_READ 3 
%define SYSCALL_WRITE 4 
%define BUFLEN 256 

     SECTION .bss     ; uninitialized data section 
src_str: resb BUFLEN   ; buffer for backwards number 
dec_str: resb BUFLEN     ; number will be converted and put in this buffer 
rlen: resb 4    ; length 

    SECTION .text   ; code begins here 
    global  prt_dec 

    ; Begin subroutine 

prt_dec: 
    push eax 
    push ebx   
    push ecx 
    push edx 
    push esi 
    push edi 
    mov  eax, [esp + 28]  ; store the decimal number 4 bytes each for each push, plus the eip 
    mov esi, src_str  ; point esi to the backwards string buffer 
    mov  edi, dec_str  ; point edi to the new buffer 
     mov  ebx, 10   ; stores the constant 10 in ebx 

div_loop: 
    mov edx, 0   ; clear out edx 
    div  ebx   ; divide the number by 10 
    add  edx, '0'    ; convert from decimal to char 
    mov [esi], edx  ; store char in output buffer 
    inc  esi   ; move to next spot in output buffer 
    inc  ecx   ; keep track of how many chars are added 
    cmp eax, 0   ; is there anything left to divide into? 
    jne  div_loop  ; if so, continue the loop 

output_loop: 
    add esi, ecx  ; move 1 element beyond the end of the buffer 
    mov al, [esi - 1]  ; move the last element in the buffer into al   
    mov [edi], al  ; move it into the first position of the converted output buffer 
    inc edi   ; move to the next position of the converted output buffer 
    dec ecx   ; decrement to move backwards through the output buffer 
    cmp ecx, 0   ; if it doesn't equal 0, continue loop 
    jne output_loop 

print: 
    mov  eax, SYSCALL_WRITE  ; write out string 
     mov  ebx, STDOUT 
     mov  ecx, dec_str 
    mov  edx, 0     
     mov  edx, rlen   
     int  080h 

pop_end: 
    pop  edi   ; move the saved values back into their original registers 
    pop  esi 
    pop edx   
    pop ecx 
    pop ebx 
    pop  eax 
    ret 

    ; End subroutine 

Driver:

%define STDIN 0 
%define STDOUT 1 
%define SYSCALL_EXIT 1 
%define SYSCALL_READ 3 
%define SYSCALL_WRITE 4 


     SECTION .data     ; initialized data section 

lf:  db 10     ; just a linefeed 

msg1: db " plus " 
len1 equ $ - msg1 

msg2: db " minus " 
len2 equ $ - msg2 

msg3: db " equals " 
len3 equ $ - msg3 

     SECTION .text     ; Code section. 
     global _start     ; let loader see entry point 
    extern prt_dec 

_start: 
    mov ebx, 17 
    mov edx, 214123 
    mov edi, 2223187809 
    mov ebp, 1555544444 


    push dword 24 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push dword 0xFFFFFFFF 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push 3413151 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push ebx 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push edx 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push edi 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push ebp 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push 2 
    call prt_dec 
    add esp, 4 

     mov  eax, SYSCALL_WRITE  ; write message 
     mov  ebx, STDOUT 
     mov  ecx, msg1 
     mov  edx, len1 
     int  080h 

    push 3 
    call prt_dec 
    add esp, 4 

     mov  eax, SYSCALL_WRITE  ; write message 
     mov  ebx, STDOUT 
     mov  ecx, msg3 
     mov  edx, len3 
     int  080h 

    push 5 
    call prt_dec 
    add esp, 4 
    call prt_lf 

    push 7 
    call prt_dec 
    add esp, 4 

     mov  eax, SYSCALL_WRITE  ; write message 
     mov  ebx, STDOUT 
     mov  ecx, msg2 
     mov  edx, len2 
     int  080h 

    push 4 
    call prt_dec 
    add esp, 4 

     mov  eax, SYSCALL_WRITE  ; write message 
     mov  ebx, STDOUT 
     mov  ecx, msg3 
     mov  edx, len3 
     int  080h 

    push 3 
    call prt_dec 
    add esp, 4 
    call prt_lf 


     ; final exit 
     ; 
exit: mov  EAX, SYSCALL_EXIT  ; exit function 
     mov  EBX, 0     ; exit code, 0=normal 
     int  080h     ; ask kernel to take over 



    ; A subroutine to print a LF, all registers are preserved 
prt_lf: 
    push eax 
    push ebx 
    push ecx 
    push edx 

     mov  eax, SYSCALL_WRITE  ; write message 
     mov  ebx, STDOUT 
     mov  ecx, lf 
     mov  edx, 1   ; LF is a single character 
     int  080h 

    pop edx 
    pop ecx 
    pop ebx 
    pop eax 
    ret 
+0

При запуске его в отладчике, что он показывает вам, когда вы выполняете инструкции и смотрите на регистры и память? –

+0

* «Я вижу, что по какой-то причине я не могу получить значение от esi в al». * - никогда не существует «любой» причины, CPU - детерминированный конечный автомат. Если вы выберете байт B из адреса A, то по адресу A ** будет ** байт B (как вы можете проверить в виде памяти). Таким образом, в вашем случае, ожидающем символа цифры, либо он был ошибочно записан в буфер 'src_str', и память не содержит его, либо вы извлекаете из неправильного адреса. Повторите попытку в отладчике. (для исправления требуется только несколько изменений в 'output_loop'. И тогда' print: 'тоже имеет одну ошибку. – Ped7g

+0

И один расширенный намек (не связанный с какими-либо из этих ошибок),' inc' и 'dec', влияет на ноль флаг на x86, поэтому в output_loop 'dec ecx'' jnz output_loop' достаточно, что 'cmp ecx, 0' не требуется. – Ped7g

ответ

1

затруднительных я имел в виду (звездочка обозначает строки, которые я сделал осязание), мы надеемся, это будет ясно из комментариев, что я сделал:

... 
div_loop: 
* xor  edx, edx ; clear out edx 
    div  ebx   ; divide the number by 10 
* add  dl, '0'  ; convert from decimal to char 
* mov  [esi], dl ; store char in output buffer 
    inc  esi   ; move to next spot in output buffer 
    inc  ecx   ; keep track of how many chars are added 
* test eax,eax  ; is there anything left to divide into? 
* jnz  div_loop ; if so, continue the loop 
* ; (jnz is same instruction as jne, but in this context I like "zero" more) 

* mov  [rlen], ecx ; store number of characters into variable 

output_loop: 
* ; esi already points beyond last digit, as product of div_loop (removed add) 
* dec  esi   ; point to last/previous digit 
    mov  al, [esi] ; move the char from the div_loop buffer into al 
    mov  [edi], al ; move it into the first position of the converted output buffer 
    inc  edi   ; move to the next position of the converted output buffer 
    dec  ecx   ; decrement to move backwards through the output buffer 
* jnz  output_loop ; if it doesn't equal 0, continue loop 

print: 
    mov  eax, SYSCALL_WRITE  ; write out string 
    mov  ebx, STDOUT 
    mov  ecx, dec_str 
* mov  edx, [rlen] ; read the number of digits from variable 
    int  080h 

    ... 
+0

Интересно ... Я думаю, что большая проблема заключалась в использовании dl вместо edx при перемещении в esi. Это потому, что его размер читается как байт вместо слова? Если нет, вы знаете, почему? Я сделал некоторые из этих изменений, и большинство выходных данных верны, но segfaults, как только он дойдет до 2 плюс 3, но я немного перепутаю dbg, чтобы понять, почему. – swingonaspiral

+0

@swingonaspiral: ваш 'mov [esi], edx' для сохранения цифры также должен работать нормально, просто переписывая бит следующего буфера, который не имеет значения, потому что вы перезаписываете его, а затем, наоборот, в противном случае. То, что ваш output_loop был в основном неудачным из-за 'add esi, ecx' .., это было бы правильным только с' mov esi, src_str' перед этим 'add' в каждом цикле цикла. Что не нужно, так как 'esi' уже указывает за последнюю цифру из письма. В самом деле, если вы будете медленно проходить через него и смотреть регистры + окно памяти, установленное на буферах, оно должно объяснить это лучше меня. – Ped7g

+0

о segfault: я быстро посмотрел, и я понятия не имею, не заметил никакой ошибки в «драйвере». Попытайтесь получить точную инструкцию о сбое в отладчике, а затем выяснить, почему он сбой. Это, как правило, более легкая часть. Затем выясните, как этот код перешел в такое состояние и что он должен был делать в правильной версии и где источник проблемы. – Ped7g