NPRINT: a TSR to submit a file to DOS's PRINT.COM
Christopher D. Orr
chris at lxn.chi.il.us
Sat Mar 17 06:14:37 AEST 1990
I have written a TSR in Assembly that is fairly useful to our organization.
I am interested in getting feedback from other users about its style and
implementation.
NOTE: this program makes use of the undocumented interrupt 28h and DOS's
function 34h (Critical Section Flag). I believe that this was the best way
to implement it, but, as they say, let the NET decide :-)
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# nprint.asm
# nprint.doc
# This archive created: Fri Mar 16 13:54:17 1990
export PATH; PATH=/bin:$PATH
if test -f 'nprint.asm'
then
echo shar: will not over-write existing file "'nprint.asm'"
else
cat << \SHAR_EOF > 'nprint.asm'
;****************************************************************************
; 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
SHAR_EOF
if test 17331 -ne "`wc -c < 'nprint.asm'`"
then
echo shar: error transmitting "'nprint.asm'" '(should have been 17331 characters)'
fi
fi # end of overwriting check
if test -f 'nprint.doc'
then
echo shar: will not over-write existing file "'nprint.doc'"
else
cat << \SHAR_EOF > 'nprint.doc'
NPRINT v1.0
(c) 1990 by Christopher D. Orr
OVERVIEW:
NPRINT is a TSR designed to prompt the user for a filename and
pass that file off to DOS's PRINT.COM. If no filename is
specified, NPRINT will attempt a print screen. If no printer is
detected (or a print error occurs), NPRINT will inform the user
and abort the print screen. If a print screen is successful,
NPRINT will send a formfeed to the printer.
FUNCTIONAL DESCRIPTION:
* NPRINT will only activate in modes 3 or 7.
* The 25th text line is saved.
* The user is prompted to enter a filename on the 25th text line.
* A request is made to PRINT to print the file.
* Errors returned by PRINT are displayed to the user.
* The 25th text line is restored.
* A blank filename will cause a print screen to occur
* NPRINT checks to see if the printer is available
* If the printer is not available, NPRINT will not attempt a print screen
* After a successful print screen, NPRINT will send a formfeed to the printer
ACTIVATION:
NPRINT is activated by pressing the [PrtScr] key.
SHAR_EOF
if test 1086 -ne "`wc -c < 'nprint.doc'`"
then
echo shar: error transmitting "'nprint.doc'" '(should have been 1086 characters)'
fi
fi # end of overwriting check
# End of shell archive
exit 0
Newsgroups: comp.sys.ibm.pc.programmer
Subject: NPRINT: a TSR to submit a file to DOS's PRINT.COM
Distribution: usa
I have written a TSR in Assembly that is fairly useful to our organization.
I am interested in getting feedback from other users about its style and
implementation.
NOTE: this program makes use of the undocumented interrupt 28h and DOS's
function 34h (Critical Section Flag). I believe that this was the best way
to implement it, but, as they say, let the NET decide :-)
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# nprint.asm
# nprint.doc
# This archive created: Fri Mar 16 13:54:17 1990
export PATH; PATH=/bin:$PATH
if test -f 'nprint.asm'
then
echo shar: will not over-write existing file "'nprint.asm'"
else
cat << \SHAR_EOF > 'nprint.asm'
;****************************************************************************
; 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
SHAR_EOF
if test 17331 -ne "`wc -c < 'nprint.asm'`"
then
echo shar: error transmitting "'nprint.asm'" '(should have been 17331 characters)'
fi
fi # end of overwriting check
if test -f 'nprint.doc'
then
echo shar: will not over-write existing file "'nprint.doc'"
else
cat << \SHAR_EOF > 'nprint.doc'
NPRINT v1.0
(c) 1990 by Christopher D. Orr
OVERVIEW:
NPRINT is a TSR designed to prompt the user for a filename and
pass that file off to DOS's PRINT.COM. If no filename is
specified, NPRINT will attempt a print screen. If no printer is
detected (or a print error occurs), NPRINT will inform the user
and abort the print screen. If a print screen is successful,
NPRINT will send a formfeed to the printer.
FUNCTIONAL DESCRIPTION:
* NPRINT will only activate in modes 3 or 7.
* The 25th text line is saved.
* The user is prompted to enter a filename on the 25th text line.
* A request is made to PRINT to print the file.
* Errors returned by PRINT are displayed to the user.
* The 25th text line is restored.
* A blank filename will cause a print screen to occur
* NPRINT checks to see if the printer is available
* If the printer is not available, NPRINT will not attempt a print screen
* After a successful print screen, NPRINT will send a formfeed to the printer
ACTIVATION:
NPRINT is activated by pressing the [PrtScr] key.
SHAR_EOF
if test 1086 -ne "`wc -c < 'nprint.doc'`"
then
echo shar: error transmitting "'nprint.doc'" '(should have been 1086 characters)'
fi
fi # end of overwriting check
# End of shell archive
exit 0
--
Christopher D. Orr | WISE OLD SAYING:
UUCP: {edsews,lehi3b15}!lxn!chris | Subtlety is the art of saying what you
or chris at lxn.chi.il.us | think and getting out of the way
or chris%lxn at clout.chi.il.us | before it is understood.
More information about the Alt.sources
mailing list