Обновить

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

Было интересно, спасибо. Понастольгировал по ассемблеру и 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, через который можно делать разные операции: вывод на экран, чтение с клавиатуры, работу с файлами, управление памятью, завершение программы с кодом возврата.

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

Ну можно было тогда вообще ограничиться вульгарным RETN. Для СОМ-файла на вершину стека при запуске помещается ноль, JMP short CS:0 отправит на начало PSP, где как раз лежит то самое INT 20h.

А копирование CS в DS так и вовсе лишнее, потому как на старте СОМ-файла все сегментные регистры получают одно и то же значение. А если на это не закладываться (мало ли в каком DOSe будем запускать, вдруг он неаккуратный) - всё равно можно было упростить себе жизнь использованием пары команд PUSH CS; POP DS;.

Но цель-то статьи, скорее всего - разъяснение, как построить точный опкод конкретной команды.

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

.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

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

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

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

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

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

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

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

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

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

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

НЛО прилетело и опубликовало эту надпись здесь

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

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

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

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

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

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

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

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

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

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

😊

Браво! 👏

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

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

Строго говоря, 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 игр. Если Вы хотите что-то подсказать, чтобы Вам хотелось бы увидеть, то я с радостью прислушаюсь

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

Публикации