![]() |
1. Заголовок или название темы должно быть информативным
2. Все тексты программ должны помещаться в теги [CODE=asm] [/CODE]
3. Прежде чем задавать вопрос, см. "FAQ",если там не нашли ответа, воспользуйтесь ПОИСКОМ, возможно, такую задачу уже решали!
4. Не предлагайте свои решения на других языках, кроме Ассемблера. Исключение только с согласия модератора.
5. НЕ используйте форум для личного общения! Все, что не относиться к обсуждению темы - на PM!
6. Проверяйте программы перед тем, как выложить их на форум!!
![]() ![]() |
![]() |
BlackShadow |
![]() ![]()
Сообщение
#1
|
Гость ![]() |
От нечего делать опишу основные моменты при программировании на встроенном ассемблере в Паскале.
Есть 2 способа задействовать эту замечательную возможность:
Ну, на самом деле применений целое море, о чём можно убедиться полистав этот раздел форума. Рассмотрим вопрос адресации в реальном режиме (именно он используется "by def" при компиляции в BP). Адрес состоит из двух частей: сегментной части и смещения. Обе части являются 16-ти разрядными двоичными числами или, что на практике и применяется, 4-х разрядными шестнадцатиричными. Рассмотрим пример и на нём разберёмся, что и какая часть значит: Пусть сегментная часть (далее Seg) = $ABCD, а смещение (далее Ofs) = $1234. Это означает, что эта пара Seg:Ofs хранит следующий адрес Seg * $10 + Ofs = $ABCD * $10 + $1234 = $ABCD0 + $1234 = $ACF04. Как легко заметить, пользуясь таким способом адресации мы можем указать адрес любой ячейки памяти в пределах первого мегабайта ($00000..$FFFFF) и даже чуть-чуть больше, но это не имеет значения, т.к. процессор в реальном режиме даёт доступ только к первому МБ. Возникает естественный вопрос: а зачем нужен этот геморрой, и почему нельзя просто указывать полный адрес? А вот нельзя. А потому, что процессор при работе с памятью опирается на информацию, которая хранится в его регистрах, а т.к. мы используем 16-битный вариант команд, то, соответственно, в 1 регистр более 16 бит (4 16-ричные цифры) не впихнуть. Поэтому и приходится использовать 2 регистра: сегментный и какой-нибудь, который можно использовать для адресации. Рассмотрим предназначение регистров процессора:
Теперь давайте рассмотри примитивный набор команд. mov dst, src копирует значение src в dst. Есть один важный момент: командой mov нельзя скопировать значение одной переменной в другую за один приём. Примеры: (Показать/Скрыть)
inc p увеличивает на 1 значение операнда. После компиляции эта команда занимает меньше места чем команда прибавления единицы. Примеры: (Показать/Скрыть)
dec p соответственно уменьшает операнд на 1. add dst, src прибавляет к src значение dst. Результат сохраняется в dst, так что просто число там написать нельзя. Примеры: (Показать/Скрыть)
sub dst, src вычитает из dst значение src. mul n умножает значение регистра AL (AX) на n. Если n размером в 1 байт, то происходит следующее: AX = AL * n, если же слово, то старшие 16 бит произведения сохраняются в DX, а младшие в AX, т. е., умножив AX=$1010 на $100 получим в DX $0010 и в AX $1000. Примеры: (Показать/Скрыть)
div n делит значение в AX (DX:AX, как в команде mul) на n. При этом остаток сохраняется в AH (DX), а целая часть от деления в AL (AX). Например: (Показать/Скрыть)
cmp a, b - сравнивает значения a и b и устанавливает флаги процессора в соответствии с результатом сравнения. Например: (Показать/Скрыть)
jmp L - команда безусловного перехода на метку L. То что в BP называется GoTo. Например: (Показать/Скрыть)
j<cc> L - серия команд условного перехода. Тут <cc> определяет условия перехода:
Например: (Показать/Скрыть)
Рассмотрим ещё команду loop L она сравнивает CX с 0 и, если он отличен, то уменьшает его на 1 и делает переход на указанную метку. Пример: (Показать/Скрыть)
Теперь для закрепления сказанного рассмотрим реализацию вычисления факториала:
Стоит объяснить ещё и то, как возвращаются значения функций. Это всё зависит от типа результата: Byte, Char - через AL Word, Integer - через AX LongInt - старшая часть в DX, а младшие 16 бит в AX. Pointer - сегментная часть в DX, смещение - AX. Остальные типы возвращаются более извращённым способом... Так же отмечу, что убрав проверку на <0 можно переписать эту функцию так: Function Factorial(n: Integer): Integer; Assembler; Правда проблема переполнения остаётся, но зато покажите мне компилятор, который стандартное Function Factorial(n: Integer): Integer; скомпилирует вот так вот красиво... |
Dark |
![]()
Сообщение
#2
|
![]() Знаток ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 408 Пол: Мужской Репутация: ![]() ![]() ![]() |
Цитата Рассмотрим ещё команду LOOP <L> она сравнивает CX с 0 и, если он отличен, то уменьшает его на 1 и делает переход на указанную метку. Пример: MOV CX,5 l3: INC AX LOOP l3 {Пять раз увеличит AX на 1} ОШИБКА!!! ![]() LOOP СНАЧАЛА вычитает 1 а ЗАТЕМ только сравнивает с 0, и если результат не нулевой то делает переход на метку, т.е. такой цикл как MOV CX,0 MOV AX,0 l3: INC AX LOOP l3 НЕ игнорируеться, НИЧЕГО НЕ СДЕЛАВ, а выполнится 65536 раз, и в результате у нас окажется в AX 0. Кстати, команда REP, тоже используемая для циклов СНАЧАЛА проверяет CX на 0 А ПОТОМ вычитает 1. Но это команда обработки цепочек, и она не обрабатывает кусок программы (хотя кто мешает написать rep call PodProgram??) т.е. MOV CX,0 MOV AX,1 rep INC AX ничего не даст. Т.е. у нас в AX будет 2, так как один раз он команду обработает Сообщение отредактировано: Dark - 10.05.2004 6:40 -------------------- - Где я?
- Во тьме. - В какой тьме? - Во тьме твоего мозга. |
Dark |
![]()
Сообщение
#3
|
![]() Знаток ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 408 Пол: Мужской Репутация: ![]() ![]() ![]() |
Флаги В архитектуре компьютера существует флаговый регистр FLAGS, занимающий 16 бит (начиная с процессора 80386 этот регистр расширен до 32-х битного EFLAGS, но его младшее слово по прежнему называется FLAGS для совместимости с 8086/80286), и содержащий значения флагов, которые управляются различными командами для индикации состояния операции. Флаги содержат значение до тех пор, пока другая операция не поменяет их состояния. Всего из 16 бит используются 9. Номер бита: 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 ОПИСАНИЕ(Справа налево) :
Содержимое расширенного регистра EFLAGS:
Дополнительные флаги:
Сообщение отредактировано: volvo - 19.01.2009 14:44 -------------------- - Где я?
- Во тьме. - В какой тьме? - Во тьме твоего мозга. |
Dark |
![]()
Сообщение
#4
|
![]() Знаток ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 408 Пол: Мужской Репутация: ![]() ![]() ![]() |
Вот некоторый пример
![]() uses crt; Вопросы? Задавайте. Просьба - протестировать, есть ли вообще глюки. Учтите, положительный ответ он выдает ДО 32768, а потом отрицательный. -------------------- - Где я?
- Во тьме. - В какой тьме? - Во тьме твоего мозга. |
BlackShadow |
![]()
Сообщение
#5
|
Гость ![]() |
Продолжим ликбез
![]() Стек Сейчас я вот возьму и расскажу про стек. Положение стека в памяти определяется парой регистров SS:SP. Причём эта комбинация указывает именно на вершину стека. Что с ним можно делать? В него можно чего-то сохранять и чего-то восстанавливать. Особенностью является то, что элементы стека - 16-битные слова. Для работы с ним используются 2 команды: PUSH <Src>, которая производит следующие действия:
PUSH AX И естественно POP <Dst>, которая
POP AX Так же есть команды PUSHA и POPA, которые сохраняют регистры AX, BX, CX, DX, SI, DI, BP в стеке (PUSHA) или восстанавливают их оттуда (POPA). По своей сути эти команды аналогичны цепочке команд PUSH или POP. Как правило PUSHA и POPA "обрамляют" код на Assembler'е, в котором происходит недопустимое изменение нескольких регистров. Например: написали обработчик прерывания. Допустим от таймера. И в этом обработчике мы совсем не позаботились о сохранении регистров. Выполняется какая-нибудь чудная программа, которая занята какими-нибудь чудными вычислениями, а тут происходит плановое прерывание от таймера, вызывается наш обработчик, сбивает все регистры и возвращает управление к программе. В итоге в штате Колорадо банкомат на мостовую выплюнул $700 ![]() Стоит вспомнить и про команды PUSHF и POPF, которые сохраняют/восстанавливают регистр флагов в/из стека. Например: CMP AX, BX Тут сравниваются значения регистров AX и BX, сохраняются флаги, затем производятся какие-то операции, способные изменить состояние флагов, затем они восстанавливаются вместе с результатом сравнения. Зачем это надо? Как уже можно понять, стек часто используется для сохранения значения регистров. Например, рассмотрим реализацию вложенного цикла:
А теперь следует отметить, что некоторые команды процессора так же используют стек для сохранения регистров. А именно команда CALL <Proc> вызывает процедуру <Proc>. И вот как это происходит:
Есть ещё одно ОЧЕНЬ важное применение стека - это передача параметров в функцию. Возьмём пример: procedure DrawWindow(x1, y1, width, height: Integer;Как передать параметры в такую процедуру? Через регистры, естественно не получится - их просто не хватит. Есть вариант "от MicroSoft'а" написать функцию типа Drawwindow(p: DrawWindowParameters), но это тоже не всегда удобно. В таком случае можно воспользоваться стеком. Так как местоположение стека одинаково для всех функций, то и DrawWindow сможет их оттуда извлечь. Только не командами POP, а при помощи нехитрого трюка. Скопируем в BP значение SP и будем обращаться к параметрам, как[BP+...]. Возникает серия вопросов.
Как закрепление, перепишем функцию Factorial с использованием рекурсии: Function Factorial(n: Integer): Integer; Assembler; Следует отметить, что функция написанная на Паскале автоматически очищает стек от параметров (это достигается путём вставки команды RET n, которая освобождает стек от указанного объёма ненужной инфы). А так же, что, если функция описана типа f(p1, p2, p3), то и при её вызове в стек надо сохранять сначала p1, затем p2 и в последнюю очередь p3. Зачем я это говорю? А потому что в C это всё совсем наоборот: стек чистит не функция, а тот кто её вызвал и параметры сохраняются в обратном порядке. |
BlackShadow |
![]()
Сообщение
#6
|
Гость ![]() |
Голова болит, работа достала... Ну как тут не вспомнить про Assembler?
![]() Строковые команды Из названия можно было бы подумать, что в assembler'е тоже есть что-то "стандартное" типа Length или Copy. А вот и нет. Эти команды просто довольно часто используются для работы со строками или просто с цепочками данных. Но для начала объясню действие ещё 2 команд: LDS/LES <Reg>,<Src>, которые загружают из 4-ёх байтового указателя значения в регистр DS/ES соответственно (сегментную часть указателя) и в указанный регистр вторую часть указателя - смещение. Эти команды довольно удобны, т.к. все указатели Pascal'я имеют именно такую структуру. Вернёмся к строковым командам. Общий принцип таков:
Теперь рассмотрим сами команды. LODSB/LODSW загружает в AL или AX соответственно (последняя буква в названии команд этого типа и обозначает размер операнда: W - слово, B - байт) значение из DS:SI. Для наглядной иллюстрации напишем функцию StrLen Function StrLen(s:PChar):Integer;Assembler; Команда STOSB/STOSW сохраняет значение AL/AX по адресу ES:DI. Пример - функция, обнуляющая строку: Procedure StrNull(s:PChar);Assembler; MOBSB/MOVSW копируют байт/слово из DS:SI в ES:DI. Для примера возьмём процедуру копирования строки: Procedure StrCopy(Dst:PChar;Src:PChar);Assembler; SCASB/CMPSW Сравнивает значение источника с AL/AX. Для примера перепишем StrLen: Function StrLen(s:PChar):Integer;Assembler; CMPSB/CMPSW сравнивают два байта/слова из DS:SI и ES:DI и в соответствии с этим устанавливают флаги. Как пример можно привести сравнение строк, но мне, если честно, откровенно влом сейчас заниматься такими извратами. WARNING!!! Все примеры приведённые тут являются чисто демонстрационными! Никакого практического применения они иметь не должны, т. к. далеко не оптимальны. А вот намного всё упрощают и улучшают префиксы повторений REPxx. Возможные варианты: REP/REPE/REPNE (ну или REP/REPZ/REPNZ, если так кому больше нравится). Команда с таким префиксом выполняется CX раз, если тому не помешает условие. Следующий пример уже можно использовать в жизни: Function StrLen(s:PChar):Integer;Assembler; Можно оптимизировать эту функцию и далее, но при помощи команд, которые я ещё не описывал. Ну и напоследок приведу пример, в котором команда из этого набора используется не для работы со строками: Procedure VsyoNeboVPopugayah;Assembler; Ну вот вроде и всё, что я хотел сказать на эту тему... |
BlackShadow |
![]()
Сообщение
#7
|
Гость ![]() |
Битовые операции Начнём с логико-арифметических операций. NOT <n>: инвертирует <n>. Т. е. было там 11001000, а стало 00110111. Пример:
AND <Dst>,<Src>: выполняет побитовый "and" <Src> и <Dst> и сохраняет результат в <Dst>. Пример: Function NotOdd(n: Integer): Boolean; Assembler; OR <Dst>,<Src>: выполняет побитовый "and" <Src> и <Dst> и сохраняет результат в <Dst>. Пример:
XOR <Dst>,<Src>: как ни странно, но вытворяет "xor" с операндами. Пример:
И последнее из этого разряда, что приходит мне в голову, это TEST <a>,<b>. Выполняет "and" с операндами, устанавливает флаги, но результат НИКУДА не записывается. Довольно удобная команда при работе с битовыми масками, как например байт атрибутов в DOS. Теперь поговорим о сдвигах. SHR/SHL <Dst>,<n>: сдвигает <Dst> вправо/влево на <n> бит. Причём n должно задаваться как конкретное число (в случае использования встроенного ассемблера - при включенном $G+) или регистром CL (можно и при $G-), в котором учитываются только 4 бита (0 .. 15). На освободившееся место записываются 0. Очень удобная команда, т.к. с её помощью очень быстро умножаются/делятся числа на степень двойки, т.к. SHR AX, 3 аналогично делению AX на 16, а SHL AX, 3 равносильно умножению AX на 8. А ещё есть ROR/ROL <Dst>,<n>, которые отличаются от SHR тем, что "ушедшие" биты не "теряются", а записываются на свободное место. Например:
Ну, а теперь доведём функцию StrLen (из предыдущей темы) до ума:
Так будет получше... |
FreeMan |
![]()
Сообщение
#8
|
- ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 480 Пол: Мужской Репутация: ![]() ![]() ![]() |
я читаю. кстати, вот пара процедур включения, выключения клавы
-------------------- бб
|
BlackShadow |
![]()
Сообщение
#9
|
Гость ![]() |
По документации, ты конечно прав...
Но вот только не работает это с под WinBlows ![]() |
BlackShadow |
![]()
Сообщение
#10
|
Гость ![]() |
Теория это хорошо, но перейдём к
Практика использования Довольно редко приходится заменять то, что уже написано разработчиками из Borland'а. Всё-таки там тоже не дураки сидят. Но иногда приходится написать что-нибудь своё... А потом прикинуть и понять, что на Assembler'е это куда симпатичнее выходит. Есть такая команда INT <n>, которая вызывает указанное прерывание. Важно отметить то, что <n> это число и только число. Прерывания - это набор каких-либо функций, которые предоставляются BIOS'ом, DOS'ом и некоторыми дровами. Откуда процессор знает где храниться обработчик какого-нибудь прерывания? Объясняю. Есть такая таблица прерываний. Она начинается с самого начала оперативки (с адреса 00000h) и занимает 1024 байта. Там для каждого прерывания храниться вектор (адрес обработчика). Таким образом команду INT n можно заменить чем-то вроде
Вот только сбиваются ES и AX, чего как правило делать нельзя. Что мы можем получить от прерываний Мы можем получить доступ к функциям BIOS'а. Они доступны через прерывания
Ещё вектора прерываний указывают на некоторые таблицы BIOS'а. Например таблицы параметров FDD, HDD, таблицы символов... И т.д. Есть ещё аппаратные прерывания. Их не вызывают, они сами приходят ![]() Самая крупная и, наверно, многофункциональная, группа прерываний, это прерывания установленные операционной системой. Они располагаются в диапазоне $20-$2F. И вот что они позволяют:
Осталась последняя группа прерываний - те, которые устанавливаются by ПО. Например INT 33h предоставляет возможность работы с мышью, если запущен *Mouse.Com. Точнее это он перехватывает это прерывание и предоставляет вам какие-то возможности. Ну вот, хотел перейти к практике, а уже столько теории впаял... Давайте тогда хотя бы две функции напишем, которые практически ОЧЕНЬ нужны, а создав модуль с ними, мы получаем возможность не подключать CRT только ради них (ну не нравится мне CRT - глючит он, а патчи искать как правило влом):
Как видите, пара строк, а CRT из-за этих двух функций подключать уже не надо. А ведь частенько при экспериментах с графикой нужно что-то типа ReadKey, чтоб паузу сделать и посмотреть, что получается. |
Dark |
![]()
Сообщение
#11
|
![]() Знаток ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 408 Пол: Мужской Репутация: ![]() ![]() ![]() |
Хых, как всегда ты на шаг впереди ;) конеечно, у тебя инет дневной ; )))))
Прерывания. дубль 2 Команда INT прерывает обработку программы, передает управление в DOS или BIOS для определенного действия и затем возвращает управление в прерванную программу для продолжения обработки. Наиболее часто прерывание используется для выполнения операций ввода или вывода. Для выхода из программы на обработку прерывания и для последующего возврата команда INT выполняет следующие действия:
Этот процесс выполняется полностью автоматически. Каждое прерывание зафиксировано за серией схожих задач, например прерывание 10h - работа с видео, 21h - набор DOS функций 13h - работа с винчестером, и т.д. При обращении к прерываниям мы устанавливаем номер вызываемой функции в AH, в остальные же регистры мы помещаем параметры вызываемой функции, которые - как и возвращение результата зависят от спецификации функции. ВЫВОД СТРОКИ НА ЭКРАН (DOS прерывание) Вывод на экран в базовой версии DOS требует определения текстового сообщения в области данных, установки в регистре AH значения 09 (вызов функции вывода на экран) и указания команды DOS INT 21H. В процессе выполнения операции конец сообщения определяется по ограничителю ($), как это показано ниже: var Знак ограничителя "$" можно кодировать непосредственно внутри строки: 'Имя покупателя?$', или прибавлять в процессе обработки. Используя данную операцию, нельзя вывести на экран символ доллара "$". Кроме того, если знак доллара будет отсутствовать в конце строки, то на экран будут выводиться все последующие символы, пока знак "$" не встретиться в памяти. Команда LEA загружает адрес области NAMPRMP в регистр DX для передачи в функцию адреса выводимой информации. Адрес поля STR, загружаемый в DX по команде LEA, является относительным, поэтому для вычисления абсолютного адреса данных DOS складывает значения регистров DS и DX (DS:DX), поэтому в тех случаях, когда данные хранятся не в основном сегменте данных необходимо изменить значение сегмента DS на нужный: Procedure Pr(Str: String); РАСШИРЕННЫЙ ВВОД ДАННЫХ С КЛАВИАТУРЫ: (DOS прерывание) Расширенный ввод - значит с ограничением по количеству символов. Процедура ввода данных с клавиатуры проще, чем вывод на экран. Для ввода, использующего базовую DOS, область ввода требует наличия списка параметров, содержащего поля, которые необходимы при выполнении команды INT. Во-первых, должна быть определена максимальная длина вводимого текста. Это необходимо для предупреждения пользователя звуковым сигналом, если набран слишком длинный текст; символы, превышающие максимальную длину, не принимаются. Во-вторых, в списке параметров должно быть определенное поле, куда команда возвращает действительную длину введенного текста в байтах. Ниже приведен пример, в котором определен список параметров для области ввода. Первый байт содержит максимальную длину вводимых данных. Так как это однобайтовое поле, то возможное максимальное значение его - шест. FF или 255. Второй байт необходим DOS для занесения в него действительного числа введенных символов. Третьим байтом начинается поле, которое будет содержать введенные символы. MAXLEN: byte { Максимальная длина } Для запроса на ввод необходимо поместить в регистр AH номер функции - 10 (шест. 0AH), загрузить адрес списка параметров в регистр DX и выполнить INT 21H: MOV AH, 0AH ; Запрос функции ввода Команда INT ожидает, пока пользователь не введет с клавиатуры текст, проверяя при этом, чтобы число введенных символов не превышало максимального значения, указанного в списке параметров (20 в нашем примере). Для указания конца ввода пользователь нажимает клавишу Return. Код этой клавиши (шест. 0D) также заносится в поле ввода (NAMEFLD в нашем примере). Если, например, пользователь ввел имя BROWN (Return), то список параметров будет содержать информацию: дес.: ¦20¦ 5¦ В¦ R¦ O¦ W¦ N¦ #¦ ¦ ¦ ¦ ¦ ... Во второй байт списка параметров (ACTLEN в нашем примере) команда заносит длину введенного имени - 05. Код Return находится по адресу NAMEFLD + 5. Символ # использован здесь для индикации конца данных, так как шест. 0D не имеет отображаемого символа. Поскольку максимальная длина в 20 символов включает шест. 0D, то действительная длина вводимого текста может быть только 19 символов.
-------------------- - Где я?
- Во тьме. - В какой тьме? - Во тьме твоего мозга. |
BlackShadow |
![]()
Сообщение
#12
|
Гость ![]() |
Я бы посоветовал
Procedure Input(Var s:String);Assembler; |
BlackShadow |
![]()
Сообщение
#13
|
Гость ![]() |
Текстовый ввод/вывод Всем прекрасно известны такие незаменимые вещи как Write/WriteLn и Read/ReadLn. Очень удобные хотя бы потому, что умеют работать с неограниченным количеством параметров, а также умеющие вводить/выводить не только строки, но и числа, включая дробные. Довольно удобно, что ни говори. Но вот есть в них недостаток - ну не умеют они красным по белому. А вот захотелось мне флаг Зимбабве в текстовом режиме изобразить. Что делать? Многие, конечно, сразу вспомнят про CRT. А я напомню, что не люблю его... И про RunTime Error 200 тоже напомню... Ну, это не важно. Суть в том, что я собираюсь показать некоторые функции BIOS'а для работы с экраном в текстовом режиме и показать как их подстроить под Pascal так, чтобы не расстаться с Write'ом, забить на CRT, и получить нормальный текстовый ввод/вывод. Все функции для работы с экраном BIOS предоставляет через INT 10h. Рассмотрим часть функций, которые подходят под тему. Остальные расскажу в другой раз. Итак:
Теперь расскажу как это всё дело всунуть в Write. В модуле DOS описана структура TextRec, экземпляром которой и являются стандартные Input и Output (если кто не знает, то Input и OutPut - это 2 текстовых файла, которые описаны в модуле System и открываются в начале исполнения программы. Функция Write записывает всё в OutPut, если не указать какой другой файл, а Read, соответственно, читает из Input). Этот тип хранит 4 важных для нас поля:
Берём файл OutPut, настраиваем на свои функции и полетели. А функции должны быть такими: OepnFunc и CloseFunc - пустышки, которые просто возвращают 0. Например:
А адреса InOutFunc и FlushFunc можно настроить на вывод, т. к. они всё равно занимаются одним и тем же. Образно функцию вывода можно описать так: Function DoOutPut(Var f: TextRec): Integer; Far; Вроде как и не сложно... Вопрос только в реализации DoWriteChar. Тут я пока оставлю полную свободу ваше фантазии, а свою реализацию покажу как-нибудь потом. Возникает логичный вопрос: тема называется ввод/вывод, так где же ввод? А вот с вводом дела обстоят крайне туго. BIOS не предоставляет никаких возможностей для ввода. Так что для написания красивого ввода приходится разрабатывать порой довольно хитрые функции и подставлять их в Input. Иногда бывает проще заполнить ту область, где будет осуществляться ввод подходящим атрибутом и воспользоваться стандартными средствами, но только НЕ ПОДКЛЮЧАЯ Crt, т. к. он забьёт на установленные там атрибуты и намалюет по-своему. Ну вот и всё на этот раз. В следующий раз, я думаю, что напишу как заменить всё оставшееся, от Crt ![]() Сообщение отредактировано: volvo - 19.01.2009 15:49 |
Dark |
![]()
Сообщение
#14
|
![]() Знаток ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 408 Пол: Мужской Репутация: ![]() ![]() ![]() |
Продолжим
Секция функций для работы с файлами и дисками. Работа с файлами.
Для работы с файлами используются функции прерывания 21h. Все операции связаны с так называемым файловым хендлом (дескриптором) (file handle), 16 битным числом, при помощи которого DOS идентифицирует файл. ---------------------------------------------------------------------------- Открытие файла: 3Dh IN: ah = 3Dh al = режим открытия: биты 2-0: 000 (только чтение), 001 (только запись) или 010 (чтение и запись) DS:DX = указатель на имя файла (указанное в ASCIIZ формате - на конце #0) OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК AX = дескриптор файла, его надо сохранить, иначе дальнейшая работа с файлом невозможна. ---------------------------------------------------------------------------- Закрытие файла: 3Eh IN: ah = 3Eh bx = дескриптор файла OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК ---------------------------------------------------------------------------- Чтение файла: 3Fh IN: ah = 3Fh bx = дескриптор файла cx = количество байт для чтения DS:DX = куда поместить прочитанные данные OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК AX = количество прочитанных байт, если 0 - пытаемся читать из конца файла. ---------------------------------------------------------------------------- Запись в файл: 40h IN: ah = 40h bx = дескриптор файла cx = количество записываемых байт DS:DX = откуда брать данные RETURN: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК AX = количество записанных байт, если не равняется тому, что было в CX - ошибка ---------------------------------------------------------------------------- Создание файла, с усечением существующего до 0 размера: 3Ch IN: ah = 3Ch cl = атрибуты файла bit 0: только чтение bit 1: скрытый bit 2: системный bit 3: метка тома bit 4: директория bit 5: архивный bit 6&7: резервные биты DS:DX = указатель на ASCIIZ имя файла OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК AX = дескриптор файла ---------------------------------------------------------------------------- Удаление файла: 41h IN: ah = 41h DS:DX = указатель на ACIIZ имя OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК ---------------------------------------------------------------------------- Сдвиг указателя: 42h IN: ah = 42h bx = дескриптор файла CX:DX = 32 битный указатель на место сдвига AL: =0 (отсчет от начала файла), =1 (отсчет от текущей позиции), или =2 (отсчет от конца файла) OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК DX:AX = 32 битный указатель на то, где мы сейчас. ---------------------------------------------------------------------------- Получить/Сменить атрибуты файла: 43h IN: ah = 43h DS:DX = указатель на имя файла в ASCIIZ формате al: =0 (вернуть атрибуты в CX) или l=1 (установить атрибуты из CX) OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все идет по плану =) ---------------------------------------------------------------------------- Переименовать файл: 56h IN: ah = 42h DS:DX = указатель на существующее имя ES:DI = указатель на новое имя файла в ACIIZ CL = маска атрибутов OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все ОК При помощи этой функции можно переносить файл в другие каталоги. ---------------------------------------------------------------------------- Получить/установить дату и время последней модификации файла: 57h IN: ah = 57h al: =00 (вернуть время в CX дату в DX) или =01 (установить новое время из CX новую дату из DX) bx = дескриптор файла OUT: CF = 1: Ошибка AX = код ошибки CF = 0: Все идет по плану =)
---------------------------------------------------------------------------- Простейший пример - программка считывает файл, имя которого вводится как параметр программы и выводит его на экран.
-------------------- - Где я?
- Во тьме. - В какой тьме? - Во тьме твоего мозга. |
Dark |
![]()
Сообщение
#15
|
![]() Знаток ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 408 Пол: Мужской Репутация: ![]() ![]() ![]() |
Оки, тады продолжаем
Работа с диском (DOS функции)
------------------------------------------------------------------------- Получить номер текущего диска: 19h IN: ah = 19h OUT: al = номер диска (00h -> A; 01h -> B; 00h -> C и т.д.) ------------------------------------------------------------------------- Изменить номер текущего диска: 0Eh IN: ah = 0Eh dl = новый диск (00h -> A; 01h -> B; 00h -> C и т.д.) OUT: al = номер диска, последнего в системе (00h -> A; 01h -> B; 00h -> C и т.д.) ------------------------------------------------------------------------- Свободное место на диске: 36h IN: ah = 36h dl = номер диска, (00h - текущий, 01h - A 02h - B и т.д.) OUT: ax = FFFF - неправильно задано устройство в dl или ax = число секторов в кластере bx = свободные кластеры cx = размер сектора в байтах dx = общее число кластеров на диске Таким образом, свободное пространство на диске - ax*bx*cx, полный объем - ax*cx*dx ------------------------------------------------------------------------- Создать каталог: 39h IN: ah = 39h ds:dx = указатель на имя создаваемого каталога в ASCIIZ формате (64 символа) OUT: CF = 0: Все ОК AX = не определен CF = 1: ошибка AX = код ошибки ------------------------------------------------------------------------- Удалить каталог: 3Ah IN: ah = 3Ah ds:dx = указатель на путь к удаляемому каталогу в ASCIIZ формате (64 символа) OUT: CF = 0: Все ОК AX = не определен CF = 1: ошибка AX = код ошибки ------------------------------------------------------------------------- Изменить текущий каталог: 3Bh IN: ah = 3Bh ds:dx = указатель на путь к новому каталогу в ASCIIZ формате (64 символа) OUT: CF = 0: Все ОК AX = не определен CF = 1: ошибка AX = код ошибки ------------------------------------------------------------------------- Получить текущий каталог: 47h IN: ah = 47h dl = номер диска ds:si = указатель на путь к новому каталогу в ASCIIZ формате (64 символа) OUT: CF = 0: Все ОК AX = неопределен (или 100h) CF = 1: ошибка AX = код ошибки Поиск файлов Поиск происходит следующим образом:
Область DTA (Data Transfer Area) располагается в PSP (префикс программного сегмента) со смещением 80h от его начала и занимает 128 байт. DTA может хранить две структуры:
Структура DTA:
------------------------------------------------------------------------- Получить адрес DTA: 2Fh IN: ah = 2Fh OUT: ES:BX = адрес DTA ------------------------------------------------------------------------- Установить адрес DTA: 1Ah IN: ah = 1Ah ds:dx = новая область DTA OUT: ------------------------------------------------------------------------- Найти первый файл по шаблону: 4Eh IN: ah = 4Eh cx = Атрибуты файла ds:dx = указатель на имя файла в ASCIIZ (можно применять символы ? и *) OUT: CF = 0: файл найден, информация в DTA CF = 1: Ошибка AX = 2 (файл не найден), 3 (несуществующий путь) или 12h (больше нет файлов) ------------------------------------------------------------------------- Найти следующий файл по шаблону: 4Fh IN: ah = 4Fh DTA заполнена предыдущим вызовом функции 4Eh OUT: CF = 0: файл найден, информация в DTA CF = 1: Ошибка AX = 12h - больше нет файлов ------------------------------------------------------------------------- Пример поиска файла var Таким образом несложно написать редактор наподобии EDIT от MICROSOFT =))) А также, применять для разработки своих вирусов... Сообщение отредактировано: volvo - 19.01.2009 19:42 -------------------- - Где я?
- Во тьме. - В какой тьме? - Во тьме твоего мозга. |
Гость |
![]()
Сообщение
#16
|
Гость ![]() |
1.1.Написать и отладить программу на языке Паскаль 7 с ассемблерной
вставкой рассмотренную в примере 1. Program P_asms; Var x,y:INTEGER; function ALU(XX,YY:INTEGER):Integer;Assembler; asm MOV AX,XX %занести в регистр АХ(приемник) значение регистра ХХ(источник) ADD AX,YY %сложение АХ=АХ+YY end; begin readln(x,y); writeln('rezultat:',ALU(x,y)); readln; end. И комментария к каждой строке ? |
![]() ![]() |
![]() |
Текстовая версия | 10.09.2024 21:04 |