Я пытаюсь изучить сборку NASM, но, похоже, я стараюсь бороться с тем, что кажется просто на языках высокого уровня.
Все учебники, которые я использую, обсуждают с использованием строк - на самом деле это кажется одной из их любимых вещей. Печать привет мир, переход от верхнего регистра к нижнему регистру и т.д.
Однако я пытаюсь понять, как увеличивать и печатать шестнадцатеричные цифры в сборке NASM и не знаю, как действовать. Например, если я хочу напечатать # 1 - n в Hex, как бы это сделать без использования библиотек C (которые все ссылки мне удалось найти)?
Моя основная идея заключалась бы в том, чтобы иметь переменную в разделе .data, которую я бы продолжал увеличивать. Но как мне извлечь шестнадцатеричное значение из этого места? Кажется, мне нужно преобразовать его в строку сначала...?
Приветствуется любой совет или образец кода.
Ответ 2
Это домашнее задание?
Биты - это биты. Бит, байт, слово, двойное слово, это аппаратные термины, что-то наборы инструкций/ассемблер будут ссылаться. шестнадцатеричные, десятичные, восьмеричные, неподписанные, подписанные, строковые, символьные и т.д. являются проявлениями языков программирования. Аналогично .text,.bss,.data и т.д. Также являются проявлениями программных инструментов, набор инструкций не заботится о том, чтобы один адрес был .data и один из них был .text, это одна и та же инструкция в любом случае. Есть причины, почему все эти вещи языка программирования существуют, иногда очень хорошие причины, но не путайте при попытке решить эту проблему.
Чтобы преобразовать из бит в человекообразный ascii, вам сначала нужно знать свою таблицу ascii и побитовые операторы, а также, логический сдвиг, арифметический сдвиг и т.д. Плюс загружать и хранить и другие вещи.
Подумайте математически, что нужно, чтобы получить от некоторого числа в регистре/памяти в ascii hex. Скажите 0x1234, который равен 0b0001001000110100. Чтобы человек мог его прочитать, да, вам нужно получить его в строку из-за отсутствия лучшего термина, но вам необязательно хранить четыре символа плюс нуль в соседних ячейках памяти, чтобы что-то с ним делать. Это зависит от вашей выходной функции. Обычно объекты вывода, основанные на символах, сводятся к одному виду output_char(), который называется много раз.
Вы можете преобразовать в строку, но это больше работает, для каждого символа ascii вы вычисляете вызов какой-то одноразовой выходной функции. putchar() - пример функции символа выходного символа байта.
Итак, для двоичного кода вы хотите изучить один бит за раз и создать 0x30 или 0x31. Для восьмеричных, 3 бита за раз и создайте от 0x30 до 0x37. Hex основан на 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 - 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 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 бит чисел и вы хотели конвертировать в восьмеричную. Возьмите по три бита за раз (помните, что люди читают слева направо, поэтому начинайте с верхних бит) и добавьте 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) ассемблером, поэтому он остается как упражнение читателя для разработки эквивалента. Дело в том, что это смещение, а также добавление этой материи, разбиение ее на элементарные шаги и инструкции оттуда естественно оттуда.