Я использую синтаксис MASM с библиотекой Irvine32 в Visual Studio.
Я попытался заставить эту программу преобразовать двоичный код в десятичный и наоборот, и теперь я застрял на том, как преобразовать двоичный код в шестнадцатеричный и наоборот.
Вот мой код
INCLUDE Irvine32.inc
.data
str1 BYTE ">>> Please select the conversion type: ", 0
str2 BYTE " 1. Binary to Decimal", 0
str3 BYTE " 2. Decimal to Binary", 0
str4 BYTE " 3. Exit", 0
str5 BYTE "-----------------------------------------", 0
str6 BYTE "Enter your choice: ", 0
str7 BYTE "Please Enter 8-bit binary digits (e.g., 11110000): ", 0
str8 BYTE "The decimel integer of ", 0
str9 BYTE " is ", 0
str10 BYTE " ", 0
str11 BYTE "Please Enter a decimal integer less than 256: ", 0
str12 BYTE "The binary of ", 0
str13 BYTE " is ", 0
str14 BYTE " ", 0
str15 BYTE "Bye.", 0
choice DWORD ?
num1 BYTE 9 DUP(? )
num2 WORD ?
numLength DWORD 0
outLoop DWORD 0
base DWORD 2
deciNum DWORD ?
.code
main PROC
_mainprog : ;USED TO DISPLAY THE MENU & EXIT
mov edx, offset str1
call WriteString
call crlf
mov edx, offset str2
call WriteString
call crlf
mov edx, offset str3
call WriteString
call crlf
mov edx, offset str4
call WriteString
call crlf
mov edx, offset str5
call WriteString
call crlf
mov edx, offset str6
call WriteString
call ReadDec ;DISPLAY OUTPUT
mov choice, eax ;EAX = 1
cmp choice, 1 ;IF = 1
je _proc1 ;JUMP TO PROC1
cmp choice, 2 ;IF = 2
je _proc2 ;JUMP TO PROC2
cmp choice, 3 ;IF = 3
je _proc3 ;JUMP TO PROC3
_proc1 : ;BINARY TO DECIMAL
mov edx, offset str7
call WriteString
mov edx, offset num1 ;ASSIGN NUM1 TO EDX TO WRITE THE BINARY IN STRING FORMAT
mov ecx, sizeof num1 ;ASSIGN TO ECX TO ACT AS THE COUNTER (COUNT THE LENGTH OF THE BINARY DIGIT)
call ReadString ;USER INPUT BINARY NUMBER
mov numLength, eax ;USER INPUT EAX STORED IN NUMLENGTH
mov eax, 0 ;EAX ASSIGNED TO 8
mov esi, 0 ;ESI TO ACCESS THE STRING BINARY INPUT THAT WE ENTERED
mov ecx, numLength
whileProc1 :
cmp ecx, 0 ;COMPARE COUNTER WITH 0
je printResult ;IF ECX =/ 0, IT WILL NOT JUMP TO PRINT RESULT
mov outLoop, ecx ;8 ASSIGNED TO OUTLOOP AND ASSIGNED TO REGISTER ECX
ifProc :
cmp num1[esi], '0' ;IF 1ST VALUE IN THE BINARY DIGIT = 0
je incEsiProc ;JUMP TO INCREASE ESI PROC
elseIfProc:
cmp num1[esi], '1' ;IF 1ST VALUE IN BINARY DIGIT = 1
mov ecx, numLength ;NUMLENGTH ASSIGNED TO ECX
sub ecx, esi ;
dec ecx
mov eax, 1
whileProc2 :
je stopProc
mov ebx, base
mul ebx
dec ecx
jmp whileProc2
stopProc :
add deciNum, eax
jmp incEsiProc
incEsiProc :
inc esi
mov ecx, outLoop
dec ecx
jmp whileProc1
printResult :
mov edx, offset str8
call WriteString
mov edx, offset num1
call WriteString
mov edx, offset str9
call WriteString
mov eax, deciNum
call WriteDec
mov edx, offset str10
call WriteString
call crlf
call crlf
jmp _mainprog
_proc2: ;DECIMAL TO BINARY
mov edx, offset str11
call WriteString
mov edx, offset num2
call ReadInt
mov num2, ax
mov ebx, 1
mov edx, offset str12
call WriteString
mov ax, num2
call WriteDec
mov edx, offset str13
call WriteString
call WriteBinB
mov edx, offset str14
call WriteString
call crlf
call crlf
jmp _mainprog
_proc3:
mov edx, offset str15
call WriteString
call crlf
call WaitMsg
exit
main ENDP
END main
Возможность попросить пользователя ввести число и преобразовать шестнадцатеричный код в двоичный/десятичный и наоборот
В этой библиотеке у Вас есть процедуры ReadHex, WriteBin, WriteDec.
@0Signal Ive добавил как таковой: '_proc4: ; BINARY TO HEXA mov edx, offset str8 call WriteString mov edx, offset num1 mov ecx, sizeof num1 call ReadString mov eax, deciNum call WriteHex call crlf call crlf jmp _mainprog ' Но вывод 00000000
Скопируйте код _proc1 и замените WriteDec на WriteHex
Я заставил его работать! Как преобразовать шестнадцатеричный код в двоичный/десятичный?
Используйте call ReadHex. Введите шестнадцатеричное значение, результат будет сохранен в eax и используйте WriteBin или WriteDec
Теперь это работает! Теперь у меня проблема: если я постоянно запускаю операции, значения каким-то образом складываются? Например. Если я выберу двоичный код в десятичный, введите 11001100, результат будет 204. Затем я снова выберу двоичный код в десятичный и снова введу 11001100, результат теперь будет 408? Есть ли способ исправить это?
Да, я заметил. Просто сбросьте deciNum` mov [deciNum],0` перед вычислениями. Добавьте это после каждого ярлыка _procX, см. код ниже.
Для чисел, которые помещаются в 32-битный регистр, см. Как преобразовать двоичное целое число в шестнадцатеричную строку? . (В этом вопросе «двоичный» означает двоичное целое число в регистре, а не строку цифр ASCII «0» / «1» с основанием 2. Компьютеры, естественно, являются двоичными, сохраняя числа как группу битов.)
См. также Как напечатать целое число в программировании на уровне ассемблера без printf из библиотеки c? (itoa, целое число в десятичную строку ASCII) / Сборка NASM преобразует ввод в целое число? re: обработка оснований (например, 10), которые не являются степенью двойки, как основание 2 и основание 16. Конечно, все эти вопросы и ответы выполняются полностью вручную, без вызова библиотечных функций, таких как WriteDec или WriteHex, которые делают всю работу за вас.





Вот так выглядит программа сейчас, я использовал этот алгоритм для преобразования двоичной строки в целое число. :)
include \masm32\include\Irvine32.inc
includelib \masm32\lib\Irvine32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
menu BYTE "-----------------------------------------",13,10
BYTE " Please select the conversion type ",13,10
BYTE "-----------------------------------------",13,10
BYTE " 1. Binary to Decimal",13,10
BYTE " 2. Decimal to Binary",13,10
BYTE " 3. Binary to Hex",13,10
BYTE " 4. Hex to Binary",13,10
BYTE "-----------------------------------------",13,10
BYTE " 5. Exit",13,10
BYTE "-----------------------------------------",13,10
BYTE "Enter your choice: ",0
menu_error BYTE "*** You entered wrong number. Try again.", 0
bin_input_msg BYTE "Please enter 8-bit binary digits (e.g., 11110000): ", 0
bin_string_error BYTE "*** Cannot convert binary string to integer. Try again.", 0
bin_msg_1 BYTE "The binary of ", 0
dec_input_msg BYTE "Please enter a decimal integer less than 256: ", 0
dec_input_error BYTE "*** Enter value from range 0 - 255.", 0
dec_msg_1 BYTE "The decimal integer of ", 0
hex_input_msg BYTE "Please enter a hex value: ", 0
hex_msg_1 BYTE "The hex of ", 0
is BYTE " is ", 0
exit_msg BYTE "Bye.", 0
binary_string BYTE 9 DUP(0)
InitFlag DB 0
consoleInHandle DWORD ? ; handle to console input device
consoleOutHandle DWORD ?
;----------------------------------------------------------------------
CheckInit MACRO
; Helper macro
; Check to see if the console handles have been initialized
; If not, initialize them now.
LOCAL exit
cmp InitFlag,0
jne exit
call Initialize
exit:
ENDM
;----------------------------------------------------------------------
.code
main PROC
;----------------------------------------------------------------------
; MENU
_mainprog:
call Clrscr
mov edx, offset menu
call WriteString
call ReadDec ;eax = user's choice
call crlf
cmp eax, 1
je _bin_dec
cmp eax, 2
je _dec_bin
cmp eax, 3
je _bin_hex
cmp eax, 4
je _hex_bin
cmp eax, 5
je _exit
jmp _wrong_number
;----------------------------------------------------------------------
; BINARY TO DECIMAL
_bin_dec:
call BinaryString2Int
mov edx, offset dec_msg_1
call WriteString
mov edx, offset binary_string
call WriteString
mov edx, offset is
call WriteString
call WriteDec
call _new_line
jmp _mainprog
;----------------------------------------------------------------------
; DECIMAL TO BINARY
_dec_bin:
mov edx, offset dec_input_msg
call WriteString
call ReadDec
cmp eax,255 ;check if value is 0 - 255
ja GreaterThan255
mov edx, offset bin_msg_1
call WriteString
call WriteDec
mov edx, offset is
call WriteString
mov ebx, 1 ; 1 - byte, 2 - word, 4 - dbl word
call WriteBinB
call _new_line
jmp _mainprog
GreaterThan255:
call crlf
mov edx, offset dec_input_error
call WriteString
call _new_line
jmp _dec_bin
;----------------------------------------------------------------------
; BINARY TO HEX
_bin_hex:
call BinaryString2Int
mov edx, offset hex_msg_1
call WriteString
mov edx, offset binary_string
call WriteString
mov edx, offset is
call WriteString
call WriteHex
call _new_line
jmp _mainprog
;----------------------------------------------------------------------
; HEX TO BINARY
_hex_bin:
mov edx, offset hex_input_msg
call WriteString
call ReadHex
mov edx, offset bin_msg_1
call WriteString
call WriteHex
mov edx, offset is
call WriteString
mov ebx,4 ; 1 - byte, 2 - word, 4 - dbl word
call WriteBinB
call _new_line
jmp _mainprog
;----------------------------------------------------------------------
; NUMBER IS NOT ON THE MENU LIST
_wrong_number:
mov edx, offset menu_error
call WriteString
call _new_line
jmp _mainprog
;----------------------------------------------------------------------
; NEW LINE
_new_line:
call crlf
call crlf
call WaitMsg
call crlf
ret
;----------------------------------------------------------------------
; EXIT
_exit:
mov edx, offset exit_msg
call WriteString
call crlf
call WaitMsg
exit
main ENDP
BinaryString2Int Proc
;----------------------------------------------------------------------
; INPUT - BINARY STRING
_bin_string_input:
mov edx, offset bin_input_msg
call WriteString
mov edx, offset binary_string
mov ecx, sizeof binary_string
call ReadString
;----------------------------------------------------------------------
; BINARY STRING TO INT
; Algorithm taken from -> https://stackoverflow.com/a/49548057/20889367
mov edi, offset binary_string
movzx eax, byte ptr [edi] ; start with the first digit
sub eax, '0' ; convert from ASCII to number
cmp al, 1 ; check that it's a decimal digit [0..1]
jbe _loop_entry ; too low -> wraps to high value, fails unsigned compare check
xor eax,eax ; else: bad first digit: return 0
jmp conversionError ;ret
; rotate the loop so we can put the JCC at the bottom where it belongs
; but still check the digit before messing up our total
_next_digit:
; lea eax, [eax*4 + eax]
lea eax, [eax*2 + ecx] ; total = total*2 + digit
; imul eax, 2 / add eax, ecx
_loop_entry:
inc edi
movzx ecx, byte ptr[edi]
sub ecx, '0'
cmp ecx, 1
jbe _next_digit
ret ; return with total in eax
conversionError:
call crlf
mov edx, offset bin_string_error
call WriteString
call crlf
call crlf
jmp _bin_string_input
ret
BinaryString2Int Endp
Initialize PROC private
;----------------------------------------------------------------------
; Get the standard console handles for input and output,
; and set a flag indicating that it has been done.
; Updated 03/17/2003
pushad
INVOKE GetStdHandle, STD_INPUT_HANDLE
mov [consoleInHandle],eax
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
mov [consoleOutHandle],eax
mov InitFlag,1
popad
ret
Initialize ENDP
END main
Это очень помогает! :D Теперь я понимаю. Как преобразовать шестнадцатеричный код в двоичный/десятичный?
Это дублирует код для строки с основанием 2 в целое число. Если вы вынесете это в функцию, которая возвращает 32-битное целое число в регистре, вам понадобится только одна копия этого кода, @ArcielShin. (Кроме того, это слишком сложно, очевидно, использование mul в цикле для каждого бита вместо сдвига. Если вы собираетесь использовать один и тот же код для каждой базы, не дублируйте его. Кроме того, как показано на ️ 🔁 другие ответы , есть лучший алгоритм, для которого требуется только одно умножение на входную цифру, а не цикл.По крайней мере, используйте imul eax, base, а не mul, так как вам не нужен вывод mul EDX.)
Ну, я изменил несколько вещей. Сейчас лучше? :)
Меньше дублирования кода, но теперь это спагетти-код. Если бы двоичное преобразование в десятичное и двоичное в шестнадцатеричное просто вызывали функцию, это было бы легче понять. Вместо этого они просто прыгают в одно и то же место, а дальнейшее ветвление на choice происходит позже, поэтому логика, основанная на choice, разбрасывается, когда в этом нет необходимости. Также блок примерно из десятка инструкций с вызовом WriteBinB по-прежнему дублируется в двух местах.
И есть в основном неиспользуемые глобальные переменные, такие как numLength, которые устанавливаются, но не читаются, и избыточные комментарии, такие как mov numLength, eax ;USER INPUT EAX STORED IN NUMLENGTH. Мы уже видим mov, а предыдущая строка была вызовом функции, поэтому мы ожидаем, что она что-то вернула в EAX. Имя переменной делает довольно очевидным, что это была длина строки. То же самое и в вашей цепочке cmp/je, в комментариях не нужно повторять это. (Кроме того, у вас все еще есть choice в EAX, сравните с этим.) Я думаю, это было адаптировано из плохого кода OP, где у них даже не было значимых имен.
В любом случае, подумайте о том, как вы можете написать это на C: вам понадобятся некоторые функции, такие как read_bin / dec / bin и write_dec / hex / bin. Пункт меню перешел бы к коду, вызывающему две из этих функций, одну для ввода, другую для вывода. Хитрость заключается в том, чтобы найти хороший способ сообщать об ошибках, если вы хотите выйти из строя и не продолжать печатать, поэтому, возможно, второе возвращаемое значение, поскольку это asm, где мы можем легко это сделать. например в EDX или в состоянии FLAGS.
Значимые имена для строк — очень хорошее изменение. Создание одной многострочной строки вместо использования блока нескольких вызовов WriteString сделало бы код меню намного более компактным для случаев, когда набор строк всегда печатается вместе. Например, то, что вверху mainprog, будет просто call Clrscr / mov edx, offset whole_menu/call WriteString. IDK, если это считается плохим стилем для программ Irvine32, но мне кажется безумием делать 10 вызовов ввода-вывода, когда один из них был бы таким же простым. Конечно, ИДК, сколько времени вы готовы потратить на настройку чужого домашнего задания!
Введите двоичное число в командной строке, как в
_proc1. Это значение сохраняется вdeciNumи затем используетсяmov eax, deciNum,call WriteHex. Должно работать, я думаю. :)