Pull to refresh

Comments 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

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

UFO landed and left these words here

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

UFO landed and left these words here

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

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

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

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

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

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

UFO landed and left these words here

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

UFO landed and left these words here

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

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

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

UFO landed and left these words here

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

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

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

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

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

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

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

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

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

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

😊

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

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

Строго говоря, 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