assembly 用汇编打印十六进制数字[重复]

laximzn5  于 2023-04-30  发布在  其他
关注(0)|答案(4)|浏览(109)

此问题已在此处有答案

How to convert a binary integer number to a hex string?(3个答案)
两年前关闭。
我正在努力学习NASM汇编,但我似乎正在努力与什么似乎简单的高级语言。
我使用的所有教科书都在讨论如何使用字符串--事实上,这似乎是他们最喜欢的东西之一。打印hello world,从大写改为小写等等
然而,我试图了解如何在NASM程序集中递增和打印十六进制数字,不知道如何继续。例如,如果我想用十六进制打印#1 - n,如果不使用C库(我能找到的所有引用都使用了C库),我该怎么做呢?
我的主要想法是在。数据部分,我会继续增加。但是如何从这个位置提取十六进制值呢?我似乎需要先把它转换成字符串。...
任何建议或示例代码将不胜感激。

w1e3prcc

w1e3prcc1#

首先编写一个简单的例程,它取一个nybble值(0..15)作为输入,并输出十六进制字符('0 '。“9”,“A”,“F”)。
接下来编写一个例程,它以一个字节值作为输入,然后调用上面的例程两次,输出2个十六进制字符,即:即每个半字节一个。
最后,对于一个N字节的整数,你需要一个例程来调用第二个例程N次,每个字节一次。
您可能会发现,首先用伪代码或HLL(如C)来表达这一点很有帮助,然后再考虑如何将其转换为asm,e。g的。

void print_nybble(uint8_t n)
{
    if (n < 10) // handle '0' .. '9'
        putchar(n + '0');
    else // handle 'A'..'F'
        putchar(n - 10 + 'A');
}

void print_byte(uint8_t n)
{
    print_nybble(n >> 4); // print hi nybble
    print_nybble(n & 15); // print lo nybble
}

void print_int16(uint16_t n)
{
    print_byte(n >> 8); // print hi byte
    print_byte(n & 255); // print lo byte
}
ao218c7q

ao218c7q2#

这是家庭作业吗
比特就是比特。位、字节、字、双字,这些都是硬件术语,是指令集/汇编程序将要引用的。十六进制、十进制、八进制、无符号、有符号、字符串、字符等是编程语言表现形式。同样。文本,。bss、.data等也是软件工具的表现形式,指令集不关心一个地址是否存在。数据和一个存在。文本,无论哪种方式都是相同的指令。所有这些编程语言的东西存在是有原因的,有时候是非常好的原因,但是在试图解决这个问题时不要感到困惑。
要从位转换为人类可读的ascii,首先需要知道ascii表,以及按位运算符,and,or,逻辑移位,算术移位等加上装载和存储和其他东西。
从数学上想一想,从寄存器/内存中的某个数字转换成ascii十六进制需要什么。比如说0x 1234,它是0 b 0001001000110100。对于一个人来说,阅读它,是的,你需要把它变成一个字符串,因为没有更好的术语,但你不一定需要在相邻的内存位置存储四个字符加上一个null,以便对它做一些事情。这取决于你的输出函数。通常,基于字符的输出实体可以归结为一个被多次调用的outputchar()。
你可以转换成一个字符串,但这是更多的工作,对于每个ascii字符,你计算调用某种基于单个字符的输出函数。putchar()是字节输出字符类型函数的一个示例。
因此,对于二进制,您需要一次检查一位并创建0x 30或0x 31。对于八进制,一次3位,并创建0x 30到0x 37。十六进制是基于一次4位。
Hex有一个问题,我们想要使用的16个字符在ascii表中找不到彼此相邻的。因此,您可以使用0x 30到0x 39来表示0到9,而使用0x 41到0x 46或0x 61到0x 66来表示A到F,这取决于您的偏好或要求。因此,对于每个半字节,您可以与0xF进行AND,与9进行比较,并添加0x 30或0x 37(10+ 0x 37 = 0x 41,11+ 0x 37 = 0x 42等)。
从寄存器中的位转换为二进制的ascii表示。如果存储器中的位是1,则显示1(0x 31 ascii),如果存储器中的位是0,则显示0(0x 30 ascii)。

void showbin ( unsigned char x )
{
    unsigned char ra;

    for(ra=0x80;ra;ra>>=1)
    {
        if(ra&x) output_char(0x31); else output_char(0x30);
    }
}

使用上面的unsigned char似乎是合乎逻辑的,但是根据目标处理器的不同,unsigned int可以产生更好(更干净/更快)的代码。但那是另一个主题了
上面可以看起来像汇编程序中这样(故意不使用x86)

...
 mov r4,r0
 mov r5,#0x80
top:
 tst r4,r5
 moveq r0,#0x30
 movne r0,#0x31
 bl output_char
 mov r5,r5, lsr #1
 cmp r5,#0
 bne top
 ...

Unrolled更容易写,速度也更快,但代价是占用更多内存

...
 tst    r4, #0x80
 moveq  r0, #0x30
 movne  r0, #0x31
 bl output_char
 tst    r4, #0x40
 moveq  r0, #0x30
 movne  r0, #0x31
 bl output_char
 tst    r4, #0x20
 moveq  r0, #0x30
 movne  r0, #0x31
 bl output_char
 ...

假设您有9位数字,并希望转换为八进制。一次取三位(记住人类从左到右阅读,所以从高位开始),并添加0x 30以获得0x 30到0x 37。

...
mov r4,r0
mov r0,r4,lsr #6
and r0,r0,#0x7
add r0,r0,#0x30
bl output_char
mov r0,r4,lsr #3
and r0,r0,#0x7
add r0,r0,#0x30
bl output_char
and r0,r4,#0x7
add r0,r0,#0x30
bl output_char
...

十六进制中的单个(8位)字节可能看起来像:

...
mov r4,r0
mov r0,r4,lsr #4
and r0,r0,#0xF
cmp r0,#9
addhi r0,r0,#0x37
addls r0,r0,#0x30
bl output_character
and r0,r4,#0xF
cmp r0,#9
addhi r0,r0,#0x37
addls r0,r0,#0x30
bl output_character
...

从1到N进行循环,将该值存储在内存中并从内存中阅读(.数据),以十六进制输出:

...
mov r4,#1
str r4,my_variable
...
top:
ldr r4,my_variable
mov r0,r4,lsr #4
and r0,r0,#0xF
cmp r0,#9
addhi r0,r0,#0x37
addls r0,r0,#0x30
bl output_character
and r0,r4,#0xF
cmp r0,#9
addhi r0,r0,#0x37
addls r0,r0,#0x30
bl output_character
...
ldr r4,my_variable
add r4,r4,#1
str r4,my_variable
cmp r4,#7 ;say N is 7
bne top
...
my_variable .word 0

如果你有足够的寄存器,保存到ram是有点浪费。虽然使用x86,您可以直接在内存上操作,而不必通过寄存器。
x86与上面的(ARM)汇编程序不一样,所以读者可以通过练习来计算出等价物。关键是,它是转移,anding,和添加那个物质,把它分解成基本的步骤,指令就从那里自然地落出来了。

vom3gejh

vom3gejh3#

快速脏GAS宏

.altmacro

/*
Convert a byte to hex ASCII value.
c: r/m8 byte to be converted
Output: two ASCII characters, is stored in `al:bl`
*/
.macro HEX c
    mov \c, %al
    mov \c, %bl
    shr $4, %al
    HEX_NIBBLE al
    and $0x0F, %bl
    HEX_NIBBLE bl
.endm

/*
Convert the low nibble of a r8 reg to ASCII of 8-bit in-place.
reg: r8 to be converted
Output: stored in reg itself.
*/
.macro HEX_NIBBLE reg
    LOCAL letter, end
    cmp $10, %\reg
    jae letter
    /* 0x30 == '0' */
    add $0x30, %\reg
    jmp end
letter:
    /* 0x57 == 'A' - 10 */
    add $0x57, %\reg
end:
.endm

使用方法:

mov $1A, %al
HEX <%al>

使用<>是因为.altmacroGas altmacro macro with a percent sign in a default parameter fails with "% operator needs absolute expression"
结果:

  • %al包含0x31,即ASCII中的'1'
  • %bl包含0x41,即ASCII中的'A'

现在你可以用%al%bl,e做任何你想做的事情。例如:

  • 在多个字节上循环并将它们复制到内存(确保分配的内存是字节的两倍)
  • 使用系统或BIOS调用打印它们
jhiyze9q

jhiyze9q4#

英特尔语法。这是从我的 Bootstrap ,但你应该能够得到的想法。

print_value_of_CX:

    print_value_of_C_high:

        print_value_of_C_high_high_part:
            MOV AH, CH
            SHR AH, 0x4
            CALL byte_hex_printer

        print_value_of_C_high_low_part:
            MOV AH, CH
            SHL AH, 0x4
            SHR AH, 0x4
            CALL byte_hex_printer

    print_value_of_C_low:

        print_value_of_C_low_high_part:
            MOV AH, CL
            SHR AH, 0x4
            CALL byte_hex_printer

        print_value_of_C_low_low_part:
            MOV AH, CL
            SHL AH, 0x4
            SHR AH, 0x4
            CALL byte_hex_printer

byte_hex_printer:
    CMP AH, 0x00
    JE move_char_for_zero_into_AL_to_print
    CMP AH, 0x01
    JE move_char_for_one_into_AL_to_print
    CMP AH, 0x02
    JE move_char_for_two_into_AL_to_print
    CMP AH, 0x03
    JE move_char_for_three_into_AL_to_print
    CMP AH, 0x04
    JE move_char_for_four_into_AL_to_print
    CMP AH, 0x05
    JE move_char_for_five_into_AL_to_print
    CMP AH, 0x06
    JE move_char_for_six_into_AL_to_print
    CMP AH, 0x07
    JE move_char_for_seven_into_AL_to_print
    CMP AH, 0x08
    JE move_char_for_eight_into_AL_to_print
    CMP AH, 0x09
    JE move_char_for_nine_into_AL_to_print
    CMP AH, 0x0A
    JE move_char_for_A_into_AL_to_print
    CMP AH, 0x0B
    JE move_char_for_B_into_AL_to_print
    CMP AH, 0x0C
    JE move_char_for_C_into_AL_to_print
    CMP AH, 0x0D
    JE move_char_for_D_into_AL_to_print
    CMP AH, 0x0E
    JE move_char_for_E_into_AL_to_print
    CMP AH, 0x0F
    JE move_char_for_F_into_AL_to_print

        move_char_for_zero_into_AL_to_print:
        MOV AL, 0x30
        CALL print_teletype_stringB
        RET
        move_char_for_one_into_AL_to_print:
        MOV AL, 0x31
        CALL print_teletype_stringB
        RET
        move_char_for_two_into_AL_to_print:
        MOV AL, 0x32
        CALL print_teletype_stringB
        RET
        move_char_for_three_into_AL_to_print:
        MOV AL, 0x33
        CALL print_teletype_stringB
        RET
        move_char_for_four_into_AL_to_print:
        MOV AL, 0x34
        CALL print_teletype_stringB
        RET
        move_char_for_five_into_AL_to_print:
        MOV AL, 0x35
        CALL print_teletype_stringB
        RET
        move_char_for_six_into_AL_to_print:
        MOV AL, 0x36
        CALL print_teletype_stringB
        RET
        move_char_for_seven_into_AL_to_print:
        MOV AL, 0x37
        CALL print_teletype_stringB
        RET
        move_char_for_eight_into_AL_to_print:
        MOV AL, 0x38
        CALL print_teletype_stringB
        RET
        move_char_for_nine_into_AL_to_print:
        MOV AL, 0x39
        CALL print_teletype_stringB
        RET
        move_char_for_A_into_AL_to_print:
        MOV AL, 0x41
        CALL print_teletype_stringB
        RET
        move_char_for_B_into_AL_to_print:
        MOV AL, 0x42
        CALL print_teletype_stringB
        RET
        move_char_for_C_into_AL_to_print:
        MOV AL, 0x43
        CALL print_teletype_stringB
        RET
        move_char_for_D_into_AL_to_print:
        MOV AL, 0x44
        CALL print_teletype_stringB
        RET
        move_char_for_E_into_AL_to_print:
        MOV AL, 0x45
        CALL print_teletype_stringB
        RET
        move_char_for_F_into_AL_to_print:
        MOV AL, 0x46
        CALL print_teletype_stringB
        RET

相关问题