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

Вид материалаПрограмма

Содержание


Учебная виртуальная машина VM
Память и данные
Регистры общего назначения
Системные и управляющие регистры
Система команд
Методы адресации операндов
Основной набор операций
Callr $a; sp-=4; [sp]
Целочисленная арифметика
CMPUI $W1, $W, iB
Дробная арифметика
Битовые операции и сдвиги
Стековые операции
Pushm $a; sp+=4; [sp] = [$a]
Stspm $a; [$a] = [sp]
Ldpsw $w; $w
Дополнительный набор команд
Подобный материал:
1   2   3   4   5

Учебная виртуальная машина VM


VM представляет собой типичный CISC-компьютер, включающей элементы RISC- и ROSC-архитектур. В данной главе описаны базовые свойства учебной виртуальной машины. В дальнейшем, по мере необходимости, будем добавлять в нее те или иные особенности и новые «устройства».

Память и данные


Память VM состоит из 232 байтов с адресами от 0000 000016 до FFFF FFFF16.

Примечание

Указан максимально возможный размер памяти VM. При реализации виртуальной машины обычно задается меньший размер, например 64 килобайта.

Размер байта традиционно равен 8 битам. Размер адреса, таким образом, составляет 4 байта или 32 бита. Ячейки памяти имеют имена M[0], M[1], … M[232-1]; поэтому если x – слово, то M[x] представляет байт памяти.

Байты могут объединяться в группы по 2, 4 и 8. Четыре байта составляют слово, два байта называются полусловом, а восемь байт представляют собой двойное слово. Таким образом, память VM состоит из 232 байт, или 231 полуслов, или 230 слов, или 229 двойных слов. Следовательно, если x — слово, запись M2[x], M4[x], M8[x] обозначают полуслово, слово и двойное слово соответственно, которое содержат байт M[x]. Для полноты картины мы также пишем, что M1[x] = M[x].

Интерпретация группы байтов зависит от используемой команды. Как практически все современные компьютеры, VM способна обрабатывать данные следующих видов:
  • беззнаковые двоичные целые числа;
  • двоичные целые числа со знаком;
  • дробные числа со знаком;
  • битовые последовательности;
  • последовательности байт и многобайтных величин.

Байты и многобайтные величины представляют либо знаковые, либо беззнаковые двоичные целые числа. Младшая часть числа располагается в байте с меньшим адресом. Такое расположение байтов числа получила название little-endian. Такой порядок байтов используется, например, в микропроцессорах Intel.

Примечание

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

Противоположный порядок, при котором по младшему адресу расположена старшая часть числа, называется big-endian (см. рис. 1.5). Для человека такая запись кажется естественной, однако при разработке процессора возникают некоторые сложность, поскольку результат выполнения команды требуется записывать в память, начиная со старшей части числа.


Рис.1.5. Представление целых чисел в памяти VM

Диапазоны беззнаковых целых чисел в VM следующие:
  • байт 0 .. 255
  • полуслово 0 .. 65535
  • слово 0 .. 4 296 967 295
  • двойное слово 0 .. 18 446 744 073 709 551 615

Таким образом, адреса памяти представляются как беззнаковые целые числа размером в слово. Целые числа со знаком имеют следующие диапазоны:
  • байт -128 .. +127
  • полуслово -32 768 .. +32 767
  • слово -2 147 483 648 .. +2 147 483 647
  • двойное слово -9 223 372 036 854 775 808 .. +9 223 372 036 854 775 807

Полуслово обычно называют коротким целым, а двойное слово часто называется длинным целым. Отрицательные целые числа представляются в дополнительном коде. Например, -1 размером в один байт представляет собой двоичное число 111111112.

VM может оперировать дробными числами двух форматов:
  • короткие — с одинарной точностью длиной 32 бита (4 байта);
  • длинные — с двойной точностью длиной 64 бита (8 байт).

Машинное представление дробных чисел соответствует международному стандарту IEEE-754 (см. Приложение 3). Интерпретация слова и двойного слова как чисел с плавающей запятой зависит от используемой команды.

Для обозначения типов данных VM будем пользоваться английскими терминами, большинство из которых можно обнаружить в С++, в Java или в C#10. Числа с плавающей запятой называются так:
  • float — короткое дробное число (4 байта);
  • double — длинное дробное число (8 байт);

    Целые знаковые числа обозначаются таким образом:
  • byte — байт;
  • short — полуслово (2 байта);
  • int — слово (4 байта)
  • long — двойное слово (8 байт).

    Для обозначения беззнаковых целых добавляется слово unsigned:
  • unsigned byte — байт;
  • unsigned short — полуслово (2 байта);
  • unsigned int — слово (4 байта)
  • unsigned long — двойное слово (8 байт).

Регистры


Регистры в любом «железном» компьютере делятся на два вида: регистры общего назначения и системные регистры.

Регистры общего назначения


Виртуальная машина имеет 64 регистра общего назначения размером в слово. Эти же регистры могут использоваться как 32 регистра размером в двойное слово. Операции с целыми и дробными числами выполняются только регистрах. Однако команды загрузки и пересылки «рассматривают» регистры как локальную память процессора из 256 байт, и «умеют» загружать и сохранять любой байт, полуслово, слово или двойное слово. Далее мы будем использовать символические имена:
  • регистры-байты B0..B255;
  • регистры-полуслова S0..S127;
  • регистры-слова W0..W63;
  • регистры-двойные слова D0..D31;

Эти обозначения используются в программе на ассемблере, а в команде, находящейся в памяти VM, стоит номер регистра, занимающий один байт. Номера регистров многобайтных величин тоже занимают в команде один байт:
  • номер регистра-полуслова четный (последний бит номера равен 0);
  • номер регистра-слова кратен 4 (два последних бита номера равны 00);
  • номер регистра-двойного слова кратен 8 (три последних бита номера равны 000).

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

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

Системные и управляющие регистры


PC (Program Counter) — регистр адреса команды, 32 бита. В регистре всегда хранится адрес текущей команды. Так как длина команды кратна 2, то его значение всегда четное.

SP (Stack Pointer) — регистр указателя стека. В регистре хранится адрес вершины стека. В стеке размещаются только слова (или адреса). При помещении величины в стек сначала адрес в регистре уменьшается на 4, а затем величина помещается в стек; при извлечении из стека сначала извлекается величина, а потом адрес в регистре увеличивается на 4. Таким образом, указатель стека всегда показывает на слово на вершине стека.

PSW (Processor Status Word) — слово состояния процессора, 32 бита; включает аварийные признаки результата, маску разрешения прерываний по аварийным признакам, и набор управляющих флагов, устанавливающих специальные режимы работы процессора. Одним из флагов результата является флаг переноса CF.

Трассировка, прерывания и регистры отладки будут описаны в главе об отладчиках.

Система команд


Систему команд можно разделить на 2 подмножества: 254 команды составляют основной набор операций. Эти команды имеют код операций, состоящий из одного байта в диапазоне от 0016 до FE16. Код FF16 определяют еще один набор из 256 операций, у которых код операции занимает 2 байта. Таким образом, если первые семь бит кода операции не равны 11111112, то код операции занимает один байт, и команда относится к основному набору операций. К основному набору относится и команда с кодом 111111102 = 25410. Если же все восемь бит операции равны 111111112, то команда относится к расширенному множеству операций, и код операции состоит из 2 байтов.

Примечание

Полная таблица кодов операций VM приведена в Приложении 1.

В этой главе опишем основной набор операций. По мере необходимости будем описывать расширенное множество операций. Основной набор включает следующие типичные группы операций:
  • переходы;
  • загрузка-сохранение;
  • целая арифметика;
  • арифметика с дробными числами;
  • битовые и сдвиги;
  • стековые;
  • ввод-вывод.

Длина команды может быть 2, 4, 6 и 8 байтов. Основной формат трехадресный и 4-байтный, однако есть много одноадресных команд и ряд двухадресных.

Методы адресации операндов


Аргументы располагаются в команде, в регистре, в памяти и стеке. Методы адресации аргументов следующие:
  • непосредственная; непосредственный целый операнд располагается в команде и имеет размер 1, 2 или 4 байта; обозначается как iB (байт), iS (полуслово), iW (слово);
  • абсолютная; абсолютный адрес размером 4 байта задается в команде и обозначается как A;
  • относительная; размер смещения 2 или 3 байта; обозначается как RA;
  • регистровая; обозначается конкретным типом регистра $B, $S, $W, $D;
  • регистровая косвенная; обозначается как адресный регистр $A;
  • регистровая базово-индексная: второй и третий аргумент — регистры-слова.

В стековых операциях выполняется автоинкремент или автодекремент указателя стека SP.

Основной набор операций


Целое число со знаком, находящееся в регистре общего назначения, будем обозначать s($R), беззнаковое целое — как u($R). Короткое дробное обозначим как f($W), а длинное — d($D). При описании команд будем по традиции использовать символический код операции — английское слово или англоязычную аббревиатуру. Символический код операции соответствует мнемонике языка ассемблера VM.
Переходы

Самой важной группой команд являются команды перехода, поэтому начнем с них. Эта группа обычно включает следующие виды команд:
  • команды условного перехода;
  • команды безусловного перехода;
  • команды перехода к подпрограмме и возврата из подпрограммы;
  • команды цикла.

Еще в эту же группу обычно включают команды прерываний (и возврата из прерываний), но мы рассмотрим их в главе об отладчиках.

Группа команд условного перехода включает 8 команд. Команды условного перехода являются двухадресными и выполняют относительный переход по смещению RA в зависимости от содержимого регистра $W. При невыполнении условий перехода выполняется следующая команда. Смещение имеет размер два байта и может быть как положительным, так и отрицательным. Смещение считается в полусловах, поэтому при выполнении команды значение смещения умножается на 2 и добавляется к содержимому счетчика команд. Умножение на 2 выполняется в связи с тем, что длина команды кратна 2 и не может быть меньше 2 байт. Размер команд условного перехода — 4 байта.

JL $W, RA; если s($W) < 0, то PC <- (PC + 2*RA) mod 232

JZ $W, RA; если $W = 0, то PC <- (PC + 2*RA) mod 232

JG $W, RA; если s($W) > 0, то PC <- (PC + 2*RA) mod 232

JGE $W, RA; если s($W) >= 0, то PC <- (PC + 2*RA) mod 232

JNZ $W, RA; если $W != 0, то PC <- (PC + 2*RA) mod 232

JLE $W, RA; если s($W) <= 0, то PC <- (PC + 2*RA) mod 232

Регистр равен нулю, если все биты — нулевые; регистр не равен нулю, если хотя бы один бит (даже знаковый) — не нулевой; значение в регистре больше нуля, если знаковый разряд (старший) равен 0; значение в регистре меньше нуля, если значение знакового разряда равно 1. Эти условия одинаковы как для целых, так и для дробных чисел, поэтому эти команды работают и для целых чисел и плавающих чисел.

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

JOD $W, RA; если s($W) mod 2 = 1, то PC <- (PC + 2*RA) mod 232

JEV $W, RA; если s($W) mod 2 = 0, то PC <- (PC + 2*RA) mod 232

Целое значение в регистре является четным, если младший разряд равен 0; значение в регистре является нечетным, если младший разряд равен 1. Эти условия не зависят от того, является ли число положительным или отрицательным.

Виртуальная машина имеет несколько команд безусловного перехода.

JMP RA; PC <- (PC + 2*RA) mod 232

JMPR $A; PC <- $A

Эти команды выполняют безусловный переход:
  • JMP — относительный, по смещению от команды JMP;
  • JMPR — косвенный, по абсолютному адресу в регистре;

В команде относительного перехода JMP смещение составляет 3 байта. Длина команды косвенного перехода занимает в памяти 2 байта, а команда относительного перехода — 4 байта.

Адрес, находящийся в регистре, естественно, должен быть четным, иначе возникает прерывание по ошибке адресации.

Команды перехода к подпрограмме имеют такие же формы (и размеры) аргументов. Дополнительное действие команд CALL состоит в запоминании адреса следующей команды (адрес возврата) в стеке.

CALL RA; SP-=4; [SP] <- PC; PC <- (PC + 2*RA) mod 232

CALLR $A; SP-=4; [SP] <- PC; PC <- $A

Для возврата из подпрограммы применяется команда RET, выполняющая косвенный переход по содержимому вершины стека. Адрес возврата извлекается из стека.

RET N; PC <- [SP]; SP+=4; SP+=4*N;

Аргумент команды позволяет «извлечь» из стека N элементов. На самом деле извлечения не происходит, просто корректируется указатель стека. Длина команды, как вы можете видеть, составляет 2 байта.

Команды цикла со счетчиком обеспечивают переход заданное количество раз. При выполнении команды значение счетчика уменьшается на 1 и если значение не равно 0, то выполняется переход. При нулевом значении счетчика переход не выполняется. Значение в регистре-счетчике считается целым беззнаковым числом.

LOOP $С, RA; --u($C); если u($C)!= 0, то PC<-(PC+2*RA) mod 232

LOOP $С, $X, $Y; --u($C); если u($C)!= 0, то PC<-($X + $Y) mod 232

$C – регистр-счетчик; в качестве счетчика используется любой регистр-слово. Первая команда выполняет относительный переход; размер смещения — 2 байта. Вторая форма команды выполняет косвенный переход по адресу, вычисляемому как сумма двух регистров $X и $Y. Длина команд — 4 байта.

К командам перехода относится и команда останова:

STOP N

Команда имеет длину 2 байта. Выполнение этой команды останавливает процессор; при этом регистр PC содержит адрес команды останова.
Целочисленная арифметика

Набор операций целочисленной арифметики вполне традиционен. Однако все операции выполняются только в регистрах: VM в этом случае следует традициям RISC-компьютеров. Большинство команд — трехадресные и имеют размер 4 байта. Но есть и ряд одноадресных команд, занимающих в памяти 2 байта.

ADD $W1, $W2, $W3; s($W1) = s($W2)+ s($W3)

SUB $W1 , $W2, $W3; s($W1) = s($W2)- s($W3)

MUL $W1 , $W2, $W3; s($W1) = s($W2)× s($W3)

DIV $W1 , $W2, $W3; s($W1) = s($W2)/ s($W3)

MOD $W1 , $W2, $W3; s($W1) = s($W2)% s($W3)

Операция MOD вычисляет остаток от деления $W2 на $W3. Команда DIV выполняет целочисленное деление. При выполнении команды умножения MUL старшая часть произведения записывается в следующий после указанного регистра-результата $w1+1.

При выполнении этих операций может произойти переполнение. В этом случае в PSW устанавливается флаг целочисленного переполнения OF. Результат в регистр записывается всегда. Если разрешено прерывание, то оно выполняется после записи результата.

Регистры в команде могут быть одни и те же:

ADD $W1, $W1, $W1

Эта команда фактически выполнит умножение числа в регистре $W1 на 2.

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

ADDU $W1, $W2, $W3; u($W1) = (u($W2)+ u($W3)) mod 2­­32

Эти команды обычно используются для манипуляции с адресами, так как действия выполняются по модулю 2­­32. Прерывания при переполнении никогда не происходит, однако в PSW устанавливается флаг CF.

Команды знаковой и целочисленной арифметики могут быть записаны в формате с непосредственным операндом:

ADDI $W1, iS; s($W1) = (s($W1)+ s(iS))

И в командах беззнаковой арифметики может быть задан непосредственный операнд, например:

ADDUI $W1, iS; u($W1) = (u($W1)+ u(iS)) mod 2­­32

В процессе операции непосредственный аргумент расширяется до размера слова. Арифметические команды с непосредственными операндами выполняют действия только с регистрами-словами.

В состав основного множества операций, естественно, входят и команды сравнения.

CMP $W1, $W, $W3; s($W) = +1, 0, -1

Команда сравнения CMP устанавливает регистр $W равным:
  • +1, если s($W1) > s($W3);
  • 0, если s($W1) = s($W3);
  • -1, если s($W1) < s($W3);

Таким образом, команда сравнения позволяет «забить» любой регистр единицами (это значение -1 в дополнительном коде) или нулями.

Команда беззнакового сравнения CMPU выполняет те же действия с беззнаковыми целыми числами. Команды сравнения могут быть записаны с непосредственным операндом:

CMPI $W1, $W, iB;

CMPUI $W1, $W, iB;

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

Арифметическая группа команд включает и одноадресные команд:

NEG $W; s($W) <- -s($W)

Эта команда просто вычисляет дополнительный код числа, находящегося в регистре $W. Если в регистре содержится наименьшее отрицательное число, то преобразование не выполняется, и в PSW устанавливается флаг переполнения OF. Возникновение прерывания зависит от маски разрешенных прерываний. Если в регистре находится ноль, то никаких действий не выполняется.

ABS $W; если s($W) < 0, то s($W) <- -s($W)

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

Команды инкремента увеличивают и уменьшают значение регистра на единицу. При переполнении они работают аналогично соответствующим командам сложения.

INC $W; s($W) <- s($W) + 1

INCU $W; u($W) <- (u($W) + 1) mod 2­­32

DEC $W; s($W) <- s($W) - 1

DECU $W; u($W) <- (u($W) - 1) mod 2­­32

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

Арифметика с дробными числами тоже выполняется только на регистрах. В команде задаются только длинные регистры — вся арифметика выполняется с двойной точностью.

FADD $D1, $D2, $D3; d($D1) = d($D2) + d($D3)

FSUB $D1, $D2, $D3; d($D1) = d($D2) - d($D3)

FMUL $D1, $D2, $D3; d($D1) = d($D2) × d($D3)

FDIV $D1, $D2, $D3; d($D1) = d($D2) / d($D3)

FMOD $D1, $D2, $D3; d($D1) = d($D2) % d($D3)

FCMP $D1, $W, $D3; s($W) = +1, 0, -1

Деление, естественно выполняется дробное. Остаток от деления тоже представляется в формате дробного числа и вычисляется как

делимое – n * делитель

Здесь n является ближайшим целым к частному делимое/делитель.

В команде сравнения результат записывается точно в такой же форме, что и в командах CMP и CMPU.

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

FSGN $D; // изменение знака

FINT $D; // $D = целая часть числа

FRND $D; // округление до целого

Команда FSGN меняет знак числа на противоположный. Команда FINT отсекает дробную часть и оставляет в регистре целую часть число в дробном формате. Команда FRND выполняет правильное округление до целого и сохраняет результат в том же регистре в плавающем формате.

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

FSQRT $D; // квадратный корень

FEXP $D; // ex

FSIN $D; // sin(x)

FATAN $D; // arctg(x)

FLN $D; // ln(x)

Названия функций говорят сами за себя. Аргумент задается в регистре, результат помещается в тот же регистр. Естественно, при неверном задании аргумента (например, ноль для логарифмических функций) операция не выполняется и аргумент не изменяется. Возникновение прерывания зависит от маски прерываний.

В плавающей арифметике часто используются константы 1.0, 2.0, число е и число π. Система команд содержит ряд команд загрузки этих констант в регистры:

FLD1 $D; $D = 1.0

FLD2 $D; $D = 2.0

FLDPI $D; $D = 3.14159265358979323846 // число Пи

FLDE $D; $D = 2.71828182845904523536 // число е
Битовые операции и сдвиги

В битовых операциях слово х рассматривается как вектор v(x) из 32 отдельных битов. Учебная виртуальная машина включает следующие типичные битовые команды:

OR $W1, $W2, $W3; // поразрядное логическое сложение (ИЛИ)

AND $W1, $W2, $W3; // поразрядное логическое умножение (И)

XOR $W1, $W2, $W3; // сложение по модулю 2 (ИСКЛЮЧАЮЩЕЕ ИЛИ)

Эти три команды можно записывать в форме с непосредственным операндом. В этом случае используются регистры-байты.

ORI $B1, $B2, iB; // поразрядное логическое сложение (ИЛИ)

ANDI $B1, $B2, iB; // поразрядное логическое умножение (И)

XORI $B1, $B2, iB; // сложение по модулю 2 (ИСКЛЮЧАЮЩЕЕ ИЛИ)

Все эти команды занимают в памяти 4 байта. Команда битового отрицания

NOT $W

инвертирует биты в регистре $W и имеет размер 2 байта.

Типичными командами этой группы являются и команды сдвига. Обычно различают логический и арифметический сдвиг. В учебной машине таких команд всего 4 и они тоже работают со словами в регистрах. Команды логического сдвига:

SHU $W1, $W2, $B; u($W1)= (u($W2)×2­s($b))

SHUI $W1, $W2, iB; u($W1)= (u($W2)×2­ib)

В первом варианте регистр $W2 сдвигается на заданное в байтовом регистре $B количество разрядов, и результат записывается в $W1. Направление сдвига определяется знаком числа, записанного в регистре $B. Если число положительное, то выполняется сдвиг влево, при отрицательном значении — сдвиг вправо.

Во втором варианте количество сдвигов задается прямо в команде непосредственным аргументом. Направление сдвига определяется знаком аргумента. Последний выдвигаемый разряд устанавливает в PSW флаг CF. Освобождающиеся разряды заполняются нулями.

Две аналогичные команды выполняют арифметический сдвиг:

SH $W1, $W2, $B; s($W1)= (s($W2)×2­s($b))

SHI $W1, $W2, iB; s($W1)= (s($W2)×2­ib)

Арифметический сдвиг фактически означает умножение и деление на степень двойки. Поэтому сдвиг влево может привести к переполнению. Для положительных чисел арифметический сдвиг вправо не отличается от логического сдвига. А вот для отрицательных чисел при сдвиге вправо освобождающиеся разряды заполняются единицей — знаковым разрядом. Такое положение имеет одно важное следствие, о котором не следует забывать: если в регистре $W2 записана -1, то арифметический сдвиг вправо фактически не изменяет это значение. На рис. 1.6 показано выполнение арифметического сдвига вправо на 1 разряд.



Рис. 1.6. Арифметический сдвиг вправо на 1 разряд

Крайняя правая 1 «выдвигается» из регистра и попадает в CF, а слева «вдвигается» тоже 1, поэтому значение в регистре не изменяется. Как вы понимаете, от количества сдвигов положение не зависит.
Стековые операции

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

PUSH $W; SP+=4; [SP] = $W

PUSHM $A; SP+=4; [SP] = [$A]

Команды помещают на вершину стека слово. Симметричные команды

POP $W; $W = [SP]; SP-=4;

POPM $A; [$A] = [SP]; SP-=4;

извлекают из стека и помещают слово в регистр или в память.

Две команды копируют содержимое вершины стека, не извлекая его из стека.

STSP $W; $W = [SP];

STSPM $A; [$A] = [SP];

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

FADDS $W; f([SP])= f([SP])+ f($W)

FSUBS $W; f([SP])= f([SP])- f($W)

FMULS $W; f([SP])= f([SP])× f($W)

FDIVS $W; f([SP])= f([SP])/ f($W)

FMODS $W; f([SP])= f([SP])% f($W)

Аналогичные 5 команд выполняют действия и запоминают результат не в стеке, а в регистре, например:

FADDR $W; f($W)= f([SP])+ f($W)

Наличие стековой арифметики является элементом ROSC-архитектуры. Такие команды интенсивно используются в компиляторах. И арифметические выражения удобно вычислять, используя стек. В основной набор входит только минимальное множество команд, с помощью которых удобно вычислять арифметические выражения. Большинство стековых команд входят в расширенный набор, который мы рассмотрим в последующем.
Загрузка-сохранение

Команды пересылки — это, в первую очередь, команды загрузки и сохранения регистров. Виртуальная машина обрабатывает 4 размера целых чисел, поэтому для каждого размера в системе команд реализованы соответствующие команды загрузки и сохранения. Символические коды команд загрузки обозначаются так:
  • LDB, LDBA — загрузка байта;
  • LDS, LDSA — загрузка полуслова;
  • LDW, LDWA — загрузка слова;
  • LDD, LDDA — загрузка двойного слова;

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

LDSA $S, A; $S <- M2[A] -- абсолютная

LDS $S, $X, $Y; $S <- M2[($X+$Y)] -- регистровая

Команды просто копируют соответствующее количество байт из памяти в регистр. Поэтому команды загружают как знаковые, так и беззнаковые целые числа. Кроме того, команды LDW позволяет загружать короткое, а команды LDD — длинное дробное число.

LDWA $W, A; $W <- M4[A]

LDW $W, $X, $Y; $W <- M4[($X+$Y)]

LDDA $D, A; $D <- M8[A]

LDD $D, $X, $Y; $D <- M8[($X+$Y)]

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

STBA $B, A; M1[A] <- $B

STD $D, $X, $Y; M8[($X+$Y)] <- $D

Все команды загрузки и сохранения с аргументом-адресом имеют размер 6 байт. Регистровая форма имеет размер 4 байта.

Три команды позволяют загрузить в регистр непосредственный операнд:

LDBI $B, iS; $B <- младший байт iS

LDSI $S, iS; $S <- iS

LDWI $W, iW; $W <- iW

Команды LDBI и LDSI занимают в памяти 4 байта. В команде LDBI в регистр записывается младший байт непосредственного аргумента, который занимает в команде 2 байта.

Команда LDWI имеет размер 6 байт. Команда LDWI имеет второе мнемоническое название кода операции — LA, что означает «загрузка адреса». Непосредственная константа iW в этой команде может считаться абсолютным адресом памяти.

К командам загрузки можно отнести и команды очистки регистра CLRi, которые занимают два байта памяти:

CLRB $B $B <- 0

CLRS $S $S <- 0

CLRW $W $W <- 0

CLRD $D $D <- 0

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

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

LDSP $A; SP <- $A

LDRI $A; RI <- $A

LDPSW $W; $W <- PSW

STPSW $W; PSW <- $W

Учитывая то, что в одних и тех же регистрах размещаются и целые, и дробные числа, необходимы операции загрузки регистров с преобразованием из целого в дробное число. Пересылки с преобразованием аргументов относятся к расширенному набору операций и будут рассмотрены далее.
Ввод-вывод

Ввод-вывод учебной вычислительной машины основан на концепции портов. Порт — это регистр на внешнем устройстве. Каждый порт имеет номер. В программе номер порта записывается как беззнаковое полуслово — таким образом, общее количество портов не превышает 65536, причем нумерация начинается с 0. Порт задается либо в виде непосредственной константы, либо помещается в регистр-полуслово $S.

Вообще-то говоря, ввод-вывод в реальных условиях практически никогда не выполняется с помощью процессорных команд. Уже давно вводом-выводом в реальных компьютерах «заведует» операционная система, в состав которой входят драйверы различных устройств. Поэтому команды ввода-вывода VM определены только для полноты картины. Однако эти команды позволяют моделировать ввод-вывод низкого уровня, выполняя «физический» обмен данными между памятью виртуальной машины и «устройством».

Будем считать, что ввод из нулевого порта означает ввод символов с клавиатуры, а вывод в нулевой порт — вывод символов на экран:

IN $B; // ввод байта с клавиатуры

OUT $B; // вывод байта на экран

Обращение с нулевым портом выполняется по умолчанию, и определяется кодом операции. Так порт ввода-вывода не указывается, команды имеют длину 2 байта.

Помимо этого нам потребуются команды ввода-вывода «высокого» уровня. По аналогии с абстрактными машинами AMF и AMS определим команды ввода-вывода коротких и длинных целых и дробных чисел:

IIN $W // ввод целого с клавиатуры

IIND $D // ввод длинного целого с клавиатуры

FIN $W // ввод короткого вещественного с клавиатуры

FIND $D // ввод длинного вещественного с клавиатуры

IOUT $W // вывод целого на экран

IOUTD $D // вывод длинного целого на экран

FOUT $W // вывод короткого вещественного на экран

FOUTD $D // вывод длинного вещественного на экран

Дополнительный набор команд


Дополнительный набор команд включает еще 256 команд с двухбайтным кодом операции: первый байт равен FF16, второй определяет операцию. Однако в данном разделе мы определим только несколько двухадресных команд пересылки. Читатель может самостоятельно определить необходимые ему операции.
Пересылки

Группа команд обеспечивает пересылки между регистрами, перевод целых в дробные и обратно, а также преобразование целых меньших размеров в большие. Команды входят в расширенный набор операций с двухбайтным кодом операции; первый байт всех команд равен FF16. Все команды двухадресные и занимают в памяти 4 байта. Эти команды, как и команды загрузки-сохранения основного набора, работают со всеми видами целых, поэтому для каждой группы из 4 команд будем описывать общий формат. Четыре команды SWPx (x = B,S,W,D) выполняют обмен содержимого указанных регистров:

SWPx $R1, $R2;

Регистры должны быть одинакового размера.

Команды MOVx выполняют копирование регистра $R2 в регистр $R1:

MOVx $R1, $R2;

Регистры тоже должны быть одинакового размера.

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

CBS $S, $B // полуслово <- байт

CSW $W, $S // слово <– полуслово

CWD $D, $W // слово -> двойное слово

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

CLF $W, $D

выполняет преобразование длинного целого в регистре $D в короткое дробное с пересылкой результата в регистр $W. Симметричная ей команда

CFL $D, $W

выполняет обратное преобразование короткое дробное → длинное целое. Команда

CWD $D, $W

осуществляет перевод из целого-слова в длинное дробное число, а симметричная ей команда

CDW $W, $D

осуществляет обратное преобразование. Естественно, в командах преобразования из дробного числа в целое может произойти переполнение.

И наконец, две команды выполняют преобразования дробных чисел. Команда CFD, имеющая формат

CFD $D, $W

осуществляет преобразование короткого дробного в регистре-слове в длинное дробное число, а команда CDF, имеющая формат

CDF $W, $D

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

Все регистровые команды преобразования имеют длину 4 байта.