Печать шестнадцатеричных цифр с помощью сборки - PullRequest
4 голосов
/ 04 октября 2010

Я пытаюсь изучить сборку NASM, но мне кажется, что я борюсь с тем, что кажется просто языками высокого уровня.

Все учебники, которые я использую, обсуждают с использованием строк - на самом деле,это, кажется, одна из их любимых вещей.Печать hello world, переход с прописных букв на строчные и т. Д.

Однако я пытаюсь понять, как увеличивать и печатать шестнадцатеричные цифры в сборке NASM, и не знаю, как это сделать.Например, если я хочу напечатать # 1 - n в шестнадцатеричном формате, как бы я сделал это без использования библиотек C (которые я смог найти во всех ссылках)?

Моя основная идея была бычтобы иметь переменную в разделе .data, который я буду продолжать увеличивать.Но как извлечь шестнадцатеричное значение из этого местоположения?Мне кажется, сначала нужно преобразовать его в строку ...?

Буду признателен за любой совет или пример кода.

Ответы [ 4 ]

8 голосов
/ 04 октября 2010

Сначала напишите простую подпрограмму, которая принимает значение nybble (0..15) и выводит шестнадцатеричный символ ('0' .. '9', 'A' .. 'F').

Затем напишите подпрограмму, которая принимает значение байта в качестве входных данных, а затем дважды вызывает вышеуказанную подпрограмму для вывода 2 шестнадцатеричных символов, то есть по одному для каждого nybble.

Наконец, для N-байтового целого числа вам нужна подпрограмма, которая вызывает эту вторую подпрограмму N раз, по одному разу для каждого байта.

Может оказаться полезным сначала выразить это в псевдокоде или в HLL, например, в C, а затем подумать о том, как перевести это в asm, например,

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
}

print_int16(uint16_t n)
{
    print_byte(n >> 8); // print hi byte
    print_byte(n & 255); // print lo byte
}
1 голос

Быстрый и грязный ГАЗ-макрос

.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>

<> используются из-за .altmacro: Gas ​​altmacroмакрос со знаком процента в параметре по умолчанию завершается с ошибкой «Оператору% необходимо абсолютное выражение»

Результат:

  • %al содержит 0x31, что составляет '1' вASCII
  • %bl содержит 0x41, то есть 'A' в ASCII

Теперь вы можете делать все, что вы хотите с %al и %bl, например:

  • зацикливание на нескольких байтах и ​​копирование их в память (выделите в два раза больше памяти, чем имеется байтов)
  • распечатайте их с помощью системных вызовов или вызовов BIOS
1 голос
/ 05 октября 2010

Это домашнее задание?

Биты - биты.Бит, Байт, слово, двойное слово, это аппаратные термины, на которые собирается ссылаться наборы команд / ассемблер.шестнадцатеричный, десятичный, восьмеричный, без знака, со знаком, строка, символ и т. д. являются проявлениями языков программирования.Точно так же .text, .bss, .data и т. Д. Также являются проявлениями программных инструментов: набор команд не заботится о том, чтобы один адрес был .data, а другой - .text, в любом случае это одна и та же инструкция.Существуют причины, по которым существуют все эти вещи на языке программирования, иногда это очень веские причины, но не пытайтесь запутаться, пытаясь решить эту проблему.

Чтобы преобразовать биты в читаемый человеком ascii, вам сначала нужно знать свой ascii.таблица, и побитовые операторы, и, или, логический сдвиг, арифметический сдвиг и т. д. Плюс загрузка и сохранение и другие вещи.

Математически подумайте, что нужно получить из некоторого числа в регистре / памяти в шестнадцатеричный ascii,Скажите 0x1234, который является 0b0001001000110100.Чтобы человек мог его прочитать, да, вам нужно поместить его в строку из-за отсутствия лучшего термина, но вам не обязательно хранить четыре символа плюс ноль в смежных областях памяти, чтобы что-то с ним сделать.Это зависит от вашей функции вывода.Обычно символьные выходные объекты сводятся к одному выходному параметру (), который вызывается много раз.

Вы можете конвертировать в строку, но это более трудоемко, поскольку для каждого вычисляемого вами символа ascii прямо сейчас вызывается какая-то односимвольная выходная функция.putchar () является примером функции вывода символьного байта.

Итак, для двоичного файла вы хотите исследовать один бит за раз и создать 0x30 или 0x31.Для восьмеричного, 3 бита за раз и создать от 0x30 до 0x37.Шестнадцатеричный код основан на 4 битах за раз.

Хекс имеет проблему в том, что 16 символов, которые мы хотим использовать, не найдены рядом друг с другом в таблице ascii.Таким образом, вы используете от 0x30 до 0x39 от 0 до 9, но от 0x41 до 0x46 или от 0x61 до 0x66 от A до F, в зависимости от ваших предпочтений или требований.Таким образом, для каждого nybble вы можете И с 0xF, сравнить с 9 и ADD 0x30 или 0x37 (10 + 0x37 = 0x41, 11 + 0x37 = 0x42 и т. Д.).

Преобразование из битов в регистре в представление asciiбинарный.Если бит в памяти был 1 показом, то 1 (0x31 ascii) бита был 0 показом 0 (0x30 в 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 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
 ...

Развернутый легче писать и будет немного быстрее,компромисс - больше используемой памяти

 ...
 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-битные числа и вы хотите преобразовать их в восьмеричное.Возьмите три бита за раз (помните, что люди читают слева направо, поэтому начните с верхних битов) и добавьте 0x30, чтобы получить от 0x30 до 0x37.

...
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, сохранение этого значения в памяти и чтение его из памяти (.data), вывод в шестнадцатеричном виде:

...
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

Сохранение в оперативную память являетсянемного траты, если у вас достаточно регистров.Хотя с x86 вы можете работать непосредственно с памятью, и вам не нужно проходить через регистры.

x86 - это не то же самое, что и вышеупомянутый (ARM) ассемблер, так что читателю оставлено в качестве упражнения для чтения подобного.Суть в том, что это смещение, добавление и добавление этого вещества разбивают его на элементарные шаги, и инструкции оттуда естественно выпадают.

0 голосов
/ 17 апреля 2018

Синтаксис Intel. Это из моего загрузчика, но у вас должна получиться идея.

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
...