Search
Write a publication
Pull to refresh

Comments 54

Было интересно, спасибо. Понастольгировал по ассемблеру и com файлах.

PS скриншоты такого качества, что ничего не видно.

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

Я, вот, начинал с ассемблера PDP-11, тоже было интересно :)

А статей нет - непорядок...

Это было 30+ лет назад... :)

Юбилей, значит есть повод вспомнить и опубликовать соответствующую статью на Хабре. Эксперты всплакнут, молодое поколение узнает для себя что-то новое

Да, поддерживаю. Спасибо автору за перевод!

За снимками экрана в нормальном качестве пришлось идти в источник. @Sivchenko_translate, поправьте, пожалуйста.

b: b4 4c mov ah,0x4c
d: b0 2a mov al,0x2a

Хм. Можно было бы сэкономить один байт, сказав mov ax, .... А можно было бы и int 20h сказать, без mov-ов...

mov ax, 4C2Ah  ; AH = 4Ch, AL = 2Ah — то же самое, что два mov
int 21h

Тогда почему часто делают два mov?

  • читаемость и семантика;

  • гибкость при изменении кода возврата;

  • оптимизация компилятора не всегда выбирает mov ax, imm16;

  • психология и традиция

Пример гибкости кода возврата:

mov ah, 4Ch        ; функция exit — фиксирована
mov al, [result]   ; код возврата — переменный
int 21h

Пример кода на C:

exit(42);

Вы же можете изменить код возврата, как пример - согласно условиям?!

Различия int 20 и int 21:

int 20 - завершение программы (Terminate). Работает только для .COM-файлов - не подходит для .EXE Требует, чтобы CS указывал на начало программы, если вы изменили CS, то он не сработает. Не позволяет передать код возврата.

int 21 - главный системный вызов DOS, через который можно делать разные операции: вывод на экран, чтение с клавиатуры, работу с файлами, управление памятью, завершение программы с кодом возврата.

А если надо вывести строку со знаком доллара?

.model tiny
.code
org 100h

start:
    mov dx, offset msg      ; адрес строки
    mov si, dx              ; SI = указатель на строку

print_loop:
    lodsb                   ; загружаем байт в AL и увеличиваем SI
    cmp al, '$'             ; это наш особый символ?
    je print_dollar         ; если да — обрабатываем отдельно
    cmp al, 0               ; конец строки?
    je done
    mov dl, al              ; DL = символ
    mov ah, 02h             ; функция: вывод символа
    int 21h
    jmp print_loop

print_dollar:
    mov dl, '$'             ; выводим символ '$'
    mov ah, 02h
    int 21h
    jmp print_loop

done:
    mov ah, 4Ch             ; завершение программы
    int 21h

msg db 'Price: $25.00', 0Dh, 0Ah, 0

end start

Она простая

Это тебе нейронка насобачила? Нет никакого смысла делать отдельную ветку для $

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

А теперь ответ на Ваш вопрос: $ - специальный управляющий символ, терминатор строки

The string must be terminated by a '$' character

А зачем тогда эта избыточность? Вот что могло пойти не так если $ печатать как и все остальные символы?)))

Давайте попроще постараюсь объяснить...

Вы не можете "просто напечатать $", потому что вы не знаете, где он "настоящий", а где - конец строки.

Что сделает DOS:

  1. Начинает выводить: P, r, i, c, e, :, ...

  2. Видит $ и считает, что строка закончилась;

  3. Останавливается;

  4. На экране: "Price:"

Упс.. - строка обрезана, 25.00 и перевод строки - не выводятся. Потому что ah=09h не умеет отличать символ "$" от "конца строки".

$ - специальный управляющий символ

Тем не менее выводите вы его той же самой второй функцией 21го прерывания )

Это не избыточность, а разделение ответственности:

  • ah=09h — для строк с терминатором,

  • ah=02h — для контролируемого вывода символов.

Поэтому я постарался сделать пример попроще...

Раз уж выводите все символы через ah=02h, смысл в отдельной обработке доллара вроде как пропадает.

Для соего примера, я ставил целью не эффективность, а обучение. Пример с веткой, как обучающий приём, чтобы подчеркнуть особую роль символа $ в DOS.

"Улучшенная" версия кода (для "матёрых"):

.model tiny
.code
org 100h

start:
    mov si, offset msg

print_loop:
    lodsb                   ; AL = символ
    cmp al, 0               ; конец строки?
    je done
    mov dl, al              ; DL = символ
    mov ah, 02h             ; вывод символа
    int 21h
    jmp print_loop

done:
    mov ah, 4Ch
    int 21h

msg db 'Price: $25.00', 0Dh, 0Ah, 0

end start

int 29 display char, недокументированная

или прямая запись в видеобуфер B800:0000

Запись в буфер экрана не канает, тк неизвестна текущая позиция "просто так".

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

Челлендж для тех, кто ассемблера боится. Это в начале 90х, помню, все знакомые (причём больше физики, чем программисты) гонялись за книжками по ассемблеру, прерываниям DOS/BIOS и т.п. - типа без этого ты вообще в компьютерах не разбираешься. Сейчас большинство программистов мыслят в терминах своих ЯП/фреймворков, что там снизу - тёмный лес...

я вас не понял, в чем проявляется боязнь ассемблера, но при этом нет боязни писать в HEX? логику не прослеживаю.

тем более изучение i286 ассемблера не имеет никакого практического смысла если вы не собрались писать какой-то проект в DOS. А применение ASM вставок в современном коде например для повышения производительности - это совсем не про MOV AH и INT 21h.

для чего было гоняться за книжками по ассемблеру если был Norton Guides и куча текстовиков с описанием? Удобоваримые книжки стали появляться тогда, когда актуальность уже пропала. Бывший СССР схавал в начале 90-х всё что на западе кушали 15 лет, так что в одном кабинете у вас могли стоять и i286 и Pentium на винде.

Не особо понимаю в чем заключается мышление в терминах ЯП. Человек мыслит категориями представления данных, а не командами ЯП. Вот я знаю пару десятов ЯП - в каких категориях прикажете мне мыслить? Мышление на питоне сильно отличается от C++ при всей когда-то похожести синтаксиса. Поэтому задача всегда решается категориями представления данных, вполне вероятно когда вы осмыслите в каких данных выражена ваша задача, то и выбор ЯП вы будете производить исходя из этого, разве нет?

тем более изучение i286 ассемблера не имеет никакого практического смысла

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

Удобоваримые книжки стали появляться тогда, когда актуальность уже пропала.

У меня бумажная книжка (Джордейн) появилась заметно раньше всяких документов в электронном виде (Ralph Brown's interrupt list и иже с ними).

Вот я знаю пару десятов ЯП - в каких категориях прикажете мне мыслить?

Это нетипичный случай. Я про вариант, когда человек знает только Javascript и хорошо ещё, если может обобщить знания по Vue и React.

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

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

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

Ну я среагировал на эту выдержку:

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

В остальном, не буду спорить, ибо не имею цели ссориться.

спор != ссора ) ну и мы не спорим, а обсуждаем тему.

могу объяснить свой тезис - знакомство с ассемблером имеет практический смысл, так как для вас будут узнаваемы другие материалы, вы прочитаете о том что происходит в реальности. Статья же это некая форма челенжа и скорее интересна тем кто уже знаком с темой и хочет может быть развлечься. Изучать машинный код в таком формате глупость, так как в таком виде никто не программирует, вы не встретите литературу или код в таком же формате, то есть это не нужно никому никогда и нигде. Поэтому если вы хотите гипотетическому явасприптеру показать что такое машинный код и как с этим работать, воспользуйтесь ассемблером, это ничуть не сложнее яваскрипта (как для меня так даже легче, так как нет никаких заумных синтаксисов, просто мнемокод и параметры, из сложного наверное только то, что нет возможности создавать кучу переменных и тем более массивы как таковые, у вас для манипуляции всегда только регистры ЦПУ, а все действия выполняются либо с ними же либо с памятью, собственно всё программирование это перемещение ячеек памяти туда-сюда и арифметические операции над числами, то есть уровень 4-го класса алгебры и первого года информатики).

спор != ссора )

Сразу- да, согласен! 😜😂 ☝️Но это тот случай, где я выражаюсь в стилистику по смыслу, а не в академическую художественность, скорей с оттенками правоприменения🤣🤣🤣

Ой, а чего я это так разошелся… Совсем уже обалдел посреди дня 😇😀👋

Поехали дальше по тексту…

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

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

Поэтому я и прицелился к Вам. Мол чего это Вы нам кайфы ломаете. 🤣😜😀

Далее, да. Согласен с Вами и понял, что статья не тянет на обучалку. И прочее. В этом плане согласен. Нас три с половиной души и вас человек двадцать тридцать, кому есть что поучаствовать в беседе. Тема. Я тут даже подумал, что может автор просто и хотел в паблике обсудить это, просто как пустяк из жизни, как пример для молодых. …. Не знаю. Ну ладно. Не важно. 😇😉

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

Блин я с вами вместе, и ИИ — скоро программировать начну, практиковать…

😊

Но это не имеет ни практического ни развлекательного смысла.

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

Строго говоря, AH сам по себе — это не настоящий регистр, а просто верхние 16 разрядов регистра AX. В свою очередь, AL — это нижние 16 разрядов AX.

И в оригинале и в переводе написано неточно. И AH и AL по 8 бит, а вот AX по 16. Подробнее про регистры X86, например, тут.

Там странная статья, очень странная - много неточностей и начинается со фразы: " Регистры бывают разных типов: AH, AL, AX, EAX, RAX - это все 1 регистр.". Новичкам крышу снесёт, а эксперты её уже "разнесли" - минус 15

Если и ссылаться, то надо ссылаться на документы такого уровня: "Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1, Section 3.4 "Registers""

"Мне понравилась такая идея, и я решил повторить такой опыт, но немного в иной форме – а именно, под 16-разрядной DOS в реальном режиме."
Можно и в нереальном.

Однако, напомнило, как когда-то писал всякую мелочь прямо в hex-редакторе... Как минимум, такое точно писал. Без objdump и т.п., ибо на той дискетке кроме volkov commander ничего из средств разработки не было.
Потом таки купил диск и дальше уже turbo assembler и т.п....

Интересно, спасибо!

Было бы интересно прочитать статью о том, как сделать такую же программу, работающую вовсе без ОС (например, вместо загрузчика). Ведь там не будет каких-то прерываний dos, ведь и dos нет. Только функции BIOS.

Спасибо! Не читал ещë

Это статья не для обучения, а для развлечения тех, кто четверть века назад игрался с ассемблером.

Чтобы погрузится в ассемблер, нужно идти вот сюда. Там рассматривается программирование на ассемблере от Z80, 6502, 68000, 8086, ARM, ARM Thumb, 65816 до 6809, PDP-11, Risc-V, MIPS, TMS9900, SuperH, IBM370, PowerPC. ;)

Так же есть ютуб канал, где эти уроки представлены в формате видеотуториалов.

Далее скопируем CS в DS

Какое-то мучительное получилось копирование. Иногда такое делают без промежуточного регистра

push cs

pop ds

B4 4C B0 2A CD 21 ; exit

Из .com файла можно выйти просто инструкцией ret. Вот объяснение как это работает https://devblogs.microsoft.com/oldnewthing/20200309-00/?p=103547

Да вообще всё слишком сложно сделано. Я ожидал использования int 10h, для такой мелочи DOS не нужен.

Если хочется, чтобы вывод был не на экран, а в стандартный вывод (с возможностью перенаправить в файл), нужен таки 21h

Вспомнил. 90-е, школьные годы. Накодил какую-то штуку минимальную весёлую на ассемблере. А удобно было сразу COM-файлы прямо в Hiew писать (грубо говоря, это такой просмотрщик файлов для ~нортон коммандера) - там сразу переключаешься между байткодом и ассемблером. Так вот, накодил масенькое что-то, позвонил другу однокласснику и по телефону проводному продиктовал байты, он это так же у себя вбил и запустил. Сидите, гогочете :)

Слабенький туториал.

Если под Dos, покажите расчет условных прыжков, либо для loop, чтобы в машкоде их писать.

Если под Win, что-то такое https://wasm.in/blogs/win32-appy-by-hand.330

для x64 https://wasm.in/blogs/hello-world-v-mashinnyx-kodax.683

А вообще сегодня на Хабр зашел за "android app in bare opcodes", но видимо не судьба. На это бы взглянул с удовольствием.

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

Ah, Al это же восьмибитные части AX! Он же сам по себе 16-битный, и, вместе с BX входит в 32-битный EAX. Разве нет?

Ah, Al это же восьмибитные части AX! Он же сам по себе 16-битный

Да.

вместе с BX входит в 32-битный EAX. Разве нет?

Нет. Когда процессоры Intel перешли на 32 бита, регистры общего назначения расширили до 32 бит. AX -> EAX, BX -> EBX и т.д.

BX не объединяли с AX для получения EAX.

Когда переходили на 64 бита, провернули приблизительно такой же трюк. EAX расширили до RAX, EBX до RBX и т.д.

Можно мини рекламу? Сегодня я выпустил самую первую статью на Хабре. О регистрах, во всей красе, расскажу во второй части своей статьи про реверс DOS игр. Если Вы хотите что-то подсказать, чтобы Вам хотелось бы увидеть, то я с радостью прислушаюсь

Sign up to leave a comment.

Articles