print macro string
     lea   dx,string
     mov   ah,9
     int   21h
     endm
cseg      segment para public 'CODE'
          assume   cs:cseg,ds:cseg,ss:cseg,es:cseg
          org      100h     ;end of program segment prefix
enter:    jmp      install  ;skip over data
int1ch    equ      this  dword
int1cip   dw  0
int1ccs   dw  0
int16h    equ      this  dword
int16ip   dw  0
int16cs   dw  0
int9h    equ      this  dword
int9ip   dw  0
int9cs   dw  0
semaphore db  0
dcount    dw  0
counter   dw  0
dswitch   db  0
kswitch   db  0
message   db  65 dup(0)
crlf_sw   db  0
on_sw     db  0
message_len dw 0
char_count dw  0
period    dw  0
int_9:
; this is the keyboard interrupt,
; if int_9 is pressed then reset the deadman
; first set the deadman
;
           pushf
           call  cs:int9h
           push  ax
           mov   ax,cs:dcount
           mov   cs:counter,ax
; process the keystroke
; now we can check to see if there was a keystroke
; check the semaphore
           mov   al,1
lock       xchg  cs:semaphore,al
           cmp   al,1
           jne   no_sem
           pop   ax
           iret
no_sem:
           sti
           mov   ax,0100h
           int   16h
           jz    no_key
           cmp   ax,2000h
           jne   no_key
; check the shift status
           mov   ah,2
           int   16h
; the shift status comes in al
           and   al,0ah
           cmp   al,0ah
           jne   no_key
           mov   ah,0
           int   16h
           xor   cs:on_sw,1
           jnz   blopper
           call  blip
           call  blip
           jmp   no_key
blopper:
           call  blop
           call  blop
           call  blop
no_key:
           mov   cs:semaphore,0
           pop    ax
           iret
int_16:
; check to see if we have been called upon
           sti
          cmp   ax,'DM'
          jne   i16_1
          cmp   dx,'KG'
          jne   i16_1
          mov   ax,'EC'
           push cs
          pop   es
          iret
i16_1:
; this is the keyboard interrupt
           cmp   cs:semaphore,1
           je    no_kb
           cmp   cs:on_sw,1
           je    no_kb
           cmp   cs:kswitch,1
           jne   no_kb
; this is where we have to simulate the keystrokes
           cmp   ah,2
           jge   no_kb
           cmp   ah,0
           jl    no_kb
; its for us!
; message has the bytes to get
; char_count has the number of bytes left to send
; message_len has the length of the message
; message_len - char_count has the offset into message
           cmp   ah,0
           je    int16_f0
           jmp   int16_f1
; interrupt 16 func 1
; same as 0 except no dec of char_count
int16_f0:
           cmp  cs:char_count,0
           je   do_cr0
           push bx
           mov  bx,cs:message_len
           sub  bx,cs:char_count
           mov  al,cs:message[bx]
           dec  cs:char_count
           pop  bx
           jmp   exit_char
do_cr0:
; this is if there is cr in the works
          mov   cs:kswitch,0
           push  ax
           mov   ax,cs:dcount
           mov   cs:counter,ax
           pop   ax
          cmp   cs:crlf_sw,0
           je   no_kb
           mov   al,13
           jmp   exit_char
no_kb:
           jmp   cs:int16h
; interrupt 16 func 1
; same as 0 except no dec of char_count
int16_f1:
           cmp  cs:char_count,0
           je   do_cr1
           push bx
           mov  bx,cs:message_len
           sub  bx,cs:char_count
           mov  al,cs:message[bx]
           pop  bx
; all popped out
exit_char:
           mov  ah,0
           cmp   ah,1
           clc
       db  0cah,02,00
;           ret  far  2
do_cr1:
; this is if there is cr in the works
          cmp   cs:crlf_sw,0
           je   no_kb
           mov   al,13
           jmp   exit_char
int_1c:
           pushf
           call  cs:int1ch
           cmp   cs:on_sw,1
           je    no_counter
           cmp   cs:kswitch,1
           je    no_counter
; decrement the counter
           dec   cs:counter
; check for warning
           cmp   cs:counter,40
           jne   no_bloop
           sti
           call blip
           call blop
           call blip
no_bloop:
           cmp   cs:counter,0
           jg    no_counter
; set the kswitch
           push  ax
           mov   ax,cs:message_len
           mov   cs:char_count,ax
           pop   ax
           mov   cs:kswitch,1
no_counter:
           iret
tone:
        mov   dx,3    ; tone rate
        mov   bx,240  ; starting freq
        mov   cx,750  ; ending frequency
        cmp   si,0
        jg    do_blip
; here we do blop
        mov   bx,630  ; starting freq
        mov   cx,150  ; ending frequency
do_blip:
; turn on tone
i0:
        push  cx
        push  bx
        in     al,61h
        or     al,3
        out    61h,al
; loop parameters
        mov    cx,bx
 ; set frequency
        mov    dx,12h
        mov    ax,34deh
        div    cx
        mov    cx,ax
; set tone
        mov    al,cl
        out    42h,al
        mov    al,ch
        out    42h,al
i2:     loop i2
        pop    bx
        pop    cx
        cmp    bx,cx
        je     ip3
        add    bx,si
        jmp    i0
ip3:
 ;turn off tone
        in  al,61h
        and al,11111100b
        out 61h,al
        ret
blip:
        push  ax
        push   bx
        push   cx
        push   dx
        push   si
        mov   si,30
        call  tone
        pop   si
        pop    dx
        pop    cx
        pop    bx
        pop    ax
        ret
blop:
        push  ax
        push   bx
        push   cx
        push   dx
        push   si
        mov   si,-30
        call  tone
        pop   si
        pop    dx
        pop    cx
        pop    bx
        pop    ax
        ret
install   proc  near
; check the command line
          mov   cl,byte ptr ds:[80h]
          mov   ch,0
          cmp   cx,2
          jg    inst_1
          call  blop
          call  blop
          call  blop
          print error1
          int   20h
inst_1:
; check to see if we are already installed
          mov   ax,'DM'
          mov   dx,'KG'
          int   16h
          cmp   ax,'EC'
          jne   inst_9
; its already installed
          call  blop
          print mess1
          mov   no_inst,1
          jmp   set_parms
inst_9:
;
set_parms:
          mov   cl,byte ptr ds:[80h]
          mov   ch,0
          mov   si,81h
          cld
p_loop:
          lodsb
          cmp   al,20h
          je   end_p_loop
          cmp   al,13
          je   end_p_loop
          cmp   al,'/'
          je    got_slash
end_p_loop:
          loop  p_loop
          jmp   exit_prog
got_slash:
          lodsb
          call  upper
          cmp   al,"O"
          je    do_on
          cmp   al,"F"
          je    do_off
          cmp   al,"C"
          je    do_CRLF
          cmp   al,"M"
          je    do_message
          cmp   al,"P"
          je    do_P
          jmp   end_p_loop
do_p: ; got the period
          lodsb
          dec   cx
          jg    no_exit_prog
          jmp   exit_prog
no_exit_prog:
          push  cx
          mov   cl,100
          mov   ah,0
          and   al,0fh
          mul   cl
          mov   bx,ax
          pop   cx
          lodsb
          dec   cx
          jbe   exit_prog
          push  cx
          mov   cl,10
          mov   ah,0
          and   al,0fh
          mul   cl
          add   bx,ax
          pop   cx
          lodsb
          dec   cx
          jbe   exit_prog
          mov   ah,0
          and   al,0fh
          add   bx,ax
          mov   es:period,bx
; multiply by 18.5 times period
          push  cx
          mov   cx,bx
          mov   ax,37
          mul   cx
          inc   ax
          shr   ax,1
          mov   es:dcount,ax
          mov   es:counter,ax
          pop   cx
           jmp  ret_loop
do_on:
           mov  es:on_sw,0
           jmp  ret_loop
do_off:
           mov  es:on_sw,1
           jmp  ret_loop
do_crlf:
           mov  es:crlf_sw,1
           jmp  ret_loop
ret_loop:
          dec   cx
          jle   exit_prog
          jmp   p_loop
do_message:
; here the message must be transferred
          mov   es:message_len,0
          lea   di,message
          cld
          jmp   end_m_loop
m_loop:
          lodsb
          cmp   al,'/'
          jne   no_got_slash
          jmp   got_slash
no_got_slash:
          cmp   al,13
          je    exit_prog
          stosb
          inc   es:message_len
end_m_loop:
          loop  m_loop
          jmp   exit_prog
; exit the routine
exit_prog:
; get the interrupts
          cmp   no_inst,1
          jne   ok_install
          int   20h
ok_install:
          mov   ax,3509h
          int   21h
          mov   int9ip,bx
          mov   int9cs,es
          mov   ax,3516h
          int   21h
          mov   int16ip,bx
          mov   int16cs,es
          mov   ax,351ch
          int   21h
          mov   int1cip,bx
          mov   int1ccs,es
; set the interrupts
          push  cs
          pop   es
          lea   dx,int_9
          mov   ax,2509h
          int   21h
          lea   dx,int_16
          mov   ax,2516h
          int   21h
          lea   dx,int_1c
          mov   ax,251ch
          int   21h
          push  cs
          pop   es
          print  mess2
          lea   dx,install
          int   27h
upper:
          cmp   al,'a'
          jge   up_1
          ret
up_1:
          cmp    al,'z'
          jle    up_2
          ret
up_2:
          sub   al,32
          ret
no_inst   db  0
error1    db   'The correct form of the DEADMAN command is:',13,10
          db   'DEADMAN /Mmessage /C /Pnnn /F /O',13,10
          db   'where message is a message that will appear every',13,10
          db   'nnn seconds when the keyboard is not being used.',13,10
          db   'Anything following the /M up to the next /',13,10
          db   'or the end of the line is considered a message.',13,10
          db   'A message can be up to 64 bytes long.',13,10
          db   'DEADMAN will beep a second before he goes off so',13,10
          db   'touch the shift key if you want to stop him.',13,10
          db   'The nnn after the /P is the period that DEADMAN',13,10
          db   'will wait between messages in seconds and can be',13,10
          db   'from 001 to 999. Leading zeros are required!',13,10
          db   'The /C means follow the message with a carriage',13,10
          db   'return - line feed. /F means turn off DEADMAN.',13,10
          db   '/O means turn DEADMAN back on.',13,10,'$'
mess1     db   13,10,'DEADMAN is already installed - updating...'
          db   13,10,'$'
mess2     db   'DEADMAN is resident.',13,10
          db   'Press Alt-Left Shift D to toggle deadman on and off.',13,10
          db   'DEADMAN - Asleep at the wheel software.',13,10
          db   'Copyright 1987 by Keith P. Graham',13,10
          db   'Released to the public domain at PC-Rockland',13,10
          db   '(914) 353-2176',13,10,'$'
install   endp
cseg      ends
          end      enter
