Замена обработчика прерываний от таймера в DOS с GNU (GCC и GAS) - PullRequest
1 голос
/ 13 сентября 2011

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

Проблема теперь в том, что код, который я нашел (см. Ниже), написан для TASM, и я хочу использовать его с некоторым кодом C, который я пишу, и который я компилирую с GCC.

Я пытался преобразовать код в синтаксис GAS (GNU Assembler), но, похоже, я не могу заставить его работать (в большинстве случаев я сталкивался с авариями того или иного рода во время моих многочисленных попыток).

Я был бы очень признателен, если бы кто-нибудь мог просветить меня решением (будь то рабочая версия ассемблерного кода, который может компилировать GAS, способ сделать все это в C - ключевое слово "interrupt" не делает '). не работает, и не работает ни « атрибут ((прерывание))» и т. п., ни даже способ соединить TASM и GCC).

Я также должен, вероятно, упомянуть, что система DOS, которую я использую, на самом деле является OracleVM VirtualBox Manager, на котором запущена виртуальная машина с установленной FreeDOS, и что компилятор, который я использую для C, - это GCC, который предоставляется Среда разработки DJGPP.

Это рабочий код TASM, который у меня есть (взят из http://www.programmersheaven.com/mb/x86_asm/276128/276185/re-redefining-the-timer-interrupt-handler/):

_stack        SEGMENT    STACK
              db         32 DUP ('STACK   ')
_stack        ENDS



_code        SEGMENT PARA 'CODE'
             ASSUME  CS:_code,  SS:_stack

Lstart  LABEL  NEAR

        JMP    Linstall

;+---------------------------------------------
;| My New 1Ch INT
;| Print 'random' chars to the first video line

new_Int PROC   FAR

        DEC    BYTE PTR CS:Counter

        CLD
        PUSH   AX

        MOV    AX, 0B800h
        MOV    ES,AX                   ; ES = b800h
        MOV    DI,000h                 ; DI = 0000h

        MOV    AH,CS:Counter           ; set foreground and background color
        MOV    AL,CS:Counter           ; set char

        MOV    CX,80
        REP    STOSW                   ; From AX to ES:DI

        POP    AX
        STI

        IRET

new_Int ENDP

Counter DB     0Fh

;+-----------------------------------------
;| Store old INT and Install the new one
;|

Linstall    LABEL    NEAR

old_INT     DD       00000000h

        MOV    AL,01Ch                 ;+-
        MOV    AH,35h                  ;| Save old_INT
        INT    21h                     ;|
        MOV    WORD PTR [old_INT],BX
        MOV    WORD PTR [old_INT][2],ES



        CLI                            ;+-
        PUSH   CS                      ;| Install
        POP    DS                      ;|
        LEA    DX,new_INT
        MOV    AL,1Ch
        MOV    AH,25h
        INT    21h


        MOV    AH,0                    ;+- 
        INT    16H                     ;| Wait for a keypress



;+-----------------------------------------
;| Disinstall and exit

        CLI
        PUSH   DS
        LDS    DX,CS:[old_INT]         ;+- 
        MOV    AL,1Ch                  ;| Disinstall int
        MOV    AH,25h                  ;| 
        INT    21h                     ;| 
        POP    DS
        STI        

        MOV    AL,0                    ;+-
        MOV    AH,4Ch                  ;| Exit 
        INT    21h                     ;| 


_code   ENDS
        END    Lstart

Он полностью работает на моей машине. Я запускаю программу и вижу всю первую строку консоли, замененную цветными символами, которые постоянно меняются.

И это моя попытка преобразовать приведенный выше код в синтаксис GAS:

.file   "ttv2.s"


# Define a variable for "randomizing" characters and colors
.globl _MyVar
        .section        .bss
_MyVar:
        .space 1
        .section .text


# Define a variable for storing the address of the current ISR
.globl _OldInt
        .section        .bss
        .p2align 2
_OldInt:
        .space 4
        .section .text


# Program entry point
.text
.globl start
start:
        jmp     _Install


# This is the new Interrupt Service Routine that is going to be installed
.globl _NewInt
_NewInt:
        movb    _MyVar,  %al
        decb    %al            # Decrement our variable
        movb    %al,     _MyVar

        cld
        pushw   %ax

        movw    $0xB800, %ax
        movw    %ax,     %es    # ES = 0xB800
        movw    $0,      %di    # DI = 0

        movb    _MyVar,  %ah    # Set the foreground and background colors
        movb    _MyVar,  %al    # Set the charater to be displayed

        movw    $80,     %cx    # The screen is 80 characters wide
        rep     stosw           # Start copying from AX to AS:DI

        popw    %ax
        sti

        iret



.globl _Install
_Install:
        # Save old ISR address
        movb    $0x1C,   %al  # Set the code for the Timer interrupt
        movb    $0x35,   %ah  # 0x35 is the code for getting the current ISR
        int     $0x21         # 0x21 is the interrupt fot s/getting ISRs
        movw    %es,     %dx     #
        shll    $16,     %edx    # Save the address of the
        movw    %bx,     %dx     #  old interrupt handler
        movl    %edx,    _OldInt #


        # Install the new ISR
        cli
        pushw   %cs
        popw    %ds
        lea     _NewInt, %dx  # Set the address of the ISR we're installing
        movb    $0x1C,   %al  # Set the code for the Timer interrupt
        movb    $0x25,   %ah  # 0x25 is the code for setting a new ISR
        int     $0x21         # 0x21 is the interrupt fot s/getting ISRs

        # Wait for a key press
        movl    $0,     %eax
        int     $0x16


.globl _Uninstall
_Uninstall:
        cli
        pushw   %ds
        lds     %cs:_OldInt,    %dx  # Install the address of the old ISR

        movb    $0x1C,   %al  # Set the code for the Timer interrupt
        movb    $0x25,   %ah  # 0x25 is the code for setting a new ISR
        int     $0x21         # 0x21 is the interrupt fot s/getting ISRs

        popw    %ds
        sti


.globl _End
_End:
        # Exit
        movb    $0,     %al
        movb    $0x4C,  %ah   # 0x4C is the code for program exit in DOS
        int     $0x21


        .ident  "GCC: (GNU) 4.5.2"

Я компилирую свой файл (называемый "ttv2.s") с помощью следующих команд:

as -o ttv2.o ttv2.s
ld -o ttv2.exe ttv2.o

Когда я запускаю полученный EXE-файл (нет предупреждений или ошибок во время сборки и связывания), программа вылетает с ошибкой «Exception 0D in ring 0» (и большим количеством значений регистра). Однако версия TASM работает без сбоев! Поэтому я предполагаю, что что-то не так с тем, как я конвертировал код, или с тем, как я собираю окончательный EXE-файл. Или оба.

Немного дополнительной информации, если она поможет каким-либо образом:

  • Если я удаляю команды установки (int $0x21), сбоя не происходит, и программа ждет, пока я нажму клавишу, а затем завершается.
  • Если я сохраню команды установки, но удалю команду ожидания ключа (int $0x16), программа завершится немедленно, и сбой не произойдет.
  • Если я оставлю команды установки и заменим команду ожидания ключа активным циклом задержки (простой цикл из 4 миллиардов итераций), программа завершит работу. То же самое, что и при наличии команды ожидания ключа, но через пару секунд, а не сразу.
  • В обоих случаях при сбое (при нажатии клавиши или задержке) происходит сбой программы, даже если я удаляю только одну из двух команд установки.

Заранее спасибо за любую помощь, и извините за длинный пост ...

Ответы [ 2 ]

1 голос
/ 13 сентября 2011

Тот факт, что вы получаете ошибку о кольцах, означает, что вы по какой-то причине находитесь не в 16-битном реальном режиме (как DOS будет работать), а скорее в каком-то защищенном режиме.Поэтому убедитесь, что 1) вы компилируете в 16-битный реальный режим для ваших команд сборки (т. Е. Двоичный машинный код представляет собой 16-битные коды операций, а не 32-битные коды операций), и 2) что вы работаете в 16-битовый режим реального режима при попытке запустить ваш EXE.

Во-вторых, обратите внимание, что в версии TASM они поместили переменную Counter в сегмент кода и получают доступ к Counter через смещениеиз текущего кодового сегмента.Вы, с другой стороны, поместили переменную-счетчик _MyVar в раздел BSS.В зависимости от того, как компоновщик связывает ваш двоичный исполняемый файл, эта переменная может быть недоступна из вашего прерывания ... например, она может быть недоступна в окне 64 КБ текущего сегмента данных, когда прерывание выполняется.Поэтому я хотел бы отразить то, что они сделали в версии TASM, и поместить переменную вашего счетчика в сегмент кода, и получить к ней доступ из сегмента кода

1 голос
/ 13 сентября 2011

Возможно, вам нужно указать .code16, чтобы он создавал приложение для 16-битного реального режима.

...