#assembly #x86 #byte #nasm
#сборка #x86 #байт #nasm
Вопрос:
Я изучаю язык ассемблера в collage и я почти уверен, что ничего не понимаю, тем более, что классы находятся в Сети.
Вероятно, это очень простой вопрос, но, тем не менее, я не знаю ответа. Предположим, я хочу добавить два числа, которые я сохранил как байты a = 255
и b = 255
. Я не думаю, что это сработает, если мы сделаем это:
segment data use32 class=data
a db 255
b db 255
segment code use32 class=code
start:
mov al, [a]
add al, [b]
Поскольку сумма равна 510
, которая не может быть сохранена, AL
потому AL
что это регистр размером в 1 байт, верно? Я попробовал это с помощью NASM (который я почти не знаю, как его использовать), и я не думаю, что получил правильный ответ. Регистр EAX выглядит так в конце 0019FFFE
, а 510 в hexa — 1FE
так что, я полагаю, я должен 1FE
где-то увидеть, чего у нас нет!
Так что, я думаю, это неправильный путь. Затем я подумал, что, возможно, я смогу как-то использовать AX
для добавления. Итак, я сделал что-то вроде этого:
segment data use32 class=data
a db 255
b db 255
segment code use32 class=code
start:
mov ax, word 0
mov al, [a]
add al, [b]
Я думал, что если мы инициализируем AX
как 0, а затем добавим что-то к AL
этому, это приведет к значению, которое превысит байт, результат будет распространяться AX
, и мы получим правильный ответ. Но я добрался 001900FE
до конца, так что снова нет никаких следов a 1FE
.
Итак, как я должен добавить эти два значения? Извините, если это глупый вопрос, но я действительно в замешательстве. Я почти ничего не сохранил из class, а источников в Интернете очень мало, когда речь идет о сборке, поэтому я действительно не нашел ответа на свой вопрос в Интернете.
Комментарии:
1. Вы добавляете к
al
, который является только младшими 8 битамиeax
. Инструкция не влияет на старшие биты. Да, это ужасная ошибка x86, которая сегодня никому не нужна, и она восходит к 8-разрядным предшественникам x86 (8008/8080/8085), потому что даже тогда Intel была обязана совместимости для своего совершенно нового дизайна процессора. Если вы хотите расширить add, вы можете сначала загрузить в регистр, а затем добавить весь (16- или 32-разрядный) регистр.
Ответ №1:
Если вы еще не используете максимальный размер, который обрабатывает процессор, вы можете увеличить размер.
Вместо этого выполните сложение в 16 бит. Процессоры обычно не поддерживают добавление 8-битных значений в 16-битный ответ, но вы можете увеличить 8-битные значения до 16 бит, а затем выполнить 16-битное сложение.
Чтобы расширить до 16 бит, вы можете загрузить значение в al
, затем очистить ah
, так что теперь ax
содержит 16-битное значение. Сделайте то же самое в bl / bh / bx или cl / ch / cx, и затем вы сможете сложить два 16-разрядных значения вместе. Предполагается, что ваши входные данные представляют собой байты без знака. (Если бы они были подписанными байтами, вы бы использовали операции расширения знака для увеличения до 16 бит.) Вы также можете ax
сначала очистить, затем загрузить в al
, и у вас будет 16-битное нулевое расширенное значение ax
.
Если вы пытаетесь сделать то же самое, но вы уже используете максимальный размер для процессора, скажем, с 32-разрядным, вы бы проверили флаг переноса после добавления, который сообщит вам, было ли добавление переполнено или нет (что означает, что оно умещается в 32 бита).
Кстати, флаг переноса также устанавливается путем 8-битного сложения, поэтому вы также можете проверить это, чтобы увидеть, больше ли результат 8 бит.
Как правило, сложение двух n-битных значений дает n 1 битный ответ, а умножение двух n-битных значений дает 2n-битный ответ. На x86 флаг переноса используется в качестве дополнительного бита дополнительно, а инструкции умножения предназначены для предоставления больших ответов.
Комментарии:
1. В OP упоминается EAX; это означает, что они могут просто
movzx eax, byte [a]
/movzx ecx, byte [b]
вместо того, чтобы возиться с частичными регистрами, на 386-совместимом процессоре.2. @ErikEidt Это прояснило многие мои недоразумения, большое спасибо. Вы сказали: «Вы также можете сначала очистить ax, затем загрузить в al, и у вас будет 16-битное нулевое расширенное значение в ax». Очищаем ли мы ax (поэтому мы устанавливаем его равным 0), чтобы очистить его от мусорных значений, которые могут быть в памяти до запуска программы? Я правильно понял?
3. Да, обычно мы не очищаем регистры после их использования, поэтому мы должны думать о них как о неизвестных значениях. Обычно это не проблема, поскольку мы просто загружаем регистр новыми значениями, но для расширения нуля требуется несколько нулей. Кроме того, как упоминал @PeterCordes, вы можете увеличить их до 32 бит, и есть одна инструкция для одновременной загрузки и расширения нуля.
4. @ErikEidt Еще одна путаница, если вы не возражаете ответить. Вы сказали: «Если вы пытаетесь сделать то же самое, но вы уже используете ….. который скажет вам, переполнено ли добавление или нет (что означает, что оно умещается в 32 бита)». Что, если я получу переполнение? Есть ли способ обойти и найти фактическую сумму, если мы уже используем самые большие регистры, такие как EAX и EBX. Кроме того, скажем, я очищаю AX и BX, заполняю AL и BL 8-битными значениями, а затем суммирую AX и BX. Если сумма превышает 8 бит, будет ли установлен флаг переноса? Даже если я выполняю сложение с использованием 16-битных регистров AX и BX?
5. Бит переноса — это 33-й бит. При 33-битном ответе для 32-битного добавления переполнения нет. Только когда мы пытаемся отбросить 33-й бит и сохранить только младшие 32 бита, существует вероятность переполнения, и флаг переноса сообщит нам, когда для реального числового значения требуется 33-й бит (равный 1), что также означает, что отбрасывание этого старшего порядка 33-го 1-битноговызовет переполнение, поскольку оставшееся числовое значение в 32 битах изменится / не будет правильным.