Странное поведение от службы обработки прерываний - PullRequest
3 голосов
/ 27 декабря 2011

Я пишу подпрограмму обработки прерываний, которая должна обрабатывать прерывания, вызванные RTC, используя int 70h и IRQ8 для использования с некоторым таймером.К сожалению, у меня было много проблем с этим, поэтому я решил разделить проблему на несколько более мелких проблем, а затем решить каждую из них независимо.Для начала я отказался от аппаратной части и решил сначала реализовать прерывание в программном обеспечении.

Сейчас я использую NASM и DosBox.

Вот код ISR:

segment .code
; ----------------------------------------------
; writes a message on the screen
; every time interrupt is called
; ----------------------------------------------

INT_CODE equ 070h

my_int:
    pusha           ;saves all registers on stack so they get preserved

    ;EDIT1
    xor ax, ax      ;sets ax to zero
    mov es, ax      ;puts zero into extra segment register
    mov bx, [es:INT_CODE*4+2] ;this should give us the sergment of the ISR
    mov ds, bx      ;the segment is now in ds
    ;END OF EDIT1

    ;mov ax, 0      ;cleans any garbage in ax

    ;mov ah, 09h    ;preparing to call DOS system call, remove later
    mov ax, string_s
    mov si, ax
    call _print_string


    ;int 021h       ;this should hopefully work

    mov al, 0Ch     ; Accessing RTC
    out 070h, al    ; register C should be read
    in al, 071h     ;or there won't be any new interrupts (or so it's supposed to be)

    ;mov ax, 0      ; again we clear anything left in ax, just in case
    ;mov ah, 09h    ; preparing to write string
    mov ax, string_e
    mov si, ax
    call _print_string
    ;int 021h       ; this should work

    mov al, 20h     ;we're letting PICs know the interrupt ended
    out 0A0h, al    ;notifying second PIC
    out 020h, al    ;notifying first PIC

    popa            ;application gets its registers back

    iret

_inst_70:
    cli             ;hardware interrupts are now stopped
    xor     ax, ax
    mov     es, ax
    mov     bx, [es:INT_CODE*4]
    mov     [old_int70_off], bx
    mov     bx, [es:INT_CODE*4+2]
    mov     [old_int70_seg], bx

; adding our routine to interrupt vector table
    mov     dx, my_int
    mov     [es:INT_CODE*4], dx
    mov     ax, cs
    mov     [es:INT_CODE*4+2], ax
    sti

    ;mov ah, 09h

    mov ax, string_inst
    mov si, ax
    call _print_string
    ;int 021h

    ret

; -----------------------------------------------------------------------------
; return old int 70 h

_uninst_70:
    cli
    xor     ax, ax
    mov     es, ax
    mov     ax, [old_int70_seg]
    mov     [es:INT_CODE*4+2], ax
    mov     dx, [old_int70_off]
    mov     [es:INT_CODE*4], dx
    sti
    ret

_print_string:

    pusha
    mov     ah, 0Eh                     ; BIOS INT 10h teletype (TTY) function
.Repeat:
    lodsb                               ; takes one character from a string
    cmp     al, 0
    je     .End                         ; If it's zero, end of string
    int     10h                         ; if not, call BIOS
    jmp    .Repeat                      ; and go to next character
.End:
    popa
    ret

segment .data

string_s: db 'We're in ISR',0
string_e: db 'It's working',0
string_inst: db 'Installed',0

old_int70_seg: dw 0
old_int70_off: dw 0

Я тестирую это прерывание, используя следующие программы:

;myint
org 100h;installs the interrupt
segment .code

main:
    call   _inst_70

    ;call   _uninst_70 ; THIS IS ON PURPOSE!
    ret

%include "myint.asm"

и

;int70h
org 100h            ;calls the interrupt
segment .code
    mov ah, 09h     ; getting ready to print string
    mov dx, string1
    int 21h

    ;mov ax, 0      ;getting rid of the last message
    ;mov dx, 0

    int 070h        ;calling the interrupt

    mov ah, 09h
    mov dx, string2;
    int 21h

    ret

segment .data
string1: db 'Testing!',0
string2: db 'int 70h working',0
_print_string:

        pusha
        mov     ah, 0Eh         ; BIOS INT 10h teletype (TTY) function
.Repeat:
        lodsb                   ; takes one character from a string
        cmp     al, 0
        je     .End             ; If it's zero, end of string
        int     10h             ; if not, call BIOS
        jmp    .Repeat          ; and go to next character
.End:
        popa
        ret

Теперь мы переходим к интересной части.

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

Когда я вызываю INT70H.COM, я получаю то, что кажетсядамп области памяти.Единственными читаемыми вещами в этом являются: Testing!Testing!int 70h working и C:\NASM-DOS\NASM.EXE.

Когда я раскомментирую строки mov ax, 0 и mov dx, 0 в INT70H, я получаю Testing!, и DosBox зависает и иногда падает.То же самое происходит с VMware и VirtualBox.

Когда я закомментирую строки, которые читают регистр C RTC, с двумя комментариями mov из INT70H, я получаю Testing!Testing!int 70h working и DosBox зависает.То же самое происходит в VirtualBox и VMware.Если два mov в INT70H не закомментированы, я получаю Testing! и зависаю.

Это наводит меня на мысль, что это могут быть некоторые системные вызовы DOS (которые я не должен использовать в конечном продукте)может быть что-то плохое, но даже когда они закомментированы, когда я запускаю INT70H, компьютер зависает.

Моя главная проблема в том, что сейчас я совершенно не знаю, как начать работать над этой проблемой.

Ответы [ 3 ]

1 голос
/ 28 декабря 2011

Процедура обработки прерываний (ISR) должна сохранять все используемые им регистры и восстанавливать их (чтобы прерванное программное обеспечение не могло случайно видеть регистры). Это включает в себя регистры сегментов (например, DS и ES). Вам нужно "push ds" и "push es" в начале вашего ISR и соответствующие инструкции "pop" перед "iret".

Ни одна из функций BIOS не является входящей, и поэтому небезопасно использовать любую из них в ISR; если вы не можете гарантировать, что никакой другой код, который может быть запущен, не использует их. Это включает в себя функцию "int 0x10, ah = 0x0E" (которую вы используете в основном коде и в ISR, который прерывает основной код). Если это только для тестирования; вместо этого попробуйте выполнить прямую запись для отображения памяти (например, для текстового режима "mov ax,0xB800; mov es,ax; inc word [es:0]").

Для тестирования кода ОС зачастую проще протестировать код ОС. Например, если это было реализовано в загрузочном секторе, который намеренно не вернулся (заблокирован); тогда вам не придется беспокоиться о сохранении / восстановлении предыдущей записи IVT или беспокоиться о том, что DOS и / или любые TSR делают в фоновом режиме, что может помешать вашему тесту; и вы можете отладить его в чем-то вроде Bochs без предварительной установки DOS / FreeDOS внутри виртуальной машины. В качестве дополнительного бонуса вы сможете сделать это в целевом режиме работы (например, возможно, в 32-битном защищенном режиме), а не переписывать 16-битный код реального режима позже.

1 голос
/ 27 декабря 2011

Процедура обслуживания прерываний должна настроить регистры сегментов перед выполнением любой операции, которая зависит от них.Когда прерывание вызывается, оно может иметь контекст абсолютно всего в системе.Вызовы для печати строк особенно проблематичны, поскольку для строкового адреса они основаны на ds:dx, но ds не установлен.

Кроме того, внешне он выглядит хорошо.Смотрите, если настройка ds решает проблему зависания.Продолжайте, если нет.

0 голосов
/ 30 декабря 2011

Это была действительно странная ошибка. Большое спасибо всем, кто помог, но в итоге оказалось, что когда я настраивал RTC, я не установил выходной регистр в B, прежде чем записывать в него. Теперь работает нормально.

...