--}}
Новая тема
Вы не можете создавать новые темы.
Т.к. вы неавторизованы на сайте. Пожалуйста назовите себя или зарегистрируйтесь.
Список тем

Как обрабатывать прерывания в Borland C под DOS? Вкратце

Жизнь программиста
358
51
С друзьями на NN.RU
В социальных сетях
Поделиться
m1ndst0rm
13.10.2011
Или где найти понятно и подробно - назрела необходимость срочненько написать софт.
vfrc
13.10.2011
последний раз занимался этим в 1993. но в гугле все гуглится вроде.

www.google.com/search?gcx=w&...8F%20borland%20

вот например
it.kgsu.ru/C_ASM/c_asm18.html

если прерывание аппаратное - надо еще по возвращении в контроллере прерываний сбросить флаг.
поскольку DOS - помнить про нереентерабельность
m1ndst0rm
14.10.2011
Вот ту ссылку asm18 почитал, но как-то не сразу понятно.
"До меня не доходит статья - буду читать ещё" :)
m1ndst0rm
14.10.2011
поведение проги, построенной по аналогии с той, что по ссылке (улучшение кода С с помощью Асма - Прерывания).
Если в качестве своей функции, в которую попадёт прерывание, использовать.. ну напримр вывод текста - то прерывание завесит комп и выведет абракадабру.

Переделал - функция просто выводит числа в столбик (цикл for, в цикле printf) - уходит в ребут.

Нельзя использовать другие прерывания при вызове ещё прерываний? Ведь ввод/вывод консольный используют прерывания - они же друг другу не должны мешать..

Короче, странно. Ничего толком не поменялось, а поведение системы отличается.

код:
*******************************************************************************
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
#include <string.h>

void interrupt (far *oldVector) (...); // Глобальная переменная для первоначального вектора прерывания 0x1c
// Прототипы функций
void InitInterrupt(void);
void ActionFunc(void);
void DeinitInterrupt(void);
void interrupt WhatToDo(...); // **Что это за точки и зачем они тут?**

void main(void)
{
//setcbrk(0); // Предотвращение завершения программы с "Ctrl+C" - **нахрен не надо отключать - я закомментил**
InitInterrupt();
ActionFunc();
DeinitInterrupt();
}

//Перенаправляет вектор прерывания на нашу процедуру обработки
void InitInterrupt(void)
{ oldVector = getvect(0x1c); //Save current vector
setvect(0x1c, WhatToDo); // Set vector to функция, вызываемая по прерыванию
}


void ActionFunc(void)
{
unsigned int j;
for(j=0;j<1000;j++) printf("%u \n",j)
}

// Восстанавливает первоначальный вектор перед завершением программы
void DeinitInterrupt(void)
{ setvect(0x1c, oldVector);
}

//Обработчик прерывания 0x1c. Пишет абстрактную фигню - просто показать что работает
void interrupt WhatToDo(...)
{
printf("\n Generated via interrupt");
};
*******************************************************************************

Что не так?
Qavai
14.10.2011
Ага =)
При обработке низкоуровневых прерываний НЕЛЬЗЯ (или осторожно и правильно) использовать ничего другого высокоуровнего, потому что это может всё повесить, это называется типа повторный вход, уже забыл как это называется... какой-то "... DOS".
m1ndst0rm
14.10.2011
non reenterable :)
Значит атм в обработчике не зря всё на Асме было... :(
reentrant. всё можно. asm ("cli") / asm ("sti").
Qavai
13.10.2011
Какие именно?

Есть немаскируемое прерывание NMI. Есть маскируемые прерывания. Среди них есть аппаратные, есть программные.

Если вкратце, то нужно сохранить старый обработчик, а потом прописать дальний вызов вида CD x x x x в таблицу прерываний (это дальний адрес сегмент:смещение). В этом обработчике нужно вызвать старый обработчик, а затем сохранить все регистры, включая флаги - команды есть pushf, pusha.

Обычно это делается так:


int_16h_entry:
pushf
;... проверки
popf
db 234
old_16_int dd 0

Если же старый обработчик вызывать в начале, то при возврате нужно обязательно вызывать iret - это ret + popf.

Для ДОС сохранить прежний обработчик и задать можно функциями:

mov ax,3509h
int 21h
mov wptr [old_9],bx
mov wptr [old_9+2],es

mov ah,25h
mov dx,ofs int_09h_entry
int 21h


Запретить маскируемые прерывания можно установкой соотв. бита в порту 21h и командой cli:

proc off_ints
in al,21h
in al,21h
mov bptr [cs:old_21_port],al
mov al,255
out 21h,al
cli
ret
endp

Но только не забыть потом разрешить прерывания, иначе всё зависнет =)

proc on_ints
jmp $+2
mov al,0 ;#
old_21_port_e:
out 21h,al
sti
ret
endp

Было дело, подключал к процессору 486 к ноге NMI клавишу, создал программку, которая перехватывает NMI (int 2) и таким макаром останавливал любую программу, которая даже запрещала все прерывания (-: Было весело тогда...
m1ndst0rm
14.10.2011
Это же Асм?
Блин.. пока непонятно как сделать непосредственно - будем гуглить книжки.
Учитывая, что я не программист :)
Спасибо, буду пытаться.

ЗЫ Если кратенькое - есть внешнее устройство, пока состоящее из ПЛИС. Оно может выставлять два прерывания по шине ISA - 3 (йа приняло данные извне - прочитай их) и 4 (пора отвечать вовне - запиши в меня данные и я отправлю). В ПЛИС зашита логика приёмопередатчика одного интерфейса, система управления FIFO на приём (хранит 15 слов - больше не получилось - ПЛИС кончилась, требует чтения "под нагрузкой" примерно раз в 300 мкс), на отправку буфера нет - надо писать с большой скоростью (не реже раз в 20 мкс) подряд (поэтому под ДОС).

Вот в нужный момент это устройство выставляет одно из двух прерываний - на чтение или на запись. Их надо ловить и адекватно реагировать. Задача пока такая.
Qavai
14.10.2011
Для шины ISA с большой вероятностью есть готовые библиотеки для работы - там будет не только простая обработка прерывания, которая пишется за пару минут, но и алгоритм транспорта.
m1ndst0rm
14.10.2011
*******************************************************************************
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
#include <string.h>

void interrupt (far *oldVector) (...); // Глобальная переменная для первоначального вектора прерывания 0x1c
// Прототипы функций
void InitInterrupt(void);
void ActionFunc(void);
void DeinitInterrupt(void);
void interrupt WhatToDo(...); // **Что это за точки и зачем они тут?**

void main(void)
{
//setcbrk(0); // Предотвращение завершения программы с "Ctrl+C" - **нахрен не надо отключать - я закомментил**
InitInterrupt();
ActionFunc();
DeinitInterrupt();
}

//Перенаправляет вектор прерывания на нашу процедуру обработки
void InitInterrupt(void)
{ oldVector = getvect(0x1c); //Save current vector
setvect(0x1c, WhatToDo); // Set vector to функция, вызываемая по прерыванию
}


void ActionFunc(void)
{
unsigned int j;
for(j=0;j<1000;j++) printf("%u \n",j)
}

// Восстанавливает первоначальный вектор перед завершением программы
void DeinitInterrupt(void)
{ setvect(0x1c, oldVector);
}

//Обработчик прерывания 0x1c. Пишет абстрактную фигню - просто показать что работает
void interrupt WhatToDo(...)
{
printf("\n Generated via interrupt");
};
*******************************************************************************

Что не так? Либо завешивает комп, либо ребутит. Может printf слишком долгий (cin/cout медленный)?

Гляньте одним глазом хотя бы :)
Qavai
14.10.2011
Драйвера и низкоуровневое нужно писать только на ассемблере, потому что C создаёт много лишних команд. Да у вас в обработчике я не вижу даже сохранение флагов и регистров.
m1ndst0rm
14.10.2011
Не зря в примере всё было на асме :(
Qavai
14.10.2011
Хотя ту я поспешил, просто никогда не использовал С для этого, вероятно. это спец-команды, всё делают сами...

но всё равно повторный вход не отменишь, вообще драйвер делает минимальную обработку данных и передаёт их в буфер, например. А потом уже этот буфер может читать та же прога на С по таймеру и неспеша всё выводить на экран и т.п.
Не обязательно. Вот пример для ТС (переназначение прерывания):
uses Crt, Dos;
var
KbdIntVec : Procedure;
{$F+}
procedure Keyclick; interrupt;
begin
if Port[$60] < $80 then
{ Only click when key is pressed }
begin
Sound(5000);
Delay(1);
Nosound;
end;
inline ($9C); { PUSHF -- Push flags }
{ Call old ISR using saved vector }
KbdIntVec;
end;
{$F-}
begin
{ Insert ISR into keyboard chain }
GetIntVec($9,@KbdIntVec);
SetIntVec($9,Addr(Keyclick));
Keep(0); { Terminate, stay resident }
end.
З.Ы. Писал на Паскале в том числе программу детектирования хардов под DOS (под чистый DOS).
Qavai
04.11.2011
Это потому что указано "interrupt" и используются по сути команды ассемблера.
P.S. Забыл уточнить - вышеприведённый пример взят из справочной системы BP70 :)
m1ndst0rm
14.10.2011
У Ассемблера есть замечательная особенность: в каждой строчке всё понятно. А в целом - практически ничего не понятно :)
Смысл я уловил, Асм даже красив тем, что прост. Но всё остальное надо держать в голове.
пока чтю книжки.
Тогда изучай Форт :)
Одна строчка на асме равна одной строчке на форте.
... Ну не совсем... Но примерно так получается :)))
m1ndst0rm
04.11.2011
Пока С помучаю. Похоже, придётся многое под ДОС делать.
Под DOS и C, и Pascal сгодится.
В помощь...
pascal.sources.ru/rswag.htm
З.Ы. Не в курсе, но думаю, что для C есть нечто аналогичное.
m1ndst0rm
04.11.2011
Я не умею пока библиотеки чужие юзать и прогию.. я максимум - по аналогии с чужим кодом пишу. а так - сам как-то.. :)
Stallone
14.10.2011
Почитайте: Джордейн - Справочник программиста персональных компьютеров типа IBM PC, XT и AT.
Там много примеров как на Асм так и на С.
Stallone писал(а)
Почитайте: Джордейн - Справочник программиста персональных компьютеров типа IBM PC, XT и AT.
Там много примеров как на Асм так и на С.

+1
Очень хорошая книжка!
+2.
А ещё Питер Абель "Ассемблер и программирование для IBM PC"
И Питер Нортон, Ричард Уилтон "IBM PC и PS/2: Руководство по программированию"
Это классика :)))
m1ndst0rm
04.11.2011
Предыдйщим товарищем указанную нашёл - впечатлило. остальные пока не искал - много другой работы было.
m1ndst0rm
14.10.2011
суровая книжка, но очень подробная.
Много ассемблера. Причём в таком формате, словно читатель с рождения на нём разговаривает :)
Ну... Вот ещё примерчик на Асме. driver.asm:
; Пример простейшего драйвера
.286
.model small
.code
dd -1 ; номер драйвера
dw 0A000h ; слово атрибутов
dw offset Strategy ; смещение процедуры стратегии
dw offset Interrupt ; смещение процедуры прерывания
db 'MYDRIVER' ; 8 байт имени устройства,
; дополняем пробелами
;---------- Наши данные ---------
s1 db 'Driver installed', 0Ah, 0Dh, '$' ; сообщение об инициализации
zz1 dw (?) ; смещение \ адрес заголовка
zz2 dw (?) ; сегмент / запроса
;---------- Процедура стратегии -----
Strategy proc far
mov cs:[zz1], bx ; сохраняем адрес заголовка запроса
mov cs:[zz2], es ; в описанных нами переменных
ret ; и выходим
Strategy endp
;---------- Процедура прерывания ----
Interrupt proc far
pusha ; сохраняем все регистры
push es
push ds
pushf
push cs ; DS:=CS
pop ds
; поскольку драйвер простейший, единственная команда, им выполняемая -
; команда инициализации - описана здесь
mov dx, offset s1 ; выводим наше сообщение
mov ah, 9
int 21h
mov ax, cs:[zz2] ; адрес заголовка запроса в ES:BX
mov es, ax
mov bx, cs:[zz1]
mov ax, 100h ; AL и бит 7 в AH = 0 - ошибок нет
; бит 0 в AH = 1 - команда выполнена
mov es:[bx+3], ax ; слово статуса из AX - в заголовок запроса
push cs ; получаем адрес конца драйвера
pop ax
mov es:[bx+16], ax
mov ax, offset zend
mov es:[bx+14], ax ; и тоже возвращаем в заголовок запроса
mov al, 1 ; единственное устройство
mov es:[bx+13], al ; количество устр-в - в заголовок запроса
; конец команды инициализации
popf
pop ds
pop es
popa ; возвращаем регистры
ret ; и выход
Interrupt endp
zend: ; а по этой метке определяем длину драйвера
END
; В реальных драйверах вместо команды инициализации получаем адрес
; заголовка запроса, из него берём код команды, которую необходимо
; выполнить (его в заголовок запроса пишет DOS), по этому коду выбираем
; смещение необходимой процедуры из таблицы смещений (код инициализации=0
; и это первое значение в таблице) и идём в подпрограмму по этому адресу,
; а дальше - как в этом примере.
; Как получить драйвер?
; Турбо Ассемблер Борланд Макро Ассемблер Майкрософт
; TASM DRIVER.ASM MASM DRIVER.ASM
; TLINK DRIVER.OBJ LINK DRIVER.OBJ
; EXE2BIN DRIVER.EXE аналогично
; RENAME DRIVER.BIN DRIVER.SYS
; и можете указывать его в CONFIG.SYS
; Структуру заголовка запроса, коды команд и ошибок умышленно не привожу,
; главное - знать, как сделать, а остальное приложится. :-)
m1ndst0rm
04.11.2011
Спасибо за ответ, разрулил на Сях под Борландом :) Вроде работает, но через минуту после запуска функциональной версии железячники сломали железку :(
А ещё - как-то странно влияет железка и комп (при включении-выключении питания) влияют друг на друга, хотя тольк опо шине объединены, А ещё включение питания железяки выдаёт какие-то символы на компе.
А ещё часто чтоб заработало прерывание необходимо прошуть ПЛИС заново и запустить друое, программное (системный таймер, например) прерывание. Иначе не заработает..

короче, реализовано, но есть косячки. :) Стараемся.
diper
14.10.2011
hints:
getvect(...)
setvect(...)
void interrupt irq_handler(void) { ... }
m1ndst0rm
14.10.2011
Уже плавно подхожу к этому.
Вроде на словах - ничего сложного. а на деле - много тонкостей. но.. Дорогу осилит идущий
m1ndst0rm
14.10.2011
вкратце:
1. Запоминаем вектор прерываний до выполнения обработчика
2. Заменяем на свой
3. Делаем функцию - обработчик прерывания
4. В конце работы обработчика прерываний возвращаем прежний вектор прерываний.
так? :)
diper
14.10.2011
Примерно, но
4. В конце обработчика вызываем старый обработчик
Или вообще не вызываем если прерывания генерит только твоя железка.
Если прерывания генерит не только твоя железка то твой обработчик должен определить твоё оно или нет и если нет то сразу вызвать старый.
m1ndst0rm
14.10.2011
*******************************************************************************
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
#include <string.h>

void interrupt (far *oldVector) (...); // Глобальная переменная для первоначального вектора прерывания 0x1c
// Прототипы функций
void InitInterrupt(void);
void ActionFunc(void);
void DeinitInterrupt(void);
void interrupt WhatToDo(...); // **Что это за точки и зачем они тут?**

void main(void)
{
//setcbrk(0); // Предотвращение завершения программы с "Ctrl+C" - **нахрен не надо отключать - я закомментил**
InitInterrupt();
ActionFunc();
DeinitInterrupt();
}

//Перенаправляет вектор прерывания на нашу процедуру обработки
void InitInterrupt(void)
{ oldVector = getvect(0x1c); //Save current vector
setvect(0x1c, WhatToDo); // Set vector to функция, вызываемая по прерыванию
}


void ActionFunc(void)
{
unsigned int j;
for(j=0;j<1000;j++) printf("%u \n",j)
}

// Восстанавливает первоначальный вектор перед завершением программы
void DeinitInterrupt(void)
{ setvect(0x1c, oldVector);
}

//Обработчик прерывания 0x1c. Пишет абстрактную фигню - просто показать что работает
void interrupt WhatToDo(...)
{
printf("\n Generated via interrupt");
};
*******************************************************************************

Что не так? Либо завешивает комп, либо ребутит. Может printf слишком долгий (cin/cout медленный)?
diper
14.10.2011
В общем случае нельзя из прерываний делать системные вызовы.
Частные случаи нужно рассматривать отдельно.
m1ndst0rm
14.10.2011
Если честно - я не понял смысла вашей фразы.
Что такое тогда системный вызов?
Почему нельзя - в примере так и было сделано, только сам обработчик был на асме
diper
14.10.2011
Вероятность того что printf будет работать из прерывания близка к 0.

Пример настройки обработчика прерываний
www.codenet.ru/progr/cpp/spr/393.php
А зачем юзать printf? Если надо что-то вывести на экран - можно напрямую писать в буфер экрана :) Ну или юзать прерывание int 10h
vfrc
14.10.2011
printf в конце концов делает вызов DOS.

в программе цикл из принтфов, то есть комп уже выводит что-то, уже вызвал дос (21int).

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

как вариант, можно при возникновении прерывания взводить свой внутренний флаг. а из цикла в main опрашивать этот флаг. и оттуда уже делать printf.

можно пробовать делать cprintf, он не вызывает DOS. работать будет, хотя и недолго :-).
но он тоже нереентерабелен.
vfrc
14.10.2011
при очень большом желании можно конечно и ДОС сделать реентерабельной. но это сильно муторно.
нужно отлавливать все входы в int21, потом своппить статические области где DOS хранит все переменные.
.
но проще всего - в обработчике только взводить флаги, а в основной проге их опрашивать и уже что-то делать.


да. чет еще вспомнилось, модель памяти программы надо ставить large.
иначе компилятор может сэкономить на указателях.
diper
14.10.2011
vfrc писал(а)
да. чет еще вспомнилось, модель памяти программы надо ставить large.
иначе компилятор может сэкономить на указателях.
Не подсказывай, дай человеку поковыряться. :)
m1ndst0rm
15.10.2011
Ах вот как `)
diper писал(а)
и если нет то сразу вызвать старый.
Поправка. Слово "вызвать" ассоциируется с командой call, тогда как на адрес старого обработчика нужно сделать обычный jump. Иначе команда call сохранит исходный адрес в стеке и корректного возврата из прерывания по команде iret не произойдёт.
... Ну или предварительно поправить стек :)
diper
04.11.2011
Не факт. По идее компайлер должен корректность вызывать функцию объявленную с interrupt.
Я про переход из нового обработчика прерывания в старый.
Ежу ясно, что вызов новой процедуры прерывания пройдёт корректно, но если из новой переходить в старую - тут только прыгать...

Старая-то процедура никак не объявлена, на неё только указатель сохранён...

З.Ы. НаСИльникам. В Паскале процедура - это функция, не возвращающая в своём имени значение (аналог void-функции).
diper
04.11.2011
Если компилятор путный то видя указатель на interrupt функцию он сделает все как надо, чтобы после iret в старом обработчике возврат произошел в новый обработчик.
Ещё раз - старая функция - системная. Поэтому она никак не объявлена. На неё только сохранён указатель - в виде переменной. Обычно это простое двухсловное значение.

И речь не о том, чтобы возврат проходил в новый обработчик, а о том, чтобы по iret сама обработка прерывания завершалась.

Грубо говоря, перехватываем прерывание int21h. Проверяем регистр AH - если там наша функция, то работаем по ней. Если нет - jump на старый адрес и работает стандартное прерывание, которое завершится нормально. Независимо от того, какой программой оно вызвано. Зачем нам вообще нужно возвращаться в новый обработчик?
Qavai
15.10.2011
Вот есть преобразователь из UART в USB

www.dealextreme.com/p/usb-to-uart-5-pin-cp2102-module-serial-converter-81872

цепляете свои устройства и пишете просто прогу для WIndows, драйвера уже есть.

Есть подобные и для ISA, и для чего угодно.

www.arstech.com/item-USB-2-0-to-ISA-card-ROHS-usb2isar.html
m1ndst0rm
15.10.2011
Занятный девайс :) Почитаем :)
Спасибо!
Был в эпичной теме прошлого века.
m1ndst0rm
05.11.2011
:-P
концепция изменилась - прерывания заработали на С, без Асма, вроде ок, но есть небольшая бага - не всегда начинают работать. Чтоб заработали - надо использовать встроенное прерывание (системный таймер, например), а потом моё снова - тогда будет ловить. А ещё странно что включение-выключение железки, подвешенной на ISA шине снаружи влияет на функциониррование операционной системы.

Использую прерывания IRQ3 + IRQ4 (внешние ISA), зарываю через inport(0x20, 0x20) - типа общее закрытие "очереди прерываний". Системный таймер таким образом запросто закрывается, а мои - не всегда.
Новая тема
Вы не можете создавать новые темы.
Т.к. вы неавторизованы на сайте. Пожалуйста назовите себя или зарегистрируйтесь.
Список тем
Последние темы форумов
Конденсатор Ионистор Производитель: Elna

Ионистор Производитель: Elna America 22 штуки. Цена 250 рубшт. Супер конденсатор Ионисторы 1F*5,5 V ELNA 1 Ф, 5.5 В 1 фарад =...
Цена: 250 руб.

Амперметр цифровой амперметр ЦА-2131 .

Амперметр Прибор - цифровой амперметр ЦА-2131 Отправка в регионы после оплаты В работе не были - НОВЫЕ. Питание 220 вольт ЦА-2131...
Цена: 2 000 руб.

Цифровой мультиметр BENNING MM 1-2

Цифровой мультиметр BENNING MM 1-2 MADE IN GERMANY Отправка в регионы после оплаты В работе не был. Цена 15000 руб. Торга...
Цена: 15 000 руб.

Реостатом РСП является ползунковым реостатом сопротивления,

Реостатом РСП является ползунковым реостатом сопротивления, Отправка в регионы после оплаты. Новый не пользованный. Цена 4000...
Цена: 4 000 руб.

Разработчик .net Profit Search
70000 -
100000 руб.
Неполное среднее образование, стаж работы 3-5 лет, полная занятость
Программист-разработчик Full-Stack ГК "Kolobox"
70000 -
100000 руб.
Высшее образование, стаж работы более 5 лет, полная занятость
Frontend-разработчик Profit Search
40000 -
50000 руб.
Стаж работы 3-5 лет, частичная занятость
Программист 1С НПП ПРО-М
от 110 000 руб.
Высшее образование, стаж работы 3-5 лет, полная занятость