; Program: ZDKCOM.Z80 ; Version: 1.3 ; Date: 21 April 1989 ; Author: Carson Wilson ; Assembly: Z80ASM, SLRNK, SYSLIB4 ; Changes: Fixed bug which caused ZDKCOM to abort if the value in memory ; at BCNTR was greater than 7E hex. ; Fixed bug which prevented switching from decompile to compile ; mode with ZCPR "GO" command. ; Lines in .ZDT files now end in CRLF instead of just CR for ; easier viewing. ; Improved status reports, help screen, etc. ; ; Comments: Adapted with permission from VDKCOM by Fred Haines. ; To assemble with Z80ASM and SLRNK, ensure ZDKCOM.Z80, ; ZDKCOM.SUB, and SYSLIB.REL are available from the current ; directory, and type: ; ; Z80ASM ZDKCOM/R ; SLRNK ZDKCOM/I ; ; PROGRAM: VDKCOM.Z80 ; VERSION: 1.2 ; DATE: 16 April 1988 ; AUTHOR: Fred Haines ; Copyright 1988 by Fred Haines. Released by the author to the CP/M ; community for all reasonable noncommercial purposes. Thanks to Rob ; Friefeld for code review and corrections. ; Version 1.2 - 1 May 1988 ; ; This version is almost entirely the work of Rob Friefeld, who compacted ; subroutines, established a DSEG, and substituted a standard PRINT ; routine for SYSLIB's EPRINT routine, reducing the program from 26 ; records to ten, with consequent savings in space and speed. He also ; noted that the report of activity to the console, which I thought would ; be necessary to reassure the user that the program was working, were ; taking most of the time the program took to run. It actually runs fast ; enough that you don't have time to worry about it, so I took it out. ; Other than the substantial increase in speed, there are no new features. ; Version 1.1 - 21 April 1988 ; ; Simplified to permit lower case 'n' or 'q' character at beginning ; of any key macro to specify No Repeat or Quiet option rather than ; '' or '' strings. A slash can be used to specify that the next ; character is to be taken literally. Use '/^' to print a literal ; carat rather than designate a control character and '//' to print a ; literal slash. Single slashes will be ignored, so, if you need to ; start a no-option string with 'n' or 'q', just precede it with a ; slash. This not only makes things easier for the user, it elimates a ; lot of unduly complicated code and runs faster. Other code has been ; rationalized as well. ; Version 1.0 - 16 April 1988 ; ; VDKCOM is a "compiler" of *.VDK files for installation in VDE v2.65 ; with VINST265. This first version does not permit strings that begin ; with the character '<' or the character '^' anywhere in a string, ; except as the prefix for a control character. (The string '^^' will ; produce the control character for changing the case of a letter.) If ; you need a '<' as the first character of a macro key string, precede ; it with 'c^S' where c stands for any character but the prohibited ones. ; I have no workaround for '^'. VERS EQU 13 FALSE EQU 0 TRUE EQU NOT FALSE EXT F$DELETE ; delete existing file EXT F$MAKE ; create a new file EXT FX$GET ; fetch byte from input file EXT FX$PUT ; write byte to output file EXT FXI$CLOSE ; close input file for byte i/o EXT FXI$OPEN ; open input file for byte i/o EXT FXO$CLOSE ; close output file for byte i/o EXT FXO$OPEN ; open output file for byte i/o EXT PFN2 ; print file name to console ; CP/M equates FCB EQU 5CH ; FCB CR EQU 13 ; LF EQU 10 ; bell equ 7 BS EQU 8 ; TAB EQU 9 ; PROGRAM ; print banner CALL PRINT DB CR,LF DB 'ZDKCOM, Version ' DB VERS/10+'0','.',(VERS MOD 10)+'0' DB ' - key file compiler for ZDE',cr,lf DB 0 ; test for help request LD A,(FCB+1) ; get first character of file name CP ' ' ; no file spec? JR Z,HELP CP '/' ; help option caught? JP NZ,CHKTYP ; print help information HELP: CALL PRINT DB ' Syntax:',CR,LF DB tab,'ZDKCOM ufn.ZDT',tab DB ' - convert text file to key file',cr,lf DB tab,'ZDKCOM ufn.ZDK',tab DB ' - convert key file to text file',cr,lf DB 0 RET ; check type of input file for ZDT or ZDK CHKTYP: LD (OLDSTK),SP ; store system stack pointer ld a,lf call conout ; Initialize to ZDT->ZDK (in case of ZCPR "GO ufn.ZDT" command). LD HL,FLAG ; using last byte of file type as flag LD (HL),'T' ; input defaults to .ZDT LD HL,IOFCB1+11 ; point at file type in output FCB LD (HL),'K' ; default to .ZDK LD HL,FCB+9 ; point HL at first byte of file type LD A,(HL) ; get it into A CP 'Z' ; is it going to be ZDT or ZDK? JP NZ,TYPERR ; if it isn't, quit now INC HL ; get second character LD A,(HL) ; stick it in A CP 'D' ; still building ZDT or ZDK? JP NZ,TYPERR ; if not, bail out INC HL ; get third character LD A,(HL) ; stick it in A CP 'T' ; is it a T? JP Z,SETFCB ; bingo! CP 'K' ; no, we're converting ZDK to ZDT JP NZ,TYPERR ; if neither T nor K, just quit ; If we're writing ZDK->ZDT, reverse FCB's and flag byte LD HL,FLAG ; using last byte of file type as flag LD (HL),'K' ; input must be changed to ZDK LD HL,IOFCB1+11 ; point at file type in output FCB LD (HL),'T' ; change it to ZDT ; write input filename to both input and output i/o FCB's SETFCB: LD HL,FCB+1 ; point HL at first byte of file name LD DE,IOFCB0+1 ; point DE at i/o FCB LD BC,8 ; number of characters in file name LDIR LD HL,FCB+1 ; point HL at first byte of file name LD DE,IOFCB1+1 LD BC,8 ; number of characters in file name LDIR ; print file names to console CALL PRINT DB ' Converting ' DB 0 ld de,iofcb0+1 ; get input filename from FCB CALL PFN2 ; print it to console CALL PRINT db ' to ',0 ld de,iofcb1+1 ; same with output filename call pfn2 call print db '... ' DB 0 ; open input file LD DE,IOCTL0 ; point to control block for input file CALL FXI$OPEN ; ..and open it JP Z,NOFILE ; if file not found ; create and open output file OUTFILE: LD DE,IOFCB1 ; point to FCB in output IOCTL block CALL F$DELETE ; delete old file CALL F$MAKE ; create file described in IOCTL LD DE,IOCTL1 ; point to output IOCTL CALL FXO$OPEN ; ..and open file for byte i/o JP Z,NODIR ; if no directory space ; branch according to ZDT->ZDK or ZDK->ZDT LD A,(FLAG) ; flag is T or K in i/o input FCB file type CP 'K' ; T requires no action JP Z,ZDK ; K switches you into ZDK-ZDT conversion ; start overall program counter LD BC,512 ; ZDK is exactly four records long ; initialize byte count buffer XOR A ; zero A LD (BCNTR),A ; store it to byte counter buffer ; implant ZDENSTAL version ID number as first two bytes of file LD DE,IOCTL1 ; set for output LD A,02H ; enter two-byte version ID (0250h) CALL OUTPUT ; write first byte LD A,50H ; second ID byte CALL OUTPUT ; write second byte ; reinitialize byte count XOR A ; zero A LD (BCNTR),A ; store it to byte counter buffer ; start new string by leaving place for byte-count byte STRING: XOR A ; use temporary 00h for string-length byte CALL OUTPUT ; send it to output file CALL SETPTR ; mark place for later insert of length ; check for LF and No Repeat or Quiet Option LD DE,IOCTL0 ; initialize input file CALL FX$GET ; get next character CP 0AH ; is it the leftover line feed? CALL Z,FX$GET ; burn it and get next character CP 'n' ; is it an No Repeat option character? JR Z,NOPT ; process string as No Repeat option CP 'q' ; is it a Quiet option JR Z,QOPT ; process it as Quiet option JR GETCHR1 ; keep character we've got and process string ; process strings from input file GETCHR: LD DE,IOCTL0 ; initialize input file CALL FX$GET ; get next character GETCHR1: CP 0DH ; is it the end of the string? JR Z,COUNT ; go take care of the count CP '/' ; is it literal indicator? JR Z,LITERAL ; burn it and get next character instead CP '^' ; is it a control character? JR Z,CTRL ; convert to real control character CP 1AH ; is it EOF? JR Z,EOF ; go quit CP 0AH ; is it LF? JR Z,GETCHR ; just skip it GETCHR2: CALL OUTPUT ; ..else, output character to ZDK file JR GETCHR ; ..and get the next one ; process No Repeat and Quiet options QOPT: CALL NQOPT LD DE,IOCTL0 ; reset for input NOPT: CALL NQOPT JR GETCHR NQOPT: CALL FX$GET ; waste 'n' character CP '^' ; is it control character? CALL Z,OPCTRL ; go take care of it CP '/' ; is it a literal indicator? CALL Z,FX$GET ; waste it ADD 80H ; set high bit JP OUTPUT ; send to output file OPCTRL: CALL FX$GET ; get next character SUB '@' ; make into control character RET ; convert ASCII '^c' to real control character CTRL: CALL OPCTRL JR GETCHR2 ; string complete, get count and write to string-length byte COUNT: LD HL,(PNTR) ; get pointer to length byte into HL DEC HL ; string-length byte itself not counted LD A,(BCNTR) ; get current byte count DEC A ; subtract one for string-length byte LD (HL),A ; write it to length byte in output file buffer XOR A ; restart byte counter at zero LD (BCNTR),A ; store to the buffer JR STRING ; start new string ; burn literal indicator and output next byte no matter what it is LITERAL: CALL FX$GET ; get next character, skipping literal JR GETCHR2 ; fill remainder of four-sector file with nulls EOF: XOR A ; zero accumulator LD DE,IOCTL1 ; set to output character CALL FX$PUT ; write it and count down another byte DEC BC LD A,B ; check B against C for both set 0 OR C JR NZ,EOF ; if not, loop CLOSE: call print db cr,lf,' Done.',0 LD DE,IOCTL1 ; set for output CALL FXO$CLOSE ; close output file RETURN: call print db cr,lf,0 LD SP,(OLDSTK) ; get system stack back RET ; and program ends here ; convert ZDK file to ZDT ZDK: LD DE,IOCTL0 ; set for input CALL FX$GET ; waste two-byte version ID (0250h) CALL FX$GET ; ..to get to length-byte of first string CALL FX$GET ; we have byte count in A ; initialize both byte (string length) and string count buffers LD (BCNTR),A ; store it to byte counter buffer LD A,10 ; initialize string counter for ten strings LD (SCNTR),A ; write it to string counter buffer ; start new string to convert ZDK->ZDT NEWSTR: CALL INPUT ; read in first byte and count down string CP 80H ; is it an option (N or Q)? JR C,GETCHRK2 ; if not, skip option processing SUB 80H ; if it is, reset high bit LD (BYTBUF),A ; write character to buffer CALL INPUT ; get second byte CP 80H ; is it a Q option? JR C,NOPTION ; if not, skip QOPTION: SUB 80H ; reset high bit LD (BYTBUF+1),A ; write it to buffer LD A,'q' ; get a Q ready JR NOPTION1 NOPTION: LD (BYTBUF+1),A ; write character to buffer LD A,'n' ; get an N ready NOPTION1: LD DE,IOCTL1 CALL FX$PUT ; write it to output GETCHRK: LD A,(BYTBUF) ; get first character into A CP 20H ; is it a control character? CALL C,CTRLK ; convert it to ASCII '^c' CALL LITCHK0 ; is it a literal character? LD DE,IOCTL1 ; set for output CALL FX$PUT ; write the byte LD A,(BYTBUF+1) ; get second character GETCHRK2: CP 20H ; is it a control character (Q option)? CALL C,CTRLK ; convert it to ASCII '^c' CALL LITCHK ; check for / or ^ LD DE,IOCTL1 ; set for output CALL FX$PUT ; write it and fall through GETCHRK1: CALL INPUT ; get new character from string JR GETCHRK2 ; and go back for another ; convert real control to ASCII '^c' CTRLK: PUSH AF ; put pending control character aside LD A,'^' ; write prefix out in ASCII LD DE,IOCTL1 ; set for output CALL FX$PUT ; write the prefix POP AF ; get control character back ADD A,'@' ; make it normal printable character RET ; check for literal character as first character in two-byte buffer LITCHK0: CP '/' ; is it a slash? JR Z,ADDLIT0 ; add a slash CP '^' ; is it a caret? JR Z,ADDLIT0 ; add a slash RET ADDLIT0: CALL ADDLIT CALL FX$PUT ; and write it to output file LD A,(BYTBUF+1) ; get second character from two-byte buffer CALL FX$PUT ; and write it out to output file JR GETCHRK1 ; carry on with string processing ; general check for characters requiring literal prefix slash LITCHK: CP '/' ; is it a slash? JR Z,ADDLIT ; add a slash CP '^' ; is it a caret? JR Z,ADDLIT ; add a slash RET ; if neither, return to processing ADDLIT: PUSH AF ; put input character aside LD A,'/' ; get a slash ready LD DE,IOCTL1 ; set for output CALL FX$PUT ; write slash to output file POP AF ; get input character back RET ; output to WORK1 buffer (output file) and increment string-length byte count OUTPUT: LD DE,IOCTL1 ; set for output CALL FX$PUT ; write to work buffer DEC BC ; count down one LD A,B ; check B against C for both set 0 OR C JP Z,SIZERR ; if we're over the limit, quit LD A,(BCNTR) ; get current byte count from buffer INC A ; increment it CP 128+1 ; string limit 127 + 1 length byte JP NC,LENERR ; quit if string is over the limit LD (BCNTR),A ; and store it back to buffer RET ; input from WORK0 buffer (input file) INPUT: LD HL,BCNTR ; get current count from buffer DEC (HL) ; count it down one LD A,(HL) ; put it in accumulator for comparison CP 0FFH ; one less than zero allows for length byte JR Z,ENDSTR ; if it is, handle end of string LD DE,IOCTL0 ; else, set for input CALL FX$GET ; get next byte RET ENDSTR: LD A,0DH ; end string with LD DE,IOCTL1 ; set for output CALL FX$PUT ; write byte to output file ld a,0ah call fx$put ; Add ; update string counter or quit LD A,(SCNTR) ; get string count DEC A ; count it down one JP Z,CLOSE ; write 1Ah and close files LD (SCNTR),A ; if not done, write new count back to buffer LD DE,IOCTL0 ; set for input CALL FX$GET ; this byte is byte count for next string LD (BCNTR),A ; reinitialize byte counter JP NEWSTR ; ..and start new string ; update pointer to last string-length byte to be inserted SETPTR: OR A ; clear carry LD HL,BUFTOP ; get top of buffer in HL SBC HL,BC ; subtract counter to get pointer LD (PNTR),HL ; ..and store it to memory RET ; print routine PRINT: EX (SP),HL ; get address CALL PRINT1 ; print it EX (SP),HL RET PRINT1: LD A,(HL) INC HL OR A RET Z CALL CONOUT ; print character JR PRINT1 CONOUT: PUSH HL PUSH DE PUSH BC PUSH AF LD E,A LD C,2 CALL 5 POP AF POP BC POP DE POP HL RET ; error messages TYPERR: CALL PRINT DB ' File type must be ZDT or ZDK.',CR,LF,lf DB 0 JP HELP NOFILE: CALL PRINT DB bell,cr,lf,' Input file not found.' DB 0 JP RETURN NODIR: CALL PRINT DB bell,cr,lf,' No directory space for new file.' DB 0 JP RETURN LENERR: CALL PRINT DB bell,cr,lf,' Key macro string exceeds 127 characters.' DB 0 JP RETURN SIZERR: CALL PRINT DB bell,cr,lf,' Key macros exceed 498 characters.' DB 0 JP RETURN ; working i/o control blocks, i/o FCB's, buffers, and data storage IOCTL0: DB 4 ; use 4 128-byte pages DS 1 ; filled in by FXIO DS 2 ; filled in by FXIO DS 2 ; filled in by FXIO DW WORK0 ; address of working buffer IOFCB0: DS 1 ; filled in by FXIO to 0 DB '12345678' ; file name DB 'ZDT' ; file type FLAG EQU $-1 ; flag T or K shows sense of operation DS 24 ; filled in by FXIO IOCTL1: DB 4 DS 1 DS 2 DS 2 DW WORK1 IOFCB1: DS 1 DB '12345678' ; file name DB 'ZDK' ; file type DS 24 ; filled in by FXIO ; uninitialized data DSEG WORK0: DS 128*4 ; working input buffer 0.5K WORK1: DS 128*4 ; working output buffer 0.5K BUFTOP EQU $ ; top of buffer PNTR: DS 2 ; pointer to string-length byte in WORK1 buffer BCNTR: DS 1 ; byte-length of string SCNTR: DS 1 ; number of strings processed BYTBUF: DS 2 ; two-byte buffer for option characters OLDSTK: DS 2 ; store system stack pointer END