Про алгоритм лучше спросить у автора. Он доступен в Discord (ссылка есть в статье про radar).
Могу сказать только, что после adc al,0x10 на первой итерации int 0x10 выполняет установку 16-цветового EGA видеорежима 640x320, а на следующих выводит точку через функцию ah=0x0C, цвет которой каждый раз меняется c помощью всё той же инструкции adc al,0x10.
Адрес SS:BP+SI указывает на голову змеи. Т.е. по этому адресу лежит "координата" головы (если точнее, то смещение адреса в сегменте DS, соответствующего цвету символа; для простоты я буду называть это координатой). После чтения скан-кода клавиши и преобразования его в смещение координаты (-2 / 2 / -80 / 80) мы добавляем к этому значению координату головы, получая т.о. новую координату. Далее эта координата записывается в SS:BP+SI, сдвигая всё остальное далее по массиву.
Например, длина (CX) = 5. В массиве (по адресу SS:BP+SI) были такие слова: [11] [13] [15] [17] [19] (на самом деле прямо таких значений не будет, но для простоты пусть будет так, можете мысленно прибавить к этим числам 64000). Мы нажали вниз (80), получили новую координату: 80+11=91, новый массив будет таким: [91] [11] [13] [15] [17].
При этом мы рисуем только голову (91) и затираем хвост (19). При съедании яблока после вышеуказанной процедуры длина увеличивается на 2, и на следующем кадре будет так: [171] [91] [11] [13] [15] [17] [мусор]. В этот раз мы затираем (один раз) "мусорную" координату. В теории, это может быть координата на экране, т.е. мы можем затереть какое-то яблоко, часть себя или даже нарисовать на экране более жирную точку, если координата окажется чётной (символ с кодом 7, т.к. мы затираем хвост цветом 7). Если присмотреться к скриншоту, то можно увидеть, что внизу есть более жирная точка. Этот артефакт — как раз результат такого "удаления" несуществующего хвоста с мусорной координатой, которая оказалась чётной и попала в экран.
Этот глюк можно убрать, добавив инструкцию mov [bp+si],cx после loop, но это +2 байта (хотя можно всё равно уложиться в 64 байта, убрав звуковой щелчок out $61,al). Но тут всё равно нет 100% гарантии, что первые 5 (начальная длина змейки) координат не попадут в экран (а эти координаты исходно мусорные). Нужно добавлять ещё несколько байт в начало кода для их очистки. Но и в этом случае можно уложиться в 64 байта, если прибавлять длину змейки на 1, т.к. мусорной координаты при увеличении длины не будет, и mov [bp+si],cx здесь уже будет не нужен + кол-во inc cx уменьшится на 1 шт + можно до кучи убрать удаление курсора.
P. S. Обычно сдвиг массива "вперёд" делается с конца назад (чтобы не затереть самого себя), но используя xchg можно делать это, двигаясь с начала вперёд (как здесь).
Сайзкодинг, кстати, как правило, имеет одну важную особенность: необходимо не просто ужать код насколько это возможно, а привести его к целевому размеру (64, 256 байт и т.д.).
В гольфкодинге люди соревнуются в стиле "у кого меньше, тот и молодец" :)
Да, сайзкодинг — это оптимизация по размеру бинарника, а гольфкодинг — по размеру исходника. Это тоже прикольная штука, я пару раз участвовал (например, тут на C).
В демосцене есть платформы, называемые фэнтези консолями (например, TIC-80, MicroW8 и пр.) Так вот, в TIC-80 исходник не компилируется и хранится в файле в сжатом (заархивированном) виде. По сути, такой сайзкодинг отчасти правильнее назвать гольфкодингом, т.к. чем меньше исходник, тем меньше и сжатый файл. В общем случае. Хотя иногда увеличение размера исходного кода (ради того, чтобы в нём были повторяющиеся символы) может привести к уменьшению сжатого файла. Поэтому я написал "отчасти" :)
Правила звука не требуют, но место есть, почему бы и нет? С другой стороны, если его убрать, можно сделать выход. Или что-нибудь ещё (всё-таки 5 байт — не шуточки).
Есть while, значит можно и do-while сделать. С for и switch сложно, в 30 байт вряд ли влезет. Массивы тоже вряд ли. Вот return ещё можно попробовать, вероятно.
Я ради прикола попытался сократить немного код, получилось ужать его на 6 байт. Профит небольшой, конечно, но в целом там есть ещё порядка 30 байт на какие-нибудь фичи типа операторов / ! ~ break continue do-while ... чего там ещё нет? :)
Есть ещё один интересный способ. Не лучше и не хуже других. Так же, как и "вот и песенке конец". Для кого-то лучше сработает одно, для кого-то - другое.
Суть в том, чтобы укорачивать повторяемый фрагмент, начиная, например, сразу с 1 секунды и до тех пор, пока это не превратится в монотонный звук (монотонный - не в смысле одного чистого тона, а в смысле очень короткого фрагмента, условно говоря, длиной в 1-10 миллисекунд). Потом резко оборвать его. Вся процедура может занимать секунды 3. Если мелодия снова всплывает (обычно не сразу), повторить. И так несколько раз. Повторять можно сразу с этого монотонного звука.
В wait мы передаем заблокированный std::unique_lock, wait снимает временно блокировку и усыпает (не всегда: когда предикат в wait истинный может и не уснуть).
Подкорректирую последовательность операций.
Wait сначала проверяет предикат (если он есть), а потом уже снимает блокировку и начинает принимать уведомления, усыпляя поток. Причём, делает это фактически атомарно (т.е. между снятием блокировки и началом приёма уведомлений другой поток вклиниться не может – это тоже важно). После пробуждения устанавливает блокировку обратно (здесь тоже обычно есть оптимизация, из-за которой рекомендуют notify ставить перед unlock'ом) и снова идёт на проверку предиката.
Если предикат есть и возвращает true, никаких unlock'ов не будет.
Отличная идея! Самому нужна такая прога, а писать было в лом :)
Хорошо бы ещё добавить возможность использования прокси для отслеживания зарубежных сайтов (типа intel.com).
Про алгоритм лучше спросить у автора. Он доступен в Discord (ссылка есть в статье про radar).
Могу сказать только, что после
adc al,0x10на первой итерацииint 0x10выполняет установку 16-цветового EGA видеорежима 640x320, а на следующих выводит точку через функциюah=0x0C, цвет которой каждый раз меняется c помощью всё той же инструкцииadc al,0x10.Добавлена 45-байтовая версия змейки (в спойлере в конце статьи) :)
Адрес SS:BP+SI указывает на голову змеи. Т.е. по этому адресу лежит "координата" головы (если точнее, то смещение адреса в сегменте DS, соответствующего цвету символа; для простоты я буду называть это координатой). После чтения скан-кода клавиши и преобразования его в смещение координаты (-2 / 2 / -80 / 80) мы добавляем к этому значению координату головы, получая т.о. новую координату. Далее эта координата записывается в SS:BP+SI, сдвигая всё остальное далее по массиву.
Например, длина (CX) = 5. В массиве (по адресу SS:BP+SI) были такие слова: [11] [13] [15] [17] [19] (на самом деле прямо таких значений не будет, но для простоты пусть будет так, можете мысленно прибавить к этим числам 64000). Мы нажали вниз (80), получили новую координату: 80+11=91, новый массив будет таким: [91] [11] [13] [15] [17].
При этом мы рисуем только голову (91) и затираем хвост (19). При съедании яблока после вышеуказанной процедуры длина увеличивается на 2, и на следующем кадре будет так: [171] [91] [11] [13] [15] [17] [мусор]. В этот раз мы затираем (один раз) "мусорную" координату. В теории, это может быть координата на экране, т.е. мы можем затереть какое-то яблоко, часть себя или даже нарисовать на экране более жирную точку, если координата окажется чётной (символ с кодом 7, т.к. мы затираем хвост цветом 7). Если присмотреться к скриншоту, то можно увидеть, что внизу есть более жирная точка. Этот артефакт — как раз результат такого "удаления" несуществующего хвоста с мусорной координатой, которая оказалась чётной и попала в экран.
Этот глюк можно убрать, добавив инструкцию
mov [bp+si],cxпослеloop, но это +2 байта (хотя можно всё равно уложиться в 64 байта, убрав звуковой щелчокout $61,al). Но тут всё равно нет 100% гарантии, что первые 5 (начальная длина змейки) координат не попадут в экран (а эти координаты исходно мусорные). Нужно добавлять ещё несколько байт в начало кода для их очистки. Но и в этом случае можно уложиться в 64 байта, если прибавлять длину змейки на 1, т.к. мусорной координаты при увеличении длины не будет, иmov [bp+si],cxздесь уже будет не нужен + кол-воinc cxуменьшится на 1 шт + можно до кучи убрать удаление курсора.P. S. Обычно сдвиг массива "вперёд" делается с конца назад (чтобы не затереть самого себя), но используя
xchgможно делать это, двигаясь с начала вперёд (как здесь).Нет, меня там не было ?♂
KolibriOS — тоже магия ассемблерного искусства.
На SSE/AVX даже синус так просто не вычислишь (особенно в упакованном формате) :)
Сайзкодинг, кстати, как правило, имеет одну важную особенность: необходимо не просто ужать код насколько это возможно, а привести его к целевому размеру (64, 256 байт и т.д.).
В гольфкодинге люди соревнуются в стиле "у кого меньше, тот и молодец" :)
Да, сайзкодинг — это оптимизация по размеру бинарника, а гольфкодинг — по размеру исходника. Это тоже прикольная штука, я пару раз участвовал (например, тут на C).
В демосцене есть платформы, называемые фэнтези консолями (например, TIC-80, MicroW8 и пр.) Так вот, в TIC-80 исходник не компилируется и хранится в файле в сжатом (заархивированном) виде. По сути, такой сайзкодинг отчасти правильнее назвать гольфкодингом, т.к. чем меньше исходник, тем меньше и сжатый файл. В общем случае. Хотя иногда увеличение размера исходного кода (ради того, чтобы в нём были повторяющиеся символы) может привести к уменьшению сжатого файла. Поэтому я написал "отчасти" :)
Не оГРаничен, а оРГаничен :))
Правила звука не требуют, но место есть, почему бы и нет? С другой стороны, если его убрать, можно сделать выход. Или что-нибудь ещё (всё-таки 5 байт — не шуточки).
При этом плавность оставляет желать лучшего.
Но это ещё цветочки. Иногда видосы 256-байтовых интро занимают сотни мегабайт, имея при этом артефакты компрессии.
В некотором степени интро является очень хорошо сжатым самораспаковывающимся видеофайлом :)
Разве он не органичен в данном случае?
farbrausch — это легенда :)
Если "шитый байткод", тогда это было бы "threaded bytecode", а это "байтовый шитый код", ну или см. ниже ещё вариант.
Да, вполне. Или даже ещё проще: "Байтовый шитый код", но оригинальное название рядом нужно оставить обязательно.
Есть while, значит можно и do-while сделать. С for и switch сложно, в 30 байт вряд ли влезет. Массивы тоже вряд ли. Вот return ещё можно попробовать, вероятно.
Я ради прикола попытался сократить немного код, получилось ужать его на 6 байт. Профит небольшой, конечно, но в целом там есть ещё порядка 30 байт на какие-нибудь фичи типа операторов / ! ~ break continue do-while ... чего там ещё нет? :)
Есть ещё один интересный способ. Не лучше и не хуже других. Так же, как и "вот и песенке конец". Для кого-то лучше сработает одно, для кого-то - другое.
Суть в том, чтобы укорачивать повторяемый фрагмент, начиная, например, сразу с 1 секунды и до тех пор, пока это не превратится в монотонный звук (монотонный - не в смысле одного чистого тона, а в смысле очень короткого фрагмента, условно говоря, длиной в 1-10 миллисекунд). Потом резко оборвать его. Вся процедура может занимать секунды 3. Если мелодия снова всплывает (обычно не сразу), повторить. И так несколько раз. Повторять можно сразу с этого монотонного звука.
Мне вот тоже интересно, каков реальный профит будет от этого в чём?
Подкорректирую последовательность операций.
Wait сначала проверяет предикат (если он есть), а потом уже снимает блокировку и начинает принимать уведомления, усыпляя поток. Причём, делает это фактически атомарно (т.е. между снятием блокировки и началом приёма уведомлений другой поток вклиниться не может – это тоже важно). После пробуждения устанавливает блокировку обратно (здесь тоже обычно есть оптимизация, из-за которой рекомендуют notify ставить перед unlock'ом) и снова идёт на проверку предиката.
Если предикат есть и возвращает true, никаких unlock'ов не будет.