Среда, 01.05.2024, 06:08

DCPU-16 Tutorial

Данное руководство предназначено для людей, которые практически не имеют знаний в области программирования на ассемблере. Более строгий обзор ассемблера DCPU-16 см. в разделе Синтаксис ассемблера (перевод в процессе).

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



Память

Слова

В информатике, "слово" имеет совершенно другой смысл, чем в английского языка. "Слово" в области компьютерных наук является блоком памяти, который может содержать одно число. В случае DCPU-16, каждое слово имеет максимальный размер 16 бит. Это означает, что максимальное значение числа в едином блоке памяти на DCPU-16 составляет 2^16 - 1 (-1 потому, что мы включаем 0). Другими словами, слова на DCPU-16 имеют диапазон [0, 65535] (в десятичной системе исчисления).

Пример 16-битного слова: "0011000000111001", переводя на нормальный язык (то есть в десятичную систему исчисления) - это 12345.

Хранение

Есть два места, которые вы можете использовать для памяти на DCPU-16 - это регистры и стек.

Регистры

Внутри самого процессора, существуют 10 регистров. Регистр представляет собой блок памяти, который может хранить одно слово. Поскольку регистры находятся внутри процессора, доступ к ним осуществляется очень быстро, в отличие от оперативной памяти. Каждый регистр имеет имя. В DCPU-16 есть такие регистры: A, B, C, X, Y, Z, I, J, PC, SP.

A, B, C, X, Y, Z, I и J являются регистрами общего назначения. Вы можете использовать их, чтобы осуществить любые расчеты, которые вы хотите. PC (счетчик команд - от англ. Program Counter) и SP (указатель стека - от англ. Stack Pointer) - специальные регистры. Их цель будет объяснена в этом руководстве позже.

Стек

Стек используется для хранения и загрузки данных из оперативной памяти. Необходимо использовать стек, если вы хотите сделать расчеты, но не использовать память регистров. SP (указатель стека) регистр всегда содержит адрес вершины стека, но это дополнительная возможность и мы обсудим её позже.



Инструкции

Каждая строка кода в системе DCPU-16 соответствует одной команде. Инструкция, как правило, осуществляет одно простое вычисление. Одна строчка программы на ассемблере также соответствует одной строки кода в чистом двоичном виде. В этой статье, будут представлены только простейшие инструкции.

Инструкция SET

Набор команд просто устанавливает данное значение а заданный регистр. Формат: SET имя_регистра, значение

Пример:

SET A, 5

Это строка означает, что значение 5 устанавливается в данный регистр.

Арифметические инструкции

ADD, SUB, MUL и DIV используются для выполнения простых математических вычислений. Они выступают за "сложение", "вычитание", "умножение" и "деление", соответственно. Другими словами, им соответствуют символы + (плюс), - (минус), * (умножить), / (поделить). Вы можете использовать их так:

Это позволит прибавить к значению 5 единиц
ADD A, 5

Это позволит отнять от значения 5 единиц
SUB A, 5

Это позволит умножить значение на 5 единиц
MUL A, 5

Это позволит разделить значение на 5 единиц
DIV A, 5

УПРАЖНЕНИЕ 1:

Задача: Вычислить 5 + 40 - 3 * 12 и сохранить результат в регистр

Решение:

SET A, 5
ADD A, 540
SET B, 3
MUL B, 12
SUB A, B

Обратите внимание, внимание на порядок действий.

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

Условное исполнение

Вы можете заставить программу выполнять определенные действия, если выполнились определенные условия. Всего существует три условных инструкции - IFE (от англ. - If Equal, если значение 1 равно значению 2), IFN (от англ. If Not Equal, если значения не равны), IFG (от англ. If Greater - если значение 1 больше значения 2). Если значения не соответствую условию, то инструкция условия пропускается.

УПРАЖНЕНИЕ 2:

SET A, 3
SET B, 3
IFE A, B
SET C, 10
IFN A, B
SET C, 0
IFG C, 0
SET A, C

  1. Определите какие значения получили переменные A, B, C? (в конце статьи появятся ответы)
  2. Попробуйте создать свою программу с использованием этого материала!

Комментарии

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

Комментарии обозначаются символом ';'. Если вы поместите этот символ в своем коде, все, что идёт после этого символа будет игнорироваться компилятором и влиять на программу не будет!

Пример:

SET A, 107
; Ограничивает значение A от 20 до 100
IFG 20, A ; если 20 > A, A = 20
SET A, 20
IFG A, 100 ; если A > 100, A = 100
SET A, 100 ; любой комментарий

Метки и переходы

Ваша программа может быть нелинейной. Возможно, вам придется выполнить какую-то части кода много раз, или или заставить её резко выключится. Все это можно сделать с помощью меток. Метки - это способ дать имя определенной строчке в вашей программе. После представления метки, вы можете сразу перейти к ней обратно. Текущая строка сохраняется в виде PC (Program Counter) регистра. Вы можете изменить значение PC на метку, чтобы перейти к метке.

Метки объявляются следующим образом:

: MyLabel


Вы можете перейти к данной метке, используя этот код:

SET PC, mylabel

Обратите внимание, что порядок, в котором метки создаются не имеет значения. Вы имеете право перейти к метке еще ​​дальше по коду.

УПРАЖНЕНИЕ 3:

SET A, 2
SET B, 0
: loop_start ; начало цикла
IFE B, 0
SET PC, loop_end
ADD B, 7
SUB A, 1
SET PC, loop_start
: loop_end ; конец цикла

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

Подпрограммы

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

JSR имя_подпрограммы

Основа подпрограммы имеет следующий формат:

: mysubroutine ; : имя_подпрограммы
; А некоторые расчеты можно сделать здесь
SET PC, POP

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

Вот пример подпрограммы в действии:

SET A, 4
JSR squared
SET PC, halt

; Умножение A на A (в квадрате то есть)
: squared
MUL A, A
SET PC, POP

; Если вы установите значение PC в ту же строку, на которой программа выполняется, то программа завершится.

: halt SET PC, halt

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

Доступ к памяти

На данный момент руководство становится все более продвинутым. Выпейте еще одну чашку кофе и продолжайте читать еще внимательнее! Вам может понадобиться больше разъяснений по определенным понятиям, если раньше вы не программировали до этого на таком языке как C или на другом ассемблере.

Значением в регистре может быть адрес переменной в памяти, а не фактическое значение переменной. Для того, чтобы получить доступ к значению по этому адресу, вы можете использовать обозначение [A] (в данном случае в качестве примера мы используем регистр).

Пример:

SET [0xC000], 5 ; устанавливает значение 5 в 0xC000
SET A, 0xC000 ; устанавливает в А значение 0xC000, что в десятичной системе равно 49152 (не путайте со значением по адресу 0xC000)
SET B, [A] ; устанавливает значение B, равное значению, которое находится по адресу, который, в свою очередь, находится в A

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

Стек
 
Стек является основной точкой доступа к памяти. Стек имеет тип поведения LIFO (Последний Зашел, Первый Вышел - от англ. Last In, First Out). Другими словами, он работает как башня: вы можете добавлять новые блоки на вершину башни, или удалить блоки с вершины башни. Добавление блоков известно как "pushing" (давить - от англ. push или, как мы будем говорить "извлекать"), а удаление блоков известно как "popping" (выскакивать - от англ. pop или, как мы будем говорить "помещать").

Основное использование стека - хранение локальных переменных без "захламления" регистров. Специальный регистр SP - Stack Pointer (указатель стека) используется для запоминания того, где находиться вершина стека. Поэтому вы можете использовать его для доступа к переменным в стеке. Вот пример использования стека для хранения переменных:

УПРАЖНЕНИЕ 4:

SET PUSH, 10 ; помещаем 10 в стек
SET PUSH, 5 ; помещаем 5 в стек
SET B, 7
SET PUSH, B ; помещаем значение B в стек
SET A, [SP] ; установка в A значения вершины стека
SET B, 3
SET B, POP ; извлекаем значение с вершины стека и присваиваем его регистру B
SET 5, POP ; извлекаем 5 блоков из стека
SET 10, POP ; извлекаем 10 блоков из стека
; Стек стал пустым

Следите за кодом в этом примере, описывая содержимое стека и значение B в каждой строчке кода. Сверьте свой ответ с ответом в конце статьи.

Обратите внимание на последние две строчки в последнем примере, там нету необходимости указывать число блоков, которые вы хотите извлечь. В официальной спецификации, говорится: "Если команда пытается присвоить что-то литералу, то присвоение не происходит. Кроме этого, команда ведет себя как обычно." Я использовал только 5 и 10 чтобы вас не запутать.

Собственные функции

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

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

Пример:

SET X, 2
JSR do_calc
; A получает значение вот этого примера - 5 + 40 - 3 * 12
; X сохранил значение 2 после вызовов функции

: halt SET PC, halt

: do_calc
; начало функции
SET PUSH, X
SET PUSH, Y

SET X, 5
ADD X, 40
SET Y, 3
MUL Y, 12
SET A, X
SUB A, Y
; конец функции

SET Y, POP
SET X, POP
SET PC, POP

Увидев функции, подпрограммы и стек, можете ли вы выяснить цель "SET PC, POP" в конце функций и процедур? Вот вам подсказка - инструкция JSR делает две вещи: во-первых, запускает адрес инструкции, после того, как JSR попадает в стек, а затем изменяет PC соответственно указанной метки.



Что дальше?

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



Ответы на упражнения

Упражнение 1:

    A: 9
    B: 36

Упражнение 2:

    A: 10
    B: 3
    C: 10

Упражнение 3:

    A: 0
    B: 14

Упражнение 4:

Перед строкой 1:
            ]

после строки 1:
[          10]

после строки 2 :
[       5 10]

строка 3:
B теперь имеет значение 7

после строки 4 :
[    7 5 10]

строка 5:
A теперь имеет значение 7 (значение на вершине стека)

строка 6:
B
теперь имеет значение 3

после строки 7:
[       5 10]
B теперь имеет значение 7

после строки 8:
[          10]

после строки 9:
[             ]



Это все! С вами был Defend (перевод) и IskanderUA (корректура)! Удачного программирования... надеюсь вы всё поняли!

Defend Developing | 2024 | Нашёл баг?