Я просто изучаю это за короткий семестр, всего за 6 недель. все еще не понимаю, что такое язык ассемблера, этот код также используется
вот как это выглядит, когда я запускаю код Вывод кода
TITLE AssignmentP2 (Mult.asm)
; This program calculates the value using the following data
; Val1 = 340d
; Val2 = A3Ch
; Val3 = 45h
; FinalVal = -(Val1 – Val3) + 1 + (Val2 * 3) + 1 + 1 – 2 -1– (Val1 / 2)
INCLUDE Irvine32.inc
INCLUDE macros.inc ; Include macros for WriteDec and Crlf
.data
Val1 dword 340d
Val2 dword 0A3Ch
Val3 dword 45h
.code
main proc
mov eax, [Val1] ; Load Val1 into eax
sub eax, [Val3] ; Subtract Val3 from eax
neg eax ; Negate eax (2's complement)
inc eax ; Add 1 to eax
mov ebx, [Val2] ; Load Val2 into ebx
imul ebx, 3 ; Multiply ebx by 3
add eax, ebx ; Add ebx to eax
inc eax ; Add 1 to eax
inc eax ; Add 1 to eax
sub eax, 2 ; Subtract 2 from eax
dec eax ; Subtract 1 from eax
mov edx, eax ; Preserve the result in edx
mov ebx, [Val1] ; Load Val1 into ebx
mov ecx, 2 ; Divisor (2)
div ecx ; Divide eax by ecx (Val1 / 2)
mov ecx, eax ; Store the result (Val1 / 2) in ecx
mov eax, edx ; Restore the preserved result into eax
call WriteDec ; Call WriteDec to print the result
main endp
end main
ответ, который я ожидаю в десятичном виде, — 12417.
При ссылке вы использовали /subsystem:console
? Вам также не хватает ret
в конце main
Причина, по которой он ничего не печатает, скорее всего, заключается в том, что DIV ECX
— это настоящий EDX:EAX, разделенный на ECX. EDX следует обнулить перед DIV, поскольку EDX является частью делимого, и вычисление приводит к исключению целочисленного переполнения (которое преждевременно завершает работу программы). Поскольку EDX является частью расчета DIV, вам придется сохранить предыдущее значение в регистре, отличном от EDX (т. е. ESI). Вам также не хватает вычитания в конце. Я также считаю, что результат должен быть 7419 (десятичный). Примечание. Деление на 2 также можно выполнить путем сдвига значения вправо на 1 бит.
Если это единственная ошибка, дублируйте Почему EDX должен быть равен 0 перед использованием инструкции DIV? (за исключением того факта, что степени 2 следует просто использовать сдвиг.)
Целочисленная математика ассоциативна, 1 + 1 + 1 - 2 - 1
— это 0
, поэтому вы можете отбросить все инструкции inc
/dec
и sub eax,2
, если хотите. Конечно, есть много других упрощений, которые мог бы сделать компилятор, например, -(x - y)
становится y-x
, и оптимизация глазка, например, использование lea edx, [edx + edx*2]
для умножения на 3. (Компиляторы будут избегать EBX, потому что он зарезервирован для вызова: если main возвращается к вызывающему объекту, он должен восстановить входящее значение EBX или избегать его использования.) godbolt.org/z/xdh8cY5Tb показывает фактический ассемблерный вывод GCC и MSVC, а также неоптимизированный MSVC с использованием imul reg, mem, 3
Если предположить, что это связано с исключением разделения, очень плохо, что вам об этом ничего не говорит. Если вы написали ту же программу под Linux, оболочка сообщит вам, что ваша программа завершилась с арифметическим исключением (на самом деле там написано «исключение с плавающей запятой», поскольку POSIX требует SIGFPE
, если математические вычисления приводят к сбою, даже целочисленные математические вычисления, так что все не так). Это не идеально с точки зрения простоты понимания, какую проблему искать, но, по крайней мере, есть что погуглить). Если вы запустите это под отладчиком, покажет ли это вам проблему?
@PeterCordes ожидается, когда в программе нет обработки исключений. Если бы он запустил это в отладчике, отладчик поймал бы необработанные исключения и показал бы ошибку.
@MichaelPetch: Другие ошибки, такие как ошибки недопустимых страниц (неверные указатели) или #GP (например, из-за неправильной загрузки/сохранения SSE), также просто автоматически завершают программу под Windows по умолчанию? (По крайней мере, для консольного приложения? Кажется, я видел скриншоты диалоговых окон при недопустимом доступе.) Еще одна причина, по которой необходимо использовать отладчик!
@PeterCordes: Да, любая программа, которая не перехватывает исключения, автоматически завершится, и оболочка ничего не отобразит. Код, который вы видите, отображающий ошибку исключения, обычно окружен обработкой исключений (в языках высокого уровня есть необходимый код) и перехватывается приложением. Консольные приложения с правильной обработкой исключений будут отображать полезные сообщения об ошибках.
@MichaelPetch: Может ли лучшая оболочка обнаружить выход дочернего процесса с неперехваченным исключением SEH(?), например Cygwin или MinGW bash, или теоретически, если это не так?
Оболочка MinGW, запускающая программу Windows в этом примере, не отображает необработанные исключения. Windows PowerShell тоже этого не делает.
На самом деле я понимаю это сразу после того, как вижу комментарий @PeterCordes, поскольку он действительно очень помогает, хотя на Youtube и Google все еще требуется некоторое время, чтобы понять концепцию, но в конце концов я ее понимаю. Спасибо большое 👍
После моего последнего ответа (который оказался не очень хорошим) я решил вернуться к идее из первого поста. Расчеты + вывод. Печатает 7419.
TITLE AssignmentP2 (Mult.asm)
; This program calculates the value using the following data
; Val1 = 340d
; Val2 = A3Ch
; Val3 = 45h
; FinalVal = -(Val1 – Val3) + 1 + (Val2 * 3) + 1 + 1 – 2 -1– (Val1 / 2)
include \masm32\include\Irvine32.inc
includelib \masm32\lib\Irvine32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
Val1 dword 340d
Val2 dword 0A3Ch ; 2620
Val3 dword 45h ; 69
.code
main proc
mov eax, [Val1] ; Load Val1 into eax
sub eax, [Val3] ; Subtract Val3 from eax
neg eax ; Negate eax (2's complement)
inc eax ; Add 1 to eax
mov ebx, [Val2] ; Load Val2 into ebx
imul ebx, 3 ; Multiply ebx by 3
add eax, ebx ; Add ebx to eax
inc eax ; Add 1 to eax
inc eax ; Add 1 to eax
sub eax, 2 ; Subtract 2 from eax
dec eax ; Subtract 1 from eax
mov ebx, [Val1] ; Load Val1 into ebx
shr ebx, 1 ; divide value in ebx by 2
sub eax, ebx ; Substract value in ebx from eax
call WriteDec
exit
ret
main endp
end main
Что конкретно делает этот код, чтобы решить проблему(и) из вопроса? Почему этот код примерно в 4 раза больше кода в вопросе?
Ничего особенного, просто шаг за шагом вычисляет это уравнение и показывает в окне консоли результат каждой инструкции. Линии mov [show_reg], X
, call output
можно легко удалить, как и output proc
. В конце концов, вычислений WriteInt
достаточно, потому что я думаю, что WriteDec
не очень хорошо работает с отрицательными числами.
Огромный блок кода будет очень медленно читаться, особенно новичкам, особенно без текста, указывающего, какие изменения вы внесли. По поводу смены ключа даже комментариев нет, shr ebx, 1
. Хорошие ответы должны содержать текст, который отвечает на вопрос о том, что не так с кодом, и объясняет, что показывает блок кода.
Также ОП ожидает положительного результата. Скопировав/вставив исходное выражение в calc
с помощью Val1=340
/Val2=0xA3C
/Val3 = 0x45
, я получаю 7419, что отличается как от вашего результата, так и от результата, который они ожидали. :/ Выражение в комментарии (с исправленными не-ASCII-знаками -
) равно -(Val1 - Val3) + 1 + (Val2 * 3) + 1 + 1 - 2 -1 - (Val1 / 2)
Val1
340d или OP просто быстро набрали и вместо d
должно быть h
? 340h
?
Я изменил значение на 340h
и результат 6681
. А выражение -(Val1 - Val3) + 1 + (Val2 * 3) + 1 + 1 - 2 - 1 - (Val1 / 2)
?
Хорошо, если val1
равно 340, результат 7419
, мой код показывает хороший результат.
Я предположил, что 340d
намеренно указывает десятичное число, потому что суффикс d
для него является допустимым синтаксисом MASM. (Сбивает с толку, поскольку похоже, что кто-то забыл конечную букву h при попытке написать 340Dh
, но я видел, как многие люди писали десятичные числа «явно» таким образом в предыдущих вопросах SO.) Математический результат с Val2=0x340D
равен -12058.5
, а не целое число, так что это еще одна причина сомневаться в том, что это было задумано. Я не пытался выяснить, какие входные данные дадут «ожидаемый» результат ОП.
Кстати, посмотрите недавнюю мета-дискуссию об ответах только на код, которые не объясняют сами себя: Как мы можем поощрять ответы, которые будут полезны не только спрашивающему? - широко распространено мнение, что объяснения намного лучше для всех, кто позже натыкается на вопросы и ответы (включая людей, ищущих дубликаты для будущих вопросов), а также для ОП. Ведутся серьезные споры о том, лучше ли публиковать только код, чем вообще не отвечать, если у вас нет времени написать хотя бы одно или два предложения о коде, который вы публикуете.
Возможно, вы могли бы предварить этот ответ, сказав: «Я поигрался с этим из собственного любопытства», потому что именно так это выглядит. Если вы просто хотите увидеть, как значения изменяются по мере выполнения шагов, используйте отладчик, который выделяет последний регистр(ы), которые нужно изменить, по мере пошагового выполнения. Вот почему у нас есть отладчики, потому что, как показывает ваш ответ, написание отладочных отпечатков в asm требует гораздо больше кода, чем в C, где вы можете просто щелкнуть printf где-нибудь, не затрагивая другие переменные. (У Irvine32 есть call DumpReg
, но это не подчеркивает последнее изменение.)
Теперь в вашем ответе есть текст, но вы по-прежнему не упоминаете основную проблему с кодом в вопросе (что вообще мешало ему напечатать ответ): Почему EDX должен быть равен 0 перед использованием инструкции DIV ? . Это были только упоминания комментариев, а не реальных ответов. Тому, кто еще этого не знает, придется читать ваш ответ по одной строке за раз, чтобы сравнить его с вопросом и найти различия, чтобы увидеть, в чем они заключаются.
Из вашего вопроса неясно, какие команды вы запускали для сборки и связывания вашего кода.