Assembly 这个函数(用汇编编写)打印十六进制数有什么问题?

Assembly 这个函数(用汇编编写)打印十六进制数有什么问题?,assembly,printing,x86,hex,Assembly,Printing,X86,Hex,我只是一名汇编初学者,当时正在阅读尼克·布伦德尔(Nick Blundell)的操作系统书籍,这时我遇到了编写一个可以打印十六进制数的函数的问题。但是,尽管多次验证了逻辑,但我似乎无法找到此代码不起作用的原因。请帮忙,我将不胜感激 HEX_OUT: db '0x0000', 0 MASK: dw 0b1111000000000000 COUNTER: db 3 print_string : pusha

我只是一名汇编初学者,当时正在阅读尼克·布伦德尔(Nick Blundell)的操作系统书籍,这时我遇到了编写一个可以打印十六进制数的函数的问题。但是,尽管多次验证了逻辑,但我似乎无法找到此代码不起作用的原因。请帮忙,我将不胜感激


HEX_OUT:    db  '0x0000', 0
MASK:       dw  0b1111000000000000
COUNTER:    db  3

print_string :

    pusha                               ;SAVES ALL REGISTER VALUES TO BE RESTORED WHEN RETURNING.
    mov ah, 0x0e

    jmp print_loop                      ;NOT COMPULSORY

    print_loop :
        mov al, [bx]
        add bx, 1                       ;ADD 1, NOT 8, NOT 16.
        int 0x10
        cmp al, 0                       ;SETS A FLAG ACCORDING TO RESULT OF COMPARISON.
        jne print_loop                  ;CAUSES LOOP.
        jmp final_block                 ;CAN BE REPLACED BY THE STATEMENTS IN final_block, NO NEED FOR MAKING NEW LABEL.

    final_block :
        popa
        ret                             ;RETURNS TO THE POINT WHERE CALL HAPPENED.

print_hex :
    pusha

    mov bx, HEX_OUT
    add bx, 2

    alter_loop :                        ;LOOP TO ALTER HEX_OUT
        mov ax, [MASK]
        cmp ax, 0                       ;CONDITION TO END LOOP
        je after_loop

        mov ax, dx                      ;GETTING(INTO AX) THE DATA FOR N-TH POSITION 
        and ax, [MASK]

        mov cx, [COUNTER]

        shift_loop :
            cmp cx, 0
            je end_shift_loop
            shr ax, 4
            sub cx, 1
        end_shift_loop:

        cmp ax, 0x0009                       ;DO HEX->ALPHABET IF NUMBER IS GREATER THAN 9
        jle skip_hex_to_alphabet

        add ax, 39                       ;EQUIVALENT TO (sub ax, 48--- sub ax, 9 ---add ax, 96)

        skip_hex_to_alphabet :

        add ax, 48                      ;ADDING 48(ASCII OF 0), IS ALREADY SUBTRACTED IF N-TH NUMBER>9

        mov [bx], al                    ;STORING DATA IN LOCATION POINTED TO BY BX
        add bx, 1                       ;INCREMENT FOR LOOP
        mov ax, [MASK]                  ;CHANGING MASK
        shr ax, 4
        mov [MASK], ax

        mov ax, [COUNTER]               ;UPDATING COUNTER
        sub ax, 1
        mov [COUNTER], ax

        jmp alter_loop

    after_loop :
    mov bx, HEX_OUT
    call print_string    
    popa
    ret  
调用以下函数时:-

mov dx, 0x1fd6
call print_hex

它打印的是
0xWGd0
而不是
0x1fd6

您缺少返回到
shift\u循环的跳转,并且错误地声明了
计数器的大小

由于您使用的是
mov-cx,[COUNTER]
COUNTER
必须是一个单词,请修复它:

COUNTER:    dw  3
最后,您没有正确地移动遮罩值。在第一次迭代时,
和ax,[MASK]
产生
0x1000
,在
shift\u循环中,由于它只迭代一次,因此这一点减少到
0x0100

用跳转结束循环:

    shift_loop :
        cmp cx, 0
        je end_shift_loop
        shr ax, 4
        sub cx, 1
    jmp shift_loop

    end_shift_loop:

我的二毛钱:我已经写和读汇编超过二十年了,你们的代码让我很困惑。我没想到十六进制打印例程会在静态掩码上循环,并将结果存储在静态字符串中。对于给定的任务来说,它太复杂了。
您可以简单地使用一个减少4的可变移位计数器和一个(常数)掩码来提取半字节。然后甚至可以使用16字节的查找表将半字节转换为字符,从而避免分支

另外,由于您是为DOS编程的,因此在线查找TASM的副本并使用其调试器(
td
-Turbo debugger)是非常值得的。 很容易对变量使用错误的大小并处理垃圾,调试器会立即向您显示这一点


如果你喜欢一个具体的例子,这里有一个简单的实现

;AX = number to print in hex
hex:
  mov cx, 12              ;Shift counter, we start isolating the higher nibble (which starts at 12)
  mov bx, hexDigits       ;Lookup tables for the digitals
  mov dx, ax              ;We need a copy of the number and AX is used by the int10 service

.extract:
  mov si, dx              ;Make a copy of the original number so we don't lose it. Also we need it in SI for addressing purpose
  shr si, cl              ;Isolate a nibble by bringing it at the lower position
  and si, 0fh             ;Isolate the nibble by masking off any higher nibble

  mov al, [bx + si]       ;Transform the nibble into a digit (that's why we needed it in SI)
  mov ah, 0eh             ;You can also lift this out of the loop. It put it here for readability.
  int 10h                 ;Print it

  sub cx, 4               ;Next nibble is 4 bits apart
jnc .extract              ;Keep looping until we go from 0000h to 0fffch. This will set the CF

  ret

hexDigits db "0123456789abcdef"

您缺少返回到
shift\u循环的跳转,并且错误地声明了
计数器的大小

由于您使用的是
mov-cx,[COUNTER]
COUNTER
必须是一个单词,请修复它:

COUNTER:    dw  3
最后,您没有正确地移动遮罩值。在第一次迭代时,
和ax,[MASK]
产生
0x1000
,在
shift\u循环中,由于它只迭代一次,因此这一点减少到
0x0100

用跳转结束循环:

    shift_loop :
        cmp cx, 0
        je end_shift_loop
        shr ax, 4
        sub cx, 1
    jmp shift_loop

    end_shift_loop:

我的二毛钱:我已经写和读汇编超过二十年了,你们的代码让我很困惑。我没想到十六进制打印例程会在静态掩码上循环,并将结果存储在静态字符串中。对于给定的任务来说,它太复杂了。
您可以简单地使用一个减少4的可变移位计数器和一个(常数)掩码来提取半字节。然后甚至可以使用16字节的查找表将半字节转换为字符,从而避免分支

另外,由于您是为DOS编程的,因此在线查找TASM的副本并使用其调试器(
td
-Turbo debugger)是非常值得的。 很容易对变量使用错误的大小并处理垃圾,调试器会立即向您显示这一点


如果你喜欢一个具体的例子,这里有一个简单的实现

;AX = number to print in hex
hex:
  mov cx, 12              ;Shift counter, we start isolating the higher nibble (which starts at 12)
  mov bx, hexDigits       ;Lookup tables for the digitals
  mov dx, ax              ;We need a copy of the number and AX is used by the int10 service

.extract:
  mov si, dx              ;Make a copy of the original number so we don't lose it. Also we need it in SI for addressing purpose
  shr si, cl              ;Isolate a nibble by bringing it at the lower position
  and si, 0fh             ;Isolate the nibble by masking off any higher nibble

  mov al, [bx + si]       ;Transform the nibble into a digit (that's why we needed it in SI)
  mov ah, 0eh             ;You can also lift this out of the loop. It put it here for readability.
  int 10h                 ;Print it

  sub cx, 4               ;Next nibble is 4 bits apart
jnc .extract              ;Keep looping until we go from 0000h to 0fffch. This will set the CF

  ret

hexDigits db "0123456789abcdef"

您正在执行
addax,48
,即使执行了
addax,39
。是的,@MargaretBloom。所以,当要打印一个数字时,48被加上,当要打印字母表时,87被加上。因此,要打印
0xa
,程序首先使用掩码识别10,然后添加87,生成97,这是“a”的ascii提示:在将一个半字节移动或旋转到寄存器底部后,通过掩码简化代码,因此您可以使用简单的
和reg,0xf
。IDK为什么要对任何东西使用静态存储,而不仅仅是寄存器。您已经使用了
pusha
来保存/恢复它们。看看哪个有一个很好的简单实现,并链接到另外两个16位实现。@PeterCordes,我对汇编相当陌生。你能给我指一些说明不同变量及其声明的语法和类型的资源吗?我想这会很有帮助的。谢谢:)。有关文档和指南的更多链接,请参见。您正在执行
add ax,48
,即使执行了
add ax,39
。是的,@MargaretBloom。所以,当要打印一个数字时,48被加上,当要打印字母表时,87被加上。因此,要打印
0xa
,程序首先使用掩码识别10,然后添加87,生成97,这是“a”的ascii提示:在将一个半字节移动或旋转到寄存器底部后,通过掩码简化代码,因此您可以使用简单的
和reg,0xf
。IDK为什么要对任何东西使用静态存储,而不仅仅是寄存器。您已经使用了
pusha
来保存/恢复它们。看看哪个有一个很好的简单实现,并链接到另外两个16位实现。@PeterCordes,我对汇编相当陌生。你能给我指一些说明不同变量及其声明的语法和类型的资源吗?我想这会很有帮助的。谢谢:)。有关文档和指南的更多链接,请参见。@JeeveshJuneja欢迎您。我添加了一个具体的例子,以防你想知道我们到底在说什么。@JeeveshJuneja不客气。我添加了一个具体的例子,以防你想知道我们到底在说什么。