Перейти к содержанию

Компьютерная архитектура iiixmish2

завершено на 100%
Материал из Викиучебника — открытых книг для открытого мира
(перенаправлено с «Операционная система Xmtwolime»)
iiixmish2


Введение

[править]

Определения

[править]
Регистр процессора
Регистр процессора — это поле определённой длины, которое находится в памяти процессора. Доступ к регистру очень быстр, его содержимое можно сохранить в память и обратно. Бывает как доступен из программы, так и нет.
Язык ассемблера
Язык ассемблера — это низкоуровневый язык программирования с синтаксисом, максимально приближенным к системе команд процессора, для которого язык предназначен.
Ассемблерная директива
Ассемблерная директива («псевдокоманда») — это команда ассемблера. Директивы влияют на компиляцию программы. Примеры в Makexm2c — .skip, .ascii и .def.

Учебник

[править]

Данный учебник учит программированию на Makexm2c.

Makexm2c

[править]

Makexm2c — это язык ассемблера, предназначенный для создания программ под iiixmish2. Бо́льшая часть команд Makexm2c представляет собой мнемонические обозначения инструкций iiixmish2. Важно упомянуть, что большинство инструкций строги к типам параметров. Например, для add применимы только регистры, а для mov, — регистр как назначение, и константа как источник.

iiixmish2

[править]

Iiixmish2 — это 32-разрядная компьютерная архитектура. Она имеет ≥10 миллионов ячеек памяти, 24 пользовательских регистров, 2000 ячеек видеопамяти, 31 инструкция и экран 63×30 символов.[1]

Получение необходимого

[править]

Скачайте свободную реализацию iiixmish2 здесь, а также ассемблер здесь. Раньше мы предлагали читателям загрузить makexm2c-tools, но сейчас он устарел. Ассемблер, входящий в старый комплект, был заменён новым и быстрым mxm2c-as.

Распакуйте полученные архивы. Скомпилируйте программы, используя OpenJDK 17 и GCC. В Debian, Вы можете установить их, выполнив следующую команду:

$ sudo apt install build-essential openjdk-17-jdk

Выполните java --version.

Общий синтаксис

[править]

Makexm2c-программы могут состоять из команд:

<название> [<аргумент 0>[[,] <аргумент 1>]…]

, псевдокоманд (директив):

.<название> [<аргумент 0>[ <аргумент 1>]]

, меток:

<название>:

и комментариев.

К регистрам можно подставлять «UR» (UR0, UR1…).

Теперь мы можем написать первую программу. Она будет выводить на экран компьютера «hello, world».

Создайте файл «hello.s» и вставьте в него следующее:

; первая ячейка памяти содержит начало непривилегированной области
.byte 1000
; адрес прерывания (не используется в данный момент)
.byte 1000
; адрес системного вызова (не используется в данный момент)
.byte 1000

.orig 27

; открыть порт экрана (0)
open 0
; переход к main
mov UR0, <main>
jmp UR0

;;;;;  данные  ;;;;;

str:
    .skip 12
    .ascii <str> "hello, world"
    .byte 0

;;;;;;;;;;;;;;;;;;;;

main:
    ; стираем экран (vmem[1902] ← 1)
    mov UR6, 1
    vsv UR6, 1902
    ;;;;;;;;;;;;;;
    mov UR0, <str>
    mov UR6, 0
loop:
    ild UR0, UR8
    mov UR10, 0
    mov UR11, <final>
    if UR8 = UR10, UR11
    vsv UR8, UR6
    inc UR0
    inc UR6
    mov UR11, <loop>
    jmp UR11
final:
    ; команда обновления экрана (vmem[1901] ← 1)
    mov UR6, 1
    vsv UR6, 1901

Это код на Makexm2c, переведите его в код, понятный iiixmish2, используя ассемблер:

$ mxm2c-as hello.s hello

Отлично! Запустите эмулятор, передав полученный файл:

$ java downadow.iiixmish2.main.Iiixmish2 hello

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

Инструкция JMP выполняет переход к адресу, который извлекается из заданного регистра. Существует также команда CALL, имеющая форму call регистр, адрес, где первый операнд — регистр, в который будет положен следующий после инструкции адрес (то есть PC+1), а второй — адрес, на который надо перейти (например, <label>).

Регистры

[править]

Как уже было сказано ранее, iiixmish2 имеет 24 пользовательских регистров (п. реги́стры).

Операции[2][3]

[править]

Присваивание

[править]

Команда mov сохраняет беззнаковое 31-битное число в п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО АДРЕС п. регистра, который должен принять ЧИСЛО
ЧИСЛО ЧИСЛО для сохранения

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

mov UR10, 64

Также можно вставлять 'СИМВОЛ' вместо числа, пример:

mov UR10, 'j'

то же, что и

mov UR10, 106

Сложение

[править]

Команда add складывает два п. регистра, сохраняет результат в п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра, содержащего первое слагаемое
ЧИСЛО адрес п. регистра, содержащего второе слагаемое
ЧИСЛО адрес п. регистра, в который следует поместить сумму

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

add UR0 UR6, UR9


Запятые между аргументами и «UR» к адресам, — необязательны.

Вычитание

[править]

Команда sub вычитает из п. регистра значение другого п. регистра, результат сохраняется в п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра, содержащего уменьшаемое
ЧИСЛО адрес п. регистра, содержащего вычитаемое
ЧИСЛО адрес п. регистра, в который следует поместить разность

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

sub UR6 UR8, UR0

Загрузка

[править]

Команда ld загружает значение п. регистра в другой п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра, который следует загрузить в ДРУГОЙ п. регистр
ЧИСЛО адрес ДРУГОГО п. регистра

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

; преобразуется в `and UR1 UR1, UR0`
ld UR1, UR0

Умножение

[править]

Команда mul умножает п. регистр на другой п. регистр, результат сохраняется в п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра, содержащего множимое
ЧИСЛО адрес п. регистра, содержащего множитель
ЧИСЛО адрес п. регистра, в который следует поместить произведение

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

mul UR6 UR8, UR0

Деление

[править]

Команда div делит п. регистр на другой п. регистр, результат сохраняется в п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра, содержащего делимое
ЧИСЛО адрес п. регистра, содержащего делитель
ЧИСЛО адрес п. регистра, в который следует поместить частное

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

div UR6 UR8, UR0

Получение остатка от деления

[править]

Команда rem выполняет деление и сохраняет остаток в п. регистр. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра, содержащего делимое
ЧИСЛО адрес п. регистра, содержащего делитель
ЧИСЛО адрес п. регистра, в который следует поместить остаток

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

rem UR0 UR22, UR8

Инкремент и декремент

[править]

Команда inc увеличивает п. регистр на единицу. Пример использования:

inc UR0


Команда dec уменьшает п. регистр на единицу. Пример использования:

dec UR0

Изменение знака

[править]

Команда tnp изменяет знак п. регистра, пример использования:

; ur[16] = -ur[14]
tnp UR14, UR16

mov2 — синоним mov. Не используйте его в новом коде.

Сдвиг

[править]

Следующий код выполняет операцию вида UR20 << UR19 (сдвиг битов влево) и сохраняет результат в UR8:

lshift UR20 UR19, UR8

UR8 = UR20 >> UR19 (логический правый сдвиг):

rshift UR20 UR19, UR8

Для побитовых XOR, OR и AND, используйте одноимённые инструкции.

Префикс uns, добавляемый к инструкциям, помечает, что регистры должны интерпретироваться как беззнаковые 32-битные числа (применимо к ADD, SUB, DIV, IFB и др.), например:

unsdiv UR0 UR1, UR5

аналогично UR5 = ((unsigned)UR0 / (unsigned)UR1). Также данный префикс можно добавлять к командам isv, ild, push и pop, только в этом случае он означает 8-битную адресацию; например:

unsild 8600000, UR1

загрузит 8-битную долю ячейки памяти 8600000/4 в регистр #1.

Получение времени

[править]

Команда time копирует в заданный регистр кол-во сантисекунд, приблизительно начиная от запуска процессора; принимает один аргумент (регистр).

Комментарии

[править]

Коммента́рий — это текст в коде программы, который не влияет на её работу, но, как правило, помогает лучше понять её. В Makexm2c комментарии могут быть разных видов.[2]

Метки[2]

[править]

Ме́тки — в Makexm2c — это неотъемлемая часть языка. Обращение к метке вставляет на месте обращения адрес, по которому располагается эта метка в двоичной форме программы. Пример:

метка:
    nop
    nop
    mov UR0, <метка>

Для обращения, название метки, заключённое в < и >, вставляется в нужное место в программе.

Именованные константы[2]

[править]

Для создания именованных констант используется директива .def, пример:

.def %CONST% "0"

создаст именованную константу «CONST» со значением «0».

Обращение к именованным константам отличается от обращения к меткам лишь тем, что название заключается в %, а не в < и >.

Задание

Потренируйтесь с пройденными командами.


В iiixmish2 есть команды для создания условных переходов. Рассмотрим, как использовать их в Makexm2c.

Равно

[править]

В iiixmish2 нет инструкций вроде BRz, BRn и т. д., ветвление реализуется посредством команд IFA, IFB и THEN. Для создания перехода вроде «если значение п. регистра А равно значению регистра Б, то выполнить переход на <значение п. регистра В>», воспользуйтесь следующей конструкцией:

if АДРЕС0 = АДРЕС1, АДРЕС2
Название Описание
АДРЕС0 адрес первого п. регистра для сравнения
АДРЕС1 адрес второго п. регистра для сравнения
АДРЕС2 адрес п. регистра, на значение которого нужно перейти

Больше

[править]
if АДРЕС0 > АДРЕС1, АДРЕС2

Меньше

[править]
if АДРЕС0 < АДРЕС1, АДРЕС2

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

unsadd UR4 UR5, UR10
then UR0

выполнит переход на адрес в регистре #0, если во время исполнения команды unsadd произошло переполнение (то есть, данная команда включила флаг). Также эта инструкция используется внутренне ассемблером при компиляции if.

Видеопамять

[править]

Как уже говорилось во введении, iiixmish2 имеет 2000 ячеек видеопамяти. Видеопамять, несмотря на название, является общим буфером между компьютером и гипотетическим устройством. Стандартный порт 0 обозначает экран, о котором мы поговорим ниже.

Почти все ячейки видеопамяти могут отображаться на экране. Последние две ячейки, — 1998-ая и 1999-ая, — хранят параметр цвета для фона и параметр цвета для отображаемых ячеек видеопамяти. Возможные цвета[3]:

Значение Цвет
1 белый
2 зелёный
3 голубой или синий
4 тёмно-зелёный
5 серый
6 красный
7 жёлтый
иное чёрный


Пример изменения цвета отображаемых ячеек видеопамяти на жёлтый:

mov UR0, 7
vsv UR0, 1999

Загрузка и сохранение[2][3]

[править]

Для загрузки значения из видеопамяти в п. регистр, используйте команду vld. Её аргументы:

Синтаксис Описание
ЦЦЦЦ адрес ячейки видеопамяти для загрузки
ЧИСЛО адрес п. регистра-приёмника

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

vld 0001, UR8


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

Синтаксис Описание
ЧИСЛО адрес п. регистра для сохранения
ЦЦЦЦ адрес нужной ячейки видеопамяти

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

vsv UR0, 0063

Вы также можете вписывать адрес п. регистра вместо ЦЦЦЦ в аргументах этих команд (такое поддерживается в Makexm2c). Так, vsv UR0, UR16 сохранит содержимое п. регистра 0 в яч. видеопамяти, адрес которой находится в регистре 16.

Вы можете печатать разными цветами! Для этого, измените ячейку видеопамяти, на месте которой будет изменение цвета последующих, на 255 плюс нужное значение цвета.[3]

Задание

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

Память

[править]

Программы iiixmish2 загружаются в его па́мять.[3] Ограничения кода вне 0-privend (privend = mem[0]−1):

  1. применяется виртуальная адресация памяти, также нельзя читать/прыгать/писать в 0-privend
  2. нельзя использовать инструкцию OPEN
  3. действие инструкции OFF равно переходу к syscall (системный вызов, адрес в mem[2])

Загрузка и сохранение[2][3]

[править]

Команда ild предназначена для загрузки значений из памяти в п. регистры. Аргументы:

Синтаксис Описание
ЦЦЦЦЦЦЦ адрес ячейки памяти для загрузки
ЧИСЛО адрес п. регистра-приёмника.

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

ild 6001900, UR0

Для сохранения значения п. регистра в память, используйте isv. Аргументы:

Синтаксис Описание
ЧИСЛО адрес п. регистра для сохранения
ЦЦЦЦЦЦЦ адрес нужной ячейки памяти

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

isv UR10, 0999999


Вы можете вписывать адрес п. регистра вместо ЦЦЦЦЦЦЦ в аргументах этих команд. Команда вида push x, y, z сохраняет содержимое регистра x в ячейку памяти, адрес которой в регистре y, и увеличивает содержимое регистра y на число z. Команда вида pop y, x, z уменьшает содержимое регистра y на число z, затем сохраняет содержимое ячейки памяти, адрес которой в регистре y, в регистр x.

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

[править]

Ниже представлен пример программы, которая демонстрирует работу ввода/вывода и прерываний. То, что от символа «;» до конца строки будет комментарием.

.orig 0
.byte 1024
.byte 64
.byte 64

; таблица определений диапазонов виртуальных адресов, используемых
; при выполнении в непривилегированной области памяти
; всего 8 ячеек по три параметра:
;   начало диапазона (виртуальный адрес),
;   размер области (ноль - ячейка игнорируется)
;   соответствующий физический адрес
; например, если ячейка:
;   .byte 1000
;   .byte 500
;   .byte 4096
; то адреса 1000..1499 будут переводиться в 4096..4595
; при выполнении ISV, ILD, JMP и т. п., в непривилегированной зоне
; поиск выполняется, начиная с первой ячейки таблицы, и если
; область не найдётся, то доступ будет отклонён 
.byte 0
.byte 4096
.byte 0
.orig 27

; инициализация
open 0
mov UR1, 0
vsv UR1, 1998
mov UR17, <for_1>
mov UR18, 1
mov UR19, 0
mov UR20, 1024
mov UR21, 0
mov UR22, 2048
mov UR23, 0
; старт первого потока
mov UR0, 1024
jmp UR0

; простой механизм переключения контекста
.orig 64
    if UR19 = UR18, UR17
    isv UR0, 0001000
    isv UR1, 0001001
    isv UR2, 0001002
    isv UR3, 0001003
    isv UR4, 0001004
    isv UR5, 0001005
    isv UR6, 0001006
    isv UR7, 0001007
    isv UR8, 0001008
    isv UR9, 0001009
    isv UR10, 0001010
    isv UR11, 0001011
    ild 0001012, UR0
    ild 0001013, UR1
    ild 0001014, UR2
    ild 0001015, UR3
    ild 0001016, UR4
    ild 0001017, UR5
    ild 0001018, UR6
    ild 0001019, UR7
    ild 0001020, UR8
    ild 0001021, UR9
    ild 0001022, UR10
    ild 0001023, UR11
    mov UR19, 1
    ; загрузить сохранённые адрес и флаг в UR20 и UR21
    loff UR20, UR21
    ; переход на UR22 с установкой флага на UR23
    ljmp UR22, UR23
for_1:
    isv UR0, 0001012
    isv UR1, 0001013
    isv UR2, 0001014
    isv UR3, 0001015
    isv UR4, 0001016
    isv UR5, 0001017
    isv UR6, 0001018
    isv UR7, 0001019
    isv UR8, 0001020
    isv UR9, 0001021
    isv UR10, 0001022
    isv UR11, 0001023
    ild 0001000, UR0
    ild 0001001, UR1
    ild 0001002, UR2
    ild 0001003, UR3
    ild 0001004, UR4
    ild 0001005, UR5
    ild 0001006, UR6
    ild 0001007, UR7
    ild 0001008, UR8
    ild 0001009, UR9
    ild 0001010, UR10
    ild 0001011, UR11
    mov UR19, 0
    loff UR22, UR23
    ljmp UR20, UR21

; первый поток
.orig 1024
    mov UR11, <loop>
    ; указатель вывода
    mov UR10, 0
    ; ноль
    mov UR0, 0
loop:
    ; эта команда ничего не делает
    ; но она может замедлить время
    ; выполнения на пару мс
    nop
    ; проверка клавиши
    vld 1900, UR1
    if UR1 = UR0, UR11
    ; сохраняем символ
    vsv UR1, UR10
    ; увеличиваем указатель
    inc UR10
    ; сбрасываем память клавиатуры
    mov UR1, 0
    vsv UR1, 1900
    ; обновляем экран
    mov UR1, 1
    vsv UR1, 1901
    
    ; продолжение
    jmp UR11

; второй поток
.orig 2048
    mov UR3, 1
    xor UR1 UR3, UR1
    vsv UR1, 1999
    vsv UR3, 1901
    mov UR5, 2048
    jmp UR5

Вариант «hello, world» с функциями:

.byte 512
.byte 0
.byte 0
.orig 27

.def %R_OUTPUT% "UR23"
.def %R_RETURN% "UR22"

open 0
mov %R_OUTPUT%, 0
mov UR0, <main>
jmp UR0

string:
    .skip 16
    .ascii <string> "hello, world"

main:
    mov UR0, <string>
    call %R_RETURN%, <puts>
    
    call %R_RETURN%, <halt>

puts:
    ild UR0, UR8
    mov UR10, 0
    mov UR11, <_puts_final>
    if UR8 = UR10, UR11
    vsv UR8, %R_OUTPUT%
    inc UR0
    inc %R_OUTPUT%
    mov UR11, <puts>
    jmp UR11
_puts_final:
    mov UR11, 1
    vsv UR11, 1901
    jmp %R_RETURN%

halt:
    mov UR0, <halt>
    jmp UR0

Ещё директивы

[править]

.ascii <mem0> "hello world" приказывает ассемблеру записать строку «hello world», начиная с яч. <mem0> (адрес метки mem0). .skip N пропускает N байтов, это похоже на N nop'ов. .byte ЧИСЛО вставит десятичное ЧИСЛО. .orig 1000 выполнит переход к ячейке 1000 (следующий после этого код будет записан, начиная с ячейки 1000).

Примечания

[править]