home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1026 / nprint.asm next >
Assembly Source File  |  1990-12-28  |  17KB  |  681 lines

  1. ;****************************************************************************
  2. ;  NPRINT.COM:   A TSR to submit a file to PRINT.COM
  3. ;****************************************************************************
  4.  
  5. cseg    segment    para public 'CODE'
  6.     assume    cs:cseg, ds:nothing, es:nothing, ss:nothing
  7.  
  8.  
  9.     org    100h
  10. entpt:    jmp    install        ; Jump to the Install code
  11.  
  12. VIDEO        EQU    10h    ; Interrupts used within NPRINT
  13. KEYBOARD    EQU    16h
  14. PRINTER        EQU    17h
  15. MULTIPLEX    EQU    2Fh
  16. DOS        EQU    21h
  17. MONOSCR        EQU    0B000h    ; Monochrome Screen Memory Location
  18. COLORSCR    EQU    0B800h    ; CGA (COLOR) Screen Memory Location
  19.  
  20. copyright    db    'NPRINT v1.0:  (c) Copyright 1990 by Christopher D. Orr',13,10,'$'
  21. prompt        db    'Enter Filename: ',0
  22. no_print    db    'Printer is not responding.  Please correct.  Hit any Key',0
  23. printpack    db    0,0,0,0,0
  24. invoke_flag    db    0    ; Flag set whenever a request for us in pending
  25. cursor        dw    0    ; Original cursor position
  26. pr_attribute    db    07h    ; Attribute for the Prompt/Messages
  27. scr_attribute    db    07h    ; Attribute for data entry
  28. do_prtsc    db    0    ; Flag to indicate whether to do a screen print
  29. display_page    db    0    ; Current Screen display page
  30. tone        dw    0    ; Storage for frequency of bell when rung
  31. CritSectFlag    dd    0    ; Critical Section Flag
  32. old_int_5    dd    0    ; Address of original print screen handler
  33. old_int_28    dd    0    ; Address of original DOS handler
  34. pause_msg    db    ' ... ',0
  35. errmsg        db    'PRINT returned error code '
  36. errcode        db    0,0
  37.         db    'h:  ',0
  38.  
  39. err_table    dw    errmsg1, errmsg2, errmsg3, errmsg4, errmsg5
  40.         dw    e_unknown, e_unknown, errmsg8, e_unknown, e_unknown
  41.         dw    e_unknown, errmsgc, e_unknown, e_unknown, errmsgf
  42. errmsg1        db    'Function Invalid',0
  43. errmsg2        db    'File not Found',0
  44. errmsg3        db    'Path not Found',0
  45. errmsg4        db    'Too many files open',0
  46. errmsg5        db    'Access Denied',0
  47. errmsg8        db    'PRINT Queue full',0
  48. errmsgc        db    'Filename too long',0
  49. errmsgf        db    'Drive invalid',0
  50. e_unknown    db    'Unknown Return Code',0
  51.  
  52. datasize    =    ($ - offset copyright)
  53.  
  54.  
  55. ;-----------------------------------------------------------------------------
  56. ;  Interrupt Handlers - This procedure defines code to determine when/if we
  57. ;                       should invoke the NPRINT main routine.
  58. ;-----------------------------------------------------------------------------
  59. nprint    proc    far
  60. int28:
  61.     cmp    cs:[invoke_flag], 0FFh    ; Should we invoke ourselves ?
  62.     jnz    short int_return    ; No, so get out
  63.     call    main            ; Invoke NPRINT
  64.     pushf                ; Push the flags
  65.     cli                ; Disable interrupts
  66.     push    cs            ; Push the CS register
  67.     mov    di,offset cs:int_return    ; When we return, we want to exit
  68.     push    di
  69.     jmp    dword ptr cs:old_int_28    ; Transfer control to old int 28h
  70.  
  71. int5:
  72.     push    bx
  73.     push    es
  74.     mov    bx,word ptr CritSectFlag[0]    ; Look at the Crit. Sec. Flag
  75.     mov    es,word ptr CritSectFlag[2]
  76.     cmp    byte ptr es:[bx],0h        ; If non-zero, DOS is busy
  77.     je    ok2run                ; Zero, so invoke NPRINT
  78.     pop    es
  79.     pop    bx
  80.     mov    cs:[invoke_flag], 0FFh        ; Leave a flag to tell us to
  81.     jmp    short int_return
  82.  
  83. ok2run:
  84.     pop    es
  85.     pop    bx
  86.     call    main                ; Call NPRINT
  87.  
  88. int_return:
  89.     iret                    ; and we return ...
  90.  
  91. endp    nprint
  92.  
  93.  
  94. ;-----------------------------------------------------------------------------
  95. ;  Main Procedure - Responsible for processing user request
  96. ;-----------------------------------------------------------------------------
  97. main    proc    near
  98.     sti            ; Allow Interrupts
  99.  
  100.     push    bp        ; Access to stack
  101.     mov    bp,sp        ; Before we push anything
  102.  
  103.     pushf            ; Save the state of the machine
  104.     push    es        ; because we're gonna make
  105.     push    ds        ; a mess of it
  106.     push    di
  107.     push    si
  108.     push    dx
  109.     push    cx
  110.     push    bx
  111.     push    ax
  112.  
  113.     mov    ax,cs            ; Establish the proper segments
  114.     mov    ds,ax
  115.     mov    es,ax            ; Make ES point to code segment
  116.     assume    ds:cseg, es:cseg    ; Tell the assembler
  117.  
  118.  
  119. ok2load:
  120.     mov    [invoke_flag], 0h    ; Clear Invocation flag
  121.  
  122.     mov    ah,0fh            ; Get the current Display Mode
  123.     int    VIDEO
  124.     mov    [display_page],bh    ; Save current page
  125.     cmp    al,07h            ; Is this a Monochrome Monitor ?
  126.     je    mono            ; Yes, so we are okay
  127.     cmp    al,03h            ; Is this a CGA Monitor in 80 col mode?
  128.     je    cga            ; Yes, so we are okay
  129.     call    ring_bell        ; No - so don't execute.  Just abort.
  130.     jmp    done            ; Exit NPRINT
  131.  
  132. mono:
  133.     mov    ax,MONOSCR    ; Monochrome Display
  134.     jmp    short saveline
  135. cga:
  136.     mov    ax,COLORSCR    ; CGA display
  137.  
  138. saveline:
  139.     mov    ds,ax        ; Set up the Data Segment
  140.     mov    si,3840        ; Start at position row=25, column=1
  141.     mov    cx,80        ; 80 Character lines
  142.     lea    di, screenbuf
  143.     push    es        ; Save screen memory locations
  144.     push    ds
  145.     push    si
  146.     cld            ; Make sure we copy from right to left
  147.     rep    movsw        ; Move character & Attribute
  148.  
  149.     mov    ax,cs        ; Restore the Data Segment
  150.     mov    ds,ax
  151.  
  152.     mov    ah,03h        ; Get the Current Cursor Position
  153.     int    VIDEO
  154.     mov    [cursor], dx    ;    and save it.
  155.  
  156.     call    clear_25    ; Goto and Clear Line #25
  157.     lea    si, prompt    ; Display the filename prompt for the user
  158.     call    wr_string
  159.  
  160.     lea    dx,filename    ; Point to out filename buffer
  161.     mov    ax,60        ; Limit filename to 60 characters
  162.     call    rd_string    ; Request the filename from the user
  163.     
  164.     cmp    ax,0        ; Did we get a file name ???
  165.     jne    submit_file    ; No, so don't do the print routine
  166.     mov    ah,02h        ; Let's see if the printer is busy
  167.     mov    dx,0        ; Check printer number 0
  168.     int    PRINTER
  169.     test    ah,01101001B    ; Clear bits 8,5,3,2 -> meaningless to us
  170.     jz    prtsc_okay
  171.     lea    si, no_print    ; No, then there is a problem w/
  172.     call    clear_25    ;   the printer.  Tell the user about it
  173.     call    ring_bell
  174.     call    wr_string
  175.     call    pause
  176.     jmp    restscreen    ; No sense printing a screen now, so don't
  177.  
  178. prtsc_okay:
  179.     mov    [do_prtsc],0FFh    ; Set the Print Screen Flag
  180.     jmp    restscreen
  181.  
  182. submit_file:
  183.     mov    [do_prtsc],0h    ; Clear the Print Screen Flag
  184.     mov    word ptr [printpack]+3,ds    ; Put the address into packet
  185.     mov    word ptr [printpack]+1,offset filename
  186.  
  187. retry_file:
  188.     lea    dx,printpack    ; Point to the printer request packet
  189.     mov    ax,0101h    ; Submit packet to Print Spooler
  190.     int    MULTIPLEX    ; Invoke Multiplex Service Interrupt for PRINT
  191.     jnc    restscreen    ; If the carry flag is set, then we had an error
  192.  
  193.     cmp    ax,09h        ; Is the spooler busy ?   (NOTE: endless loop?)
  194.     je    retry_file    ; Yes, so try again
  195.     call    display_errmsg    ; Some other kind of error
  196.  
  197. restscreen:
  198.     lea    si, screenbuf    ; Point to our screen buffer
  199.     pop    di        ; Restore Screen Memory Locations
  200.     pop    es
  201.     pop    ds        ; Restore the Data Segment
  202.     mov    cx,80        ; Still name 80 character line
  203.     cld            ; Make sure we copy from right to left
  204.     rep    movsw
  205.  
  206.     mov    ax,cs        ; Restore the Data and Extended Segment values
  207.     mov    ds,ax
  208.     mov    es,ax
  209.  
  210.     mov    dx,[cursor]    ; Restore the cursor position to what it was
  211.     mov    bh,[display_page]
  212.     mov    ah,02h
  213.     int    VIDEO
  214.  
  215. ;----------------------------------------------------------------------
  216. ; Restore the state of the machine when Int 05 occured
  217. ;----------------------------------------------------------------------
  218. done:
  219.     pop    ax
  220.     pop    bx
  221.     pop    cx
  222.     pop    dx
  223.     pop    si
  224.     pop    di
  225.     pop    ds
  226.     pop    es
  227.     popf
  228.     pop    bp
  229.  
  230. IFDEF DEBUG
  231.     mov    ax,4c00h
  232.     int    DOS
  233. ENDIF
  234.  
  235. assume    ds:nothing, es:nothing
  236.     cmp    cs:[do_prtsc],0FFh    ; Is the Print Screen flag set ?
  237.     jne    nprint_exit        ; no, so we should just exit
  238.  
  239.     pushf                ; Push the flags
  240.     cli                ; Disable interrupts
  241.     push    cs            ; Push the CS register
  242.     mov    di,offset cs:formfeed
  243.     push    di
  244.     jmp    dword ptr cs:old_int_5    ; Transfer control to old int 05h
  245.  
  246. formfeed:
  247.     pushf            ; Save the state of the machine yet again
  248.     push    es
  249.     push    ds
  250.     push    di
  251.     push    si
  252.     push    dx
  253.     push    cx
  254.     push    bx
  255.     push    ax
  256.     assume    ds:cseg, es:cseg    ;Tell the assembler
  257.  
  258.     mov    ah,00h        ; Print a character
  259.     mov    al,0ch        ; Namely a Formfeed (^L)
  260.     mov    dx,0        ; Printer number one
  261.     int    PRINTER        ; Invoke Print Interrupt
  262.  
  263.     pop    ax        ; Restore everything again
  264.     pop    bx
  265.     pop    cx
  266.     pop    dx
  267.     pop    si
  268.     pop    di
  269.     pop    ds
  270.     pop    es
  271.     popf
  272.  
  273. nprint_exit:
  274.     ret                ; And we return ...
  275. main    endp
  276.  
  277.  
  278. ;======================================================================
  279. ; WR_STRING - write string to console at specified location.
  280. ;    The string is ASCIIZ.   All registers are preserved.
  281. ;----------------------------------------------------------------------
  282. wr_string    proc    near
  283.  
  284.         push    si
  285.         push    bx
  286.         push    cx
  287.         push    ax
  288.  
  289. wnext_char:
  290.         lodsb                ; Load char in AL from DS:SI
  291.         or    al,al            ; If char is 0
  292.         jz    end_string        ;   Then end of ASCIIZ string
  293.         mov    ah,09h            ;   Else, write TTY
  294.         mov    bh,[display_page]
  295.         mov    bl,[pr_attribute]
  296.         mov    cx,01h            ; Only one
  297.         int    VIDEO            ;   thru BIOS
  298.         mov    al,1
  299.         call    move_cursor        ; Move the cursor to the right
  300.         jmp    short wnext_char    ;   and do it all again
  301. end_string:
  302.         pop    ax
  303.         pop    cx
  304.         pop    bx
  305.         pop    si
  306.         ret
  307.  
  308. wr_string    endp
  309.  
  310.  
  311. ;-----------------------------------------------------------------------------
  312. ; Read a String from the Keyboard and returns a pointer to it
  313. ;    Point to String using DS:DX.  RETURNS:  AX contains number of chars read
  314. ;-----------------------------------------------------------------------------
  315. rd_string    proc    near
  316.  
  317.         push    bx
  318.         push    cx
  319.         push    di
  320.         push    dx
  321.  
  322.         mov    di,dx        ; Setup our buffer pointer
  323.         mov    bx,ax        ; Store the max length in BX
  324. rnext_char:
  325.         mov    ah,0h        ; Read Keyboard Character Function
  326.         int    KEYBOARD
  327.         cmp    al,08h        ; Backspace ?
  328.         jne    check_enter    ; No, so jump
  329.         pop    dx        ; Restore the head of buffer pointer
  330.         push    dx
  331.         cmp    di,dx        ; Are we at the start of line?
  332.         jle    rnext_char    ; Yes, so ignore the backspace
  333.  
  334.         dec    di        ; Decrement our buffer pointer
  335.         mov    al,0        ;  and move the cursor back
  336.         call    move_cursor
  337.  
  338.         push    bx        ; Now we have to erase the character
  339.         mov    bh,[display_page]
  340.         mov    cx,1
  341.         mov    bl,[scr_attribute]
  342.         mov    ah,09h        ; Function for writing a character
  343.         mov    al,20h        ; Put a space on the screen
  344.         int    VIDEO
  345.         pop    bx
  346.         jmp    short rnext_char
  347.  
  348. check_enter:        
  349.         cmp    al,0dh        ; Did we get a return ?
  350.         jne    letter
  351.         mov    byte ptr [di],0h; Already at end of string, so add null
  352.         pop    dx        ; Restore pointer to start of buffer
  353.         sub    di,dx        ; How many chars did we read ?
  354.         mov    ax,di        ; Return that number in AX
  355.         pop    di        ; Restore the Data Index Register
  356.         pop    cx
  357.         pop    bx
  358.         ret        
  359.  
  360. letter:        
  361.         cmp    al,20h        ; Check for a valid character
  362.         jle    rnext_char    ; Ignore none printable characters/keys
  363.         cmp    al,7eh
  364.         jg    rnext_char
  365.  
  366.         pop    dx        ; Restore the head of buffer pointer
  367.         push    dx
  368.         add    dx,bx        ; Maximum of "bx" characters
  369.         cmp    di,dx        ; Yes, so are we at the end of buffer?
  370.         jne    char_ok
  371.         call    ring_bell    ; Yes, so sound the alarm
  372.         jmp    rnext_char
  373.  
  374. char_ok:
  375.         mov    [di],al        ; Store the character
  376.         inc    di        ; Increment our pointer
  377.  
  378.         push    bx
  379.         mov    cx,1
  380.         mov    bl,[scr_attribute]
  381.         mov    ah,09h        ; Display the character to the user
  382.         mov    bh,[display_page]
  383.         int    VIDEO
  384.         mov    al,1        ; Increment the cursor
  385.         pop    bx
  386.         call    move_cursor
  387.         jmp    rnext_char    ; Get the next character
  388.  
  389. rd_string    endp
  390.  
  391.  
  392. ;-----------------------------------------------------------------------------
  393. ; Increment or Decrement the current cursor position.  AX=0 implies decrement
  394. ;-----------------------------------------------------------------------------
  395. move_cursor    proc    near
  396.         push    bx
  397.         push    cx
  398.         push    dx
  399.  
  400.         mov    ah,03h        ; Get cursor position
  401.         mov    bh,[display_page]
  402.         cmp    al,0        ; If code 0, then decrement cursor
  403.         je    dec_cursor
  404.         int    VIDEO
  405.         inc    dl
  406.         jmp    short last_cur
  407. dec_cursor:
  408.         int    VIDEO
  409.         dec    dl
  410. last_cur:
  411.         mov    ah,02h        ; Set the Cursor Position
  412.         int    VIDEO
  413.  
  414.         pop    dx
  415.         pop    cx
  416.         pop    bx
  417.         ret
  418. move_cursor    endp
  419.  
  420.  
  421. ;----------------------------------------------------------------------
  422. ;    HEX2 -  Convert the AL register to hexidecimal digits.
  423. ;        The characters produced are stored at ES:DI.
  424. ;        All regs preserved.
  425. ;----------------------------------------------------------------------
  426. hex2    proc    near
  427.         push    ax
  428.         push    bx
  429.         push    cx
  430.  
  431.         mov    bx,ax
  432.         std                ;String ptr decrement
  433.         add    di,1            ;Point to end of string
  434.         mov    cx,2
  435. h10:
  436.         mov    al,bl            ;Want lower half
  437.         and    al,0fh            ; of this byte
  438.         add    al,90h            ;Convert AL to ASCII
  439.         daa
  440.         adc    al,40h
  441.         daa
  442.         stosb                ;Store at ES:DI
  443.         shr    bx,1
  444.         shr    bx,1
  445.         shr    bx,1
  446.         shr    bx,1
  447.         loop    h10
  448.  
  449.         pop    cx
  450.         pop    bx
  451.         pop    ax
  452.         inc    di
  453.         cld
  454.         ret
  455. hex2    endp
  456.  
  457.  
  458. ;-----------------------------------------------------------------------------
  459. ; Erase the 25th line of the screen and position the cursor on that line
  460. ;-----------------------------------------------------------------------------
  461. clear_25    proc    near
  462.         push    ax
  463.         push    dx
  464.         push    cx
  465.         push    bx
  466.  
  467.         mov    ah,02        ; Set the Cursor Position
  468.         mov    dh,24        ; Go to the 25th line
  469.         mov    dl,0h        ; column number 1.
  470.         int    VIDEO
  471.  
  472.         mov    ah,09h        ; Clear the line ...
  473.         mov    al,20h        ; using spaces
  474.         mov    bh,[display_page]
  475.         mov    bl,07h        ; Change the attributes to white
  476.         mov    cx,80        ; Write 80 characters
  477.         int    VIDEO
  478.  
  479.         pop    bx
  480.         pop    cx
  481.         pop    dx
  482.         pop    ax
  483.         ret
  484. clear_25    endp
  485.  
  486.  
  487. ;-----------------------------------------------------------------------------
  488. ;  Pause so the user can press a key ... any key ...
  489. ;-----------------------------------------------------------------------------
  490. pause    proc    near
  491.     mov    si, offset pause_msg
  492.     call    wr_string
  493.  
  494. top_pause:
  495.     mov    ah,01h            ; Wait for a key to be pressed
  496.     int    KEYBOARD
  497.     jz    top_pause
  498.     mov    ah,00h            ; Eat the keystroke
  499.     int    KEYBOARD    
  500.  
  501. ;    cmp    al,0dh            ; Only continue with the [RETURN] key
  502. ;    jne    top_pause
  503.  
  504.     ret
  505. pause    endp
  506.  
  507.  
  508. ;-----------------------------------------------------------------------------
  509. ; Display the Error Code and Message
  510. ;-----------------------------------------------------------------------------
  511. display_errmsg    proc    near
  512.     call    clear_25
  513.     call    ring_bell
  514.     mov    di,offset errcode    ; Display the error code
  515.     call    hex2
  516.     lea    si, errmsg
  517.     call    wr_string
  518.     cmp    al,0fh            ; Is the error off the scale ??
  519.     jle    table
  520.     lea    si,e_unknown        ; Yes, so display "unknown" message
  521.     jmp    short show_error
  522.  
  523. table:
  524.     xor    bh,bh            ; Build offset into error table
  525.     mov    bl,al            ; Can't use AX as a memory index ptr
  526.     dec    bx            ; Table is base 0, so reduce the index
  527.     shl    bx,1            ; Make offset into table (*2)
  528.     mov    si,err_table[bx]
  529.  
  530. show_error:
  531.     call    wr_string        ; Now display the error
  532.     call    pause
  533.     ret
  534. display_errmsg    endp
  535.  
  536.  
  537. ;-----------------------------------------------------------------------------
  538. ; Ring the Bell to indicate that an error occured - we create a special tone
  539. ;               so that the user knows it is NPRINT that did it.
  540. ;-----------------------------------------------------------------------------
  541. ring_bell    proc    near
  542.         push    ax
  543.         push    cx
  544.         push    dx
  545.  
  546.         in    al,61h        ; Get port data
  547.         push    ax        ;   and save it
  548.         cli            ; Clear Interrupts
  549.  
  550.         mov    dx,0ch        ; Length of bell tone
  551.         mov    [tone],500h    ; Frequency
  552.         call    speaker
  553.  
  554.         mov    dx,0ch        ; Length of bell tone
  555.         mov    [tone],1000h    ; Frequency
  556.         call    speaker
  557.  
  558.         pop    ax        ; Reset
  559.         out    61h,al        ;   port data
  560.         sti            ; Reset Interrupts
  561.  
  562.         pop    dx
  563.         pop    cx
  564.         pop    ax
  565.         ret
  566. ring_bell    endp
  567.  
  568.         
  569. ;-----------------------------------------------------------------------------
  570. ; Activate the speaker for a specified time and tone.
  571. ;-----------------------------------------------------------------------------
  572. speaker        proc    near
  573. BELL30:
  574.         and    al,11111100B    ; Set bits 0 & 1 off
  575.         out    61h,al        ; Transmit to speaker
  576.         mov    cx,[tone]    ; Set the length
  577. BELL40:
  578.         loop    BELL40        ; Time Delay
  579.         or    al,00000010B    ; Set bit 1 on
  580.         out    61h,al        ; Transmit to speaker
  581.         mov    cx,[tone]    ; Set length
  582. BELL50:
  583.         loop    BELL50        ; Time Delay
  584.         dec    dx        ; Reduce Duration
  585.         jnz    BELL30        ; Continue ?
  586.         ret            ; Nope, we are all done.
  587. speaker        endp
  588.  
  589.  
  590. filename    db    61 DUP (?)    ; Storage for user inputted filename
  591. screenbuf    db    80*2 DUP (?)    ; Storage for screen line we clear
  592. lastbyte = $
  593.  
  594.  
  595. ; ---------------------------------------------------------------------------
  596. ;  Main routine to install NPRINT as a TSR
  597. ; ---------------------------------------------------------------------------
  598.  
  599. install    proc    near
  600.     assume    cs:cseg,ds:cseg,es:cseg,ss:cseg
  601.  
  602.     lea    dx,copyright
  603.     mov    ah,09h        ; Print a copyright message
  604.     int    DOS
  605.  
  606.     mov    ah,01h        ; PRINT Function
  607.     mov    al,00h        ; Print Installation Check Subfunction
  608.     int    MULTIPLEX    ; Multiplex Service Interrupt
  609.     cmp    al,0FFh
  610.     je    ok2inst        ; If we get FFh returned, then PRINT is there.
  611.     lea    dx,noprint
  612.     mov    ah,09h        ; Print a failing message
  613.     int    DOS
  614.     mov    ax,4c01h    ; Terminate with error code 1
  615.     int    DOS
  616.  
  617. ok2inst:
  618. IFDEF DEBUG
  619.     call    main
  620. ENDIF
  621.  
  622.     mov    ah,35h        ; Determine Interrupt vector for PrintScreen
  623.     mov    al,05h
  624.     int    DOS
  625.     mov    word ptr old_int_5[0],bx    ;offset
  626.     mov    word ptr old_int_5[2],es    ;segment
  627.  
  628.     mov    ah,35h        ; Determine Interrupt vector for DOS call
  629.     mov    al,28h
  630.     int    DOS
  631.     mov    word ptr old_int_28[0],bx    ;offset
  632.     mov    word ptr old_int_28[2],es    ;segment
  633.  
  634.     lea    si,copyright    ; Lets see if we are already installed.
  635.     mov    di,bx        ; Put the offset into the DI register
  636.     sub    di,datasize    ; Position ourselves to the start of the data
  637.     mov    cx,6        ; Compare 6 bytes (NPRINT)
  638.     repe    cmpsb        ; Repeat as long as they match
  639.     jne    not_installed    ; If it doesn't match, then we aren't installed
  640.     lea    dx,inst_err    ; OOPS, they match.  Tell the user about it.
  641.     mov    ah,09h
  642.     int    DOS
  643.     mov    ax,4c02h    ; Return to DOS with errorlevel 2
  644.     int    DOS
  645.  
  646. not_installed:
  647.     mov    ah,34h                ; Locate the Critical Sec. Flag
  648.     int    21h
  649.     mov    word ptr CritSectFlag[0],bx    ;offset
  650.     mov    word ptr CritSectFlag[2],es    ;segment
  651.  
  652.     mov    ah,09h        ; Tell the user that we are installing
  653.     lea    dx,inst_msg
  654.     int    DOS
  655.  
  656.     mov    ah,25h        ; Set Interrupt Vector for Print Screen
  657.     mov    al,05h
  658.     lea    dx,int5
  659.     int    DOS
  660.  
  661.     mov    ah,25h        ; Set Interrupt Vector for DOS Check
  662.     mov    al,28h
  663.     lea    dx,int28
  664.     int    DOS
  665.  
  666.     mov    dx,code_pars    ; Tell DOS how much memory we require
  667.     mov    ax,3100h    ;   and become a TSR
  668.     int    DOS
  669.  
  670. install    endp
  671.  
  672. noprint        db    'DOS PRINT.COM is not installed.',13,10,'$'
  673. inst_err    db    'NPRINT already installed.',7,13,10,'$'
  674. inst_msg    db    'NPRINT installed successfully.',13,10,'$'
  675.  
  676. code_size = (offset lastbyte - offset entpt)
  677. code_pars = (code_size / 16) + 32
  678.  
  679. cseg    ends
  680.     end    entpt
  681.