home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 8 Other
/
08-Other.zip
/
artfix03.zip
/
TIMED
/
TIMED.ASM
< prev
next >
Wrap
Assembly Source File
|
1998-05-05
|
15KB
|
495 lines
; Timed.Asm written 1998 by Tobias Ernst.
;
; This file installs interrupt service handlers that are needed by the
; patched TimEd executable and then spawns the patched TimEd executable,
; and afterwards deinstalls the interrupt service handlers. The patched
; TimEd executable is expected to have the name "timed.ovl" and reside
; in the same directory as the timed.com file that is assembled from
; timed.asm.
;
; This file is written for Borland TASM, but should also work with
; microsoft MASM and compatible assemblers, though I didn't test it.
; Assemble as follows:
;
; tasm timed.asm
; tlink timed /t
;
; If you use another linker, assure that a .COM file is generated, or
; convert the EXE file with EXE2BIN. It is important that the file is
; a .COM file!
DGROUP group _TEXT, _DATA
_TEXT SEGMENT BYTE PUBLIC 'CODE'
ASSUME CS: DGROUP, DS:DGROUP, SS:DGROUP
org 0100h
start: jmp main
; Symbolic contants for the interrupt vectors to use
vector1 equ 047h
vector2 equ 048h
vector3 equ 049h
vector4 equ 04Bh
vector5 equ 04Ch
; Interrupt Service Routine #1
; Function: Get a year number in tm.tm_year format from [bx+0a],
; and push the modulo of the division of this number by 100
; to the stack.
; Notes: As an interrupt normally does not leave anything on the
; stack, this code looks a bit unconventional
; Purpose: This routine is called when writing a FTSC date stamp
; into a *.squish or *.msg base, and when writing a hudson
; time stamp. It prevents the 2 digit date field from spilling
; (without the modulo 100 operation, "100" would be written
; there in the year 2000 instead of 00).
service1: mov cs:[tmpax],ax ;save AX
pop ax ;get the return address from stack
mov cs:[retadd1],ax
pop ax
mov cs:[retadd2],ax
pop ax
mov cs:[retadd3],ax
mov ax, word ptr[bx+0ah]
push cx
mov cl, 064h ; only pass the modulo 100 value
idiv cl ; to sprintf
pop cx
mov al,ah
xor ah,ah
push ax
push cs:[retadd3] ;Now jump back
push cs:[retadd2]
push cs:[retadd1]
mov ax,cs:[tmpax]
iret
tmpax dw 0
retadd1 dw 0
retadd2 dw 0
retadd3 dw 0
; Interrupt Service Routine #2
; Function: Get a year number in tm.tm_year format from [bx+0a],
; add 1900 to it (which will yield a correct four digit number
; in all cases, and push it to the stack.
; Notes: As an interrupt normally does not leave anything on the
; stack, this code looks a bit unconventional
; Purpose: This routine is called when writing the day number for a
; %year variable in a message template. We have patched the
; executable to use %4.4d instead of 19%2.2d, so we have to
; push a four digit year number now instead of a two digit one.
service2: mov cs:[tmpax_2],ax ;save AX
pop ax ;get return address from stack
mov cs:[retadd1_2],ax
pop ax
mov cs:[retadd2_2],ax
pop ax
mov cs:[retadd3_2],ax
mov ax, word ptr[bx+0ah]
add ax, 1900d
push ax ;push the 4 digit year number
push cs:[retadd3_2] ;jump back
push cs:[retadd2_2]
push cs:[retadd1_2]
mov ax,cs:[tmpax_2]
iret
tmpax_2 dw 0
retadd1_2 dw 0
retadd2_2 dw 0
retadd3_2 dw 0
; Interrupt Service Routine 3
; Function: Convert a two digit year number that has is stored in AX
; into a DOS time compliant year number (which gives the
; number of years that have been passed since 1980).
; The method we use is save until 2080.
; Purpose: Prevent wrong binary timestamps (TimEd derives them from the
; two digit FTSC date) in Squish and MSG areas.
service3:
cmp ax, 80d ; is ax >= 80
jae twc ; if so -> 19xx
add ax, 20d ; otherwise: 20xx
iret
twc: sub ax, 80d
iret
; Interrupt Service Routine #4
; Function: Get a year number in tm.tm_year format from [si+0a],
; and push the modulo of the division of this number by 100
; to the stack.
; Notes: As an interrupt normally does not leave anything on the
; stack, this code looks a bit unconventional
; Purpose: This routine is called when writing the message info screen.
; The modulo operation prevents wrong output like "'110" instead
; of "'10" in the year of 2010.
service4: mov cs:[tmpax_4],ax ;save AX
pop ax ;get the return address from stack
mov cs:[retadd1_4],ax
pop ax
mov cs:[retadd2_4],ax
pop ax
mov cs:[retadd3_4],ax
mov ax, word ptr[si+0ah]
push cx
mov cl, 064h ; only pass the modulo 100 value
idiv cl ; to sprintf
pop cx
mov al,ah
xor ah,ah
push ax
push cs:[retadd3_4] ;Now jump back
push cs:[retadd2_4]
push cs:[retadd1_4]
mov ax,cs:[tmpax_4]
iret
tmpax_4 dw 0
retadd1_4 dw 0
retadd2_4 dw 0
retadd3_4 dw 0
; Interrupt Service Routine #5
; Function: - Get a two digit year number from [bp-0eh] to AX
; - Convert AX to a "tm.tm_year" compatible year number, that
; is, the number of years that have passed since 1900.
; Purpose: This routine is called when reading in a message from the
; Hudson Message Base. HMB only has a two digit year field,
; so we have to interpret a two digit year number somehow.
; Here, we assume that a year number of 00..79 is in 20xx,
; while a year number of 80..99 is in 19xx. This makes HMB
; save until 12/31/2079.
service5: mov ax, word ptr[bp-0eh]
cmp ax, 80d ; is ax >= 80
jae twc2 ; if so -> 19xx
add ax, 100d ; otherwise: 20xx, add 100 dec
twc2: iret
; The main program. These routines install the correct interrupt handlers,
; execute timed.ovl, and restore the original interrupt handlers. This code
; is fairly uninteresting.
main:
mov ax, cs ; set up the segment registers and stack
mov ds, ax
mov ss, ax
mov es, ax
mov ax, offset DGROUP:stack_high
mov sp, ax
; free unneeded memory
mov bx, offset DGROUP:highwater
shr bx, 4
inc bx
mov ah, 4Ah
int 21h
; store the old interrupt vectors
mov ah, 35h
mov al, vector1
int 21h
mov [oldofs1],bx
mov [oldseg1],es
mov al, vector2
int 21h
mov [oldofs2],bx
mov [oldseg2],es
mov al, vector3
int 21h
mov [oldofs3],bx
mov [oldseg3],es
int 21h
mov al, vector4
int 21h
mov [oldofs4],bx
mov [oldseg4],es
mov al, vector5
int 21h
mov [oldofs5],bx
mov [oldseg5],es
; set the new interrupt vectors
mov ah,25h
mov al, vector1
mov dx, offset DGROUP:service1
int 21h
mov al, vector2
mov dx, offset DGROUP:service2
int 21h
mov al, vector3
mov dx, offset DGROUP:service3
int 21h
mov al, vector4
mov dx, offset DGROUP:service4
int 21h
mov al, vector5
mov dx, offset DGROUP:service5
int 21h
; prepare the call to stringops
push ds
mov ax, offset DGROUP:paramoff
push ax
push ds
mov ax, offset DGROUP:paramseg
push ax
push ds
mov ax, offset DGROUP:prognameoff
push ax
push ds
mov ax, offset DGROUP:prognameseg
push ax
mov ah,062h ; get PSP
int 21h
push bx
xor ax,ax
push ax
call _stringops ; fill in the various structures
add sp, 014h
mov ax, [paramseg] ; call the DOS EXEC function
mov es, ax
mov bx, [paramoff]
mov dx, [prognameoff]
mov ax, [prognameseg]
mov ds, ax
mov ax, 04B00h
int 21h
mov ax, cs ; restore the registers
mov ds, ax
mov ss, ax
mov ax, offset DGROUP:stack_high
mov sp, ax
mov bx, ds ; restore the interrupt vectors
mov ah, 25h
mov al, vector1
mov dx, [oldofs1]
mov cx, [oldseg1]
mov ds, cx
int 21h
mov ds, bx
mov al, vector2
mov dx, [oldofs2]
mov cx, [oldseg2]
mov ds, cx
int 21h
mov ds, bx
mov al, vector3
mov dx, [oldofs3]
mov cx, [oldseg3]
mov ds, cx
int 21h
mov ds, bx
mov al, vector4
mov dx, [oldofs4]
mov cx, [oldseg4]
mov ds, cx
int 21h
mov ds, bx
mov al, vector5
mov dx, [oldofs5]
mov cx, [oldseg5]
mov ds, cx
int 21h
mov ds, bx
mov ax, 04c00h ; terminate program
int 21h
; _stringops
; This function fills in the huge bunch of tables that is required for the
; DOS exec function. It looks a bit weired, because it has originally been
; created by a C compiler.
_stringops proc near
push bp
mov bp,sp
sub sp,12
push si
push di
; /* pass the command line on as is */
les bx,dword ptr [bp+4]
mov al,byte ptr es:[bx+128]
mov byte ptr DGROUP:_cmdlin,al
cmp byte ptr DGROUP:_cmdlin,126
jle short @1@534
mov byte ptr DGROUP:_cmdlin,126
@1@534:
xor si,si
jmp short @1@618
@1@562:
les bx,dword ptr [bp+4]
add bx,si
mov al,byte ptr es:[bx+129]
mov byte ptr DGROUP:_cmdlin[si+1],al
inc si
@1@618:
mov al,byte ptr DGROUP:_cmdlin
cbw
cmp ax,si
jg short @1@562
mov byte ptr DGROUP:_cmdlin[si+1],13
; /* adjust the name of the file to be spawned in the env_block */
les bx,dword ptr [bp+4]
mov ax,word ptr es:[bx+44]
mov word ptr [bp-2],ax
mov word ptr [bp-4],0
mov ax,word ptr [bp-2]
mov dx,word ptr [bp-4]
mov word ptr [bp-6],ax
mov word ptr [bp-8],dx
jmp short @1@702
@1@674:
inc word ptr [bp-8]
@1@702:
les bx,dword ptr [bp-8]
cmp byte ptr es:[bx],1
jne short @1@674
les bx,dword ptr [bp-8]
cmp byte ptr es:[bx+1],0
jne short @1@674
mov ax,word ptr [bp-6]
mov dx,word ptr [bp-8]
add dx,2
mov word ptr [bp-10],ax
mov word ptr [bp-12],dx
mov word ptr [bp-6],ax
mov word ptr [bp-8],dx
jmp short @1@814
@1@786:
inc word ptr [bp-8]
@1@814:
les bx,dword ptr [bp-8]
cmp byte ptr es:[bx],0
jne short @1@786
les bx,dword ptr [bp-8]
mov byte ptr es:[bx-3],79
les bx,dword ptr [bp-8]
mov byte ptr es:[bx-2],86
les bx,dword ptr [bp-8]
mov byte ptr es:[bx-1],76
; /* fill in the parameter block */
mov ax,word ptr [bp-2]
mov word ptr DGROUP:_paramblock,ax
mov word ptr DGROUP:_paramblock+2,offset DGROUP:_cmdlin
mov word ptr DGROUP:_paramblock+4,ds
mov word ptr DGROUP:_paramblock+6,offset DGROUP:_fcb2
mov word ptr DGROUP:_paramblock+8,ds
mov word ptr DGROUP:_paramblock+10,offset DGROUP:_fcb2
mov word ptr DGROUP:_paramblock+12,ds
les bx,dword ptr [bp+16]
mov word ptr es:[bx],ds
les bx,dword ptr [bp+20]
mov word ptr es:[bx],offset DGROUP:_paramblock
les bx,dword ptr [bp+8]
mov ax,word ptr [bp-10]
mov word ptr es:[bx],ax
les bx,dword ptr [bp+12]
mov ax,word ptr [bp-12]
mov word ptr es:[bx],ax
pop di
pop si
mov sp,bp
pop bp
ret
_stringops endp
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
; These variables store the old interrupt vectors
oldseg1 dw ?
oldseg2 dw ?
oldseg3 dw ?
oldseg4 dw ?
oldseg5 dw ?
oldofs1 dw ?
oldofs2 dw ?
oldofs3 dw ?
oldofs4 dw ?
oldofs5 dw ?
; These variables are used to exchange data between the main program
; and the _stringops subroutine
paramoff dw 1
paramseg dw 2
prognameoff dw 3
prognameseg dw 4
; We do need a little bit of stack for our own ...
stack_low dw 128 dup (10)
stack_high dw 0
; These structures are filled in and passed to the DOS EXEC function
_cmdlin label byte
db 128 dup (0)
_paramblock label word
db 16 dup (0)
_fcb2 label word
db 35 dup (0)
_fcb1 label word
db 35 dup (0)
; This variable marks the last address in the file, so that the rest
; of the memory can be freed.
highwater db 0
_DATA ENDS
END start