在装配中将bin转换为hex

时间:2016-11-26 11:58:13

标签: assembly x86 dos x86-16

我是初学者,我需要帮助将16位二进制数转换为十六进制。我已经完成了大部分代码,但我需要一些帮助。

  1. 如何使它只接受输入中的0和1并忽略其余的数字和字母?
  2. 转换过程后,我的十六进制编号错误。我做错了什么?
  3. 示例输入:

      

    1010101111001101

    预期产出:

      

    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
    

1 个答案:

答案 0 :(得分:3)

当您将位收集到bx时,不能将int 10h (0e)用于char输出。 int调用要求将bl设置为文本的前景色,将bh设置为指向文本页。

同样在bx中,您将计算一些数字,而不是输入数字。在调试器中尝试它(您的原始代码),在loop之后放置断点并输入(盲目地,如果它没有显示)例如" 1100110011001100",bx将是8(如果某些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"键和调试器打破{{1那个:

add为1(根据按下的键),ax从0变为" 1"按键(在进一步的迭代中)。

做完四个" 1"按键应该是显而易见的,bx等于bx(二进制为4)远离0100,因此某些内容不起作用你想要的,你必须从"我想在那里写的东西"为了我真正写下的内容",再次阅读你的代码并理解需要改变什么来获得预期的结果。

例如,在你的情况下,在1111之前添加指令shl bx,1将修复情况(将旧位移动一个位置"向上"将最低有效位设置为零,即。"准备添加ax")。

继续努力尝试调试器的东西,在没有找出调试器的情况下,几乎不可能在Assembly中做任何事情。或者继续问这里,你看到了什么,你不明白。这对于汇编编程来说确实是绝对必要的。

其他选择仅仅是模仿" CPU在你的头脑中并通过屏幕上的说明运行帮助说明(我强烈建议使用PC,PC在某种程度上对我不起作用)。与使用调试器相比,这更加困难和繁琐。可能需要数周/数月才能开始模拟"没有太多错误,所以你会在第一次尝试时发现错误。从好的方面来说,这将使您深入了解CPU的工作原理。

关于第二部分(十六进制字符串转换的数字)。

我会尽力帮助您了解您手边的内容,并从原始代码中挑选一些错误来演示如何使用它。

所以你有16位数字,比如:

add

我在每组4位之间放置空格(很少称为" nibble"),因为这是一个有趣的事实。每个半字节都精确地形成单个十六进制数字。所以上面的数字是十六进制的:

1010 1011 1100 1101  (unsigned decimal 43981)

检查每个六位数如何与上面的4位对应。

所以你想要的是将原来的16b值分成四个4位数,从最高有效到最低有效(b12-b15,b8-b11,b4-b7,b0-b3 => 16位的特定位号码:" b15 b14 b13 ... b2 b1 b0")。

每个这样的数字的值为0-15(因为它们是4位,并使用所有可能的组合),因此您希望将其转换为ASCII字符A B C D (10, 11, 12, 13) - '0'以获取值0-9,'9' - 'A'代表值10-15。

每个转换后的值都存储在内存缓冲区的下一个字节位置,最后它们形成字符串" ABCD"。

这可能听起来很明显"但它完整地描述了第2部分的内部计算,因此请确保您真正理解每一步,以便随时检查您的代码并寻找差异。

现在我将向您展示我在第二部分中看到的一些错误,试图将其连接到"理论"上方。

首先是数据和结构:

'F'

这会编译为字节:HEX_Out DB "00", 13, 10, '$' (或当被视为十六进制字节时为'0', '0', 13, 10, '$'。)

如果你在上面写30 30 0D 0A 24,你能发现问题吗?

  

您只为数字保留了两个字节(按" 00"),但是您写了四个字节,因此'A', 'B', 'C', 'D'13也会被覆盖。

现在关于10,从代码看起来你不明白IntegerToHexFromMapand做了什么(搜索bitwise operations explanation)。

shr中提取前三个字符相同的b4-b7位,然后在第四个字母中提取位b0-b3。所以这是你尝试将8位转换代码扩展到16位,但是你没有提取正确的位。

我会尝试广泛评论它的第一部分,让你知道你做了什么。

bx (copy of ax)

所以你把第b4-b7位作为第一个字符,但你需要b12-b15位。我希望你完全得到这一个,我知道它可能会让你感到困惑,哪一点是哪个以及为什么有时会有一些东西然后离开。

比特通常从最低有效(值2 0 = 1,因此我将其称为" b0")命名为最重要(值2 15 = 32768如果是16位数,我称之为" b15")。

但是由于数字原因,位从最高有效位写入最低有效位(以二进制数字表示),因此位于"左侧"以b15开头,位于"右边"以b0结束。

向右移动意味着将 b_i 移动到 b_(i-1),这实际上将其值减半,因此; 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. 可以是也被视为两个未签名的除法。

向左移动是从 b_i b_(i + 1),实际上将该值乘以2(指令shr value,1shl ,两者都产生相同的结果,因为两者都将b0设置为零。

sal是"算术"向右移位,保持最高有效位的值(符号位),因此对于sar(所有位为1),它将再次产生-1,对于所有其他数字,它将作为有符号除以2。

BTW,因为80286 CPU可以使用-1(也可以看作除以16 = 2 * 2 * 2 * 2)。你真的被迫为8086编码吗?然后可能值得加载shr bx,4 4并执行cl,而不是4 shr bx,cl。这让我感到很生气,四条相同的线条。

另外,如果您已经了解shr bx,1的作用,那么现在看起来一定很荒谬:

and

现在考虑一下如何为第一个字符提取b12-b15位以及如何修复 and bx, 00FFh ; why not 0Fh already here??? and bl, 0Fh

最后,我将告诉你如何重写它以使代码非常短,我的意思是源代码,但也是二进制大小。 (为了性能,我会写不同的代码,而不是8086,但这个应该在8086上工作):

警告 - 尝试通过以上建议自行修复您的版本。只有当你有固定的版本,然后看看我的代码,作为30年前写一些东西的新想法的灵感。此外,如果你正在做学校的分配,请确保你可以从头部说出关于XLAT指令的所有内容,因为作为一名教练,我会高度怀疑任何使用这个的学生,它的总历史和编译器不使用它,很明显,代码是由人类编写的,可能是经验丰富的。

IntegerToHexFromMap

如果你只是使用这段代码而不了解它是如何工作的,那么上帝会杀死一只小猫......你不想要那个,对吗?

最终免责声明:我没有任何16位x86环境,因此我编写了所有代码而未进行测试(我有时只尝试编译它,但语法必须是NASM类似的,所以我不会'为此MASM / TASM / emu8086源代码执行此操作)。因此可能存在一些语法错误(甚至可能是功能错误?: - O),如果您无法使其工作,请发表评论。