@ GNU AS
.syntax unified
.cpu cortex-m4
.thumb
@.charset CP1251

@.desc type=module
@ ****************************************************************************************
@ *           x-timer - модуль софт-таймеров                                             *
@ ****************************************************************************************
@ *  из одного физ.таймера делает множество программных таймеров.                        *
@ *  так же можно включить tick-функции, позволяющие измерять временнЫе интервалы        *
@ *     в тактах SYSCLK (измерять время работы блоков программ)                          *
@ *  Внимание!!!  модуль монопольно использует для работы таймер SysTick. Работа с       *
@ *     SysTick'ом другими программами Приведет к неправильной работе х-таймеров.        *
@ *                                                                                      *
@ *  1. Настройки модуля:                                                                *
@ *  1.1. Указать максимальное количество программных х-таймеров:                        *
.equ  XTIMER_MAX    , 10
@ *  1.2. Необходимость использования защиты ввода:                                      *
.equ _xtimer_max_protection_of_input, 1  @ 1 - использовать, 0 - не использовать         *
@ *   это отключит все проверки (ускорит работу модуля и сократит размер прошивки).      *
@ *  1.3. Настроить SysTick на 1kHz:                                                     *
.equ  AHB_Freq      , 84000000    @ тактовая частота (AHB)                               *
.equ  STK_Period    , 1000        @ частота счета, в гц                                  *
.equ  xtimer_IRQ_Priority, 0xF    @ приоритет прерывания х-таймера (0...15)              *
@ *  1.4. Указать необходтимость использования tick-функций                              *
.equ USE_TICK_LIB, 1           @ 1 - использовать, 0 - не использовать                   *
@ *   - при использовании нельзя сбрасывать счетчик DWT->CYCCNT !!!                      *
@ *      иначе будет считать неправильно!                                                *
@ *   1.4.1. Указать максимальное число засечек для tick-функций:                        *
.equ  MAX_NOTCH  , 10    @  на скорость не влияет, каждая засечка занимает 8 байт памяти *
@ *                                                                                      *
@ *  2. Программная работа:                                                              *
@ *  2.1. В основной программе первой надо вызвать функцию инициализации модуля:         *
@ *       xtimer_Init.                                                                   *
@ *  2.2. Затем создавать и запускать свои х-таймеры с помощью xtimer_Add (см.п. 3.2.)   *
@ *         и xtimer_Start (см.п.3.3.)                                                   *
@ *                                                                                      *
@ *                                                                                      *
@ *  3. Функции модуля х-таймер:                                                         *
@ *         для передачи параметров в/из функций используются следующие регистры:        *
@ *         R0 - mode - режим работы х-таймера                                           *
@ *         R1 - compare - время работы (граница внутреннего счетчика)                   *
@ *         R2 - counter - внутренний счетчик                                            *
@ *         R3 - FlagCnt - флаг-счётчик                                                  *
@ *         R4 - state - текущий статус                                                  *
@ *         R7 - идентификатор таймера (индекс в массиве)                                *
@ *                                                                                      *
@ *  3.1.   xtimer_Init - проверяет что модуль не инициализирован и выполняет начальную  *
@ *            инициализацию всех параметров и адресов. Правильная инициализация.        *
@ *         xtimer_ReInit - выполняет сброс и переинициализацию модуля, даже если он уже *
@ *            был проинициализирован. Все х-таймеры пропадут (кроме х-таймера           *
@ *                                                                        tick-модуля)  *
@ *  3.2.   xtimer_Add (R0=mode, R1=compare, R2=callback) => R7=tID   добавить х-таймер  *
@ *         xtimer_Create (R0=mode, R1=compare, R2=callback) => R7=tID - синоним.        *
@ *            mode - режим работы:                                                      *
.equ  XTIMER_MODE_EMPTY       , 0   @ нет таймера                                        *
.equ  XTIMER_MODE_SINGLE      , 2   @ одиночный. После срабатывания таймер автоматически *
@ *                                    остановится                                       *
.equ  XTIMER_MODE_CYCLE       , 3   @ циклический. После срабатывания таймер будет       *
@ *                                    отсчитывать время заново по-кругу                 *
.equ  XTIMER_MODE_SINGLE_FLAG , 6   @ одиночный с флагом-счетчиком. При каждом           *
@ *       срабатывании автоостановка таймера, и его флаг-счетчик будет увеличиваться     *
.equ  XTIMER_MODE_CYCLE_FLAG  , 7   @ циклический с флагом. При каждом срабатывании      *
@ *                                        флаг-счетчик будет увеличиваться              *
@ *       compare -  задает через сколько мс после старта таймер будет срабатывать       *
@ *       callback - адрес колбэка по срабатыванию таймера. +1 к адресу добавится        *
@ *          автоматически, если вдруг забыли. Колбэк получает на входе в R0 и R7        *
@ *          индекс вызвавшего его x-таймера, R1 = compare, R3 = FlagCnt.                *
@ *          Колбэк может использовать без push-pop R0, R1, R2, R3, R7, R12              *
@ *       Функция xtimer_Add возвращает в регистре R7 идентификатор созданного           *
@ *       х-таймера. Все последующие обращения к данному х-таймеру только с помощью      *
@ *       этого идентификатора (tID по тексту)                                           *
@ *    +++Если количество х-таймеров не меняется в процессе работы программы, то есть    *
@ *       до бесконечного цикла вы создали х-таймеры и не удаляете их потом, тогда       *
@ *       tID можно не хранить в переменных, а задавать в регистр R7 константой, так     *
@ *       как xtimer_Add выдает tID по-порядку от 0 до (_XTIMER_MAX - 1).                *
@ *  3.3.   xtimer_Start(R7=tID) - запускает таймер с момента остановки                  *
@ *         xtimer_Resume(R7=tID) - синоним                                              *
@ *  3.4.   xtimer_Restart(R7=tID) - перезапускает таймер сначала                        *
@ *  3.5.   xtimer_Pause(R7=tID) - ставит таймер на паузу.                               *
@ *         xtimer_Stop(R7=tID) - синоним                                                *
@ *               Продолжить - xtimer_Resume(R7=tID) / xtimer_Start(R7=tID)              *
@ *  3.6.   xtimer_SetFlagCnt(R7=tID, R3=newFlagCnt)  задать новое значение              *
@ *                                                        флаг-счетчика                 *
@ *         xtimer_GetFlagCnt(R7=tID) => R3=FlagCnt   получить в R3 текущее значение     *
@ *                                                        флаг-счетчика                 *
@ *         xtimer_ClearFlagCnt(R7=tID)           Сбросить флаг-счётчик в 0              *
@ *  3.7.   xtimer_SetCompare(R7=tID, R1=newCompare)  задать интервал работы таймеру     *
@ *         xtimer_GetCompare(R7=tID) => R1=compare   получить текущий интервал в R1     *
@ *  3.8.   xtimer_SetCounter(R7=tID, R2=newCounter)  задать значение счетчика           *
@ *         xtimer_GetCounter(R7=tID) => R2=counter   получить текущий счетчик в R2      *
@ *               эти функции работают с внутренним счетчиком х-таймера                  *
@ *  3.9.   xtimer_SetMode(R7=tID, R0=newMode)  задать новый режим работы                *
@ *           xtimer_GetMode(R7=tID) => R0=mode   получить текущий режим в R0            *
@ *  3.10.   xtimer_GetState(R7=tID) => R4=state   получить текущий статус в R4          *
@ *            статус используется если в прерывании с более высоким приоритетом, чем у  *
@ *            физ.таймера, попытаться изменить таймер, который сейчас в interrupt'e,    *
@ *            то есть в процессе изменения счетчика, произойдет отказ.                  *
@ *            Так же можно увидеть что у х-таймера сейчас работает колбэк.              *
@ *            В режиме колбэка отказа не будет как в interrupt'e                        *
.equ  XTIMER_STATE_STOP       , 0    @ остановлен                                        *
.equ  XTIMER_STATE_COUNT      , 1    @ тикает                                            *
.equ  XTIMER_STATE_INTERRUPT  , 2    @ прерывание физ.таймера                            *
.equ  XTIMER_STATE_CALLBACK   , 3    @ работает колбэк                                   *
@ *  3.11.   xtimer_isInit => R0   проверить что модуль инициализирован. Если не         *
@ *            инициализирован, то R0=0.                                                 *
@ *  3.12.   xtimer_Delay(R7=tID, R1=delay)   делает программную задержку на R1 мс       *
@ *      Если вызвать эту функцию в прерывании с тем же или более высоким приоритетом,   *
@ *      чем xtimer_IRQ_Priority, тогда ваш контроллер зависнет. То есть вызывать        *
@ *      xtimer_Delay только в основной программе или в прерывании с низким приоритетом! *
@ *      Точность xtimer_Delay =  R1 + 1мс, то есть заданный интервал точно будет на     *
@ *      паузе, плюс какой-то рассинхрон вызова с работой SysTick'a ( до 1 мс )          *
@ *                                                                                      *
@ *  4. tick-модуль:                                                                     *
@ *     В tick-модуле программно реализован 64-битный счетчик, так как системный         *
@ *        32-битный на 72 MHz переполняется каждую минуту, а 64-битный через 8000 лет.  *
@ *        модуль создан для максимально точных замеров в том числе длительных интервлов,*
@ *        и чтобы не заморачиваться с переполнениями/обнулениями DWT->CYCCNT            *
@ *     tick-модуль инициализируется вместе с x-таймером в подпрограмме xtimer_Init      *
@ *                                                                                      *
@ *  работают функции парно:                                                             *
@ *  1. сделать засечку tick_set_notch                                                   *
@ *  2. получить число тактов с момента засечки tick_get_notch                           *
@ *     получать число тактов можно многократно                                          *
@ *  tick_get_notch вычитает из разности счетчиков время работы tick-функций, таким      *
@ *    образом пользователь получает "чистое время".                                     *
@ *  функции tick_set_notch_r12 и tick_get_notch_r12 передают индекс засечки через r12.  *
@ *  функции *_non_stop_irq не запрещают/разрешают прерывания в своей работе. Сделаны    *
@ *    для использования внутри блоков где уже запрещены прерывания.                     *
@ *  составляя пару tick_set_notch - tick_get_notch нужно иметь ввиду что обе должны быть*
@ *    либо *_non_stop_irq, либо не *_non_stop_irq - просто у них разные поправки счета  *
@ *  Нумерация засечек на усмотрение пользователя                                        *
@ *  Так же можно пользоваться голой функцией tick_get для получения текущего 64-битного *
@ *    счетчика, но поправочный коэффициент на время работы самой функции пользователь   *
@ *    рассчитывает сам. И ещё - tick_get в своём теле запрещает-разрешает прерывания.   *
@ *                                                                                      *
@ ****************************************************************************************
@.enddesc

.global   XTIMER_MAX
.global   XTIMER_MODE_EMPTY
.global   XTIMER_MODE_SINGLE
.global   XTIMER_MODE_CYCLE
.global   XTIMER_MODE_SINGLE_FLAG
.global   XTIMER_MODE_CYCLE_FLAG
.global   XTIMER_STATE_STOP
.global   XTIMER_STATE_COUNT
.global   XTIMER_STATE_INTERRUPT
.global   XTIMER_STATE_CALLBACK

.equ  x_current_count, 0        @ 4 текущий счетчик
.equ  x_compare_count, 4        @ 4 значение срабатывания
.equ  x_count_flag   , 8        @ 4 счетчик срабатываний
.equ  x_callback     , 12       @ 4 адрес функции-колбэка
.equ  x_mode         , 16       @ 1 байт - режим
.equ  x_state        , 17       @ 1 байт - статус

.if USE_TICK_LIB > 0            @ определяем максимальное количество х-таймеров
.equ  _XTIMER_MAX, XTIMER_MAX + 1
.else
.equ  _XTIMER_MAX    , XTIMER_MAX
.endif

.include   "/src/inc/DWT.inc"
.include   "/src/inc/CoreDebug.inc"
.include   "/src/inc/scb.inc"
.include   "/src/inc/stk.inc"


.section .bss
@ xtimer_regs:        .word      0
@ xtimer_init_flag:   .word      0    @ флаг, показывающий инициализацию системы
xtimers:            .space     ( 18 * _XTIMER_MAX )    @ структуры таймеров




.section  .asmcode

@.desc     name=xtimer_Init type=proc
@ +--------------------------------------+
@ |   начальная инициализация системы    |
@ +--------------------------------------+
@ |                                      |
@ | входные параметры: нет               |
@ |                                      |
@ | выходные параметры: нет              |
@ |                                      |
@ } изменяемые регистры по выходу: нет   |
@ |                                      |
@ +--------------------------------------+
@.enddesc
.global xtimer_Init
.global xtimer_ReInit
xtimer_Init:        push       { r0 }        @ надо проверить инициализацию
                    ldr        r0, = SysTick_BASE
                    ldr        r0, [ r0, STK_CTRL ]
                    bfc        r0, STK_CTRL_COUNTFLAG_N, 1
                    cmp        r0, ( STK_CTRL_CLKSOURCE + STK_CTRL_TICKINT + STK_CTRL_ENABLE )
                    pop        { R0 }
                    it eq
                    bxeq       lr    @ если модуль уже был инициализирован, то досрочный выход
xtimer_ReInit:      push       { R0 - R3 }
                    mov        r0, 0  @ r0 = tID
                    mov        r3, 0  @ r3 для обнуления структур таймеров
                    ldr        r1, = xtimers @ r1 = @xtimers - начальный адрес структур таймеров
                    ldr        r2, = _XTIMER_MAX @ граничный индекс tID
lab1_1:             cmp        r0, r2 @ if ( tID >= _XTIMER_MAX ) -> lab1_2
                    bcs        lab1_2
                    str        r3, [ r1, x_current_count ] @ обнуляем всё
                    str        r3, [ r1, x_compare_count ]
                    str        r3, [ r1, x_count_flag ]
                    str        r3, [ r1, x_callback ]
                    strh       r3, [ r1, x_mode ]    @ здесь mode и state обнуляем одной командой
                    add        r1, 18        @ адрес следующего таймера
                    add        r0, 1         @ индекс следующего таймера
                    b          lab1_1
lab1_2:

.if USE_TICK_LIB > 0     @ Если включены tick-функции, то выполняем инициализацию и настраиваем х-таймер
           @  инициализация tick-модуля
                    push       { R12, lr }
                    ldr        r2, = CoreDebug_BASE    @ CoreDebug->DEMCR
                    ldr        r3, [ r2, CoreDebug_DEMCR ]    @ r3 = CoreDebug->DEMCR
                    orr        r3, CoreDebug_DEMCR_TRCENA    @ r3 |= CoreDebug_DEMCR_TRCENA
                    str        r3, [ r2 ]    @ включили трассировку
                    ldr        r2, = DWT_BASE
                    ldr        r3, [ r2, DWT_CTRL ]    @ r3 = DWT->CTRL
                    orr        r3, DWT_CTRL_CYCCNTENA    @ r3 |= DWT_CTRL_CYCCNTENA
                    str        r3, [ r2, DWT_CTRL ]    @ включили счетчик CYCCNT
                    mov        r0, 0
                    str        r0, [ r2, DWT_CYCCNT ]    @ DWT->CYCCNT = 0 - сбросили счетчик
                    ldr        r3, = MAX_NOTCH
                    cmp        r3, 0
                    beq        lab1_6    @ если нет засечек, то пропускаем

           @ засекаем время на вызовов функций засечек и рассчитываем корректировку по-факту
                    ldr        r2, = correct_dwt32
                    mov        r12, 0    @ индекс засечки
                    mov        r3, 0    @ correct_dwt32
                    str        r3, [ r2 ]    @ correct_dwt32 = 0
                    str        r3, [ r2, 4 ]    @ cur_dwt = 0
                    str        r3, [ r2, 8 ]    @ cur_dwt = 0
lab1_3:             bl         tick_set_notch
                    nop
                    bl         tick_get_notch
                    cmp        r0, 1
                    beq        lab1_4        @ получили правильный результат
                    add        r3, r0
                    sub        r3, 1         @  R3 = R3 + r0 - 1
                    str        r3, [ r2 ]    @ correct_dwt32 = R3
                    b          lab1_3        @  добиваемся замера [ nop ]  в 1 такт !!!
lab1_4:
                    ldr        r2, = correct_dwt32_nsi
                    mov        r12, 0   @ индекс засечки
                    mov        r3, 0    @ correct_dwt32_nsi
                    str        r3, [ r2 ]    @ correct_dwt32_nsi = 0
                    str        r3, [ r2, 4 ]    @ cur_dwt = 0
                    str        r3, [ r2, 8 ]    @ cur_dwt = 0
lab1_5:             bl         tick_set_notch_non_stop_irq
                    nop
                    bl         tick_get_notch_non_stop_irq
                    cmp        r0, 1
                    beq        lab1_6        @ получили правильный результат
                    add        r3, r0
                    sub        r3, 1         @  R3 = R3 + r0 - 1
                    str        r3, [ r2 ]    @ correct_dwt32_nsi = R3
                    b          lab1_5        @  добиваемся замера [ nop ]  в 1 такт !!!


lab1_6:             ldr        r1, = notch  @ адрес первой засечки
                    ldr        r3, = MAX_NOTCH * 8    @ крайний недопустисый индекс
                    mov        r2, 0    @ индекс-бегунок
                    mov        r0, 0    @ обнулитель
           @ обнуляем массив notch
lab1_7:             cmp        r2, r3
                    bcs        lab1_8
                    str        r0, [ r1, r2 ]
                    add        r2, 4
                    str        r0, [ r1, r2 ]
                    add        r2, 4
                    b          lab1_7
lab1_8:             pop        { R12, lr }
          @ настройка и запуск х-таймера для tick-модуля
                    @  xtimer_Add:
                    ldr        r3, = xtimers + ( ( _XTIMER_MAX - 1 ) * 18 )    @ адрес последнего х-таймера
                    ldr        r0, = XTIMER_MODE_CYCLE
                    strb       r0, [ r3, x_mode ]    @ задаем режим XTIMER_MODE_CYCLE
                    ldr        r0, = ( ( ( 4294967295 / AHB_Freq ) / 2 ) - 1 ) * STK_Period @ период вызова tick_get на 1 секунду меньше чем предельно необходимый ( 4294967295 / 72000000 / 2 ) * 1000
                    str        r0, [ r3, x_compare_count ]
                    ldr        r2, = tick_get
                    orr        r2, 1    @ к адресу добавляем 1 если её небыло
                    str        r2, [ r3, x_callback ]
                    mov        r0, 1
                    str        r0, [ r3, x_current_count ]    @ = 1
                    ldr        r0, = XTIMER_STATE_COUNT
                    strb       r0, [ r3, x_state ]    @ = XTIMER_STATE_COUNT
.endif
    @ запуск SysTick'a
                   @ SYSTICK_INIT:
                    LDR        R0, = SysTick_BASE
                   @ установка значения пересчета для получения частоты 1000 гц
                    LDR        R1, = ( AHB_Freq / STK_Period ) - 1
                    STR        R1, [ R0, STK_LOAD ]
                    @ источник частоты AHB + прерывания + включаем SYSTICK
                    LDR        R1, = ( STK_CTRL_CLKSOURCE + STK_CTRL_TICKINT + STK_CTRL_ENABLE )
                    STR        R1, [ R0, STK_CTRL ]
                    @ установка приоритета прерываний от SysTick
                    LDR        R0, = SCB_BASE
                    LDR        R1, [ R0, SCB_SHPR3 ]
                    ORR        R1, R1, ( xtimer_IRQ_Priority & 0xF ) << ( SCB_SHPR3_PRI_15_N + 4 )    @   0xF0 << 24
                    STR        R1, [ R0, SCB_SHPR3 ]

                    pop        { r0 - r3 }
                    bx         lr


@.desc     name=xtimer_isInit type=proc
@ +----------------------------------------------------------+
@ |   показывает инициализирован модуль x-таймеров или нет   |
@ +----------------------------------------------------------+
@ |                                                          |
@ | входные параметры: нет                                   |
@ |                                                          |
@ | выходные параметры: R0 = 0 если не инициализирован       |
@ |                                                          |
@ } изменяемые регистры по выходу: R0                        |
@ |                                                          |
@ +----------------------------------------------------------+
@.enddesc
.global xtimer_isInit
xtimer_isInit:      ldr        R0, = SysTick_BASE
                    ldr        R0, [ R0, STK_CTRL ]
                    bfc        r0, STK_CTRL_COUNTFLAG_N, 1
                    cmp        r0, ( STK_CTRL_CLKSOURCE + STK_CTRL_TICKINT + STK_CTRL_ENABLE )
                    it         ne
                    movne      r0, 0
                    bx         lr


@.desc     name=xtimer_Delay type=proc
@ +----------------------------------------------------------+
@ |       создает задержку выполнения программы              |
@ +----------------------------------------------------------+
@ |                                                          |
@ | входные параметры: R7 - идентификатор tID                |
@ |                    R1 - время задержки в мс              |
@ |                                                          |
@ | выходные параметры: нет                                  |
@ |                                                          |
@ } изменяемые регистры по выходу: нет                       |
@ |                                                          |
@ +----------------------------------------------------------+
@.enddesc
.global xtimer_Delay
xtimer_Delay:   push       { r2 - r3 }
.if _xtimer_max_protection_of_input > 0
              ldr    r3, = XTIMER_MAX
              cmp    r7, r3
              itt    cs
              popcs  { r2 - r3 }
              bxcs   lr
              cmp    r1, 1
              itt    cc
              popcc  { r2 - r3 }
              bxcc   lr
.endif
              ldr    r3, = xtimers
              mov    r2, 18
              mla    r3, r7, r2, r3
.if   _xtimer_max_protection_of_input > 0
              ldrb   r2, [ r3, x_mode ]
              cmp    r2, XTIMER_MODE_EMPTY
              itt    eq
              popeq  { r2 - r3 }
              bxeq   lr
.endif
              ldr    r2, = XTIMER_STATE_STOP
              strb   r2, [ r3, x_state ]    @ статус - остановлен
              str    r1, [ r3, x_compare_count ]    @ задаем время срабатывания
              ldr    r2, = XTIMER_MODE_SINGLE_FLAG
              strb   r2, [ r3, x_mode ]             @ задаем режим - одиночный с флаг-счётчиком
              mov    r2, 0
              strb   r2, [ r3, x_count_flag ]       @ флаг-счётчик = 0
              str    r2, [ r3, x_callback ]         @ колбэк = 0 (нет колбэка)
              str    r2, [ r3, x_current_count ]    @ счётчик = 0 (задержка+)
              ldr    r2, = XTIMER_STATE_COUNT
              strb   r2, [ r3, x_state ]            @ статус - работает

lab2_1:       ldr    r2, [ r3, x_count_flag ]    @ ждем флаг-счётчика
              cmp    r2, 0
              beq    lab2_1
              pop    { r2 - r3 }
              bx     lr




@.desc     name=IRQ_SysTick type=proc
@ +-------------------------------------------------------+
@ |  обработчик прерывания переполнения от физ.таймера    |
@ +-------------------------------------------------------+
@ |                                                       |
@ | входные параметры: нет                                |
@ |                                                       |
@ | выходные параметры: нет                               |
@ |                                                       |
@ } изменяемые регистры по выходу: R0                     |
@ |                                                       |
@ +-------------------------------------------------------+
@.enddesc
.GLOBAL   IRQ_SysTick
IRQ_SysTick:  push   { lr }
              mov    r0, 0 @ индекс таймера tID
              ldr    r2, = _XTIMER_MAX @ ограничение индекса
              ldr    r1, = xtimers @ адрес структуры таймера
lab3_1:       cmp    r0, r2 @ if( tID >= _XTIMER_MAX )
              it     cs    @ если просмотрели все таймеры, то выходим
              popcs  { pc }
              ldrb   r3, [ r1, x_mode ] @ r3 = mode
              cmp    r3, XTIMER_MODE_EMPTY
              ittt   eq    @ если нет таймера то переходим к следующему
              addeq  r0, 1    @ увеличиваем индекс tID
              addeq  r1, 18   @ адрес следующей структуры
              beq    lab3_1   @ смотрим след. таймер
              ldrb   r3, [ r1, x_state ] @ r3 = state
              cmp    r3, XTIMER_STATE_COUNT
              ittt   ne  @ если этот таймер не считает, то
              addne  r0, 1    @ увеличиваем индекс tID
              addne  r1, 18   @ адрес следующей структуры
              bne    lab3_1   @ смотрим след. таймер
              ldr    r3, [ r1, x_current_count ]
              ldr    r12, [ r1, x_compare_count ]
              cmp    r3, r12  @ проверяем дотикал ли до compare
              itttt  cc    @ если не дотикал, то
              addcc  r3, 1    @ увеличиваем внутренний счетчик
              strcc  r3, [ r1, x_current_count ]    @ сохраняем
              addcc  r0, 1    @ увеличиваем индекс tID
              addcc  r1, 18   @ адрес следующей структуры
              it     cc    @ не хватило it-блока на 5ю инструкцию
              bcc    lab3_1
                 @ а если дотикал - то срабатывание:
              mov    r3, XTIMER_STATE_INTERRUPT
              strb   r3, [ r1, x_state ] @ изменили статус на interrupt
              ldrb   r3, [ r1, x_mode ]  @ r3 = mode
              cmp    r3, XTIMER_MODE_SINGLE_FLAG
              bcc    lab3_2    @ если без флага-счётчика , то --> lab3_3
              ldr    r3, [ r1, x_count_flag ]  @ загружаем флаг-счётчик
              cmp    r3, 0xFFFFFFFF  @ проверяем что флаг-счётчик меньше предельного значения
              itt    ne       @ если не предел, то
              addne  r3, 1    @ увеличиваем флаг-счётчик на 1 и
              strne  r3, [ r1, x_count_flag ]  @ сохраняем

lab3_2:       mov    r3, 1    @ текущий счетчик при срабатывании становится не 0, а 1 чтобы в следующем прерывании
              str    r3, [ r1, x_current_count ]    @ уже видели что прошел один "тик" таймера
       @ колбэк должен вызываться в последнюю очередь - чтобы пользователь мог изменить статус таймера
              ldr    r12, [ r1, x_callback ] @ r12 = @callback
              cmp    r12, 0x40    @ если адрес < 0x40 (попали в таблицу IRQ-векторов ядра), значит
              bcc    lab3_3  @ нет колбэка у данного таймера -> lab3_3
              mov    r3, XTIMER_STATE_CALLBACK
              strb   r3, [ r1, x_state ]  @ изменили статус на callback
              push   { r0 - r2, r7 }  @ сохраняем ключевые регистры
              mov    r7, r0 @ дублируем tID в r7 для удобства польз.
              ldr    r3, [ r1, x_count_flag ]
              ldr    r1, [ r1, x_compare_count ]
                  @ r1=compare, r3=FlagCnt, r7=tID
              blx    r12    @  вызываем пользовательский колбэк ( R0 - номер таймера )
              pop    { r0 - r2, r7 }  @ восстанавливаем ключевые регистры
              ldrb   r12, [ r1, x_state ] @ смотрим какой статус после колбэка
              cmp    r12, XTIMER_STATE_CALLBACK
              ittt   ne    @ если польз. изменил статус в колбэке, то
              addne  r0, 1     @ увеличиваем индекс tID
              addne  r1, 18    @ адрес следующей структуры
              bne    lab3_1    @ смотрим след.таймер
lab3_3:       @ без колбэка
              ldrb   r3, [ r1, x_mode ]
              ands   r3, 1
              ite    ne
              movne  r3, XTIMER_STATE_COUNT   @ для mode = xxx0 (CYCLE) продолжаем считать
              moveq  r3, XTIMER_STATE_STOP    @ для mode = xxx1 (SINGLE) останавливаем таймер
              strb   r3, [ r1, x_state ]      @ сохраняем статус
              add    r0, 1    @ увеличиваем индекс tID
              add    r1, 18   @ адрес следующей структуры
              b      lab3_1   @ смотрим след.таймер






@.desc     name=xtimer_Sync type=proc
@ +-------------------------------------------------------------+
@ |   синхронизирует счетчик таймера R1 со счетчиком таймера R0 |
@ +-------------------------------------------------------------+
@ |                                                             |
@ | входные параметры: R0 - главный таймер                      |
@ |                    R1 - подгоняемый таймер                  |
@ |                                                             |
@ | выходные параметры: R0 = 0 если не синхронизировались       |
@ |                                                             |
@ } изменяемые регистры по выходу: R0                           |
@ |                                                             |
@ +-------------------------------------------------------------+
@.enddesc
.global xtimer_Sync
xtimer_Sync:        push       { r1 - r3 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r2, = XTIMER_MAX
                    cmp        r0, r2    @ проверяем допустимость входных параметров
                    bcs        lab4_1
                    cmp        r1, r2
                    bcs        lab4_1
.endif
                    ldr        r3, = xtimers
                    mov        r2, 18
                    mla        r0, r0, r2, r3 @ R0 = адрес начала структуры таймера R0
                    mla        r1, r1, r2, r3 @ R1 = адрес начала структуры таймера R1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r3, [ r0, x_mode ]
                    cmp        r3, XTIMER_MODE_EMPTY
                    beq        lab4_1
                    ldrb       r3, [ r0, x_state ]
                    cmp        r3, XTIMER_STATE_INTERRUPT
                    beq        lab4_1
                    ldrb       r3, [ r1, x_mode ]
                    cmp        r3, XTIMER_MODE_EMPTY
                    beq        lab4_1
                    ldrb       r3, [ r1, x_state ]
                    cmp        r3, XTIMER_STATE_INTERRUPT
                    beq        lab4_1
.endif
                    ldr        r3, [ r0, x_current_count ]
                    str        r3, [ r1, x_current_count ]
                    pop        { r1 - r3 }    @ здесь R0 не равен 0
                    bx         lr
.if _xtimer_max_protection_of_input > 0
lab4_1:             mov        r0, 0
                    pop        { r1 - r3 }
                    bx         lr
.endif


@.desc     name=xtimer_Add type=proc
@ +-------------------------------------------------------------------------+
@ |         Добавить новый х-таймер с заданными параметрами.                |
@ +-------------------------------------------------------------------------+
@ |                                                                         |
@ | входные параметры: R0 - режим, например XTIMER_MODE_CYCLE               |
@ |                    R1 - сколько "тиков" считать (compare > 0)           |
@ |                    R2 - адрес функции колбэка или 0 если не нужен       |
@ |                                                                         |
@ | выходные параметры: R7 = идентификатор таймера (tID)                    |
@ |                                                                         |
@ } изменяемые регистры по выходу: R7                                       |
@ +-------------------------------------------------------------------------+
@.enddesc
.global xtimer_Add
xtimer_Add:
.if _xtimer_max_protection_of_input > 0
                    cmp        r0, XTIMER_MODE_SINGLE
                    beq        lab5_1
                    cmp        r0, XTIMER_MODE_CYCLE
                    beq        lab5_1
                    cmp        r0, XTIMER_MODE_SINGLE_FLAG
                    beq        lab5_1
                    cmp        r0, XTIMER_MODE_CYCLE_FLAG
                    beq        lab5_1
                    mov        r7, 0xFFFFFFFF    @ неверный режим -> возвращаем неверный идентификатор
                    bx         lr
lab5_1:             cmp        r1, 0
                    itt        eq                @ если compare = 0, то
                    moveq      r7, 0xFFFFFFFF    @ возвращаем неверный идентификатор
                    bxeq       lr
.endif
                    push       { r3 - r5 }
                    ldr        r3, = xtimers    @ адрес
                    mov        r7, 0    @ индекс
                    ldr        r4, = XTIMER_MAX    @ ищем только в пределах пользовательских индексов
lab5_2:             ldrb       r5, [ r3, x_mode ]
                    cmp        r5, XTIMER_MODE_EMPTY
                    beq        lab5_3    @ нашли empty - записываем
                    add        r7, 1
                    add        r3, 18
                    cmp        r7, r4
                    ittt       cs                @ если не нашли свободного таймера
                    movcs      r7, 0xFFFFFFFF    @ возвращаем неверный идентификатор
                    popcs      { r3 - r5 }
                    bxcs       lr
                    b          lab5_2

lab5_3:             strb       r0, [ r3, x_mode ]    @ нашли empty
                    str        r1, [ r3, x_compare_count ]
                    cmp        r2, 0x40
                    ite        hi
                    orrhi      r4, r2, 1    @ к адресу добавляем 1 если её небыло
                    movls      r4, 0
                    str        r4, [ r3, x_callback ]
                    mov        r4, 1
                    mov        r5, 0
                    str        r4, [ r3, x_current_count ] @ = 1
                    str        r5, [ r3, x_count_flag ]    @ = 0
                    strb       r5, [ r3, x_state ]         @ = XTIMER_STATE_STOP
                    pop        { r3 - r5 }
                    bx         lr



@.desc     name=xtimer_Resume type=proc
@ +------------------------------------------------------------+
@ |   запускает х-таймер в работу с места постановки на паузу  |
@ +------------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера        |
@ | выходные параметры: нет                                    |
@ } изменяемые регистры по выходу: нет                         |
@ +------------------------------------------------------------+
@.enddesc
.global xtimer_Resume

@.desc     name=xtimer_Start type=proc
@ +-------------------------------------------------------+
@ |  запускает х-таймер в работу ( с места остановки )    |
@ +-------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера   |
@ | выходные параметры: нет                               |
@ } изменяемые регистры по выходу: нет                    |
@ +-------------------------------------------------------+
@.enddesc
.global xtimer_Start
xtimer_Resume:
xtimer_Start:       push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX  @ только пользовательские х-таймеры
                    cmp        r7, r1
                    itt        cs
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1  @  r1 = r7 * 18 + addr(xtimers)
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r1, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq      { r1 - r2 }
                    bxeq       lr
.endif
                    mov        r2, XTIMER_STATE_COUNT
                    strb       r2, [ r1, x_state ]
                    pop        { r1 - r2 }
                    bx         lr


@.desc     name=xtimer_Restart type=proc
@ +------------------------------------------------------+
@ |              перезапускает х-таймер                  |
@ +------------------------------------------------------+
@ |          флаг-счётчик не сбрасывает!                 |
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: нет                              |
@ } изменяемые регистры по выходу: нет                   |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_Restart
xtimer_Restart:     push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    itt        cs
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1   @ r1 = r7 * r2 + r1 = tID * 18 + xtimers = базовый адрес нужного х-таймера
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r1, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq      { r1 - r2 }
                    bxeq       lr
.endif
                    mov        r2, 1
                    str        r2, [ r1, x_current_count ]
                    mov        r2, XTIMER_STATE_COUNT
                    strb       r2, [ r1, x_state ]
                    pop        { r1 - r2 }
                    bx         lr


@.desc     name=xtimer_Pause type=proc
@ +------------------------------------------------------+
@ |         ставит х-таймер на паузу                     |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: нет                              |
@ } изменяемые регистры по выходу: нет                   |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_Pause

@.desc     name=xtimer_Stop type=proc
@ +------------------------------------------------------+
@ |         останавливает х-таймер ( на паузу )          |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: нет                              |
@ } изменяемые регистры по выходу: нет                   |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_Stop
xtimer_Stop:
xtimer_Pause:       push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    itt        cs
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r1, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq      { r1 - r2 }
                    bxeq       lr
.endif
                    mov        r2, XTIMER_STATE_STOP
                    strb       r2, [ r1, x_state ]
                    pop        { r1 - r2 }
                    bx         lr


@.desc     name=xtimer_SetFlagCnt type=proc
@ +--------------------------------------------------------+
@ |     установить новое значение флага-счётчика           |
@ +--------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера    |
@ |                    R3 - новое значение флага-счетчика  |
@ | выходные параметры: нет                                |
@ } изменяемые регистры по выходу: нет                     |
@ +--------------------------------------------------------+
@.enddesc
.global xtimer_SetFlagCnt
xtimer_SetFlagCnt:  push       { r0 - r1 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    itt        cs
                    popcs      { r0 - r1 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r0, 18
                    mla        r1, r7, r0, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r0, [ r1, x_mode ]
                    cmp        r0, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq     { r0 - r1 }
                    bxeq       lr
.endif
                    str        r3 , [ r1, x_count_flag ]
                    pop        { r0 - r1 }
                    bx         lr


@.desc     name=xtimer_ClearFlagCnt type=proc
@ +--------------------------------------------------------+
@ |              сбросить флаг-счётчик                     |
@ +--------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера    |
@ |                                                        |
@ | выходные параметры: нет                                |
@ } изменяемые регистры по выходу: нет                     |
@ +--------------------------------------------------------+
@.enddesc
.global xtimer_ClearFlagCnt
xtimer_ClearFlagCnt:  push       { r0 - r1 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    itt        cs
                    popcs      { r0 - r1 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r0, 18
                    mla        r1, r7, r0, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r0, [ r1, x_mode ]
                    cmp        r0, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq     { r0 - r1 }
                    bxeq       lr
.endif
                    mov        r0, 0
                    str        r0 , [ r1, x_count_flag ]
                    pop        { r0 - r1 }
                    bx         lr



@.desc     name=xtimer_GetFlagCnt type=proc
@ +------------------------------------------------------+
@ |       получить текущее значение флага-счетчика       |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: R3 - значение флага счетчика     |
@ } изменяемые регистры по выходу: R3                    |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_GetFlagCnt
xtimer_GetFlagCnt:  push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    ittt       cs
                    movcs      r3, 0
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r1, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    ittt       eq
                    moveq      r3, 0
                    popeq      { r1 - r2 }
                    bxeq       lr
.endif
                    ldr        r3, [ r1, x_count_flag ]
                    pop        { r1 - r2 }
                    bx         lr


@.desc     name=xtimer_SetCompare type=proc
@ +-----------------------------------------------------+
@ |    установить новый интервал  работы х-таймера      |
@ +-----------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера |
@ |                    R1 - новый интервал работы       |
@ | выходные параметры: нет                             |
@ } изменяемые регистры по выходу: нет                  |
@ +-----------------------------------------------------+
@.enddesc
.global xtimer_SetCompare
xtimer_SetCompare:  push       { r2 - r3 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r3, = XTIMER_MAX
                    cmp        r7, r3
                    itt    cs
                    popcs      { r2 - r3 }
                    bxcs       lr
.endif
                    ldr        r3, = xtimers
                    mov        r2, 18
                    mla        r3, r7, r2, r3
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r3, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq      { r2 - r3 }
                    bxeq       lr
.endif
                    str        r1, [ r3, x_compare_count ]
                    pop        { r2 - r3 }
                    bx         lr



@.desc     name=xtimer_GetCompare type=proc
@ +------------------------------------------------------+
@ |      получить интервал работы х-таймера              |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: R1 - интервал работы х-таймера   |
@ } изменяемые регистры по выходу: R1                    |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_GetCompare
xtimer_GetCompare:  push       { r2 - r3 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r3, = XTIMER_MAX
                    cmp        r7, r3
                    ittt       cs
                    movcs      r1, 0
                    popcs      { r2 - r3 }
                    bxcs       lr
.endif
                    ldr        r3, = xtimers
                    mov        r2, 18
                    mla        r3, r7, r2, r3
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r3, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    ittt       eq
                    moveq      r1, 0
                    popeq      { r2 - r3 }
                    bxeq       lr
.endif
                    ldr        r1, [ r3, x_compare_count ]
                    pop        { r2 - r3 }
                    bx         lr


@.desc     name=xtimer_SetCounter type=proc
@ +------------------------------------------------------+
@ |    установить новое значение внутреннему счетчику    |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ |                    R2 - новый счетчик (значение)     |
@ | выходные параметры: нет                              |
@ } изменяемые регистры по выходу: нет                   |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_SetCounter
xtimer_SetCounter:  push       { r0 - r1 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    itt        cs
                    popcs      { r0 - r1 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r0, 18
                    mla        r1, r7, r0, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r0, [ r1, x_mode ]
                    cmp        r0, XTIMER_MODE_EMPTY
                    itt        eq
                    popeq      { r0 - r1 }
                    bxeq       lr
.endif
                    str        r2, [ r1, x_current_count ]
                    pop        { r0 - r1 }
                    bx         lr




@.desc     name=xtimer_GetCounter type=proc
@ +------------------------------------------------------+
@ |  получить значение внутреннего счетчика х-таймера    |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: R2 - текущий счетчик (значение)  |
@ } изменяемые регистры по выходу: R2                    |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_GetCounter
xtimer_GetCounter:  push       { r0 - r1 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    ittt       cs
                    movcs      r2, 0xFFFFFFFF  @ неверный индекс tID
                    popcs      { r0 - r1 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r0, 18
                    mla        r1, r7, r0, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r0, [ r1, x_mode ]
                    cmp        r0, XTIMER_MODE_EMPTY
                    ittt       eq
                    moveq      r2, 0xFFFFFFFF @ несуществующий таймер
                    popeq      { r0 - r1 }
                    bxeq       lr
.endif
                    ldr        r2, [ r1, x_current_count ]
                    pop        { r0 - r1 }
                    bx         lr






@.desc     name=xtimer_SetMode type=proc
@ +------------------------------------------------------+
@ |         задать новый режим работы                    |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ |                    R0 - новый режим работы           |
@ | выходные параметры: нет                              |
@ } изменяемые регистры по выходу: нет                   |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_SetMode
xtimer_SetMode:     push       { r2 - r3 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r3, = XTIMER_MAX
                    cmp        r7, r3
                    itt        cs
                    popcs      { r2 - r3 }
                    bxcs       lr
.endif
                    ldr        r3, = xtimers
                    mov        r2, 18
                    mla        r3, r7, r2, r3  @ r3 = addr(xtimers) + r7 * 18 - базовый адрес выбранного таймера
                    ldrb       r2, [ r3, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    beq        lab15_2
.if _xtimer_max_protection_of_input > 0
                    cmp        r0, XTIMER_MODE_SINGLE
                    beq        lab15_1
                    cmp        r0, XTIMER_MODE_CYCLE
                    beq        lab15_1
                    cmp        r0, XTIMER_MODE_SINGLE_FLAG
                    beq        lab15_1
                    cmp        r0, XTIMER_MODE_CYCLE_FLAG
                    bne        lab15_2
.endif
lab15_1:            strb       r0, [ r3, x_mode ] @ записываем новый режим
lab15_2:            pop        { r2 - r3 }
                    bx         lr





@.desc     name=xtimer_GetMode type=proc
@ +------------------------------------------------------+
@ |    получить текущий режим х-таймера                  |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: R0 - текущий режим (значение)    |
@ } изменяемые регистры по выходу: R0                    |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_GetMode
xtimer_GetMode:     push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    ittt       cs
                    ldrcs      r0, = XTIMER_MODE_EMPTY
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1
                    ldrb       r0, [ r1, x_mode ]
                    pop        { r1 - r2 }
                    bx         lr


@.desc     name=xtimer_GetState type=proc
@ +------------------------------------------------------+
@ |    получить текущий статус х-таймера                 |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: R0 - текущий статус (значение)   |
@ } изменяемые регистры по выходу: R0                    |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_GetState
xtimer_GetState:    push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    ittt       cs
                    movcs      r0, XTIMER_STATE_STOP
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1
.if _xtimer_max_protection_of_input > 0
                    ldrb       r2, [ r1, x_mode ]
                    cmp        r2, XTIMER_MODE_EMPTY
                    ittt       eq
                    moveq      r0, XTIMER_STATE_STOP
                    popeq      { r1 - r2 }
                    bxeq       lr
.endif
                    ldrb       r0, [ r1, x_state ]
                    pop        { r1 - r2 }
                    bx         lr



@.desc     name=xtimer_Delete type=proc
@ +------------------------------------------------------+
@ |    удалить х-таймер из системы                       |
@ +------------------------------------------------------+
@ | входные параметры: R7 - tID - идентификатор таймера  |
@ | выходные параметры: нет                              |
@ } изменяемые регистры по выходу: нет                   |
@ +------------------------------------------------------+
@.enddesc
.global xtimer_Delete
xtimer_Delete:      push       { r1 - r2 }
.if _xtimer_max_protection_of_input > 0
                    ldr        r1, = XTIMER_MAX
                    cmp        r7, r1
                    itt        cs
                    popcs      { r1 - r2 }
                    bxcs       lr
.endif
                    ldr        r1, = xtimers
                    mov        r2, 18
                    mla        r1, r7, r2, r1
                    ldr        r2, = XTIMER_MODE_EMPTY
                    strb       r2, [ r1, x_mode ]
                    pop        { r1 - r2 }
                    bx         lr




@  /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/



.if USE_TICK_LIB > 0


.global   MAX_NOTCH
.section .bss
.align 2
new_dwt32:     .word    0       @ новое значение DWT->CYCCNT
correct_dwt32: .word    0       @ переменная для корректировки рассета на вызов функций
correct_dwt32_nsi: .word      0    @ переменная для корректировки рассета на вызов функций non_stop_irq
cur_dwt:       .space   8       @ текущий 64-счетчик dwt (для рассчетов)
notch:         .space   ( 8 * MAX_NOTCH )    @ массив засечек


.section  .asmcode

@.desc     name=tick_get
@ +---------------------------------------+
@ | возвращает текущий 64-битный счетчик  |
@ +---------------------------------------+
@ |                                       |
@ | входные параметры: нет                |
@ |                                       |
@ | выходные параметры: R1_R0 - счетчик   |
@ |                                       |
@ } изменяемые регистры по выходу: R0, R1 |
@ |                                       |
@ +---------------------------------------+
@.enddesc
.global tick_get
tick_get:     cpsid  i    @ общие прерывания выкл. (чтобы исключить одновременные запросы из обработчиков прерываний!!!)
              cpsid  f    @ fault IRQ выкл.
              ldr    r1, = DWT_BASE + DWT_CYCCNT
              ldr    r0, [ r1 ]    @ R0 = DWT->CYCCNT 32-bit
              push   { r2 - r3 }
              ldr    r2, = cur_dwt
              ldr    r3, [ r2 ]     @ r1_r3 = cur_dwt
              ldr    r1, [ r2, 4 ]  @ старшее слово cur_dwt
              cmp    r0, r3         @ сравниваем младшее с DWT->CYCCNT (на переполнение 32-битного)
              itt    ls
              addls  r1, 1          @ если переполнение, то увеличиваем старшее слово на 1
              strls  r1, [ r2, 4 ]  @ и сохраняем его
              str    r0, [ r2 ]     @ сохраняем младшее слово
              pop    { r2 - r3 }
              cpsie  f              @ fault IRq вкл.
              cpsie  i              @ общие прерывания вкл.
              bx     lr


@.desc     name=tick_set_notch
@ +---------------------------------------+
@ |      поставить засечку времени        |
@ +---------------------------------------+
@ |                                       |
@ | входные параметры: R12 - индекс       |
@ |                                       |
@ | выходные параметры: нет               |
@ |                                       |
@ } изменяемые регистры по выходу: нет    |
@ |                                       |
@ +---------------------------------------+
@.enddesc
.global tick_set_notch
tick_set_notch: push  { r1 - r4 }
              ldr    r1, = MAX_NOTCH
              cmp    r12, r1
              itt    cs   @ if ( r12 >= MAX_NOTCH ) -> выход
              popcs  { r1 - r4 }
              bxcs   lr
                   @ дубликат tick_get без потерь на push, pop, bx lr
              cpsid  i    @ общие прерывания выкл. (чтобы исключить одновременные запросы из обработчиков прерываний!!!)
              cpsid  f    @ fault IRQ выкл.
              ldr    r1, = DWT_BASE + DWT_CYCCNT
              ldr    r4, [ r1 ]      @ R4 = DWT->CYCCNT 32-bit

              ldr    r2, = cur_dwt   @ r2 = addr( cur_dwt )
              ldr    r3, [ r2 ]      @ младшее слово cur_dwt
              ldr    r1, [ r2, 4 ]   @ старшее слово cur_dwt
              cmp    r4, r3          @ сравниваем младшее с DWT->CYCCNT (на переполнение 32-битного)
              ittee  hi
              nophi                   @ если небыло переполнения, то nop для
              nophi                   @       одинакового числа тактов
              addls  r1, 1            @ если было переполнение, то увеличиваем старшее слово на 1
              strls  r1, [ r2, 4 ]    @ и сохраняем его

              str    r4, [ r2 ]    @ сохраняем DWT->CYCCNT в младшее слово cur_dwt
              ldr    r2, = notch
              add    r2, r2, r12, lsl 3    @ r2 = addr(notch[0]) + r12 * 8

              str    r4, [ r2 ]    @ сохраняем 64-битный счетчик в массив notch[r12]
              str    r1, [ r2, 4 ]    @ сохраняем 64-битный счетчик в массив notch[r12]

              cpsie  f    @ fault IRq вкл.
              cpsie  i    @ общие прерывания вкл.

              pop    { r1 - r4 }
              bx     lr




@.desc     name=tick_get_notch
@ +-----------------------------------------------+
@ |  возвращает текущее время с момента засечки   |
@ +-----------------------------------------------+
@ |   ( 64-битное время в тактах SYSCLK )         |
@ |                                               |
@ | входные параметры: R12 - индекс засечки       |
@ |                                               |
@ | выходные параметры: R1-R0 - счетчик засечки   |
@ |                     ( R0 - младшее слово )    |
@ |                                               |
@ } изменяемые регистры по выходу: R0, R1         |
@ +-----------------------------------------------+
@.enddesc
.global tick_get_notch
tick_get_notch:
              cpsid  i    @ общие прерывания выкл. (чтобы исключить одновременные запросы из обработчиков прерываний!!!)
              cpsid  f    @ fault IRQ выкл.
              ldr    r1, = DWT_BASE + DWT_CYCCNT
              ldr    r0, [ r1 ]    @ R0 = DWT->CYCCNT 32-bit
              push   { r2 - r4 }
              ldr    r2, = cur_dwt
              ldr   r3, [ r2 ]    @ r1_r3 = cur_dwt
              ldr   r1, [ r2, 4 ]    @ r1_r3 = cur_dwt
              cmp    r0, r3    @ сравниваем младшее с DWT->CYCCNT (на переполнение 32-битного)
              itt  ls
              addls    r1, 1    @ если было переполнение, то увеличиваем старшее слово на 1
              strls    r1, [ r2, 4 ]    @ и сохраняем его
              str    r0, [ r2 ]    @ сохраняем младшее слово в cur_dwt
               @  R1_R0 текущий 64-битный счетчик ( R0 - младшее слово )
              ldr    r4, = notch
              add    r4, r4, r12, lsl 3    @ addr( notch[0] ) + r12 * 8  - адрес нужной засечки
               @  R3_R2 = notch[r12]
              ldr    r2, [ r4 ]
              ldr    r3, [ r4, 4 ]
               @ R1_R0 = R1_R0 - R3_R2
              subs   R0, R0, R2    @ вычтите младшие значащие слова  R0 = R0 - R2
              sbc    R1, R1, R3    @ вычтите старшие значащие слова с переносом ( carry ) R1 = R1 – R3 – carry
               @ вычитаем время на вызов функций
              ldr    r2, = correct_dwt32
              ldr    r2, [ r2 ]  @ r2 = correct_dwt32
              SUBS   R0, R0, R2    @ вычтите младшие значащие слова  R0 = R0 - R2
              SBC    R1, R1, 0    @ вычтите старшие значащие слова с переносом ( carry ) R1 = R1 – carry
              pop    { r2 - r4 }
              cpsie  f    @ fault IRq вкл.
              cpsie  i    @ общие прерывания вкл.
              bx     lr



@.desc     name=tick_set_notch_non_stop_irq
@ +---------------------------------------+
@ |      поставить засечку времени        |
@ +---------------------------------------+
@ |     ( не отключая прерывания )        |
@ | входные параметры: R12 - индекс       |
@ |                                       |
@ | выходные параметры: нет               |
@ |                                       |
@ } изменяемые регистры по выходу: нет    |
@ +---------------------------------------+
@.enddesc
.global tick_set_notch_non_stop_irq
tick_set_notch_non_stop_irq: push  { r1 - r4 }
              ldr    r1, = MAX_NOTCH
              cmp    r12, r1
              itt    cs   @ if ( r12 >= MAX_NOTCH ) -> выход
              popcs  { r1 - r4 }
              bxcs   lr
              ldr    r1, = DWT_BASE + DWT_CYCCNT
              ldr    r4, [ r1 ]      @ R4 = DWT->CYCCNT 32-bit
              ldr    r2, = cur_dwt    @ r2 = addr( cur_dwt )
              ldr    r3, [ r2 ]      @ младшее слово cur_dwt
              ldr    r1, [ r2, 4 ]   @ старшее слово cur_dwt
              cmp    r4, r3          @ сравниваем младшее с DWT->CYCCNT (на переполнение 32-битного)
              ittee  hi
              nophi                   @ если небыло переполнения, то nop для
              nophi                   @       одинакового числа тактов
              addls  r1, 1            @ если было переполнение, то увеличиваем старшее слово на 1
              strls  r1, [ r2, 4 ]    @ и сохраняем его
              str    r4, [ r2 ]       @ сохраняем DWT->CYCCNT в младшее слово cur_dwt
              ldr    r2, = notch
              add    r2, r2, r12, lsl 3    @ r2 = addr(notch[0]) + r12 * 8
              str    r4, [ r2 ]    @ сохраняем 64-битный счетчик в массив notch[r12]
              str    r1, [ r2, 4 ]    @ сохраняем 64-битный счетчик в массив notch[r12]
              pop    { r1 - r4 }
              bx     lr




@.desc     name=tick_get_notch_non_stop_irq
@ +-----------------------------------------------+
@ |  возвращает текущее время с момента засечки   |
@ +-----------------------------------------------+
@ |   ( 64-битное время в тактах SYSCLK )         |
@ |     ( не отключая прерывания )                |
@ |                                               |
@ | входные параметры: R12 - индекс засечки       |
@ |                                               |
@ | выходные параметры: R1-R0 - счетчик засечки   |
@ |                     ( R0 - младшее слово )    |
@ |                                               |
@ } изменяемые регистры по выходу: R0, R1         |
@ +-----------------------------------------------+
@.enddesc
.global tick_get_notch_non_stop_irq
tick_get_notch_non_stop_irq:
              ldr    r1, = DWT_BASE + DWT_CYCCNT
              ldr    r0, [ r1 ]    @ R0 = DWT->CYCCNT 32-bit
              push   { r2 - r4 }
              ldr    r2, = cur_dwt
              ldr   r3, [ r2 ]    @ r1_r3 = cur_dwt
              ldr   r1, [ r2, 4 ]    @ r1_r3 = cur_dwt
              cmp    r0, r3    @ сравниваем младшее с DWT->CYCCNT (на переполнение 32-битного)
              itt  ls
              addls    r1, 1    @ если было переполнение, то увеличиваем старшее слово на 1
              strls    r1, [ r2, 4 ]    @ и сохраняем его
              str    r0, [ r2 ]    @ сохраняем младшее слово в cur_dwt
               @  R1_R0 текущий 64-битный счетчик ( R0 - младшее слово )
              ldr    r4, = notch
              add    r4, r4, r12, lsl 3    @ addr( notch[0] ) + r12 * 8  - адрес нужной засечки
               @  R3_R2 = notch[r12]
              ldr    r2, [ r4 ]
              ldr    r3, [ r4, 4 ]
               @ R1_R0 = R1_R0 - R3_R2
              subs   R0, R0, R2    @ вычтите младшие значащие слова  R0 = R0 - R2
              sbc    R1, R1, R3    @ вычтите старшие значащие слова с переносом ( carry ) R1 = R1 – R3 – carry
               @ вычитаем время на вызов функций
              ldr    r2, = correct_dwt32_nsi
              ldr    r2, [ r2 ]  @ r2 = correct_dwt32_nsi
              SUBS   R0, R0, R2    @ вычтите младшие значащие слова  R0 = R0 - R2
              SBC    R1, R1, 0    @ вычтите старшие значащие слова с переносом ( carry ) R1 = R1 – carry
              pop    { r2 - r4 }
              bx     lr


.endif @ USE_TICK_LIB > 0







