2016-11-26 124 views
1

我是初學者,我需要將16位二進制數轉換爲十六進制的幫助。我已經完成了大部分代碼,但是我需要一些幫助。在程序集中將二進制轉換爲十六進制

  1. 如何使它只接受0和1在輸入和忽略其餘的數字和字母?
  2. 轉換過程後,我得到錯誤的十六進制數。我做錯了什麼?

示例輸入:

預期輸出:

ABCD

電流輸出:

AAAC

這裏是我的代碼:

.MODEL SMALL 
.STACK 1000h 

.DATA 
    title db 'Convert BIN to HEX:.',13,10,'$' 
    HEX_Map DB '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 
    HEX_Out DB "00", 13, 10, '$' ; string with line feed and '$'-terminator 

.CODE 

main PROC 
    mov ax, @DATA     ; Initialize DS 
    mov ds, ax 

    mov ah, 0         
    mov al, 3    ;clearing             
    int 10h                 

    mov ah, 9                 
    lea dx, title               
    int 21h  ;displays title 

    mov dx, 0 

loop16:                 
    mov cx, 16 ;loop goes 16 Times because I need 16 bit binary input 
    mov bx, 0 

;here I'm checking if input numer is 0 or 1, but it doesn't work as I want  
read:                  
    mov ah, 10h                 
    int 16h       

    cmp al, '0'                 
    jb read                   

    cmp al, '1'                
    ja read10 



read10:                  
    mov ah, 0eh                 
    int 10h                  
    sub al, 48 ;conversion, sub 48 from ascii since 0 is on 48th place in ascii, but I'm not sure if this part is must to be or not      

    jmp end_loop 

end_loop:                 
    mov ah, 0  ;ah=0 so we can add ax to bx   
    add bx, ax    

    loop read   
    push bx       ;here I push bx on stack, bx is as my input number           

    mov al, 13 
    mov ah, 0eh 
    int 10h 

    mov al, 10 
    mov ah, 0eh 
    int 10h 



    mov di, OFFSET HEX_Out   ; First argument: pointer 
    pop bx       ;Here I take input number from stack 
    mov ax, bx 
    call IntegerToHexFromMap  ; Call with arguments 
    mov ah, 09h      ; Int 21h/09h: Write string to STDOUT 
    mov dx, OFFSET HEX_Out   ; Pointer to '$'-terminated string 
    int 21h       ; Call MS-DOS 

    mov ah, 10h                 
    int 16h 

    mov ax, 4C00h     ; Int 21h/4Ch: Terminate program (Exit code = 00h) 
    int 21h       ; Call MS-DOS 
main ENDP 

IntegerToHexFromMap PROC 
    mov si, OFFSET Hex_Map   ; Pointer to hex-character table 

    mov bx, ax      ; BX = argument AX 
    and bx, 00FFh     ; Clear BH (just to be on the safe side) 
    shr bx, 1 
    shr bx, 1 
    shr bx, 1 
    shr bx, 1      ; Isolate high nibble (i.e. 4 bits) 
    mov dl, [si+bx]     ; Read hex-character from the table 
    mov [di+0], dl     ; Store character at the first place in the output string 

    mov bx, ax      ; BX = argument AX 
    and bx, 00FFh     ; Clear BH (just to be on the safe side) 
    shr bx, 1 
    shr bx, 1 
    shr bx, 1 
    shr bx, 1      ; Isolate high nibble (i.e. 4 bits) 
    mov dl, [si+bx]     ; Read hex-character from the table 
    mov [di+1], dl     ; Store character at the first place in the output string 

    mov bx, ax      ; BX = argument AX 
    and bx, 00FFh     ; Clear BH (just to be on the safe side) 
    shr bx, 1 
    shr bx, 1 
    shr bx, 1 
    shr bx, 1      ; Isolate high nibble (i.e. 4 bits) 
    mov dl, [si+bx]     ; Read hex-character from the table 
    mov [di+2], dl     ; Store character at the first place in the output string 

    mov bx, ax      ; BX = argument AX (just to be on the safe side) 
    and bx, 00FFh     ; Clear BH (just to be on the safe side) 
    and bl, 0Fh      ; Isolate low nibble (i.e. 4 bits) 
    mov dl, [si+bx]     ; Read hex-character from the table 
    mov [di+3], dl     ; Store character at the second place in the output string 

    ret 
IntegerToHexFromMap ENDP 

IntegerToHexCalculated PROC 
    mov si, OFFSET Hex_Map   ; Pointer to hex-character table 

    mov bx, ax      ; BX = argument AX 
    shr bl, 1 
    shr bl, 1 
    shr bl, 1 
    shr bl, 1      ; Isolate high nibble (i.e. 4 bits) 
    cmp bl, 10      ; Hex 'A'-'F'? 
    jl .1       ; No: skip next line 
    add bl, 7      ; Yes: adjust number for ASCII conversion 
    .1: 
    add bl, 30h      ; Convert to ASCII character 
    mov [di+0], bl     ; Store character at the first place in the output string 

    mov bx, ax      ; BX = argument AX 
    shr bl, 1 
    shr bl, 1 
    shr bl, 1 
    shr bl, 1      ; Isolate high nibble (i.e. 4 bits) 
    cmp bl, 10      ; Hex 'A'-'F'? 
    jl .2       ; No: skip next line 
    add bl, 7      ; Yes: adjust number for ASCII conversion 
    .2: 
    add bl, 30h      ; Convert to ASCII character 
    mov [di+1], bl     ; Store character at the first place in the output string 

    mov bx, ax      ; BX = argument AX 
    shr bl, 1 
    shr bl, 1 
    shr bl, 1 
    shr bl, 1      ; Isolate high nibble (i.e. 4 bits) 
    cmp bl, 10      ; Hex 'A'-'F'? 
    jl .3       ; No: skip next line 
    add bl, 7      ; Yes: adjust number for ASCII conversion 
    .3: 
    add bl, 30h      ; Convert to ASCII character 
    mov [di+2], bl     ; Store character at the first place in the output string 

    mov bx, ax      ; BX = argument AX (just to be on the safe side) 
    and bl, 0Fh      ; Isolate low nibble (i.e. 4 bits) 
    cmp bl, 10      ; Hex 'A'-'F'? 
    jl .4       ; No: skip next line 
    add bl, 7      ; Yes: adjust number for ASCII conversion 
    .4: 
    add bl, 30h      ; Convert to ASCII character 
    mov [di+3], bl     ; Store character at the second place in the output string 

    ret 
IntegerToHexCalculated ENDP 

END main       ; End of assembly with entry-procedure 
+0

我看到你已經檢查了'0'和'1' - 那不行,那麼?此外,請[編輯]你的問題,並提供一個輸入,預期輸出和當前輸出的例子。 – usr2564301

+0

@RadLexus我剛剛編輯。不,它不工作。當我將字母和其他數字作爲輸入時,它允許它,並且我不知道如何讓它只允許0和1. – Anna

+1

您可以在每個中斷調用旁邊添加註釋,詳細說明它的功能嗎?自從我認識他們以來已經有一段時間了。順便說一句,代碼的下半部分有很好的評論。 – usr2564301

回答

3

當你收集到的比特bx不能使用int 10h (0e)的焦炭產量。 int調用要求bl設置爲文本的前景色,bh指向文本頁面。

也在bx你會統計一個人的數量,而不是輸入數量。嘗試它在調試器(你的原始代碼),把loop後,並輸入(如果它不顯示)(例如,「1100110011001100」,bx將輸入(如果某些int通話銷燬bx,我可能會錯誤沒辦法,只是在我的頭上)。

因此,要解決您的輸入部分我會去int 21h, 2,而不是用於顯示的字符,像這樣的(也修正結果的bx積累):

; read 16 bits from keyboard ('0'/'1' characters accepted only) 
    mov cx, 16 ; loop goes 16 Times because I need 16 bit binary input 
    xor bx, bx ; result number (initialized to zero) 

read: 
    mov ah, 10h 
    int 16h  ; read character from keyboard 

    cmp al, '0' 
    jb read  ; ASCII character below '0' -> re-read it 

    cmp al, '1' 
    ja read  ; ASCII character above '1' -> re-read it 

    mov dl,al ; keep ASCII for output in DL 

    shr al,1 ; turn ASCII '0'(0x30)/'1'(0x31) into CF=0/1 (Carry Flag) 
    rcl bx,1 ; enrol that CF into result from right (and shift previous bits up) 

    mov ah,2 ; output character in DL on screen 
    int 21h 

    loop read ; read 16 bits 

我沒有檢查的休息代碼,因爲如果我願意的話,我會強烈地癢徹底改寫它,所以讓我們暫且堅持輸入部分。


調試器應該讓你一步一個指令每單位時間(或把斷點上的任何行,直到它跑起來)。

因此,您可以在每個步驟後檢查寄存器和內存中的值。

如果您將提前在原代碼的add bx,ax的例子把斷點,你應該能夠在調試器讀取(打「1」鍵和調試器上破add後):

ax爲1 (根據按鍵),並且bx從0變爲「1」按鍵的次數(在進一步的迭代中)。

做這樣四「1」按鍵後,它應該是顯而易見的你,那bx等於4(二進制0100)遠遠開來1111,從而爲你想要的東西,不工作,你必須調整從「我想要寫有」到「我真的寫了」,再次閱讀你的代碼和理解需要改變什麼來獲得預期的結果。

在您的情況下,例如在add之前添加指令shl bx,1可以解決這種情況(將舊位移動一個位置「向上」,將最低有效位設置爲零,即「準備添加ax」)。

保持試圖調試器的東西硬,這幾乎是不可能做到在任何大會沒有搞清楚調試器。或者在這裏問,你看到什麼,你不明白什麼。對於Assembly編程來說,這是非常重要的。

另一種選擇是在你的腦袋只爲「模仿」的CPU和從與幫助筆記屏幕(我建議強烈紙,PC不知何故不適合我的工作)運行指令。這比使用調試器更加困難和乏味。在開始「模仿」​​而沒有太多錯誤之前,可能需要幾周/幾個月的時間,所以您通常會在第一次嘗試時發現錯誤。在光明的一面,這會讓你深刻理解CPU的工作原理。


關於第二部分(數字到十六進制字符串轉換)。

我會盡力幫助你瞭解你手頭有什麼,拿起從原來的代碼一些錯誤,演示如何使用它。

所以,你有16位數字,如:

1010 1011 1100 1101 (unsigned decimal 43981) 

我把每一組的4位(很少稱爲「蠶食」)之間的空間,因爲有一個有趣的事實。準確地說,每個半字節形成一個十六進制數字。所以上面的數字是十六進制的:

A B C D  (10, 11, 12, 13) 

檢查每個hexa數字如何與上面的4位對應。

所以你想要的是將原來的16b值分成四個4位數,從最高有效位到最低有效位(b12-b15,b8-b11,b4-b7,b0-b3 => 16位的特定位編號:「b15 b14 b13 ... b2 b1 b0」)。

每一個這樣的號碼將是值0-15(因爲他們是4位,並使用所有可能的組合),這樣的話你想將其轉換成ASCII字符'0' - '9'爲值0-9和'A' - 值爲10-15的'F'

而且每個轉換後的值被存儲到存儲緩衝器,對下一個字節的位置,所以在端部,它們形成串「ABCD」。

這可能聽起來「很明顯」,但它的第2部分內計算的完整描述,所以一定要確保你真正瞭解每一步,這樣你就可以對這個隨時檢查你的代碼和搜索的差異。現在


我會告訴你一些我在第二部分中看到的錯誤,試圖將它連接到上面的「理論」。

數據和結構第一:

HEX_Out DB "00", 13, 10, '$' 

這編譯爲字節:'0', '0', 13, 10, '$'(或30 30 0D 0A 24爲十六進制字節觀察時)。

如果您在上面寫上'A', 'B', 'C', 'D',您能發現問題嗎?

您對數僅保留兩個字節(由「00」),但寫四個字節,所以也1310將被覆蓋。


現在關於IntegerToHexFromMap,從代碼它看起來像你有什麼不明白andshr不(搜索bitwise operations explanation)。

提取前三個字符與bx (copy of ax)相同的b4-b7位,然後爲第四個字母提取位b0-b3。所以這是你試圖將8位轉換代碼擴展到16位,但是你不提取正確的位。

我會盡力廣泛評論它的第一部分,讓你知道你做了什麼。

; bx = 16 bit value, mark each bit as "a#" from a0 to a15 
    and bx, 00FFh 
; the original: a15 a14 a13 ... a2 a1 a0 bits get 
; AND-ed by:  0 0 0 ... 1 1 1 
; resulting into bx = "a7 to a0 remains, rest is cleared to 0" 
    shr bx, 1 
; shifts bx to right by one bit, inserting 0 into top bit 
; bx = 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 a3 a2 a1 (a0 is in CF) 
    shr bx, 1 
; shifts it further 
; bx = 0 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 a3 a2 (a1 is in CF) 
    shr bx, 1 
; bx = 0 0 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 a3 (a2 ...) 
    shr bx, 1 
; bx = 0 0 0 0 0 0 0 0 0 0 0 0 a7 a6 a5 a4 

; so if bx was value 0x1234 at the beginning, now bx = 0x0003 

; conversion to ASCII and write is OK. 

因此,你需要位b4-b7作爲第一個字符,但你需要位b12-b15。我希望你完全瞭解這一點,我知道在開始的時候可能會引起混淆,哪些是爲什麼有時會出現問題,然後離開。

位通常由至少顯著命名(值2 = 1,所以我稱之爲「B0」)到最顯著(值2 = 32768以16比特數的情況下,我稱之爲「 B15" )。

但是由於數字原因,位從最高有效位寫入最低有效位(二進制數),因此「左」的位以b15開始,而「右」的位以b0結束。

轉移到右單元來移動b_iB_(I-1),這有效地減半它的價值,所以shr value,1也可以由兩個被視爲無符號除法。

移位到左邊是從b_iB_第(i + 1),有效地乘以2的值(指令shlsal,都產生相同的結果,作爲b0被設置爲零以兩者)。

sar是「算術」右移,保持最顯著位完整的(符號位)的值,所以-1(所有位爲1),它會再次產生-1,因爲它可以作爲符號的除法兩個所有其他號碼。

自從80286 CPU可以使用shr bx,4(它也可以被看作除以16 = 2 * 2 * 2 * 2)。你真的被迫爲8086編碼?那麼它可能是值得加載cl與4和做shr bx,cl,而不是四個shr bx,1。這讓我非常惱火,有四條相同的路線。

此外,如果你已經瞭解and呢,這必須現在看起來可笑的你:

and bx, 00FFh ; why not 0Fh already here??? 
    and bl, 0Fh 

現在考慮了一會兒如何提取比特B12-B15的第一個字符,以及如何解決您的IntegerToHexFromMap


最終我會告訴你如何重寫它,使代碼非常短,我的意思是源代碼,但也是二進制大小。 (對於性能我會編寫不同的代碼,而不是8086,但這個應該在8086上工作):

警告 - 嘗試通過上述建議自行修復您的版本。只有當你有固定的版本,然後看看我的代碼,作爲一個新想法的靈感,如何寫一些事情是在30年前寫的。另外,如果你正在做學校派遣,請確保你可以從頭說一切關於XLAT的指令,因爲作爲一個講師,我會對使用這一個的任何學生高度懷疑,這是完全的歷史,因爲編譯器不使用它,這是顯而易見的該代碼是由人類編寫的,可能是經驗豐富的。

IntegerToHexFromMap PROC 
    ; ax = number to convert, di = string buffer to write to 
    ; modifies: ax, bx, cx, dx, di 

    ; copy of number to convert (AX will be used for calculation) 
    mov dx, ax 
    ; initialize other helpful values before loop 
    mov bx, OFFSET HEX_Map ; Pointer to hex-character table 
    mov cx, 00404h   ; for rotation of bits and loop counter 
     ; cl = 4, ch = 4 (!) Hexadecimal format allows me 
     ; to position the two "4" easily in single 16b value. 

FourDigitLoop: ; I will do every digit with same code, in a loop 
    ; move next nibble (= hexa digit) in DX into b0-b3 position 
    rol dx, cl 
    ; copy DX b0-b3 into AL, clear other bits (AL = value 0-15) 
    mov al, dl 
    and al, 0Fh 
    ; convert 0-15 in AL into ASCII char by special 8086 instruction 
    ; designed to do exactly this task (ignored by C/C++ compilers :)) 
    xlat 
    ; write it into string, and move string pointer to next char 
    mov [di],al 
    inc di 
    ; loop trough 4 digits (16 bits) 
    dec ch 
    jnz FourDigitLoop 

    ret 
IntegerToHexFromMap ENDP 

如果你只使用不理解它是如何工作的,神會殺死一隻小貓這個代碼...你不想要的,對吧?最終免責聲明:我沒有任何16位x86環境,所以我編寫了所有代碼而沒有測試(我只嘗試編譯它,有時候,但語法必須是NASM樣,所以我不這樣做對於這個MASM/TASM/emu8086來源)。因此,一些語法錯誤可能存在(甚至可能是功能錯誤?:-O),以防萬一你無法使其工作,評論。

+0

謝謝你這麼多東西!這真的幫助了我,現在我明白了更多的它是如何工作的。 我知道你說第二部分是要重寫,但是你還能幫助mi嗎?我想在循環中進行轉換,所以它會重複4次,因爲二進制數字是16位,但我不知道如何開始 – Anna

+0

@關於「如何開始」,安娜回答了延長的問題,你首先用人類語言寫作,然後嘗試將它一點一點地寫入簡單的si中mpler步驟,直到它們開始類似於簡單的計算步驟。循環4次? 「1)counter = 4,2)label:3)遞減計數器,當不爲零時跳轉到標籤」 - >準備從中寫入代碼。「第2部分」答案的第一部分顯示瞭如何將英語中的「16位轉換爲4位4位」。 ...還有**請繼續嘗試在調試器**中運行所有內容並探索它的部件,這樣您就可以觀看CPU,哪條指令會改變什麼。 – Ped7g

+0

@安娜,並開始使用指令參考指南閱讀有關每條指令,它做了什麼。如何發現,爲什麼「這條指令產生了不同於我預期的結果,WTF?」是最簡單的方法。 (我使用谷歌與'「x86指令名稱」指令「搜索字符串,然後尋找與熟悉的域名,如」renejeschke「和其他幾個網站)。 – Ped7g

相關問題