2017-04-20 75 views
0

這是一個簡單的問題,但它讓我頭暈目眩。我需要將一串字符(輸入爲負十進制數)轉換爲無符號整數。 rdi寄存器保存要轉換的字符串。 rax寄存器將保存結果。x86 NASM將字符串轉換爲整數

xor rsi, rsi 
    xor rax, rax 
    xor dl, dl 
    xor rdx, rdx 
convert: 
    mov dl, [rdi+rsi] ;+rsi causes segmentation fault 

    cmp dl, "-" 
    jz increment 

    cmp dl, "." 
    jz dtoi_end 

    sub dl, "0" 

    mov rdx, 10 
    mul rdx 

    add rax, dl   ;invalid combination 

    inc rsi 
    jmp convert 

increment: 
    inc rsi 
    jmp convert 

convert_end: 
    ret 
  1. 我需要遍歷每個角色,我試圖通過註冊RSI使用此。但是每次我嘗試這個時,都會出現分段錯誤。

  2. 組合錯誤無效。我知道這是因爲寄存器是不同的大小,但我失去了如何繼續添加轉換後的ascii值回rax。

這裏有一個類似的問題,幫助我瞭解的過程中更好,但我已經碰了壁: Convert string to int. x86 32 bit Assembler using Nasm

+0

'DL'是'RDX'的一部分。你對'DL'所做的事情你也對'RDX'做了,反之亦然。其次,'mul rdx'採用操作數和'RAX',將它們相乘並將結果存儲在'RDX:RAX'中。這不是你明顯想要的。你必須重新考慮你對寄存器的使用。 – rkhb

+0

當你訪問你不應該訪問的內存時,'mov dl,[rdi + rsi]'崩潰。 rdi指向哪裏?我個人不會添加rdi和rsi,因爲您使用2個寄存器用於一個目的。初始化rsi以指向「字符串」,並用「mov dl,[rsi]'+'inc rsi'加載字節 – Tommylee2k

回答

1

我需要遍歷每個角色,並且我想通過使用rsi寄存器來使用它。但是每次我嘗試這個時,都會出現分段錯誤。

根據您所顯示的代碼,並RDI包含字符串的開頭的地址的說法,我可以看到幾個不同的原因,你會得到在負載分段錯誤。

也許問題是RDI包含一個8字符的ASCII字符串(通過值傳遞),而不是包含該字符串(通過引用傳遞)的內存位置的地址?

另一種更可能的可能性是它在循環的前幾次迭代中工作正常,但是隨後您開始嘗試讀取字符串的末尾,因爲您沒有正確終止循環。您所展示的代碼中沒有dtoi_end標籤,也沒有您實際跳到convert_end標籤的地方。這些應該是同一個標籤嗎?如果我傳遞字符串「-2」,會發生什麼?你的循環何時終止?在我看來,它不會!

您需要某種方式來指示整個字符串已被處理。有幾個常用的方法。一種是在字符串末尾使用一個哨兵終結符字符,就像C使用ASCII NUL字符一樣。在你的循環內部,你會檢查正在處理的字符是否爲0(NUL),如果是,跳出循環。另一個選擇是將字符串的長度作爲附加參數傳遞給函數,就像Pascal對計數長度字符串所做的那樣。然後,你需要在循環內部進行測試,以檢查是否已經處理了足夠多的字符,如果是,則跳出循環。

我會盡量不要太講究這個,但你應該已經能夠通過使用調試器自己檢測到這個問題。逐行執行代碼,觀察變量/寄存器的值,並確保您瞭解正在發生的事情。這基本上就是我在分析你的代碼時所做的,除了我的頭腦是調試器,在我自己的腦海裏「執行」了代碼。儘管如此,讓計算機執行它更容易(也更不容易出錯),這就是調試器發明的原因。如果你的代碼不工作,並且你沒有在調試器中逐行執行,你還沒有努力去解決這個問題。事實上,單步穿越你寫的每個功能是一個很好的習慣,因爲(A)它會確保你理解你寫的內容的邏輯,(B)它會幫助你找到錯誤。

組合錯誤無效。我知道這是因爲寄存器是不同的大小,但我失去了如何繼續添加轉換後的ascii值回rax。

您必須使尺寸匹配。你可以做add al, dl,但是你會限制結果爲8位的字節。這可能不是你想要的。因此,您需要將dl轉換爲64位QWORD,如rax。要做到這一點的顯而易見的方法是使用零擴展的MOVZX指令。換句話說,它將值「擴展」爲更大的大小,用0填充高位。這就是你想要的無符號值。對於帶符號的值,您需要執行符號感知擴展(即將符號位考慮在內),並且要這樣做,您可以使用MOVSX指令。

在代碼:

movzx rdx, dl 
add rax, rdx 

別注意,因爲評論者之一指出的,DL僅僅是RDX寄存器的最低8位:

| 63 - 32 | 31 - 16 | 15 - 8 | 7 - 0 | 
-------------------------------------- 
        | DH | DL | 
-------------------------------------- 
      |   EDX   | 
-------------------------------------- 
|     RDX    | 

因此,它對xor dl, dlxor rdx, rdx是多餘的。後者完成前者。另外,每次修改dl時,實際上都會修改rdx的最低8位,這會導致錯誤的結果。提示,提示:這是你用調試器單步執行的其他東西,但你可能已經發現了它(儘管你可能不明白爲什麼!)。

此外,根本不需要做xor rdx, rdx!您可以通過執行xor edx, edx完成相同的任務,more efficiently


只是爲了好玩,這裏是一個可能的實現代碼:

; Parameters: RDI == address of start of character string 
;    RCX == number of characters in string 
; Clobbers: RDX, RSI 
; Returns: result is in RAX 

    xor esi, esi 

convert: 
    ; See if we've done enough characters by checking the length of the string 
    ; against our current index. 
    cmp rsi, rcx 
    jge convert_end 

    ; Get the next character from the string. 
    mov dl, BYTE [rdi + rsi] 

    cmp dl, "-" 
    je increment 

    cmp dl, "." 
    je convert_end 

    ; Efficient way to multiply by 10. 
    ; (Faster and less difficult to write than the MUL instruction.) 
    add rax, rax 
    lea rax, [4 * rax + rax] 

    sub dl, "0" 
    movzx rdx, dl 
    add rax, rdx 

    ; (fall through to increment---no reason for redundant instructions!) 

increment: 
    inc rsi   ; increment index/counter 
    jmp convert  ; keep looping 

convert_end: 
    ret 

(警告:這樣做的邏輯是未經測試我只是改寫了更優化的方式將現有的代碼,而不bug)。