; DSKNUM.MAC (formerly DISKNUM.MAC) ; Vers equ 14 SubVers equ ' ' ; revision level ; ; A ZCPR33+ utility to create null disk labels. ; ; USAGE: ; ; DSKNUM {dir:}{label}{.num} {{/}options} ; ; If a DIR or DU specification is not given, the current drive and/or ; user is assumed, unless an internal default user is installed. If ; no label name is given, an internal default is used. If no disk ; number is given, the internal next number is used. If no option is ; given, DSKNUM labels a single disk and exits. By default DSKNUM ; saves the last number used internally, so you won't have to remember ; what it was. ; ; OPTIONS: Slash not required if option is second token. ; ; M Multiple label mode. ; ; S Do not save last disk number. ; ; Numerous configuration options are available in the first sector of ; the program COM file. See documentation for more information. ; ; Let me know if there are any problems. ; ; Gene Pizzetta ; 481 Revere Street ; Revere, MA 02151 ; ; Voice: (617) 284-0891 ; Newton Centre Z-Node: (617) 965-7259 ; GEnie: E.Pizzetta ; ; Version 1.4 -- July 28, 1990 -- Gene Pizzetta ; Complete rewrite. Requires ZCPR 3.3 or higher. Name changed to ; DSKNUM from DISKNUM. Labels a single disk from command line. ; Still labels multiple disk interactively. Accepts label name ; from command line in lieu of internal default label name. Accepts ; disk number from command line in lieu of internal stored number. ; Label attributes configurable. Gets its own filename and directory ; location from external file control block on first invocation. ; Name is stored for subsequent execution with GO command. Has ; type 3 header. Resets only target disk instead of entire disk ; system under Z3PLUS and ZSDOS. Configurable to reset disk or not ; when saving last number internally (resets not needed for hard ; drives). Accepts target user area from command line or configurable ; to use internal default user. Aborts if label already exists on disk. ; Sets ZCPR3 error flag if an invalid DU is given (2), if the disk ; number exceeds 999 (9), if the disk is out of directory space (11), ; if a label already exists on the disk (16), if an invalid option ; is given (19), or any other error (4). ^C aborts to operating ; system at any prompt. Brief usage screen if "//" is given. ; Configurable with ZCNFG 1.6 or higher. ; ; Version 1.3 -- March 3, 1989 -- Gene Pizzetta ; Minor bug correction. ; ; Version 1.2 -- February 28, 1989 -- Gene Pizzetta ; Corrected several file handling problems in CP/M 2.2 version. ; Added user area support. ; ; Version 1.1 -- December 26, 1987 -- Gene Pizzetta ; Added CP/M 2.2 support. ; ; Version 1.0 -- December 24, 1987 -- Gene Pizzetta ; Original release for CP/M-Plus. ; ; Developed with SLRMAC and SLRNK+. ; ; System addresses . . . ; CpmFcb equ 05Ch ; default file control block CpmDma equ 080h ; default DMA buffer ; ; Character codes . . . ; CtrlC equ 03h ; ^C BEL equ 07h ; bell TAB equ 09h ; tab CR equ 0Dh ; carriage return LF equ 0Ah ; linefeed ESC equ 1Bh ; escape ; ; BDOS service functions . . . ; CpmVer equ 12 ; CP/M version request ResSys equ 13 ; reset disk system FSrchF equ 17 ; search for first match SetAtt equ 30 ; set file attributes ResDrv equ 37 ; reset individual drives DosVer equ 48 ; ZRDOS, ZSDOS version request ; MACLIB Z80 ; this is extended Intel ; ; Routines from VLIB, Z3LIB, and SYSLIB . . . ; ext bdos,epstr,crlf,pafdc,cout,cin,isdigit,comphd,eval10,phl4hc ext retud,logud,initfcb,setdma,f$open,f$mopen,f$close,r$write ext zsyschk,z33chk,z3vinit,gzmtop,tinit,dinit,getefcb,pfn1 ext prtname,puter2 ext stndout,stndend ; ; TYP3HDR.MAC, Version 1.1 -- Extended Intel Mnemonics ; This code has been modified as suggested by Charles Irvine so that ; it will function correctly with interrupts enabled. ; Extended Intel mnemonics by Gene Pizzetta, April 30, 1989. ; Entry: jr Start0 ; must use relative jump db 0 ; filler db 'Z3ENV',3 ; type-3 environment Z3EAdr: dw 0FE00h ; filled in by Z33 dw Entry ; intended load address ; ; Configuration area . . . ; db 'DSKNUM' ; default name for CFG file db Vers/10+'0',Vers mod 10+'0' ; version for CFG file db 'ROFLG>' ; set read-only attribute in label ROFlg db 0 ; ..0 = no, non-zero = yes db 'SYSFLG>' ; set system attribute in label SysFlg: db 0 ; ..0 = no, non-zero = yes db 'ARCFLG>' ; set archive attribute in label ArcFlg: db 0 ; ..0 = no, non-zero = yes db 'RSTFLG>' ; reset drive before saving number RstFlg: db 0 ; ..0 = no, non-zero = yes db 'LBLTAG>' ; first character of label LblTag: db '#' ; "!" or "#" recommended db 'DFTLBL>' ; default disk label, if not given DftLbl: db 'DISK <' ; ..(7 upper-case characters) db 'LBLUSR>' ; user area for label, or FF to use LblUsr: db 0FFh ; ..current or given user LstNum: dw 0 ; last number written to disk ; Start0: lxi h,0 ; point to warmboot entry mov a,m ; save the byte there di ; protect against interrupts mvi m,0C9h ; replace warmboot with a return opcode rst 0 ; call address 0, pushing RetAddr onto stack RetAddr: mov m,a ; restore byte at 0 dcx sp ; get stack pointer to point dcx sp ; ..to the value of RetAddr pop h ; get it into HL and restore stack ei ; we can allow interrupts again lxi d,RetAddr ; this is where we should be xra a ; clear carry flag push h ; save address again dsbc de ; subtract -- we should have 0 now pop h ; restore value of RetAddr jz Start ; if addresses matched, begin real code ; lxi d,NotZ33Msg-RetAddr ; offset to message dad d xchg ; switch pointer to message into DE mvi c,9 jmp 0005h ; return via BDOS print string function ; NotZ33Msg: db 'Not Z33+$' ; abort message if not Z33-compatible ; ; Messages . . . ; MsgUse: db 'DSKNUM Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers db ' (loaded at ',0 MsgUs1: db 'h)',CR,LF db 'Usage:',CR,LF,' ',0 MsgUs2: db ' {dir:}{label}{.num} {{/}options}',CR,LF db 'Options:',CR,LF db ' M Multiple label mode',CR,LF db ' S Don''t save last number',0 MsgNxt: db 'Next Label: ',0 MsgDot: db ' .. ',0 MsgDsk: db ' .. Press any key (ESC = Quit) .. ',0 MsgRng: db BEL,'Next number out of range',0 MsgBad: db BEL,'Bad disk number',0 MsgIOp: db BEL,'Invalid option',0 MsgExs: db BEL,'Label exists',0 MsgFDr: db BEL,'No directory space',0 MsgIDr: db BEL,'Invalid directory',0 MsgWEr: db BEL,'File write error, ',0 MsgNSv: db 'Not saved',0 MsgZ33: db BEL,'ZCPR33+ required',0 MsgPrg: db BEL,'Can''t find ',0 MsgAgn: db ' .. Any key to try again .. ',0 MsgAbt: db 'Aborted',0 MsgDne: db 'Saved',0 ; ; Start of program . . . ; Start: lhld Z3EAdr call z3vinit call z33chk ; check for ZCPR33+ lxi h,MsgZ33 jnz epstr ; (it's not) sspd OldStk ; save old stack pointer lxi h,OldStk sphl ; ..and set up new stack ; lda PrgNam ; is this a rerun? cpi ' ' jrnz Start1 ; (yes, it is) call getefcb ; get external FCB address inx h ; increment it to filename (EFCB+1) lxi d,PrgNam ; ..and move program name to storage lxi b,11 ldir inx h ; point to program user (EFCB+13) mov a,m ; ..and move to storage sta PrgUsr inx h ; point to program drive (EFCB+14) mov a,m ; move program drive to storage dcr a ; make A=0 sta PrgDrv Start1: call ScanOp ; scan for options lda CpmFcb+15 ; valid directory? ora a jrz Skip1 mvi a,2 ; set error code (invalid directory) sta ErCode lxi h,MsgIDr jmp Exit ; (nope) Skip1: call tinit ; initialize terminal call retud ; get default DU mov a,b ; store drive sta TgtDrv lda CpmFcb ; get drive, if any ora a jrz Start2 ; (no drive, use default) dcr a sta TgtDrv Start2: lda LblUsr ; check for default user cpi 32 jrc Start3 lda CpmFcb+13 ; get user Start3: sta TgtUsr mov c,a ; put user in C lda TgtDrv ; put drive in B mov b,a call logud ; log into target DU lda LblTag ; move label tag to FCB sta LblFcb+1 lxi h,CpmFcb+1 ; check for filename (label) mov a,m cpi ' ' jrz Start4 ; (none, use default) cpi '/' jrz Start4 mov b,a ; see if tag was given lda LblTag cmp b jrnz Start5 ; (no) inx h ; point past tag jr Start5 Start4: lxi h,DftLbl ; move default disk label to FCB Start5: lxi d,LblFcb+2 lxi b,7 ldir lxi h,CpmFcb+9 ; check for filetype (number) mov a,m cpi ' ' jrz Start6 ; (none, use default) call isdigit jrnz BadNum ; (not a digit, so abort) call eval10 ; get number mov a,m ; get terminating character xchg ; move from DE to HL ora a ; was character a null? jrz Start7 ; (yes, okay) cpi ' ' ; a space? jrz Start7 ; (okay, too) BadNum: mvi a,9 ; it's a bad number sta ErCode lxi h,MsgBad jmp Exit ; Start6: lhld LstNum ; get last number call NumChk inx h ; increment it Start7: shld CurNum ; save it as current number lxi d,LblFcb+9 ; insert it into FCB call mhl3dc lda OpMFlg ; check for mode ora a jrnz MMode ; (multiple label mode) ; ; Single label module . . . ; call PrtNxt ; print next label lxi h,MsgDot call epstr call DskRst ; reset disk call ChkDup ; labelled already? jnz Exit ; (yep) call MakFil ; create label jnz Exit ; (space error) call FilAtt ; set attributes lda OpSFlg ; do we save last number? ora a jrnz NoSave ; (no) lhld CurNum ; get last label number used shld LstNum ; ..and store in data sector call DskSav ; save last number jmp Finish ; ; Multiple label module . . . ; MMode0: call crlf MMode: call PrtNxt ; print next label lxi h,MsgDsk ; press any key ... call AskOpr cpi ESC ; quitting? jrz MMode1 ; (yes) call DskRst ; reset disk call ChkDup ; labelled already? cnz epstr jnz MMode0 ; (yep) call MakFil ; create label cnz epstr jnz MMode0 call FilAtt lhld CurNum ; get last label number used shld LstNum ; ..and store in data sector call NumChk inx h ; increment it shld CurNum ; save it as current number lxi d,LblFcb+9 ; insert it into FCB call mhl3dc jr MMode0 ; ..and loop ; MMode1: lda OpSFlg ; are we saving number? ora a jrnz NoSave ; (no) call DskSav ; yes, go do it jmp Finish ; ; Common exit routines . . . ; NoSave: lxi h,MsgNSv jr Exit ; Finish: lxi h,MsgDne jr Exit ; Abort: lxi h,MsgAbt mvi a,4 ; set error code sta ErCode Exit: call epstr call dinit ; clear terminal lda ErCode call puter2 lspd OldStk ret ; ; Subroutines . . . ; ; DskSav -- Save data sector to disk. ; DskSav: call DskRs0 ; reset disk system lxi h,PrgNam ; put program name in FCB lxi d,LblFcb+1 lxi b,11 ldir lda PrgDrv ; log into program DU mov b,a lda PrgUsr mov c,a call logud DskSv1: lda RstFlg ; do we reset drive? ora a cnz DskRst ; (yes) lxi d,LblFcb call initfcb call f$open ; open ourselves jrnz GetDsk ; we can't find ourselves ; lxi h,Entry ; set dma address to our 1st record call setdma lxi h,0 ; set record number call r$write ; write record jrnz DskErr ; (error) call f$close ; close file ret ; GetDsk: call crlf lxi h,MsgPrg ; request program disk call PrtNx1 lxi h,MsgAgn call AskOpr jr DskSv1 ; ..and try again ; DskErr: call crlf lxi h,MsgWEr ; file write error call epstr jmp Abort ; ; FilAtt -- Sets label attributes based on configuration bytes. ; FilAtt: lda ROFlg ; check read-only flag ora a jrz FilAt1 ; (no, skip read-only) lda LblFcb+9 ; set read-only attribute ori 80h ; ..Read Only sta LblFcb+9 FilAt1: lda SysFlg ; check system flag ora a jrz FilAt2 ; (no, skip system) lda LblFcb+10 ; set system attribute ori 80h sta LblFcb+10 FilAt2: lda ArcFlg ; check archive flag ora a jrz FilAt3 ; (no, skip archive) lda LblFcb+11 ; set archive attribute ori 80h sta LblFcb+11 FilAt3: lxi d,LblFcb mvi c,SetAtt call bdos ret ; ; DskRst -- resets current drive only under ZSDOS and CP/M-Plus; ; otherwise, resets disk system. (Based on Carson Wilson's RCPR v1.5 ; for Z34RCP.) ; DskRst: mvi c,CpmVer ; get CP/M version call bdos cpi 30h ; CP/M Plus? jrnc DskRs1 ; (yes) mvi c,DosVer call bdos ; ZRDOS or CP/M? mov a,h ora a jrnz DskRs1 ; (no, assume function 37 is bug-free DskRs0: mvi c,ResSys ; reset disk system call bdos ret ; reset single drive DskRs1: call retud ; get current drive mov a,b ; put it in A inr a ; shift range to 1..16 lxi h,1 ; map drive "A:" DskRs3: dcr a ; done yet? jrz DskRs4 ; (yes) dad h ; shift vector to next drive jr DskRs3 DskRs4: xchg ; put vector in DE mvi c,ResDrv ; reset single drive call bdos ret ; ; MakFil -- create, open, and close a zero-length file. Return ; Z if okay, NZ if no directory space. ; MakFil: lxi d,LblFcb call initfcb call f$mopen ; create and open file jrnz MakFi1 ; (no directory space) call f$close ; close file ret ; MakFi1: mvi a,11 ; set error code (directory full) sta ErCode lxi h,MsgFDr ret ; ; ChkDup -- checks for an existing filename beginning with the ; tag character. Returns Z if not found, NZ if found. ; ChkDup: lda LblTag ; stuff label tag into FCB sta CpmFcb+1 lxi h,WildNm ; fill rest with ?'s lxi d,CpmFcb+2 lxi b,10 ldir lxi d,CpmFcb call initfcb mvi c,FSrchF ; does it exist? call bdos inr a rz mvi a,16 ; set error code (duplicate filespec ?!?) sta ErCode lxi h,MsgExs ; say label exists ret ; ; ScanOp -- scan command line for options ; ScanOp: xra a ; initialize option flags sta OpMFlg sta OpSFlg sta ErCode ; ..and error code lxi h,CpmDma+1 ; point to command line call EatSpc ; jump past spaces inx h ora a rz ; (no more) cpi '/' ; option flag? jrz GetOpt ; (yes) ScanO1: mov a,m ; get past filespec inx h ora a rz ; (no more) cpi ' ' jrz ScanO2 cpi TAB jrnz ScanO1 ScanO2: call EatSpc ora a rz ; (no more) cpi '/' jrnz GetOpt inx h ; GetOpt: mov a,m ; get option ora a rz ; (no more) cpi '/' ; help request? jrz Usage cpi 'M' jrz SetMOp cpi 'S' jrz SetSOp inx h cpi ' ' jrz GetOpt mvi a,19 ; set error code (invalid option) sta ErCode lxi h,MsgIOp jmp Exit ; SetMOp: mvi a,0FFh sta OpMFlg inx h jr GetOpt ; SetSOp: mvi a,0FFh sta OpSFlg inx h jr GetOpt ; Usage: lxi h,MsgUse ; print usage message call epstr lxi h,Entry ; print load address call phl4hc lxi h,MsgUs1 call epstr call prtname lxi h,MsgUs2 jmp Exit ; ; EatSpc -- Gobbles up spaces and tabs ; EatSpc: mov a,m inx h cpi ' ' ; is it a space? jrz EatSpc ; (yes) cpi TAB ; it it a tab? jrz EatSpc ; (yes) dcx h ret ; ; AskOpr -- get response from user ; AskOpr: call epstr call cin ; wait for character cpi CtrlC ; ^C ? rnz ; (nope) jmp Abort ; ; MHL3DC -- Store HL as 3 decimal characters in 3-byte memory buffer ; pointed to by DE (based on Carson Wilson's SMHL5DC+ module in ZSLIB 2.1). ; MHL3DC: push psw ; save regs push b push h pushix push d ; for output popix mvi b,0 ; B=0 for no leading spaces lxi d,100 ; store 100's call MHDC1 MHDC6: lxi d,10 ; store 10's call MHDC1 mov a,l ; store 1's adi '0' ; convert to ASCII call MHDC8 popix ; restore regs pop h pop b pop psw ret ; ; Divide HL by DE and store quotient with leading spaces ; MHDC1: xra a ; set count MHDC2: ora a ; clear carry dsbc de jrc MHDC3 ; done if carry set (further borrow) inr a ; increment count jr MHDC2 MHDC3: dad d ana a ; check for zero jrnz MHDC4 ora b ; 0 = no leading spaces (A=0, A or B = 0 if B=0) jrz MHDC4 mvi a,' ' ; store space jr MHDC8 MHDC4: mvi b,0 ; turn off leading spaces for rest of output MHDC7: adi '0' ; convert to ASCII MHDC8: pushix ; get storage address pop d inxix call MOUT ret ; ; MOUT - Store A to memory at DE (from Carson Wilson's ZSLIB 2.1) ; Entry: A = value to store ; DE = address of memory buffer (1 byte) ; Exit: DE = address of byte after output ; Uses: DE ; MOUT: stax d inx d ret ; ; NumChk -- checks if number is 999 and, if so, aborts. ; NumChk: lxi d,999 call comphd rc lda OpMFlg ; check mode ora a cnz crlf mvi a,9 ; set error code (bad numerical expression) sta ErCode lxi h,MsgRng jmp Exit ; ; PrtNxt -- Print next disk label ; PrtNxt: lxi h,MsgNxt ; report next label PrtNx1: call epstr call stndout call retud ; get current DU mov a,b ; put drive in A adi 'A' ; make it printable call cout mov a,c ; put user in A call pafdc mvi a,':' ; print colon call cout lxi d,LblFcb+1 ; print label call pfn1 call stndend ret ; ; Data . . . ; PrgNam: db ' ' ; program name storage PrgDrv: db 0 ; program drive location PrgUsr: db 0 ; program user location WildNm: db '??????????' ; for existence test ; ; Uninitialized data . . . ; DSEG ; OpMFlg: ds 1 ; non-zero = option M (multiple labels) OpSFlg: ds 1 ; non-zero = option S (don't save) CurNum: ds 2 ; current label number TgtDrv: ds 1 ; default drive TgtUsr: ds 1 ; default user ErCode: ds 1 ; error code ds 50 ; stack OldStk: ds 2 ; stack pointer storage LblFcb: ds 36 ; label file control block ; end