.include "qf_common.inc"

.text

; Си-прототип:
;   void qf_mat44_mul(const float* a, const float* b, float* c);

; Оптимизации:
;  * для 4-х клеточного МП - MCp0411100101

.global qf_mat44_mul
qf_mat44_mul:
    .alias qf_mat44_mul.a #SP,4
    .alias qf_mat44_mul.b #SP,8
    .alias qf_mat44_mul.c #SP,12
    .alias qf_mat44_mul.old_i32 #SP,-4
    .alias qf_mat44_mul.old_i33 #SP,-8
    .alias qf_mat44_mul.old_i34 #SP,-12

    .alias qf_mat44_mul.stackb 12      ; Количество байт под стек. #BP(#38) не используется.
    .alias qf_mat44_mul.old_i32.newSP #SP,8
    .alias qf_mat44_mul.old_i33.newSP #SP,4
    .alias qf_mat44_mul.old_i34.newSP #SP


    jmp qf_mat44_mul.P0

    ; выделяем наш кадр стека. Часть 1 из 3
    SP13 := getl #SP
    ; сохранение адресов аргументов в регистрах
    a := rdl qf_mat44_mul.a
    b := rdl qf_mat44_mul.b
    c := rdl qf_mat44_mul.c
    ; выделяем наш кадр стека. Часть 2 из 3
    SP23 := subl @SP13, qf_mat44_mul.stackb
    ; Получение значений регистров 32, 33 и 34
    old_i32 := getl #32
    old_i33 := getl #33
    old_i34 := getl #34
    ; выделяем наш кадр стека. Часть 3 из 3
    setl #SP, @SP23
    ; Запись в индексные регистры указателей на аргументы
    setl #32, @a
    setl #33, @b
    setl #34, @c
    ; Сохранение старых значений индексных регистров в стеке
    wrl  @old_i32, qf_mat44_mul.old_i32
    wrl  @old_i33, qf_mat44_mul.old_i33
    wrl  @old_i34, qf_mat44_mul.old_i34
complete

.local qf_mat44_mul.P0
qf_mat44_mul.P0:
    ; следующий параграф. Часть 1 из 2
    exit := rdl #SP, qf_mat44_mul.stackb
    ; Чтение матрицы "a"
    va01 := rdq #32
    va23 := rdq #32,8
    va45 := rdq #32,16
    va67 := rdq #32,24
    va89 := rdq #32,32
    vaAB := rdq #32,40
    vaCD := rdq #32,48
    vaEF := rdq #32,56
    ; следующий параграф. Часть 2 из 2
    jmp @exit
    ; Вспомогательные значения для упаковки матрицы "a" в строки. Часть 1 из 2
    va54 := pack @va45, @va45
    va76 := pack @va67, @va67
    ; Чтение матрицы "b"
    vb01 := rdq #33
    vb23 := rdq #33,8
    vb45 := rdq #33,16
    vb67 := rdq #33,24
    vb89 := rdq #33,32
    vbAB := rdq #33,40
    vbCD := rdq #33,48
    vbEF := rdq #33,56
    ; Вспомогательные значения для упаковки матрицы "a" в строки. Часть 2 из 2
    vaDC := pack @vaCD, @vaCD
    vaFE := pack @vaEF, @vaEF
    ; Упаковка строк матрицы "a"
    va04 := patch @va45, @va01
    va8C := patch @vaCD, @va89
    va15 := pack @va54, @va01
    va9D := pack @vaDC, @va89
    va26 := patch @va67, @va23
    vaAE := patch @vaEF, @vaAB
    va37 := pack @va76, @va23
    vaBF := pack @vaFE, @vaAB
    ; Скалярные произведения строк матрицы "a" со столбцами матрицы "b".
    ; Сначала находим все промежуточные компоненты потоком независимых инструкций.
    ; Ниже вычисляем все суммы. На процессоре MCp0411100101 так получается
    ; несколько быстрее, чем вычисление каждого скалярного произведения подряд.
    madd @va04, @vb01
    madd @va8C, @vb23
    madd @va15, @vb01
    madd @va9D, @vb23
    madd @va26, @vb01
    madd @vaAE, @vb23
    madd @va37, @vb01
    madd @vaBF, @vb23
    madd @va04, @vb45
    madd @va8C, @vb67
    madd @va15, @vb45
    madd @va9D, @vb67
    madd @va26, @vb45
    madd @vaAE, @vb67
    madd @va37, @vb45
    madd @vaBF, @vb67
    madd @va04, @vb89
    madd @va8C, @vbAB
    madd @va15, @vb89
    madd @va9D, @vbAB
    madd @va26, @vb89
    madd @vaAE, @vbAB
    madd @va37, @vb89
    madd @vaBF, @vbAB
    madd @va04, @vbCD
    madd @va8C, @vbEF
    madd @va15, @vbCD
    madd @va9D, @vbEF
    madd @va26, @vbCD
    madd @vaAE, @vbEF
    madd @va37, @vbCD
    madd @vaBF, @vbEF
    vc0 := addf @32, @31
    vc1 := addf @31, @30
    vc2 := addf @30, @29
    vc3 := addf @29, @28
    vc4 := addf @28, @27
    vc5 := addf @27, @26
    vc6 := addf @26, @25
    vc7 := addf @25, @24
    vc8 := addf @24, @23
    vc9 := addf @23, @22
    vcA := addf @22, @21
    vcB := addf @21, @20
    vcC := addf @20, @19
    vcD := addf @19, @18
    vcE := addf @18, @17
    vcF := addf @17, @16
    ; Запись результатов,
    ;   "wrl" тут работает чуть быстрее, чем упаковка и запись с "wrq".
    wrl @vc0, #34
    wrl @vc1, #34,4
    wrl @vc2, #34,8
    wrl @vc3, #34,12
    wrl @vc4, #34,16
    wrl @vc5, #34,20
    wrl @vc6, #34,24
    wrl @vc7, #34,28
    wrl @vc8, #34,32
    wrl @vc9, #34,36
    wrl @vcA, #34,40
    wrl @vcB, #34,44
    wrl @vcC, #34,48
    wrl @vcD, #34,52
    wrl @vcE, #34,56
    wrl @vcF, #34,60

    ; убираем наш кадр стека. Часть 1 из 3
    SP13 := getl #SP
    ; Восстановление старых значений индексных регистров. Часть 1 из 2
    old_i34 := rdl qf_mat44_mul.old_i34.newSP
    old_i33 := rdl qf_mat44_mul.old_i33.newSP
    old_i32 := rdl qf_mat44_mul.old_i32.newSP
    ; убираем наш кадр стека. Часть 2 из 3
    SP23 := addl @SP13, qf_mat44_mul.stackb
    ; Восстановление старых значений индексных регистров. Часть 2 из 2
    setl #34, @old_i34
    setl #33, @old_i33
    setl #32, @old_i32
    ; убираем наш кадр стека. Часть 3 из 3
    setl #SP, @SP23
complete