Как создавался редактор ассемблерного кода sasm
Содержание:
- Assembler в авиапроме: Интервью с разработчиком автопилотов на ASM для самолётов и беспилотников
- Annotating code
- Aside: Number formats
- Assembling source code
- Конвенция вызова функций STDCALL (WinApi : Windows 95 — Windows 10).
- Реализация
- Программа 1. Получение данных из командной строки
- Как мыслит процессор
- Как сжать загрузчик для STM8 до размера 8 байт в памяти FLASH
- Как я отказался от вычисления квадратного корня
- Почему следует изучать язык ассемблера?
- Русская идеология
- Создаём исполняемый файл PRG.COM.
Assembler в авиапроме: Интервью с разработчиком автопилотов на ASM для самолётов и беспилотников
Человек веками мечтал о небе. Братья Уилбур и Орвилл Райт, Альберто Сантос-Дюмон и братья Вуазен подарили его людям. И человек с каждым десятилетием поднимался всё выше и выше, увеличивал скорости, манёвренность, предельные перегрузки, преодолевал звуковой барьер, сталкивался раз за разом с труднейшими инженерными вызовами.
Авиация изменила мир — и его современный облик во многом сформировался за счёт быстрых авиасообщений, когда достижение другого континента или удалённой страны перестало быть трудным и опасным путешествием длиной в несколько недель, а то и месяцев.
Но с ростом скоростей и расстояний повысились требования к управляющим системам летательных аппаратов — микроконтроллерам, прошивкам, автопилотам.
Мне повезло познакомиться с отечественным разработчиком Владом Гордеевым, который занимается как разработкой систем дистанционного управления полетом самолётов, так и систем автоматического управления полетом ЛА. Я задал ему несколько вопросов — и оказался на пару часов в мире, где всё решают миллисекунды, такты, алгоритмы и нет права на ошибку.
Annotating code
It’s often useful to annotate a line of code with a comment. I use annotations
to leave notes to myself when I’m trying to understand how some piece of
machine code works.
Let’s consider again the code loaded by the sample script.
The instruction at address calls a subroutine that uses all
the addressing mode variants of the command. Let’s add an annotation
to that line of code to remind ourselves later what its purpose is.
Now whenever we disassemble code that includes the instruction at address
, we will see our annotation.
To remove an annotation, use the command with an address but without a description.
Aside: Number formats
go6502 accepts numbers in multiple formats. In most of the examples we’ve seen
so far, addresses and byte values have been specified in base-16 hexadecimal
format using the prefix.
The following table lists the number-formatting options understood by go6502:
Prefix | Format | Base | Example | Comment |
---|---|---|---|---|
(none) | Decimal | 10 | -151 | See note about hex mode. |
Hexadecimal | 16 | |||
Hexadecimal | 16 | |||
Binary | 2 | |||
Binary | 2 | |||
Decimal | 10 | Useful in hex mode. |
If you prefer to work primarily with hexadecimal numbers, you can change the
«hex mode» setting using the command.
In hex mode, numeric values entered without a prefix are interpreted as
hexadecimal values. However, because hexadecimal numbers include the letters
through , the interpreter is unable to distinguish between a number and
an identifier. So identifiers are not allowed in hex mode.
Assembling source code
go6502 has a built-in cross-assembler. To assemble a file on disk into a raw
binary file containing 6502 machine code, use the command (or
for short).
The command loads the specified source file, assembles it, and
if successful outputs a raw file containing the machine code into the
same directory. It also produces a source map file, which is used to
store (1) the «origin» memory address the machine code should be loaded at,
(2) a list of exported address identifiers, and (3) a mapping between source
code lines and memory addresses.
Once assembled, the binary file and its associated source map can be loaded
into memory using the command.
To be continued…
Конвенция вызова функций STDCALL (WinApi : Windows 95 — Windows 10).
Операционная система Windows содержит набор встроенных функций, которые обеспечивают удобство программирования, обслуживания и работы системы — так называемые WinApi — Windows Application programming interfaces.
Функций огромное количество. Они входят в стандартный пакет Windows любой версии и содержаться в библиотеках *.dll, расположенных в системных директориях Windows (System, System32, SysWOW64 — для 64 битной системы). Например, kernel32.dll содержит огромное количество функций, входящих в т.н. «Ядро операционной системы», например MoveFile — «переместить файл».
Для операционной системы Windows (WinApi) был разработана отдельная конвенция вызова функций. Она включила в себя преимущества PASCAL и С (Си) конвенций.
Конвенция STDCALL (WinApi) имеет следующие особенности.
Параметры загоняются в стек слева направо — снизу вверх, стек очищается вызываемая функция.
К слову можно сказать, что возвращаемое функцией WinApi значение содержиться в 32 битном регистре eax (нам он пока не известен, но это расширенный до 32 бит регистр ax).
Функция (процедура) содержит пять параметров:myFunc (a,b,c,d,e)
;Ассемблерный код:
push e; Пятый параметр — сверху
push d;
push c;
push b;Второй параметр
push a; Первый параметр (самый левый) — снизу
call myFunc
…
;———————————————————————-
;Функция содержит пять параметров:
myFunc:
push bp
mov bp,sp; Создаём стековый кадр. В bp — указатель на стековый кадр, регистр bp использовать нельзя!
e equ ; Последний параметр — сверху ()
d equ
c equ
b equ
a equ
…
;команды, которые могут использовать стек:
mov ax,e ; считать параметр «a» — . Можно и так, но это менее понятно: mov ax,
; Команды CALL при вызове функции, в стек поместили адрес возврата — 2 байта для процедуры типа NEAR (или 4 — для FAR), а потом еще и ВР — 2 байта (push bp — в начале нашей функции)
mov bx,с ; считать параметр «c» — . Можно и так, но это менее понятно: mov bx,
;…ещё команды
…
pop bp
ret 10 ; Из стека дополнительно извлекается 10 байт — стек освобождает вызываемая функция
1 |
;Ассемблерный код: pushe; Пятый параметр — сверху pushd; pushc; pushb;Второй параметр pusha; Первый параметр (самый левый) — снизу callmyFunc … ;———————————————————————- myFunc pushbp movbp,sp; Создаём стековый кадр. В bp — указатель на стековый кадр, регистр bp использовать нельзя! eequbp+12; Последний параметр — сверху () dequbp+10 cequbp+8 bequbp+6 aequbp+4 … ;команды, которые могут использовать стек: movax,e; считать параметр «a» — . Можно и так, но это менее понятно: mov ax, ; Команды CALL при вызове функции, в стек поместили адрес возврата — 2 байта для процедуры типа NEAR (или 4 — для FAR), а потом еще и ВР — 2 байта (push bp — в начале нашей функции) movbx,с; считать параметр «c» — . Можно и так, но это менее понятно: mov bx, ;…ещё команды … popbp ret10; Из стека дополнительно извлекается 10 байт — стек освобождает вызываемая функция |
Реализация
Опыта написания GUI у меня до этого не было, да и C++ я на тот момент не знал (в 1 семестре у нас был C). Решил всему учиться сразу при написании проекта. Много слышал про фреймворк Qt и написание на нем приложений с графическим пользовательским интерфейсом. Его и было решено выбрать для проекта.
Qt — бесплатный кроссплатформенный фреймворк, да и достаточно популярный, что немаловажно при изучении. К тому же в комплекте с ним идет очень удобная среда разработки Qt Creator и замечательная справка Qt Assistant (однако только на английском)
Первые шаги
В первый день почитал немного лекций 2 курса по C++. Затем, так как хотелось побыстрее начать, прочитал первые 100 страниц книги Бланшета, Саммерфилда «Программирование GUI на C++» о программировании с использованием Qt.
Сначала был написан просто текстовый редактор с логом построения и окнами ввода/вывода. Программа умела собирать код, который был в текстовом редакторе и запускать построенную программу, передавая ей ввод и получая её вывод (и для сборки, и для запуска использовались QProcess).
Подсветка синтаксиса
«Какая же IDE может быть без подсветки?» — подумал я и решил прикрутить оную. Qt — очень богатый фреймворк, и даже для этой, с виду не такой распространенной задачи, там было решение — QSyntaxHighlighter. Нужно было только отнаследоваться от него и заимплементить функцию highlightBlock, которая будет вызываться автоматически, когда нужно. Функция подсвечивала синтаксис, проходя по списку пар <регулярное выражение QRegExp, необходимый формат текста (цвет, курсив и т.д.)>. Про регулярные выражения узнал опять же из Qt Assistant — очень удобная вещь, с тех пор применял их не в одной задаче.
Отладчик
По ходу дела я выкладывал первые версии на обозрение однокурсников и получал некоторый фидбек. Одной из самых востребованных и интересных фич был отладчик. И на самом деле, в начале, при написании программ на языке ассемблера, было где наделать ошибок. К тому же для более полного понимания интересно было посмотреть результаты работы отдельных команд (как меняются регистры, как выставляются флаги). Отладчик очень бы помог в этих случаях.
Решено было использовать стандартный консольный GDB, опять же запуская его через QProcess, на вход подавать нужные команды, соответствующие действиям пользователя в интерфейсе, и разбирая теми же регулярками его вывод.
Там были свои заморочки, связанные с дебагом именно кода, который получается из программы на языке ассемблера. Это происходило из-за того, что ассемблер NASM не добавлял полной отладочной информации в исполняемый файл (только информацию по функциям и переменным). Приходилось разбирать его листинги, чтобы, например, понять в каком месте в тексте программы мы сейчас находимся (GDB говорил только место в памяти).
Так или иначе, отладчик был реализован — можно было ходить по программе, ставить брейкпоинты, через некоторое время добавил показ регистров и памяти.
Ещё фичи
Перевел программу на русский — в приложениях на Qt хороший тон не использовать в коде отличный от английского язык. С помощью Qt это делается очень просто: все строки, которые нужно переводить, заключаются в функцию tr (например, tr("string to translate")). Затем Qt Linguist парсит все такие упоминания строк и позволяет перевести их и сохранить в файл с переводом. Этот файл загружается в программе при выборе другого языка.
В редактор добавил возможность делать правильные отступы табом, комментировать участок кода и другие необходимые мелочи. Добавил вкладки (QTabWidget), настройки цветовой цветов, опций сборки. Все достаточно просто, с точки зрения реализации здесь ничего особенного отметить не смогу.
В программу была включена библиотека макросов ввода/вывода io.inc для NASM (она была взята из курса «Архитектура и язык ассемблера» и немного изменена для правильной работы с отладчиком, а также была написана ее x64 версия). Она очень удобна для новичков: когда не знаешь соглашения о вызовах и не можешь вызвать printf, очень удобно вызвать макрос из этой библиотеки. Описание макросов библиотеки находится на сайте программы.
Летом 2014 добавил поддержку x64 и ассемблеров MASM, FASM, GAS (все по аналогии с NASM). Для поддержки нескольких ассемблеров выделил интерфейс Assembler — добавлять новые ассемблеры стало проще.
Программа 1. Получение данных из командной строки
Программа получает данные из командной строки и выводит их в небольшом windows-окне.
.486
.model flat, stdcall
option casemap: none
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib
include /masm32/macros/macros.asm
uselib masm32, comctl32, ws2_32
.data
.code
start:
call GetCommandLine ; результат будет помещен в eax
push 0
push chr$(«Command Line»)
push eax ; текст для вывода берем из eax
push 0
call MessageBox
push 0
call ExitProcess
end start
1 |
.486 .model flat,stdcall option casemapnone includemasm32includewindows.inc includemasm32includeuser32.inc includemasm32includekernel32.inc includelibmasm32libuser32.lib includelibmasm32libkernel32.lib includemasm32macrosmacros.asm uselib masm32,comctl32,ws2_32 .data .code start call GetCommandLine;результатбудетпомещенвeax push push chr$(«Command Line») push eax;текстдлявыводаберемизeax push call MessageBox push call ExitProcess endstart |
Код с вызовом функций можно было бы заменить кодом:
invoke GetCommandLine
invoke MessageBox, 0, eax, chr$(«Command Line»), 0
invoke ExitProcess, 0
1 |
invoke GetCommandLine invoke MessageBox,,eax,chr$(«Command Line»), invoke ExitProcess, |
invoke — это встроенный макрос для упрощения кода, и при компиляции всё это преобразуется в ассемблерные команды.
Т.е. код
invoke MessageBox, 0, eax, chr$(«Command Line»), 0
1 | invoke MessageBox,,eax,chr$(«Command Line»), |
эквивалентен коду
push 0
push chr$(«Command Line»)
push eax
push 0
call MessageBox
1 |
push push chr$(«Command Line») push eax push call MessageBox |
Стек — это удобное место для хранения информации. Чаще всего он используется при вызове функций.
Все WinAPI-функции созданы по соглашению stdcall, то есть, передача аргументов в них производится через стек в обратном порядке. Возвращают эти функции значение в регистре eax.
Как мыслит процессор
Чтобы понять, как работает Ассемблер и почему он работает именно так, нам нужно немного разобраться с внутренним устройством процессора.
Кроме того, что процессор умеет выполнять математические операции, ему нужно где-то хранить промежуточные данные и служебную информацию. Для этого в самом процессоре есть специальные ячейки памяти — их называют регистрами.
Регистры бывают разного вида и назначения: одни служат, чтобы хранить информацию; другие сообщают о состоянии процессора; третьи используются как навигаторы, чтобы процессор знал, куда идти дальше, и так далее. Подробнее — в расхлопе ↓
Какими бывают регистры
Общего назначения. Это 8 регистров, каждый из которых может хранить всего 4 байта информации. Такой регистр можно разделить на 2 или 4 части и работать с ними как с отдельными ячейками.
Указатель команд. В этом регистре хранится только адрес следующей команды, которую должен выполнить процессор. Вручную его изменить нельзя, но можно на него повлиять различными командами переходов и процедур.
Регистр флагов. Флаг — какое-то свойство процессора. Например, если установлен флаг переполнения, значит процессор получил в итоге такое число, которое не помещается в нужную ячейку памяти. Он туда кладёт то, что помещается, и ставит в этот флаг цифру 1. Она — сигнал программисту, что что-то пошло не так.
Флагов в процессоре много, какие-то можно менять вручную, и они будут влиять на вычисления, а какие-то можно просто смотреть и делать выводы. Флаги — как сигнальные лампы на панели приборов в самолёте. Они что-то означают, но только самолёт и пилот знают, что именно.
Сегментные регистры. Нужны были для того, чтобы работать с оперативной памятью и получать доступ к любой ячейке. Сейчас такие регистры имеют по 32 бита, и этого достаточно, чтобы получить 4 гигабайта оперативки. Для программы на Ассемблере этого обычно хватает.
Так вот: всё, с чем работает Ассемблер, — это команды процессора, переменные и регистры.
Здесь нет привычных типов данных — у нас есть только байты памяти, в которых можно хранить что угодно. Даже если вы поместите в ячейку какой-то символ, а потом захотите работать с ним как с числом — у вас получится. А вместо привычных циклов можно просто прыгнуть в нужное место кода.
Как сжать загрузчик для STM8 до размера 8 байт в памяти FLASH
Со времени написания предыдущей статьи ” Как сжать загрузчик для STM8 до размера 18 байт в памяти FLASH” появились две версии загрузчика STM8uLoader . Загрузчик STM8uLoader версии $36 научился передавать управление прикладной программе по любому адресу в памяти RAM без участия хост-программы. Размер 18 байт загрузчика в памяти FLASH не изменился, в области OPTION Bytes размер увеличился до 53 байта (занял все доступное пространство).
В отдельную ветку выделилась версия $0D загрузчика. Основное требование к этой версии: максимально сжать код. На сегодняшний день размер кода во FLASH памяти 8 байт в EEPROM памяти 35 байт.
Как я отказался от вычисления квадратного корня
Из песочницы
Очень часто при цифровой обработке сигналов необходимо вычислить длину вектора, обычно это делается по формуле A=SQRТ(X^2+Y^2). Здесь возвести в квадрат значение не сложно, но операция вычисления квадратного корня не является простой операцией, особенно для микроконтроллеров. Кроме того, алгоритмы вычисления корня выполняются не стабильное время, и для алгоритмов, в которых таких вычислений много, становится сложно прогнозировать время, необходимое для вычислений.
С такой задачей столкнулся и я. О том, как я отказался от процедуры вычисления корня, читайте ниже.
Почему следует изучать язык ассемблера?
В современной практике индустриального программирования языки ассемблера применяются крайне редко. Для разработки низкоуровневых программ практически в большинстве случаев используется язык си, позволяющий достигать тех же целей многократно меньшими затратами труда, причем с такой же, а иногда и большей эффективностью получаемого исполняемого кода (последнее достигается за счет применения оптимизаторов). На ассемблере сейчас реализуются очень специфические участки ядер операционных систем и системных библиотек. Более того, программирование на ассемблере было вытеснено и из такой традиционно ассемблерной области, как программирование микроконтроллеров. Большей частью прошивки для них также пишут на си. Тем не менее программирование на языке ассемблера очень часто применяется при написании программ, использующих возможности процессора, не реализуемые языками высокого уровня, а также при программировании всевозможных нестандартных программистских хитростей. Отдельные ассемблерные модули, как и ассемблерные вставки в текст на других языках, присутствуют и в ядрах операционных систем, и в системных библиотеках того же языка си и других языков высокого уровня. Сегодня едва ли кому придет в голову сумасшедшая мысль писать крупную программу на чистом ассемблере.
Так зачем же тратить время на его изучение? По ряду веских причин, и вот одна из них: ассемблер — это краеугольный камень, на котором покоится все бесконечное пространство программирования, начиная от рождения первого процессора. Каждый физик мечтает разгадать тайну строения вселенной, найти эти загадочные первичные неделимые (низкоуровневые) элементы, из которых она состоит, не удовлетворяясь лишь смутным о том представлением квантовой теории. Ассемблер же и есть та первичная материя, из которой состоит вселенная процессора. Он — тот инструмент, который дает человеку способность мыслить в терминах машинных команд. А подобное умение просто необходимо любому профессиональному программисту, даже если никогда в жизни он не напишет ни единой ассемблерной строчки. Нельзя отрицать того, что невозможно стать математиком, совершенно не имея понятия об элементарной арифметике. На каком бы языке вы ни писали программы, необходимо хотя бы в общих чертах понимать, что конкретно будет делать процессор, исполняя ваше высочайшее повеление. Если такого понимания нет, программист начинает бездумно применять все доступные операции, совершенно не ведая, что на самом деле он творит.
Вообще, профессиональный пользователь компьютера, системный ли администратор, или программист, может позволить себе что-то не знать, но ни в коем случае не может позволить не понимать сути происходящего, как устроена вычислительная система на всех ее уровнях, от электронных логических схем до громоздких прикладных программ. А непонимание чего-то влечет за собой ощущение в глубине подсознания некоей загадочности, непостижимого таинства, происходящего по мановению чьей-то волшебной палочки. Такое ощущение для профессионала недопустимо категорически. Он просто обязан быть уверен вплоть до глубинных слоев подсознания, что то устройство, с которым он имеет дело, ничего волшебного и непознаваемого собой не представляет.
Иными словами, до тех пор пока существуют процессоры, ассемблер будет необходим.
В этом отношении совершенно не важно, какую конкретно архитектуру и язык какого конкретного ассемблера изучать. Зная один язык ассемблера, ты с успехом можешь начать писать на любом другом, потратив лишь некоторое время на изучение справочной информации
Но самое главное в том, что, умея мыслить языком процессора, ты всегда будешь знать, что, для чего, почему и зачем происходит. А это уже не просто уровень программирования мышкой, а путь к созданию программного обеспечения, несущего печать великого мастерства.
Русская идеология
В русском дизассемблере подход прямо противоположный. Вся дизассемблируемая программа представлена как ОБЛАСТЬ ДАННЫХ. И задачей дизассемблера является наоборот выделить область кода. А что останется, то и будет областью данных. И здесь на первый вопрос машины — а с чего начать, где взять первый байт кода? — ответ четкий и однозначный. У любой программы есть точка входа и где находится ее адрес тоже известно. Поэтому первая инструкция проста — бери стартовый адрес. Он указывает на первый байт кода. Следующая инструкция — декодируй первый байт. Далее анализируй. Если команда простая, типа MOV, ADD, значит за ней следующий байт кода. Тогда шагай на него и снова декодируй. Если встретилась команда JMP, тогда инструкция — за нее шагать нельзя. Там еще не ясно что. Но, у команды JMP есть операнд, который четко указывает на область кода. Соответственна и инструкция будет однозначна — бери адрес перехода и продолжай шагать с новой области кода. Если встретилась команда RET, или функция DOS — завершить процесс, тогда остановись и оглянись назад. Возможно, по ходу процесса встречались условные переходы. Они возможно указывают еще на не пройденные области кода. Тогда их надо пройти. А уж когда все адреса кода отработаны, тогда заверши процесс. Настало время представить результат человеку для творческой работы.
Разумеется, по ходу процесса будут встречаться команды CALL вызова процедуры. На первый взгляд ситуация не сложная. Эта команда предполагает возврат на место после нее. А значит запомни адрес вызова и шагай дальше. Однако, на практике не во всех процедурах авторы предусматривают этот возврат. Они, пусть и не часто, но могут завершиться функцией DOS — завершить процесс. А это значит, после такой команды CALL продолжения кода может и не быть. Возможны и другие варианты, например текстовые сообщения, после команды CALL, и только после этого сообщения продолжение кода. По этой причине дизассемблеру дана четкая инструкция — при первой встрече всегда прерви текущую дорогу кода, войди в процедуру и изучи ее по выше описанному алгоритму. А по результату принимай решение. Если есть хотя бы одно нормальное завершение процедуры командой RET, тогда вернись из нее и продолжи предыдущую траекторию кода. Иначе прервись.
В процессе дизассемблирования могут возникать ситуации, на которые заранее инструкций не напишешь. Например, те же команды JMP и CALL могут иметь и неявную адресацию перехода\вызова. Эти адреса могут предварительно загружаться в регистры или размещаться в области данных в виде таблиц. В этих ситуациях уровня машинного декодирования явно недостаточно. Необходимо перейти на уровень смыслового декодирования, чтобы разобраться, например, со структурой таблицы. В таких случаях машине дана инструкция — запроси дополнительную инструкцию у человека. А для этого пометь эту проблемную ситуацию и продолжи дальше, не нарушая принципы описанного выше алгоритма.
Помимо основного процесса есть и попутные, параллельные
Например, не маловажной задачей является выявление OFFSETных меток
Здесь дизассемблеру дана инструкция — при встрече команд обращающихся к памяти, обрати внимание, какой регистр используется для этого. Затем оглянись назад и найди место, где была последняя загрузка в этот регистр, и в этом месте оставь самому себе дополнительную инструкцию — автоофсет
На следующем проходе она будет отработана. Разумеется, машине не всегда удается решить эту задачу, поэтому на втором проходе машине дана инструкция — все места загрузки регистров без автоофсета пометь, как проблемные, в помощь человеку….
Подробнее об интерактивном режиме, распаковке файлов в процессе дизассемблирования можно узнать в пакете программы RD16. Она выставлена в разделе «дополнения к языкам программирования».
Создаём исполняемый файл PRG.COM.
Для достижения нашей цели делаем следующее.
;Строка, после точки с запятой является комментарием
;и не обрабатывается ассемблером
; prg.asm — название файла.
.model tiny ; создаём программу типа СОМ
.code ; начало сегмента кода
org 100h ; начальное значение смещения программы в памяти — 100h
start:
mov ah,9 ; номер функции DOS — в АН
mov dx,offset message ; адрес строки — в DX
int 21h ; вызов т.н. «прерывания» — системной функции DOS
ret ; завершение СОМ-программы
message db «Hello, World!»,0Dh,0Ah,’$’ ; строка для вывода
end start ; конец программы.
1 |
;Строка, после точки с запятой является комментарием .modeltiny; создаём программу типа СОМ .code; начало сегмента кода org100h; начальное значение смещения программы в памяти — 100h start movah,9; номер функции DOS — в АН movdx,offsetmessage; адрес строки — в DX int21h; вызов т.н. «прерывания» — системной функции DOS ret; завершение СОМ-программы messagedb»Hello, World!»,0Dh,0Ah,’$’; строка для вывода endstart; конец программы. |
В папке D:\TASM.2_0\TASM\ находим «батник» ASM-COM.BAT со следующим текстом:
tasm.exe prg.asm
tlink.exe /t /x prg.obj
1 |
tasm.exeprg.asm tlink.exetxprg.obj |
Первая строка — запуск транслятора с названием нашего файла с кодом, расположенного в одной директории с транслятором.
Вторая строка — запуск компилятора с параметрами /t /x и название объектного файла — prg.obj, получившегося в результате выполнения первой команды.
Чтобы посмотреть список всех возможных параметров с пояснениями для файлов tasm.exe и tlink.exe необходимо запустить эти программы без параметров. Если вы сделаете это, не выходя из оболочки NC, то, чтобы просмотреть чистое окно DOS нажмите Ctrl+O, чтобы вернуться в NC, нажмите сочетание клавиш повторно.
После запуска ASM-COM.BAT в этой же директории появится файл prg.com. Запустив его мы увидим сообщение «Hello World!» в окне MS-DOS (при необходимости просмотра, снова применяем Ctrl+O).
Батник ASM-EXE.BAT предназначен для создания исполняемого файла формате *.EXE (предусматривает раздельную сегментацию для кода, данных и стека — наиболее распространённый формат исполняемых файлов DOS).
Батник COMPLEX.BAT предназначен для создания исполняемых файлов из двух файлов кода (названия обязательно должны быть prg.asm, prg1.asm).
Наша первая программа на ассемблере прекрасно работает!