Печать шестнадцатеричных цифр со сборкой

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

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

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

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

Любой совет или образец кода будут оценены.

Сначала напишите простую процедуру, которая принимает значение 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 } 

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

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

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

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

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

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

У Hex есть проблема, что 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;

     для (ра = 0x80; ра; ра >> = 1)
     {
         if (ra & x) output_char (0x31);  else output_char (0x30);
     }
 }

Может показаться логичным использовать unsigned char выше, но unsigned int, в зависимости от целевого процессора, может создать гораздо лучший (более чистый / быстрый) код. но это еще одна тема

Вышеприведенное может выглядеть так, как показано на ассемблере (намеренно НЕ используя x86)

  ...
  mov r4, r0
  mov r5, # 0x80
 Вверх:
  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
 и r0, r0, # 0x7
 добавьте r0, r0, # 0x30
 bl output_char
 mov r0, r4, lsr # 3
 и r0, r0, # 0x7
 добавьте r0, r0, # 0x30
 bl output_char
 и r0, r4, # 0x7
 добавьте r0, r0, # 0x30
 bl output_char
 ...

Один (8 бит) байт в шестнадцатеричном виде может выглядеть так:

 ...
 mov r4, r0
 mov r0, r4, lsr # 4
 и r0, r0, # 0xF
 cmp r0, # 9
 addhi r0, r0, # 0x37
 addls r0, r0, # 0x30
 bl output_character
 и 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
 ...
 Вверх:
 ldr r4, my_variable
 mov r0, r4, lsr # 4
 и r0, r0, # 0xF
 cmp r0, # 9
 addhi r0, r0, # 0x37
 addls r0, r0, # 0x30
 bl output_character
 и r0, r4, # 0xF
 cmp r0, # 9
 addhi r0, r0, # 0x37
 addls r0, r0, # 0x30
 bl output_character
 ...
 ldr r4, my_variable
 добавить r4, r4, # 1
 str r4, my_variable
 cmp r4, # 7, N - 7
 bne top
 ...
 my_variable .word 0

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

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

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

 .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 : макрос газа altmacro с знаком процента в параметре по умолчанию не выполняется: «% оператор нуждается в абсолютном выражении»

Результат:

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

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

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

Синтаксис 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 
  • Является ли использование double быстрее, чем float?
  • Самый быстрый способ сделать горизонтальную векторную сумму float на x86
  • Эффективная функция сравнения целого числа
  • Изменение целевых настроек процессора в Visual Studio 2010 Express
  • 16-разрядные режимы адресации NASM x86
  • Режимы микросовключения и адресации
  • System.BadImageFormatException: не удалось загрузить файл или сборку (из installutil.exe)
  • Какие целые операции с дополнением 2 можно использовать без обнуления высоких бит в входах, если требуется только низкая часть результата?
  • Изменение режима округления с плавающей запятой
  • Как получить доступ к массиву символов и изменить строчные буквы на верхний регистр, и наоборот
  • Потерянные циклы на Intel? Несоответствие между rdtsc и CPU_CLK_UNHALTED.REF_TSC
  • Давайте будем гением компьютера.