title 'WSMULCOP.Z80' listw 95 ;WSMULCOP.Z80 ;A program to make unattended multiple printouts from WordStar ; using EX15.COM, a public-domain substitute for SUBMIT.COM. ;WSMULCOP.Z80 copyright 11-19-85 by M. L. Halbert ; 104 Morgan Road ; Oak Ridge, TN 37830 ; ; Permission granted for unlimited private use. ;Usage: A>WSMULCOP ;------ ;WSMULCOP asks name of file to be printed and no. of copies desired ; (up to 99 copies). ;Method: WSMULCOP.COM generates a disk file PR.SUB. This file is ;------- executed by EX15.COM. PR.SUB consists of: ; ; A:WS ; p B:document.ext^[ (or other drivecode, ; p B:document.ext^[ if specified) ; . . . (n times) ; Y PR.SUB ; ; (EX15 converts ^[ to , which tells WordStar to ; use the defaults for its 6 questions for a P command.) ; ; WSMULCOP.COM places a reference to PR.SUB in the command ; tail buffer at 80h and moves part of itself to ; high storage (2000H),well above the top of EX15.COM. ; (EX15 later relocates itself as well, but that is of no ; concern for WSMULCOP.COM.) The moved segment of WSMULCOP.COM ; then loads EX15.COM at 0100H and jumps there, thereby ; starting EX15 in motion. ; ; After the n copies are printed, EX15 erases PR.SUB and ; leaves the user at the Opening Menu in WordStar. ;Restrictions: ;------------- ;Both WSMULCOP.COM and EX15.COM are assumed to be on A:. ;The default drivecode of the file to be printed is B:, but ; any code may be prefixed to the filename. Validity of the ; drivecode and the existence of the file are not checked until ; EX15 tries to execute PR.SUB. ;Assembly: ;--------- ;Written for the modified Crowe Z80 assembler as supplied by ; Micro Cornucopia on their Kaypro User Disk K-25, identified ; as CROWE(JH/DC/JGO) Z80 1.3e. Although the program is written ; with Zilog Z80 mnemonics, only instructions common to the 8080 ; have been used. ;Making the .COM file may be accomplished as follows: ; ***** ;Equates ;------- wboot equ 0 ;start Warm Boot stsize equ 20 ;stack size tpa equ 100h ;start of TPA tail equ 80h ;CCP puts command tail here bdos equ 05h ;jump to start of bdos dfcb equ 05Ch ;default file control block cr equ 0Dh ;carriage return lf equ 0Ah ;line feed ;Next two are for segment of code to be moved out of the way ; so EX15.COM can be loaded. high equ 2000h ;well above top of EX15.COM lenth equ 20h ;length of segment to be moved org tpa ;Initialization. ;--------------- ld sp,stack ;set stack pointer ;Set up default fcb for pr.sub ld hl,prsub ;filename in fcb format ld de,dfcb ;dest. is default fcb ld c,12 ;loop counter call copy ;Complete dfcb by inserting 24 nulls ld c,24 sub a ;clear 'a' loop0: ld (de),a ;insert null character inc de dec c jp nz,loop0 ;Delete old pr.sub if it exists. ld de,dfcb ld c,19 ;BDOS service 19, delete file call bdos ;(no need to check response) ;Make a directory entry. ld de,dfcb ld c,22 ;BDOS service 22, make a file call bdos cp 0FFh ;a=FFh if unsuccessful jp nz,query1 ld de,mess1 ;'dir. full' message ld c,9 ;BDOS service 9: call bdos ; Write a string to console jp wboot ;quit ;Dialog with user, process responses. ;--------------------------------------- ;Ask for document filename query1: ld de,q1 ;first query ld c,9 call bdos ld de,ans1 ;first answer ld c,10 ;BDOS service 10: call bdos ; Get a line from console ;Fill out WordStar print-command line ld de,combuf ;start of command-line buffer ld a,'p' ld (de),a inc de ld a,' ' ld (de),a inc de ;Check if user has supplied optional drivecode ld hl,ans1+3 ;get 4th byte ld a,(hl) cp ':' ;is it a colon? jp z,chars ;yes, include in buffer ld a,'B' ;no, setup B: as default ld (de),a inc de ld a,':' ld (de),a inc de chars: ld hl,ans1+1 ;2nd byte = no. of chars. ld a,(hl) ld c,a ;put no. of char. in loop counter ld hl,ans1+2 ;start of filename.ext call copy ;Insert ^[ after filename.ext, which EX15 will convert to , ; to tell WordStar to choose default print options. ld a,'^' ld (de),a inc de ld a,'[' ld (de),a inc de ld a,cr ld (de),a inc de ld a,lf ld (de),a ;Calculate and save total character count in print-command line ld hl,ans1+3 ;first retrieve 4th byte of ans1 ld a,(hl) sub ':'-1 ;= +1 if user supplied colon ld b,a ld hl,ans1+1 ;get no. of chars. in ans1 ld a,(hl) add a,6 ;6 added chars: p, ,^,[,cr,lf dec b jp z,storl ;was colon present? add a,2 ;no, so 2 more added chars (B,:) storl: ld de,length ld (de),a ;save in 'length' jp query2 ;skip over beep first time ;Ask for number of copies beep: ld e,07 ;07h for beep ld c,2 ;BDOS service 2, output a byte call bdos query2: ld de,q2 ;second query ld c,9 call bdos ld de,ans2 ;second answer ld c,10 call bdos ;Decode decimal digits for no. of copies, ncop sub a ;clear A register ld (ncop),a ;initialize ncop to zero ld hl,ans2+1 ;2nd byte has digit count ld a,(hl) ;digit count cp 3 ;less than 3 digits? jp nc,beep ;no, try again ld b,a ;ok, save for later ld c,a ;use in loop below loopc: inc hl ;next position dec c jp nz,loopc ;c=0 means units digit ld a,(hl) ;get units digit call chkdig ;check for valid ASCII digit sub '0' ;convert from ASCII to hex ld (ncop),a ;store result dec b ;finished when b=0 jp z,conf ;yes: only one digit present dec hl ;no, get tens digit ld a,(hl) call chkdig ;check for valid ASCII digit sub '0' ;convert from ASCII to hex ;Multiply by 10 by adding 10 the correct no. of times ld c,a ;addition counter sub a ;clear 'a' loop3: add a,10 dec c jp nz,loop3 ;nonzero: not finished ld c,a ;save for addition to units ld a,(ncop) ;retrieve units value add a,c ld (ncop),a ;cumulative result ;Ask for confirmation ; First retrieve ASCII digits for message conf: ld de,conf1+11 ;location of tens digit ld a,' ' ;blank in case no tens digit ld (de),a ;store blank ld hl,ans2+1 ;get number of digits ld a,(hl) cp 1 ;1 digit only? jp z,one ;yes inc hl ;no, get tens digit ld a,(hl) ld (de),a ;enter tens digit one: inc hl inc de ld a,(hl) ;get units digit ld (de),a ;enter it in message ;Initialize filename part of message to blanks ld de,conf1+24 ;location in message for name ld c,14 ;max no. of chars. loopb: ld a,' ' ;blank ld (de),a ;enter blank inc de dec c jp nz,loopb ;Enter filename over the blanks ld de,conf1+24 ;location in message for name ld hl,combuf+2 ;start of d:filename ld a,(length) ;no. of chars. in command line sub 6 ;extra chars. ld c,a ;initialize loop counter call copy ;Show message on screen ld de,conf1 ld c,9 call bdos ld de,conf2 ld c,9 call bdos ;Response: need check only for Y or y. ld c,1 ;BDOS service 1, get a byte call bdos ;byte returned in 'a' register cp 'y' jp z,enter1 cp 'Y' jp z,enter1 jp query1 ;go back for another dialog ;Enter WordStar print-command lines into write buffer. ;----------------------------------------------------- ;Initialize disk-write buffer to 1Ah (EOF char. for ASCII files) enter1: call init ld de,wbuf ;initialize buffer pointer ld b,128 ;initialize buffer counter ;Copy first command line (A:WS,cr,lf) ld c,6 ;char. count of first command ld hl,line1 ;location of 1st command line enter2: ld a,(hl) ;get char. from command line ld (de),a ;enter character into buffer inc hl ;increment command line counter inc de ;increment buffer pointer dec b ;decrement buffer counter dec c ;decrement loop counter jp nz,enter2 ;Insert print command ncop times. ;When counter shows end of write buffer, have to write to disk ; and start a new buffer. loop4: ld a,(length) ld c,a ;char. count of command line ld hl,combuf ;start of command-line buffer loop5: ld a,(hl) ;get next character ld (de),a ;enter character inc hl ;set up next char. to get inc de ;dest. pointer for next time dec b ;at end of buffer? jp nz,contin ;no, continue with next char. call write ;yes, go write on disk call init ;initialize buffer to EOF (1Ah) ld de,wbuf ;reset buffer pointer ld b,128 ;reinitialize buffer counter contin: dec c ;check character counter jp nz,loop5 ;if c=0, job is complete ;Use ncop to count no. of lines remaining to be entered ld hl,ncop ;get ncop dec (hl) ;decrement ncop jp nz,loop4 ;if 0, no more copies needed ;Enter last line of pr.sub ld hl,erapr ;command to erase pr.sub ld c,12 ;12 characters in this line loop6: ld a,(hl) ld (de),a inc hl inc de dec b jp nz,keepon ;if buffer not full, keep on call write ;buffer full, so write on disk call init ;initialize buffer to EOF ld de,wbuf ;reset buffer pointer ld b,128 ;reinitialize buffer counter keepon: dec c jp nz,loop6 ;continue with next character call write ;write last buffer onto disk ;Close PR.SUB file ld de,dfcb ld c,16 ;BDOS service 16: call bdos ; Close a file ;PR.SUB now ready. Set up command tail, load EX15, and go! ;---------------------------------------------------------- ;Set up fcb for EX15.COM in dfcb location ld de,dfcb ld hl,exfcb ;'1EX15 COM' ld c,12 ;character count call copy ;Set remaining 24 bytes in dfcb to nulls ld c,24 ;character count sub a ;null loopz: ld (de),a inc de dec c jp nz,loopz ;Set up command tail to simulate what CCP does ld de,tail ;start of command tail ld a,9 ;char. count incl. blank ld (de),a ;goes into 1st byte of tail inc de ld hl,erapr+1 ;' A:PR.SUB' ld c,9 ;char. count incl. blank call copy sub a ;get a null ld (de),a ;null to terminate tail ;Open file EX15.COM ld de,dfcb ld c,15 ;BDOS service 15: call bdos ; Open a file cp 0FFh ;a=FFh if file not found jp nz,setadd ld de,mess3 ;"file not found" message ld c,9 ;write to console call bdos jp wboot ;quit ;Set buffer address (destination for read) setadd: ld de,tpa ld c,26 call bdos jp hicop ;skip over subr., data ;Subroutines. ;------------ ;Subroutine to copy from one part of memory to another. ; hl is source pointer, de is DEstination pointer, ; c is loop counter. Set up these registers prior to call. copy: ld a,(hl) ;get character from source ld (de),a ;put character into destination inc hl ;increment source pointer inc de ;increment destination pointer dec c ;decrement loop counter jp nz,copy ret ;Subroutine to check for valid ASCII digit in 'a' register chkdig: cp '0' ;carry flag set if a < '0' jp c,beep ;if so, prompt for new input cp '9'+1 ;carry = 0 if a .ge. '9'+1 jp nc,beep ret ;Subroutine to initialize buffer to EOF character (1Ah) init: push bc push hl ld c,128 ;size of wbuf ld hl,wbuf ;start of buffer initbf: ld a,1Ah ld (hl),a inc hl dec c jp nz,initbf pop hl pop bc ret ;Subroutine to write buffer onto disk. write: push bc ;save in case loops not done push hl ;also this--bdos changes hl ;Set buffer address to start of write buffer. ld de,wbuf ;start of buffer ld c,26 ;BDOS service 26: call bdos ; Set buffer address ld de,dfcb ;fcb address ld c,21 ;BDOS service 21: call bdos ; Sequential write cp 0 ;a=0 means no error pop hl ;restore before return pop bc ret z ;return if a = 0 (no error) ld de,mess1 ;direc-full message (if a=1) cp 1 ;a=1 means directory full jp z,err ;a not 0 or 1, so must be 2 ld de,mess2 ;a=2 means disk full err: ld c,9 ;show error message ... call bdos jp wboot ;... and quit ;Data and message storage ;------------------------ defs stsize-1 ;lower locations in stack stack: defb 0 ;stack "top" (first loc. used) prsub: defb 1 ;drivecode for A: in fcb defm 'PR SUB' ;filename and type for fcb wbuf: defs 128 ;disk-write buffer line1: defm 'A:WS' q1: defb cr ;double duty: end of line1: defb lf ; start of q1: defb cr ;skip a line before query1 defb lf defm 'File to be printed (give drivecode if not B:): $' q2: defb cr defb lf defm 'Number of copies (max=99): $' ans1: defb 16 ;answer to q1, 16 chars. max. defs 15 ans2: defb 06 ;answer to q2 defs 5 combuf: defs 24 ;for print-command buffer length: defb 0 ;chars. in print-command line ncop: defb 0 ;no. of copies to be printed mess1: defm 'Directory A: full!$' mess2: defm 'Disk A: full!$' mess3: defm 'EX15.COM not found on A:!$' conf1: defb cr ;start a new line defb lf defb cr ;skip a line defb lf defm 'Making xx copies of d:document.ext -- $' conf2: defm 'OK? (y/n): $' erapr: defm 'Y A:PR.SUB' ;WordStar delete-file command defb cr defb lf exfcb: defb 1 ;drivecode for A: defm 'EX15 COM' ;file name ;Copy to high storage ;-------------------- ;Copy part of code (from start: to start:+lenth) ; to high location to avoid being overwritten ; when EX15.COM is read in. hicop: ld de,high ;'high' is above top of EX15 ld hl,start ;start of segment to be moved ld c,lenth ;length of segment to be moved call copy jp high ;execute the moved segment org high ;Read in EX15.COM at tpa start: ld de,dfcb ld c,20 ;BDOS service 20: call bdos ; Sequential read cp 0 ;a.NE.0 means no more records jp z,start ;read another record jp tpa ;start execution of EX15 end