За всички които имат мерак да се научат как да поправят форума като се чупи но не им се четат дебели книги - едно образователно клипче:
И цял канал в тубата с едно напористо моме ...
https://www.youtube.com/@lauriewired/videos
Закачам и едно видео ако някой има мерак да види за какво иде реч:
Няма да го гледам, защото страдам от енвидия(да не се бърка с измисления синдром дисвидия) и вярвам в думите на чичкото с коженото яке че програмирането е умряла работа.
За по засуканите работи си трябва човек. Особено ако си по близо до желязото. Също и гоненето на бъгове. Оптимизирането за някакви ограничения.
Това моме прави доста добра комбинация от асемблер, дизасемблиране и високо ниво. Чак се учудвам, че на такова младо момиче в днешно време и е интересно това с което сме започнали през 80-те ... по скоро има някой дъртак/чичак зад кадър който подготвя темите и снима а момата е главната героиня и чете сценария.
Бря, още интересни асембладжийски линкове тия дни:
An introduction to ARM64 assembly on Apple Silicon Macs
Коментарите в хакер нюз също си струват:
Аз лично не съм запознат с арм ис. Жуля х86 и х64.
По приятен е. Класическия АРМ е проектиран не от чип дизайнери ами от програмисти които пишат на асм и като са сложили още от начало правилните основи и до момента си е ОК при все развитие през годините. Отделно имаш условно изпълнение на всяка инструкция, което позволява да се пише много сбит и функционален код без преходи т.е. инвалидиране на кеш, пайплине и т.н. Това се е получило защото оригинално инструкциите са по 32 бита и в тях не е проблем да се заделят 4 бита за условно изпълнение и да останат достатъчно битове за самата инструкция, регистри, че и константи. Затови постнах на това моме клипчето с алгоритъма на Евклид - това е каноничния пример за ефективността на АРМ асемблера.
Опа, момата явно не го познава като хората условното изпълнение и алгоритъма го прави като на х86 асемблер.
Те тук е обяснено както трябва - http://www.cburch.com/books/arm/
C кода:
a = 40;
b = 25;
while (a != b) {
if (a > b) a -= b;
else b -= a;
}
на АРМ32 става те това:
MOV R0, #40 ; R0 is a
MOV R1, #25 ; R1 is b
again CMP R0, R1
SUBGT R0, R0, R1
SUBLT R1, R1, R0
BNE again
halt B halt
На х86 е малко по-дълго, но това е специфичен случай за да блесне арм.
mov eax,40
mov ecx,25
jmp @f
align 8
@@:
mov edx,eax
mov ebx,ecx
sub eax,ecx
cmovc eax,edx
sub ecx,edx
cmovc ecx,ebx
cmp eax,ecx
jne @b
Няма спор, но примера е перфектен да се покаже мощта на условното изпълнение на инструкции. Модерните компилатори го ползва и генерират много сбит branchless код. Като добавиш и произволния shift на единия операнд или константа и става муцка.
cmovc го има като инструкция чак от P6 т.е. Pentium Pro и при това тогава не се е поддържал от всички процесори и повечето програми не са го ползвали за да са съвместими с по старите процесори които се задържаха още едно 5-10 години след това. АРМ32 си ги има условните инструкции още от чертожната маса '82-83 ... това му е хубавото на АРМ - мислен е от програмисти измъчени да пишат на асемблер за 6502 и да броят цикли на всяка инструкция - затова са направили ИСА която да става и за писане на ръка на асемблер и да е подходящ и за компилатор на езици от високо ниво.
Малко по-дълго е без cmov. Ако искаме да върви и на 386:
mov eax,40
mov ecx,25
@@: mov edx,eax
mov ebx,ecx
sub eax,ecx
sbb esi,esi
and esi,ecx
add eax,esi
sub ecx,edx
sbb esi,esi
and esi,edx
add ecx,esi
cmp eax,ecx
jne @b
Но е добре да се разширява кръгозора. За сега няма алтернатива на векторните операции(авх256) при х86/х64. На арм са достъпни само екземпляри със 128 битови вектори. Поне до колкото ми е известно.
Ти ли го написа или го крадна от някъде?
Тези хватки с sbb reg, reg или neg и т.н. са си висш пилотаж и малко хора ги ползват. Докато пишех на АСМ бях цар на такива хватки, сега съм ги позабравил вече.
Най яката ми хватка беше като писах една защита от дебъгване с exe компресор - започваше с iret защото стека го бях нагласил да има адрес на следващата инструкция и флагове за забрана на прекъсвания и т.н. Т.е. още от тук разните дизасемблери вече са спънати, че не знаят къде е следващата инструкция - която беше навряна в операнд на друга. Абе красота. Естествено бях си написал дебъгер който нямаше проблем с тези хватки. Нито като някой му прихване INT1 и INT3 т.е. прекъсванията за дебъгване. Голямо диване бях навремето. Жалко, че не сме се познавали с Жонката онези години.
Другата мизерия дето бях направил беше, че на int 1 бях закачил код който заменя адреса за връщане с един регистър т.е. по този начин инструкциите се разпределят на две места и може да правиш преходи като пипаш в този регистър - май беше BP.
После ми се скапа харда и всички сорсове заминаха 😕
Има. Ама не е в нормалния процесор ами е в GPU-то - CUDA
Тази стратегия със sbb съм я открил по времето когато условните преходи бяха архивраг. Ползвам я много отдавна. Стигнах сам до тази схема да се ползва като маска. Виж, опкодовете не ги помня на изуст. Но гледам да съм в крак с х86/х64 инструкциите.
Така и не можах да схвана логиката как се изпълняват тези куди и опенцли.
ПС: Иначе, последната схема с която се гордея е да ползвам авх256 за имплементиране на суич/кейс. Ако кейсовете са 16 битови имаме 16 тествания на операция.
format PE64 console
entry main
section ".code" code readable executable
macro wswitch wkey {
common
local switchID
wswitch.ID equ switchID
macro case key \{
\local caseID
switchID#.case equ caseID
caseID\#.key equ key
.\#caseID\#.def:
\}
macro default \{
case
\}
macro break \{
jmp .#switchID#.continue
\}
macro end.switch \{
vpbroadcastw ymm0,wkey
mov rcx,.#switchID#.keys
@@:
vpcmpeqw ymm1,ymm0,[rcx]
vpcmpeqw ymm2,ymm0,[rcx + 0x20]
vpackuswb ymm1,ymm2,ymm1
vpermq ymm1,ymm1,11011000b
vpmovmskb edx,ymm1
lzcnt eax,edx
test edx,edx
jnz @f
add rcx,0x40
cmp rcx,.#switchID#.keys.end
jb @b
jmp .#switchID#.default
align 4
@@:
lea rcx,[rcx + rax * 2 - .#switchID#.keys]
cmp rcx,.#switchID#.keys.end - .#switchID#.keys
jae .#switchID#.default
jmp qword[.#switchID#.entry + rcx * 4]
switchID#.mac
jmp .#switchID#.continue
align 64
.#switchID#.keys:
irpv caseid, switchID#.case \\{
if caseid\\#.key eq
.#switchID#.default = .\\#caseid\\#.def
else
dw caseid\\#.key
end if
\\}
if .#switchID#.default eq
.#switchID#.default = .#switchID#.continue
end if
.#switchID#.keys.end:
align 64
.#switchID#.entry:
irpv caseid, switchID#.case \\{
if ~caseid\\#.key eq
dq .\\#caseid\\#.def
end if
\\}
.#switchID#.entry.end:
align 64
purge case
purge default
purge break
purge end.switch
.#switchID#.continue:
\}
macro switchID#.mac
}
main:
wswitch [test0]
{case 1
mov ebx,1
break
case 2
case 3
case 4
case 5
case 6
case 7
case 10
wswitch [test1]
\{case 0
case 1
case 3
default
\}end.switch
break
default
}end.switch
section ".data" data writeable
test0 dw 10
test1 dw 3
section ".import" import data readable
Човек, от 20 години не съм писал на асемблер и бая съм ръждясал вече. Останах си на ниво асмблер за дос. Тези векторни инструкции така са ми на китайски. Вложените макроси и те.
sbb reg,reg и т.н. ги научих с дебъгване. Някои компилатори така правеха abs, sign т.е. - 1,0,1 според знака и т.н. И то после логично може да ги комбинираш и да правиш каквото си искаш без преходи.
А за опкодовете - от толкова гледане на дизасемблер на времето ги бях научил. АСКИ таблицата още я знам.
Най интересното беше защо на 8080 инструкция за CALL и на 8086 инструкцията за прекъсване INT са с един и същи опкод. Защото Интел като са пускали 8086 са решили за по евтино да преизползват периферията за 8080 и там котнролера за прекъсвания 8259 (ако не бъркам) като стане прекъсване поема шината за данни и подава на процесора байтовете на инструкция за call до съответния адрес на прекъсването. А за 8086 се подава INT XX. И за по лесно са ползвали един и също опкод за двата режима - 0xCD