; ; DISKNUM.MAC -- Version 1.1 ; ; A utility to create null disk labels under CP/M. ; A Z80 CPU is required. ; ; USAGE: ; ; DISKNUM {d:} ; ; If a drive specification is not given, the current default drive is ; assumed. ; ; DISKNUM creates consecutively numbered disk labels. The labels are ; null files with a three-digit number in the filetype, compatible with ; MCAT, FATCAT, and similar disk cataloging programs. On exit, DISKNUM ; will save internally the next number to be used (if you don't tell it ; not to), so you don't have to keep track of the numbers yourself. ; ; If the ATTRIB label (below) is set TRUE, then DISKNUM will set the ; disk label file attributes to RO (so you won't accidentally erase it), ; SYS (so it won't appear in your ordinary directory), and ARC (so it ; won't be copied to another disk by a backup program). ; ; I got the idea for DISKNUM from a similar program, VOLNUM, written ; for CP/M 2.2. Unfortunately, that program had some problems and the ; source code wasn't available, so I decided to start from scratch. ; This utility was written for CP/M 3.x, but should run under CP/M 2.2 ; if it is re-assembled with the CPM3 equate set to FALSE. (See CP/M ; 2.2 NOTE below.) ; ; DISKNUM has the following features: ; - A carriage return always means "yes" at any prompt. ; - You can enter a new disk number at any time and subsequent ; numbers will be incremented from there. ; - The target disk can be in any drive. You aren't stuck with ; labelling disks only in drive B or only in drive A. ; - The target drive will be reset before writing each disk label ; for safety, but only that drive, NOT your whole disk system ; (unless you are using a CP/M 2.2 system). ; - The program can run from any drive, but only from the current ; user area. It will find out what drive it's on so it can save ; the next number on exit, but it can't find itself if it was ; loaded from another user area. ; - DISKNUM must know what its name is. If you want to change ; the name of the program or create several versions, you must ; also change the name internally at the label PrgFil. You ; can also change it with SID or EDFILE. Just look for "DISKNUM COM" ; in the first sector and change it to whatever you want. ; - The distributed version sets the disk volume label to "-DISK.nnn". ; That, too, can be changed with SID or EDFILE, or in the source ; at the label NumFil. ; - A single disk drive can easily be used. Once DISKNUM is running ; you can change disks freely. DISKNUM will ask for its program ; disk if it needs it to save the next disk label number, but it ; will look first before bothering you. ; ; CP/M 2.2 NOTE: I don't know a way under 2.2 for a program to detect ; the drive it was loaded from (this is no problem under CP/M Plus). To ; run DISKNUM from a drive other than A you will have to change the first ; byte at the label PrgFil, which is currently a "1". Make that byte the ; number of the drive DISKNUM will be loaded from: B=2, C=3, etc. In ; addition, since 2.2 does not support BDOS Function 37 to reset selected ; drives, the entire disk system will be reset before each disk access. ; ; Formatting and labelling disks is a real chore. I hope this makes life ; a bit easier for somebody besides myself. Let me know if there are any ; problems. ; ; Version 1.0 -- December 24, 1987 -- Gene Pizzetta ; 481 Revere Street ; Revere, MA 02151 ; Voice: (617) 284-0891 ; CompuServe: 72060,505 ; GEnie: E.Pizzetta ; Q-Link: GeneP ; ; Version 1.1 -- December 26, 1987 -- Gene Pizzetta ; Added CP/M 2.2 support. ; ; Developed with SLRMAC. May also be assembled with MAC (Z80.LIB required). ; ; FALSE equ 00h TRUE equ not FALSE ; ATTRIB equ TRUE ; to set RO, SYS, and ARC attributes CPM3 equ TRUE ; set FALSE for CP/M 2.2 ; WBoot equ 0000h ; warm boot Bdos equ 0005h ; BDOS entry LoadDrv equ 050h ; Drive loaded from Fcb equ 05Ch ; default file control block FcbDr equ Fcb ; drive FcbName equ Fcb+1 ; filename FcbType equ Fcb+9 ; filetype AttRO equ FcbType ; file attribute T1 (RO) AttSYS equ FcbType+1 ; file attribute T2 (SYS) AttARC equ FcbType+2 ; file attribute T3 (ARC) FcbEx equ Fcb+12 ; extent FcbCr equ Fcb+32 ; current record FcbR0 equ Fcb+33 ; random record (low byte) FcbR1 equ Fcb+34 ; random record (middle byte) FcbR2 equ Fcb+35 ; random record (high byte) BEL equ 07h ; bell CR equ 0Dh ; carriage return LF equ 0Ah ; linefeed ESC equ 1Bh ; escape ; ; BDOS service functions ; ConIn equ 1 ConOut equ 2 PrtStr equ 9 ResSys equ 13 FOpen equ 15 FClose equ 16 FCreate equ 22 CurDsk equ 25 SetDMA equ 26 SetAtt equ 30 WRand equ 34 ResDrv equ 37 ; ; Macros ; MACLIB Z80 ; BLKMOVE macro FROM,TO,LENGTH ; Z80 block move lxi h,FROM lxi d,TO lxi b,LENGTH ldir endm ; ; org 100h ; ; DatSec -- This 128 bytes will be saved back to disk on exit ; DatSec: jmp MAIN NumFil: db 0,'-DISK 000' ; filename used for disk label ds 36-($-NumFil) PrgFil: db 1,'DISKNUM COM' ; filename used by this program ds 36-($-PrgFil) TmpNum: db 0,0,0 ; scratch workspace ds 128-($-DatSec) ; ; End of DatSec ; ; =================== ; ; Messages ... ; MsgSOn: db 'DISKNUM for CP/M ' IF CPM3 db 'Plus' ELSE db '2.2' ENDIF db ' Version 1.1$' MsgNxt: db CR,LF,LF,'Next Disk: $' MsgNew: db CR,LF,LF,'Insert Disk in Drive ' NumDrv: db 0,' . . . and press ,',CR,LF db ' to Quit, or Enter new number: $' MsgCan: db BEL,CR,LF,LF,'Quit? $' MsgInv: db BEL,CR,LF,LF,'Invalid character.$' MsgEx: db BEL,CR,LF,LF,'File Exists.$' MsgDir: db BEL,CR,LF,LF,'No Directory Space.$' MsgSav: db CR,LF,LF,'Save last number? $' MsgPrg: db BEL,CR,LF,LF,'Put program disk in Drive ' PrgDrv: db 0,CR,LF,' . . . and Press any key. $' MsgFCs: db BEL,CR,LF,LF,'Error closing program file.$' MsgDon: db CR,LF,LF,'DONE$' ; ; Program entry point ... ; MAIN: IF CPM3 lda LoadDrv ; get the drive we came from sta PrgFil ; ..and save it (CP/M Plus only) ELSE lda PrgFil ; get program file for CP/M 2.2 ENDIF adi 40h ; ..make it ASCII sta PrgDrv ; ..and store in string lda FcbDr ; is there a drive in the tail? cpi 0 jrnz GotDr ; (yes) mvi c,CurDsk ; no, get current disk call Bdos inr a ; increment it for FCB GotDr: sta NumFil ; store the target drive adi 40h ; ..make it ASCII sta NumDrv ; ..store in string lxi d,MsgSOn ; and sign on call PRINT ; ; Main Loop ... ; Over: lxi d,MsgNxt ; report next number call PRINT lda NumFil+9 ; get next disk number call PUTCH lda NumFil+10 call PUTCH lda NumFil+11 call PUTCH lda NumFil ; get drive in A call RESET ; ..and reset it lxi d,MsgNew ; tell what the choices are call PRINT lxi d,TmpNum ; get response mvi b,3 ; be prepared for 3 numbers GetRes: call GETCH cpi ESC ; are we quitting? jrz Cancel ; (yes) cpi CR ; are we continuing? jrz Cont ; (yes) cpi ':' ; is it a number? jrnc Inval ; (no) cpi '0' jrc Inval stax d ; store number in string inx d ; increment pointer dcr b ; decrement counter mov a,b ora a ; zero yet? jrnz GetRes ; (no) call GETCH ; we've got three valid numbers cpi CR ; ..now wait for a carriage return jrnz Inval ; (it wasn't a return) BLKMOVE TmpNum,NumFil+9,3 ; Cont: BLKMOVE NumFil,Fcb,36 ; move filename to FCB call OPEN ; open file ora a ; does it already exist? jrz Exists ; (yes) call MAKEF ; no, so create it inr a ; was it successful? jrz DirFul ; (no) call CLOSE ; now close it IF ATTRIB call FILATT ; set attributes ENDIF call UPONE ; increment the disk number jmp Over ; Inval: lxi d,MsgInv ; report invalid call PRINT jmp Over ; Exists: lxi d,MsgEx ; report file exists call PRINT jmp Over ; DirFul: lxi d,MsgDir ; report directory full call PRINT jmp Over ; Cancel: lxi d,MsgCan ; quit? call PRINT call GETCH ; get response cpi 'n' jz Over ; (no, continue) cpi 'N' jz Over ; ; End of Main Loop ... fall through to SavNum ; ; ==================== ; ; Save DatSec to disk ... ; SavNum: lxi d,MsgSav ; ask if number save is desired call PRINT call GETCH cpi 'N' jrz EXIT cpi 'n' jrz EXIT cpi 'Y' jrz DskSav cpi 'y' jrz DskSav cpi CR jrz DskSav jr SavNum ; DskSav: lda PrgFil ; get program drive call RESET ; ..and reset it call SAVE ; save DatSec jrnz GetDsk ; (program not found) lxi d,MsgDon ; tell 'em we're through call PRINT ; EXIT: jmp WBoot ; ..and return to CP/M ; GetDsk: lxi d,MsgPrg ; request program disk mvi c,PrtStr call Bdos call GETCH ; wait for it jr DskSav ; ..and try again ; ; End of Main Program ... ; ; ======================== ; ; Subroutines ... ; ; OPEN: lxi d,Fcb ; open file mvi c,FOpen call Bdos ret ; MAKEF: lxi d,Fcb ; make file mvi c,FCreate call Bdos ret ; RWRITE: lxi d,Fcb ; write random sector mvi c,WRand call Bdos ret ; CLOSE: mvi c,FClose ; close file lxi d,Fcb call Bdos ret ; GETCH: push b push d mvi c,ConIn ; get character from console call Bdos pop d pop b ret ; PUTCH: mvi c,ConOut ; put character to console mov e,a call Bdos ret ; PRINT: mvi c,PrtStr ; print string to console call Bdos ret ; IF ATTRIB FILATT: lda AttRO ; set file attributes ori 80h ; ..Read Only sta AttRO lda AttSYS ; ..System ori 80h sta AttSYS lda AttARC ; ..Archive ori 80h sta AttARC lxi d,Fcb mvi c,SetAtt call Bdos ret ENDIF ; UPONE: lda NumFil+11 ; increment disk number inr a cpi ':' jrz UpTen sta NumFil+11 ret ; UpTen: mvi a,'0' sta NumFil+11 lda NumFil+10 inr a cpi ':' jrz Up100 sta NumFil+10 ret ; Up100: mvi a,'0' sta NumFil+10 lda NumFil+9 inr a cpi ':' jrnz UpDone mvi a,'0' UpDone: sta NumFil+9 ret ; RESET: IF CPM3 lxi h,00000001b ; assume it's drive A shld TmpNum ; store it in workspace dcr a ; make A=0, etc. mov c,a ; drive is in reg A, store in C ora a ; is it 0? jrz ResDsk ; (yes, nothing to do) mvi a,2 ; set A to number of bytes lxi h,TmpNum ; put mask address in HL Reset1: mov e,l ; save address of LSB mov d,h mov b,a ; move length to B ora a ; clear carry Reset2: ralr m ; rotate byte left inx h ; increment to MSB djnz Reset2 mov l,e ; restore address of LSB mov h,d dcr c ; decrement number of shifts jrnz Reset1 ; ..and loop until 0 ResDsk: lhld TmpNum ; get mask into HL xchg ; ..move it to DE mvi c,ResDrv ; ..and reset it ELSE mvi c,ResSys ; under CP/M 2.2 reset all drives ENDIF call Bdos ret ; SAVE: BLKMOVE PrgFil,Fcb,36 ; set up FCB lxi d,DatSec ; set DMA mvi c,SetDMA call Bdos call OPEN ; open file ora a ; check for open error rnz ; (no file) call RWRITE ; ..write sector call CLOSE ; ..and close it ora a ; check for close error jnz NClose ; (found one) ret ; NClose: lxi d,MsgFCs ; say we can't close it mvi c,PrtStr call Bdos jmp EXIT ; ..and pack it in ... ; end