02. Арифметические операции и стек

Занятие 2. Арифметические операции и работа со стеком #

Следующим шагом мы освоим простейшие операции с константами, арифметикой и манипуляциями над стеком.

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

Самый простой способ занести какое-либо значение в стек, это вызвать команду ldc, а точнее один из ее вариантов:

КомандаТип операнда в CILОписание
ldc.i4int32Загрузка целых чисел (System.Int32)
ldc.i8int64Загрузка целых чисел (System.Int64)
ldc.r4float32Загрузка чисел с плавающей запятой (System.Single)
ldc.r8float64Загрузка чисел с плавающей запятой (System.Double)

Например так:

ldc.r4	123.456	

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

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

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

ОперацияОписание
addсложение
subвычитание
mulумножение
divделение
remостаток от деления
andпобитовое И
orпобитовое ИЛИ
xorпобитовое ИСКЛЮЧАЮЩЕЕ ИЛИ
shlсдвиг целых чисел влево
shrсдвиг целых чисел со знаком вправо
shr.unсдвиг целых чисел без знака вправо
negизменение знака числа
notпобитовое НЕ (для целых чисел)

Ни одна из приведенных в таблице команд не требует операндов – они извлекаются из стека. При этом нужно учитывать порядок помещения операндов в стек: сначала первый (делимое, уменьшаемое, …), затем второй (делитель, вычитаемое, …).

В процессе работы со стеком нужно помнить несколько правил:

  • каждая команда (или метод стандартной библиотеки) извлекает из стека нужное ей количество операндов (унарная – один, бинарная – два, …, отдельные команды вообще не требуют исходных данных) и возвращает результат в стек
  • для операций add, div, mul, rem и sub оба операнда должны иметь одинаковый тип (допускаются как целые, так и вещественные). Если типы операндов не совпадают нужно использовать специальные операции преобразования
  • количество слотов в стеке ограниченно и задается директивой .maxstack
  • при выходе из процедуры стек должен быть пуст. Неиспользованные данные должны быть из него удалены.

В процессе работы могут быть полезны некоторые специальные команды манипулирования стеком:

ОперацияОписание
dupкопирует (дублирует) содержимое вершины стека
popудаляет элемент из вершины стека

Чтобы опробовать эти возможности мы разработаем программу, которая будет вычислять расстояние от начала координат, до некоторой точки на плоскости (по известной формуле \(\sqrt{x^2+y^2}\) . Для простоты мы будем задавать значение координат этой точки в виде констант.

.assembly Step_2 {}

.method static void main()
{
	.entrypoint
	.maxstack 3
	
	ldc.r8	3.0	
	dup
	mul

	ldc.r8	4.0
	dup
	mul
	
	add
	
	call	float64 [mscorlib]System.Math::Sqrt(float64)
	
	call	void [mscorlib]System.Console::WriteLine(float64)
	
	call	string [mscorlib]System.Console::ReadLine()
	pop
		
	ret
}

К данному коду требуется ряд пояснений:

  • все операции в данной программе выполняются над вещественными числами повышенной точности (формат double в С#, или float64 в CIL)
  • возведение в квадрат заменяется последовательностью команд dup и mul
  • для извлечения квадратного корня используется статический метод Sqrt из класса System.Math, который содержит большой набор различных математических функций
  • чтобы увидеть результат работы нашей программы в консоли мы вызываем процедуру ReadLine из класса System.Console, которая блокирует дальнейшее выполнение до тех пор, пока пользователь не наберет строку с клавиатуры и не нажмет Enter. Но так как нам не нужна эта строка (точнее мы просто ждем, пока не будет нажата Enter) занесенную в стек строку нам следует удалить командой pop.

Задание #

  1. Проверьте работу приведенной программы.
  2. Модифицируйте программу так, чтобы она находила расстояние между произвольными точками \(\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}\)