使用GNU(GCC和GAS)替换DOS中的定时器中断处理程序

时间:2011-09-13 17:36:30

标签: gcc assembly dos interrupt-handling gas

正如标题所暗示的那样,我正在尝试用自己的一个替换DOS中的Timer中断的现有处理程序。 在对各种解决方案进行了广泛的搜索之后,我发现了一些汇编代码,它确实完成了这一点,我甚至设法编译并测试它,并且看到它有效。

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

我试图将代码转换为GAS(GNU汇编程序)语法,但我似乎无法使其工作(我经常在多次尝试中遇到过这种或那种类型的崩溃)。

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

我还应该提一下,我正在使用的DOS系统实际上是运行安装了FreeDOS的虚拟机的OracleVM VirtualBox Manager,而我用于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文件(在汇编和链接期间没有警告或错误)时,程序崩溃并出现错误“0中的异常0D”(以及许多寄存器值)。 然而,TASM版本顺利运行! 所以我猜测我转换代码的方式或者我正在构建最终EXE的方式有问题。或两者兼而有之。

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

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

提前感谢任何和所有的帮助,并对这篇漫长的帖子感到抱歉......

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

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