Это — тёплая, ламповая статья об Ассемблере и разработке ПО. Здесь мы не будем пытаться писать убийцу Майкрософта или Андроида. Мы будем писать убийцу 2048. Здесь не будет докера и терраформа с кубером. Зато здесь вы сможете найти большое количество материалов по Ассемблеру, которые помогут вам вновь погрузиться в мир трёхбуквенных инструкций. Доставайте пивко, и поехали. (Саундтреком к этой статье можно считать IBM 1401 a system manual)


Недавно, было дело, сидел и ждал результатов какой-то конференции на одном из предприятий. Сидеть было скучно, и я вытащил мобильник, чтобы погрузиться в мир убивания времени. Но, к моему огорчению, мы были в месте с нереально слабым сигналом, и я понял, что нахожусь в том странном и непонятном мире, когда интернета нету. Ничего путного на мобиле у меня установлено не было, посему я переключил своё внимание на гостевой лаптоп. Внутрикорпоративный прокси спрашивал логин и пароль для интернета, коих у меня не имелось. Ступор. Я вспомнил 1990-е, когда интернет был только по модему и добывать его надо было через поход на почту или в «Интернет-кафе». Странное чувство.

К счастью, на вышеозначенном компьютере была обнаружена игрушка под названием 2048. Замечательно, подумал я, и погрузился в складывание кубиков на целых 30 минут. Время было убито чётко и резко. Когда пришла пора уходить, я попытался закрыть игрушку, и увидел, что она подвисла. Я по привычке запустил менеджер задач и хотел уже было убить несчастную, когда вдруг мои глаза увидели потребление 250-ти мегабайт оперативной памяти. Волосы встали дыбом под мышками, пока я пристреливал кобылку. Страшные 250 мегабайт оперативки не хотели вылезать из моей головы.

Я сел в машину и поехал домой. Во время поездки я только и думал о том, как можно было так раскормить 2048 до состояния, когда она будет пожирать 250 мегабайт оперативки. Ответ был достаточно прост. Зоркий глаз системщика увидел электрон, который запускал нагружённый яваскриптовый движок, который рендерил на экране 16 16-ти битовых чисел.

И я подумал, а почему-бы не сделать всё намного более компактно? Сколько битов тебе на самом деле надо, для того, чтобы хранить цифровое поле 2048?

Для начала обратимся к интернетам. Учитывая, что мы играем абсолютно правильную игру и все ставки на нас, то при самом хорошем расходе, мы не сможем набрать больше 65536. Ну, или если всё будет в нашу пользу, и мы будем получать блоки с четвёрками в 100 процентах случаев, то мы можем закончить с тайлом в 131072. Но это на грани фантастики.

Итак, у нас есть поле из 16-ти тайлов, размером до 131072, который умещается в Int. В зависимости от битности системы, int может быть 4 или 8 байт. То есть, 16*4 = 64 байта, хватило бы для хранения всего игрового поля.

Хотя, на самом деле, это тоже жутко много. Мы ведь можем хранить степени двойки, так ведь?

;00 = nothing
;01 = 2
;02 = 4
;03 = 8
;04 = 16
;05 = 32
;06 = 64
;07 = 128
;08 = 256
;09 = 512
;0a = 1024
;0b = 2048
;0c = 4096
;0d = 8192
;0e = 16384
;0f = 32768
;10 = 65536 - maximum with the highest number is 2
;11 = 131072 - maximum with the highest number 4
;12 = 262144 - impossible


Ага, мы можем запихнуть каждую клетку поля в один байт. На самом деле, нам нужно всего лишь 16 байт, на то, чтобы хранить всё игровое поле. Можно пойти немного дальше и сказать, что случай, когда кто-то соберёт что-то больше 32768 — это граничный случай, и такого быть не может. Посему можно было бы запихнуть всё поле в полубайты, и сократить размер всего поля до восьми байт. Но это не очень удобно. (Если вы реально забыли бинарное и шестнадцатеричное счисление, то тут нужно просто сесть, погуглить и вспомнить его)

Итак, подумал я, если всё это можно запихнуть в 16 байт, то чего бы этим не заняться. И как же можно отказаться от возможности вспомнить мой первый язык программирования — Ассемблер.

[flashback mode on]

image

Картинки детства. Выпуск №45

Именно в этой статье я вычитал про разные компиляторы, нашёл мануалы и попробовал писать. Писалось плохо, потому что я понимал, что мне не хватает понимания основ, и нужен был какой-то фундамент, который позволил бы работать более стабильно.

image

Ужасы детства. Ссылка на издание

Из всех сайтов, приведённых в примерах журнала Хакер, в живых не осталось ни одного. Но, не бойтесь, дело живо и инструкции публикуются. Вот здесь, например, есть одно из самых подробных описаний работы с Ассемблером.

[flashback mode off]

Когда я добрался домой и сел за свой компьютер, я понял, пошёл вспоминать молодость. Как скомпилировать ассемблер? В своё время, когда мы всему этому учились, у нас был TASM, MASM и MASM32. Я лично пользовался последними двумя. В каждом ассемблере был линкер и сам компилятор. Из этих трёх проектов в живых остался только оригинальный MASM.

Для того чтобы его установить в 2021 году, надо сливать Visual Studio и устанавливать кучу оснасток, включая линкер. А для этого надо качать полтора гигабайта оснасток. И хотя я, конечно, нашёл статьи о том, как использовать llvm-link вместо link при работе с Ассемблером, там нужно то ещё скрещивание ужей с ежами и альбатросами. Такими непотребностями мы заниматься не будем.

Хорошо, в таком случае, что? С удивлением обнаружил, что большое количество курсов по Ассемблеру х64 написано для линукса. YASM и NASM там правят бал и работают просто прекрасно. Что хорошо для нас, NASM отлично запускается и работает на Windows. Типа того.

Запускается-то он, запускается, но линкера у него в комплекте нету. (По-русски этот линкер должен называться компоновщиком, но мне это непривычно и звать я его буду линкером или линковщиком). Придётся использовать Майкрософтский линковщик, а как мы знаем, для его использования нам нужно качать гигабайты MSVS2021. Есть ещё FASM, но он какой-то непривычный, а в NASM бонусом идёт отличная система макросов.

Опять же, дружить всё это с llvm-link мне было очень занудно, потому что ни одна из инструкций не описывала того, как эту сакральную магию правильно применять.

Весь интернет пестрит рассказами про то, как прекрасен MinGW. Я же, будучи ленивым, пошёл по упрощённому пути и слил систему разработки CodeBlocks. Это IDE со всякими свистопипелками и, самое главное, наличием установленного MinGW.

Отлично, устанавливаем всё, добавляем в PATH и теперь мы можем компилировать, запуская:

nasm -f win64 -gcv8 -l test.lst test.asm
gcc test.obj -o test.exe -ggdb

Отлично! Давайте теперь сохраним данные в памяти:

stor    db    0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00
fmt     db     "%c %c %c %c", 0xd, 0xa,"%c %c %c %c", 0xd, 0xa,"%c %c %c %c", 0xd, 0xa,"%c %c %c %c", 0xd, 0xa, "-------",0xd, 0xa, 0

Вот наше игровое поле stor, а вот — беспощадное разбазаривание оперативной памяти — строка форматирования fmt, которая будет выводить это игровое поле на экран.

Соответственно, для того, чтобы обратиться к какой-либо клетке поля, мы можем считать байты следующим образом:

; byte addressing

; 00 00 00 00    [stor]      [stor+1]    [stor+2]    [stor+3]

; 00 01 00 00    [stor+4]    [stor+5]    [stor+6]    [stor+7]

; 00 01 00 00    [stor+8]    [stor+9]    [stor+a]    [stor+b]

; 00 00 00 00    [stor+c]    [stor+d]    [stor+e]    [stor+f]

Тут начинаем втягиваться в разницу того самого 16-ти битного ассемблера под ДОСом из страшного Хакера 2002 года и нашего 64х битного ассемблера прямиком из 2021.

У нас были регистры ax, bx и так далее, помните? Все они делились на две части: _l _h, типа al, ah для записи байта в верхнюю часть ax или в нижнюю его часть. Соответственно, al был восьми битовым, ax был 16-ти битовым, а если вы были счастливым обладателем нормального процессора, то вам был доступен eax для целых 32х бит. Хаха! Добро пожаловать в новые процессоры. У нас теперь есть rax для записи 64х бит.

Что, страшно читать про регистры? Теряетесь и вообще не понимаете о чём идёт речь? Обратитесь к ответу frosty7777777 по адресу qna.habr.com/q/197637. Он приводит список книг по Ассемблеру на русском языке.

Более того, в мире 64х битных процессоров у нас в распоряжении есть не только EAX, EBX, EDX и ECX (не будем забывать про EDI, EBP, ESP и ESI, но и играться с ними тоже не будем). Нам даны R8 – R15 – это замечательные 64х битные регистры. Зубодробилка начинается, если вы хотите считывать данные из этих регистров. Байты можно считать обращаясь к r10b, слова находятся по адресу r10w, двойные слова можно найти по r10d, а ко всем 64ти четырём битам можно обратиться через к10. Почему всё это не назвать так же, как и предыдущие регистры — чёрт его знает. Но ничего, привыкнем.

Более того, благодаря SSE, SSSE и AVX у нас на руках ещё есть 15 регистров по 128 или 256 бит. Они названы XMM0-XMM15 для 128 бит и YMM0-YMM15 для 256 бит. С ними можно вытворять интересные вещи. Но статья не об этом.

Идём дальше. Как выводить данные на экран. Помните ДОС и те замечательные времена, когда мы делали:

mov  dx, msg      ; the address of or message in dx
mov  ah, 9        ; ah=9 - "print string" sub-function
int  0x21         ; call dos services

Теперь забудьте. Прямой вызов прерываний нынче не в моде, и делать этого мы больше не сможем. Если вы ассемблируете под линуксом, вы сможете дёргать системные вызовы, или пользоваться прерыванием 80, которое, отвечает за выплёвывание данных на экран. А вот под Windows у вас нет иных вариантов, кроме как воспользоваться printf. (Нет, конечно, можно было бы получить дескриптор консоли и писать напрямую, но тут уже совсем было бы неприлично). В принципе, это не так-то плохо. Printf это часть стандартной библиотеки Си, и вызывать его можно на чём угодно.

Посему программу мы начнём с пары объявлений для компилятора и линкера:

bits        64
default     rel

    global  main
    extern  printf
    extern  getch
    extern  ExitProcess

Первая строка указывает, что мы работаем на настоящем, ламповом 64х битном процессоре. Последние 3 строки говорят, что нам нужно будет импортировать 3 внешних функции. Две printf и getch для печатания и читания данных и ExitProcess из стандартной библиотеки Windows для завершения приложения.

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

push     rbp
mov      rbp, rsp
sub      rsp, 32

lea      rcx, [lost]    ;Load the format string into memory
call     printf

Сохраняем текущую позицию стека, выравниваем стек и даём ему дополнительные 32 байта. Про магию выравнивания стека можно читать вот здесь. (Статья на английском, как и многие из рекомендованных мною материалов. Комментируйте, если есть на русском, мы добавим.) Загружаем в регистр CX адрес строки под названием lost, которая определена как lost db "You are done!",0xd, 0xa, 0 и вызываем printf, которая эту строку и выведет на экран.

Два основных момента, о которых надо знать — это как выравнивать стек, и как передавать параметры в функции. В примере чуть выше, мы передаём только один параметр. А вот для показа значения всех 16 полей в командной строке мы должны передать 16 параметров, для этого нам нужно будет грузить часть их них в регистры, а часть записывать в стек. Вот — очень запутанный пример того, как программа вызывает printf с 16-ю параметрами для того, чтобы отобразить игровое поле на экране.

Хорошо, что мы уже умеем? Можем грузить данные в память и из памяти, перекладывать в многочисленные регистры и запускать функции из стандартной библиотеки.

Будем использовать getch для того, чтобы считать ввод с клавиатуры. Управление будет вимовским, то есть, hjkl для того, чтобы двигать тайлы. Просто пока не будем мучиться со стрелочками.

Что осталось сделать? Написать саму логику программы.

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

Во первых, направление неважно. Что бы пользователь не нажимал на клавиатуре, мы всегда можем это развернуть и сказать что это просто сжимание 16ти байт слева направо. Но так как ряды у нас не пересекаются, то мы можем сказать, что вся логика-это сжимание четырёх байт слева направо, повторённое четыре раза.

А так как у нас всего лишь четыре байта, то мы можем просто написать логику на граничных кейсах. Какая разница?

Посему считываем направление, проходимся по всем значениям в одной строке и загружаем их в регистры r10 – r14. С этими регистрами и будем работать.

Чтобы облегчить нам жизнь, мы воспользуемся макросами NASM. Пишем два макроса, один для считывания памяти в регистры, другой для переписывания регистров в память. В данном объявлении макроса мы говорим, что у нас будут 4 параметра — 4 адреса в памяти. Их то мы и двигаем в регистры или из регистров. (Все параметры позиционные, % обращается к конкретной позиции)

%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

%macro regtomem 4
	mov [stor + %4], r10b
	mov [stor + %3], r11b
	mov [stor + %2], r12b
	mov [stor + %1], r13b
%endmacro

Тут всё просто.

После этого, передвижение всего поля в любом направлении будет простой задачей. Вот пример направления down. Мы просто выгружаем байты из памяти в регистры, вызываем процедуру, которая обсчитывает сдвиг и двигаем байты обратно в память.

down:
	push 	rbp
	mov 	rbp, rsp
	sub 	rsp, 32

	memtoreg	0x0, 0x4, 0x8, 0xc
	call 	shift
	regtomem	0x0, 0x4, 0x8, 0xc

	
	memtoreg 	0x1, 0x5, 0x9, 0xd
 	call 	shift
	regtomem 	0x1, 0x5, 0x9, 0xd
	

	memtoreg 	0x2, 0x6, 0xa, 0xe
	call 	shift
	regtomem 	0x2, 0x6, 0xa, 0xe
	

	memtoreg 	0x3, 0x7, 0xb, 0xf
	call 	shift
	regtomem 	0x3, 0x7, 0xb, 0xf
	
	leave
	ret

Если посмотреть на другие направления — происходит всё, то же самое, только мы берём байты в другой последовательности, чтобы симулировать «движение» влево, вправо, вниз и вверх.

Процедура самого сдвига находится в этом файле и является самой запутанной процедурой. Более того, точно вам могу сказать, в определённых кейсах она не работает. Надо искать и дебажить. Но, если вы посмотрите на сам код этой процедуры, она просто сравнивает кучу значений и делает кучу переходов. Математики в этой процедуре нет вообще. inc r11 — это единственная математика, которую вы увидите. Собственно говоря, единственное, что происходит в игре с математической точки зрения, это просто прибавление единицы к текущему значению клетки. Так что нам незачем грузить процессор чем-либо ещё.

Запускаем, пробуем — всё хорошо. Цифры прыгают по экрану, прибавляются друг к другу. Нужно дописать небольшой спаунер, который будет забрасывать новые значения на поле. Желания писать собственный рандомизатор прямо сию секунду у меня не было, так что будем просто запихивать значение в первую пустую клетку. А если оной не найдём, то скажем, что игра проиграна.

Складываем всё воедино, собираем, пробуем.

Красота исполнения -5 из десяти возможных. Мы, заразы такие, даже не потрудились конвертировать степени двойки обратно в числа. А могли бы. Если добавить табуляций в вывод, то всё может выглядеть даже поприличнее.

Смотрим в потребление оперативной памяти:

image

Итого — 2.5 мегабайта. Из них 1900 килобайт это общие ресурсы операционной системы. Почему так жирно? Потому что наш printf и ExitProcess используют очень много других системных вызовов. Если распотрошить программу с помощью x64dbg (кстати, замечательный бесплатный дебаггер, не IDA, но с задачей справляется), то можно увидеть, какие символы импортируются и потребляются.

Сама же программа использует 300 килобайт памяти на всё про всё. Это можно было бы ужать, но статья не об этом.

▍ Итак, что же мы теперь знаем про Ассемблер в 2021 году


  1. Он всё ещё живой и люди им пользуются. Существует масса инструментов разработки для всех ОС. Вот, например, ассемблер для новых маковских чипов М1. А здесь можно слить более 5000 страниц документации по процессорам Intel. Ну а если у вас завалялась где-то Raspberry Pi (а у кого она не завалялась?), то вам сюда.
  2. Не всё так просто, как это было в наши стародавние времена, где надо было заучивать таблицу прерываний наизусть. Сегодня мануалов больше и они тяжеловеснее.
  3. Но и не всё так сложно. Опять же, сегодня мануалы найти проще, да и StackOverflow имеет достаточно данных про ассемблер. Да и на Хабре есть большое количество тёплых ламповых статей про Ассемблер.
  4. Скрещивать ассемблер и другие языки программирования не так-то сложно. Мы с вами в этом примере импортировали функции, а можем их экспортировать. Достаточно знать правила работы со стеком, чтобы передавать данные туда и обратно.
  5. Серьёзные системщики, которые могут раздебажить BSOD на лету и распотрошить любую программу с целью её пропатчить, могут читать подобный код без каких-либо проблем. Так что, если вам нужно серьёзно заняться системным программированием, то без ASM вы далеко не двинетесь. (Пусть даже вы не будете писать на асьме напрямую, а будете использовать C-ASM или читать листинги программ)


▍ Для чего вам это надо?


Для того чтобы вы понимали, как работают процессоры. В те старые, тёплые, ламповые времена, когда мне приходилось писать на ASM, я глубоко усвоил основополагающие данные о работе компьютера. После того как вы понимаете, как работать с памятью, что происходит в программе и, как и куда передаются ваши данные, у вас не будет проблем учить любые другие языки программирования. Система управления памяти в С и С++ покажется вам более удобной и приятной, а освоение Rust не займёт много времени.

В этой статье я привёл большое количество ссылок на материалы. Ещё раз обращу ваше внимание на вот эту страницу. Здесь автор собрал в одном файле замечательное руководство по Ассемблеру в Windows.

А вот здесь огромная документация для YASM на русском.

Я бы рекомендовал всем тем, кто только начинает писать программы на языках высокого уровня, взять небольшой пет-проект и написать его на Ассемблере. Так, чисто для себя, чтобы разобраться, на чём вы работаете.

▍ Тёплый, ламповый конкурс на пиво


В дополнение ко всему, вот вам конкурс на пиво. Весь код «работающего» приложения 2048 находится по адресу: github.com/nurked/2048-asm

Вот как выглядит игра на данный момент:


Играем чистыми степенями двойки

Слить скомпилированный бинарник можно по адресу . Играем нажатиями hjkl, выходим по нажатию s.

Для принятия участия в конкурсе вам надо будет сделать PRы, в которых:
Всем тем кто принимал участие — спасибо! Особенно tyomitch, который перепиарил весь проект. И Healer за работу с кнопками.

  1. Переписан спаунер, и он на самом деле рандомно выбирает клетку на экране, в которой появляется новая фишка на поле.
  2. Переписан отображатель, и он выводит в консоль числа, а не степени двоек, возможно даже с подобием сетки.
  3. Добавлены цвета.
  4. Найдена и исправлена ошибка, когда мы сжимаем следующую строку: 7 6 6 1, она сожмётся до 8 1 0 0 за один раз, вместо 7 7 1 0, 8 1 0 0
  5. По нажатию на s игра должна закрываться, но сейчас она тихо падает, потому что стек обработан неправильно. Это нужно починить.
  6. Управление всё-таки нужно сделать стрелочками.

За первый работающий PR по каждому из этих пунктов я лично отправляю создателю денег на пиво пейпалом. Пишите в личку.

Всем успешного учения ассемблера!

Комментарии (100)


  1. dlinyj
    23.09.2021 16:39
    +6

    Да, ассемблер, сколько в этом звуке, для сердца нашего слилось. х86 асм я как-то миновал (ну то есть там пописывал, но серьёзно к этому относиться нельзя). Но вот основательно пописал для с51 и AVR и чутка для arm. Даже сегодня, когда расчехляешь arduino, в голове считаешь такты.
    Прекрасный язык, с одним чудовищным недостатком: непереносим.


    1. Nurked Автор
      23.09.2021 19:27
      +2

      Ну конечно. Хотя, не знаю что сказать, но недостатком ассемблера я это назвать не могу. Это как говорить о недостатках кентавров — нет рогов, и летать не умеет. Как бы и правда, но всё же не того типа лошадь. 8-)

      С другой стороны — намного проще работа с памятью. Вот нигде, где была работа с памятью, не было проще, чем в асьме. Возможно потому, что за всем надо было следить вручную, и у тебя даже нет идеи переменных. В итоге - у тебя есть что надо, и память не течёт направо и налево.


      1. dlinyj
        23.09.2021 23:45
        +1

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


      1. zuek
        24.09.2021 16:38
        +2

        В итоге - у тебя есть что надо, и память не течёт направо и налево.

        Это Вы напрямую с DOS'овскими MCB не пытались работать, а мои поделки (светодиодами в LPT-порту помигать, попищать писи-скрипером по таймеру, загрузить в матричный принтер русские шрифты, если на LPT-порту статусы похожи на переинициализацию принтера) были более изобретательными при работе с оперативной памятью и файловыми буферами, и чтобы у TSR-сторожа, следящего за принтером, не "текла память" (я тогда даже термина такого не знал, но с явлением столкнулся), пришлось довольно много отладочной информации собирать.

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


    1. KvanTTT
      24.09.2021 04:29
      +1

      Если вам нужен переносимый ассемблер, возьмите MSIL или JVM байткод.


      1. CrashLogger
        24.09.2021 10:56
        +2

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


        1. PsyHaSTe
          26.09.2021 00:18
          +1

          Есть ещё LLVM который никуда не запускается. То что многие существующие байткоды принято житовать не означает, что это единственный возможный вариант.


      1. dlinyj
        24.09.2021 12:16

        Да мне си пока хватает


      1. alliumnsk
        24.09.2021 16:17

        На ассемблере по сравнению с Си некоторые вещи можно сделать радикально по-другому, и иногда в разы лучше. Предоставляет ли какую-нибудь потенциальную выгоду использование JVM вместо Java? Кроме более громоздкого синтаксиса.


        1. tyomitch
          24.09.2021 16:37
          +1

          В прошлом году здесь постили хороший пример: компилятор Java позволяет использовать до 2746 значений в enum, тогда как при создании .class-файла вручную этот предел расширяется в 24 раза.


          1. alliumnsk
            25.09.2021 08:54
            +2

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


            1. tyomitch
              25.09.2021 09:34
              +3

              Я думаю, что и все примеры на Си такого рода: всё более полезное добавляют в язык как минимум интринсиками.


              1. alliumnsk
                25.09.2021 11:40
                +1

                На ассемблере можно сделать процедуру с несколькими входами, нетривиальные операции со стеком, прыжки вместо setjmp/longjump.
                Как на Си с помощью инстринсиков сделать продедуру, которая сигнализирует об ошибке в флаге переноса? Инстринсики для add with carry есть но они покрывают только ограниченные случаи использования.


            1. PsyHaSTe
              26.09.2021 00:21
              +2

              Один из вариантов MSIL — возможность написать функцию по ансейф трансмутации из одного типа в другой. Что-то типа reinterpret cast — сама функция же просто делает ret собственного единственного аргумента. Бывает полезно при обработке низкоуровневых данных, когда нужно представить данные в немного другом виде (например преобразование byte[] -> long[]. В последних версиях C# оно появилось в стд в виде Unsafe.As<T> но во-первых раньше его не было, во-вторых если таргетится не последний неткор то этой функции в стандартной поставке нет и нужно продолжать использовать свою.


        1. SVNKz
          27.09.2021 18:36
          +1

          В MathCad 14, например, встроен прекрасный ассемблер, на котором удобно отлаживать программные вложенные циклы. Если битовые значения брать из массива BMP - картинки, то результат работы высвечивается подробно и красиво.


          1. Nurked Автор
            27.09.2021 18:39

            Ну, это всё же, другой ассемблер. Он хоть и ассемблер, но для виртуального процессора.

            Это как тот пацан, который написал эмулятор процессора на экселе. Процессор Шрёдингера. Он одновременно и низкоуровневый и нет.


            1. SVNKz
              30.09.2021 12:40
              +1

              Это самый настоящий ассемблер для отладки программ фильтрации или обработки видеокадра, представленного в виде BMP - файла, - ничего другого для себя пока представить не могу.


  1. beeruser
    23.09.2021 16:48
    +6

    xor r10, r10

    mov r10b, byte [stor + %4]

    Чем вас movzx не устроил?


    1. Nurked Автор
      23.09.2021 18:34
      +4

      Тем что нужно больше мануалов читать. Спасибо большое, пойду их крутить дальше.


  1. klimin007
    23.09.2021 17:18
    +11

    Лично я сразу вспоминаю ассемблер Z80... Некоторые машинные коды даже сейчас помню... )


    1. Nurked Автор
      23.09.2021 20:20

      С сожалением скажу, что до спектрума я так и не добрался. 8-(


      1. klimin007
        24.09.2021 08:54
        +1

        Нет, на Спектруме только играли, а на ассемблере писал на КУВТ Yamaha MSX2.


        1. Z80A
          24.09.2021 17:14
          +4

          На Спектруме демосцена так писала, как КУВТу и не снилось.
          Защита с переписыванием LDIR инструкций во время исполнения, xor с регистром R, раскатывание циклов, недокументированные инструкции процессора, синхронизация с развёрткой для мультиколора, жонглирование теневым ПЗУ для прямого доступа к контроллеру FDD, семплирование через музсопроцессор в режиме COVOXa, божественные MONS, GENS и последователи их TASM и STS…


    1. SVNKz
      30.09.2021 09:59
      +2

      Ассемблер мини компьютера Д3-28 считаю был лучшим в своём классе по удобству и лёгкости освоения - всё множество команд было расписано в таблице на формате А3 и никаких дополнительных документов для работы не требовалось.

      Однажды была сделана попытка скомпоновать команды ассемблера из софта AVR Studio аналогичным образом - этот вариант компоновки команд и сегодня был бы востребован...

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


      1. klimin007
        30.09.2021 12:10

        Я только конец эры ДЗ-28 застал… Ни разу не пользовался… Не знал, что там тоже ассемблер был… Почитал документацию, прикольно!


  1. beerware
    23.09.2021 18:51
    +3

    Вот это ассемблер:

    MOV R0,R1

    010001 в восьмеричном. По памяти пишу )


    1. Nurked Автор
      23.09.2021 19:03

      А это что за ассемблер?


      1. pae174
        23.09.2021 20:59
        +5

        PDP-11, ДВК-2, Электроника-60, СМ-4, Электроника БК-0010, и некоторые другие. Это очень популярная тема и удобная система команд.


        1. Nurked Автор
          23.09.2021 21:15

          Ого! Да, я всё-таки пропустил это. Начинал писать в 1997 году, но даже тогда у меня на руках был неимоверно понтовый компьютер. Букашек видел только в школе, но и там нам не давали ничего, кроме черепашки.


        1. Ivanii
          24.09.2021 09:03
          +1

          В юности писал и редактировал простые програмки для БК прямо в машинных кодах, например записывал сигнал с ИК пульта ДУ телевизора, статическая оперативка работала быстрее и тайминги предсказуемы в отличии от динамической.

          Система команд https://ru.wikipedia.org/wiki/PDP-11#Особенности_PDP-11


    1. Sergey_Cheban
      26.09.2021 00:15
      +1

      О да.

      MOV (PC)+, R1
      #12345


      1. SVNKz
        30.09.2021 12:20
        +1

        На формате листа А3 было размещено краткое описание 256 команд ассемблера - 16 строк по 16 квадратных табличек с описанием команды. Найти любую команду можно было за кратчайшее время...


  1. piton_nsk
    23.09.2021 19:12
    +2

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


    1. Nurked Автор
      23.09.2021 19:29

      Хотелось консольного, лампового. Хотя WinAPI втянуть так же просто, но хотелось сделать что-то с очень низким потреблением памяти. А даже прогрузка самых базовых библиотек занимает мегабайты, как вы можете видеть.


      1. piton_nsk
        23.09.2021 19:45
        +1

        Прямо сейчас, конечно, точно утверждать не могу, но ЕМНИП для консоли нужно user32 и/или kernel32. В общем надо пробовать.


        1. Nurked Автор
          23.09.2021 19:47

          Так, я это буду считать подначкой! :-) могу даже попробовать спортировать на фасм.


        1. tyomitch
          23.09.2021 23:07
          +1

          Вывод в консоль — это WriteFile из kernel32

          user32 нужен только для GUI



      1. 8street
        24.09.2021 12:38
        +1

        Возможно, puts будет ещё меньше. Но там форматирования нет, надо ручками.


        1. tyomitch
          24.09.2021 14:19
          +1

          Не будет: хоть то хоть другое втаскивает msvcrt


      1. Z80A
        24.09.2021 17:29

        Почему бы не сразу рвануть в бутлоадер?


        1. tyomitch
          24.09.2021 17:39
          +2

          Версия, более актуальная для 21 века: habr.com/ru/company/ruvds/blog/571624
          Переписать её на ассемблере несложно, но делать этого я, конечно, не буду.


      1. pvvv
        24.09.2021 21:13
        +2

        .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МБ памяти.


  1. trak
    23.09.2021 19:47
    +5

    Запахло перфокартами и перфолентами. Всплакнул.


    1. Nurked Автор
      23.09.2021 20:17
      +2

      Ах, к сожалению, перфокарты мне приходилось использовать как закладки и как бумагу для заметок. В живую я их не делал.

      Но если вам так уж всплакнулось, то рекомендую послушать альбом замечательного композитора Йохана Йохансонна (который писал музыку к Прибытию). Альбом называется "IBM 1401 инструкция пользователя"

      https://www.youtube.com/watch?v=lCiUtRnG-bg


  1. perfect_genius
    23.09.2021 19:58
    +1

    линкер должен называться компоновщиком, но мне это непривычно

    Также вам непривычно "скачать", да? "Слить" — сейчас это про утечки.


    1. Nurked Автор
      23.09.2021 20:19

      Если честно, то глагол который я использую чаще всего в этом случае это "get". Как "получить" я его перевести не смогу, это будет звучать наравне с "потрачено". Приходится придумывать, и не всегда мои придумывания приводят к правильным ответам.


  1. raamid
    23.09.2021 20:24
    +1

    1. Он всё ещё живой и люди им пользуются. Существует масса иснтрументов разработки для всех ОС. Вот, например, ассемблер для новых маковских чипов М1. А здесь можно слить более 5000 страниц документации по процессорам Intel. Ну а если у вас завалялась где-то Raspberry Pi (а у кого она не завалялась?), то вам сюда.

    Похоже ссылки не работают.


    1. Nurked Автор
      23.09.2021 20:30

      Починил! Не гневите! 8-)


      1. Firsto
        24.09.2021 05:47

        И всё же нету ссылок. :-)


        1. Nurked Автор
          24.09.2021 07:14
          +1

          Починено.

          Может у вас — кеш-агрессор?


  1. voidptr0
    23.09.2021 21:26
    +2

    Когда моя мини-программа которая много читала из файлов на диске, что-то с ними делала и опять записывала на диск (ну вот такая "сложная" программа для нескольких файлов в 10к) заработала, я был готов к ее оптимизации. Но время исполнения в 14 мс на 5600X убило идею в зародыше. Конечно я понимаю, что криворуких кодеров, которые пишут страшный код для чтения JSON в 10 Мб хватает, но для современных процессоров x86... Мне страшно это говорить в слух, но в большинстве случаев оптимизация не нужна, тем более на ассемблере.

    А так, да - весело, проностальгировал, вспомнил программирование TSR-программ под DOS.


    1. Nurked Автор
      23.09.2021 21:46

      Согласен. Но я бы вот как сформулировал мыслю:

      Современное ПО сильно требует оптимизации. Но не всё, что высокоуровнево - это ад на земле. На самом деле в мире есть приличные высокоуровневые программы. Но, в то же время, существует масса когда, который написан криворуко до ужаса.

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


      1. titbit
        23.09.2021 22:12
        +1

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

        Хочу уточнить, что скорее понимание как и главное почему так устроена архитектура используемой системы дала бы понимание. А ассемблер — это просто инструмент, которым иногда можно и нужно пользоваться.


        1. Nurked Автор
          23.09.2021 22:14
          +1

          Полностью согласен. Кто статью писать будет? Вы или я?


  1. mSnus
    23.09.2021 21:36
    +2

    Читал с ностальгией, но с надеждой никогда к ассемблеру не возвращаться. Хотя, может, в современных IDE все проще и надежнее… нет, никогда, никогда больше! хватит того, что в голове зачем-то хранится всякий jmp FFFF:0000 и int 19


  1. vamal
    23.09.2021 21:39
    +6

    300к для такой игры — это овер-овер-овердофига.
    А регистры нынче большие, туда всё поле влезет. Можно вообще без памяти обойтись. :-)


    1. Nurked Автор
      23.09.2021 21:40

      Согласен, хотелось бы меньше. Но, к сожалению, тут надо будет выяснять что да как делать. Весь этот оверхед это просто использование стандартной библиотеки винды. Если посмотреть на символы в деббагере, то всё то, что идёт комплектом к ExitProcess - это приличное количество функций.


      1. vamal
        23.09.2021 21:46
        +2

        Полагаю, для достижения нормального для такой программы размера, следует всё таки разбраться с линкером и дёргать winapi вместо подключения стандартной библиотеки.
        Кстати, FASM классный!


  1. CodeRush
    23.09.2021 21:43
    +6

    %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

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

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

    Понятно, что все это буйство оптимизации разобьется все равно об последующее копирование 16 параметров в стек для printf, но без него статья получается странная, потому что непонятно, зачем оно все, если на С можно написать ровно то же самое, только проще в разы.

    Надо будет написать статью про современное применение ассемблера там, где он реально нужен — в прошивке до инициализации оперативной памяти или cache-as-ram, в ядре ОС для перехода во всякие хитрые режимы исполнения и выхода из них, и т.п.


    1. Nurked Автор
      23.09.2021 21:55
      +1

      Байто**ля... Писец. New achievement unlocked. Согласен. Лучше слова и не придумаешь. Но тут я уже смотрел с точки зрения оптимизации, сколько это будет стоить в циклах. Я так предположил что память-то закешируется и тянуть её из L1 в регистры будет несложно, в таком случае двигание регистров будет дополнительной нагрузкой, которая себя просто не окупит.

      Но, с другой стороны, вы правы. Если мы говорим об уменьшении размера оперативки, то это было бы да - просто считать трёхбитовыми значениями степень, в которую надо возводить. В таком случае всё можно запихнуть в один 64х битный регистр. Но опять-же, у нас на данный момёнт жёсткие потери из-за подгрузки библиотек.

      А по поводу статьи - это вы правы. Посмотрим 8-)


      1. titbit
        23.09.2021 22:01
        +3

        Но тут я уже смотрел с точки зрения оптимизации, сколько это будет стоить в циклах.

        Наверное, если сильно заморочиться можно на одних регистрах написать. Вон, сортировку внутри широченного xmm/ymm делают же, чем тут хуже? :)


        1. Nurked Автор
          23.09.2021 22:17

          2048, v2 извращённая. Убираем поддержку стандартной библиотеки винды, выводим поле опкодами BSOD или как-нить так. Пилим всё аккуратно и сохраняем всё поле в одном регистре. Радуемся футпринту в 2кб.

          Кстати... Я так подумал, а ведь если я запущю всё это на FreeDOS у меня будет возможность добираться до современных регистров и, в то же время, использовать int 21h, что жутко сэкономит память.


          1. titbit
            23.09.2021 22:43

            Я так подумал, а ведь если я запущю всё это на FreeDOS у меня будет возможность добираться до современных регистров и, в то же время, использовать int 21h, что жутко сэкономит память.

            Формально вам понадобится какой-нибудь 64-битный dos-extender, если не хотите сами писать кучу обвязки для перехода в 64-битный режим и вызова оттуда какого-нибудь int 21h. Можно обойтись и 32-битным расширителем, выбор коих намного больше. Но зачем, если сэкономить память можно и в современных windows/linux.

            2048, v2 извращённая.

            Уж если речь зашла про dos, то можно на голом mmu написать, он на x86 вполне тьюринг-полный. Такие извращения только под dos и можно пускать, ну или прямо из первичного загрузчика.


            1. CrashLogger
              24.09.2021 11:07
              +1

              В DOS можно совершенно спокойно использовать 32-х битные регистры без всяких расширителей. Расширитель нужен для адресации памяти за пределами 1Мб.


              1. drWhy
                24.09.2021 20:47
                +1

                Unreal mode позволяет адресовать 4 ГБ.


      1. CodeRush
        23.09.2021 22:39
        +8

        Мерять надо, понятно, но мой собственный палец (это которым «в небо») говорит, что арифметические операции, выполняемые целиком на ALU, выполняются нынче за один такт (на современных х86 после Skylake это так даже для 256- и 512-битовых регистрах), а половина примерно мувов из регистра в регистр — за ноль (потому что там внизу регистровый файл, и переименование фактически бесплатное). L1D на этих же процессорах умеет при идеальных условиях читать две линии по 512 бит за такт, или писать одну, но там если самому с выравниванием не заморочиться хоть немного, можно попасть на границу кэш-линии (и просесть вдвое на ровном месте), или физической страницы (и просесть в ~200 раз), ну и плюс мы тут всяко не одни исполняемся, и из кэша наше игровое поле может вымыть в любой момент…

        Короче, мерять надо все равно, и написать кучу разных вариантов, но обычно те, кто это все умеет и практикует — они уже на работе сыты этим всем по горло. Студентам зато можно дать в качестве задачи со звездочкой.


        1. Nurked Автор
          23.09.2021 22:44

          Снимаю шляпу!

          Переписывать это можно бесконечно. Если посмотрите на исходники - всё это писалось быстро и без уж очень большой оптимизации.


        1. titbit
          23.09.2021 22:50
          +1

          они уже на работе сыты этим всем по горло. Студентам зато можно дать в качестве задачи со звездочкой.

          Насчет сыты по горло — это правда. Это же объясняет, почему так мало статей, а тем более не в корпоративных блогах.
          А насчет задач со зведочкой, я даю такую: сколько доступов в память в худшем случае может быть сделано при выполнении команды nop, включая ее выборку. И сколько (хотя бы примерно) их вообще может быть максимально для одной команды x86.


          1. CodeRush
            24.09.2021 01:33
            +3

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

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

            image


            1. titbit
              24.09.2021 10:07
              +1

              Уточню: команда лежит в обычной кэшируемой оперативной памяти, это 1-байтовый nop, который ни при предвыборке, ни при исполнении не вызвал никаких исключений. Рассматриваем только результат одной команды, никаких крешей, прерываний, dma/rdma/smm и т.п. нет. Ядро при простоты тоже одно.

              Сразу скажу, что число чтений памяти больше одного, число записей (!) тоже больше одного. А есть еще не nop, а более хитрые команды, там все еще хитрее.


              1. CodeRush
                24.09.2021 14:04
                +2

                То, что чтений может быть больше одного — это нетрудно сделать, TLB-промаха достаточно для того, чтобы процессор вынужден был выполнить трансляцию адресов, для которой понадобится одно или несколько обращений к таблице трансляции (в зависимости от того, как именно настроен paging).
                С несколькими записями тоже проблем нет — кладем инструкцию последним байтом страницы, заполняем кэши так, чтобы ничего ни с этой, ни со следующей страницы в них не было, выполняем на эту нашу инструкцию nop неожиданный дальний переход — pipeline flush, cache flush, протокол когерентности заставил сделать write-back как минимум двух вымытых из кэшей линий (которые теперь заменили одна с конца этой страницы, ее запросил декодер, а другая — с начала следующей, эту запросил префетчер).


                1. titbit
                  24.09.2021 14:33
                  +2

                  Я не сомневался, что вы ответите на этот несложный вопрос :)
                  Просто хотел подчеркнуть, что иногда последствия самых вроде безобидных команд могут быть не сразу очевидными. Обычные программисты с такими фокусами вряд ли столкнутся, разве что при очень тщательной оптимизации, а вот системщики — более вероятно. Кроме того, это просто полезно знать, чтобы понимать как устроена не самая простая архитектура x86.

                  p.s. позволю себе еще немного коварства: а если кэша нет (отключен и по коду и по данным, память не кешируемая и т.д.) можно ли получить несколько записей в память? а на каком-нибудь i386sx, где все еще проще можно?


                  1. CodeRush
                    24.09.2021 19:14
                    +2

                    иногда последствия самых вроде безобидных команд могут быть не сразу очевидными
                    Зачастую, эти последствия бывают неочевидными даже для самих разработчиков процессора, целая плеяда микроархитектурных атак вроде Spectre, Meltdown, Foreshadow, RIDL, Fallout и остальных — отличное тому подтверждение. Знать и понимать — надо, конечно, но надо тоже осознавать пределы своего понимания, а то ведь я до сих пор вижу в индустрии людей, которые в своем ассемблерном коде старательно избегают pipeline hazard'ы, которых их нынешний процессор давно уже не боится, а на Pentium Pro их код запускаться точно уже не будет…

                    Про ситуацию с «кэша нет» думать уже не хочу, прошу пардону, потому что и лень, и «так верстают только мудаки (тм)». Можно пойти почитать мануал на тему «кто там в память сбрасывает свой architectural state, и при каких условиях», вспомнить про сегментную адресацию и thread local storage, и прочие разные штуки, но я уже в пижаме и мне уже слишком влом.


                  1. rogoz
                    25.09.2021 16:25
                    +2

                    p.s. позволю себе еще немного коварства: а если кэша нет (отключен и по коду и по данным, память не кешируемая и т.д.) можно ли получить несколько записей в память? а на каком-нибудь i386sx, где все еще проще можно?
                    В таблицах для страничной адресации есть флаг «accessed», на 386 при любой трансляции линейного адреса просматриваются 2 таблицы, в каждой нужно бит установить.


        1. pae174
          23.09.2021 23:04
          +1

          из кэша наше игровое поле может вымыть в любой момент

          На некоторых архитектурах есть cache locking и предзагрузка данных/кода в кэш.


          1. CodeRush
            23.09.2021 23:09
            +2

  1. titbit
    23.09.2021 21:46
    +2

    Более того, благодаря 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.


  1. K_Chicago
    24.09.2021 09:42
    -5

    Давно хотел сказать, ненавижу это вот "погнали" или "поехали".

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

    И вот это омерзительное "доставайте пивко"! Почему бы не семки и "есть чо"?

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

    По моему опыту, любое обучение, освоение технического материала с алкогольной интоксикацией несовместимо чуть более чем абсолютно.

    Пожалуйста, не надо превращать технический блог в фидопойку.


    1. tyomitch
      24.09.2021 12:18
      +3

      Пожалуйста, не надо превращать технический блог в фидопойку.

      Это вы ещё не заметили, что в коде вместо .bss автор опечатался и поставил .bbs :D


    1. dlinyj
      24.09.2021 14:02
      +6

      «Я понял, в чём ваша беда. Вы слишком серьёзны. Умное лицо — ещё не признак ума, господа. Все глупости на Земле делаются именно с этим выражением. Улыбайтесь, господа, улыбайтесь!»


      На самом деле стиль автора — это отсылка ко времени «Хакера», тем кто его не читал, не понять такой формат общения, хотя именно на нём выросли современные специалисты IT в СНГ. И, да, я согласен на счёт алкоголя, но данный стиль — это просто добрые воспоминания.


      1. K_Chicago
        25.09.2021 05:17
        -2

        Приведённая цитата приличествует барону. Кухаркиным детям она не приличествует.

        В этом проблема. Все считают себя баронами.


        1. dlinyj
          25.09.2021 13:02
          +1

          Интересно, к кому причисляете себя вы, с таким высокомерием и пафосом, а главное серьезным лицом? Можете не отвечать, и так всё ясно.


          1. K_Chicago
            27.09.2021 00:46
            -4

            К потомкам дворян я себя не причисляю.

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

            А вы?


    1. K_Chicago
      25.09.2021 05:16
      -6

      судя по обилию минусующих, очень многие тоскуют по разухабистым 90-м.

      Не удивлюсь. Несчастная Россия!


      1. dlinyj
        25.09.2021 13:08
        +1

        Забавные выводы, каждый видит то, что хочет увидеть и пожалеть других. Хотя про 90-е речи даже не было, как и «Хакера» в те годы.


    1. kuza2000
      27.09.2021 11:09
      +2

      Ох, и не знал, что я подонок и разговариваю на сленге) И "погнали", и "поехали" я использую в своем разговоре...


      1. Nurked Автор
        27.09.2021 18:35
        +2

        Ай-яй-яй! Как же это вы, сударь, не используете речевые обороты, приличествующие достопочтенному бомонду. Нельзя же так-с. В мире ваших вычеслительных машин нет приличной ианеры самовыражения.

        Мне это всё напоминает:

        Щенок! Щенок... не нужен? Дёшево отдам. (Погуглите если не знаете)


  1. DmitrySpb79
    24.09.2021 10:31
    +3

    Хе-хе, начали с подсчета полубайт, а в итоге все равно получилось 2.5Мб :) Если бы написать на чистом Си, думаю было бы не сильно больше, но зато 100% переносимо, хоть на CP/M запускай. Автору респект, но нет, обратно на ассемблер уже не тянет :)


    1. tyomitch
      24.09.2021 12:25
      +1

      Это потому что автор линкуется с сишной стандартной библиотекой.
      Если переписать на WinAPI, WS сразу снижается до 1.5 МБ


  1. CrashLogger
    24.09.2021 11:14
    +3

    ИМХО не самое полезное применение ассемблера. Вот если эту штуку написать для Arduino или любого другого микроконтроллера с четырьмя кнопками и экраном - будет нагляднее. Под Windows все равно все упирается в системные вызовы, которые нам не подвластны, а на голом железе все в наших руках.


  1. DX168B
    24.09.2021 11:23
    +2

    Ассемблер не пугает. Главное, чтобы мануалы по инструкциям были. В свое время я учил ассемблер для AVR микроконтроллеров. Потом перешёл на Си и С++. И вот однажды мне пришлось на STM32F407 организовать цепочку цифровых обработок одного сигнала и выжать максимум производительности с ядра ARM Cortex-M4. А тут только ассемблер и нужен. Мануал по ассемблеру вышеупомянутого ядра оказался довольно увлекательным. Там очень много интересных инструкций на все случаи жизни. Ну и задачу я свою решил. Не стал писать вставки и ограничился вызовами intrinsic функций библиотеки CMSIS.


  1. Calc
    24.09.2021 14:13
    +2

    cygwin еще не советовали для ленивых?
    Там есть всё, что требуется для таких развлечений. И yasm и yasm и MinGW и линкеры


  1. alliumnsk
    24.09.2021 17:29
    +1

    А на что заменить mov ax,13h; int 10h;?

    4. это вы очень оптимистично так. Для случаев отличных от голого си, сложность вырастает существенно, особенно для управляемых языков.


    1. Z80A
      24.09.2021 17:31
      +2

      А на что заменить mov ax,13h; int 10h;?

      На RegisterClass/CreateWindow


  1. Mike-M
    24.09.2021 22:42
    +2

    Шекспира лучше читать в подлиннике.
    Так и с процессором лучше разговаривать на его родном языке — языке Ассемблера, без дополнительного «переводчика» в виде Си компилятора.
    Был и остаюсь фанатом FASM: х86, AVR, ARM.


    1. K_Chicago
      27.09.2021 01:07
      -1

      Скажите, а вы пробовали читать Шекспира в подлиннике?

      Например, сонет 116:

      Let me not to the marriage of true mindes

      Admit impediments,loue is not loue

      Which alters when it alteration findes,

      Or bends with the remouer to remoue.

      O no,it is an euer fixed marke

      That lookes on tempeſts and is neuer ſhaken;

      It is the ſtar to euery wandring barke,

      Whoſe worths vnknowne,although his higth be taken.

      Lou's not Times foole,though roſie lips and cheeks

      Within his bending ſickles compaſſe come,

      Loue alters not with his breefe houres and weekes,

      But beares it out euen to the edge of doome:   

       If this be error and vpon me proued,    

      I neuer writ,nor no man euer loued.


      1. Mike-M
        27.09.2021 12:47

        Сонеты Шекспира меня не заинтересовали. А вот детективы Сидни Шелдона — да, прочел штук пять в подлиннике.
        Минус Вашему комментарию я не ставил. Но тоже считаю его неуместным, т.к. в той крылатой фразе заложен смысл не про Шекспира, а про native language.


        1. K_Chicago
          28.09.2021 08:20
          -2

          Вы изволили с апломбом заявить "Шекспира лучше читать в подлиннике", я привел пример совершенно нечитаемого подлинника Шекспира, был заминусован ненавистниками...видимо, Шекспира в подлиннике? Или теми, кому "умники" не нравятся, т.е. обычному б....у. Минусующие в этом слове себя узнают.

          Может быть вам следует следить за своими изъяснениями и отвечать за то что вы изволили нам всем подарить публично? Я понимаю, фраза "отвечать за базар" в рунете крайне непопулярна, конечно. В этом и прелесть рунета.

          Я примерно понимаю, что вы хотели сказать "Шекспира лучше читать в подлиннике", это просто желание подтвердить "умным" высказыванием ваше крайне сомнительное утверждение "с процессором лучше разговаривать на его родном языке". Не буду даже впадать в обсуждение этой, IMHO, бредовой фразы, вы безусловно имеете на нее право.

          Просто не привлекайте для обоснования своей личной позиции бессмысленные шексиро-related высказывания, вы тем самым не только демонстрируете свое незнание "Шекспира в подлиннике", но и компрометируете свои высказывания, которые вы пытались приукрасить этим простихоспади Шекспиром.


  1. morgot
    28.09.2021 01:56
    +2

    Есть masm64 от того же hutch (автор замечательного пакета masm32). Да,ехе нужно брать со студии,но тут есть макросы,инклуды , примеры. Также советую посмотреть uasm - masm синтаксис но гораздо больше возможностей ( создание файлов пол разные ос,даже подобие ооп)