Комментарии 100
Прекрасный язык, с одним чудовищным недостатком: непереносим.
Ну конечно. Хотя, не знаю что сказать, но недостатком ассемблера я это назвать не могу. Это как говорить о недостатках кентавров — нет рогов, и летать не умеет. Как бы и правда, но всё же не того типа лошадь. 8-)
С другой стороны — намного проще работа с памятью. Вот нигде, где была работа с памятью, не было проще, чем в асьме. Возможно потому, что за всем надо было следить вручную, и у тебя даже нет идеи переменных. В итоге - у тебя есть что надо, и память не течёт направо и налево.
На счёт недостатка, не хотел яду добавить, я сам люблю ассемблер, но отошёл от него, потому что программы которые я писал на нём, можно использовать только на целевой архитектуре, а сишный код я часто переносил туда-сюда, и это решающий фактор.
В итоге - у тебя есть что надо, и память не течёт направо и налево.
Это Вы напрямую с DOS'овскими MCB не пытались работать, а мои поделки (светодиодами в LPT-порту помигать, попищать писи-скрипером по таймеру, загрузить в матричный принтер русские шрифты, если на LPT-порту статусы похожи на переинициализацию принтера) были более изобретательными при работе с оперативной памятью и файловыми буферами, и чтобы у TSR-сторожа, следящего за принтером, не "текла память" (я тогда даже термина такого не знал, но с явлением столкнулся), пришлось довольно много отладочной информации собирать.
Но да, структура кода была более понятная (минимум абстракций), и отлавливать свои ошибки было сильно проще, чем ловить неизвестные ошибки в чужих библиотеках.
Если вам нужен переносимый ассемблер, возьмите MSIL или JVM байткод.
Только смысла в этом нет. На ассемблере пишут ради скорости и компактности, а когда у тебя запускается виртуальная машина на сотни мегабайт чтобы исполнить твое маленькое приложение - это сводит все старания на нет.
На ассемблере по сравнению с Си некоторые вещи можно сделать радикально по-другому, и иногда в разы лучше. Предоставляет ли какую-нибудь потенциальную выгоду использование JVM вместо Java? Кроме более громоздкого синтаксиса.
Спасибо, не знал. Но полезность этого примера довольно ограничена, если все примеры которые удастся найти, такого рода -- это подтверждает мой тезис
На ассемблере можно сделать процедуру с несколькими входами, нетривиальные операции со стеком, прыжки вместо setjmp/longjump.
Как на Си с помощью инстринсиков сделать продедуру, которая сигнализирует об ошибке в флаге переноса? Инстринсики для add with carry есть но они покрывают только ограниченные случаи использования.
Один из вариантов MSIL — возможность написать функцию по ансейф трансмутации из одного типа в другой. Что-то типа reinterpret cast
— сама функция же просто делает ret
собственного единственного аргумента. Бывает полезно при обработке низкоуровневых данных, когда нужно представить данные в немного другом виде (например преобразование byte[]
-> long[]
. В последних версиях C# оно появилось в стд в виде Unsafe.As<T>
но во-первых раньше его не было, во-вторых если таргетится не последний неткор то этой функции в стандартной поставке нет и нужно продолжать использовать свою.
В MathCad 14, например, встроен прекрасный ассемблер, на котором удобно отлаживать программные вложенные циклы. Если битовые значения брать из массива BMP - картинки, то результат работы высвечивается подробно и красиво.
Ну, это всё же, другой ассемблер. Он хоть и ассемблер, но для виртуального процессора.
Это как тот пацан, который написал эмулятор процессора на экселе. Процессор Шрёдингера. Он одновременно и низкоуровневый и нет.
Лично я сразу вспоминаю ассемблер Z80... Некоторые машинные коды даже сейчас помню... )
С сожалением скажу, что до спектрума я так и не добрался. 8-(
Нет, на Спектруме только играли, а на ассемблере писал на КУВТ Yamaha MSX2.
Защита с переписыванием LDIR инструкций во время исполнения, xor с регистром R, раскатывание циклов, недокументированные инструкции процессора, синхронизация с развёрткой для мультиколора, жонглирование теневым ПЗУ для прямого доступа к контроллеру FDD, семплирование через музсопроцессор в режиме COVOXa, божественные MONS, GENS и последователи их TASM и STS…
Ассемблер мини компьютера Д3-28 считаю был лучшим в своём классе по удобству и лёгкости освоения - всё множество команд было расписано в таблице на формате А3 и никаких дополнительных документов для работы не требовалось.
Однажды была сделана попытка скомпоновать команды ассемблера из софта AVR Studio аналогичным образом - этот вариант компоновки команд и сегодня был бы востребован...
Причём тип используемого языка не имеет значения - главное это "согласование" особенности зрительного восприятия образов со структурой команд, которые мозг, мой во всяком случае, тоже запоминает в виде набора ассоциативно связанных между собой картинок.
Вот это ассемблер:
MOV R0,R1
010001 в восьмеричном. По памяти пишу )
А это что за ассемблер?
PDP-11, ДВК-2, Электроника-60, СМ-4, Электроника БК-0010, и некоторые другие. Это очень популярная тема и удобная система команд.
Ого! Да, я всё-таки пропустил это. Начинал писать в 1997 году, но даже тогда у меня на руках был неимоверно понтовый компьютер. Букашек видел только в школе, но и там нам не давали ничего, кроме черепашки.
В юности писал и редактировал простые програмки для БК прямо в машинных кодах, например записывал сигнал с ИК пульта ДУ телевизора, статическая оперативка работала быстрее и тайминги предсказуемы в отличии от динамической.
Система команд https://ru.wikipedia.org/wiki/PDP-11#Особенности_PDP-11
О да.
MOV (PC)+, R1
#12345
Прикольная штука. Я ассемблером баловался как бы уже не лет десять назад в последний раз и наверное чего забыл. Почему printf, а не через вин апи?
Хотелось консольного, лампового. Хотя WinAPI втянуть так же просто, но хотелось сделать что-то с очень низким потреблением памяти. А даже прогрузка самых базовых библиотек занимает мегабайты, как вы можете видеть.
Прямо сейчас, конечно, точно утверждать не могу, но ЕМНИП для консоли нужно user32 и/или kernel32. В общем надо пробовать.
Так, я это буду считать подначкой! :-) могу даже попробовать спортировать на фасм.
user32 нужен только для GUI
Возможно, puts будет ещё меньше. Но там форматирования нет, надо ручками.
Переписать её на ассемблере несложно, но делать этого я, конечно, не буду.
.global main
.data
hello:
.string "Hello world\0"
.text
main:
push $0
push $0
lea hello, %eax
push %eax
push $0
call MessageBoxA
ret
собирается tcc -m32 test.s -luser32 в 1.5кБ exe и хочет 1МБ памяти.
Запахло перфокартами и перфолентами. Всплакнул.
Ах, к сожалению, перфокарты мне приходилось использовать как закладки и как бумагу для заметок. В живую я их не делал.
Но если вам так уж всплакнулось, то рекомендую послушать альбом замечательного композитора Йохана Йохансонна (который писал музыку к Прибытию). Альбом называется "IBM 1401 инструкция пользователя"
линкер должен называться компоновщиком, но мне это непривычно
Также вам непривычно "скачать", да? "Слить" — сейчас это про утечки.
Он всё ещё живой и люди им пользуются. Существует масса иснтрументов разработки для всех ОС. Вот, например, ассемблер для новых маковских чипов М1. А здесь можно слить более 5000 страниц документации по процессорам Intel. Ну а если у вас завалялась где-то Raspberry Pi (а у кого она не завалялась?), то вам сюда.
Похоже ссылки не работают.
Когда моя мини-программа которая много читала из файлов на диске, что-то с ними делала и опять записывала на диск (ну вот такая "сложная" программа для нескольких файлов в 10к) заработала, я был готов к ее оптимизации. Но время исполнения в 14 мс на 5600X убило идею в зародыше. Конечно я понимаю, что криворуких кодеров, которые пишут страшный код для чтения JSON в 10 Мб хватает, но для современных процессоров x86... Мне страшно это говорить в слух, но в большинстве случаев оптимизация не нужна, тем более на ассемблере.
А так, да - весело, проностальгировал, вспомнил программирование TSR-программ под DOS.
Согласен. Но я бы вот как сформулировал мыслю:
Современное ПО сильно требует оптимизации. Но не всё, что высокоуровнево - это ад на земле. На самом деле в мире есть приличные высокоуровневые программы. Но, в то же время, существует масса когда, который написан криворуко до ужаса.
Понимание ассемблера дало бы народу понимание того, как не писать сильно неоптимизированный код
Понимание ассемблера дало бы народу понимание того, как не писать сильно неоптимизированный код
Хочу уточнить, что скорее понимание как и главное почему так устроена архитектура используемой системы дала бы понимание. А ассемблер — это просто инструмент, которым иногда можно и нужно пользоваться.
Читал с ностальгией, но с надеждой никогда к ассемблеру не возвращаться. Хотя, может, в современных IDE все проще и надежнее… нет, никогда, никогда больше! хватит того, что в голове зачем-то хранится всякий jmp FFFF:0000 и int 19
А регистры нынче большие, туда всё поле влезет. Можно вообще без памяти обойтись. :-)
Согласен, хотелось бы меньше. Но, к сожалению, тут надо будет выяснять что да как делать. Весь этот оверхед это просто использование стандартной библиотеки винды. Если посмотреть на символы в деббагере, то всё то, что идёт комплектом к ExitProcess - это приличное количество функций.
Все было плюс-минус хорошо до вот этого момента,%macro memtoreg 4
xor r10, r10
mov r10b, byte [stor + %4]
xor r11, r11
mov r11b, byte [stor + %3]
xor r12, r12
mov r12b, byte [stor + %2]
xor r13, r13
mov r13b, byte [stor + %1]
%endmacro
Не надо так, если у вас все игровое поле влезает в пару широких машинных регистров — читайте\пишите его туда\оттуда целиком за один выравненный доступ в память, а потом разберите побайтно сдвигами, если вам прямо так неудобно с ними оперировать.
Понятно, что все это буйство оптимизации разобьется все равно об последующее копирование 16 параметров в стек для printf, но без него статья получается странная, потому что непонятно, зачем оно все, если на С можно написать ровно то же самое, только проще в разы.
Надо будет написать статью про современное применение ассемблера там, где он реально нужен — в прошивке до инициализации оперативной памяти или cache-as-ram, в ядре ОС для перехода во всякие хитрые режимы исполнения и выхода из них, и т.п.
Байто**ля... Писец. New achievement unlocked. Согласен. Лучше слова и не придумаешь. Но тут я уже смотрел с точки зрения оптимизации, сколько это будет стоить в циклах. Я так предположил что память-то закешируется и тянуть её из L1 в регистры будет несложно, в таком случае двигание регистров будет дополнительной нагрузкой, которая себя просто не окупит.
Но, с другой стороны, вы правы. Если мы говорим об уменьшении размера оперативки, то это было бы да - просто считать трёхбитовыми значениями степень, в которую надо возводить. В таком случае всё можно запихнуть в один 64х битный регистр. Но опять-же, у нас на данный момёнт жёсткие потери из-за подгрузки библиотек.
А по поводу статьи - это вы правы. Посмотрим 8-)
Но тут я уже смотрел с точки зрения оптимизации, сколько это будет стоить в циклах.
Наверное, если сильно заморочиться можно на одних регистрах написать. Вон, сортировку внутри широченного xmm/ymm делают же, чем тут хуже? :)
2048, v2 извращённая. Убираем поддержку стандартной библиотеки винды, выводим поле опкодами BSOD или как-нить так. Пилим всё аккуратно и сохраняем всё поле в одном регистре. Радуемся футпринту в 2кб.
Кстати... Я так подумал, а ведь если я запущю всё это на FreeDOS у меня будет возможность добираться до современных регистров и, в то же время, использовать int 21h, что жутко сэкономит память.
Я так подумал, а ведь если я запущю всё это на FreeDOS у меня будет возможность добираться до современных регистров и, в то же время, использовать int 21h, что жутко сэкономит память.
Формально вам понадобится какой-нибудь 64-битный dos-extender, если не хотите сами писать кучу обвязки для перехода в 64-битный режим и вызова оттуда какого-нибудь int 21h. Можно обойтись и 32-битным расширителем, выбор коих намного больше. Но зачем, если сэкономить память можно и в современных windows/linux.
2048, v2 извращённая.
Уж если речь зашла про dos, то можно на голом mmu написать, он на x86 вполне тьюринг-полный. Такие извращения только под dos и можно пускать, ну или прямо из первичного загрузчика.
В DOS можно совершенно спокойно использовать 32-х битные регистры без всяких расширителей. Расширитель нужен для адресации памяти за пределами 1Мб.
Короче, мерять надо все равно, и написать кучу разных вариантов, но обычно те, кто это все умеет и практикует — они уже на работе сыты этим всем по горло. Студентам зато можно дать в качестве задачи со звездочкой.
Снимаю шляпу!
Переписывать это можно бесконечно. Если посмотрите на исходники - всё это писалось быстро и без уж очень большой оптимизации.
они уже на работе сыты этим всем по горло. Студентам зато можно дать в качестве задачи со звездочкой.
Насчет сыты по горло — это правда. Это же объясняет, почему так мало статей, а тем более не в корпоративных блогах.
А насчет задач со зведочкой, я даю такую: сколько доступов в память в худшем случае может быть сделано при выполнении команды nop, включая ее выборку. И сколько (хотя бы примерно) их вообще может быть максимально для одной команды x86.
Если подразумевать, что ничего такого у нас нет, и задача без особого подвоха, то получится примерно «пошли декодировать, нарвались на начало длинного нопа с префиксами, который попал посередине на границу физических страниц, при этом в TLB ничего нет нужного, и потому надо идти в память декодировать, и еще оказалось, эта вторая страница не исполняемая, ошибка декодирования, кровь, кишки, распидорасило, охулиард записей в память на логи и крэш-дамп».

Сразу скажу, что число чтений памяти больше одного, число записей (!) тоже больше одного. А есть еще не nop, а более хитрые команды, там все еще хитрее.
С несколькими записями тоже проблем нет — кладем инструкцию последним байтом страницы, заполняем кэши так, чтобы ничего ни с этой, ни со следующей страницы в них не было, выполняем на эту нашу инструкцию nop неожиданный дальний переход — pipeline flush, cache flush, протокол когерентности заставил сделать write-back как минимум двух вымытых из кэшей линий (которые теперь заменили одна с конца этой страницы, ее запросил декодер, а другая — с начала следующей, эту запросил префетчер).
Просто хотел подчеркнуть, что иногда последствия самых вроде безобидных команд могут быть не сразу очевидными. Обычные программисты с такими фокусами вряд ли столкнутся, разве что при очень тщательной оптимизации, а вот системщики — более вероятно. Кроме того, это просто полезно знать, чтобы понимать как устроена не самая простая архитектура x86.
p.s. позволю себе еще немного коварства: а если кэша нет (отключен и по коду и по данным, память не кешируемая и т.д.) можно ли получить несколько записей в память? а на каком-нибудь i386sx, где все еще проще можно?
иногда последствия самых вроде безобидных команд могут быть не сразу очевиднымиЗачастую, эти последствия бывают неочевидными даже для самих разработчиков процессора, целая плеяда микроархитектурных атак вроде Spectre, Meltdown, Foreshadow, RIDL, Fallout и остальных — отличное тому подтверждение. Знать и понимать — надо, конечно, но надо тоже осознавать пределы своего понимания, а то ведь я до сих пор вижу в индустрии людей, которые в своем ассемблерном коде старательно избегают pipeline hazard'ы, которых их нынешний процессор давно уже не боится, а на Pentium Pro их код запускаться точно уже не будет…
Про ситуацию с «кэша нет» думать уже не хочу, прошу пардону, потому что и лень, и «так верстают только мудаки (тм)». Можно пойти почитать мануал на тему «кто там в память сбрасывает свой architectural state, и при каких условиях», вспомнить про сегментную адресацию и thread local storage, и прочие разные штуки, но я уже в пижаме и мне уже слишком влом.
p.s. позволю себе еще немного коварства: а если кэша нет (отключен и по коду и по данным, память не кешируемая и т.д.) можно ли получить несколько записей в память? а на каком-нибудь i386sx, где все еще проще можно?В таблицах для страничной адресации есть флаг «accessed», на 386 при любой трансляции линейного адреса просматриваются 2 таблицы, в каждой нужно бит установить.
из кэша наше игровое поле может вымыть в любой момент
На некоторых архитектурах есть cache locking и предзагрузка данных/кода в кэш.
Более того, благодаря SSE, SSSE и AVX у нас на руках ещё есть 15 регистров по 128 или 256 бит. Они названы XMM0-XMM15 для 128 бит и YMM0-YMM15 для 256 бит.
16 же, от 0 до 15. В 32-битном режиме только 8. А еще 8 FPU/MMX тоже есть и в них тоже можно хранить что угодно при желании. Ну а уж если AVX512 вспомнить, то там аж 32 ZMM и еще маски…
p.s. младшие части XMM/YMM/ZMM — это одни и те же регистры, т.е. биты 0-127 совпадают в xmm0 и в ymm0 и в zmm0.
Пожалуйста, не надо превращать технический блог в фидопойку.
Это вы ещё не заметили, что в коде вместо
.bss
автор опечатался и поставил .bbs
:D«Я понял, в чём ваша беда. Вы слишком серьёзны. Умное лицо — ещё не признак ума, господа. Все глупости на Земле делаются именно с этим выражением. Улыбайтесь, господа, улыбайтесь!»
На самом деле стиль автора — это отсылка ко времени «Хакера», тем кто его не читал, не понять такой формат общения, хотя именно на нём выросли современные специалисты IT в СНГ. И, да, я согласен на счёт алкоголя, но данный стиль — это просто добрые воспоминания.
Ох, и не знал, что я подонок и разговариваю на сленге) И "погнали", и "поехали" я использую в своем разговоре...
Хе-хе, начали с подсчета полубайт, а в итоге все равно получилось 2.5Мб :) Если бы написать на чистом Си, думаю было бы не сильно больше, но зато 100% переносимо, хоть на CP/M запускай. Автору респект, но нет, обратно на ассемблер уже не тянет :)
ИМХО не самое полезное применение ассемблера. Вот если эту штуку написать для Arduino или любого другого микроконтроллера с четырьмя кнопками и экраном - будет нагляднее. Под Windows все равно все упирается в системные вызовы, которые нам не подвластны, а на голом железе все в наших руках.
Ассемблер не пугает. Главное, чтобы мануалы по инструкциям были. В свое время я учил ассемблер для AVR микроконтроллеров. Потом перешёл на Си и С++. И вот однажды мне пришлось на STM32F407 организовать цепочку цифровых обработок одного сигнала и выжать максимум производительности с ядра ARM Cortex-M4. А тут только ассемблер и нужен. Мануал по ассемблеру вышеупомянутого ядра оказался довольно увлекательным. Там очень много интересных инструкций на все случаи жизни. Ну и задачу я свою решил. Не стал писать вставки и ограничился вызовами intrinsic функций библиотеки CMSIS.
cygwin еще не советовали для ленивых?
Там есть всё, что требуется для таких развлечений. И yasm и yasm и MinGW и линкеры
А на что заменить mov ax,13h; int 10h;?
4. это вы очень оптимистично так. Для случаев отличных от голого си, сложность вырастает существенно, особенно для управляемых языков.
Так и с процессором лучше разговаривать на его родном языке — языке Ассемблера, без дополнительного «переводчика» в виде Си компилятора.
Был и остаюсь фанатом FASM: х86, AVR, ARM.
Есть masm64 от того же hutch (автор замечательного пакета masm32). Да,ехе нужно брать со студии,но тут есть макросы,инклуды , примеры. Также советую посмотреть uasm - masm синтаксис но гораздо больше возможностей ( создание файлов пол разные ос,даже подобие ооп)
Трясём стариной — или как вспомнить Ассемблер, если ты его учил 20 лет назад