COMPAQDOS300 equ 0 ; see text ! ; DCF77.ASM ; Written for the print described in Elrad 7+8/93. ; Copyright (C) 1992 Dipl.-Ing. Harald Milz and Elrad ; ; to be assembled with TASM DCF77, TLINK /t DCF77 ; the compilerswitch must be set to match processor: .8086 ; here no processor is prescribed. He who wants can ; make adjustments and say e.g. .186. i86 = (not @cpu) and 2 ; Bit 1 not set ? i186 = @cpu and 2 i286 = @cpu and 4 i386 = @cpu and 8 NOWARN RES if i86 pusha macro irp reg, push reg endm endm popa macro irp reg, pop reg endm endm ; if more than 3 bits are to be shifted, it is better ; (shorter), to do this with aid of CX. For shifts under 4 bits ; repeat-shifts are better (faster and shorter). perf macro task,what,num if (num GT 3) push cx mov cl,num task what,cl pop cx else rept num task what,1 endm endif endm endif ; the processors 80186/188 know multiple-shift commands. ; for 8086/88 the makro PERF will be called. xshl macro what,num if i186 shl what,num else perf shl,what,num endif endm xshr macro what,num if i186 shr what,num else perf shr,what,num endif endm xrcr macro what,num if i186 rcr what,num else perf rcr,what,num endif endm xrcl macro what,num if i186 rcl what,num else perf rcl,what,num endif endm WARN RES setownstk macro mov cs:[old_ss],ss ; secure old stackpointer mov cs:[old_sp],sp cli mov sp,cs mov ss,sp ; set new mov sp,offset stkstart; at CS:104h sti endm setoldstk macro cli mov ss,cs:[old_ss] ; reset old mov sp,cs:[old_sp] ; stackpointer sti endm .model tiny .code prognr equ 5 ; for INT 2Fh (prog-recognition) func_test equ 0 ; function DCF77 installed ? func_off equ 1 ; switch off DCF77 func_on equ 2 ; switch on DCF77 dcf equ 201h ; port-Adr. DCF-receiver JOYSTICK_1 equ 01000000b ; detect-Bit = Bit 6 (Pin 10) SIGNAL_1 equ 10000000b ; signalbit = Bit 7 (Pin 14) JOYSTICK_2 equ 00100000b ; detect-Bit = Bit 5 (Pin 2) SIGNAL_2 equ 00010000b ; signalbit = Bit 4 (Pin 7) timeout equ 80 ; about 1,1 sec ; The following range of variables was stored in the PSP, to save ; room. It should not be moved forward any more, becouse at least ; MSDOS 5.0 in the release of 22.03.91 stores the DOS-Version in the ; WORD-variables with an offset of 40h and when calling "Get Version" ; (INT 21h, AH=30h) reads those adresses (!). The stack for the set- ; routine is located after that. org 50h shift db ?,?,?,?,? ; shiftregister 40 bit count db ? ; ticker-counter seconds db ? ; seconds-counter clock db ? ; clock-buffer hours db ? ; \ minutes db ? ; | day db ? ; > buffer for Hex-values month db ? ; | year dw ? ; / date db ? ; set date ? time db ? ; set time ? old_sp dw ? ; buffer for DOS SP old_ss dw ? ; buffer for DOS SS criterr dw ?,? ; adress Critical Error Flag indos dw ?,? ; adress INDOS-Flag switch db ? ; IRET or JMP min_flag db ? ; if 59th second reached posit db ? ; if data ready signal db ? ; bitmaske for receiver detect db ? ; bitmaske for joystick-number bit_pos db ? ; here the recognized bits are stored timer_fast db ? ; timer altered? bit db ? ; here the last recognized bit is stored org 80h ; here DOS stores the command-line cmnd_len db ? ; length of line cmnd_data db ? ; ACSII text org 100h start: jmp install ; jump to install-routine even stkstart: ;========================================================== int8new proc far cmp cs:[timer_fast],0 ; is timer altered ? jne int8dcf ; then run DCF jmp tick ; else keep on ticking int8dcf: pusha push es push ds push cs push cs pop es ; set segmentregister pop ds inc [count] ; increment tickercounter cmp [count],timeout/10; and read ... (alter here for ; other trigger timing!) jne no_read ; abt 109 ms after rising (70/160 ms) mov dx,dcf ; flank in al,dx ; read bit xor al,[signal] ; for this receiver we must invert! mov cl,[bit_pos] rcl al,cl ; rotate x to the left mov bx,offset shift ; ... bit in carry mov [bit],1 jc shift_0 mov [bit],0 shift_0: call shift_1 ; shift in buffer jmp short int81 ; and leave no_read: cmp [count],(timeout*6)/8 ; wait for 820 ms (blocking) ; then coninue jb int81 ; search flank cmp [count],timeout ; if time-out ... jge minute ; ... assume minute. mov dx,dcf in al,dx ; read DCF-gate .... xor al,[signal] ; invert again mov ah,al ; ... and store value in AH xor al,[clock] ; compare with previous ... mov [clock],ah ; ... and store new value test al,[signal] ; Bit 4 ? (if flank: 1) jz int81 ; if no flank (0), leave test ah,[signal] ; signal after flank = 0 ? jz int81 ; then it was a decending flank. ; rising flank: inc [seconds] ; one second later ... cmp [seconds],60 ; if 60 th second reached, jne int80 mov [seconds],0 ; ... clear seconds cmp [min_flag],1 ; if minutes-flag set, jne int80 mov [posit],1 ; ... set posit-flag and mov [min_flag],0 ; ... clear minutes-flag int80: mov [count],0 ; after rising flank, set COUNT ; to 0 ... int81: pop ds ; ... and leave. pop es ; clear stack popa inc cs:[switch] ; have enough ticks passed? cmp cs:[switch],timeout/20 jne no_tick ; no? -> do not tick ! mov cs:[switch],0 tick: db 0eah ; JMP FAR int8old dw 0,0 no_tick: cli push ax mov al,20h ; interrupt-controller out 20h,al ; clear pop ax ; (end-of-interrupt-signal) iret minute: cmp [seconds],58 ; exactly 58 seconds? if not ; either the reception is ; jammed or it is the first ; minute after switching on. ; in both cases DO NOT ; SET CLOCK! je minute_past ; if ok -> calculate mov [seconds],59 ; else sync seconds to 59 jmp int80 ; ... leave, because a minute ; has not passed minute_past: mov [min_flag],1 ; report minute as ended inc [seconds] ; for 59th second mov bx,offset shift ; shift left once call shift_1 ; ready to go! ; test the parity for the date here, because bits are alligned ; perfectly here (bit-numbers according to timing [1]) mov al,[bx] ; first byte (bits 52 - 59) and al,7fh ; mask first bit mov ah,[bx+1] ; second byte (44 - 51) mov cl,[bx+2] ; third byte (36 - 43) xor ah,al xor ah,cl ; XOR all jpo no_date ; odd parity -> leave mov [date],1 ; even parity -> 1 no_date: call shift_1 ; for correct bit position ; calculate time: mov al,[bx+4] ; min or al,al jpo int80 ; if parity odd and al,7fh ; mask bit 1 ; data is BCD-encoded! call bcd_hex ; convert to HEX mov [minutes],al mov al,[bx+3] ; std and al,7fh ; mask or al,al jpo int80 ; if parity false and al,3fh ; mask first two bits call bcd_hex ; convert mov [hours],al mov [time],1 ; now time can be set. ; Datum auswerten: cmp [date],1 ; if not ok je calc_date ; do not continue jmp int80 calc_date: mov al,[bx+1] ; month set right and al,1fh ; mask call bcd_hex mov [month],al call shift_1 call shift_1 call shift_1 ; after 5 shifts the call shift_1 ; year is alligned right in [bx+1] call shift_1 mov al,[bx+1] ; year call bcd_hex xor ah,ah cmp al,92 ; if year < 92, jnc after92 ; year 20xx is assumed, add ax,100 after92: add ax,1900 ; if not 19xx mov [year],ax call shift_1 ; after 2 shifts ... call shift_1 mov al,[bx+3] ; mask day and al,3fh call bcd_hex mov [day],al jmp int80 ; end int8new endp ;========================================================== shift_1 proc near rcr byte ptr [bx],1 ; byte wise or else rcr byte ptr [bx+1],1 ; the orientation is rcr byte ptr [bx+2],1 ; not correct. rcr byte ptr [bx+3],1 rcr byte ptr [bx+4],1 ret shift_1 endp ;========================================================== bcd_hex proc near mov bp,ax ; BCD in AL and ax,000fh ; mask xchg ax,bp ; exchange and ax,00f0h ; mask xshl ax,4 ; shift left or ax,bp ; ignore bits aad ; quasi-ASCII in AX ret ; HEX in AL bcd_hex endp ;========================================================== even int28new proc far ; undoc. for MS-DOS 5.0 call set_clock int28end: db 0eah ; JMP FAR int28old dw 0,0 int28new endp ;========================================================== even int2Anew proc far cmp ah,84 ; equals INT 28h jne int2Aend call set_clock int2Aend: db 0eah int2Aold dw 0,0 int2Anew endp ;========================================================== even int2Fnew proc far cmp ah,0ffh ; 0FFh = not accounted for jne int2Frest cmp bx,prognr ; identify jne int2Frest cmp al,func_test ; identify ?? jne int2F_off mov al,0ffh ; 0FFh = "present" jmp short int2Fready int2F_off: cmp al,func_off ; switch off ?? jne int2F_on xor ax,ax ; restore timer to call set_timer ; normal tick-rate mov cs:[timer_fast],0 mov cs:[posit],0 jmp short int2Fready int2F_on: cmp al,func_on ; switch on ?? mov ax, 40h ; change timer again call set_timer mov cs:[timer_fast],1 jne int2Fready int2Fready: iret int2Frest: push ax ; save ax for5 other ; programmes cmp ax,1680h ; equals to INT 28h jne int2F_1 call set_clock jmp short int2Fend int2F_1: cmp ax, 1605h ; will WINDOWS start ?? jne int2F_2 xor ax,ax ; reset timer to normal call set_timer ; tick rate mov cs:[timer_fast],0 mov cs:[posit],0 jmp short int2Fend int2F_2: cmp ax, 1606h ; will WINDOWS stop ?? jne int2Fend mov ax, 40h ; change tick rate again call set_timer mov cs:[timer_fast],1 int2Fend: pop ax ; pass function on to other ; programmes db 0eah ; JMP FAR int2Fold dw 0,0 int2Fnew endp ;========================================================== set_clock proc near cmp cs:[posit],1 ; should clock be set at all? je set_now jmp end_set set_now: setownstk ; init own satck push bx push es les bx,dword ptr cs:[indos] cmp byte ptr es:[bx],1 ; test InDOS-Flag jg set_out ; if InDOS <= 1 here ; INT 21h-calls may be ; honoured safely. les bx,dword ptr cs:[criterr] cmp byte ptr es:[bx],0 jne set_out ; critical error flag ? push ax ; push used registers push cx push dx push ds push cs pop ds ; DS := CS mov [posit],0 ; reset point flag cmp [date],1 ; date ok ? jne set_time mov ah,2bh mov cx,[year] mov dh,[month] mov dl,[day] ; preset register int 21h ; and set set_time: cmp [time],1 ; time ok ? jne do_not_set ; fr die ganz genauen: mov al,[count] ; calculate 1/100 second xor ah,ah xor dx,dx mov bx,10986/timeout ; 2*10E5 * 3600 / 65536 mul bx mov bx,100 div bx mov dl,al mov ah,2dh mov ch,[hours] ; preset register mov cl,[minutes] mov dh,[seconds] int 21h ; and set do_not_set: mov [date],0 ; "set" mov [time],0 pop ds ; restore used pop dx ; registers pop cx pop ax set_out: pop es pop bx setoldstk ; restore stack end_set: ret set_clock endp ;========================================================== set_timer proc near cli ; block hardware-interrupts push ax mov al,00110110b ; load lo-, then hi-byte; Mode 3 out 43h,al mov al,0 out 40h,al pop ax out 40h,al ret set_timer endp ;========================================================== ; everything up to here will be loaded resident (if its ok) end_resident: ;========================================================== on_off proc near push ax mov ax,0ff00h+func_test ; call INT 2Fh with own ID mov bx,prognr ; if answer positive --> int 2fh ; already installed mov dx, offset k_driver cmp al,0ffh jne error ; fault: no driver pop ax mov ah, 0ffh mov bx,prognr ; call; if answer int 2fh ; positive -> already done ret on_off endp ;========================================================== cmnd_line proc near mov cl, [cmnd_len] ; test if DOS has transferred cmp cl, 0 ; at all je no_param ; investigate command line mov bx, offset cmnd_data next: cmp byte ptr [bx], ' ' ; skip SPACE je blank cmp byte ptr [bx], 08h ; skip TAB je blank cmp byte ptr [bx], 0ffh ; skip Alt-255 je blank cmp byte ptr [bx], '-' ; options must start je test_cmnd ; with '-' or '/' cmp byte ptr [bx], '/' je test_cmnd jmp cmnd_error blank: inc bx ; investigate next character dec cl ; if present jne next jmp short no_param ; else try to install ; investigate for valid command test_cmnd: cmp cl, 2 ; there must be at least another jb cmnd_error ; 2 characters, else fault inc bx and byte ptr [bx], 0dfh ; convert to upper-case cmp byte ptr [bx], 'U' ; was it '-U' ?? jne test_cmd_1 ; no: continue mov al, func_off ; switch off driver call on_off mov dx, offset is_off ; give message jmp exit ; and end program test_cmd_1: cmp byte ptr [bx], 'A' ; was it '-A' ?? jne cmnd_error ; no: continue mov al, func_on ; switch on driver call on_off mov dx, offset is_on ; give message jmp exit ; and end program cmnd_error: mov dx, offset help ; give help message jmp error ; and end program no_param: ; already installed? mov ax,0ff00h+func_test ; call INT 2Fh with own ID mov bx,prognr ; if answer positive -> int 2fh ; already installed mov dx, offset fault cmp al,0ffh je error ; error: already installed ret cmnd_line endp ;========================================================== error proc near mov ah,9 int 21h ; give message mov ax,4c01h ; return code 1 int 21h ; and end error endp ;========================================================== exit proc near mov ah,9 int 21h ; give message mov ax,4c00h ; return code 0 int 21h ; and end exit endp ;========================================================== install proc near mov dx,offset dcf77 mov ah,9 ; give message int 21h call cmnd_line ; clac parameter. the function ; now returns when the driver ; should be installed ! ; initialise variables (set to 0): mov cx,offset stkstart - offset shift mov di,offset shift xor al,al rep stosb ; receiver present? mov dx,dcf in al,dx mov [detect],JOYSTICK_1 ; init variables mov [signal],SIGNAL_1 test al,JOYSTICK_1 ; buffer testbit jz init_1 mov dx,offset no_dcf test al,JOYSTICK_2 ; was it the second? jnz error ; no, wrong. mov byte ptr [js_number], '2' mov [detect],JOYSTICK_2 mov [signal],SIGNAL_2 init_1: mov dx,offset js_1 mov ah,9 ; give message int 21h ; calculate bit-number for set bit in bit-mask mov al, [signal] ; load bit-mask for signal clc xor cl, cl ; bitnumber = 0 init_2: inc cl ; increment bit-number and rcl al, 1 ; shift bit-mask until jnc init_2 ; bit is in carry mov [bit_pos],cl ; get address of INDOS-flag mov ah,34h ; undoc. before MSDOS 5.0 int 21h mov [indos],bx mov [indos+2],es ; get address of critical error flag if COMPAQDOS300 mov ax,[indos] add ax,1aah ; see text mov [criterr],ax mov ax,[indos+2] mov [criterr+2],ax else mov ah,30h ; get DOS-version int 21h xchg ah,al ; hi-byte in Al! mov dx,offset f_version cmp ah,2 jb error ; if DOS 1.x jg DOS_30 ; if DOS 3.x mov ax,[indos] inc ax ; in DOS 2.x one byte behind mov [criterr],ax mov ax,[indos+2] mov [criterr+2],ax jmp short criterr_end DOS_30: cmp ax,030ah ; hex 3.10 jge DOS_3x mov ax,[indos] dec ax ; in DOS 3.0 mov [criterr],ax mov ax,[indos+2] mov [criterr+2],ax jmp short criterr_end DOS_3x: DOS_5: cmp ah,4 ; for version 4.x je DOS_4x mov ax,5d06h ; in V. 3.1x - 3.3x and 5.0 ; DRDOS 5.0 equals DOS 3.31 jmp short criterr_adr DOS_4x: mov ax,5d0bh ; in version 4.x criterr_adr: int 21h ; pointer comes in DS:SI mov cs:[criterr],si mov cs:[criterr+2],ds push cs pop ds ; reset DS criterr_end: endif ; secure interrupts mov ax,3508h int 21h mov [int8old],bx mov [int8old+2],es mov ax,3528h int 21h mov [int28old],bx mov [int28old+2],es mov ax,352ah int 21h mov [int2Aold],bx mov [int2Aold+2],es mov ax,352fh int 21h mov [int2Fold],bx mov [int2Fold+2],es ; reset interrupts mov ax,2528h mov dx,offset int28new int 21h mov ax,252ah mov dx,offset int2Anew int 21h mov ax,252fh mov dx,offset int2Fnew int 21h ; change timer-channel 0: mov ax, 40h call set_timer ; and change INT 08: mov dx,offset int8new mov ax,2508h int 21h sti ; release interrupts mov [timer_fast],1 mov es,ds:[2ch] ; release environment mov ah,49h int 21h mov dx,offset end_resident add dx, 000fh xshr dx, 4 mov ax,3100h ; end resident part int 21h install endp dcf77 db 13,10,'*** DCF77 *** Versie 1.1 ... $' js_1 db 'aan joystick-poort ' js_number db '1 genstalleerd.',13,10,'$' k_driver db 'Driver niet gevonden!',13,10,'$' help db 13,10,10,'Aanroepen: DCF77 [optie]',13,10 db 'Opties: -? Toon deze text ',13,10 db ' -Uit Driver deactiveren ',13,10 db ' -Aan Driver reactiveren ',13,10 db ' i.p.v. ''-'' kan ook ''/'' gebruikt worden',13,10 db '$' fault db 'Driver al genstalleerd!',13,10,'$' no_dcf db 'Ontvanger niet gevonden!',13,10,'$' f_version db 'Verkeerde DOS-versie!',13,10,'$' is_off db 'Gedeactiveerd.',13,10,'$' is_on db 'Gereactiveerd.',13,10,'$' end start