Разное

Ассемблер сложение: Учебный курс. Урок 10. Сложение и вычитание с переносом

Учебный курс. Урок 10. Сложение и вычитание с переносом

Регистры процессоров ограничены в разрядности архитектурой процессора (как правило, x86/64), поэтому сложение чисел, как из реального мира, так и из виртуального в ассемблере имеет дополнительные нюансы. В предыдущем Уроке 9. Сложение и вычитание операции сложения и вычитания выполнялись только в границах размеров регистров.

Сложение операндов превышающих размеры регистров приводит к переполнению и установке флага CF. Команда ADC предназначена для сложения с учетом флага CF, а команда SBB вычитает с учетом флага CF. Данные инструкции корректно складывают и вычитают любые целые числа, превышающие вместимость регистров процессора (16 бит в рамках курса).

Алгоритм таких арифметических операции прост:

  • в коде программы командами ADD и SUB сначала складываются или вычитаются младшие разряды чисел
  • затем командами ADC и SBB складываются или и вычитаются старшие части

Команды ADC и SBB используют значение флага CF, куда записываются перенос из старшего разряда, что позволяет складывать числа любой длины по частям.

Представьте эту процедуру как сложение и вычитание чисел столбиком.

Обратите внимание на иллюстрацию, показывающую операцию сложения 16 битных чисел (с использованием ADD):

Обратите внимание, произошел перенос из младшей части результата (старший 7-й бит) в старшую часть (младший 8-й бит). Если бы мы были ограничены разрядностью процессора в 8 бит, то складывая эти числа по частям, сначала младшую, а затем старшую часть, и оба раза командой ADD, мы потеряли бы этот вынесенный бит и получили некорректный результат. Ситуацию исправляет флаг CF, который хранит перенос из старшего разряда. Поэтому нам нужно использовать для сложения старшей части только команду ADC:

Ровно так же обстоит дело с вычитанием чисел превышающих размеры регистров. Для наглядности рассмотрим на примере программы, которая выполнит вычисление формулы x = k + l – m + 1, где операнды x, k, l и m являются беззнаковыми 32-битными целыми числами. Как вы уже поняли, мы будем складывать и вычитать их с учетом флага переноса, то есть в два подхода: первыми выполнив команды над младшими разрядами чисел, и завершив вычисления командами ADC и SBB с учётом флага переноса.

Обратите внимание, что в контексте этой программы нельзя использовать команду INC, поскольку INC не влияет на флаг переноса! К примеру, у нас беззнаковое десятичное число 65535 в регистре AX, то, сделав INC AX, мы получим 0 в AX, а флаг CF не изменится!

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov ax,word[k] ;Загружаем младшую часть k в AX mov bx,word[k+2] ;Загружаем старшую часть k в BX add ax,word[l] ;Складываем младшие части k и l adc bx,word[l+2] ;Складываем старшие части k и l sub ax,word[m] sbb bx,word[m+2] ;BX:AX = k+l-m add ax,1 ;Команда INC здесь не подходит! adc bx,0 ;BX:AX = k+l-m+1 mov word[x],ax ;\ mov word[x+2],bx ;/ Сохраняем результат в x mov ax,4C00h ;\ int 21h ;/ Завершение программы ;——————————————————- k dd 120000 l dd 80500 m dd 2300 x dd ?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

use16                 ;Генерировать 16-битный код

org 100h              ;Программа начинается с адреса 100h

    mov ax,word[k]    ;Загружаем младшую часть k в AX

    mov bx,word[k+2]  ;Загружаем старшую часть k в BX

    add ax,word[l]    ;Складываем младшие части k и l

    adc bx,word[l+2]  ;Складываем старшие части k и l

    sub ax,word[m]

    sbb bx,word[m+2]  ;BX:AX = k+l-m

    add ax,1          ;Команда INC здесь не подходит!

    adc bx,0          ;BX:AX = k+l-m+1

    mov word[x],ax    ;\

    mov word[x+2],bx  ;/ Сохраняем результат в x

    mov ax,4C00h      ;\

    int 21h           ;/ Завершение программы

;——————————————————-

k dd 120000

l dd  80500

m dd   2300

x dd      ?

Директива word в коде программы ограничивает размер операнда до 16 бит. Первым в памяти хранится младший разряд, поэтому для чтения старшего разряда нужно сместиться на 2 байта вправо. В нашем случае word[k] обращается к младшему разряду операнда k, а word[k+2] к старшему.

Рассмотрим, как в отладчике представлены переменные

k, l, m и x:

Процессоры Intel  имеют особенный порядок представления значений в памяти. Первым в памяти (по младшему адресу) идет младший байт числа (little endian). Вправо по старшенству идут остальные байты числа. То есть в окошке дампа значения правильнее читать справа налево. В отличие от дампа памяти значения в регистрах показаны в привычном порядке. Для наглядности приведены значения операнда k в окне дампа и в регистрах (старший разряд в BX, а младший в AX).

«Плюшка» ассемблера в том, что программист не ограничен в длине целых чисел. То есть складывать и вычитать можно сколь угодно длинные числа. В противоположность к языкам высокого уровня, где размеры переменных, как правило, ограничены компилятором. От слов к делу, чтобы наглядно показать сложение двух 7-байтных числа (с использованием только одного регистра процессора):

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov ax,word[x] add ax,word[y] mov word[z],ax mov ax,word[x+2] adc ax,word[y+2] mov word[z+2],ax mov ax,word[x+4] adc ax,word[y+4] mov word[z+4],ax mov al,byte[x+6] adc al,byte[y+6] mov byte[z+6],al mov ax,4C00h ;\ int 21h ;/ Завершение программы ;——————————————————- x dd 0xF1111111 dw 0xF111 db 0x11 y dd 0x22222222 dw 0x2222 db 0x22 z rb 7

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

use16                 ;Генерировать 16-битный код

org 100h              ;Программа начинается с адреса 100h

    mov ax,word[x]

    add ax,word[y]

    mov word[z],ax

    mov ax,word[x+2]

    adc ax,word[y+2]

    mov word[z+2],ax

    mov ax,word[x+4]

    adc ax,word[y+4]

    mov word[z+4],ax

    mov al,byte[x+6]

    adc al,byte[y+6]

    mov byte[z+6],al

    mov ax,4C00h      ;\

    int 21h           ;/ Завершение программы

;——————————————————-

x dd 0xF1111111

  dw 0xF111

  db 0x11

y dd 0x22222222

  dw 0x2222

  db 0x22

z rb 7

Чтобы обратиться к самому старшему байту значения используется директива byte[x+6]. Думаю, здесь вы уже поняли, что к чему Использование команды MOV безопасно между командами сложения или вычитания, поскольку команда MOV не затрагивает флаги процессора.

Внимательно прочитали материал? Отлично! Самое время выполнить упражнение к уроку — вычислить формулу x = k — 1 + n — m. Объявите все операнды как 3-х байтные значения без знака. Скомпилируйте программу без ошибок и проверьте в отладчике Tourbo Debugger. Разместите результат в комментариях к этому уроку.

Учебный курс. Урок 9. Сложение и вычитание

После того, как мы изучили механизм представления и обработки чисел процессором, самое время начать знакомство с инструкциями. Ну а к самым простым инструкциям в ассемблере безусловно относятся мнемоники для сложения и вычитания чисел — ADD и SUB.

Сложение — команда ADD

Инструкция ADD выполняет сложение двух операндов. ADD можно «скармливать» числа со знаком и без (такова специфика дополнительного кода). Обязательным правилом является равность операндов по размеру, между собой можно складывать только два 16-битных числа или два 8-битных.

В первый операнд (приемник) будет помещен результат операции сложения. Многие инструкции придерживаются этого правила.

Работа этой инструкции изменяет флаги процессора. В силу того, что результат может выйти за диапазон приемника (переполнение), стать отрицательным, равным нулю и т.д. поведение флагов позволяет следить за результатом выполнения программы. Интерпретация значения флагов во много зависит от того, воспринимает ли программа операнды как числа со знаком или без:

  • Флаг CF становится равным единице, если складываемые операнды являются знаковыми и результат вышел за пределы приемника и случился перенос из старшего разряда. CF = 1 при сложении беззнаковых операндов указывает на переполнение и некорректный результат.
  • Флаг OF = 1 говорит, что произошло переполнение при сложении чисел со знаком.
  • Флаг SF имеет смысл только для операндов со знаком. SF = 1 если результат отрицательный, и SF = 0, если результат положительный. В случае с беззнаковыми операндами он просто получает значение старшего бита приемника.
  • Флаг ZF = 1 указывает, что результат равен нулю.
  • Флаг PF указывает на бинарную четность результата. Это означает что двоичное представление результата содержит четное количество единиц и нулей. Например: 0010111b.

Пример использования команды add:

add ax,10 ;AX = AX + 5 add dx,cx ;DX = DX + CX add dx,cl ;Ошибка: разный размер операндов

add ax,10     ;AX = AX  + 5

add dx,cx     ;DX = DX + CX

add dx,cl     ;Ошибка: разный размер операндов

Вычитание — команда SUB

Инструкция SUB выполняет вычитание операндов. Как и в случае с инструкцией ADD результат операции помещается в первый операнд (приемник). Аналогично изменяются флаги процессора.

На самом деле процессор не умеет вычитать числа. Он все делает через сложение Просто перед сложением он подменяет знак второго операнда на противоположный. Так что между инструкциями SUB и ADD даже больше общего.

Ну а чтобы изменить знак числа, когда нам нужно, есть специальная инструкция NEG. Инструкция принимает единственный операнд, знак которого меняется на противоположный.

Примеры использования инструкций SUB и NEG:

sub ax,dx ;AX = AX — DX neg cx ;CX = -CX

sub ax,dx      ;AX = AX — DX

neg cx         ;CX = -CX

Инструкции INC и DEC. Инкремент и декремент

Нередко в теле программы то или иное число нужно увеличить или убавить ровно на единицу. В программировании прибавление к операнду единицы называется инкрементом, а вычитание единицы от операнда — декрементом. Инструкции INC и DEC принимают единственный операнд. Выполнение этих инструкций не меняет значение флага CF, что является важной деталью.

Пример программы

Предлагаю закрепить полученные знания, создав маленькую программу. Нам нужно написать код, который вернет результат вычисления формулы: x = a — (b — c + 1) + (-d). Каждая переменная представляет целое 8-битное число со знаком. Переменные объявим и присвоим им значения в конце кода программы. Пример такой программы:

use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h mov al,[a] ;Загружаем значение a в AL mov ah,[b] ;Загружаем значение b в AH sub ah,[c] ;AH = AH — c = b-c inc ah ;AH = AH + 1 = b-c+1 sub al,ah ;AL = AL — AH = a-(b-c+1) mov cl,[d] ;CL = d neg cl ;CL = -CL = -d add al,cl ;AL = AL + CL = a-(b-c+1)+(-d) mov [e],al ;Сохраняем результат в e mov ax,4C00h ;\ int 21h ;/ Завершение программы ;——————————————————- a db -8 b db 2 c db 3 d db -8 e db ?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

use16               ;Генерировать 16-битный код

org 100h            ;Программа начинается с адреса 100h

    mov al,[a]      ;Загружаем значение a в AL

    mov ah,[b]      ;Загружаем значение b в AH

    sub ah,[c]      ;AH = AH — c = b-c

    inc ah          ;AH = AH + 1 = b-c+1

    sub al,ah       ;AL = AL — AH = a-(b-c+1)

    mov cl,[d]      ;CL = d

    neg cl          ;CL = -CL = -d

    add al,cl       ;AL = AL + CL = a-(b-c+1)+(-d)

    mov [e],al      ;Сохраняем результат в e

    mov ax,4C00h    ;\

    int 21h         ;/ Завершение программы

;——————————————————-

a db -8

b db 2

c db 3

d db -8

e db ?

Обратите внимание, что в коде программы имена переменных обрамлены квадратными скобками. Имя переменной ссылается на адрес, а помещая его в скобки мы говорим компилятору fasm, что нас интересует значение (число) по этому адресу. Так можно считать или переписать значение переменной.

После компиляции программы (процесс описан в Уроке 2. Первая программа) откройте программу в Turbo Debugger. Здесь можно отследить выполнение кода программы шаг за шагом. В окне дампа памяти по адресам переменных будут их значения. Для быстрого перехода к нужному адресу кликните по окну дампа памяти правой кнопкой мыши и в контекстном меню нажмите Goto…

В выделенных байтах памяти без труда угадываются наши переменные:

Упражнение

В этом уроке предлагаю вам самостоятельно написать программу для определения значения формулы y = e + 1 — (k — 1 — m). Программа должна оперировать с 16-битными целыми числами со знаком. Чтобы узнать результат вычисления, после компиляции программы откройте её в отладчике, выполните все строки и проверьте, какое значение записалось по адресу переменной y. Не стесняйтесь делиться своими результатами в комментариях к этому уроку

Сборка

— самая простая программа добавления x86

mov в EDX не имеет смысла, регистр возвращаемого значения — AL / AX / EAX / RAX / RDX:RAX для ширины от 1 байта до 16 байтов на x86-64. EDX или RDX используются только для широких возвращаемых значений, слишком широких для RAX. (Или в 32-разрядном режиме 64-разрядные значения возвращаются в паре регистров EDX:EAX, поскольку RAX отсутствует.)

Это верно для всех стандартных соглашений о вызовах x86 32-bit и x86-64, включая i386. и x86-64 System V ABI, используемые в GNU/Linux.


Если вы пишете main или любую функцию, которую вы хотите вызвать из другого файла, это должен быть символ .globl . (Если только вы не .include "foo.s" вместо сборки отдельно + компоновка.) Это то, что делает его видимым в таблице символов, чтобы компоновщик разрешал ссылки на него. например из вызовите main в уже скомпилированном коде для _start , в crt0.o или что-то в этом роде, что вы можете увидеть привязку gcc, если запустите gcc -v foo.S . (Это было чрезмерным упрощением; glibc _start на самом деле передает адрес main в качестве аргумента __libc_start_main , который находится в libc.so.6 , поэтому есть некоторый код из libc, который выполняется до main . См. Запуск программы Linux x86 или — Как, черт возьми, нам добраться до main()?)

Если вы создаете статический исполняемый файл без CRT (определение _start вместо main и создание собственного exit_group системный вызов), вы можете просто добавить инструкции в файл и позволить компоновщику ( ld ) выбрать верхнюю часть раздела .text в качестве точки входа ELF, если он не найдет _start символ. (Используйте readelf -a a.out , чтобы увидеть подобную информацию.)

Если вы планируете запустить программу под GDB только для пошагового выполнения пары интересующих вас инструкций, вы можете даже не указывать выход- чисто часть. (Для этого используйте GDB starti команда для запуска с временной точкой останова перед первой инструкцией пользовательского пространства, поэтому вам не нужно устанавливать точку останова вручную по абсолютному адресу (поскольку нет символа).)

 $ cat > foo.S
mov $1 + 2, %edi # вычисляем во время сборки
mov $231, %eax # _NR_exit_group
системный вызов
$ gcc -static -no-pie -nostdlib foo.S # как +ld вручную
/usr/bin/ld: предупреждение: не удается найти символ записи _start; по умолчанию 0000000000401000
$ ./a.out ; эхо $?
3
$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffe0706a3c0 /* 54 переменных */) = 0
выход_группа (3) = ?
+++ вышел с 3 +++
 

Если ваша система 32-битная, то как по умолчанию используется в 32-битном режиме, используйте 32-битный int $0x80 с другими регистрами.

Наконец, какой ресурс лучше всего подходит для поиска операционных кодов?

Обычно я оставляю вкладку браузера открытой на странице https://www.felixcloutier.com/x86/, которая представляет собой фрагмент HTML из руководства Intel vol.2. В исходном PDF-файле есть несколько вводных глав о том, как читать записи, поэтому ознакомьтесь с ним, если какие-либо обозначения вас смущают. Есть старые отрывки руководств Intel, в которых не указаны инструкции SIMD, так что для меня это бесполезно, но, возможно, вам как новичку это нужно.

Другие ресурсы связаны с вики-тегом x86, включая http://ref.x86asm.net/coder64.html, который организован по коду операции, а не по мнемонике, и имеет столбцы кратких ссылок, чтобы напомнить вам, читается ли инструкция или изменяет ФЛАГИ, и если да, то какие, и тому подобное.

Создание вашей первой простой программы с помощью языка ассемблера MIPS — Sweetcode.io

Язык ассемблера MIPS просто относится к языку ассемблера процессора MIPS. Термин MIPS является аббревиатурой от Microprocessor Without Interlocked Pipeline Stages. Это архитектура с сокращенным набором инструкций, разработанная организацией MIPS Technologies.

Язык ассемблера MIPS очень полезен для изучения, поскольку многие встраиваемые системы работают на процессоре MIPS. Знание того, как кодировать на этом языке, дает более глубокое понимание того, как эти системы работают на более низком уровне.

Перед тем, как начать программировать на языке ассемблера MIPS

Прежде чем вы начнете штамповать код на языке ассемблера MIPS, вам необходимо сначала получить очень хорошую интегрированную среду разработки. Это может помочь скомпилировать и выполнить код языка ассемблера MIPS. Программное обеспечение, которое я бы рекомендовал для этой цели, — это MARS (MIPS Assembler and Runtime Simulator). Вы можете легко найти его в Google и скачать.

Введение в архитектуру MIPS

Типы данных

  1. Все инструкции в MIPS 32-битные.
  2. Байт в архитектуре MIPS представляет собой 8 бит; полуслово представляет 2 байта (16 бит), а слово представляет 4 байта (32 бита).
  3. Для каждого символа, используемого в архитектуре MIPS, требуется 1 байт памяти. Каждое используемое целое число требует 4 байта памяти.

Литералы
В архитектуре MIPS литералы представляют все числа (например, 5), символы, заключенные в одинарные кавычки (например, «g»), и строки, заключенные в двойные кавычки (например, «Дэдпул»).

Регистры
Архитектура MIPS использует 32 регистра общего назначения. Каждому регистру в этой архитектуре предшествует «$» в инструкции на языке ассемблера. Вы можете обращаться к этим регистрам одним из двух способов. Либо используйте номер регистра (то есть от $0 до $31), либо имя регистра (например, $t1).

Общая структура программы, созданной на языке ассемблера MIPS

Типичная программа, созданная на языке ассемблера MIPS, состоит из двух основных частей. Это раздел объявления данных программы и раздел кода программы.

Раздел объявления данных программы на ассемблере MIPS

Раздел объявления данных программы — это часть программы, идентифицируемая директивой ассемблера .data. Это часть программы, в которой создаются и определяются все переменные, которые будут использоваться в программе. Это также часть программы, где память выделяется в основной памяти (ОЗУ).

Программа на ассемблере MIPS объявляет переменные следующим образом:

 имя: .storage_type значение(я) 

«Имя» относится к имени создаваемой переменной. «Storage_type» относится к типу данных, которые должна хранить переменная. «Значение(я)» относится к информации, которая будет храниться в создаваемой переменной. Следующий синтаксис языка ассемблера MIPS создает одну целочисленную переменную с начальным значением 5:

 .data.
    var1: .word 5 

Раздел кода программы на языке ассемблера MIPS

Раздел кода программы — это часть программы, в которой написаны инструкции, которые должны выполняться программой. Он помещается в раздел программы, определяемый директивой ассемблера .text. Начальная точка раздела кода программы отмечена меткой «main», а конечная точка раздела кода программы отмечена системным вызовом выхода. Этот раздел программы на языке ассемблера MIPS обычно включает в себя манипулирование регистрами и выполнение арифметических операций.

Управление регистрами

При манипулировании регистрами язык ассемблера MIPS использует концепции загрузки и косвенной или индексированной адресации.

В концепции адресации загрузки адрес оперативной памяти переменной в программе на ассемблере MIPS копируется и сохраняется во временном регистре. Например, чтобы скопировать адрес переменной с именем «var1» во временный регистр $t0, требуется следующий синтаксис языка ассемблера MIPS:

 la $t0, var1 

В концепции косвенной адресации значение, хранящееся в определенном адресе оперативной памяти, копируется во временный регистр. Например, используйте следующий синтаксис языка ассемблера MIPS для копирования целочисленного значения, хранящегося в адресе оперативной памяти регистра $t0, в регистр $t2

 lw $t2, ($t0) 

В концепции индексированной адресации адрес оперативной памяти регистра может быть смещен на указанное значение, чтобы получить значение, хранящееся в другом адресе оперативной памяти. Например, чтобы получить значение, хранящееся в адресе оперативной памяти, который находится на расстоянии четырех адресов от адреса оперативной памяти регистра $t0, и сохранить это значение в регистре $t2, требуется следующий синтаксис языка ассемблера MIPS. :
lw $t2, 4($t0)

Выполнение арифметических операций

В языке ассемблера MIPS большинство арифметических операций используют три операнда, и все эти операнды являются регистрами. Размер каждого операнда — слово, а общий формат для выполнения арифметических операций на языке ассемблера MIPS показан следующим образом:

 арифметическая_операция регистр_хранилища, первый_операнд, второй_операнд 

Где «арифметическая_операция» относится к выполняемой арифметической операции. , «регистр_хранилища» относится к регистру, который используется для хранения результата арифметического вычисления; «first_operand» относится к регистру, содержащему первый операнд арифметической операции, а «second_operand» относится к регистру, содержащему второй операнд арифметической операции.

Наиболее распространенными арифметическими операциями, реализованными на языке ассемблера MIPS, являются сложение, вычитание, умножение и деление. В следующей таблице представлены различные перечисленные арифметические операции и то, как они представлены на языке ассемблера MIPS:

Создание простой программы на языке ассемблера MIPS

В этом посте мы создадим простую программу, которая может получить два разных числа от пользователя и выполнять арифметические операции сложения, вычитания и умножения на этих двух числах.

Первое, что нужно сделать при создании этой программы, это определить переменные, которые будут использоваться для хранения строк, используемых в программе.

#Это простая программа для получения от пользователя двух чисел и выполнения
#основных арифметических функций, таких как сложение, вычитание и умножение с ними

#Программа:
#1. Распечатайте операторы, чтобы попросить пользователя ввести два разных числа
#2. Сохраните два числа в разных регистрах и распечатайте «меню» арифметических инструкций для пользователя
#3. На основе выбора, сделанного пользователем, создайте структуры ответвлений для выполнения команд и распечатайте результат
#4. Выйти из программы

 .data
    prompt1: .asciiz "Введите первое число: "
    prompt2: .asciiz "Введите второе число: "
    меню: .asciiz "Введите число, связанное с операцией, которую вы хотите выполнить: 1 => добавить, 2 => вычесть или 3 => умножить: "
    resultText: .asciiz "Ваш окончательный результат: " 

Второе, что нужно сделать, это предварительно загрузить целочисленные значения, представляющие различные инструкции, которые должна выполнять программа, в соответствующие регистры для хранения.

 .текст
.globl основной
главный:
    #Следующий блок кода предназначен для предварительной загрузки целочисленных значений, представляющих различные инструкции, в регистры для хранения
    li $t3, 1 # Это для загрузки непосредственного значения 1 во временный регистр $t3
    li $t4, 2 # Это для загрузки непосредственного значения 2 во временный регистр $t4
    li $t5, 3 # Это для загрузки непосредственного значения 3 во временный регистр $t5 

Затем распечатайте инструкции, требующие от пользователя ввести два числа, над которыми он хотел бы выполнить арифметические операции.

 #просьба пользователя указать первый номер
    li $v0, 4 #команда для печати строки
    la $a0, prompt1 # загрузка строки для печати в аргумент для разрешения печати
    системный вызов # выполнение команды
    

    
    #следующий блок кода предназначен для чтения первого числа, предоставленного пользователем
    li $v0, 5 #команда чтения целого числа
    syscall #выполнение команды для чтения целого числа
    move $t0, $v0 #перемещение числа, считанного с пользовательского ввода, во временный регистр $t0
    
    #просим пользователя указать второй номер
    li $v0, 4 #команда для печати строки
    la $a0, prompt2 # загрузка строки в аргумент для включения печати
    системный вызов # выполнение команды
    
    
    #чтение второго числа, которое будет предоставлено пользователю
    li $v0, 5 #команда для чтения числа, предоставленного пользователем
    syscall #выполнение команды для чтения целого числа
    move $t1, $v0 #перемещение числа, считанного с пользовательского ввода, во временный регистр $t1 

Следующим шагом является распечатка команд, которые пользователь может выполнять с двумя предоставленными номерами. Это сделано для того, чтобы пользователь мог выбрать курс действий.

#следующий блок кода должен напечатать все команды, которые пользователь может выполнить в отношении #двух предоставленных им чисел

 li $v0, 4 #команда для вывода строки
    la $a0, menu #загрузка строки в аргумент для включения печати
    системный вызов # выполнение команды
    


    #следующий блок кода должен прочитать число, предоставленное пользователем
    li $v0, 5 #команда чтения целого числа
    системный вызов # выполнение команды
    move $t2, $v0 #эта команда перемещает указанное целое число во временный регистр $t2 

На этом этапе вам необходимо создать структуры управления, которые определяют, какие инструкции должны выполняться на основе команды, введенной пользователем.

Следующие строки кода определяют, что должно происходить в зависимости от целочисленного значения #, предоставленного пользователем

 beq $t2,$t3,addProcess # Переход к 'addProcess', если $t2 = $t3
    beq $t2,$t4,subtractProcess # Переход к 'subtractProcess', если $t2 = $t4
    beq $t2,$t5,multiplyProcess # Переход к 'multiplyProcess', если $t2 = $t5 

Следующий код указывает на добавление двух указанных цифр.

 добавитьПроцесс:
    add $t6,$t0,$t1 # добавляет значения, хранящиеся в $t0 и $t1, и присваивает их временному регистру $t6
    
    #Следующая строка кода предназначена для вывода результатов вычислений выше
    li $v0,4 #это команда для печати строки
    la $a0,resultText # загружает строку для печати в аргумент $a0 для печати
    syscall #выполняет команду
    
    #следующая строка кода выводит результат вычисления сложения
    ли $v0,1
    la $a0, ($t6)
    системный вызов
    
    li $v0,10 #Это завершение программы 

Это пример кода инструкции по вычитанию двух указанных чисел.

 вычестьПроцесс:
    sub $t6,$t0,$t1 # добавляет значения, хранящиеся в $t0 и $t1, и присваивает их временному регистру $t6
    li $v0,4 #это команда для печати строки
    la $a0,resultText # загружает строку для печати в аргумент $a0 для печати
    syscall #выполняет команду
    
    #следующая строка кода выводит результат вычисления сложения
    ли $v0,1
    la $a0, ($t6)
    системный вызов
    
    li $v0,10 #Это завершение программы 

Этот код предназначен для инструкций по умножению двух указанных чисел.

 умножитьПроцесс:
    mul $t6,$t0,$t1 # добавляет значения, хранящиеся в $t0 и $t1, и присваивает их временному регистру $t6
    li $v0,4 #это команда для печати строки
    la $a0,resultText # загружает строку для печати в аргумент $a0 для печати
    syscall #выполняет команду
    
    #следующая строка кода выводит результат вычисления сложения
    ли $v0,1
    la $a0, ($t6)
    системный вызов
    
    li $v0,10 #Это завершение программы 

Когда вы закончите реализацию показанного выше кода, просто скомпилируйте свою программу и запустите ее. Поздравляем! Вы реализовали свою первую программу на ассемблере MIPS. 😊

Дополнительные ссылки, чтобы узнать больше о синтаксисе языка ассемблера MIPS:
https://chortle.ccsu.edu/AssemblyTutorial/index.html

Заключение

:
Надеюсь, что в этом уроке вы получили все основные навыки вам нужно помочь вам создать очень простую программу на языке ассемблера MIPS.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *