FFSKIP Disables output to the printer via INT 17h until a specified number of form-feed-delimited pages have been sent to the printer. The two primary uses of FFSKIP: 1. Disable the printer. A count of 65535 effectively does this if all you print is text. 2. Skip the first n pages of a file sent to the printer *if the file uses form-feeds to separate pages*. The command: FFSKIP [count] [comment text] (count can be 0 to 65535, actual result will be unpredictable if a higher number is specified. If omitted, it defaults to 0 (FFSKIP disabled).) The first non-digit, non-space character ends count evaluation and the rest of the command-line is ignored. Examples: a. Load FFSKIP in de-activated state if not already loaded. Deactivate resident copy if already loaded. FFSKIP b. The same as above. (Comments are allowed on command line and are ignored.) FFSKIP wears army boots. c. Load FFSKIP if not already resident. Count set to 65535 so printer output will be discarded until 65535 form-feeds are sent to the printer or FFSKIP is reset by another command or a printer reset function call. Effectively, turn off the printer. FFSKIP 65535 d. Your printer ribbon broke whle printing page 84 of a 123-page report with TYPE report.txt >LPT1 To continue later where you left off and skip the first 83 pages after you get a new printer ribbon: FFSKIP 83 TYPE report.txt >LPT1 (This situation is what prompted me to write FFSKIP in the first place.) Result: a. If this is the first time FFSKIP is run, it will stay resident with the count saved in a flag word. If this is not the first time, the current count will be passed to the earlier copy and FFSKIP will terminate normally. b. When FFSKIP is resident and the count is 0, it is effectively disabled and all printer BIOS calls are passed to the original printer interrupt routine. c. When FFSKIP is resident and the count is not 0, BIOS calls to the printer interrupt vector INT 17h are intercepted and the following results are generated: Function 00 (Write Character): -- If the character is a form-feed (new page) then the count is decremented. -- The character is discarded without being sent to the printer. -- FFSKIP returns a status of bit 7 (not busy) and bit 4 (selected) set (status 90h, printer OK) Function 01 (Initialize printer port): -- FFSKIP resets its counter to 0 (turns itself off) and jumps to the original printer interrupt vector to invoke any other initialization code. Function 02 (Get printer status) -- FFSKIP returns a status of bit 7 (not busy) and bit 4 (selected) set (90h, printer OK) Functions 03 and higher (Undefined at time of writing FFSKIP). -- FFSKIP returns a status of bit 4 (I/O error). Commented disassembly of FFSKIP =============================== The original source file for FFSKIP is on one of my old, currently inaccessible 5 1/4 inch floppies (if the disk is even readable anymore). That's how old FFSKIP is. The following was produced by editing a DEBUG disassembly of FFSKIP. NOTE: All numbers in instructions are in DEBUG hexadecimal format except where noted and this disassembly is for information only. ****** This file is not usable with an assembler without editing. ****** A complete hex-dump of FFSKIP: ------------------------------ 178D:0100 EB 65 90 20 46 46 53 4B-49 50 2E 43 4F 4D 20 2D .e. FFSKIP.COM - 178D:0110 2D 2D 20 62 79 20 4E 2E-20 4C 2E 20 44 65 46 6F -- by N. L. DeFo 178D:0120 72 65 73 74 20 20 2D 2D-2D 2D 20 00 00 00 00 00 rest ---- ..... 178D:0130 00 9C 2E F7 06 2F 01 FF-FF 75 06 9D 2E FF 2E 2B ...../...u.....+ 178D:0140 01 80 FC 02 77 14 74 0E-80 FC 01 74 11 3C 0C 75 ....w.t....t.<.u 178D:0150 05 2E FF 0E 2F 01 B4 90-9D CF B4 08 9D CF 2E C7 ..../........... 178D:0160 06 2F 01 00 00 EB D4 FC-8A 0E 80 00 B5 00 E3 10 ./.............. 178D:0170 BF 81 00 B0 20 0E 07 F3-AE E8 70 00 89 16 2F 01 .... .....p.../. 178D:0180 BE 03 01 8B FE 0E 1F 0E-07 B9 28 00 FC AC 0C 20 ..........(.... 178D:0190 AA E2 FA B8 70 00 8C CB-8B 2E 03 01 BF 03 01 8E ....p........... 178D:01A0 D8 3B 2D 74 08 40 3B C3-72 F2 EB 18 90 8B F7 8E .;-t.@;.r....... 178D:01B0 C3 B9 28 00 F3 A6 75 ED-8E C0 8E DB BE 2F 01 8B ..(...u....../.. 178D:01C0 FE A5 CD 20 33 C0 8E D8-8B 1E 5C 00 2E 89 1E 2B ... 3.....\....+ 178D:01D0 01 8B 1E 5E 00 2E 89 1E-2D 01 0E 1F BA 31 01 B8 ...^....-....1.. 178D:01E0 17 25 CD 21 BA 67 01 83-C2 10 CD 27 4F 4F 33 D2 .%.!.g.....'OO3. 178D:01F0 47 8A 05 2C 30 72 17 3C-09 77 13 50 8B C2 33 D2 G..,0r.<.w.P..3. 178D:0200 BB 0A 00 F7 E3 8B D0 58-B4 00 03 D0 EB E2 C3 .......X....... Commented disassembly: ---------------------- ENTRY: 0100 EB65 JMP 0167 ; go to START of transient code. 0102 90 NOP SIGNATURE: 0103 204646534B49502E434F4D20 DB " FFSKIP.COM " 010F 2D2D2D206279204E2E204C2E DB "--- by N. L." 011B 204465466F726573742020 DB " DeForest " 0126 2D2D2D2D20 DB "---- " OLD_INT_17H: ; filled in by GO-TSR routine 012B 0000 DW 0000 ; segment 012D 0000 DW 0000 ; offset COUNTER: 012F 0000 DW 0000 ; On entry: AH = 00 = Write Character ; AH = 01 = Initialize printer port ; AH = 02 = Get printer status ; AH > 02 = Undefined at time of writing FFSKIP NEW_INT_17_ROUTINE: 0131 9C PUSHF ; SAVE FLAGS 0132 2EF7062F01FFFF TEST WORD PTR CS:[012F],FFFF ; COUNTER = 0? 0139 7506 JNZ 0141 ; No? IGNORE. CHAIN_TO_OLD: 013B 9D POPF ; RESTORE FLAGS 013C 2EFF2E2B01 JMP FAR CS:[012B] ; GO TO ORIGINAL ; PRINTER INTERRUPT ; ROUTINE. IGNORE: 0141 80FC02 CMP AH,02 ; Check function, status request? 0144 7714 JA 015A ; If higher, FUNCTION_3_OR_HIGHER 0146 740E JZ 0156 ; If status request, jump. 0148 80FC01 CMP AH,01 ; Initialize printer? 014B 7411 JZ 015E ; Jump if yes. PRINT_BYTE: 014D 3C0C CMP AL,0C ; Form-feed? 014F 7505 JNZ 0156 ; No, just ignore. 0151 2EFF0E2F01 DEC WORD PTR CS:[012F] ; Decrement skip count. STATUS: 0156 B490 MOV AH,90 ; Return bit 7 (not busy) and bit 4 (selected) 0158 9D POPF ; restore flags 0159 CF IRET ; return to caller. FUNCTION_3_OR_HIGHER: 015A B408 MOV AH,08 ; I/O error status code 015C 9D POPF ; restore flags 015D CF IRET ; return to caller. FUNCTION_1: ; Initialize printer 015E 2EC7062F010000 MOV WORD PTR CS:[012F],0000 ; clear the count 0165 EBD4 JMP 013B ; and then go to the original interrupt ; vector. ; FYI -- quoting from Ralf Brown's Interrupt List: ; Bitfields for printer status: ; Bit(s) Description (Table 00631) ; 7 not busy ; 6 acknowledge ; 5 out of paper ; 4 selected ; 3 I/O error ; 2-1 unused ; 0 timeout ; Notes: If both, bit 5 "out of paper" and 4 "selected" are set, the ; MS-DOS/ PC DOS kernel assumes that no printer is attached. ; for Tandy 2000, bit 7 indicates printer-busy when set rather than clear ; --------- Transient code run when run as a program ------------- START: 0167 FC CLD 0168 8A0E8000 MOV CL,[0080] ; How many characters in command-line? 016C B500 MOV CH,00 ; convert from byte count to word count 016E E310 JCXZ 0180 ; skip if nothing to process, go to SEARCH 0170 BF8100 MOV DI,0081 ; point to start of command-line 0173 B020 MOV AL,20 ; skip spaces 0175 0E PUSH CS ; ES:DI --> command-line 0176 07 POP ES 0177 F3 REPZ ; scan line until end or non-space found 0178 AE SCASB 0179 E87000 CALL 01EC ; EVALUATE from there 017C 89162F01 MOV [012F],DX ; set COUNTER to evaluated value SEARCH: 0180 BE0301 MOV SI,0103 ; Point DS:SI and ES:DI at SIGNATURE 0183 8BFE MOV DI,SI 0185 0E PUSH CS 0186 1F POP DS 0187 0E PUSH CS 0188 07 POP ES 0189 B92800 MOV CX,0028 ; 40 bytes (28h) to check 018C FC CLD ; Scan forward CASECHANGE: 018D AC LODSB ; Get a byte 018E 0C20 OR AL,20 ; Force lower-case if upper 0190 AA STOSB ; Replace with new value 0191 E2FA LOOP 018D ; Repeat for entire signature ; "Why do you do this?" wou ask. ; I want to search for earlier copies of ; FFSKIP in memory in case it's already ; resident. However, I don't want to falsely ; detect a copy of *this* invocation of FFSKIP ; that happens to be sitting in the system's ; disk buffers. Altering the SIGNATURE prevents ; such false recognition. 0193 B87000 MOV AX,0070 ; Start search at 0070:0103 0196 8CCB MOV BX,CS ; for comparison below 0198 8B2E0301 MOV BP,[0103] ; BP <-- 1st word of signature SEARCH2: 019C BF0301 MOV DI,0103 ; Point to possible start of signature 019F 8ED8 MOV DS,AX ; at segment to check 01A1 3B2D CMP BP,[DI] ; 1st 2 bytes match? 01A3 7408 JZ 01AD ; Yes, scan entire signature area. NOTFOUNDYET: 01A5 40 INC AX ; Increment segment to search. 01A6 3BC3 CMP AX,BX ; Has search reached *this* segment? 01A8 72F2 JB 019C ; No, loop back to check next segment. 01AA EB18 JMP 01C4 ; No earlier copy found. Go TSR. 01AC 90 NOP SCAN4SIG: 01AD 8BF7 MOV SI,DI ; DS:SI --> earlier segment possible signature 01AF 8EC3 MOV ES,BX ; and ES:DI points to this segment signature 01B1 B92800 MOV CX,0028 ; 40 bytes to check 01B4 F3 REPZ ; Compare the two 01B5 A6 CMPSB ; areas. 01B6 75ED JNZ 01A5 ; If no match, go back to NOTFOUNDYET to check ; next segment. 01B8 8EC0 MOV ES,AX ; Point ES:DI to counter in other segment 01BA 8EDB MOV DS,BX ; and DS:SI to counter in this segment. 01BC BE2F01 MOV SI,012F 01BF 8BFE MOV DI,SI 01C1 A5 MOVSW ; Copy counter from this FFSKIP to earlier one. 01C2 CD20 INT 20 ; Old TERMINATE command. GO_TSR: 01C4 33C0 XOR AX,AX ; Point DS to Interrupt Vector area. 01C6 8ED8 MOV DS,AX 01C8 8B1E5C00 MOV BX,[005C] ; Get old INT 17h segment. 01CC 2E891E2B01 MOV CS:[012B],BX ; Save in OLD_INT_17H segment word. 01D1 8B1E5E00 MOV BX,[005E] ; Get old INT 17h offset. 01D5 2E891E2D01 MOV CS:[012D],BX ; Save in OLD_INT_17H offset word. 01DA 0E PUSH CS ; Point DS:DX to NEW_INT_17_ROUTINE 01DB 1F POP DS 01DC BA3101 MOV DX,0131 01DF B81725 MOV AX,2517 ; Function 25 (set Int vector), Int 17 01E2 CD21 INT 21 ; Hook INT 17 01E4 BA6701 MOV DX,0167 ; Offset past end of NEW_INT_17_ROUTINE 01E7 83C210 ADD DX,+10 ; add 16 bytes to ensure no truncation error 01EA CD27 INT 27 ; Terminate and Stay Resident EVALUATE: 01EC 4F DEC DI ; Back up DI to compensate for INC below 01ED 4F DEC DI 01EE 33D2 XOR DX,DX ; Count initially 0 EVAL_LOOP: 01F0 47 INC DI ; Point to first/next character to examine 01F1 8A05 MOV AL,[DI] ; Get the character 01F3 2C30 SUB AL,30 ; Subtract ASCII "0" 01F5 7217 JB 020E ; If not a digit, we are done. 01F7 3C09 CMP AL,09 ; AL now 0 to 9? 01F9 7713 JA 020E ; If above that, again not a digit. 01FB 50 PUSH AX ; Save number 01FC 8BC2 MOV AX,DX ; Word in DX --> DWord in DX:AX 01FE 33D2 XOR DX,DX 0200 BB0A00 MOV BX,000A ; Decimal ten 0203 F7E3 MUL BX ; Multiply current count by ten 0205 8BD0 MOV DX,AX ; Count back to DX 0207 58 POP AX ; Latest digit back to AL 0208 B400 MOV AH,00 ; Zero-extend AL to AX 020A 03D0 ADD DX,AX ; Add new digit to count 020C EBE2 JMP 01F0 ; Loop back to check for another digit. 020E C3 RET ; Evaluation done, count in DX.