正如标题所暗示的那样,我正在尝试用自己的一个替换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
),则不会发生崩溃,程序会等待我按键然后退出。int $0x16
),程序会立即退出,并且没有崩溃。提前感谢任何和所有的帮助,并对这篇漫长的帖子感到抱歉......
答案 0 :(得分:1)
您可能需要指定.code16
,以便为16位实模式构建应用程序。
答案 1 :(得分:1)
您收到有关响铃的错误这一事实意味着您出于某种原因不是处于16位实模式(如DOS将运行),而是处于某种形式的保护模式。因此,请确保1)您正在为汇编命令编译为16位实模式(即,二进制机器代码是16位操作码,而不是32位操作码),以及2)您运行的是16-尝试运行EXE时进行实模式设置。
其次,请注意,在TASM版本中,他们已将Counter
变量放在代码段中,并通过当前代码段的偏移量访问Counter
。另一方面,您已将计数器变量_MyVar
放在BSS部分中。根据链接器链接二进制可执行文件的方式,可能无法从中断访问该变量...例如,当中断运行时,可能无法在当前数据段的64Kb窗口中访问该变量。因此,我将反映他们在TASM版本中所做的工作,并将您的计数器变量放在代码段中,并从代码段访问它