Forums » Программное обеспечение »
Программирование на ассемблере. Пара вопросов о производительности
Added by qewerty over 12 years ago
Вот допустим есть функция перемножения матриц 4 на 4 (указатели на стек и фрейм не меняются, т.к. локальные переменные на стеке не нужны). Матрицы хранятся по столбцам (как в OpenGL).
.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
jmp qf_mat44_mul.P0
; сохранение адресов аргументов в регистрах
rdl qf_mat44_mul.a
setl #32, @1
rdl qf_mat44_mul.b
setl #33, @1
rdl qf_mat44_mul.c
setl #34, @1
complete
.local qf_mat44_mul.P0
qf_mat44_mul.P0:
rdl #SP
jmp @1
; чтение матрицы b
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
; вспомогательные значения, для упаковки матрицы a в строки
va54 := pack @va45, @va45
vaDC := pack @vaCD, @vaCD
va76 := pack @va67, @va67
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
; чтение матрицы 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 со стобцами матрицы b
madd @va04, @vb01
madd @va8C, @vb23
vc_0 := addf @1, @2
madd @va15, @vb01
madd @va9D, @vb23
vc_1 := addf @1, @2
madd @va26, @vb01
madd @vaAE, @vb23
vc_2 := addf @1, @2
madd @va37, @vb01
madd @vaBF, @vb23
vc_3 := addf @1, @2
; --
madd @va04, @vb45
madd @va8C, @vb67
vc_4 := addf @1, @2
madd @va15, @vb45
madd @va9D, @vb67
vc_5 := addf @1, @2
madd @va26, @vb45
madd @vaAE, @vb67
vc_6 := addf @1, @2
madd @va37, @vb45
madd @vaBF, @vb67
vc_7 := addf @1, @2
; --
madd @va04, @vb89
madd @va8C, @vbAB
vc_8 := addf @1, @2
madd @va15, @vb89
madd @va9D, @vbAB
vc_9 := addf @1, @2
madd @va26, @vb89
madd @vaAE, @vbAB
vc_A := addf @1, @2
madd @va37, @vb89
madd @vaBF, @vbAB
vc_B := addf @1, @2
; --
madd @va04, @vbCD
madd @va8C, @vbEF
vc_C := addf @1, @2
madd @va15, @vbCD
madd @va9D, @vbEF
vc_D := addf @1, @2
madd @va26, @vbCD
madd @vaAE, @vbEF
vc_E := addf @1, @2
madd @va37, @vbCD
madd @vaBF, @vbEF
vc_F := addf @1, @2
; упаковка результатов
vc01 := patch @vc_1, @vc_0
vc23 := patch @vc_3, @vc_2
vc45 := patch @vc_5, @vc_4
vc67 := patch @vc_7, @vc_6
vc89 := patch @vc_9, @vc_8
vcAB := patch @vc_B, @vc_A
vcCD := patch @vc_D, @vc_C
vcEF := patch @vc_F, @vc_E
; запись результатов
wrq @vc01, #34
wrq @vc23, #34,8
wrq @vc45, #34,16
wrq @vc67, #34,24
wrq @vc89, #34,32
wrq @vcAB, #34,40
wrq @vcCD, #34,48
wrq @vcEF, #34,56
complete
Есть кой-какие вопросы о том, как сделать её максимально быстрой:
1.) Имеет ли смысл сохранять адреса аргументов в индексных регистрах для дальнейшего использования?
Т.е. сделав:
rdl qf_mat44_mul.a
setl #32, @1
Далее можно загрузить матрицу так:
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
Вместо того, чтобы читать адрес в коммутатор и вычислять адреса вот так:
pa01 := rdl qf_mat44_mul.a
pa23 := addl @pa01, 8
pa45 := addl @pa01, 16
pa67 := addl @pa01, 24
pa89 := addl @pa01, 32
paAB := addl @pa01, 40
paCD := addl @pa01, 48
paEF := addl @pa01, 56
va01 := rdq @pa01
va23 := rdq @pa23
va45 := rdq @pa45
va67 := rdq @pa67
va89 := rdq @pa89
vaAB := rdq @paAB
vaCD := rdq @paCD
vaEF := rdq @paEF
Но последнее позволяет реализовать функцию одним параграфом. Т.е. меня интересует есть ли смысл избегать переключения параграфов, а также скорость выполнения чтения из адреса в регистре со смещением и из вычисленного адреса в коммутаторе. Возможно есть какие-то подводные камни.
2.) Имеет ли смысл минимизировать кол-во операций записи в память?
Например, тут у меня сделано так:
; упаковка результатов
vc01 := patch @vc_1, @vc_0
vc23 := patch @vc_3, @vc_2
vc45 := patch @vc_5, @vc_4
vc67 := patch @vc_7, @vc_6
vc89 := patch @vc_9, @vc_8
vcAB := patch @vc_B, @vc_A
vcCD := patch @vc_D, @vc_C
vcEF := patch @vc_F, @vc_E
; запись результатов
wrq @vc01, #34
wrq @vc23, #34,8
wrq @vc45, #34,16
wrq @vc67, #34,24
wrq @vc89, #34,32
wrq @vcAB, #34,40
wrq @vcCD, #34,48
wrq @vcEF, #34,56
Но можно сделать и так:
wrl @vc_0, #34
wrl @vc_1, #34,4
wrl @vc_2, #34,8
...
wrl @vc_E, #34,56
wrl @vc_F, #34,60
Что делает 16 операций записи в память, а не 8, но все 16 операций независимы, тогда как в первом случае операции записи зависят от упаковки значений.
Replies (82)
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 12 years ago
интересная программа, спасибо! сделаю пару замечаний, если речь идет о перемножении матриц трансформации (наверное, это предположение я сделал из-за упоминания в тексте OpenGL).
Матрицы трансформации перемещают, меняют объект в трехмерном пространстве и имеют следующий вид:
[ R_xx, R_xy, R_xz, T_x ] [ R_yx, R_yy, R_yz, T_y ] [ R_zx, R_zy, R_zz, T_z ] [ 0, 0, 0, 1 ]
Матрица трансформации действует(умножается) на вектор, описывающий точку тела и имеющий координаты
[ X ] [ Y ] [ Z ] [ 1 ]
Вектор трехмерный, хотя и описывается четырьмя координатами, четвертая координата у вектора всегда равна 1. Для каждой матрицы трансформации четвертая строка всегда имеет вид
[ 0, 0, 0, 1]
Числа Tx,Ty,Tz отвечают за перемещение вектора в пространстве, а матрица 3х3, состоящая из букв R_ij - это матрица поворота, растяжения/сжатия.
В этом случае, мне кажется, в программе нужно использовать свойства матрицы: Последняя строка имеет всегда один и тот же вид, и соответственно, ее не нужно умножать - ответ известен.
четвертый член в скалярном произведении строки матрицы a на столбец b тоже известен и равен нулю для первых 3х строк матрицы a и единицы для последней.
Т.е. вообще говоря для задания матриц трансформации, достаточно задания 3х4 значений.
Кроме того у матрицы трансформации есть антисимметрия относительно диагонали. Я не знаю как его использовать в 3х мерном случае, однако, в моей задаче, которая сводится к поворотам на двумерной плоскости, я сначала решил применить матрицу трансформации
[ cos(a) sin(a) 0 ] [ -sin(a) cos(a) 0 ] [ 0 0 1 ]
но затем заменил ее на умножение на комплексное число.
В самом деле:
[ cos(a) -sin(a) 0 ] [ X ] [ X cos(a) - Y sin(a) ] [ sin(a) cos(a) 0 ] x [ Y ] = [ Y cos(a) + X sin(a) ] [ 0 0 1 ] [ 1 ] [ 1 ]
что соответствует умножению комплексных чисел
(A, B) x (C, D) = (AC - BD, AD + BD)
где (A,B) есть (X,Y), a (C,D) eсть (cos(a), sin(a)) и это умножение выполняется встроенной командой процессора mulc.
PS: Наконец сам себе сформулировал (для своей двумерной задачи) что двигаюсь правильно.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 12 years ago
Да, речь идёт именно о матрицах трансформации. Понятно, что нижняя строка у таких матриц всегда (0, 0, 0, 1), однако в данном случае ничего от этого факта выиграть не получится, т.к. кол-во операций для скалярного произведения 3-х и 4-х компонентных векторов одинаково.
madd @va04, @vb01
madd @va8C, @vb23
vc_0 := addf @1, @2
; против
madd @va04, @vb01
addf @va_8, @vb_2
vc_0 := addf @1, @2
А раз так, то пусть уж честно считает. =) Собственно, мои вопросы касаются архитектурно-специфических оптимизаций, а не алгоритмических.
Вектор трехмерный, хотя и описывается четырьмя координатами, четвертая координата у вектора всегда равна 1.
У вектора (как направления) четвёртый компонент всегда 0, это у точки 1, что, при умножении на матрицу, позволяет ей принимать перемещение, а вектору это не нужно.
Повороты в 3-х мерном пространстве гораздо сложнее и могут быть заданы разными методами (углы Эйлера для всех случаев XYZ, XZY, YXZ и т.д, кватернионы).
А это:
x = X cos(a) - Y sin(a)
y = Y cos(a) + X sin(a)
классический способ поворота двумерной точки (http://ru.wikipedia.org/wiki/Поворот). Если у Вас нет сложной иерархии объектов, то его лучше и использовать. Матрицы полезны, чтобы собирать сложные иерархические трансформации в одну сущность.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 12 years ago
Однако, произведение последней строки a [0,0,0,1] на столбцы b можно не считать.
это не нужно:
madd @va37, @vb01
madd @vaBF, @vb23
madd @va37, @vb45
madd @vaBF, @vb67
madd @va37, @vb89
madd @vaBF, @vbAB
madd @va37, @vbCD
madd @vaBF, @vbEF
и даже не инициализировать
va37 := pack @va76, @va23 vaBF := pack @vaFE, @vaAB
хотя, я понимаю, что основной вопрос в оптимизации. но, смотрите, сейчас в программе около 90 строк, а нужно для параграфа 64. Десять строк я уже убрал.
Еще более радикально, представьте что матрица трансформации инициализирована в порядке
[ 0 3 6 9 ] [ 1 4 7 A ] [ 2 5 8 B ]
это сильно сократит программу.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 12 years ago
qewerty, в вашем коде для достижения большего быстродействия можно поставить все команды madd подряд, а затем после них сделать сложение.
Запись программы в один параграф, на мой взгляд, в данном случае не должна принести увеличения скорости, но это лучше проверить с помощью таймера.
С записью в память по 64 или 32 бита тоже можно поэкспериментировать и посмотреть результат по таймеру.
У вас отладочная плата имеется своя или используете удалённый доступ?
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 12 years ago
krufter_multiclet,
в вашем коде для достижения большего быстродействия можно поставить все команды madd подряд, а затем после них сделать сложение
Да, у меня были такие мысли, но с учётом того, что команды исполняются по готовности потребителя, я сомневался. Спасибо!
У вас отладочная плата имеется своя или используете удалённый доступ?
Я пока только на модели запускал. Плату заказал недавно, сегодня из курьерской службы звонили, сказали завтра привезут. На днях смогу потестировать.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 12 years ago
подозрительно быстро на удаленном доступе выполнилось. таймер я не использовал. сделал 65542 умножений матриц 4х4, ответа долго ждать не пришлось, меньше секунды. если есть демонстрация использования таймера, сделаю замер(замеры) с таймером.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 12 years ago
trott, ну если допустить, что каждая команда выполняется за один такт, то эта функция на 4-х клеточном процессоре в идеале должна занять около 25 тактов, что при 80 МГц должно дать 3.2 мильёна умножений в секунду. =)
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 12 years ago
qewerty, это вопрос к производителю, сколько тактов у него занимает каждая операция. кстати, очень хотелось бы узнать, cколько?
я вчера оценивал результат. у меня получилось, в предположении что умножение/сложение занимают 10 тактов. 100х100х10^3х10=10^7 или 10 миллионов. поделим на 4 клетки = 2.5 мильёна
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 12 years ago
Примеры работы с таймером имеются конечно. Могу прикрепить сюда пример.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 12 years ago
да, пожалуйста, если можно пример ... и есть ли данные, сколько тактов занимает madd, pack, rdl, и прочие.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 12 years ago
С помощью таймера периферии:
.include "HDL50001_pcf.inc"
.alias ron1 1
.alias ron3 3
.text
initTimer0:
jmp StartTimer0
getl 0xFFFFFFFF
wrl @1, TIM0_CNT ;период
getl 0x00000001
wrl @1, TIM0_PSC ;значение предделителя
complete
StartTimer0:
jmp Get_start_time
getl 0x00000005
wrl @1, TIM0_CR
complete
Get_start_time:
rdl TIM0_CNTVAL
setl #ron1, @1
jmp code
complete
code:
;code here
jmp Get_finish_time
complete
Get_finish_time:
rdl TIM0_CNTVAL
getl #ron1
subl @1, @2
setl #ron3, @1
jmp initUART0
complete
В текущей версии у таймера периферии необходимо период умножить на два, чтобы получить количество тактов(у системного таймера делать это не нужно).
В потоке на каждом такте выдаётся результат выполнения команды, например madd, rdl, pack... Точное время выполнения команд по отдельности может быть вычислено только с помощью пользовательской потактовой модели(или мы можем проанализировать параграф на своей модели и сказать сколько и что выполняется). Можем только составить чистое время выполнения команд в ALU без учёта выходного диспетчера.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty about 12 years ago
На LDM-MCp0411100101-Q208 Evolution получилось выжать около мильёна умножений в секунду на 80 МГц. Функция вот такая: http://dumpz.org/605010/. Выполняется около 80 тактов, точнее сказать сложно из-за расходов на организацию цикла, кстати, тоже на асме, т.к. на Си слишком большие накладные расходы были, практически равные умножению.
А вот можно посмотреть аналогичный тест на Raspberry Pi: http://thinkingeek.com/2013/05/12/arm-assembler-raspberry-pi-chapter-14/. Лучший результат (асм+неон) смог умножить 2 в 21 степени матриц (2097152) за 1.51 сек. И это на 700 МГц (до 1 ГГц в турбо режиме, но будем брать 700). Для 80 МГц получается 158725 умножений в сек, что в 6 раз меньше чем у Мультиклета на МГц.
Хочу ещё попробовать сделать более жизненные тесты и сравнить с Raspberry Pi, которая у меня тоже имеется.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet about 12 years ago
Спасибо за приятную информацию и проведённые тесты.
А не покажете как цикл организовали? Или весь исходник? Т.е. вызов функции был из Си?
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty about 12 years ago
krufter_multiclet, цикл, как выше писал, на асме тоже. Как-то так (воспроизвожу по памяти, т.к. сейчас не могу посмотреть исходники):
; Си-прототип (возвращает замеренное таймером значение):
; int qf_test_mat44_mul(const float* a, const float* b, float* c);
.global qf_test_mat44_mul
qf_test_mat44_mul
jmp start_timer
getl 0xFFFFFFFF
wrl @1, TIM0_CNT
getl 0x00000001
wrl @1, TIM0_PSC
complete
.local start_timer
start_timer
jmp get_start_time
getl 0x00000005
wrl @1, TIM0_CR
complete
.local get_start_time
get_start_time
rdl TIM0_CNTVAL
setl #1, @1
jmp qf_loop_mat44_mul
complete
.local check_timer_and_quit
check_timer_and_quit:
rdl TIM0_CNTVAL
getl #1
subl @1, @2
rdl #SP
jmp @1
wrl @3, #SP,4
complete
.global qf_loop_mat44_mul
qf_test_mat44_mul:
jmp qf_loop_mat44_mul.CHECK
getl 1000000
setl #0, @1
complete
.local qf_loop_mat44_mul.CHECK
qf_test_mat44_mul:
c := getl #0
je @c, check_timer_and_quit ; как ваше Get_finish_time, вычисляет разницу таймера, но выходит по #SP
jne @c, qf_mat44_mul
subl @c, 1
setl #0, @1
complete
И в самой функции qf_mat44_mul, в параграфе P0, возврат делается на в qf_test_mat44_mul.CHECK, а не по #SP:
.global qf_mat44_mul
qf_mat44_mul:
.alias qf_mat44_mul.a #SP,8
.alias qf_mat44_mul.b #SP,12
.alias qf_mat44_mul.c #SP,16
jmp qf_mat44_mul.P0
; Сохранение адресов аргументов в регистрах
rdl qf_mat44_mul.a
rdl qf_mat44_mul.b
rdl qf_mat44_mul.c
setl #32, @3
setl #33, @3
setl #34, @3
complete
.local qf_mat44_mul.P0
qf_mat44_mul.P0:
; вот так:
jmp qf_loop_mat44_mul.CHECK ; лучше было бы в регистре сохранить адрес возврата
; вместо:
; rdl #SP
; jmp @1
...
Всё это не по-сишному конечно, но в частных случаях вполне возможно, к тому же оптимизирующий компилятор также может не оформлять стековый кадр если функции он не нужен.
А qf_test_mat44_mul вызывалась уже из Си.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty about 12 years ago
Чего-то я напутал в первый раз. Пост выше поправил. Вечером до дома доберусь, выложу исходники.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott about 12 years ago
qewerty, я протестировал свой микроконтроллер stm32f4-discovery(87мГц) на умножение матриц 4х4. результат: 5 секунд для 1 мильона умножений. код на C выложен на https://github.com/trot-t/stm32-matrix . плавающая точка включена.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by Zveruga about 12 years ago
Интригующие тесты. Во внутричиповой памяти Мультиклета памяти больше чем у Спектрума. Если можно будет подключить монитор, то возможно написать демку с 3D сценой, на подобии из тех, что были на спектруме.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet about 12 years ago
Да на VGA монитор выводили красный цвет, дальше времени не было.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty about 12 years ago
Прошлый тест совсем уж наивный был, выкладывать не стал. Кое-что доработал, исходники можно взять тут: https://bitbucket.org/qewerty/qf/overview. Функция умножения теперь сохраняет и восстанавливает значения регистров и судя по всему должна работать корректно. Да и цикл теперь более честный, с пробросом аргументов через стек.
Кому интересно может собрать, проверить и убедиться:
hg clone https://bitbucket.org/qewerty/qf cd qf make mc-ploader qf_mat44_mul_TEST
Будет мигать диодами с интервалом в миллион матричных умножений. =)
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by Sergei.S about 12 years ago
Перемножение матриц - задача с явным параллелизмом, так что это не супер аргумент в пользу мультклета. Потому как гипотетический микроконтроллер с 4 обычными ядрами так же покажет пропорциональный рост производительности на этой задаче. И тут уже надо мерить не число умножений/мегагерц, а число умножений/площадь кристалла или число умножений/ватт. и так далее. Т.е. приводить задачу к объему вентелей, требуемых для ее решения.
Так же очень интересно, а что будет с перемножением матриц хотябы 100 х 100 например, что бы понять как работает подсистема памяти.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty about 12 years ago
Sergei.S,
Перемножение матриц - задача с явным параллелизмом
Да неужели? В том то и суть. Важно КАК оно распараллеливается. А задачи где параллелизма нет, нельзя распараллелить никак.
гипотетический микроконтроллер с 4 обычными ядрами так же покажет пропорциональный рост производительности на этой задаче.
Да, только на потоки код должен разбивать программист, а это гораздо сложней чем написать рядом несколько независимых инструкций. А если матрицы нужно умножать строго последовательно, в виду их зависимости, то никакого прироста не будет (в графе сцены, например, они частично зависимы). А если одно умножение на несколько потоков раздробить, то может быть даже ещё медленней из-за расходов на синхронизацию.
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by mouse about 12 years ago
Хочу добавить свои пять копеек. Пишу реализацию delay-функции на асме. Пробы пера. Первый вариант имеет оверхед с лишними вызовами двух команд в пределах одного параграфа:
.include "HDL50001_pcf.inc"
.alias SP 39
.global MCp_delay
.text
MCp_delay:
rdl #SP, 8
setl #32, @1
jmp loop
complete
loop:
getl #32
je @1, return
jne @2, loop
subl @3, 1
setl #32, @1
complete
return:
rdl #SP
jmp @1
completeВторой вариант с разбивкой на отдельные параграфы и с оверхедом на доп. вызов getl #32:.include "HDL50001_pcf.inc"
.alias SP 39
.global MCp_delay
.text
MCp_delay:
rdl #SP, 8
setl #32, @1
jmp loop
complete
loop:
getl #32
je @1, return
jne @2, decrement
complete
decrement:
getl #32
subl @1, 1
setl #32, @1
jmp loop
complete
return:
rdl #SP
jmp @1
completeПервый вариант получается байтов на 14 короче. Вариант с авто-инкрементом индексных регистров не подходит. И сходу он у меня не полетел:.include "HDL50001_pcf.inc"
.alias SP 39
.global MCp_delay
.text
MCp_delay:
rdl #SP, 8
setl #32, @1
setq #33, 0
setl #MODR, 2 ; пробовал маску вплоть до 15
jmp loop
complete
loop:
jmp loop
getl #32
xor @1, #33 ; в модели регистр 0x21 принимал значение 0xf_0000_000f и не менялся от вызова к вызову параграфа
; какой есть альтернативный способ сравнения двух значений?
je @1, return
complete
return:
rdl #SP
jmp @1
completeЧто лучше — оверхед с выполнением лишних инструкций или лишние параграфы? И что предпочтительнее в случае: jmp loop
[…]
je @1, return
вызывать jmp loop или после команды je @1,return вызывать jne @2, loop?RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott about 12 years ago
mouse, в вашем примере с автоинкрементом установка значения в #MODR разрешает инкрементировать индексный регистр #33, а маска, которая записывается в сам регистр #33, запрещает автоинремент.
MCp_delay:
rdl #SP, 8
setl #32, @1
setq #33, 0 ; <= здесь выставили базу = 0, маску = 0, индекс = 0. если маска во всех разрядах равна 0, то инкремента не будет,
; из-за того, что он запрещен маской, 0000000000000000 - маска запретила изменять все разряды у индекса.
; 11111111111111 - такая маска разрешит менять все разряды индекса в регистре
setl #MODR, 2 ; пробовал маску вплоть до 15 <== здесь говорим, что будем автоинкрементировать единственный индексный регистр #32, это не маска, а резрешение инкремента
jmp loop
complete
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott about 12 years ago
mouse, такая программа в модели 3 раза делает автоинкремент регистра #32
.include "HDL50001_pcf.inc"
.text
.alias SP 39
.global MCp_delay
.text
MCp_delay:
; rdl #SP, 8
getl 2 ; сделаем 3 шага автоинкремента для проверки в модели
mask := getl 65535 ; готовим маску 1111111111111111
ind_aux := subl @mask, @2
ind := slll @ind_aux, 16 ; индекс
addl @ind, @mask ; заполняем индекс и маску
patch @1, 0 ; склеиваем маску индекс и базу в 64 разряда
setq #32, @1 ; записываем значение в индексный регистр #32
setl #MODR, 1 ; разрешаем модификацию первого индексного регистра, он имеет #32
jmp loop
complete
loop:
exa #32
je @1, return ; проверка на 0
jne @2, loop
complete
return:
getl 0x1000 ; остановимся, компилятор не любит пустых параграфов
; rdl #SP
; jmp @1
complete
RE: Программирование на ассемблере. Пара вопросов о производительности - Added by mouse about 12 years ago
trott wrote:
mouse, в вашем примере с автоинкрементом установка значения в #MODR разрешает инкрементировать индексный регистр #33, а маска, которая записывается в сам регистр #33, запрещает автоинремент.
[...]
setl #MODR, 2 ; пробовал маску вплоть до 15 <== здесь говорим, что будем автоинкрементировать единственный индексный регистр #32, это не маска, а резрешение инкремента
Если обратиться к документации , то выставленный бит разрешает инкремент. Что значит "маска / не маска"? Здесь я вас не понимаю. Про регистр #32, думаю, опечатка, т.к. 2-й бит отвечает за регистр #33.
Давайте вернёмся к моему начальному сообщению:
mouse, […]Вариант с авто-инкрементом индексных регистров не подходит[…]
Мы имеем 16-ти битный автоинкрементирующийся счётик . Как мне сделать точную паузу более 5 мс при частоте процессора 100MHz? Есть ещё один тонкий момент с автоинкрементом. В момент вызова MCp_delay из Си мне придётся сохранять значение как оригинального MODR, так и индексного регистра, дабы после завершения функции вернуть всё на место (я же не знаю, кем этот регистр может быть ещё использован). Т.е. функция откушает 8 (а то и все 12) байт на ровном месте + ненужные операции над памятью.