Question

I'm writing an interrupt service routine which is supposed to process interrupts caused by RTC using int 70h and IRQ8 for use with some timer. Unfortunately, I've been having lots of problems with that, so I decided to separate the issue into several smaller problems and then solve each one independently. For start, I gave up on the hardware part and decided to first implement interrupt in software.

Right now, I'm using NASM and DosBox.

Here's the ISR code:

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

I'm testing this interrupt using following programs:

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

main:
    call   _inst_70

    ;call   _uninst_70 ; THIS IS ON PURPOSE!
    ret

%include "myint.asm"

and

;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

Now we're getting to the interesting part.

When I call the installation program, I get the message that the interrupt is installed and the program seems to end fine.

When I call INT70H.COM, I get what seems to be a dump of an area of memory. The only readable things in that are: Testing!Testing!int 70h working and C:\NASM-DOS\NASM.EXE.

When I uncomment the mov ax, 0 and mov dx, 0 lines in INT70H, I get Testing! and DosBox hangs and sometimes crashes. Same happens of VMware and VirtualBox.

When I comment out the lines which are reading register C of the RTC with the two movs from INT70H commented, I get Testing!Testing!int 70h working and DosBox hangs. Same thing happens on VirtualBox and VMware. If the two movs in INT70H are uncommented, I get Testing! and hang.

This lead me to believe that it could be some DOS system calls (which I'm not supposed to use in the end product) could be doing something bad, but even with them commented out, when I run INT70H, computer hangs.

My main problem is that right now I have absolutely no idea how to start working on this problem.

Était-ce utile?

La solution 3

This was a really strange bug. Thanks a lot to all who helped, but in the end, it turned out that when I was setting up the RTC, I didn't set output register to B before writing to it. Now it works fine.

Autres conseils

The interrupt service routine must set up the segment registers before doing any operation which depends on them. When the interrupt is called, it can have the context of absolutely anything in the system. The calls for printing strings are especially problematic as they rely on ds:dx for the string address, but ds is not set.

Other than that, it superficially looks fine. See if setting up ds solves the hang issue. Follow up if not.

The interrupt service routine (ISR) must save any registers it uses and restore them (so that the software that was interrupted doesn't see registers randomly trashed). This includes segment registers (e.g. DS and ES). You need "push ds" and "push es" near the start of your ISR and the corresponding "pop" instructions before the "iret".

None of the BIOS functions are re-entrant and therefore it's unsafe to use any of them in an ISR; unless you can guarantee that no other code that could possibly be running uses them. This includes the "int 0x10, ah = 0x0E" function (which you're using in the main code, and in the ISR that interrupts the main code). If it's only there for testing; try direct writes to display memory instead (e.g. for text mode, "mov ax,0xB800; mov es,ax; inc word [es:0]").

For testing OS code, often it's easier to test OS code. For example, if this was implemented in a boot sector that deliberately didn't return (locked up); then you wouldn't have to bother saving/restoring the previous IVT entry or worry about what DOS and/or any TSRs are doing in the background that might interfere with your test; and you could debug it in something like Bochs without installing DOS/FreeDOS inside the virtual machine first. As an added bonus you'd be able to do it in the target operating mode (e.g. possibly 32-bit protected mode) rather than rewriting 16-bit real mode code later.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top