1

正如标题所暗示的,我正在尝试用我自己的一个替换 DOS 中的 Timer 中断的现有处理程序。在广泛搜索各种解决方案后,我发现了一些完全可以做到这一点的汇编代码,我什至设法编译和测试它,并看到它可以工作。

现在的问题是我找到的代码(见下文)是为 TASM 编写的,我希望将它与我正在编写的一些 C 代码一起使用,我用 GCC 编译这些代码。

我试图将代码转换为 GAS(GNU 汇编器)语法,但我似乎无法让它工作(在我多次尝试期间,我大多经历过一种或另一种崩溃)。

如果有人能用解决方案启发我,我将不胜感激(无论是 GAS 可以编译的汇编代码的工作版本,还是在 C 中完成整个事情的方法——“中断”关键字不起作用, “属性((中断))”等也没有——甚至是在 TASM 和 GCC 之间架起桥梁的一种方式)。

我还应该提到我正在使用的 DOS 系统实际上是一个 OracleVM VirtualBox 管理器,它运行一个安装了 FreeDOS 的虚拟机,而我用于 C 的编译器是 DJGPP 开发环境提供的 GCC .

这是我拥有的工作 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 文件(在组装和链接过程中没有警告或错误)时,程序崩溃并出现错误“异常 0D in ring 0”(以及许多寄存器值)。但是,TASM 版本可以顺利运行!所以我猜我转换代码的方式或者我构建最终EXE的方式有问题。或两者。

一些额外的信息,如果它以任何方式有帮助:

  • 如果我删除安装命令 (the int $0x21),则不会发生崩溃,程序会等待我按下一个键然后退出。
  • 如果我保留安装命令,但删除等待键命令(int $0x16),程序立即退出,并且没有崩溃。
  • 如果我保留安装命令,并将wait-for-key 命令替换为活动延迟循环(一个简单的 40 亿次迭代循环),程序崩溃的方式与 wait-for-key 命令到位时相同,但几秒钟后,而不是立即。
  • 在崩溃的两种情况下(按键或延迟),即使我只删除了两个安装命令之一,程序也会崩溃。

在此先感谢您提供的任何和所有帮助,并为冗长的帖子感到抱歉...

4

2 回答 2

1

您可能需要指定.code16以便它为 16 位实模式构建应用程序。

于 2011-09-13T17:40:52.970 回答
1

您收到有关环的错误这一事实意味着您出于某种原因不在 16 位实模式下(就像运行 DOS 一样),而是处于某种形式的保护模式下。因此,请确保 1) 您正在为您的汇编命令编译为 16 位实模式(即二进制机器代码是 16 位操作码,而不是 32 位操作码),以及 2) 您在 16-尝试运行 EXE 时的位实模式设置。

其次,请注意,在 TASM 版本中,他们已将Counter变量放在代码段中,并Counter通过当前代码段的偏移量访问 。另一方面,您已将计数器变量_MyVar放在 BSS 部分。根据链接器链接二进制可执行文件的方式,该变量可能无法从您的中断中访问……例如,当中断运行时,它可能无法在当前数据段的 64Kb 窗口内访问。因此,我将反映他们在 TASM 版本中所做的事情,并将您的计数器变量放在代码段中,并从代码段中访问它

于 2011-09-13T17:55:45.583 回答