【问题标题】:Printing hex from dx with nasm使用 nasm 从 dx 打印十六进制
【发布时间】:2015-02-22 13:50:31
【问题描述】:

我实际上想用 nasm 打印 dx 寄存器的内容。因此内容为 16 位十六进制数字,例如 0x12AB。

因此我首先实现了一个能够打印字符串的函数:

print_string:
    pusha
    mov ah, 0xe

print_character:
    mov al, [bx]
    inc bx
    or al, al
    jz print_done
    int 0x10
    jmp print_character

print_done:
    popa
    ret

你可以这样使用这个函数:

mov bx, MSG
call print_string

MSG:
    db 'Test',0

现在我想要一个函数,将十六进制转换为字符串,以便 print_string 能够打印它。我在想类似的事情:

print_hex:
    pusha
    mov bx, HEX_OUT
    ; HEX_OUT is a kind of template string
    ; now i want to get the hex of dx into this template in order to be able to print it
    ; However I'm not sure how to manage this
    call print_string
    popa
    ret

HEX_OUT:
    db '0x0000', 0

不幸的是,我不确定如何将十六进制从 dx 获取到 bx,分别是 HEX_OUT。有人可以帮助我还是有人有想法?

我想在最后这样使用它:

mov dx, 0x12AA
call print_hex

提前谢谢你!

更新:

如前所述,我可以像这样实现分离和打印:

print_hex:
    pusha
    mov bx, PREFIX
    call print_string

next_character:
    mov bx, dx
    and bx, 0xf000
    shr bx, 4
    add bh, 0x30
    cmp bh, 0x39
    jg add_7

print_character_hex:
   mov al, bh
   mov ah, 0x0e
   int 0x10
   shl dx, 4
   or dx, dx
   jnz next_character
   popa
   ret

add_7
   add bh, 0x7
   jmp print_character_hex

PREFIX:
   db '0x', 0

我试过这样的方法来用我的函数和缓冲区打印它:

print_hex:
    ;Added this here    
    mov cx, HEX_OUT + 2

print_character_hex:
    mov [cx], bh

尽管由于“无效的有效地址”而无法组装。我需要做什么才能做到这一点?

【问题讨论】:

  • 到目前为止,您尝试过什么将数字拆分为十六进制数字? (不包括 print_string 子例程的工作;您还不如使用 DOS 函数 09h。)
  • 您可以通过右移 4 位的倍数并与 0x0F 进行与运算来隔离十六进制数字。然后将该值映射到您放置在字符串缓冲区中的字符。重复直到你覆盖了所有的数字。
  • 为了首先分离高半字节(半字节),我们可以使用“rol dx, 4”指令。然后我们可以将最低字节移动到第二个寄存器中,以便将值与 0x0F 进行与运算。现在我们可以通过添加 0x30 将值转换为 ASCII,对于从 10 到 15 的所有数字,我们必须添加数字 7 才能得到字母从 A 到 F。
  • 感谢您的回复。到目前为止,我所做的是隔离十六进制数字并将其转换为相应的 ascii 代码。我也可以按数字打印。但是我想使用字符串缓冲区和我的字符串打印功能。现在我无法将 ascii 放入我的缓冲区。你能帮我解决这个问题吗?我已经更新了上面的帖子。
  • 好的,我做到了。我不能为此目的使用 cx 。相反,我不得不使用 si 作为索引寄存器。

标签: string assembly printing hex nasm


【解决方案1】:

这会调整已接受的答案,以包括 Kyle G. 对 0 问题的修复。

; vim: :set ft=nasm:

[bits 16]

; subroutine to print a hex number.
;
; ```
; dx = the hexadecimal value to print
; ```
;
; Usage
;
; ```
; mov dx, 0x1fb6
; call print_hex
; ```
;
; used as an answer here: https://stackoverflow.com/a/27686875/7132678
;
print_hex:
        ; push all registers onto the stack
        pusha

        ; use si to keep track of the current char in our template string mov si, HEX_OUT + 2
        mov si, HEX_OUT + 2

        ; start a counter of how many nibbles we've processed, stop at 4
        mov cx, 0

next_character:
        ; increment the counter for each nibble
        inc cx

        ; isolate this nibble
        mov bx, dx
        and bx, 0xf000
        shr bx, 4

        ; add 0x30 to get the ASCII digit value
        add bh, 0x30

        ; If our hex digit was > 9, it'll be > 0x39, so add 7 to get
        ; ASCII letters
        cmp bh, 0x39
        jg add_7

add_character_hex:
        ; put the current nibble into our string template
        mov [si], bh

        ; increment our template string's char position
        inc si

        ; shift dx by 4 to start on the next nibble (to the right)
        shl dx, 4

        ; exit if we've processed all 4 nibbles, else process the next
        ; nibble
        cmp cx, 4
        jnz next_character
        jmp _done

_done:
        ; copy the current nibble's ASCII value to a char in our template
        ; string
        mov bx, HEX_OUT

        ; print our template string
        call print_string

        ; pop all arguments
        popa

        ; return from subroutine
        ret

add_7:
        ; add 7 to our current nibble's ASCII value, in order to get letters
        add bh, 0x7

        ; add the current nibble's ASCII
        jmp add_character_hex

; our global template string. We'll replace the zero digits here with the
; actual nibble values from the hex input.
HEX_OUT:
        db '0x0000', 0

另外,值得注意的是,这是来自https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf,第 3.5.1 节,问题 5,第 23 页的练习。

【讨论】:

    【解决方案2】:

    好吧,我能够管理它。谢谢您的帮助!这是工作代码:

    print_hex:
       pusha
       mov si, HEX_OUT + 2
    
    next_character:
      mov bx, dx
      and bx, 0xf000
      shr bx, 4
      add bh, 0x30
      cmp bh, 0x39
      jg add_7
    
    add_character_hex:
      mov al, bh
      mov [si], bh
      inc si
      shl dx, 4
      or dx, dx
      jnz next_character
      mov bx, HEX_OUT
      call print_string
      popa
      ret
    
    add_7:
      add bh, 0x7
      jmp add_character_hex
    
    HEX_OUT:
      db '0x0000', 0
    

    【讨论】:

    • 当 16 位字以 1 个或多个 0 结尾时,这最终会失败。尝试调用它两次,第一次使用mov dx,0x1fb6,第二次使用mov dx,0x2000。它将打印出0x1FB6,然后是0x2FB6。要更正此问题,请使用 ax(或任何其他未使用的寄存器)作为计数器来强制循环运行 4 次。
    【解决方案3】:

    您可以使用此代码打印一个十六进制数字

    mov cx,12
      mov dx,1f34h 
      st: 
       push dx
       shr dx,cl  
       and dl,15
      print:
      add dl, 0x30 
      cmp dl, 0x39 
      jle go
      add dl, 0x7  
      go:
      mov ah, 0x2
      int 0x21  
      sub cl,4  
      pop dx  
      cmp cl,0
      jl end_code
      jmp st
      end_code:
    

    【讨论】:

    • 好的 SO 答案应该对代码进行注释和/或包含文本来解释如何它们的工作原理,以及为什么这样做是个好主意。此外,为了获得良好的风格,您应该将代码缩进比标签多一级。就效率而言,这段代码实际上还不错。或者至少代码大小:使用int 0x21 打印不会很快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多