Здравствуйте! Ситуация следующая. Нужно, чтобы в момент отключения электричества UPS, который подключен к компьютеру через COM-порт, подал сигнал на комп. Как правильнее сделать? Повесить резидент на прерывание 14H и ждать прерывания со стороны устройства? Или повеситься на 1CH (таймер) и n раз в секунду проверять состояние порта? Вообще сталкиваюсь с такой задачей впервые. Не ясно, нужно ли самому опрашивать устройство или можно повеситься на аппаратное прерывание. А также такой вопрос - нужно ли инициализировать порт (функция 00H прерывания 14H)? И ещё, туда же: как узнать номер порта, если нужно будет действовать инструкцией OUT? Огромное спасибо за помощь!!!!!!!!! ps - Не нашёл подходящего раздела форума... пишу сюда
1. Какой UPS? Совместим ли он по протоколу с Megatec? 2. Если да, то, на мой взгляд, такая схема действий: - a) инициализируем порт (лучше с помощью int 14h) с параметрами как выше по ссылке; - b) раз в секунду-две или три делаем опрос состояния UPS (аналогично - int 14h); - c) напрямую в порты лучше не соваться, для протокола Megatec хватает и тех возможностей, что дает int 14h; - d) обнаружение UPS на каком-либо порту можно сделать простым перебором - выбрали порт, проинициализировали, отправили запрос, если не получили адекватного ответа, тогда опрашиваем следующий порт. 3. Если протокол не Megatec-совместимый, тогда какова скорость передачи данных и прочие параметры? Если, скажем, скорость 115200 b/s, тогда придется искать другие пути инициализации порта, например, прямым программированием портов. 4. В любом случае, думается мне, какой бы протокол ни был, наверняка он построен по типу "запрос-ответ". Так что принципиально мало что отличается от п.2. Главное - иметь описание протокола обмена.
Спасибо!!! мега полезная информация. Счас попробую переварить, а пока - первый же вопрос: для того, чтобы опрашивать порты раз в n секунд, мне ведь надо будет повесить резидент на прерывание 08H! Так вот, вопрос такой - можно ли из обработчика этого 08H прерывания вызвать прерывание 14H для опроса порта? не возникнет ли проблем с таймером..? Спасибо ещё раз!!!
1. Можно повеситься на int 08h, тогда после своих дел вместо iret необходимо передавать управление старому обработчику, чтобы он мог тикать системные часы. 2. Можно повеситься на int 1ch - оно вызывается каждый раз, когда стандартный обработчик int 08h сделает все свои дела по тиканью системных часов. Однако это не рекомендуется, ибо не все tsr-проги, могущие занять вектор 1ch, будучи запущенными после твоего драйвера, заботятся о передаче управления старому (а в данном случае твоему) обработчику. 3. Практически дел с этими функциями не имел. Однако, учитывая, что в любом UART'е есть FIFO, за исключением совсем древних, команды из нескольких байт можно посылать сразу, не испытывая каких-либо затруднений. Даже если FIFO нет или BIOS его не включает, функция передачи вроде сразу возвращает управление, не ожидая готовности, так что это можно делать самостоятельно, анализируя status. То же относится и к функции приема - управление вроде как возвращается сразу, а о том, принято что-нибудь или нет, можно узнать, анализируя тот же status. Таким образом, если правильно организовать обработку прерывания (08h или 1ch) и обработку таймаутов, никаких проблем возникать не должно. Если мои измышления неверны, просьба к кому-нибудь - особенно к тем, кто работал с этими функциями - поправить меня.
Просветите поподробнее плиз... Или дайте наводку что почитать - как работать с этим status'ом?....... В одном из случаев мне придётся делать из обработчика 08H прерывания целую кучу операций - и принять с упса сигнал, и передать команду на упс, и создать файл, и с int 10H поработать.... Как тут быть? Можно ли из прерывания передать управление на какую-то большую подпрограмму, но только чтобы не тормозить таймер (и, соответственно, всю аппаратуру)??? и куда в этом случае возвращаться??????? Может, есть какие-то мехнизмы для подобных случаев? help словом... ps - Проверил, протокол моего ups совместим с Megatec (некоторые команды, правда, не работают).
Если я правильно понял задачу, то вот набросок для tasm'а: Код (Text): .386 .model small PING_CONST equ 18 ; приблизительно раз в секунду ;********************************************************************* Text segment public 'code' assume cs:Text, ds:Data UPS_EntryPoint: mov ax, Text mov ds, ax mov IsOperatingNow, 0 ; ??? тут у нас первоначальная инициализация, ; ??? далее проверяем, нужно ли установить резидент, ; ??? либо же удалить существующий. ; ... ; если надо установить, тогда jmp UPS_InstallService ; если удалить, то jmp UPS_RemoveService ; ни то, ни се - выход mov ax, 04C00h int 21h ;--------------------------------------------------------------------- UPS_InstallService: mov ax, 03508h int 21h mov Old08IP, bx mov Old08CS, es push ds mov ax, cs mov ds, ax lea dx, UPS_Ping mov ax, 02508h int 21h pop ds ; ??? тут выходим, оставаясь резидентными ; ... ;--------------------------------------------------------------------- UPS_RemoveService: push ds lds dx, Old08Handler mov ax, 02508h int 21h pop ds mov ax, 04C00h int 21h ;--------------------------------------------------------------------- UPS_Ping: ;----------------------------------- ; Это наш обработчик прерывания 08h ;----------------------------------- push ax bx ds mov ax, Data mov ds, ax xor al, al cmp PingCounter, al ; защита от дурака jnz Uping_090 Uping_010: mov PingCounter, PING_CONST cmp IsOperatingNow, al jnz Uping_090 ; о! пора запустить нашу выхухоль! ;---------------------------------- ; Содержимое стека: ; |Flags(x)| |Flags(x)| ; | CS(x) | | CS(x) | ; | IP(x) | | IP(x) | ; | AX | |Flags(x)| ; | BX | | CS(u) | ; | DS | | IP(u) | ; << SP | CS(o) | ; | IP(o) | ; << SP ; ^имеем^ ^надо сделать^ ; (x - параметры прерванного кода, ; u - параметры UPS_Operate, ; o - параметры старого обработчика) inc IsOperatingNow ; планируем передачу ей управления после старого обработчика mov ax, [sp+10] ; Flags(x) mov bx, cs ; CS(u) pop ds xchg bx, [sp] xchg ax, [sp+2] push offset UPS_Operate ; IP(u), must be 16-bit! push ax bx ds mov ax, Data mov ds, ax ; далее доделываем преобразование стека Uping_090: ; передаем управление старому обработчику ;----------------------------------------- ; Содержимое стека: ; |Flags(x)| |Flags(x)| ; | CS(x) | | CS(x) | ; | IP(x) | | IP(x) | ; | AX | | CS(o) | ; | BX | | IP(o) | ; | DS | << SP ; << SP ; ^имеем^ ^надо сделать^ ; (x - параметры прерванного кода, ; o - параметры старого обработчика) dec PingCounter mov bx, Old08IP mov ax, Old08CS pop ds xchg bx, [sp] xchg ax, [sp+2] retf ;--------------------------------------------------------------------- UPS_Operate: ;------------------- ; Это наша выхухоль ;------------------- ; ??? теперь здесь делаем что хотим и как хотим ; ??? тупим столько, сколько нужно ; ??? дергаем какие угодно функции DOS/BIOS ; ??? и т.д. ; ... cli dec IsOperatingNow iret Text ends ;********************************************************************* Data segment public 'data' Old08Handler label dword Old08IP dw 0 Old08CS dw 0 PingCounter db 0 IsOperatingNow db 0 Data ends ;********************************************************************* end UPS_EntryPoint Все ли ясно?
Суть всей мышиной возни в том, чтобы выполнять UPS_Operate без проблем: если подпрограмма будет выполняться меньше секунды, то она все равно будет запускаться раз в секунду; если же время ее выполнения будет больше секунды, повторных вхождений в нее не будет. При этом, в обоих случаях, прерывания разрешены и старый обработчик получает управление без пропусков, соответственно системные часы будут тикать как надо и контроллер прерываний и что-нибудь еще будет обслуживаться без проблем: Случай 1) UPS_Operate не получает управление: Int 08h -> UPS_Ping -> старый обработчик -> прерванный код. Случай 2) UPS_Operate должен получить управление: Int 08h -> UPS_Ping -> старый обработчик -> UPS_Operate -> прерванный код.
Да... не знаю, как и благодарить. Принцип ясен. В коде подробнее разберусь, как будет свободный часок. Думаю, проблем не должно возникнуть. СПАСИБО!!!
Кстати, я его не проверял, даже не компилил, так что если обнаружатся ошибки, просьба сообщить - я поправлю. Мало ли кому еще понадобится.
он мне при обработке tasm'ом выдаёт "Illegal indexing mode" (mov ax, [sp+10]). Ну до этого я пока не добрался (пока пишу остальные подпрограммы, которые потом распихаю по этому скелету). Кроме того, переделываю чуть-чуть под tiny модель. А так больше никаких ошибок (если что найду по сути алгоритма, напишу). ps - по-моему, там надо сделать что-то вроде Код (Text): push sp pop bp mov ax, [BP+10]
Код (Text): push sp pop bp mov ax, [BP+10] Ну во-первых вроде ж можно сделать проще и без участия стека: Код (Text): mov bp, sp mov ax, [bp+10] Во-вторых, самое главное (и неприятное) - то, что при вводе в использование еще одного регистра его надо сохранить, желательно там же, на стеке, но тогда все смещения изменятся. По поводу illegal indexing mode - я вспонил, надо использовать не sp, а esp, но тогда надо быть уверенным, что старшая половина esp обнулена. Вообще адресация именно по esp для меня кажется удобной, при этом регистр ebp остается свободным для других целей, например: Код (Text): ; --------------------------------------------------------- ; -=* R-E-A-D---K-E-Y---O-R---E-R-R-O-R---M-E-S-S-A-G-E *=- ; --------------------------------------------------------- LifGetKeywordID: ;------------------------------------------ ; IN: EBX - table address ; OUT: NC - keyword found ; and EBX - keyword ID ; C - keyword not found ;------------------------------------------ ; GKID Workspace GKIDWS struc gkidws_begin = $ GKIDTmpregESI dd ? GKIDTmpregEBX dd ? GKIDSymbolCnr dd ? GKIDKeywordID dd ? GKIDKeywordLen dd ? GKIDSymbolPtr dd ? GKIDTemplatePtr dd ? GKIDTemplates dd 00100h - (GKIDTemplates - gkidws_begin) dup(?) gkidws_tmplts_size = $ - GKIDTemplates gkidws_size = $ - GKIDTmpregESI ends ;------------------------------------------ ; GKID Subroutine push eax and dl, 0FEh call LifSkipComments ; clean up and init GKIDWS mov eax, ecx mov ecx, gkidws_size / 4 LifGKID_005: push dword ptr 0 loopd LifGKID_005 mov ecx, eax mov [esp].GKIDTmpregESI, esi ; starting mov esi, ebx LifGKID_010: mov al, [esi] inc esi mov ah, [edi] cmp al, 0FFh jz LifGKID_120 cmp al, 080h jz LifGKID_090 cmp al, 0F0h jnz LifGKID_020 ; control code 0F0h - set current keyword ID lea esi, [esi + 4] mov eax, [esi - 4] mov [esp].GKIDTmpregEBX, eax jmp LifGKID_010 LifGKID_020: cmp al, 0F1h jnz LifGKID_022 ; control code 0F1h - set template lea esi, [esi + 8] mov ebx, [esp].GKIDTemplatePtr cmp ebx, gkidws_tmplts_size / 8 - 1 jnc LifGKID_Err2 mov eax, [esi - 4] mov [[esp].GKIDTemplates + ebx*8], eax mov eax, [esi - 8] mov [[esp].GKIDTemplates + ebx*8 + 004h], eax inc ebx mov [esp].GKIDTemplatePtr, ebx jmp LifGKID_010 LifGKID_022: cmp al, "#" jnz LifGKID_028 ; control symbol "#" - use template if next symbol 0..9 or A..Z mov al, [esi] inc esi cmp al, "0" jc LifGKID_028 cmp al, "Z" ja LifGKID_028 cmp al, "9" ja LifGKID_025 sub al, "0" jmp LifGKID_026 LifGKID_025: cmp al, "A" jc LifGKID_028 sub al, "A" - 10 LifGKID_026: movzx ebx, al mov eax, [[esp].GKIDTemplates + ebx*8 + 004h] mov ebx, [[esp].GKIDTemplates + ebx*8] test ebx, ebx jz LifGKID_Err3 call LifGetKeywordID cmp eax, ebx jnz LifGKID_070 jmp LifGKID_010 LifGKID_028: cmp al, "%" jnz LifGKID_050 ; control symbol "%" - means " ", "_" or nothing mov al, [esi] cmp al, "%" jnz LifGKID_030 ; double control symbol "%%" means single "%" inc esi jmp LifGKID_060 LifGKID_030: cmp ah, "_" jz LifGKID_040 cmp ah, " " jnz LifGKID_010 LifGKID_040: xchg esi, [esp].GKIDTmpregESI call LifMAIN_7 ; EDI++ xchg esi, [esp].GKIDTmpregESI jmp LifGKID_010 LifGKID_050: xchg esi, [esp].GKIDTmpregESI call LifMAIN_7 xchg esi, [esp].GKIDTmpregESI cmp ah, "A" jc LifGKID_060 cmp ah, "Z" ja LifGKID_060 or ah, 020h LifGKID_060: cmp al, ah jnz LifGKID_070 inc [esp].GKIDSymbolCnr jmp LifGKID_010 LifGKID_070: ; skip to next keyword mov al, [esi] inc esi cmp al, 0FFh jz LifGKID_120 cmp al, 080h jnz LifGKID_070 LifGKID_080: mov [esp].GKIDSymbolCnr, 0 mov edi, [esp].GKIDTmpregESI jmp LifGKID_010 LifGKID_090: mov eax, [esp].GKIDSymbolCnr cmp eax, [esp].GKIDKeywordLen jc LifGKID_080 jz LifGKID_Err1 LifGKID_100: mov [esp].GKIDKeywordLen, eax mov eax, [esp].GKIDTmpregEBX mov [esp].GKIDKeywordID, eax mov [esp].GKIDSymbolPtr, edi jmp LifGKID_080 LifGKID_120: cmp [esp].GKIDSymbolPtr, 0 stc jz LifGKID_130 mov edi, [esp].GKIDSymbolPtr mov ebx, [esp].GKIDKeywordID mov eax, [esp].GKIDKeywordLen clc LifGKID_130: mov esi, [esp].GKIDTmpregESI lea esp, [esp + gkidws_size] pop eax ret LifGKID_Err1: PSTR TxtIniDuplicateKey mov eax, [esp].GKIDTmpregEBX PHEXD eax PCHR "," PCHR " " mov eax, [esp].GKIDKeywordID PHEXD eax PENT QUIT LifGKID_Err2: PSTR TxtIniNoMoreTempls QUIT LifGKID_Err3: PSTR TxtIniNoTemplate QUIT Подпрограмма находит соответствие ключевому слову по [edi] в таблице по [esi] и возвращает индекс в ebx. И хотя данный пример для модели flat, он все равно иллюстрирует, как можно использовать стек для локальных переменных, как это делается в C/C++, но со свободным ebp.