Опубликован: 19.01.2025 | Доступ: свободный | Студентов: 0 / 0 | Длительность: 05:57:00
Лекция 8:

Лабораторная работа №7. Вызов подпрограммы, работа со стеком

< Лекция 7 || Лекция 8: 12 || Лекция 9 >

7.2.4 Стек

Для динамического хранения локальных переменных и адресов возврата нужен стек. Стек регулируется соответствующим соглашением ABI:

  • выделенный регистр sp (x2)
  • определено дно стека и его начальное значение
  • начальное значение отделено от дна буфером
  • стек растёт вниз по одному слову (4 байта)
  • операции добавления и снятия:
    • при добавлении сначала уменьшается указатель, затем записывается значение
    • при снятии сначала считывается значение, затем увеличивается указатель
    • при такой организации не используется исходная ячейка стека (начальное значение)

Пример

li t1 123	      # какое-то значение
addi sp sp -4   # положить на стек - 1
sw t1 (sp)      # положить на стек - 2
addi t2 t1 100  # какое-то ещё значение
addi sp sp -4   # положить на стек - 1
sw t2 (sp)      # положить на стек - 2
lw t3 (sp)      # доступ к вершине стека
lw t4 4(sp)     # доступ ко 2-му элементу стека
lw t0 (sp)      # снятие со стека - 1
addi sp sp 4    # снятие со стека - 2
addi sp sp -4   # положить на стек - 1
sw zero (sp)    # положить на стек - 2

Особенности хранения данных в стеке

  • Несколько более эффективно, чем в произвольном месте памяти (lw/sw не превращаются в псевдоинструкции).
  • Использует адресацию относительно постоянно меняющегося sp. Как следствие, требует аккуратного подсчёта текущей глубины стека.
  • Не требует явного указания адреса и заведения метки в программе на языке ассемблера.
  • Может привести к сбоям в работе при переполнении/исчерпании/неаккуратном использовании стека.

7.3 Задание к лабораторной работе

  1. Создать программу согласно варианту, которая
    • выполняет заданные действия,
    • выводит на экран сообщения о результатах/ошибках.
  2. Откомпилировать программу и запустить на исполнение.
  3. Отладить программу, проследить изменения в регистрах и ОП.

7.3.1 Описание последовательности выполнения работы

Скомпилируйте исходную программу и убедитесь в ее работоспособности в RISC-V ОС, либо через qemu-riscv64.

7.3.2 Пример выполнения задания на защиту

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

function gcd(a, b)
               	while a ? b
	           	if a > b
    	       	a := a ? b
	           	else
    	       	b := b ? a
               	return a

Фрагмент решения:

main:
	# read_int (t0)
	# read_int (t1)
                   
	mv  a0, t0
	mv  a1, t1
	jal euclid
 
	li  a7, 1
	ecall
	li a7, 10
	ecall
 
euclid:
	beq a0, a1, finish
	blt a0, a1, if_less
	sub a0, a0, a1
	j   euclid
if_less:
	sub a1, a1, a0
	j   euclid
finish:
	jr ra

ВАРИАНТЫ ЗАДАНИЙ

  1. Напишите программу, которая вводит целые числа M и N и выводит сетку M x N, составленную с помощью + и -. Вы должны написать подпрограмму, который принимает два параметра: M и N, и выводит строку, подобную этой: +-+-+-+-+
    Input:
    3
    4
    Output:
    +-+-+-+
    | | | |
    +-+-+-+
    | | | |
    +-+-+-+
    | | | |
    +-+-+-+
    | | | |
    +-+-+-+
     
  2. Написать функцию, которая меняет местами два значения, переданные в стек.
  3. Написать функцию, которая находит минимальное и максимальное значение в заданном массиве целых чисел.

7.4 Вопросы для контроля

  1. В чем состоит основная идея использования подпрограмм?
  2. Какими командами можно реализовать вызов подпрограммы в RISC-V?
  3. Для чего используется стек?
< Лекция 7 || Лекция 8: 12 || Лекция 9 >