Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

RusFAQ.ru: программирование на языке Assembler


Информационный Канал Subscribe.Ru

РАССЫЛКИ ПОРТАЛА RUSFAQ.RU

/ КОМПЬЮТЕРЫ И ПО / Языки программирования / Assembler

Выпуск № 188
от 17.07.2005, 16:10

Администратор:Калашников О.А.
В рассылке:Подписчиков: 247, Экспертов: 15
В номере:Вопросов: 5, Ответов: 11


Вопрос № 23368: Привет, эксперты, это опять LvT. Вопрос №1: Как в масме добавлять свою секцию, а то я хочу кинутть код не в .text, а в .My_Name. Например, в VC есть директива #pragma, т.е. так: #pragma DATA_SEG ("Shared"); А в а...
Вопрос № 23375: Здравствуйте, уважаемые эксперты! Увидел, что при создании exe файла под DOS(на MASM) необходимо явно осуществить связывание регистра DS с сегментом данных программы: mov ax,@data mov ds,ax Без этого в DS вообще не понятно, что лежит....
Вопрос № 23379: Здравствуйте, В ассемблере я пока что новичок, начал изучать с MS DOS, сразу же появилось несколько вопросов: 1. Прерывания. (например int 21), как я понял это функции, то есть набор каких-то команд, программа посылает значения соответствующ...
Вопрос № 23385: Еще один вопрос, касающийся прерываний, хотел узнать как в ассемблере осуществляется следующий механизм: На экране что-то рисуется (псевдографикой), в это время пользователю предлагают ввести строку, в то время пока он вводит эту строчку на экран...
Вопрос № 23397: Вопрос: Как сделать, что бы программа выводила числа, а не ASCII? CSEG segment assume cs:CSEG, ds:CSEG, ss:CSEG org 100h BEGIN: mov cx,12-3 mov al,1 mov bl,1 _m: mov dl,al add al,bl pus...

Вопрос № 23.368
Привет, эксперты, это опять LvT.
Вопрос №1:
Как в масме добавлять свою секцию,
а то я хочу кинутть код не в .text,
а в .My_Name. Например, в VC есть
директива #pragma, т.е. так:
#pragma DATA_SEG ("Shared");
А в асме я писал так:
#pragma CODE_SEG ("My_Name");
Но компилятор кидает 100 сообщений об ошибках,
и, достигнув лимита, вылетает :-(
Так как можно добавить свою секцию?
А то я пишу программу для шифрования
кода других прог по секциям, то есть
для защиты ShareWare.
Если кому интересно, то скину потом сырец.
Заранее благодарен.
Вопрос №2:
Я это пишу уже третий вопрос,
но так и не увидел его в самой рассылке.
Это почему так? Её стали редактировать?
Отправлен: 11.07.2005, 22:15
Вопрос задал: Skarrywizzard (статус: Посетитель)
Всего ответов отправлено: 2

Отвечает: Евгений Иванов
Здравствуйте, Skarrywizzard!
используй файлы DEF.
SEGMENTS
SHARE CLASS 'MyName' SHARED PRELOAD


---------
Что имеем - не храним, потерявши - плачем
Ответ отправил: Евгений Иванов (статус: Профессор)
Отправлен: 12.07.2005, 07:38

Отвечает: Алексей Смуриков
Здравствуйте, Skarrywizzard!
Формат таков:
Syntax: .CODE [name]

Description:

Starts a code segment (with segment name , if given)
and ends the previous segment, if any. Aligns the segment on a
2-byte boundary (.8086, .186, .286) or a 4-byte boundary (.386,
.486). The .MODEL directive must precede this directive.

Segment name is an optional parameter that overrides the
default segment name. If is not specified, the assembler
generates a segment called _TEXT (tiny, small, compact, and flat

models) or _TEXT (medium, large, and huge models).

Ответ отправил: Алексей Смуриков (статус: Студент)
Отправлен: 12.07.2005, 09:21


Вопрос № 23.375
Здравствуйте, уважаемые эксперты!
Увидел, что при создании exe файла под DOS(на MASM) необходимо явно осуществить связывание регистра DS с сегментом данных программы:
mov ax,@data
mov ds,ax
Без этого в DS вообще не понятно, что лежит.

Вопросы =)
1) Откуда при ассемблировании известен физический адрес сегмента данных @data? Ведь никто не знает куда его запихнет операционная система.
Если заглянуть в exe-шник, то видно, что MASM вставил вместо команды "mov ax,@data" байты "B8 00 00", что вообще-то соответствует команде "mov ax,0".
Две сгенерированные программы отличаются только 2 байтами в exe-заголовке(по смещениям 6h и 1Eh).
А в отладчике там стоит именно mov ax,<адрес сегмента данных>!
2) Зачем это надо(явное связывание)? Почему не хватает директивы assume ds:<имя сегмента данных>?
На www.microsoft.com по поводу assume написано: "Enables error checking for register values. After an ASSUME is put into effect, the assembler watches for changes to the values of the given registers.".
Что, по-моему, означает, что он ничего не привязывает, а лишь чего-то проверяет(чего, кстати, не понятно).
В связи с этим напрашивается...
3) Откуда операционная система знает какой сегментный регистр каким адресом инициализировать, если не делать никаких указаний на функциональное назначение сегментов при их определении?
Скажем, все ли правильно в таком объяснении: "Сегмент стека всегда в программе один, все сегменты из разных объектных файлов объединяются. У всех них необходимо явно указывать директиву stack, поэтому адрес того, что нужно положить в SS известен.
В CS вначале кладется сегмент с точкой входа программы, а при вызове процедуры из другого сегмента CS заменяется на адрес сегмента с этой процедурой(адрес возврата кладется в стек). А DS на то и необходимо явно инициализировать, потому что программа не знает какой из множества ее сегментов выступает в качестве сегмента данных.(вопрос 1)"
4) Кстати про стек... Какой нужен его минимальный размер? В отладчике видно, что почему-то даже в процессе 20 прерывания SP уменьшается довольно-таки сильно, то есть для размера стека уже нужен какой-то нижний предел.
И если его не хватит, то SP становится равным FFFFh, то есть начинают портится какие-то чужие данные?

Заранее спасибо за все ответы =)

Приложение:

Отправлен: 11.07.2005, 23:40
Вопрос задал: seerix (статус: Посетитель)
Всего ответов отправлено: 2

Отвечает: Ayl
Здравствуйте, seerix!

EXE-программа, в отличии от COM, может занимать несколько сегментов, причем может иметь несколько сегментов кода, несколько сегментов данных и т.д. Надеюсь, что ты это знаешь.
Теперь по вопросам.
1. При ассемблировании физический адрес сегмента данных неизвестен. В заголовок EXE-программы помещаются все точки программы, в которых идет обращение к физическому адресу сегмента (псевдопеременные @CODE, @DATA и т.п., обращения типа SEG <метка>). Постоянными являются только смещения относительно начала сегмента. Операционная система при загрузке просматривает таблицу привязки сегментов и подставляет реальные значения в эти точки программы. Точнее, значения по указанным адресам изменяются на постоянное число. Потому что при линковке программы все базовые адреса сегментов заполняются определенным образом (например, первый кодовый сегмент равен 0, второй кодовый сегмент - 1000h, сегмент данных - 2000h, сегмент стека - 3000h, второй сегмент данных - 4000h). Пусть ОС грузит программу с сегмента 8000h. Тогда она просто прибавляет разницу между 8000 и 0 (т.е. 8000) ко всем адресам по указанным точкам. Тогда первый кодовый сегмент получит адрес 8000, второй кодовый сегмент - 9000, сегмент данных - A000, сегмент стека - B000, второй сегмент данных - C000.
Это же самое происходит и тогда, когда программа не запускается, а считывается в память для отладки. Поэтому под отладчиком ты видишь правильные адреса сегментов.

2. Assume - это директива компилятора. Она подсказывает компилятору, с помощью какого сегментного регистра лучше всего генерировать доступ к переменным данного сегмента. Допустим, у тебя будут 2 сегмента данных. В первом (DATA_ARRAY) ты разместишь постоянный массив из 65536 байт (больше в этот сегмент ничего уже не влезет), а во втором (DATASEG) - все остальные переменные.

Теперь записываешь команду ASSUME cs:CODESEG, ds:DATASEG, es:DATA_ARRAY, ss:STACKSEG

И когда ты будешь обращаться к простым переменным, обращение компилятором будет производиться через регистр ds, а когда будешь обращаться к массиву - то через es.

Но команда ASSUME НЕ УСТАНАВЛИВАЕТ значения сегментных регистров! Это действие остается на совести программиста! Поэтому если ты вручную не напишешь:
push DATA_ARRAY
pop es

то не сможешь получить доступ к своему массиву.

3. А операционной системе на все наплевать! Она тупо берет значения из заголовка EXE-программы и на основании их модифицирует байты по указанным адресам. Значения регистров CS, IP, SS и SP устанавливаются также из заголовка (точка входа и вершина стека), причем CS и SS заполняются реальными адресами.

Насчет того, что стек всегда один. В принципе, это так. Компилятор объединяет все сегменты стека в один. Но при этом ты сам запросто можешь перенести стек куда угодно. Но в каждый конкретный момент ты можешь работать только с одним сегментом стека. И одним сегментом кода (хотя их всего может быть несколько). А сегментов данных не только может быть несколько, но и одновременно (без перезагрузки сегментных регистров) ты можешь обращаться до 6-ти сегментов (4 сегмента данных: DS, ES, FS, GS; переменные в сегменте кода через CS и переменные в стеке (например, параметры и локальные переменные процедур) через SS).

4. Ну ты сам считай, какой нужен минимальный размер. Если ты стек не используешь или используешь мало (неглубокая вложенность процедур с малым числом параметров, стек только для хранения регистров), то тебе и 256 байт будет более чем достаточно. Если же ты используешь рекурсию, локальные переменные (в т.ч. и массивы), глубокая вложенность процедур, то тебе и всего сегмента может не хватить. Прикинь, насколько сильно ты используешь стек и исходи из этого. Неплохо увеличить твою максимальную прикидку байт на 100-256. Тогда это позволит без проблем пережить аппаратные прерывания (они первоначально используют стек прерванной программы для сохранения точки прерывания и некоторых регистров).

А если стека не хватит, то ты либо завесишь комп, либо получишь виндовое окошко с сообщением: "Программа выполнила недопустимую операцию и будет закрыта"
---------
Трудное - то, что можно сделать немедленно. Невозможное - то, для выполнения чего требуется немного больше времени
Ответ отправил: Ayl (статус: Профессор)
Отправлен: 12.07.2005, 13:01
Оценка за ответ: 5
Комментарий оценки:
Ayl, супер! Ты всегда даешь очень подробные профессиональные ответы. =) 5+ =)

Отвечает: Евгений Иванов
Здравствуйте, seerix!
есть таблица перераспределения. RELOCATION TABLE.
там все адреса этих чисел. чтобы их заменить на настоящие.
то есть, там сегменты по порядку идут. 0, 1, 2 и т.д.
в процессе загрузки программы загрузчик просматривает программу и заменяет соотв сегменты.

---------
Что имеем - не храним, потерявши - плачем
Ответ отправил: Евгений Иванов (статус: Профессор)
Отправлен: 14.07.2005, 22:30


Вопрос № 23.379
Здравствуйте,
В ассемблере я пока что новичок, начал изучать с MS DOS, сразу же появилось несколько вопросов:
1. Прерывания. (например int 21), как я понял это функции, то есть набор каких-то команд, программа посылает значения соответствующих регистров в функцию (например int 21). И в этой функции происходят какие-то действия. Прав ли я? Если да, то хотелось бы узнать как можно увидеть ассемблерный код, того или иного прерывания.
Для примера рассмотрим например int21 (0ah) – ввод строки с клавиатуры, в программе мы указываем максимальную длину строки, если пользователь набирает больше символов чем установленный максимум, происходит вывод звукового сигнала и символы больше не принимаются, можно ли как-то просмотреть этот 0ah и обойти ту часть кода которая выводит звуковой сигнал (пикает speaker)
2. Как я уже сказал изучаю пока что DOS, хотел узнать как в ассемблере од DOS задействовать мышь (как я понял Mouse.com везде стандартный драйвер) или какие надо ставить Breakpoint’ы в работе с DOS программами чтобы отловить нажатие кнопки мыши.
Большое спасибо.

Отправлен: 12.07.2005, 00:38
Вопрос задал: Freshman (статус: Посетитель)
Всего ответов отправлено: 2

Отвечает: Ayl
Здравствуйте, Freshman!

1. Да, прерывания это набор стандартных функций либо BIOS, либо операционной системы. Плюс есть несколько зарезервированных прерываний для драйверов (например, для драйвера EMS), и часть прерываний доступна для пользователя.

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

Обойти часть кода, издающего beep можно либо забив его командой NOP (90h), либо поставив IRET (выход из прерывания) или JMP на команду, идущую за командами пищания.

2. За работу мыши отвечает прерывание 33h. Посмотри описание его функций...
---------
Трудное - то, что можно сделать немедленно. Невозможное - то, для выполнения чего требуется немного больше времени
Ответ отправил: Ayl (статус: Профессор)
Отправлен: 12.07.2005, 13:09
Оценка за ответ: 5

Отвечает: Евгений Иванов
Здравствуйте, Freshman!
мышь - это COM-порт. это прерывания. 0Ch для COM1.
порты есть. это 3FDh - входное значение при поступлении прерывания.

смотри файл. там есть код для обнаружения нажатия на среднюю кнопку .
всё это очень просто.

mov dx,3fdh
in al,dx
mov ah,al
mov dl,0f8h
in al,dx
xchg al,ah
mov dl,0fdh
out dx,al
mov al,ah
call printdec@
..........

proc printdec@
push ax
xor si,si
xor ah,ah
mov cl,100
div cl
movzx dx,ah
call dec1

mov cl,10
div cl
movzx dx,ah
call dec1

inc si
call dec1
mov ax,72ch
stosw
pop ax
ret
endp

proc dec1
add al,48
cmp al,48
jnz dec2
or si,si
jz dec3
dec2:
inc si
mov ah,7
stosw
dec3:
mov ax,dx
ret
endp

Писалось всё это в далёком 1999 году ;)

Удачи!

Прикреплённый файл: Загрузить >>
Срок хранения файла на сервере RusFAQ.ru составляет 14 суток с момента отправки ответа.
---------
Что имеем - не храним, потерявши - плачем

Ответ отправил: Евгений Иванов (статус: Профессор)
Отправлен: 14.07.2005, 22:38


Вопрос № 23.385
Еще один вопрос, касающийся прерываний, хотел узнать как в ассемблере осуществляется следующий механизм:
На экране что-то рисуется (псевдографикой), в это время пользователю предлагают ввести строку, в то время пока он вводит эту строчку на экране продолжает рисоваться картинка. (примерно как в Norton Commander – идет копирование файла, на мониторе отображается строка состояния (движущийся индикатор) и он может прервать операцию нажатием Esc, в ассемблере при вызове прерывания (например обработки клавиатуры), программа останавливаться и ждет, когда пользователь введет строку, а уж потом начинает выполнять программу дальше – как все таки, сделать так что бы все работало «одновременно».
Большое спасибо.
Отправлен: 12.07.2005, 02:59
Вопрос задал: Freshman (статус: Посетитель)
Всего ответов отправлено: 3

Отвечает: Ayl
Здравствуйте, Freshman!

2 варианта.

1. Организуешь примерно такой цикл:

@loop:
mov ah, 1
int 16h ; проверить символ в буфере клавиатуры
jz @@No_Chars ; нет - другие действия

xor ax, ax
int 16h ; получить символ
; сохранить символ в строке
...

@No_Chars:
; другие действия
...
jmp @@loop

2. С помощью прерываний. Перехватываешь прерывания от клавиатуры и от таймера. Выполняешь основной цикл (например, копируешь файл). По прерыванию от клавиатуры обрабатываешь нажатый символ. По прерыванию от таймера делаешь что-нибудь еще (например, выводишь индикатор копирования).

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

Выбирай, что тебе больше нравится. Первый случай реализуется очень просто, второй - сложнее.

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

---------
Трудное - то, что можно сделать немедленно. Невозможное - то, для выполнения чего требуется немного больше времени
Ответ отправил: Ayl (статус: Профессор)
Отправлен: 12.07.2005, 14:18
Оценка за ответ: 5

Отвечает: Евгений Иванов
Здравствуйте, Freshman!
нужно попеременно вызывать тот или иной код.
---------
Что имеем - не храним, потерявши - плачем
Ответ отправил: Евгений Иванов (статус: Профессор)
Отправлен: 12.07.2005, 23:41

Отвечает: Стас
Здравствуйте, Freshman!
На самом деле Ayl все упростил :)
Вариантов не 2, а очень много.
Например ждать нажатия клавиш (mov ah,0 int 16h), а в это время на таймере (int9 или других) должен висеть обработчик прерывания, который и будет "рисовать".
Можно вообще не пользоваться Int-ами организовать все через память и порты.
И еще много вариантов...
Ты сам должен выбрать нужный в зависимости от твоих задач. И возможностей.
Ответ отправил: Стас (статус: Практикант)
Отправлен: 13.07.2005, 02:22


Вопрос № 23.397
Вопрос:
Как сделать, что бы программа
выводила числа, а не ASCII?

CSEG segment
assume cs:CSEG, ds:CSEG, ss:CSEG
org 100h
BEGIN:

mov cx,12-3
mov al,1
mov bl,1
_m:
mov dl,al
add al,bl
push ax
mov ax,0
push dx
mov dl,al
mov ah,2
int 21h
pop dx
pop ax
mov bl,dl
loop _m

int 20h

CSEG ends
end BEGIN

; таблица вывода на экран
; 3 4 5 6 7 8 9 10 11 12
;al=1|2|3|5|8|13|21|34|55|89
;bl=1|1|2|3|5|8 |13|21|34|55
;dl=?|1|2|3|5|8 |13|21|34|55

Отправлен: 12.07.2005, 12:59
Вопрос задал: Терсков Алексей Николаевич (статус: Посетитель)
Всего ответов отправлено: 2

Отвечает: Ayl
Здравствуйте, Терсков Алексей Николаевич!

Написать процедуру вывода числа из регистра на экран. Для этого нужно делить число в регистре на 10 до тех пор, пока в остатке не будет 0. И выводить остатки каждого деления, прибавляя 48 (ASCII-код цифры 0). Только учти, что цифры числа будут генерироваться в обратном порядке.

Либо можешь использовать такую процедуру (выводит на экран в 10-й с.с. 1-байтовое число из регистра AL):

Print_AL proc
; in: AL
xor ah, ah
mov bx, 100 ; BH = 0; BL = 100
div bl ; AH = остаток, AL = частное

or al, al
jz @@Cont1

or al, 30h
int 29h
inc bh ; флаг того, что что-то уже печатали

@Cont1:
mov bl, 10
xchg ah, al
xor ah, ah
div bl

or al, al
or ah, al ; ZF = 0 если и десятки и сотни исходного числа = 0
jz @@Cont2

or al, 30h
int 29h

@Cont2:
xchg ah, al
or al, 30h
int 29h

ret
Print_AL endp

---------
Трудное - то, что можно сделать немедленно. Невозможное - то, для выполнения чего требуется немного больше времени
Ответ отправил: Ayl (статус: Профессор)
Отправлен: 12.07.2005, 14:36

Отвечает: Евгений Иванов
Здравствуйте, Терсков Алексей Николаевич!
прибавляй код нуля (символа) - это "0"=30h.

если нужно вывести числа больше 9, то используй деление на 10 (100,1000 ....)
---------
Что имеем - не храним, потерявши - плачем
Ответ отправил: Евгений Иванов (статус: Профессор)
Отправлен: 14.07.2005, 22:26


Отправить вопрос экспертам этой рассылки

Приложение (если необходимо):

* Код программы, выдержки из закона и т.п. дополнение к вопросу.
Эта информация будет отображена в аналогичном окне как есть.

Обратите внимание!
Вопрос будет отправлен всем экспертам данной рассылки!

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


Форма НЕ работает в почтовых программах The BAT! и MS Outlook (кроме версии 2003+)!
Чтобы отправить вопрос, откройте это письмо в браузере или зайдите на сайт RusFAQ.ru.


© 2001-2005, RusFAQ.ru, Россия, Москва. Все права защищены.
Идея, дизайн, программирование, авторское право: Калашников О.А.

Яндекс


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.faq
Отписаться
Вспомнить пароль

В избранное