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

2023-12-04

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

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

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

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

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

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

  • If I remove安装命令(int $0x21),没有崩溃,程序等待我按键然后退出。
  • If I keep安装命令,但是remove等待按键命令(int $0x16),程序立即退出,没有crash。
  • If I keep安装命令,以及replace带有活动延迟循环(40 亿次迭代的简单循环)的 wait-for-key 命令,程序崩溃的方式与 wait-for-key 命令到位时的情况相同,但在几秒钟后,而不是比立即。
  • 在这两种崩溃的情况下(通过按键或延迟),即使我只删除两个安装命令之一,程序也会崩溃。

预先感谢您提供的所有帮助,并对冗长的帖子表示歉意......


您可能需要指定.code16因此它构建了 16 位实模式的应用程序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • Visual Studio 2017 上的简单装配程序

    386 model flat c stack 100h printf PROTO arg1 Ptr Byte data msg1 byte Hello World 0Ah 0 code main proc INVOKE printf ADD
  • 汇编基础知识:输出寄存器值

    我刚刚开始学习汇编语言 我已经陷入了 在屏幕上显示存储在寄存器中的十进制值 的部分 我使用 emu8086 任何帮助将不胜感激 model small Specifies the memory model used for program
  • 在linux x86平台上学习ARM所需的工具[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有一个 x86 linux 机器 在阅读一些关于 ARM 的各种信息时 我很好奇 现在我想花一些时间学
  • 尝试使用 x86 程序集 GNU GAS 在数组索引处赋值时出现错误

    我在用x86GNU 与 GCC 的程序集 并尝试实现相当于以下内容的程序集c c int x 10 x 0 5 但是 当我尝试运行 使用命令 a out 我的汇编代码如下 第一次编译后gcc filename s 错误Segmentatio
  • 使用 gcc 的中间 GIMPLE 格式

    根据本文 http en wikipedia org wiki Intermediate languagegcc 在生成代码之前使用多种中间格式 我读到 GIMPLE 格式使用三个地址代码 这似乎是最容易使用的中间语言 但我需要更多细节 因
  • qemu kvm:如何获取性能监控中断?

    我在操作系统内核中编写了一些函数 以便在指令计数器溢出时发出性能监控中断 PMI 它在我的机器 Intel core i5 上运行良好 但是当我使用 qemu 在 qemu 上运行它时 qemu system x86 64 enable k
  • 汇编8086监听键盘中断

    我有与此完全相同的问题 边画边听键盘 https stackoverflow com questions 13970325 8086 listen to keyboard while drawing 但第一个答案 接受的答案 只听键盘一次
  • 使用 AVX 内在函数代替 SSE 并不能提高速度 - 为什么?

    我已经使用 Intel 的 SSE 内在函数相当长一段时间了 并取得了良好的性能提升 因此 我希望 AVX 内在函数能够进一步加速我的程序 不幸的是 直到现在情况并非如此 可能我犯了一个愚蠢的错误 所以如果有人能帮助我 我将非常感激 我使用
  • 在 Ubuntu 上用 C 项目编译和链接 GTK 3

    我相信这不是重复的问题 在发布此问题之前我已经看过所有问题 答案 我想我这里的情况有所不同 我使用Ubuntu 12 04并下载GTK 2 和 3 我从 GNOME 网站复制了一个简单的 GTK 源代码 但是当我在终端中使用这个命令时 gc
  • `printf()` 中格式说明符“%qd”的用途是什么?

    我看到格式说明符 qd浏览时github https github com Microsoft clang blob master test Sema format strings c代码 然后我检查了 GCC 编译器 它工作正常 incl
  • 在 Linux 上将 libquadmath 与 C++ 链接

    我有一个示例代码 include
  • 这种对有效类型规则的使用是否严格遵守?

    C99和C11中的有效类型规则规定 没有声明类型的存储可以用任何类型写入 并且存储非字符类型的值将相应地设置存储的有效类型 抛开 INT MAX 可能小于 123456789 的事实不谈 以下代码对有效类型规则的使用是否严格符合 inclu
  • 弹出 x86 堆栈以访问函数 arg 时出现分段错误

    我正在尝试链接 x86 程序集和 C 我的C程序 extern int plus 10 int include
  • 字节码和位码有什么区别[重复]

    这个问题在这里已经有答案了 可能的重复 LLVM 和 java 字节码有什么区别 https stackoverflow com questions 454720 what are the differences between llvm
  • 从 exe 文件中获取汇编级代码?

    我当时正在做linux汇编编程 在过去的几天里我已经转而学习windows汇编编程 我在用ml作为我的汇编器和golink作为我的链接器 我有我的汇编代码并已获得我的exe从中 现在我需要取回它的十六进制 xff xab x55等等 在li
  • 如何在 Debian 上编译 DOS 程序?

    在我的汇编语言课程中 我们使用 DPMI 编写 DOS 程序 不幸的是 我无法一直使用 32 位 Windows 机器 我在我使用的几乎每台计算机上都安装了 Debian 虚拟机 我已经安装了 DOSBox 和 DOSEMU 有什么办法可以
  • GCC:数组类型具有不完整的元素类型

    我已经宣布了struct 我尝试传递这些结构的数组 以及double双精度数组和一个整数 到一个函数中 我得到一个 数组类型具有不完整的元素类型 当我编译它时来自 gcc 的消息 我在通过考试的过程中犯了什么错误struct到函数 type
  • 使用 NEON 优化 Cortex-A8 颜色转换

    我目前正在执行颜色转换例程 以便从 YUY2 转换为 NV12 我有一个相当快的函数 但没有我预期的那么快 主要是由于缓存未命中 void convert hd uint8 t orig uint8 t result uint32 t wi
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作

随机推荐