Как стать автором
Обновить

256 байт веселья, или как развлечь себя Ассемблером когда скучно

Уровень сложностиСредний
Время на прочтение15 мин
Количество просмотров28K
Всего голосов 151: ↑151 и ↓0+183
Комментарии78

Комментарии 78

Хабр - торт, понял половину в свете z80. Все так пишут что это так просто (не в упрек ниразу), но надо знать архитектуру, а тут еще прерывания dos. Было интересно, два раза перечитал, частично яснее, но не до конца. Спасибо.

ну я почти все необходимые прерывания здесь и привел. Нам же тут с файловой системой, или дисководом не работать :)
Еще с десяток портов ввода-вывода... Но я например на память их-то тоже не помню, при желании нагуглить можно. Или посмотреть чужие исходники (я свои почти всегда публикую вместе с COM-файлом).

PS: для тех, кто не хочет заморачиваться с особенностями архитектуры придумали "фантазийные консоли", там на Lua код, и никаких тебе прерываний и портов. Многим нравится.

Архитектуру - да.
А прерывания можно и из справочника тянуть на начальном этапе. Зубков или Юров в помощь. Я особенно Зубкова люблю.
Принципы оптимизации из статьи станут понятны после прочтения учебника.

А вот ограничение скорости, опираясь на вертикальную развёртку это уже, конечно, специфика жанра. Такое только на профильных ресурсах искать. И с опытом.

лично я - нет. И думаю, что портировать это в DOS будет сложнее, чем написать с нуля... Но вообще, есть энтузиасты... Даже Javascript + Canvas в DOS реализовали https://github.com/SuperIlu/DOjS (норкоманы чёртовы!)

P.S.: но так-то вебсервер для DOS существует http://rubbermallet.org/software.html (не в 200 строк на асм, но...)

  • MS-DOS имеет крайне ограниченную сетевую функциональность и там нет API для сокетов, который нужен, чтобы написать web server

  • 200 строчек на ассемблере под x64 по итогам компиляции легко могут перевалить по размеру за килобайт

Блин, ну вот оно же всего две строчки, коротеньких, но сколько в них детства и молодости.... Аж мурашки побежали...

mov ax, 13h ;

int 10h

Это как будто запах бабушкиных пирогов учуял внезапно, через 30 лет, просто гуляя по улице..

Мануалов (в бумажном виде) - видеть не доводилось. Только txt-файлы, в FIDO добытые, с BSD (могу уже ошибаться, четверть века назад дело было).

А вот как сам трейсил (в отладчике от Borland TurboPascal 7.0) - это уже не забывается

(хоть и было в прошлом тысячелетии).

А у меня доступа к FIDO не было, и жил я в некоторой кавказской республике, где все было очень грустно с компьютеризацией. Так что приходилось ездить за 400 км на ближайший приличный радиорынок, там за деньги записывали на дискеты всякий стаф, а позже продавали и CD с хламом выкачанным с BBS-ок и фидошных эхо. Купиш такой BBS#04 CD, и вот тебе и фидо на пару месяцев, там и сорцы всякие на Pascal'e, и коллекция MOD, S3M (и красивеньких плееров к ним), если повезёт - пару-тройку демок (вроде Panic, Second Reality, Crystal Dream...). Так и жили...

У Вас был стимул и много времени чтобы заниматься чем-то интересным. У тех, у кого был Fidonet, целыми днями отвисали в эхах бездарно тратя своё время на флеймы. Ну почти как сейчас на Хабре. ;)

Ну почему, были кодерские, сценерские эхи, где люди делились знаниями, сорцами, релизили свои поделки на BBS, делали чарты, конкурсы... Ну да, не без флеймов (мне попадались на тех CD-дисках и логи эх и irc-чатов, электронные журналы вроде Harm, Hacker. Но с временным лагом в 1-2 года). В то время как я сидел и кодил в стол, собирая инфу, доки, сорцы по крупицам. Лучше все же иметь фидонет/интернет, чем не иметь :)

Те-же имевшие доступ к FIDOnet, не так редко, находили файлы с документацией

(по тому-же низкоуровневому программированию железа),

и делились (на безвозмездной основе) с теми, у кого небыло доступа

(например, небыло проводного телефона).

Речь веду о взаимодействии одноклассников (или знакомых) в средней школе.

Хотя и с аппаратной конфигурацией ПК - тоже вопрос был решаем. Можно было, например, докупить RAM, HDD, модем, AnyOthet.

Что и делалось.

Насчёт времени: изначально - его у всех одинаково

(много или мало - другой вопрос, философский).

All within my hands 😀

Была такая штука под названием Floppynet. Это все тот же Fidonet, только вместо модема - обычная дискета, на которую клались и переносились упакованные бандлы с почтой. Как правило от ноды к пойнту и обратно. Такой "безмодемный" пойнт заглядывал пару раз в неделю на работу к ноде с парой дискет, приносил свои бандлы для отправки и забирал причитающиеся ему бандлы. Все это прекрасно обрабатывалось фидошным софтом и никто не замечал, что у человека нет модема. :-) Аналогичная схема работала через локальные сети, где для каждого пойнта расшаривался каталог с inbound и outbound почтой.

У нас в городе была целая студенческая сеть HomeNet на флопах - каждое утро студенты менялись дискетами, приходили домой (или в общагу) и переписывали файлы с почтой на комп и обратно, и так каждый день. Жизнь студента была интересной, определенно был стимул появиться в ВУЗе хотя бы для того, чтобы почту забрать/передать. Сейчас появляются только перед зачетом/экзаменом. ;-)

Сорцы на Паскале - это хорошо :)

Это ведь установка какого-то видеорежима?

Вроде графического.

Лучше помню int 21h (функции OS),

int 13h (работа с дисками),

08/1Сh - аппаратное прерывание таймера/вариант от OS.

именно, в статье об этом говорится.

Я так понял тут 486 нужен только для xadd, а без него можно было бы и на 8086 запустить?

для 8086 надо будет еще movsd заменить на movsw,
и вместо "shr al, 2" сделать две инструкции:
shr al, 1
shr al, 1

ну и да, "xadd bx, ax" заменить на
xchg ax, bx
add bx, ax

Запустится на 8086, но будет 1 фпс в секунду, дай бог ) Все же тут упор сделан не на скорость.

олды подошли

Там разве не в cl счётчик был?

Для сдвига.

Это, если не ошибаюсь, уже в 286-ых появилась возможность задавать количество позиций для сдвига

в виде параметра в коде инструкции.

на 8086 можно или 1 или cl. Но cl как вы понимаете здесь контрпродуктивно.

теперь нужен подробный гайд "как развлечь себя переписав ядро linux на чистом c"

А вот не надо. На сколько я знаю, в ядре Linux есть немало ассемблера, и он там не зря.

Там уже и Rust есть

Мы все дальше от бога!

Да, на языках высокого уровня (HLL) - гораздо проще структурировать код.

А на ассемблере - всегда хочется сделать итоговый файл поменьше. Почему и используется ряд ухищрений/оптимизаций, что не делают код понятнее, хотя и - получается сделать файл меньшего размера.

Ну, так-то делать итоговый файл поменьше - не всегда самоцель, при использования ассемблера. Целью может быть оптимизация скорости. Правда при этом совершенно не обязательно всю программу писать на ассемблере.

Нужно писать новое ядро, компактное, с ограниченным набором сисколов, и только для одной платформы - RISC-V.

Эмммм... А на чём сейчас написан Линукс?

Вроде как раз на Си Оо

Что касается sizeCoding-a:

Однобайтная команда retn (return near, 0C3h) выталкивает со стека слово 0h, а по адресу 0000 в сегменте памяти находится слово 0CD 20h, что на ассемблере есть int 20h, т.е. нормальное завершение программы.

Да, можно и самостоятельно вызвать int 20h, можно и функцию (на память, могу уже ошибиться) 4Ch (в AH, в AL - код, что станет значением параметра ErrorLevel). Здесь, кстати, и стек можно "не выравнивать", всё будет нормально.

По вопросу SizeCoding-же:

интересовался вопросами и безопасности, речь о вирусах. Информацию об этом получал, бОльшей частью, из файлов VirList, что шли вместе с антивирусом DrWeb.

У Aidstest тоже был какой-то подобный файл, но в продукте Игоря Данилова (DrWeb) этот файл

(с описанием вирусов) был наиболее содержательным.

Помню, сам осваивал работу с MCB (Memory Control Block), и научился прятать свой код в недрах самой OS, модифицируя и MCB. Писал, например, программу, что в помимо какого-то полезного действия оставляла в памяти код

("садился" новый обработчик на прерывание таймера, 1Ch, и выводил на экран 80х25 змейку,

что двигалась по текстовому экрану, "съедая" существовавший текст;

тёмные юзвери, конечно, пугались, но эта программка была безвредной. Запускалась - на ПК {286-ых} в компьютерном классе, на уроках информатики).

Да, и с MBR знакомился (с partition table, что содержалась в первом секторе накопителя ЖМД). Большого разнообразия BIOS-ов тогда ещё небыло, и получалось перезаписать сектор с MBR без warning-ов. Хотя, как помню, что находил и такой метод, как размещение в буфере клавиатуры (0х04хх) признаков нажатия клавиши "y", что принималось системой как согласие пользователя на запись в MBR, даже если стоял соответствующий флаг в BIOS.

И: с разновидностями вирусов я был достаточно хорошо знаком

(загрузочные, из MBR, файловые {com, exe, и даже bat были}, резидентные и нет, шифрующиеся и нет, даже свой полиморфик, файлово-загрузочный написал, хоть и не распространял).

Устойчивость системы в Real Time Mode была не такой высокой

(в защищённом режиме - в память другой программы, без прав супервизора, "не залезешь").

Да, помню демку DOD.com, что была из класса 4096. Там была музыка, как помню, для Adlib-совместимой карты (а SoundBlaster был совместим с Adlib). Тогда эти демки просто собирались в отдельном каталоге.

Те "винты", кстати, у меня целы (210MB - первый, может уже его и нет, но содержимое - должно быть на IDE-винте, одном из следующих).

MS-DOS там - 6.22, MS-Win 3.1, 3.11. Из HLL - Borland TurboPascal 7.0, бейсик-интерпретатор, и Borland TurboAssembler 4.0. А чтобы нормально писать на том-же Ассемблере, надо знать, как работает "железо".

Напишите об этом статью.

Да, size coding-ом занимался, когда bootExe вирус в загрузочном секторе делал. 1990 год.

и функцию (на память, могу уже ошибиться) 4Ch

21го прерывания. Да есть такая, но сайзкодинг говорит, что её вызвать будет длиннее, чем int 20h (который для com-файла заменяется на ещё более короткий retn, о чем в статье есть), а значит, лучше не использовать. Как же, помню ещё - B4 4C CD 21

Да-да, тоже очень много подчерпнул из описания ДрВэба! Очень был познавательный файлик.

Если у, человека есть время заниматься хобби по программированию и он работает значит у него основные программы уже не, развиваются и ему скучно

На работе почти всегда скучные задачи. Увы, бизнесу не нужны шедевры, ему нужен конвеер. Поэтому и развлекаемся в свободное время своими хобби-проектами )

На самом деле ничего это не значит, кроме того что у человека хобби - ретрокомпьютинг

Насчёт

'значит у него основные программы уже не развиваются':

и то, что вы называете "основными программами" - не есть что-то фундаментальное, неизменное.

Именно благодаря этому человечество пока не вымерло, как вид.

DosBox 0.74-3 (который не обновлялся с 2019-го)

Ничего страшного, MS-DOS не обновляется с 2001-го!

ну как бы да, но нет... В DosBox 0.74-3 много ошибок в эмуляции железа (он эмулирует не только DOS, но и процессор, видео-карту, звуковую и т.д.), много таких "лояльных" моментов, которые на реальной машине вызовут зависание или тормоза. Многое уже исправлено в форках типа DosBox-x и Dosbox Staging.

Также в DosBox 0.74-3 нет поддежки процессоров выше Pentium. То есть уже MMX там нет (не говоря уже про SSE). Нет поддержки кое-каких звуковых чипов-синтезаторов. Кривая эмуляция Gravis Ultrasound. Вобщем много чего, что давно требует обновления.

Так что страшно, страшно... Мы не знаем что это. Если бы знали...

Понятно, что здесь уже изначально техническая тема перекочевала в философскую.

Но и - приведу тогда те соображения, что есть

(по этой теме, всё - imho):

конкретная операционная система - это лишь один из шагов, что привёл к нынешнему состоянию дел. И потому считаю это неверным, высказывать подобные рассуждения ("Если бы...").

Надо уметь смотреть вперёд, при этом активно используя приобретённые ранее знания и навыки. Чтобы прошлое - было не якорем, а дополнительной надстройкой, что повышает КПД используемых новых агрегатов.

All within my hands

В нашей вселенной Йозеф Кнехт стал бы демосценером.

Я вот тоже сабя на праздниках развлекаю. Пишу программу Monitor для загрузки и запуска других программ на своей синтезируемой СнК. Задача оказалась нифига не простой, особенно сохранение контекста и восстановление после crash-а, инициализация crt и libc. Без ассемлерного кода - никак. Так глядишь RV32 асм изучу и освою linker.ld скриптинг. ;)

Вот кстати, буквально на днях прошел Dihalt 2025 Winter в Нижнем, и там была такая вот демка: https://www.youtube.com/watch?v=jIQhSY1BZxQ

Прикольно, спасибо. У меня получается что-то типа RPi Pico, но с польностью открытой аппаратурой и на открытой архитектуре. Нужно добавить всякой звукосинтезирующей аппаратуры в духе Pokey или SID и будет своя демосценнерская платформа. ;)

Вот такое бы вкорячить сразу https://picog.us/ :)

хотя, на самом деле каждый может сам себе эмуляцию любого звукового чипа закодить, я думаю, при нынешних-то мощностях ARM-процессоров.

С Пикогусему у меня коллега балуется на 486-й машине.

PCM требует много памяти для сэмплов и звуковых файлов и он уже есть у меня в виде 4-х канального 12-битного DAC-а. Но это не спортивно, так как реализовано отдельной микросхемой на плате, а не синтезируемой аппаратурой вутри ПЛИС. Хочу заморочиться именно своей аппаратурой для формирования звука в формате высокоскоростного однобитового потока (DSD64).

Соглашусь, ещё можно рассуждать о "чистоте" звука, понимая под этим наличие гармонических искажений (речь о "глубине", т.е. разрядности битрейта, используемого при дискретизации).

Но про 4 канала:

1) уха у человека - всё-же два;

2) да и сама звуковая волна, распространяясь в пространстве как волна упругой деформации, имеет размерность, равную двум (уравнение сферы - второго порядка).

Добавление дополнительных источников звука - лишь способно исказить (за счёт интерференции)

уже созданную (другим источником звука) волну.

И - почему тогда остановились именно на 4-х каналах, а не на 6-ти или 8-ми?

И - почему тогда остановились именно на 4-х каналах, а не на 6-ти или 8-ми?

Всё достаточно прозаично - у микросхемы ЦАП есть 8 каналов, но места на плате было только для того, чтобы вывести четыре. :-) На audio jack выведено два.

Вот всех этих сайзкодеров палкой в мобильную разработку, пусть приложения ужимают.))))

наоборот, мобильных разрабов в сайзкодинг :D
но есть нюанс - я читал, что приложения огромные из-за того, что то ли андроид, то ли гугл требуют от разрабов запихивать ассеты под несколько разных разрешений экрана, что раздувает объем приложения. Может, код там и сайзить некуда уже.

Я знаю что будет - они покопаются в ресурсах прог, скажут что все зло от разрешений FHD (или уже QHD стандарт?) и ресурсов/текстур под них, задаунскейлят всё под VGA - и настанет щщастье :) /s

А смысл ? Там 90% приложения - это картинки и анимации.

Я совсем чайник в ассемблере, попробовал традиционный Hello, world написать в NASM, так после компиляции у меня исполняемый файл получился 9,5 килобайт)

Это вы наверное под Windows 64-bit делали "Hello, world!" со статической линковкой чего-то там

Только что проверил хелловорлд Win64 2,5 килобайта)

ну, в антисайзкодинг тоже надо уметь :)

Да что там уметь-то?

Неужели сложно добавить nop-ов?

Или - речь о том, чтобы не так просто сжимаемым (тем же RLE) оказался код?

Да что там уметь-то?

Неужели сложно добавить nop-ов?

Или - речь о том, чтобы не так просто сжимаемым (тем же RLE) оказался код?

Linux x64, компиляция nasm -f elf64 -F dwarf -l hw.lst -o hw.o hw.asm

Линк ld -g -m elf_x86_64 -o hw hw.o

Почитаю про параметры ld.

У меня первый ассемблер это был/и до сих пор остается это для платформы chip8, где в нем всего 35 команд и 16 регистров. А самый первые "программы" которые я учился писать это был td4as для TD4 CPU (процессор на 13 микросхем стандартной логики), в нем всего 4 регистров и 5-7 команд, максимальный размер программы может быть до 16 байт.

У "мегапроцессора" тоже четыре регистра и три дюжины команд: https://habr.com/ru/articles/309654/

Два журнала для программистов-практиков «Монитор» этому господину

Адово плюсую за статью

В детстве тоже увлекался подобным. Вспомнился один трюк:

in al, 60h
das
jc @mainloop

Обезжиривание на один байт ;)

есть такое, только это выход по нажатию любой клавиши

По мере чтения статьи у меня возникали разные вопросы, но потом я сам находил на них ответы. Чтобы это не пропало, я это сюда в виде комментария сброшу.

Если я правильно понял, то идея демо такая: буфер не очищается, на каждом шаге в него добавляются белые точки, и потом проходит блюр. Наверное, при многократных исполнениях такого цикла изображение должно стремиться к белому. Но операция деления на 4 проходит с отбрасыванием дробной части, что, в свою очередь, даёт смещение в сторону чёрного. То есть подобрав количество добавляемых белых точек можно получить относительно стабильный баланс серого.

Я так понимаю, что способ определить начало буфера через добавление константы к сегментному регистру работает, потому что тупо есть органичение, что com-файлы не могут быть больше 64кб, поэтому нефиг заморачиваться.

В том цикле, который палитру создаёт, вам точно надо mov ax, 1010hделать каждый раз внутри цикла? Может вынести в начало?

С блюром у вас занятно: для самого верхнего ряда вы обращаетесь и к предыдущим 320 байтам, которые вообще не в буфере. Кроме того, поскольку вы счётчик цикла инициализируете в 0000h, то цикл обработает 10000h байт, хотя достаточно обработать fa00h байт. Но, поскольку вы стремитесь к уменьшению размера программы, а не к скорости, то это понятно.

В цикле блюра можно было бы обойтись одним регистром, тем же SI. Я думаю, что inc si выставит флаг нуля, и вместо loop сделать переход через jnz. Если SI для этого не подойдёт, то его в цикле можно заменить на BX, он тоже умеет в адресацию со смещением.

Очень хорошая статья!

В том цикле, который палитру создаёт, вам точно надо mov ax, 1010hделать каждый раз внутри цикла? Может вынести в начало?

Да, можно и в начало. Правда есть еще один трюк, помогающий скостить 2 байта, если таки оставить "mov ax, 1010h" в цикле: убрать "int 10h" в инициализации видеорежима, а в цикле инита палитры "int 10h" поставить перед "mov ax, 1010h". Таким образом на первом цикле установки палитры как раз включится видеорежим (посколько AX все еще будет равен 0013h), а уж последующие циклы начнут устанавливать палитру. Поэтому я по привычке оставляю mov ax, 1010h внутри цикла. В статье решил не запутывать читателя лишний раз.

С блюром у вас занятно: для самого верхнего ряда вы обращаетесь и к предыдущим 320 байтам, которые вообще не в буфере.

На самом деле в "реальном режиме" нельзя обратиться к памяти вне сегмента. А буфер у нас - это весь сегмент (64кб). "add al, [si-320]" эквивалентен операции "add al, [si+65216]", ну а если si+65216 превышает 65535, то происходит сброс c переносом.

хотя достаточно обработать fa00h байт.

Если проходить блуром только 64000 точек, то будут некрасивые артефакты вверху экрана. Догадайтесь почему :)

В цикле блюра можно было бы обойтись одним регистром, тем же SI. Я думаю, что inc si выставит флаг нуля, и вместо loop сделать переход через jnz.

Да, можно. Правда в данном случае это на размер не повлияет (так как мы все равно убрали "xor cx, cx"). Но это правильный ход. Причем на старых процессорах (386-й, 486-й) "LOOP" еще и значительно медленнее, чем "dec + jnz", так что если важна скорость, то про LOOP вообще стоит забыть :)

На самом деле с представленным в статье кодом можно еще много чего сделать, чтобы уменьшить размер, я намеренно не стал дожимать все что мог, ожидая что читатели сами найдут такие места.

Спасибо за вдумчивое изучение кода :)

Интересно, что можно было бы в ARM уместить в 8192 байта, если писать код на ассемблере ?

Эх, напомнили... Как-то без всякой задней мысли содрал из какой-то книжки по асму простенький видео-эффект и благополучно забыл про него. Максимально простой, без изысков, что-то килобайта то ли на на 2, то ли на 4 (exe). Не помню уже. И потом вместе с пачкой других кинул знакомому В ФИДО. Эта редиска несклько оптимизировал код и превратил в COM, так что тот изрядно похудел. Вызов был принят и у меня получилось уже что-то около 200байт. И вот так мы перекидывались им какое-то время, пока, в итоге у меня не получилось 63 байта :) Последний байт пришлось искать две недели. Было весело!

Вызов был принят и у меня получилось уже что-то около 200байт. И вот так мы перекидывались им какое-то время, пока, в итоге у меня не получилось 63 байта :) Последний байт пришлось искать две недели. Было весело!

А потом они нам рассказывают, что программировать скучно!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории