.386 .MODEL FLAT, STDCALL ;#region Прототипы внешних функций ; Внешние функции описываются директивой EXTERN, ; после знака @ указывается общая длина передаваемых параметров, ; после двоеточия указывается тип внешнего объекта – процедура EXTERN GetStdHandle@4: PROC ; Позволяет получить дискрипторы ввода и вывода EXTERN WriteConsoleA@20: PROC EXTERN CharToOemA@8: PROC EXTERN ReadConsoleA@20: PROC EXTERN ExitProcess@4: PROC; функция выхода из программы EXTERN lstrlenA@4: PROC; функция определения длины строки EXTERN wsprintfA: PROC; т.к. число параметров функции не фиксировано, ; используется соглашение, согласно которому очищает стек вызывающая процедура ;#endregion Прототипы внешних функций .DATA ; сегмент данных ;#region Данные ; выводимая строка, в конце добавлены ; управляющие символы: 13 – возврат каретки, 10 – переход на новую ; строку, 0 – конец строки; с использованием директивы DB ; резервируется массив байтов WRITE_VALUE DB "Введите число в 16-ти ричной системе: ",13,10,0 WRITE_LENGTH DD ? ; Длина строки ; Деректитва DD резервирует память объемом 32 бита (4 байта) ; знак «?» используется для неинициализированных данных FIRST_STRING DB 50 dup (?); Первое число в строковм виде FIRST_LENGTH DD ? ; Длина первого числа FIRST_INT DD 0 ; Значение hex строки в dec SECOND_STRING DB 50 dup (?); Второе число SECOND_LENGTH DD ? ; Длина второго числа SECOND_INT DD 0 ; Значение hex строки в dec RESULT_INT DD ? ; Результат (dec строка) LENS DD ? ; Буффер для длины строки (для ввода/вывода) D_IN DD ? ; Дескриптор ввода D_OUT DD ?; Дескриптор вывода ;#endregion Данные BUFF DB 50 dup (?) .CODE; сегмент кода ;--------------------------------------------------- ; Преобразование QWORD в десятичное число ; Параметры вызова: ; dwHigh - Старшее двойное слово ; dwLow - Младшее двойное слово ; lpBuff - указатель на буфер-приемник ;--------------------------------------------------- bignum PROC pushad ; Сохранить все регистры mov edi, OFFSET BUFF ; Указатель на буфер-приемник xchg esi,edx ; Сохранить старший dword mov ebx,10 ; Основание системы счисления xor ecx,ecx ; Счетчик десятичных цифр @bignum_1: xchg eax,esi ; Расчитать десятичную цифру xor edx,edx div ebx xchg esi,eax div ebx or dl,'0' ; Преобразовать результат в символ цифры push edx ; Сохранить цифру в стеке inc ecx ; Увеличить счетчик цифр or eax,eax ; Все преобразовали? jnz @bignum_1 @bignum_2: pop eax ; Записать все цифры из стека в буфер stosb loop @bignum_2 xor eax,eax ; Признак окончания строки stosb popad ; Восстановить все регистры ret ; Ворзврат из процедуры bignum endp ;#region HEX_SYMBOL_TO_DEC ; Получает из HEX символа DEC число ; Входные данные: ; 1. Регистр BL ; Выходные данные: ; 1. Регистр BL HEX_SYMBOL_TO_DEC PROC CMP BL, '9' ;; JG @IfLetter ;; SUB BL, '0' ;; Если BL больше '9' (т.е. символ a-f) jmp @EndIf ;; То @IfLetter: ;; BL = BL - 'a' + 10 SUB BL, 'a' ;; Иначе ADD BL, 10 ;; BL = BL - '0' @EndIf: ;; RET ;; HEX_SYMBOL_TO_DEC ENDP ;#endregion HEX_SYMBOL_TO_DEC MAIN PROC; описание процедуры ;#region Получаем дескрипторы ввода и вывода ; Аргумент функции: -10 для ввода, -11 для вывода, -12 для сообщения об ошибки ; Функция возвращает дескриптор в регистр EAX PUSH -10 CALL GetStdHandle@4 ; Перемещаем регистр вывода в новую переменную D_IN MOV D_IN, EAX PUSH -11 CALL GetStdHandle@4 ; Перемещаем регистр вывода в новую переменную D_OUT MOV D_OUT, EAX ;#endregion Получаем дескрипторы ввода и вывода ;#region Перекодируем строку WRITE_VALUE для DOS ; Сначала перекодируем строку STRN для DOS (т.к. она содержит русские буквы) ; Перемещаем в регистр EAX адрес строки STRN (OFFSET возвращает адрес) MOV EAX, OFFSET WRITE_VALUE ; У функции CharToOemA@8 два параметра - откуда берем строку и куда кладем ; Адрес строки PUSH EAX; параметры функции помещаются в стек командой PUSH PUSH EAX CALL CharToOemA@8; вызов функции ;#endregion Перекодируем строку WRITE_VALUE для DOS ;#region Определяем длину строки WRITE_VALUE ; Аргумент функции: адрес строки. Возвращаемое значение: длина строки в регистре EAX PUSH OFFSET WRITE_VALUE CALL lstrlenA@4; MOV WRITE_LENGTH, EAX ;#endregion Определяем длину строки WRITE_VALUE ;#region Выводим строку WRITE_VALUE PUSH 0; 5-ый параметр всегда 0 PUSH OFFSET LENS; В переменную LENS запишется длина строки, которая была выведена PUSH WRITE_LENGTH; Длина строки, сколько хотим вывести PUSH OFFSET WRITE_VALUE; Адрес строки PUSH D_OUT; Регистр вывода CALL WriteConsoleA@20 ;#endregion Выводим строку WRITE_VALUE ;#region Вводим строку FIRST_STRING ; FIRST_LENGTH больше на два, т.к. дополнительно введены ; 13 - возврат каретки и 10 - переход на новую строку PUSH 0; 5-ый параметр всегда 0 PUSH OFFSET FIRST_LENGTH; Длина считанной строки строки PUSH 50; Сколько символов хотим считать PUSH OFFSET FIRST_STRING; Куда читаем PUSH D_IN; Регистр ввода CALL ReadConsoleA@20 SUB FIRST_LENGTH, 2 ;#endregion Вводим строку FIRST_STRING ;#region Проверка на то, что длина строки больше трех CMP FIRST_LENGTH, 3 jl string_to_small ; если строка меньше 5, то переходим прыгаем ;#endregion Проверка на то, что длина строки больше трех ;#region Еще раз выводим строку WRITE_VALUE PUSH 0; 5-ый параметр всегда 0 PUSH OFFSET LENS; В переменную LENS запишется длина строки, которая была выведена PUSH WRITE_LENGTH; Длина строки, сколько хотим вывести PUSH OFFSET WRITE_VALUE; Адрес строки PUSH D_OUT; Регистр вывода CALL WriteConsoleA@20 ;#endregion Еще раз выводим строку WRITE_VALUE ;#region Вводим строку SECOND_STRING ; 6. Вводим строку SECOND_STRING PUSH 0; 5-ый параметр всегда 0 PUSH OFFSET SECOND_LENGTH; Длина считанной строки строки PUSH 50; Сколько символов хотим считать PUSH OFFSET SECOND_STRING; Куда читаем PUSH D_IN; Регистр ввода CALL ReadConsoleA@20 SUB SECOND_LENGTH, 2 ;#endregion Вводим строку SECOND_STRING ;#region Проверка на то, что длина строки больше трех CMP SECOND_LENGTH, 3 jl string_to_small ; если строка меньше 5, то переходим прыгаем ;#endregion Проверка на то, что длина строки больше трех ;#region Первая строка в dec MOV ECX, FIRST_LENGTH MOV ESI, OFFSET FIRST_STRING XOR EAX, EAX @READ_BYTES: MOV EAX, FIRST_INT MOV EDX, 16 MUL EDX ; Результат кладется в EDX и EAX, но т.к. число не превышает 32 бита, то EDX пуст MOV FIRST_INT, EAX DEC CL ; Уменьшаем число символов на 1 MOV BL, [ESI] ; Читаем символ в BL INC ESI CALL HEX_SYMBOL_TO_DEC ; Переводим его в число MOV AL, BL ;; Чтобы сложить это число с EDI cbw ;; перемещаем его в регистр AL cwde ;; и резайзим его до AX, а затем до EAX ADD FIRST_INT, EAX CMP ECX, 0 JG @READ_BYTES ; Если кол-во оставшихся символов больше 0, то читаем еще один байти ;#endregion Первая строка в dec ;#region Вторая строка в dec MOV ECX, SECOND_LENGTH MOV ESI, OFFSET SECOND_STRING XOR EAX, EAX @READ_BYTES2: MOV EAX, SECOND_INT MOV EDX, 16 MUL EDX ; Результат кладется в EDX и EAX, но т.к. число не превышает 32 бита, то EDX пуст MOV SECOND_INT, EAX DEC CL ; Уменьшаем число символов на 1 MOV BL, [ESI] ; Читаем символ в BL INC ESI CALL HEX_SYMBOL_TO_DEC ; Переводим его в число MOV AL, BL ;; Чтобы сложить это число с EDI cbw ;; перемещаем его в регистр AL cwde ;; и резайзим его до AX, а затем до EAX ADD SECOND_INT, EAX CMP ECX, 0 JG @READ_BYTES2 ; Если кол-во оставшихся символов больше 0, то читаем еще один байти ;#endregion Вторая строка в dec MOV EAX, FIRST_INT MUL SECOND_INT CALL bignum ; TODO: ; Вывести полученное число PUSH 0; параметр: код выхода CALL ExitProcess@4 string_to_small: PUSH 1; CALL ExitProcess@4 MAIN ENDP END MAIN