home *** CD-ROM | disk | FTP | other *** search
- ;****************************************************************************
- ; NPRINT.COM: A TSR to submit a file to PRINT.COM
- ;****************************************************************************
-
- cseg segment para public 'CODE'
- assume cs:cseg, ds:nothing, es:nothing, ss:nothing
-
-
- org 100h
- entpt: jmp install ; Jump to the Install code
-
- VIDEO EQU 10h ; Interrupts used within NPRINT
- KEYBOARD EQU 16h
- PRINTER EQU 17h
- MULTIPLEX EQU 2Fh
- DOS EQU 21h
- MONOSCR EQU 0B000h ; Monochrome Screen Memory Location
- COLORSCR EQU 0B800h ; CGA (COLOR) Screen Memory Location
-
- copyright db 'NPRINT v1.0: (c) Copyright 1990 by Christopher D. Orr',13,10,'$'
- prompt db 'Enter Filename: ',0
- no_print db 'Printer is not responding. Please correct. Hit any Key',0
- printpack db 0,0,0,0,0
- invoke_flag db 0 ; Flag set whenever a request for us in pending
- cursor dw 0 ; Original cursor position
- pr_attribute db 07h ; Attribute for the Prompt/Messages
- scr_attribute db 07h ; Attribute for data entry
- do_prtsc db 0 ; Flag to indicate whether to do a screen print
- display_page db 0 ; Current Screen display page
- tone dw 0 ; Storage for frequency of bell when rung
- CritSectFlag dd 0 ; Critical Section Flag
- old_int_5 dd 0 ; Address of original print screen handler
- old_int_28 dd 0 ; Address of original DOS handler
- pause_msg db ' ... ',0
- errmsg db 'PRINT returned error code '
- errcode db 0,0
- db 'h: ',0
-
- err_table dw errmsg1, errmsg2, errmsg3, errmsg4, errmsg5
- dw e_unknown, e_unknown, errmsg8, e_unknown, e_unknown
- dw e_unknown, errmsgc, e_unknown, e_unknown, errmsgf
- errmsg1 db 'Function Invalid',0
- errmsg2 db 'File not Found',0
- errmsg3 db 'Path not Found',0
- errmsg4 db 'Too many files open',0
- errmsg5 db 'Access Denied',0
- errmsg8 db 'PRINT Queue full',0
- errmsgc db 'Filename too long',0
- errmsgf db 'Drive invalid',0
- e_unknown db 'Unknown Return Code',0
-
- datasize = ($ - offset copyright)
-
-
- ;-----------------------------------------------------------------------------
- ; Interrupt Handlers - This procedure defines code to determine when/if we
- ; should invoke the NPRINT main routine.
- ;-----------------------------------------------------------------------------
- nprint proc far
- int28:
- cmp cs:[invoke_flag], 0FFh ; Should we invoke ourselves ?
- jnz short int_return ; No, so get out
- call main ; Invoke NPRINT
- pushf ; Push the flags
- cli ; Disable interrupts
- push cs ; Push the CS register
- mov di,offset cs:int_return ; When we return, we want to exit
- push di
- jmp dword ptr cs:old_int_28 ; Transfer control to old int 28h
-
- int5:
- push bx
- push es
- mov bx,word ptr CritSectFlag[0] ; Look at the Crit. Sec. Flag
- mov es,word ptr CritSectFlag[2]
- cmp byte ptr es:[bx],0h ; If non-zero, DOS is busy
- je ok2run ; Zero, so invoke NPRINT
- pop es
- pop bx
- mov cs:[invoke_flag], 0FFh ; Leave a flag to tell us to
- jmp short int_return
-
- ok2run:
- pop es
- pop bx
- call main ; Call NPRINT
-
- int_return:
- iret ; and we return ...
-
- endp nprint
-
-
- ;-----------------------------------------------------------------------------
- ; Main Procedure - Responsible for processing user request
- ;-----------------------------------------------------------------------------
- main proc near
- sti ; Allow Interrupts
-
- push bp ; Access to stack
- mov bp,sp ; Before we push anything
-
- pushf ; Save the state of the machine
- push es ; because we're gonna make
- push ds ; a mess of it
- push di
- push si
- push dx
- push cx
- push bx
- push ax
-
- mov ax,cs ; Establish the proper segments
- mov ds,ax
- mov es,ax ; Make ES point to code segment
- assume ds:cseg, es:cseg ; Tell the assembler
-
-
- ok2load:
- mov [invoke_flag], 0h ; Clear Invocation flag
-
- mov ah,0fh ; Get the current Display Mode
- int VIDEO
- mov [display_page],bh ; Save current page
- cmp al,07h ; Is this a Monochrome Monitor ?
- je mono ; Yes, so we are okay
- cmp al,03h ; Is this a CGA Monitor in 80 col mode?
- je cga ; Yes, so we are okay
- call ring_bell ; No - so don't execute. Just abort.
- jmp done ; Exit NPRINT
-
- mono:
- mov ax,MONOSCR ; Monochrome Display
- jmp short saveline
- cga:
- mov ax,COLORSCR ; CGA display
-
- saveline:
- mov ds,ax ; Set up the Data Segment
- mov si,3840 ; Start at position row=25, column=1
- mov cx,80 ; 80 Character lines
- lea di, screenbuf
- push es ; Save screen memory locations
- push ds
- push si
- cld ; Make sure we copy from right to left
- rep movsw ; Move character & Attribute
-
- mov ax,cs ; Restore the Data Segment
- mov ds,ax
-
- mov ah,03h ; Get the Current Cursor Position
- int VIDEO
- mov [cursor], dx ; and save it.
-
- call clear_25 ; Goto and Clear Line #25
- lea si, prompt ; Display the filename prompt for the user
- call wr_string
-
- lea dx,filename ; Point to out filename buffer
- mov ax,60 ; Limit filename to 60 characters
- call rd_string ; Request the filename from the user
-
- cmp ax,0 ; Did we get a file name ???
- jne submit_file ; No, so don't do the print routine
- mov ah,02h ; Let's see if the printer is busy
- mov dx,0 ; Check printer number 0
- int PRINTER
- test ah,01101001B ; Clear bits 8,5,3,2 -> meaningless to us
- jz prtsc_okay
- lea si, no_print ; No, then there is a problem w/
- call clear_25 ; the printer. Tell the user about it
- call ring_bell
- call wr_string
- call pause
- jmp restscreen ; No sense printing a screen now, so don't
-
- prtsc_okay:
- mov [do_prtsc],0FFh ; Set the Print Screen Flag
- jmp restscreen
-
- submit_file:
- mov [do_prtsc],0h ; Clear the Print Screen Flag
- mov word ptr [printpack]+3,ds ; Put the address into packet
- mov word ptr [printpack]+1,offset filename
-
- retry_file:
- lea dx,printpack ; Point to the printer request packet
- mov ax,0101h ; Submit packet to Print Spooler
- int MULTIPLEX ; Invoke Multiplex Service Interrupt for PRINT
- jnc restscreen ; If the carry flag is set, then we had an error
-
- cmp ax,09h ; Is the spooler busy ? (NOTE: endless loop?)
- je retry_file ; Yes, so try again
- call display_errmsg ; Some other kind of error
-
- restscreen:
- lea si, screenbuf ; Point to our screen buffer
- pop di ; Restore Screen Memory Locations
- pop es
- pop ds ; Restore the Data Segment
- mov cx,80 ; Still name 80 character line
- cld ; Make sure we copy from right to left
- rep movsw
-
- mov ax,cs ; Restore the Data and Extended Segment values
- mov ds,ax
- mov es,ax
-
- mov dx,[cursor] ; Restore the cursor position to what it was
- mov bh,[display_page]
- mov ah,02h
- int VIDEO
-
- ;----------------------------------------------------------------------
- ; Restore the state of the machine when Int 05 occured
- ;----------------------------------------------------------------------
- done:
- pop ax
- pop bx
- pop cx
- pop dx
- pop si
- pop di
- pop ds
- pop es
- popf
- pop bp
-
- IFDEF DEBUG
- mov ax,4c00h
- int DOS
- ENDIF
-
- assume ds:nothing, es:nothing
- cmp cs:[do_prtsc],0FFh ; Is the Print Screen flag set ?
- jne nprint_exit ; no, so we should just exit
-
- pushf ; Push the flags
- cli ; Disable interrupts
- push cs ; Push the CS register
- mov di,offset cs:formfeed
- push di
- jmp dword ptr cs:old_int_5 ; Transfer control to old int 05h
-
- formfeed:
- pushf ; Save the state of the machine yet again
- push es
- push ds
- push di
- push si
- push dx
- push cx
- push bx
- push ax
- assume ds:cseg, es:cseg ;Tell the assembler
-
- mov ah,00h ; Print a character
- mov al,0ch ; Namely a Formfeed (^L)
- mov dx,0 ; Printer number one
- int PRINTER ; Invoke Print Interrupt
-
- pop ax ; Restore everything again
- pop bx
- pop cx
- pop dx
- pop si
- pop di
- pop ds
- pop es
- popf
-
- nprint_exit:
- ret ; And we return ...
- main endp
-
-
- ;======================================================================
- ; WR_STRING - write string to console at specified location.
- ; The string is ASCIIZ. All registers are preserved.
- ;----------------------------------------------------------------------
- wr_string proc near
-
- push si
- push bx
- push cx
- push ax
-
- wnext_char:
- lodsb ; Load char in AL from DS:SI
- or al,al ; If char is 0
- jz end_string ; Then end of ASCIIZ string
- mov ah,09h ; Else, write TTY
- mov bh,[display_page]
- mov bl,[pr_attribute]
- mov cx,01h ; Only one
- int VIDEO ; thru BIOS
- mov al,1
- call move_cursor ; Move the cursor to the right
- jmp short wnext_char ; and do it all again
- end_string:
- pop ax
- pop cx
- pop bx
- pop si
- ret
-
- wr_string endp
-
-
- ;-----------------------------------------------------------------------------
- ; Read a String from the Keyboard and returns a pointer to it
- ; Point to String using DS:DX. RETURNS: AX contains number of chars read
- ;-----------------------------------------------------------------------------
- rd_string proc near
-
- push bx
- push cx
- push di
- push dx
-
- mov di,dx ; Setup our buffer pointer
- mov bx,ax ; Store the max length in BX
- rnext_char:
- mov ah,0h ; Read Keyboard Character Function
- int KEYBOARD
- cmp al,08h ; Backspace ?
- jne check_enter ; No, so jump
- pop dx ; Restore the head of buffer pointer
- push dx
- cmp di,dx ; Are we at the start of line?
- jle rnext_char ; Yes, so ignore the backspace
-
- dec di ; Decrement our buffer pointer
- mov al,0 ; and move the cursor back
- call move_cursor
-
- push bx ; Now we have to erase the character
- mov bh,[display_page]
- mov cx,1
- mov bl,[scr_attribute]
- mov ah,09h ; Function for writing a character
- mov al,20h ; Put a space on the screen
- int VIDEO
- pop bx
- jmp short rnext_char
-
- check_enter:
- cmp al,0dh ; Did we get a return ?
- jne letter
- mov byte ptr [di],0h; Already at end of string, so add null
- pop dx ; Restore pointer to start of buffer
- sub di,dx ; How many chars did we read ?
- mov ax,di ; Return that number in AX
- pop di ; Restore the Data Index Register
- pop cx
- pop bx
- ret
-
- letter:
- cmp al,20h ; Check for a valid character
- jle rnext_char ; Ignore none printable characters/keys
- cmp al,7eh
- jg rnext_char
-
- pop dx ; Restore the head of buffer pointer
- push dx
- add dx,bx ; Maximum of "bx" characters
- cmp di,dx ; Yes, so are we at the end of buffer?
- jne char_ok
- call ring_bell ; Yes, so sound the alarm
- jmp rnext_char
-
- char_ok:
- mov [di],al ; Store the character
- inc di ; Increment our pointer
-
- push bx
- mov cx,1
- mov bl,[scr_attribute]
- mov ah,09h ; Display the character to the user
- mov bh,[display_page]
- int VIDEO
- mov al,1 ; Increment the cursor
- pop bx
- call move_cursor
- jmp rnext_char ; Get the next character
-
- rd_string endp
-
-
- ;-----------------------------------------------------------------------------
- ; Increment or Decrement the current cursor position. AX=0 implies decrement
- ;-----------------------------------------------------------------------------
- move_cursor proc near
- push bx
- push cx
- push dx
-
- mov ah,03h ; Get cursor position
- mov bh,[display_page]
- cmp al,0 ; If code 0, then decrement cursor
- je dec_cursor
- int VIDEO
- inc dl
- jmp short last_cur
- dec_cursor:
- int VIDEO
- dec dl
- last_cur:
- mov ah,02h ; Set the Cursor Position
- int VIDEO
-
- pop dx
- pop cx
- pop bx
- ret
- move_cursor endp
-
-
- ;----------------------------------------------------------------------
- ; HEX2 - Convert the AL register to hexidecimal digits.
- ; The characters produced are stored at ES:DI.
- ; All regs preserved.
- ;----------------------------------------------------------------------
- hex2 proc near
- push ax
- push bx
- push cx
-
- mov bx,ax
- std ;String ptr decrement
- add di,1 ;Point to end of string
- mov cx,2
- h10:
- mov al,bl ;Want lower half
- and al,0fh ; of this byte
- add al,90h ;Convert AL to ASCII
- daa
- adc al,40h
- daa
- stosb ;Store at ES:DI
- shr bx,1
- shr bx,1
- shr bx,1
- shr bx,1
- loop h10
-
- pop cx
- pop bx
- pop ax
- inc di
- cld
- ret
- hex2 endp
-
-
- ;-----------------------------------------------------------------------------
- ; Erase the 25th line of the screen and position the cursor on that line
- ;-----------------------------------------------------------------------------
- clear_25 proc near
- push ax
- push dx
- push cx
- push bx
-
- mov ah,02 ; Set the Cursor Position
- mov dh,24 ; Go to the 25th line
- mov dl,0h ; column number 1.
- int VIDEO
-
- mov ah,09h ; Clear the line ...
- mov al,20h ; using spaces
- mov bh,[display_page]
- mov bl,07h ; Change the attributes to white
- mov cx,80 ; Write 80 characters
- int VIDEO
-
- pop bx
- pop cx
- pop dx
- pop ax
- ret
- clear_25 endp
-
-
- ;-----------------------------------------------------------------------------
- ; Pause so the user can press a key ... any key ...
- ;-----------------------------------------------------------------------------
- pause proc near
- mov si, offset pause_msg
- call wr_string
-
- top_pause:
- mov ah,01h ; Wait for a key to be pressed
- int KEYBOARD
- jz top_pause
- mov ah,00h ; Eat the keystroke
- int KEYBOARD
-
- ; cmp al,0dh ; Only continue with the [RETURN] key
- ; jne top_pause
-
- ret
- pause endp
-
-
- ;-----------------------------------------------------------------------------
- ; Display the Error Code and Message
- ;-----------------------------------------------------------------------------
- display_errmsg proc near
- call clear_25
- call ring_bell
- mov di,offset errcode ; Display the error code
- call hex2
- lea si, errmsg
- call wr_string
- cmp al,0fh ; Is the error off the scale ??
- jle table
- lea si,e_unknown ; Yes, so display "unknown" message
- jmp short show_error
-
- table:
- xor bh,bh ; Build offset into error table
- mov bl,al ; Can't use AX as a memory index ptr
- dec bx ; Table is base 0, so reduce the index
- shl bx,1 ; Make offset into table (*2)
- mov si,err_table[bx]
-
- show_error:
- call wr_string ; Now display the error
- call pause
- ret
- display_errmsg endp
-
-
- ;-----------------------------------------------------------------------------
- ; Ring the Bell to indicate that an error occured - we create a special tone
- ; so that the user knows it is NPRINT that did it.
- ;-----------------------------------------------------------------------------
- ring_bell proc near
- push ax
- push cx
- push dx
-
- in al,61h ; Get port data
- push ax ; and save it
- cli ; Clear Interrupts
-
- mov dx,0ch ; Length of bell tone
- mov [tone],500h ; Frequency
- call speaker
-
- mov dx,0ch ; Length of bell tone
- mov [tone],1000h ; Frequency
- call speaker
-
- pop ax ; Reset
- out 61h,al ; port data
- sti ; Reset Interrupts
-
- pop dx
- pop cx
- pop ax
- ret
- ring_bell endp
-
-
- ;-----------------------------------------------------------------------------
- ; Activate the speaker for a specified time and tone.
- ;-----------------------------------------------------------------------------
- speaker proc near
- BELL30:
- and al,11111100B ; Set bits 0 & 1 off
- out 61h,al ; Transmit to speaker
- mov cx,[tone] ; Set the length
- BELL40:
- loop BELL40 ; Time Delay
- or al,00000010B ; Set bit 1 on
- out 61h,al ; Transmit to speaker
- mov cx,[tone] ; Set length
- BELL50:
- loop BELL50 ; Time Delay
- dec dx ; Reduce Duration
- jnz BELL30 ; Continue ?
- ret ; Nope, we are all done.
- speaker endp
-
-
- filename db 61 DUP (?) ; Storage for user inputted filename
- screenbuf db 80*2 DUP (?) ; Storage for screen line we clear
- lastbyte = $
-
-
- ; ---------------------------------------------------------------------------
- ; Main routine to install NPRINT as a TSR
- ; ---------------------------------------------------------------------------
-
- install proc near
- assume cs:cseg,ds:cseg,es:cseg,ss:cseg
-
- lea dx,copyright
- mov ah,09h ; Print a copyright message
- int DOS
-
- mov ah,01h ; PRINT Function
- mov al,00h ; Print Installation Check Subfunction
- int MULTIPLEX ; Multiplex Service Interrupt
- cmp al,0FFh
- je ok2inst ; If we get FFh returned, then PRINT is there.
- lea dx,noprint
- mov ah,09h ; Print a failing message
- int DOS
- mov ax,4c01h ; Terminate with error code 1
- int DOS
-
- ok2inst:
- IFDEF DEBUG
- call main
- ENDIF
-
- mov ah,35h ; Determine Interrupt vector for PrintScreen
- mov al,05h
- int DOS
- mov word ptr old_int_5[0],bx ;offset
- mov word ptr old_int_5[2],es ;segment
-
- mov ah,35h ; Determine Interrupt vector for DOS call
- mov al,28h
- int DOS
- mov word ptr old_int_28[0],bx ;offset
- mov word ptr old_int_28[2],es ;segment
-
- lea si,copyright ; Lets see if we are already installed.
- mov di,bx ; Put the offset into the DI register
- sub di,datasize ; Position ourselves to the start of the data
- mov cx,6 ; Compare 6 bytes (NPRINT)
- repe cmpsb ; Repeat as long as they match
- jne not_installed ; If it doesn't match, then we aren't installed
- lea dx,inst_err ; OOPS, they match. Tell the user about it.
- mov ah,09h
- int DOS
- mov ax,4c02h ; Return to DOS with errorlevel 2
- int DOS
-
- not_installed:
- mov ah,34h ; Locate the Critical Sec. Flag
- int 21h
- mov word ptr CritSectFlag[0],bx ;offset
- mov word ptr CritSectFlag[2],es ;segment
-
- mov ah,09h ; Tell the user that we are installing
- lea dx,inst_msg
- int DOS
-
- mov ah,25h ; Set Interrupt Vector for Print Screen
- mov al,05h
- lea dx,int5
- int DOS
-
- mov ah,25h ; Set Interrupt Vector for DOS Check
- mov al,28h
- lea dx,int28
- int DOS
-
- mov dx,code_pars ; Tell DOS how much memory we require
- mov ax,3100h ; and become a TSR
- int DOS
-
- install endp
-
- noprint db 'DOS PRINT.COM is not installed.',13,10,'$'
- inst_err db 'NPRINT already installed.',7,13,10,'$'
- inst_msg db 'NPRINT installed successfully.',13,10,'$'
-
- code_size = (offset lastbyte - offset entpt)
- code_pars = (code_size / 16) + 32
-
- cseg ends
- end entpt
-