Попробуйте использовать функцию DPMI 0204h и 0205h вместо '_dos_getvect' и '_dos_setvect' соответственно.
Средой выполнения вашей программы является DOS32A или сервер/хост DPMI. Поэтому используйте API, который они предоставили, вместо использования средств DOS int21h. Но DOS32A перехватывает прерывания int21h, так что ваш код должен работать нормально, насколько это касается реального режима.
На самом деле вы установили только обработчик прерываний реального режима для IRQ14 с помощью функций '_dos_getvect' и '_dos_setvect'.
Вместо этого, используя функции DPMI, вы устанавливаете обработчик прерывания защищенного режима для IRQ14, и DOS32a будет автоматически передавать прерывание IRQ14 этому обработчику защищенного режима.
Напомним: расширитель dos/сервер DPMI может находиться в защищенном режиме или в реальном режиме, пока установлено IRQ.
Это связано с тем, что ваше приложение использует какой-либо API-интерфейс DOS или BIOS, поэтому расширителю необходимо переключиться в реальный режим, чтобы выполнить их, и вернуться обратно в защищенный режим, чтобы передать управление вашему приложению защищенного режима.
DOS32a делает это, выделяя обратный вызов реального режима (по крайней мере, для аппаратных прерываний), который вызывает ваш обработчик защищенного режима, если установлено IRQ14, когда расширитель находится в реальном режиме.
Если расширитель находится в защищенном режиме, а IRQ14 установлен, он автоматически передаст управление вашему обработчику IRQ14.
Но если вы не установили обработчик защищенного режима для своего IRQ, то DOS32a не будет выделять никаких обратных вызовов реального режима, и ваш обработчик IRQ реального режима может не получить управление. Но он должен получить контроль AFAIK.
В любом случае попробуйте две вышеупомянутые функции. И сделайте цепочку к предыдущему обработчику прерывания int76h, как сказал Шон.
Короче:
В случае DOS32a вам не нужно использовать функции _dos_getvect и _dos_setvect. Вместо этого используйте функции DPMI 0204h и 0205h для установки обработчика IRQ защищенного режима.
Совет: в вашем обработчике прерываний первым шагом должна быть проверка действительно ли ваше устройство генерирует прерывание или это какое-то другое устройство, разделяющее этот irq (IRQ14 в вашем случае). Вы можете сделать это, проверив 'бит ожидания прерывания' на вашем устройстве, если он установлен, выполните обслуживание вашего устройства и цепочку к следующему обработчику. Если он не установлен в 1, просто цепочка к следующему обработчику.
ОТРЕДАКТИРОВАНО: Используйте последнюю версию DOS32a вместо одной это идет с OW.
Обновление от 14 августа 2012 г.:
Да, вы можете использовать макросы FP_SEG и FP_OFF для получения селектора и смещения соответственно, точно так же, как вы использовали бы эти макросы в реальных режимах для получения сегмента и смещения.
Вы также можете использовать макрос MK_FP для создания дальних указателей из селектора и смещения. например. MK_FP(селектор, смещение).
Вы должны объявить свой обработчик прерываний с помощью ключевого слова '__interrupt' при написании обработчиков на C.
Вот фрагмент:
#include <i86.h> /* for FP_OFF, FP_SEG, and MK_FP in OW */
/* C Prototype for your IRQ handler */
void __interrupt __far irqHandler(void);
.
.
.
irq_selector = (unsigned short)FP_SEG( &irqHandler );
irq_offset = (unsigned long)FP_OFF( &irqHandler );
__dpmi_SetVect( intNum, irq_selector, irq_offset );
.
.
.
или попробуйте это:
extern void sendEOItoMaster(void);
# pragma aux sendEOItoMaster = \
"mov al, 0x20" \
"out 0x20, al" \
modify [eax] ;
extern void sendEOItoSlave(void);
# pragma aux sendEOItoSlave = \
"mov al, 0x20" \
"out 0xA0, al" \
modify [eax] ;
unsigned int old76_selector, new76_selector;
unsigned long old76_offset, new76_offset;
volatile int chain = 1; /* Chain to the old handler */
volatile int tick=0; // global and volatile...
void (__interrupt __far *old76Handler)(void) = NULL; // function pointer declaration
void __interrupt __far new76Handler(void) {
tick = 5; // simple code to change the value of tick...
.
.
.
if( chain ){
// disable irqs if enabled above.
_chain_intr( old76Handler ); // 'jumping' to the old handler
// ( *old76Handler )(); // 'calling' the old handler
}else{
sendEOItoMaster();
sendEOItoSlave();
}
}
__dpmi_GetVect( 0x76, &old76_selector, &old76_offset );
old76Handler = ( void (__interrupt __far *)(void) ) MK_FP (old76_selector, old76_offset)
new76_selector = (unsigned int)FP_SEG( &new76Handler );
new76_offset = (unsigned long)FP_OFF( &new76Handler );
__dpmi_SetVect( 0x76, new76_selector, new76_offset );
.
.
ЗАМЕТКА:
Вы должны сначала дважды проверить, что IRQ#, который вы перехватываете, действительно назначен/сопоставлен с выводом прерывания вашего соответствующего устройства PCI. IOW, сначала прочитайте 'Регистр строки прерывания' (НЕ регистр вывода прерывания) из пространства конфигурации PCI и перехватите только этот irq#. Допустимые значения для этого регистра в вашем случае: от 0x00 до 0x0F включительно, где 0x00 означает IRQ0, а 0x01 означает IRQ1 и так далее.
Код POST / BIOS записывает значение в «Регистр линии прерывания» во время загрузки, и вы НЕ ДОЛЖНЫ изменять этот регистр любой ценой (конечно, если вы не имеете дело с проблемами маршрутизации прерываний, с которыми будет иметь дело писатель ОС)
Вы также должны получить и сохранить селектор и смещение старого обработчика, используя вызов DPMI 0204h, на случай, если вы привязываетесь к старому обработчику. Если нет, не забудьте отправить EOI (End-of-interrupt) ОБЕЕМ главному и подчиненному PIC в случае, если вы подключили IRQ, принадлежащий подчиненному PIC (т.е. INT 70h-77h, включая INT 0Ah), и ТОЛЬКО главному PIC в случае, если вы перехватили IRQ, принадлежащий мастеру PIC.
В плоской модели адрес BASE равен 0, а предел равен 0xFFFFF, а бит G (т. е. бит гранулярности) = 1.
База и ограничение (вместе с битами атрибутов (например, бит G) сегмента) находятся в дескрипторе, соответствующем конкретному сегменту. Сам дескриптор находится в таблице дескрипторов.
Таблицы дескрипторов представляют собой массив, каждая запись которого имеет размер 8 байт.
Селектор — это просто указатель (или индекс) на запись 8-байтового дескриптора в таблице дескрипторов (либо GDT, либо LDT). Таким образом, селектор НЕ МОЖЕТ быть 0.
Обратите внимание, что младшие 3 бита 16-битного селектора имеют особое значение, и только старшие 13 бит используются для индексации записи дескриптора из таблицы дескрипторов.
GDT = глобальная таблица дескрипторов
LDT = локальная таблица дескрипторов
В системе может быть только один GDT, но много LDT.
Поскольку номер записи 0 в GDT зарезервирован и не может использоваться. Насколько я знаю, DOS32A не создает никаких LDT для своих приложений, вместо этого он просто выделяет и инициализирует записи дескриптора, соответствующие приложению, в самом GDT.
Селектор НЕ ДОЛЖЕН быть равен 0, так как архитектура x86 считает селектор 0 недопустимым, когда вы пытаетесь получить доступ к памяти с помощью этого селектора; хотя вы можете успешно поместить 0 в любой регистр сегмента, только когда вы пытаетесь получить доступ (чтение/запись/выполнение) к этому сегменту, процессор генерирует исключение.
В случае обработчиков прерываний базовый адрес не обязательно должен быть равен 0, даже в случае плоского режима. Для этого у среды DPMI должны быть веские причины. В конце концов, вам все еще нужно заняться сегментацией на каком-то уровне в архитектуре x86.
PCI device config register 0x5 bit2(Interrupt Disabled) = 0
PCI device config register 0x6 bit3(Interrupt status) = 1
Я думаю, вы имеете в виду регистры команд и состояния Bus master соответственно. На самом деле они находятся либо в пространстве ввода-вывода, либо в пространстве памяти, но НЕ в пространстве конфигурации PCI. Таким образом, вы можете читать/записывать их напрямую через инструкции IN/OUT или MOV.
Для чтения/записи регистров конфигурации PCI вы должны использовать методы конфигурации red/write или подпрограммы PCI BIOS.
ЗАМЕТКА:
Многие контроллеры дисков PCI имеют бит, называемый битом «Включение/выключение прерывания». Регистр, содержащий этот бит, обычно находится в пространстве конфигурации PCI, и его можно найти в таблице данных.
Собственно, этот параметр предназначен для «переадресации» прерывания, генерируемого устройством, подключенным к контроллеру PCI, на шину PCI.
Если прерывания отключены с помощью этого бита, то даже если ваше устройство (подключенное к контроллеру PCI) генерирует прерывание, прерывание НЕ будет перенаправлено на шину PCI (и, следовательно, процессор никогда не узнает, произошло ли прерывание), но прерывание бит (этот бит отличается от бита «Включение/отключение прерывания») в контроллере PCI по-прежнему установлен для уведомления о том, что устройство (подключенное к контроллеру PCI, например, жесткий диск) сгенерировало прерывание, чтобы программа могла прочитать этот бит и принять соответствующие меры. Это похоже на опрос с точки зрения программирования.
Обычно это применимо только к нешинным мастер-передачам.
Но похоже, что вы используете главную передачу шины (то есть DMA), поэтому в вашем случае это не должно применяться.
Но в любом случае, я бы посоветовал вам внимательно прочитать техническое описание контроллера PCI, особенно в поисках битов/регистров, связанных с обработкой прерываний.
ОТРЕДАКТИРОВАНО:
Ну, что касается программирования на уровне приложений, вам не нужно сталкиваться/использовать указатели _far, поскольку ваша программа не будет обращаться ни к чему вне вашего кода.
Но это не совсем так, когда вы переходите к программированию на системном уровне, вам нужно получить доступ к регистрам устройств с отображением памяти, внешнему ПЗУ или реализации обработчиков прерываний и т. д.
История меняется здесь. Создание сегмента, т. е. выделение дескриптора и получение связанного с ним селектора, гарантирует, что даже если в коде есть ошибка, он не будет раздражающе изменять что-либо внешнее по отношению к тому конкретному сегменту, из которого выполняется текущий код. Если он попытается это сделать, процессор выдаст ошибку. Таким образом, при доступе к внешним устройствам (особенно к регистрам устройства с отображением памяти) или доступе к некоторым данным ПЗУ, например, BIOS и т. д., рекомендуется выделить дескриптор и установить базовые и сегментные ограничения в соответствии с областью, которую вам нужно выполнить. прочитать/записать и продолжить. Но вы не обязаны это делать.
Некоторый внешний код, находящийся, например, в rom, предполагает, что они будут вызываться с помощью дальнего вызова.
Как я уже говорил ранее, в архитектуре x86 на каком-то уровне (чем ниже вы идете) вам нужно иметь дело с сегментацией, поскольку полностью отключить ее невозможно. Но в плоской модели сегментация присутствует как помощь программисту, как я сказал выше, при доступе к внешним (по отношению к вашей программе) вещам. Но вам не нужно использовать, если вы не хотите этого делать.
Когда вызывается обработчик прерывания, он не знает базы и ограничений программы, которая была прервана. Он не знает атрибутов сегмента, ограничений и т. д. прерванной программы, мы говорим, кроме CS и EIP, что все регистры находятся в неопределенном состоянии по отношению к обработчику прерывания. Поэтому ее необходимо объявить как функцию far, чтобы указать, что она находится где-то вне исполняемой в данный момент программы.
person
jacks
schedule
28.07.2012