As the title suggests, I'm trying to replace the existing handler for the Timer interrupt in DOS with one of my own. After searching far and wide for a variety of solutions, I found some Assembly code which does exactly that, and I have even managed to compile and test it, and saw that it works.
The problem now is that the code I found (see further down) is written for TASM, and I wish to use it with some C code that I'm writing, which I compile with GCC.
I've tried to convert the code into GAS (GNU Assembler) syntax, but I can't seem to get it to work (I mostly experienced crashes of one kind or another during my numerous attempts).
I would very much appreciate it if anyone could enlighten me with a solution (be it a working version of the assembly code that GAS can compile, a way to do the entire thing in C -- the "interrupt" keyword doesn't work, and neither does "attribute ((interrupt))" and the like -- or even a way to bridge between TASM and GCC).
I should also probably mention that the DOS system I'm using is actually an OracleVM VirtualBox Manager running a virtual machine with FreeDOS installed on it, and that the compiler I'm using for C is the GCC that is provided with the DJGPP development environment.
This is the working TASM code I have (taken from 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
It fully works on my machine. I start the program and see the entire first line of the console replaced by colorful characters that change all the time.
And this is my attempt to convert the above code into GAS syntax:
.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"
I compile my file (called "ttv2.s") with the following commands:
as -o ttv2.o ttv2.s
ld -o ttv2.exe ttv2.o
When I run the resulting EXE file (there are no warnings or errors during the assembly and linkage), the program crashes with the error "Exception 0D in ring 0" (and lots of register values). The TASM version, however, works without a hitch! So I'm guessing that there is something wrong either with the way I converted the code, or with the way I'm building the final EXE. Or both.
A bit of additional information, should it help in any way:
- If I remove the installation commands (the
int $0x21
), there is no crash, and the program waits for me to hit a key and then exits. - If I keep the installation commands, but remove the wait-for-key command (the
int $0x16
), the program exits immediately, and there is no crash. - If I keep the installation commands, and replace the wait-for-key command with an active delay loop (a simple loop of 4 billion iterations), the program crashes the same way it did when the wait-for-key command was in place, but after a couple of seconds, rather than immediately.
- In both cases with the crash (with the key press or the delay), the program crashes even if I remove just one of the two installation commands.
Thanks in advance for any and all assistance, and sorry for the lengthy post...