/* -----------------------------------------------------------------------------
* Project Name   : Architectures of Processor Systems (APS) lab work
* Organization   : National Research University of Electronic Technology (MIET)
* Department     : Institute of Microdevices and Control Systems
* Author(s)      : Andrei Solodovnikov
* Email(s)       : hepoh@org.miet.ru

See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
* ------------------------------------------------------------------------------
*/
  .section    .boot

 .global _start
_start:
  la    gp, _gbl_ptr     # Инициализация глобального указателя
  la    sp, _stack_ptr   # Инициализация указателя на стек

# Инициализация (зануление) сегмента bss
  la    t0, _bss_start
  la    t1, _bss_end
_bss_init_loop:
  blt   t1, t0, _irq_config
  sw    zero, 0(t0)
  addi  t0, t0, 4
  j     _bss_init_loop

# Настройка вектора (mtvec) и маски (mie) прерываний, а также указателя на стек
# прерываний (mscratch).
_irq_config:
  la    t0, _int_handler
  li    t1, -1 # -1 (все биты равны 1) означает, что разрешены все прерывания
  la    t2, _trap_stack_ptr
  csrw  mtvec, t0
  csrw  mscratch, t2
  csrw  mie, t1

# Вызов функции main
_main_call:
  li    a0, 0 # Передача аргументов argc и argv в main. Формально, argc должен
  li    a1, 0 # быть больше нуля, а argv должен указывать на массив строк,
              # нулевой элемент которого является именем исполняемого файла,
              # Но для простоты реализации оба аргумента всего лишь обнулены.
              # Это сделано для детерминированного поведения программы в случае,
              # если программист будет пытаться использовать эти аргументы.

  # Вызов main.
  # Для того чтобы программа скомпоновалась, где-то должна быть описана
  # функция именно с таким именем.
  call  main
# Зацикливание после выхода из функции main
_endless_loop:
  j     _endless_loop

# Низкоуровневый обработчик прерывания отвечает за:
#   * Сохранение и восстановление контекста;
#   * Вызов высокоуровневого обработчика с передачей id источника прерывания в
#     качестве аргумента.
# В основе кода лежит обработчик из репозитория urv-core:
# https://github.com/twlostow/urv-core/blob/master/sw/common/irq.S
# Из реализации убраны сохранения нереализованных CS-регистров. Кроме того,
# судя по документу приведенному ниже, обычное ABI подразумевает такое же
# сохранение контекста, что и при программном вызове (EABI подразумевает еще
# меньшее сохранение контекста), поэтому нет нужды сохранять весь регистровый
# файл.
# Документ:
#  https://github.com/riscv-non-isa/riscv-eabi-spec/blob/master/EABI.adoc
_int_handler:
  # Данная операция меняет местами регистры sp и mscratch.
  # В итоге указатель на стек прерываний оказывается в регистре sp, а вершина
  # программного стека оказывается в регистре mscratch.
  csrrw sp, mscratch,sp

  # Далее мы поднимаемся по стеку прерываний и сохраняем все регистры.
  addi  sp, sp, -80  # Указатель на стек должен быть выровнен до 16 байт, поэтому
                     # поднимаемся вверх не на 76, а на 80.
  sw    ra, 4(sp)
  # Мы хотим убедиться, что очередное прерывание не наложит стек прерываний на
  # программный стек, поэтому записываем в освободившийся регистр низ
  # программного стека, и проверяем что приподнятый указатель на верхушку
  # стека прерываний не залез в программный стек.
  # В случае, если это произошло (произошло переполнение стека прерываний),
  # мы хотим остановить работу процессора, чтобы не потерять данные, которые
  # могут помочь нам в отладке этой ситуации.
  la    ra, _stack_ptr
  blt   sp, ra, _endless_loop

  sw    t0, 12(sp) # Мы перепрыгнули через смещение 8, поскольку там должен
                   # лежать регистр sp, который ранее сохранили в mscratch.
                   # Мы запишем его на стек чуть позже.
  sw    t1, 16(sp)
  sw    t2, 20(sp)
  sw    a0, 24(sp)
  sw    a1, 28(sp)
  sw    a2, 32(sp)
  sw    a3, 36(sp)
  sw    a4, 40(sp)
  sw    a5, 44(sp)
  sw    a6, 48(sp)
  sw    a7, 52(sp)
  sw    t3, 56(sp)
  sw    t4, 60(sp)
  sw    t5, 64(sp)
  sw    t6, 68(sp)

  # Кроме того, мы сохраняем состояние регистров прерываний на случай, если
  # произойдет еще одно прерывание.
  csrr  t0, mscratch
  csrr  t1, mepc
  csrr  a0, mcause
  sw    t0, 8(sp)
  sw    t1, 72(sp)
  sw    a0, 76(sp)

  # Вызов высокоуровневого обработчика прерываний.
  # Для того чтобы программа скомпоновалась, где-то должна быть описана
  # функция именно с таким именем.
  call  int_handler

  # Восстановление контекста. В первую очередь мы хотим восстановить CS-регистры,
  # на случай, если происходило вложенное прерывание. Для этого, мы должны
  # вернуть исходное значение указателя стека прерываний. Однако его нынешнее
  # значение нам еще необходимо для восстановления контекста, поэтому мы
  # сохраним его в регистр a0, и будем восстанавливаться из него.
  mv    a0,sp

  lw    t1, 72(a0)
  addi  sp, sp, 80
  csrw  mscratch, sp
  csrw  mepc, t1
  lw    ra, 4(a0)
  lw    sp, 8(a0)
  lw    t0, 12(a0)
  lw    t1, 16(a0)
  lw    t2, 20(a0)
  lw    a1, 28(a0)   # Мы пропустили a0, потому что сейчас он используется в
                     # качестве указателя на верхушку стека и не может быть
                     # восстановлен.
  lw    a2, 32(a0)
  lw    a3, 36(a0)
  lw    a4, 40(a0)
  lw    a5, 44(a0)
  lw    a6, 48(a0)
  lw    a7, 52(a0)
  lw    t3, 56(a0)
  lw    t4, 60(a0)
  lw    t5, 64(a0)
  lw    t6, 68(a0)
  lw    a0, 40(a0)

  # Выход из обработчика прерывания
  mret
