chris@lxn.chi.il.us (Christopher D. Orr) (03/17/90)
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@lxn.chi.il.us | think and getting out of the way or chris%lxn@clout.chi.il.us | before it is understood.