<#+STARTUP: showall indent hidestars

#+TITLE: Системный таймер

* Таймер

#+BEGIN_SRC c :noweb yes :tangle ../kernel/include/sys/timer.h
  #pragma once

  #define BASE_FREQ 1193180
  #define CLOCK_FREQ  1000

  #include "common.h"

  #define sleep(_d) sleep_ms((_d) * CLOCK_FREQ);
  #define timestamp() (getTicks() / (getFrequency() / 1000))

  <<get_ticks_h>>
  <<get_uptime_h>>
  <<get_frequency_h>>
  <<sleep_ticks_h>>
  <<sleep_ms_h>>
  <<init_timer_h>>
#+END_SRC

#+BEGIN_SRC c :noweb yes :tangle ../kernel/src/sys/timer.c
  /**
   ,* @file sys/timer.c
   ,* @author Пиминов Никита (nikita.piminoff@yandex.ru)
   ,* @brief Модуль системного таймера
   ,* @version 0.3.5
   ,* @date 2022-10-01
   ,* @copyright Copyright SayoriOS Team (c) 2022-2024
   ,*/
  #include  "sys/timer.h"
  #include  "sys/isr.h"
  /* #include  "sys/scheduler.h" */
  #include  "drv/fpu.h"
  #include  "io/ports.h"

  extern bool scheduler_working;

  size_t tick = 0;                /* Количество тиков */
  size_t frequency = CLOCK_FREQ;  /* Частота */

  // FIXME: Invalid and deprecated implementation.
  void microseconds_delay(size_t microseconds) {
      for (size_t i = 0; i < microseconds; ++i)
          inb(0x80);
  }

  <<get_ticks>>

  <<get_uptime>>

  <<get_frequency>>

  <<sleep_ticks>>

  <<sleep_ms>>

  <<init_timer>>

  size_t timestamp_for_asm() { return tick / (frequency / 1000); }

#+END_SRC

** get_ticks

Получение кол-ва тиков с момента старта

#+NAME: get_ticks_h
#+BEGIN_SRC c
  size_t getTicks();
#+END_SRC

#+NAME: get_ticks
#+BEGIN_SRC c
  /**
   ,* @brief Получить количество тиков
   ,*
   ,* @return size_t - Количество тиков с момента старта
   ,*/
  size_t getTicks(){
      return tick;
  }
#+END_SRC

** get_iptime

#+NAME: get_uptime_h
#+BEGIN_SRC c
  double getUptime();
#+END_SRC

#+NAME: get_uptime
#+BEGIN_SRC c
  double getUptime() {
      if(getFrequency() == 0) {
          return 0.0;
      }else{
          return (double)tick / (double)frequency;
      }
  }
#+END_SRC

** get frequency

#+NAME: get_frequency_h
#+BEGIN_SRC c
  size_t getFrequency();
#+END_SRC

#+NAME: get_frequency
#+BEGIN_SRC c
  /**
   ,* @brief Получить частоту таймера
   ,*
   ,* @return uint32_t - Частота таймера
   ,*/
  size_t getFrequency(){
      return frequency;
  }
#+END_SRC

** sleep
*** sleep_ticks

#+NAME: sleep_ticks_h
#+BEGIN_SRC c
  void sleep_ticks(uint32_t delay);
#+END_SRC

#+NAME: sleep_ticks
#+BEGIN_SRC c
  /**
   ,* @brief Ожидание по тикам
   ,*
   ,* @param delay - Тики
   ,*/
  void sleep_ticks(uint32_t delay){
      size_t current_ticks = getTicks();
      while (1){
          if (current_ticks + delay < getTicks()){
              break;
          }
      }
  }
#+END_SRC

*** sleep_ms

#+NAME: sleep_ms_h
#+BEGIN_SRC c
  void sleep_ms(uint32_t milliseconds);
#+END_SRC

#+NAME: sleep_ms
#+BEGIN_SRC c
  /**
   ,* @brief Ожидание по милисекундам
   ,*
   ,* @param milliseconds - Милисекунды
   ,*/
  void sleep_ms(uint32_t milliseconds) {
      uint32_t needticks = milliseconds * frequency;
      sleep_ticks(needticks / 1000);

      /* (milliseconds * frequency + 500) / 1000 */
  }
#+END_SRC

** init timer

Регистрирует timer_callback на IRQ0

#+NAME: init_timer_h
#+BEGIN_SRC c
  void init_timer(uint32_t f);
#+END_SRC

#+NAME: init_timer
#+BEGIN_SRC c :noweb yes
  <<timer_callback>>

  /**
   ,* @brief Инициализация модуля системного таймера
   ,*
   ,* @param - Частота
   ,*/
  void init_timer(uint32_t f){
      frequency = f;

      uint32_t divisor;
      uint8_t low;
      uint8_t high;

      divisor = BASE_FREQ / f;

      outb(0x43, 0x36);

      low = (uint8_t) (divisor & 0xFF);
      high = (uint8_t) ((divisor >> 8) & 0xFF);

      outb(0x40, low);
      outb(0x40, high);

      register_interrupt_handler(IRQ0, &timer_callback);
  }
#+END_SRC

#+NAME: timer_callback
#+BEGIN_SRC c
  /**
   ,* @brief Таймер Callback
   ,*
   ,* @param regs - Регистр
   ,*/
  static void timer_callback(__attribute__((unused)) registers_t regs){
      tick++;

      if (is_multitask() && scheduler_working)
          task_switch();
  }
#+END_SRC
