本文介绍了在组装中将 bin 转换为十六进制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm beginner and I need help with converting 16-bit binary number to hex. I have done most of the code, but I need help with a couple of things.

  1. 如何让它只接受输入中的 0 和 1 而忽略其余的数字和字母?
  2. 转换过程后,我得到了错误的十六进制数字.我做错了什么?

示例输入:

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

推荐答案

您不能使用 int 10h (0e) 用于将位收集到 bx 中时的字符输出.int 调用需要将 bl 设置为文本的前景色,并将 bh 设置为指向文本页面.

You can't use int 10h (0e) for char output when you collect bits into bx. That int call requires bl set to foreground colour of text and bh to point to text page.

同样在 bx 中,您将计算 1 的数量,而不是输入的数字.在调试器中尝试(您的原始代码),在 loop 之后放置断点并输入(如果没有显示,则盲目输入)例如1100110011001100",bx 将是 8(如果某些int 调用destroy bx,我可能是错的,我没有运行它,只是在我的脑海中).

Also in bx you will count number of ones, not the input number. Try it in debugger (your original code), put breakpoint after loop and enter (blindly, if it doesn't show) for example "1100110011001100", bx will be 8 (I may be wrong if some int call destroy bx, I didn't run it, just in my head).

所以为了修复你的输入部分,我会选择 int 21h, 2 而不是用于显示字符,像这样(也修复了 bx 中结果的累积):

So to fix your input part I would go for int 21h, 2 instead for displaying the chars, like this (also fixes the accumulation of result in 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

我没有检查其余的代码,因为如果我检查了,我会很想完全重写它,所以暂时只看输入部分.

I didn't check the rest of the code, because if I would, I would have strong itch to rewrite it completely, so let stick with the input part only for the moment.

调试器应该允许您每次执行一条指令(或在任何行上放置断点,并一直运行到它为止).

The debugger should allow you to step one instruction per time (or to put breakpoints on any line, and run up till it).

这样你就可以在每一步之后检查寄存器和内存中的值.

So you can examine values in registers and memory after each step.

如果您将例如在原始代码中将断点放在 add bx,ax 之前,您应该能够在调试器中读取(在按下1"键并且调试器在 添加):

If you will for example put breakpoint ahead of your add bx,ax in original code, you should be able to read in debugger (after hitting "1" key and debugger breaking on the add) that:

ax 为 1(根据按键),bx 从 0 到1"按键的计数(在进一步迭代中).

ax is 1 (according to key pressed), and bx goes from 0 to the count of "1" key presses (in further iterations).

在按四次1"键之后,你应该很明显,bx 等于 4(0100 二进制)离 1111 很远,因此有些东西不能按你的意愿工作,你必须从我想在那里写的东西"重新调整到我真正写的东西",再次阅读你的代码并理解需要改变什么才能得到预期的结果.

After doing like four "1" key presses it should be obvious to you, that bx equal to 4 (0100 in binary) is far off from 1111, thus something doesn't work as you wanted and you have to readjust from "what I wanted to wrote there" to "what I really wrote", read your code again and understand what needs to be changed to get expected result.

例如,在您的情况下,在 add 之前添加指令 shl bx,1 将解决这种情况(将旧位向上"移动一个位置,留下最低有效位设置为零,即准备添加斧头").

In your case for example adding instruction shl bx,1 ahead of add would fix the situation (moving old bits by one position "up", leaving least significant bit set to zero, ie. "ready for add ax").

继续努力尝试调试器的东西,如果不弄清楚调试器,几乎不可能在 Assembly 中做任何事情.或者继续在这里问,你看到了什么,你不明白什么.这对于汇编编程来说绝对是必不可少的.

Keep trying the debugger stuff hard, it's almost impossible to do anything in Assembly without figuring out debugger. Or keep asking here, what you see and what you don't understand. It's really absolutely essential for Assembly programming.

其他选项只是在您的头脑中模拟"CPU,并通过帮助说明运行屏幕上的说明(我强烈建议使用纸张,PC 不知何故不适合我).这比使用调试器要困难和乏味得多.可能需要数周/数月才能开始模拟"而不会出现太多错误,因此您通常会在第一次尝试时发现错误.从好的方面来说,这会让您深入了解 CPU 的工作原理.

Other option is just to "emulate" CPU in your head and run the instructions from the screen with help notes (I suggest strongly paper, PC somehow doesn't work well for me). This is much more difficult and tedious, than using debugger. May take weeks/months before you start to "emulate" without too many mistakes, so you will spot bugs usually on first try. On the bright side this would give you deep understanding of how CPU works.

我会尽力帮助您理解手头的内容,并从原始代码中找出一些错误来演示如何使用它.

I will try to help you understand what you have at hand, and pick up some mistakes from original code to demonstrate how to work with it.

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

So you have 16 bit number, like:

1010 1011 1100 1101  (unsigned decimal 43981)

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

I put spaces between each group of 4 bits (rarely called as "nibble"), because there's a funny fact. Each nibble forms single hexadecimal digit, exactly. So the number above is in hexadecimal:

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

检查每个十六进制数字如何与上面的 4 位对应.

Check how each hexa digit corresponds with the 4 bits above.

所以您想要的是将原始 16b 值分解为四个 4 位数字,从最重要到最不重要(b12-b15、b8-b11、b4-b7、b0-b3 => 16 位数字中的特定位: "b15 b14 b13 ... b2 b1 b0").

So what you want is to break the original 16b value into four 4 bit numbers, from most significant to least significant (b12-b15, b8-b11, b4-b7, b0-b3 => particular bits from 16 bit number: "b15 b14 b13 ... b2 b1 b0").

每个这样的数字的值都是 0-15(因为它们是 4 位,并且使用所有可能的组合),所以你想把它变成 ASCII 字符 '0'-'9' 用于值 0-9,'A'-'F' 用于值 10-15.

Each such number will be of value 0-15 (because they are 4 bits, and using all possible combinations), so then you want to turn that into ASCII character '0'-'9' for values 0-9, and 'A'-'F' for values 10-15.

并且每个转换后的值都存储到内存缓冲区中,在下一个字节位置,因此最终它们形成字符串ABCD".

And each converted value is stored into memory buffer, on next byte position, so in the end they form string "ABCD".

这听起来显而易见",但它是第 2 部分内部计算的完整描述,因此请确保您真正理解每个步骤,以便您可以随时对照此检查代码并寻找差异.

This may sound "obvious", but it's complete description of inner-calculation of part 2, so make sure you really understand each step, so you can check your code against this any time and search for differences.

现在我将向您展示我在第二部分中看到的一些错误,尝试将其与上面的理论"联系起来.

Now I will show you some of the bugs I see in second part, trying to connect it to the "theory" above.

数据和结构优先:

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

这将编译为字节:'0', '0', 13, 10, '$'(或 30 30 0D 0A 24 当被视为十六进制字节时).

This compiles to bytes: '0', '0', 13, 10, '$' (or 30 30 0D 0A 24 when viewed as hexadecimal bytes).

如果你在上面写'A', 'B', 'C', 'D',你能发现问题吗?

If you write 'A', 'B', 'C', 'D' over it, can you spot the problem?

现在关于 IntegerToHexFromMap,从代码看来你不明白 andshr​​ 的作用(搜索 按位运算说明).


Now about IntegerToHexFromMap, from the code it looks like you don't understand what and and shr does (search for the bitwise operations explanation).

您从 bx(ax 的副本) 中为前三个字符提取相同的 b4-b7 位,然后为第四个字母提取位 b0-b3.所以这是您尝试将 8 位转换代码扩展到 16 位,但您没有提取正确的位.

You extract for first three characters the same b4-b7 bits from bx (copy of ax), then for the fourth letter you extract bits b0-b3. So this is your try to extend 8 bit conversion code to 16 bit, but you don't extract the correct bits.

我将尝试对它的第一部分进行广泛的评论,让你知道你做了什么.

I will try to extensively comment the first part of it, to give you idea what you did.

; 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 位.我希望你能完全理解这个,我知道一开始可能会让人困惑哪个位是哪个,以及为什么有时右边有东西然后左边.

So you take bits b4-b7 for first character, but you need bits b12-b15. I hope you fully get this one, I know it can be confusing at start which bit is which and why sometimes there is something on right and then left.

位通常从最不重要的(值 2 = 1,所以我称之为b0")到最重要的(值 2 = 32768,以防万一16 位数字,我称之为b15").

Bits are usually named from least significant (value 2 = 1, so I call it "b0") to most significant (value 2 = 32768 in case of 16 bit number, I call it "b15").

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

But for numeric reasons bits are written from most significant to least significant (in binary numbers), so bits on "left" starts with b15, and bits on "right" end with b0.

向右移动意味着将 b_i 移动到 b_(i-1),这实际上将其值减半,所以 shr​​ value,1也可以看作是无符号除以二.

Shifting to right means to move b_i to b_(i-1), which effectively halves it's value, so shr value,1 can be viewed also as unsigned division by two.

左移是从 b_ib_(i+1),有效地将值乘以 2(指令 shlsal,两者都产生相同的结果,因为两者都将 b0 设置为零).

Shifting to left is from b_i to b_(i+1), effectively multiplies the value by two (instructions shl and sal, both producing same result, as b0 is set to zero with both).

sar 是算术"右移,保持最高有效位的值不变(符号位),因此对于 -1(所有位均为 1)它将产生再次-1,对于所有其他数字,它的工作方式为有符号除以二.

sar is "arithmetic" shift right, keeping value of most significant bit intact (sign bit), so for -1 (all bits are 1) it will produce again -1, for all other numbers it works as signed division by two.

顺便说一句,从 80286 CPU 开始,您可以使用 shr​​ bx,4(也可以看作除以 16 = 2*2*2*2).你真的被迫为 8086 编码吗?那么可能值得用 4 加载 cl 并执行 shr​​ bx,cl,而不是四个 shr​​ bx,1.这让我很恼火,四行相同.

BTW since 80286 CPU you can use shr bx,4 (which can be also seen as divide by 16 = 2*2*2*2). Are you really forced to code for 8086? Then it may be worth to load cl with 4 and do shr bx,cl, instead of four shr bx,1. That annoys hell out of me, four identical lines.

此外,如果您已经了解 的作用,那么现在您一定觉得这很荒谬:

Also if you already understand what and does, this must look ridiculous to you now:

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

现在考虑一下如何提取第一个字符的 b12-b15 位以及如何修复您的 IntegerToHexFromMap.

Now contemplate for a while how to extract bits b12-b15 for first character and how to fix your IntegerToHexFromMap.

最终我将向您展示我将如何重写它以使代码非常短,我的意思是源代码,但也是二进制大小.(为了性能,我会编写不同的代码,而不是 8086,但这个代码应该适用于 8086):

And ultimately I will show you how I would rewrite it to have the code very short, I mean source, but also binary size. (for performance I would write different code, and not for 8086, but this one should work on 8086):

警告 - 尝试按照上述建议自行修复您的版本.只有当你有固定版本时,再看看我的代码,作为对 30 年前一些东西是如何编写的新想法的灵感.此外,如果您正在做学校分配,请确保您可以说出有关XLAT 指令的所有内容头,因为作为一名讲师,我会高度怀疑任何使用这个的学生,它是完整的历史,因为编译器不使用它,很明显代码是由人类编写的,并且可能是有经验的.

WARNING - try to fix your version on your own by above advices. Only when you will have fixed version, then look at my code, as an inspiration for new ideas how some things were written 30 years ago. Also if you are doing school assigment, make sure you can say everything about XLAT instruction from head, because as a lector I would be highly suspicious about any student using this one, it's total history and as compilers don't use it, it's obvious the code was written by human, and probably experienced one.

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

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

If you will just use this code without understanding how it works, god will kill a kitten... you don't want that, right?

最终免责声明:我没有任何 16 位 x86 环境,所以我没有测试就写了所有代码(我有时只是尝试编译它,但语法必须是 NASM 式的,所以我不这样做此 MASM/TASM/emu8086 来源).因此可能存在一些语法错误(甚至可能是功能错误?:-O),如果您无法使其正常工作,请发表评论.

Final disclaimer: I don't have any 16bit x86 environment, so I wrote all the code without testing (I only try to compile it sometimes, but the syntax must be NASM-like, so I don't do that for this MASM/TASM/emu8086 sources). Thus some syntax bugs may be there (maybe even functional bug? :-O ), in case you will be unable to make it work, comment.

这篇关于在组装中将 bin 转换为十六进制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 22:11