; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * ZDB * ; * * ; * The Z-System Database * ; * Name/Address File Manager * ; * * ; * (C) 1990, 1991 by Joseph I. Mortensen * ; * * ; * * ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; ; Original Author: Joseph I. Mortensen ; 4214 Chelsea Ct. ; Midland, MI ; 517-835-6923 ; Compuserve 70037,3161 ; GEnie J.MORTENSEN3 ; Ladera Z-Node ; ; Major Collaborator ; and Co-Author: Terry Hazen ; ; Assembler/Linker: ZMAC/ZML or Z80ASM/SLRNKP ; ; For documentation see ZDBxx.HLP ; vers equ 18 ; version suffix equ 'B' ; suffix character or ' ' if none month equ 02 ; revision month day equ 04 ; day year equ 92 ; year ; ; the usual equates ; ctrla equ 'A'-40h ; move cursor one word left ctrld equ 'D'-40h ; move cursor right ctrle equ 'E'-40h ; move cursor up one field ctrlf equ 'F'-40h ; move cursor one word right ctrlg equ 'G'-40h ; delete character ctrlq equ 'Q'-40h ; quit add/edit without saving changes ctrlr equ 'R'-40h ; refresh screen ctrls equ 'S'-40h ; move cursor left ctrlt equ 'T'-40h ; delete word right ctrlv equ 'V'-40h ; insert character mode ctrlw equ 'W'-40h ; quit add/edit and save changes ctrlx equ 'X'-40h ; move cursor down one field ctrly equ 'Y'-40h ; delete from cursor to end of field ; bdos equ 05h bel equ 07h bs equ 08h tab equ 09h lf equ 0ah ff equ 0ch cr equ 0dh esc equ 1bh del equ 7fh fcb equ 5ch false equ 00h true equ 0ffh on equ true lstat equ 15 ; ; jthlib routines .request jthlib ext fnamz ; ; zplib routines .request zplib ext vprint,vpstr,gxymsg,gotoxy ; ; zslib routines .request zslib ext mdata1,binbcd ; ; dslip routines .request dslib ext timini,rclock ; ; vlib routines .request vlib ext stndout,stndend,at ;,gxymsg,vprint,vpstr ext cls,ereol,gz3init,@ghl ;gotoxy ext tinit,dinit,@goxy,curon,curoff,grxon,grxoff ; ; z3lib routines .request z3lib ext zsyschk,gzmtop,waitp1s ; ; syslib routines .request syslib ext fxo$open,fxo$close,fx$put ext f$make,f$open,f$close,f$write,getfs ext f$exist,r$write,r$read,setdma,mfn2,f$rename,f$delete ext codend,condin,logud,bios ; fname, ext isctrl,compb,acase3,ssbini,sort ext capin,cin,cout,pa2hc,phldc,phlfdc,mafdc ext lout,lcrlf,comphd,eval10,pout ; ; Program begins here ; jp start db 'Z3ENV' ; standard z3 header db 1 ; type 1 program z3addr: dw 0 ; ; Bios printer ready check flag. Normally set to on (0ffh) to call the ; bios list status routine to check for printer ready. If your system ; hangs on a call to the bios list status routine, set this flag to 0: ; pchkf: db on ; set to 0 to skip bios printer check ; ; Character used to terminate data fields ; termf: db '<' ; char to terminate fields, 00h if none ; ; Default implied CFG filename at 010Dh for use with ZCFNG (8 characters): ; db 'ZDB',vers/10+ '0',vers mod 10 + '0' db suffix db ' ' db 0 ; termination ; ; Default data file name (16 characters - fill unused positions with ; spaces): ; 'duu:filename.typ' deffn: db 'ZDB.DTA ' ; default data file name db 0 ; termination ; ;; Formfeed Flag -- set to zero for no form feed at end of label printing ;; session ;;ffflg: db on ; eliminated in V. 1.8 ; ; Spare configuration patch space ; ds 2 ; spare configuration patch bytes ; ; Printer string patches go here. Each string must have a length byte. ; Extra space is provided for longer printer codes. To eliminate any ; of these strings, make the first byte 0. The following are epson ; fx-85 compatible codes. ; ; Printer reset string, sent at start and end of each label/envelope ; printing session (4 characters maximum): ; reset: db 2 ; length byte db esc,'@' ; printer reset string ds 2 ; extra space ; ; Set printer for printing return address. This string is sent to the ; printer after it is reset each time the return address is printed ; (12 characters maximum): ; rin: db 7 db 0fh ; set condensed print db esc,'x',1 ; set nlq mode db esc,'p',1 ; set proportional print ds 5 ; extra space ; ; Set printer for printing main address. Cancel any return address ; settings that may require it. Do NOT use reset, as it will cancel ; the label form length (12 characters maximum): ; ain: db 9 db 12h ; cancel condensed print db esc,'E' ; set emphasized print db esc,'x',1 ; set nlq mode db esc,'p',1 ; set proportional print ds 3 ; extra space ; ; Set column for envelope address left margin: ; lemarg: db 40 ; start address at column 40 ; ; Set column for label address left margin: ; llmarg: db 0 ; 0 for flush left ; ; Use return address in label (only if labels are large enough): ; lra: db 0 ; 0 if no return address is desired ; ; Set number of lines per label (8 characters maximum): ; labln: db 3 db esc,'C',6 ; set lines per label ds 5 ; extra space ; ; Return address patch goes here. Space is provided for up to 80 ; characters. If you plan on using a return address on your labels, ; add enough line feeds at the end of the return address to bring ; you to the first line of the label address. The rest of the ; line feeds required for an envelope are at 'addrsp'. For ; normal business size envelopes, the total number of line feeds ; must remain 14 and the string must be 0-terminated. To ; eliminate the return address, change retadr to a terminating ; null (00h) and put 14 line feeds at 'addrsp'. ; retadr: line1: db 'Joseph I. Mortensen' db cr,lf line2: db '4214 Chelsea Ct.' db cr,lf line3: db 'Midland, MI 48640' db cr,lf,lf ; ; Pad the remainder of the address space with 0's ; rept 80-($-retadr) db 0 endm ; ; Space from return address to address. For normal business ; envelopes, the total number of line feeds in the return address ; and addrsp should be 14. If the number of line feeds in the ; return address -> first label address line is the normal three ; lines, addrsp should be set to 10 line feeds. ; addrsp: db 10 ; ; command line help message ; hlpmsg: call vprint db 'ZDB vers ',vers/10+ '0','.',vers mod 10 + '0' db suffix db ' Name/Addr Database' db cr,lf db ' Syntax: ZDB [[dir:]datafile.typ] ',cr,lf dc ' Default datafile: ' ld hl,deffn ; display default filename call vpstr call vprint dc cr,lf jp exit2 ; ; program starts here ; start: ld (stack),sp ld sp,stack ; set up internal stack ; xor a ld hl,data ; initialize data area ld de,data+1 ld (hl),a ld bc,datalen ldir ; ld hl,(z3addr) ; check for z3 system call zsyschk jp nz,exit2 ; not present, exit call gz3init ; initialize vlib stuff ld de,90h ; add TCAP offset from env add hl,de ; and ld (tcap),hl ; store it for use by edloop call tinit ; initialize terminal ; ld a,(fcb+1) ; check command tail cp '/' ; asking for help? jp z,hlpmsg ; yes, print help message call gettim ; get the time jr z,clkok ; if ok, jump nclk: call noclk ; otherwise get date from manual entry clkok: ld a,(fcb) ; get drive call chkdrv ld (fdrv),a ; store it ld (datafil),a ; and in header ; ld a,(fcb+13) ; get user for file ld (fusr),a ; store it ; ; if no data file specified, use default datafile name ; ld a,(fcb+1) ; check first filename character cp ' ' jr nz,reopen ; filename specified ld de,fcb ; no filename, use default ld hl,deffn call fnamz ; parse filename to fcb JR CLKOK ; ; after writing a new sorted file, program loops to reopen ; reopen: ld a,(fusr) ; set user area for file call setua ld (fcb+12),a ; set extent to 0 ld (fcb+32),a ; likewise w/ current record ld a,(fdrv) sub a,41h ld b,a ld a,(fusr) ld c,a call logud ld de,datafil+1 ; save user in header call mafdc ex de,hl ld a,':' ld (hl),a ; store colon inc hl push hl ; save header pointer ; ; open file ; ld de,fcb call f$exist ; check to see if it exists jr nz,open call f$make ; if not, create and open it inc a jp z,nogood open: call f$open jp nz,nogood ld de,fcb+1 ; save original name in buffer ld hl,nambuf call mfn2 pop hl ; restore header pointer call mfn2 ; save filename ; ld de,fcb call getfs ; get file size in records ld (recs),hl call frame ; do frame and screen layout call index ; create index ; ld hl,(recs) ; save it ld a,h ; check for empty file or l jr nz,dotop ; not empty ; doadd: call curtim ; display time and date ld a,'A' ; empty file, so adding new record jr menua ; is the only choice! ; dotop: call firstr ; display first record and wait for cmd ; menu: call clrmnu ; main menu line db 1,'A=Add C=Call D=Del E=Ed F=Find X=Xfind <>=Prv/Nxt ' dc 'O=Outp I=Idx ^S=Sort' call qquit ; ; main program loop ; main: call curtim ; display current time & date call at db 24,79 ; position cursor at '?' in menu line call getchar ; wait for command menua: ld de,cmdtbl ; run command thro' command table call acase3 jr main ; ; end of main loop ; ; ; command table ; cmdtbl: db 18 ; no. of entries in command table dw menu db 'Q' dw exit db esc dw exit db 'A' dw new db 'E' dw edit db 'D' dw delete db 'F' dw find db ',' dw prev db '<' dw prev db '.' dw next db '>' dw next db 'O' dw output db ctrlr ; ^r to refresh screen if it gets out of whack dw refscr db ctrls ; ^s for sort dw asksrt db 'I' dw settyp db 'T' dw firstr db 'X' dw qfind db 'B' dw last db 'C' dw dial ; ; exit routines ; exit: ld de,fcb call f$close call curon call dinit exit2: ld sp,(stack) ret ; ; main subroutines ; ; screen display routines ; frame: call curoff ; turn off cursor call cls ; clear screen call at ; do standout header db 1,1 ld b,66 ; do standout bar call pad call at db 2,1 call grxon ld b,79 ld a,(@ghl) frloop: call cout djnz frloop call at db 23,1 ld b,79 frlp0: call cout djnz frlp0 call grxoff ; ld hl,panel ld b,(hl) ; enter with hl=panel pointer inc hl ; scrnloop: call @goxy call vpstr djnz scrnloop ret ; refscr: call frame ; refreshes entire screen call currec jp menu ; ; display current record number and total ; bldisp: currec: ld hl,(recptr) call lhlhl ; get record number of current record call divhl2 ; divide by 2 inc hl ; make it rel 1 currec0:call gxymsg db 1,45,1,0 call phldc call vprint dc ' of ' ld hl,(recs) ; get total number of records call divhl2 ; divide by 2 call phlfdc call vprint ; terminate field and end standout dc ' ',2 call clrdis ; and fall through to displa ; ; display current record ; displa: ld hl,pospanel ; point to cursor position panel ld de,fieldpanel ; point to field panel ld b,(hl) inc hl displaloop: call @goxy ; position cursor to field start inc hl ; point to next field call stndout ex de,hl ; hl=field address push hl ; save field address pointer call lhlhl ; get field addr in hl call vpstr ; display field call stndend pop hl inc hl ; point to next field inc hl ex de,hl djnz displaloop ; newdat: call gxymsg ; displays date db 3,66,1,0 ; date location ld hl,datmod+1 ; get month call paslsh inc hl ; get day call paslsh dec hl dec hl ; get year ld a,(hl) call pa2hc jp stndend ; divhl2: srl h ; divide hl by 2, result in hl rr l ret ; curtim: call gxymsg ; displays current date and time db 1,63,1,0 ld hl,today+1 call paslsh inc hl call paslsh dec hl dec hl ld a,(hl) call pa2hc call gettim jr nz,notime ld a,' ' call cout inc hl inc hl inc hl ld a,(hl) call pa2hc ld a,':' call cout inc hl ld a,(hl) call pa2hc notime: jp stndend ; gettim: call timini ; see if there is a clock jr nz,clkfnd inc a ret clkfnd: push hl ld hl,today ; point to clock buffer call rclock pop hl ret ; paslsh: ld a,(hl) call pa2hc ; print date with slash ld a,'/' jp cout ; ; fill record display fields with blanks ; clrdis: ld hl,pospanel ; point to cursor position panel ld b,(hl) inc hl clrloop: call @goxy ; position cursor to field start push bc ; save count ld b,(hl) ; get field length call pad ; pad field with blanks pop bc ; restore count inc hl ; point to next field djnz clrloop ret ; ; ; routines for adding, editing, and deleting records ; edit: xor a ; edit existing data record ld (newflg),a call decfptr call edita jp wrecs ; write data record and resort index ; new: ; adding a new record call chkmem ; check to see if enough memory left jp c,nomem ; out of memory, jump to out of memory message call clrdis ; ok, continue call iniblk ; initialize edblk ld a,true ; set "new" flag ld (newflg),a ; edita: ld hl,today ; get date from "today" ld de,datmod ; and move to datmod ld bc,3 ldir call newdat ; get the date and display it call clrmnu db 1,'^X/TAB/RET=nxt fld ^E=prv fld ^S/^D=char l/r' dc ' ^Y=era cursor rt ESC=exit',2 ; edfields: ld hl,pospanel ; point to cursor position panel ld de,fieldpanel ; point to field address panel ld b,(hl) ; number of fields inc hl ; efloop: ld a,(hl) ; save row position ld (lpos),a call @goxy ; position cursor push bc ; save count ld b,(hl) ; get field length dec hl ; save column position ld a,(hl) ld (cpos),a inc hl ; point to next field inc hl ex de,hl ; hl=field address push hl call lhlhl ; get field pointer in hl push de call edloop ; edit field pop de pop hl inc hl ; point to next field address inc hl ex de,hl ; hl=next field cursor position pop bc cp esc ; quit edloop? jr z,term ; yes, go to end cp ctrlw ; ^W same as esc jr z,term cp ctrlq ; ^Q to quit without saving edit jr z,term cp ctrle ; if ^e, go to previous field jr z,prevf djnz efloop ; else do next field jr term ; done ; prevf: or a ; clear carry push bc ; save count ld bc,6 ; back up to previous field cursor pos sbc hl,bc ex de,hl ld bc,4 ; back up to previous field sbc hl,bc ex de,hl pop bc ; restore count inc b ; back up one field ld a,(pospanel) ; get number of fields cp b ; test for first field jr nc,efloop ; loop or fall thru if past first field ; term: push af call delins ; delete insert msg pop af cp ctrlq ; exit without saving? jr nz,term1 ; ld a,(newflg) ; editing an existing record? or a jr nz,xadd ; add abort pop hl ; if edit abort, discard return address jr xedit xadd: call ckdel ; quit if no non-deleted records call decfptr ; if add abort, back up one xedit: call current ; redisplay current record jp menu ; term1: call clrmnu dc 1,'TAB=restart/[SAVE] ?',2,bs call cin cp tab jp z,edita ld a,(newflg) ; editing an existing record? or a ret z ; yes, we're done here ; ld hl,(recs) ; get number of index records ld (xfptr),hl ; save it for updinx and to ld (fptr),hl ; keep track of appended records ld hl,(n) ld (count),hl ; save number of index records call rwrtblk call updinx ; add new key to index table call inxsrt ; rebuild order table and sort index ld hl,(recs) ; increment record count inc hl inc hl ld (recs),hl ; call clrmnu dc 1,'Add another record? [Y]/n ?',2,bs call getchar cp 'N' jp nz,new ; ; since the index may have been resorted and we only know the ; current fptr, we need to find the corresponding recptr to properly ; redisplay the current record at its new location in the index ; getrp: call decfptr ; back up one getrp0: ld hl,(first) ; start of index ld de,14 ; record number offset ; recplp: add hl,de ; hl=recptr push hl ; save recptr call lhlhl ; hl=fptr for index record ld de,(fptr) ; de=fptr call comphd ; does this record have the right fptr? pop hl ; restore recptr ld de,16 ; offset to next jr nz,recplp ; not this index record, check next ld (recptr),hl ; save record pointer call current ; display record jp menu ; ; write 256 bytes to random record ; rwrtblk:ld hl,edblk call rwrite ; write new record ld hl,edblk1 jp rwrite ; delete record ; delete: call clrmnu dc bel,1,'Are you sure? y/[N] ?',2,bs call getchar cp 'Y' jp nz,menu ; ld a,'D' ld (newflg),a call iniblk ; fill current record w/ nulls ld hl,edblk ld (hl),on ; make first byte 0ffh call decfptr ; decrement file record ; wrecs: call rwrtblk ; write 256 bytes to file ld hl,(recptr) ; get record pointer ld de,14 sbc hl,de ; back up to index pointer ex de,hl ; pointer in de for movkey call movkey ; update index key ld de,ssb call sort ; sort index ; ld a,(newflg) ; deleting a record? cp 'D' jp nz,getrp ; exit from edit, so get record pointer call ckdel ; quit if no non-deleted records call next jp menu ; ; file movement routines ; ; find and display next record ; next: call ckeoi ; check for end of index jr nz,oknxt ; not eoi ; firstr: call gotop ; set pointer to beginning of file current:call rread ; read current record jr ckrec ; oknxt: call riread ; increment pointers, read next record ckrec: call delrec ; check for deleted record jp nz,bldisp ; ok, display it jr next ; deleted, so get next record ; ; move to and display last record ; last: ld hl,(xrecptr) ; get last index record pointer in hl call savptr jr current ; ; move to and display previous record ; prev: call backup ; move pointer back call rread ; read a record ld hl,edblk ; check first byte to see if it's a ld a,(hl) ; valid record. 0ffh = deleted record. inc a jr z,prev ; if deleted, try the previous one jp bldisp ; ; get first index record number ; gotop: ld hl,(first) ; get start of index table getpt0: ld de,14 ; offset to record number getptr: add hl,de savptr: ld (recptr),hl ; save record pointer call lhlhl ld (fptr),hl ; save record number ret ; ; back up to previous record ; backup: ld hl,(recptr) ; get current index record or a ; clear carry ld de,16 ; back up one index record sbc hl,de ; get new record pointer ld de,(first) ; check if past first call comphd jr nc,savit ld hl,(xrecptr) ; point to last record pointer savit: jr savptr ; save pointers ; ; Increment record pointers ; mvrptr: call ckeoi ; check for end of index table jr z,gotop ; yes, start over ; mxrptr: ld hl,(recptr) ; get index record pointer ld de,16 ; point to next jr getptr ; ; get contents of hl in hl ; lhlhl: ld a,(hl) ; get record number inc hl ld h,(hl) ld l,a ret ; ; decrement record pointer ; decfptr:ld hl,(fptr) dec hl dec hl ld (fptr),hl ret ; ; search routines. uses a revised 'scanner' from syslib which eliminates ; case sensitivity. QFIND uses an index in RAM to find file pointers for ; last names or zip codes, depending on which index is selected. FIND ; runs through the file, top to bottom, to do its search. ; scanner: ; main loop ; scan: ld a,b ; done if b