Advertisement
VladimirKostovsky

Ассемблер, лаба 1. Final

Nov 29th, 2023 (edited)
1,111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. .386
  2. .MODEL FLAT, STDCALL ; Определяем модель памяти
  3. OPTION CASEMAP: NONE ; Опции компилятора
  4.  
  5.  
  6. ; Объявление внешних функций из библиотеки kernel32.dll
  7. EXTERN WriteConsoleA@20: PROC ; Функция для вывода строки в консоль
  8. EXTERN CharToOemA@8: PROC ; Функция для преобразования кодировки строки в DOS
  9. EXTERN GetStdHandle@4: PROC ; Функция для получения хэндла устройства ввода/вывода
  10. EXTERN lstrlenA@4: PROC ; Функция для определения длины строки
  11. EXTERN ExitProcess@4: PROC ; Функция для завершения процесса
  12. EXTERN ReadConsoleA@20: PROC ; Функция для считывания строки с консоли
  13.  
  14. ; Секция данных
  15. .DATA
  16. ; Строки для вывода
  17. STR1    DB "Введите 1 число: ", 10, 0 ; Строка с приглашением для ввода первого числа
  18. STR2    DB "Введите 2 число: ", 10, 0 ; Строка с приглашением для ввода второго числа
  19. STR3    DB "Результат: ", 0 ; Строка для вывода результата
  20. ERROR_STR   DB "Произошла ошибка, введенное число должно быть от 3 до 8 символов!", 0 ; Строка с сообщением об ошибке при вводе числа неверной длины
  21. OVERFLOW    DB "Произошла ошибка переполнения регистра, невозможно определить результат произведения", 0 ; Строка с сообщением об ошибке при переполнении регистра в процессе умножения
  22.  
  23. ; Переменные для хранения данных
  24. DOUT    DD ? ; Хэндл для вывода в консоль
  25. DIN     DD ? ; Хэндл для ввода с консоли
  26. LENS    DD ? ; Переменная для хранения длины введенной строки
  27. NUM1    DD ?          ; Переменная для хранения первого числа
  28. NUM2    DD ?          ; Переменная для хранения второго числа
  29. BUF     DB 20 dup (?) ; Буфер для временного хранения введенных строк
  30. OS      DD ? ; Коэффициент для вычислений
  31. S_10    DD 10 ; Константа для конвертации числа из восьмеричной системы в десятичную
  32. SIGN_FIRST      DB ? ; Флаг знака первого числа (0 - положительное, 1 - отрицательное)
  33. SIGN_SECOND     DB ? ; Флаг знака второго числа (0 - положительное, 1 - отрицательное)
  34. SIGN    DB ? ; Флаг знака результата (0 - положительный, 1 - отрицательный)
  35.  
  36. ; Секция кода
  37. .CODE
  38. MAIN PROC
  39.  
  40. ; Изменяем кодировку строк на 'досовскую'
  41. MOV EAX, OFFSET STR1
  42. PUSH EAX
  43. PUSH EAX
  44. CALL CharToOemA@8 ; Преобразование кодировки первой строки
  45.  
  46. MOV EAX, OFFSET STR2
  47. PUSH EAX
  48. PUSH EAX
  49. CALL CharToOemA@8 ; Преобразование кодировки второй строки
  50.  
  51. MOV EAX, OFFSET STR3
  52. PUSH EAX
  53. PUSH EAX
  54. CALL CharToOemA@8 ; Преобразование кодировки третьей строки
  55.  
  56. MOV EAX, OFFSET ERROR_STR
  57. PUSH EAX
  58. PUSH EAX
  59. CALL CharToOemA@8 ; Преобразование кодировки строки с сообщением об ошибке
  60.  
  61. MOV EAX, OFFSET OVERFLOW
  62. PUSH EAX
  63. PUSH EAX
  64. CALL CharToOemA@8 ; Преобразование кодировки строки с сообщением о переполнении
  65.  
  66. ; Получаем хэндлы для вывода и ввода
  67. PUSH -11
  68. CALL GetStdHandle@4
  69. MOV DOUT, EAX ; Получение хэндла для вывода
  70.  
  71. PUSH -10
  72. CALL GetStdHandle@4
  73. MOV DIN, EAX ; Получение хэндла для ввода
  74.  
  75. ; Выводим приглашение для ввода первого числа
  76. ; Загружаем указатель на строку STR1 в регистр EAX
  77. PUSH OFFSET STR1
  78. ; Вызываем функцию lstrlenA для определения длины строки
  79. CALL lstrlenA@4
  80. ; Подготавливаем параметры для вызова WriteConsoleA@20
  81. PUSH 0
  82. PUSH OFFSET LENS
  83. PUSH EAX
  84. PUSH OFFSET STR1
  85. PUSH DOUT
  86. ; Вызываем функцию WriteConsoleA для вывода строки в консоль
  87. CALL WriteConsoleA@20
  88.  
  89. ; Считывание первого числа из консоли
  90. ; Подготавливаем параметры для вызова ReadConsoleA@20
  91. PUSH 0
  92. PUSH OFFSET LENS
  93. PUSH 200
  94. PUSH OFFSET BUF
  95. PUSH DIN
  96. ; Вызываем функцию ReadConsoleA для считывания строки с консоли
  97. CALL ReadConsoleA@20
  98.  
  99. ; Преобразование первого числа из строки символов в число
  100. MOV ESI, OFFSET BUF
  101. DEC LENS
  102. DEC LENS   
  103. MOV ECX, LENS
  104.  
  105. MOV AL, [ESI]   ; Проверяем первое число на отрицательность
  106. CMP AL, '-'
  107. JNE CONTINUE_F  ; Если первый символ не -, то продолжаем переводить число из строки
  108. DEC LENS        ; Если первый знак -, то уменьшаем длину строки на 1
  109. MOV ECX, LENS
  110. INC ESI         ; Перемещаем регистр ESI на 1 вперед (на первую цифру числа)
  111. MOV SIGN_FIRST, 1 ; Меняем знак первого числа на -
  112.  
  113. CONTINUE_F:     ; Продолжаем переводить число
  114.     CMP LENS, 3
  115.     JB ERROR
  116.     CMP LENS, 8
  117.     JA ERROR
  118.     MOV OS, 8
  119.     XOR EAX, EAX
  120.     XOR EBX, EBX
  121.     CONV:
  122.         MOV BL, [ESI]
  123.         SUB BL, 48
  124.         MUL OS
  125.         ADD EAX, EBX
  126.         INC ESI
  127.     LOOP CONV
  128.     MOV NUM1, EAX
  129.  
  130.  
  131. PUSH OFFSET STR2        ; Выводим приглашение для ввода второго числа
  132. CALL lstrlenA@4
  133. PUSH 0
  134. PUSH OFFSET LENS
  135. PUSH EAX
  136. PUSH OFFSET STR2
  137. PUSH DOUT
  138. CALL WriteConsoleA@20   ; Выводим приглашение для ввода второго числа в консоль
  139.  
  140. ; считывание второго числа из консоли
  141. PUSH 0
  142. PUSH OFFSET LENS
  143. PUSH 200
  144. PUSH OFFSET BUF
  145. PUSH DIN
  146. CALL ReadConsoleA@20
  147.  
  148. ; Преобразование второго числа из сторки символов в число
  149. MOV ESI, OFFSET BUF
  150. DEC LENS
  151. DEC LENS
  152. MOV ECX, LENS
  153.  
  154. MOV AL, [ESI]       ; Проверяем второе число на отрицательность
  155. CMP AL, '-'
  156. JNE CONTINUE_S      ; Если первый символ числа не минус, то продолжаем конверитровать число
  157. DEC LENS            ; Если минус, то уменьшаем длину строки на 1
  158. MOV ECX, LENS
  159. INC ESI             ; Сдвигаем регистр ESI на одни (чтобы указывал на число)
  160. MOV SIGN_SECOND, 1  ; меняем знак числа на -
  161.  
  162. CONTINUE_S:     ; Продолжаем переводить число
  163.     CMP LENS, 3
  164.     JB ERROR
  165.     CMP LENS, 8
  166.     JA ERROR
  167.     MOV OS, 8
  168.     XOR AX, AX
  169.     XOR BX, BX
  170.     SONV:
  171.         MOV BL, [ESI]
  172.         SUB BL, 48
  173.         MUL OS
  174.         ADD AX, BX
  175.         INC ESI
  176.     LOOP SONV
  177.     MOV NUM2, EAX
  178.  
  179. ; Выводим строку с результатом
  180. PUSH OFFSET STR3
  181. CALL lstrlenA@4
  182. PUSH 0
  183. PUSH OFFSET LENS
  184. PUSH EAX
  185. PUSH OFFSET STR3
  186. PUSH DOUT
  187. CALL WriteConsoleA@20
  188.  
  189. ; Проверяем какой должен быть знак у результата
  190. CMP SIGN_FIRST, 1
  191. JNE AGAIN
  192. CMP SIGN_SECOND, 0
  193. JNE AGAIN
  194. MOV SIGN, 1
  195.  
  196. AGAIN:
  197.     CMP SIGN_FIRST, 0
  198.     JNE ALG
  199.     CMP SIGN_SECOND, 1
  200.     JNE ALG
  201.     MOV SIGN, 1
  202.  
  203. ALG:
  204.     XOR EDX, EDX
  205.     XOR EBX, EBX
  206.     XOR EAX, EAX
  207.     MOV EAX, NUM1
  208.     MOV EBX, NUM2
  209.     MUL EBX             ; производим умножение введенных чисел
  210.     JC OVER
  211.     MOV EBX, EAX
  212.  
  213. MOV ESI, OFFSET BUF
  214. CMP SIGN, 0             ; Если результат отрицательный, то добавить в строку знак '-'.
  215. JE FUNC
  216. MOV AX, 45              ; 45 - код знака '-'.
  217. MOV [ESI], AX
  218. INC ESI
  219. FUNC:  
  220.     MOV EAX, EBX          ; Копируем значение EBX в EAX
  221.     XOR EDX, EDX          ; Обнуляем EDX (остаток от деления)
  222.     XOR EDI, EDI          ; Обнуляем EDI (счетчик цифр)
  223.    
  224. CONVERT_FROM8TO10:
  225.     CMP EBX, S_10         ; Сравниваем EBX с 10
  226.     JAE FUNC1             ; Если EBX >= 10, переходим к FUNC1
  227.     JB FUNC5              ; Если EBX < 10, переходим к FUNC5
  228.  
  229. FUNC1:
  230.     DIV S_10              ; Делим содержимое EAX на 10, результат в EAX, остаток в EDX
  231.     ADD DX, '0'           ; Прибавляем '0' к остатку, чтобы получить ASCII-код цифры
  232.     CMP DX, '9'           ; Сравниваем остаток с '9'
  233.     JA FUNC2              ; Если остаток > '9', переходим к FUNC2
  234.     JBE FUNC3             ; Если остаток <= '9', переходим к FUNC3
  235.  
  236. FUNC2:
  237.     ADD DX, 7             ; Добавляем 7 к остатку для преобразования в ASCII-код цифры
  238. FUNC3:
  239.     PUSH EDX              ; Кладем остаток в стек (для инвертирования)
  240.     ADD EDI, 1            ; Увеличиваем счетчик цифр
  241.     XOR EDX, EDX          ; Обнуляем EDX
  242.     XOR EBX, EBX          ; Обнуляем EBX
  243.     MOV BX, AX            ; Переносим результат деления в BX (новое число для деления)
  244.     MOV ECX, 2            ; Задаем значение ECX для цикла инвертирования
  245. LOOP CONVERT_FROM8TO10
  246. FUNC5:
  247.     ADD AX, '0'           ; Прибавляем '0' к результату для преобразования в ASCII-код
  248.     CMP AX, '9'           ; Сравниваем результат с '9'
  249.     JA FUNC6              ; Если результат > '9', переходим к FUNC6
  250.     JBE FUNC7             ; Если результат <= '9', переходим к FUNC7
  251.  
  252. FUNC6:
  253.     ADD AX, 7             ; Добавляем 7 к результату для преобразования в ASCII-код
  254. FUNC7:
  255.     PUSH EAX              ; Кладем результат в стек (для инвертирования)
  256.     ADD EDI, 1            ; Увеличиваем счетчик цифр
  257.     MOV ECX, EDI          ; Переносим значение счетчика цифр в ECX
  258. CONVERTS:
  259.     POP [ESI]              ; Извлекаем значения из стека и записываем в буфер по адресу ESI
  260.     INC ESI               ; Увеличиваем ESI
  261. LOOP CONVERTS
  262.  
  263. PUSH OFFSET BUF          ; Кладем указатель на буфер в стек
  264. CALL lstrlenA@4          ; Получаем длину строки в буфере
  265. PUSH 0                   ; Флаг для указания на ASCIIZ-строку
  266. PUSH OFFSET LENS         ; Кладем указатель на переменную LENS в стек
  267. PUSH EAX                ; Кладем длину строки в стек
  268. PUSH OFFSET BUF          ; Кладем указатель на буфер в стек
  269. PUSH DOUT               ; Кладем хэндл вывода в стек
  270. CALL WriteConsoleA@20    ; Вызываем функцию вывода строки в консоль
  271.  
  272. PUSH 0                   ; Код завершения процесса
  273. CALL ExitProcess@4       ; Завершаем программу
  274.  
  275. PUSH 0                   ; Резервируем место в стеке (заглушка)
  276. CALL ExitProcess@4  ; Вызываем функцию ExitProcess для завершения программы
  277.  
  278. ERROR:
  279.     PUSH OFFSET ERROR_STR; Помещаем в стек указатель на строку ERROR_STR
  280.     CALL lstrlenA@4      ; Вызываем функцию lstrlenA для определения длины строки
  281.     PUSH 0               ; Помещаем параметры для вызова функции WriteConsoleA@20 в стек
  282.     PUSH OFFSET LENS
  283.     PUSH EAX
  284.     PUSH OFFSET ERROR_STR
  285.     PUSH DOUT
  286.     CALL WriteConsoleA@20   ; Вызываем функцию WriteConsoleA для вывода строки в консоль
  287.     PUSH 0                  ; Помещаем параметр для завершения процесса в стек
  288.     CALL ExitProcess@4      ; Вызываем функцию ExitProcess для завершения программы
  289.  
  290. OVER:
  291. ; Выводим сообщение об ошибке переполнения
  292.  
  293.     PUSH OFFSET OVERFLOW    ; Помещаем в стек указатель на строку OVERFLOW
  294.     CALL lstrlenA@4         ; Вызываем функцию lstrlenA@4 для определения длины строки
  295.     PUSH 0                  ; Помещаем параметры для вызова функции WriteConsoleA@20 в стек
  296.  
  297.     PUSH OFFSET LENS
  298.     PUSH EAX
  299.     PUSH OFFSET OVERFLOW
  300.     PUSH DOUT
  301.     CALL WriteConsoleA@20
  302.     PUSH 0
  303.     CALL ExitProcess@4
  304.  
  305. MAIN ENDP
  306. END MAIN
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement