TITLE PROM BURNING UTILITY ;******************************************************** ; ; Hard Disk Monitor Program ; ; Copyright (c) 1983 GRH Enterprisess, Cupertino, CA ; by Girvin Herr ; ;******************************************************** ; ; 1- Implements the loading of object files ; in the Intel HEX format into the memory buffer. ; 3- Extensive error checking against memory buffer bounds, will not allow ; operating system or code to be compromised. ; ; Uses the following commands: ; ;*HElp ; Displays the command summary list. ;*REad addr filename ; Reads file 'filename' with optional 'HEX' format starting at buffer ; relative location 'addr'. Buffer bounds are checked. ;*SAve first, last HEX filename ; Saves the buffer image starting at buffer relative location 'first' ; through 'last' in Hex file format as file 'filename' ;*FIll first, last, value ; Fills buffer relative locations from 'first' to 'last' (inclusive) ; with 'value'. Buffer bounds are checked. ;*MOve first, last, dest ; Moves code from buffer relative position 'first' through 'last' ; into new buffer relative location starting at 'dest'. Buffer ; bounds for 'dest' are checked. ;*COpy count <,dest <,skip>> ; Copy 'count' bytes from prom starting at buffer relative location ; ',dest' (if 'dest' is not specified then a default of 0 is used). ; If 'dest' is specified, then the optional skip count specifies the ; number of buffer bytes to skip between each buffer load. ;*DIsplay first <,last> ; Display buffer data from buffer relative location 'first' through ; buffer relative location 'last'. If 'last' is not specified then ; 256 bytes are displayed. If 'first' is not specified, then the ; previous 'last' is used for 'first'. ;*Substitute addr ; Allows changing byte values starting at buffer relative location ; 'addr' one at a time. Upon execution, this command outputs: ; 'addr' & the current data value then waits for the user response. ; If the user enters hex data, the 2 Hex digits are loaded into ; the 'addr' location then 'addr' is incremented by 1 & the process ; is repeated. If a RETURN is entered (nul line) the data value is ; left as is & 'addr' is incremented by 1 & the process is repeated. ; If a period '.' is entered as the 1st character, the command is ; terminated. Note, the Hex values entered must have 0 in the high ; byte. I.E. 00xx or just plain xx or even yy00xx will be ok. ; ;********************************************************* ; ; REVISION STATUS: ; X.0 - 6 Jul 83 GRH ; Test release ; REVSN: EQU 'X0' ; ;********************************************************* SUBTTL DECLARATIONS FALSE EQU 0 TRUE EQU NOT FALSE LISTINC EQU FALSE ; ; SYSTEM PARAMS ; SYSTEM EQU 0000H DFLTDK: EQU 0004H ;DEFAULT DISK STORAGE LOC BDOS: EQU 0005H ;CP/M ENTRY POINT DEFBFR: EQU 0080H ;DEFAULT BUFFER LOC TPA EQU 0100H ;START OF PROGRAM ; CPMEOF: EQU 1AH ;CP/M END OF FILE CHAR (CTRL-Z) RDCF: EQU 10 OPENF: EQU 15 RDF: EQU 20 ; ; ASCII CHARACTERS ; LF EQU 0AH CR EQU 0DH ; ; CONSTANTS ; STKSIZ: EQU 64 ;STACK SIZE SECSIZ: EQU 128 ;DISK SECTOR SIZE TYPE: EQU 8 ;OFFSET LOCATION OF TYPE EXTENSION OF FILENMAE SUBTTL PROGRAM AREA ORG TPA ; ; ENTRY VECTOR ; JP PROM ;ENTRY VECTOR FROM SYSTEM ; ; PTR TO END OF PROGRAM (START OF PROM OVERLAY) ; $MEMRY DW PGMEND ; ; SIGN ON MESSAGE HERE TO GIVE COPYRIGHT VISIBILITY ; SIGNON: DB CR,LF,'Hard Disk Monitor Utility Ver ' DB HIGH REVSN DB '.' DB LOW REVSN DB CR,LF,'Copyright (c) 1983' DB ' GRH Enterprises Cupertino, CA' DB CR,LF,'$' ; ; MAIN PROGRAM ; PROM: LD SP,STACK ;INITIALIZE LD HL,(BDOS+1) ;GET LAST MEMORY LOC. DEC HL LD (MEMTOP),HL LD DE,SIGNON ;OUTPUT SIGN-ON MSG CALL EDITOR ; ; COMMAND INPUT ENTRY. ALL COMMANDS RETURN HERE IF OK. ; CONT1: CALL CRLF ;NEW LINE LD C,'*' ;OUTPUT PROMPT CALL CONOUT CALL RDCON ;GET USER INPUT LD HL,(CMDPTR) CALL SKIP ;IF NO INPUT THEN ERR JR Z,CONT1 LD (CMDPTR),HL ;UPDATE PTR EX DE,HL ;DE := COMMAND PTR LD HL,CMDTBL NXTCMD: LD B,(HL) ;B := CMD CHAR CNT LD A,B ;IF COUNT == 0 THEN DONE OR A JR Z,CMDERR CMDCHK: INC HL ;IF CHARS NOT MATCH THEN EXIT LD A,(DE) CP (HL) JR NZ,NOT1ST INC DE ;NEXT CHAR DJNZ CMDCHK ;IF COUNT EXHAUSTED THEN MUST BE COMMAND INC HL ;FETCH SERVICE ADDR LD E,(HL) INC HL LD D,(HL) PUSH DE ;SET UP FOR COMMAND SCANNING LD HL,(CMDPTR) ;SET UP OPERANDS FOR SOME COMMANDS LD C,' ' CALL SEARCH CALL SKIP ;ALSO PASS IN ZF IF NO FIELDS LD (CMDPTR),HL LD HL,CONT1 ;SET UP RETURN ADDR EX (SP),HL ;EXECUTE COMMAND JP (HL) ; NOT1ST: INC HL ;WASTE ENTRY DJNZ NOT1ST INC HL ;SKIP ADDRESS INC HL LD DE,(CMDPTR) ;RESTORE CMD PTR TO START OF CMD JR NXTCMD ;IF NOT LAST ENTRY THEN LOOP ; COMMAND NOT FOUND IN TABLE CMDERR: LD DE,CMDERM ;ELSE OUTPUT ERROR MESSAGE CALL EDITOR JR CONT1 ;TRY NEXT COMMAND SYNERR: POP DE ;WASTE RETURN ADDR FOR SUBRS LD DE,SYNERM CALL EDITOR JR CONT1 SUBTTL COMMANDS ;********************************** ; ; READ FILE INTO BUFFER COMMAND ; READ ADDR FILENAME ; ;********************************** READC: JP Z,SYNERR ;IF NO OPERAND THEN ERR LD HL,(CMDPTR) ;GET ADDR CALL GETNUM JP C,SYNERR ;IF ILLEGAL NUMBER THEN ERR CALL CPBNDS ;IF READ AREA INTO RESERVED AREA THEN JP C,BNDSM ;ABORT LD (PARAM1),HL LD HL,(CMDPTR) ;GET FILENAME LD C,' ' CALL SEARCH CALL SKIP LD (CMDPTR),HL LD HL,(CMDPTR) ;SEEK FILENAME LD C,' ' CALL SEARCH LD (CMDPTR),HL ; ; WE SHOULD NOW BE AT THE FILENAME ; RC1: LD HL,FCB ;FORMAT FCB CALL FORMAT JP C,SYNERR JP NZ,SYNERR ;IF WILDCARDS USED THEN ERR ; ; FORMAT OK, SO OPEN FILE ; LD DE,FCB CALL OPEN INC A ;IF NOT THERE THEN JR NZ,RDC1 LD DE,FNFM ; OUTPUT 'FILE NOT FOUND' JP EDITOR ; ; OUTPUT BOUNDS ERROR MESSAGE ; BNDSM: LD DE,BOUNDM ;THEN OUTPUT ERROR MESSAGE JP EDITOR MEMER1: LD DE,MEMERM CALL EDITOR JP CONT1 ; ; HEX FILE READ ROUTINE ; RDC1: RDC2: XOR A ;SET UP BUFFER FOR 1ST READ LD (BFRCNT),A LD HL,(PARAM1) ;SET PTR TO LOAD ADDR LD (BFRPTR),HL HEXRD1: CALL GETCH ;FETCH CHARACTER OF FILE JP C,HXEMPT ;IF FILE EMPTY THEN ERR CP ':' ;IF CHAR <> COLON THEN IGNORE JR NZ,HEXRD1 LD D,0 ;CHECKSUM = 0 CALL BYTE ;IF FILE EMPTY THEN ERR JP C,HXERR AND A ;IF RECORD LENTH == 0 THEN DONE JP Z,CONT1 LD E,A ;RECORD COUNT = BYTE CALL BYTE ;GET LOAD ADDRESS OF 1ST RECORD & USE AS BASE JP C,HXERR LD H,A ;HIGH BYTE OF ADDR CALL BYTE JP C,HXERR LD L,A ;LOW BYTE OF ADDR LD (HEXBASE),HL ;SAVE BASE ADDRESS LD HL,(BFRPTR) ;SET LOAD PTR TO START OF BUFFER LD (HEXPTR),HL LD C,E ;IF LOAD_PTR + COUNT <= LAST THEN GET DATA LD B,0 ADD HL,BC LD BC,(MEMTOP) INC BC OR A SBC HL,BC JR C,GETREC JR MEMER1 ;ELSE OUTPUT MEMORY ERROR ; HEXRD: CALL GETCH ;GET CHAR OF FILE JR C,HXERR CP ':' ;IF CHAR <> COLON THEN IGNORE JR NZ,HEXRD LD D,0 ;CHECKSUM = 0 CALL BYTE JR C,HXERR ;FILE EMPTY AND A ;IF RECORD LENGTH = 0 THEN DONE JP Z,CONT1 LD E,A ;RECORD COUNT = BYTE CALL BYTE ;GET LOAD ADDRESS JR C,HXERR LD H,A CALL BYTE JR C,HXERR LD L,A LD BC,(HEXBASE) ;IF NEW ADDR < BASE ADDR THEN ERR SBC HL,BC JR C,HXADERR LD BC,(BFRPTR) ;ELSE COMPUTE NEW PTR ADD HL,BC LD (HEXPTR),HL LD C,E ;IF PTR + COUNT > BFR_TOP THEN ERR LD B,0 ADD HL,BC LD BC,(MEMTOP) INC BC OR A SBC HL,BC JP NC,MEMER1 ; GETREC: CALL BYTE ;GET RECORD TYPE JR C,HXERR ;FILE EMPTY OR A ;IF RECORD TYPE <> 0 THEN ERROR JR NZ,HXTYPER LD HL,(HEXPTR) ;RESTORE PTR LOOP: CALL BYTE ;INPUT DATA JR C,HXERR LD (HL),A INC HL DEC E ;IF (COUNT = COUNT -1) =<> 0 THEN LOOP JR NZ,LOOP CALL BYTE ;CHECK CHECKSUM JR C,HXERR XOR A ADD D JP Z,HEXRD HXERR: LD DE,HXERM ;IF ERROR THEN TELL SO JP EDITOR HXADERR: LD DE,HXADRM ;OUTPUT ILLEGAL LOAD ADDRESS REGRESSION JP EDITOR HXTYPER: LD DE,HXTYPM ;OUTPUT ILLEGAL HEX RECORD TYPE JP EDITOR HXEMPT: LD DE,HXEMTM ;OUTPUT FILE EMPTY JP EDITOR ;******************************* ; ; SAVE FILE COMMAND ; SAVE FIRST, LAST HEX FILENAME ; ;******************************* SAVEC: JP Z,SYNERR ;IF NO OPERANDS THEN ERR LD DE,PARAM1 ;GET 1ST & LAST LD HL,(CMDPTR) CALL GET2PAR JP C,SYNERR LD HL,(PARAM2) ;COUNT = LAST - FIRST + 1 LD DE,(PARAM1) OR A SBC HL,DE JP C,SYNERR ;IF FIRST > LAST THEN ERR INC HL LD (PARAM3),HL ;SAVE COUNT IN P3 EX DE,HL ;OFFSET FIRST FOR PTR CALL CPBNDS LD (PARAM1),HL LD HL,(CMDPTR) LD C,' ' ;POINT TO FILENAME CALL SEARCH CALL SKIP LD (CMDPTR),HL JP Z,SYNERR LD HL,FCB ;FORMAT FCB CALL FORMAT JP C,SYNERR JP NZ,SYNERR LD DE,FCB ;OPEN FILE, IF EXISTING THEN PROMPT FOR DELETE CALL OPEN INC A JR Z,WRC1 LD DE,DELMSG CALL EDITOR CALL CONIN ;IF 'Y' OR 'y' THEN DELETE AND 5FH ;CONVERT TO LOWER CASE CP 'Y' RET NZ ;ELSE RETURN TO COMMAND MODE WRC2: LD DE,FCB ;DELETE FILE 1ST LD C,19 CALL BDOS WRC1: LD DE,FCB ;MAKE NEW FILE LD C,22 CALL BDOS ; ; MAIN LOOP OF HEX OUTPUT ROUTINE ; LD A,0 ;CLEAR WRITE BUFFER LD (BFRCNT),A LD HL,DEFBFR LD (DBPTR),HL LD HL,(PARAM1) ;INIT START ADDR WR0: LD C,':' ;OUTPUT START OF RECORD SYMBOL CALL PCHAR JP C,WERR LD BC,16 PUSH HL ;SAVE START ADDR WR1: LD HL,(PARAM3) ;IF COUNT - 16 >= 0 THEN OR A LD A,L ;SAVE COUNT FOR RESTORING SBC HL,BC LD B,C ; REC_COUNT = 16 LD (PARAM3),HL ; COUNT -= 16 JR NC,WR2 ; ELSE REC_COUNT = COUNT LD B,A LD HL,0 ; COUNT = 0 FOR LAST RECORD LD (PARAM3),HL WR2: POP HL ;RESTORE START PTR LD D,0 ;CHECKSUM = 0 LD A,B ;OUTPUT RECORD LENGTH OR A ;IF COUNT = 0 THEN SKIP TO EOF JR Z,WR5 CALL PBYTE JR C,WERR PUSH HL ;SAVE PTR PUSH DE ;SAVE CS LD DE,($MEMRY) ;REMOVE OFFSET FOR OUTPUT OR A SBC HL,DE POP DE ;CS LD A,H ;OUTPUT LOAD ADDR CALL PBYTE JR C,WERRP LD A,L CALL PBYTE POP HL ;PTR JR C,WERR XOR A ;OUTPUT 0 RECORD TYPE CALL PBYTE JR C,WERR WR3: LD A,(HL) ;OUTPUT BYTE FROM MEMORY CALL PBYTE JR C,WERR INC HL ;PTR += 1 DJNZ WR3 ;NEXT BYTE XOR A ;OUTPUT -CHECKSUM SUB A,D CALL PBYTE JR C,WERR LD C,CR ;OUTPUT CR-LF CALL PCHAR JR C,WERR LD C,LF CALL PCHAR JR C,WERR PUSH HL LD HL,(PARAM3) ;IF COUNT = 0 THEN DONE LD A,L OR H POP HL JP NZ,WR0 ;ELSE DO ANOTHER LINE LD C,':' ;OUTPUT END OF FILE RECORD CALL PCHAR JR C,WERR WR5: LD B,5 ;OUTPUT 5 00 BYTES WR4: XOR A CALL PBYTE JR C,WERR DJNZ WR4 CALL CLOSE JR C,WERR RET ;NORMAL EXIT WERRP: POP HL ;BALANCE STACK WERR: LD DE,WRERRM ;OUTPUT WRITE ERROR JP EDITOR ;************************* ; ; FILL COMMAND ; FILL FIRST, LAST, VALUE ; ;************************* FILLC: JP Z,SYNERR ;IF NO OPERANDS THEN ERR CALL GET3PAR JP C,SYNERR CALL CKBNDS JP C,BNDSM LD DE,(PARAM1) ;FIRST LD HL,(PARAM2) ;LAST SBC HL,DE ;IF FIRST > LAST THEN ERR JP C,SYNERR PUSH HL ;ELSE COUNT = LAST - FIRST POP BC LD A,C ;IF CNT = 0 THEN ERR OR B JP Z,SYNERR LD A,(PARAM3) ;GET VALUE LD HL,(PARAM1) LD (HL),A ;SET FIRST LOCATION PUSH HL ;DESTINATION = SOURCE + 1 POP DE INC DE LDIR ;FILL RET ;************************ ; ; MOVE COMMAND ; MOVE FIRST, LAST, DEST ; ;************************ MOVEC: CALL GET3PAR JP C,SYNERR LD HL,(PARAM3) ;IF DESTINATION OUT OF BOUNDS THEN ERR CALL CPBNDS JP C,BNDSM LD (PARAM3),HL LD HL,(PARAM1) ;ADD BUFFER BIAS TO VALUES CALL CPBNDS LD (PARAM1),HL LD HL,(PARAM2) CALL CPBNDS LD (PARAM2),HL LD DE,(PARAM1) ;IF FIRST > LAST THEN ERR OR A SBC HL,DE JP C,SYNERR PUSH HL ;ELSE CNT = # OF BYTES POP BC EX DE,HL ;IF DEST > FIRST THEN LD DE,(PARAM3) SBC HL,DE JR C,SPLIT ;TOP DOWN INC BC ;ELSE BOTTOM UP LD HL,(PARAM1) LD A,C ;IF COUNT = 0 THEN ERR OR B JP Z,SYNERR LDIR RET ; SPLIT: LD HL,(PARAM3) ;DEST = DEST + CNT ADD HL,BC EX DE,HL LD HL,(PARAM2) INC BC ;INCLUSIVE LD A,C OR B JP Z,SYNERR LDDR RET ;************************ ; ; HELP COMMAND ; HELP ; ;************************ HELPC: LD DE,HELPM JP EDITOR ;***************************** ; ; DISPLAY MEMORY COMMAND ; DISPLAY <,LAST> ; ;***************************** DISPC: LD HL,(CMDPTR) JR Z,DISP1 ;IF NO OPERANDS THEN USE DEFAULTS LD A,(HL) ;IF 1ST CHAR = ',' THEN USE DEFAULT CP ',' JR Z,DISP1 CALL GETNUM ;ELSE GET PARAMETER 1 JP C,SYNERR CALL CPBNDS ;ADD OFFSET LD (DISTRT),HL DISP1: LD HL,(CMDPTR) ;IF NO ,NNNN THEN END= START + 12 LINES LD A,',' CP (HL) JR Z,DISP2 LD C,A CALL SEARCH JR NZ,DISP2 LD HL,(DISTRT) ;ELSE END = START MOD 16 + 255 LD A,L AND 0F0H LD L,A LD DE,255 ADD HL,DE JR DISP3 ; DISP2: INC HL ;GET VALUE OF END CALL SKIP JP Z,SYNERR LD (CMDPTR),HL CALL GETNUM JP C,SYNERR CALL CPBNDS ;ADD OFFSET TOWARD BUFFER DISP3: LD (DISEND),HL DISP4: CALL CRLF ;NEW LINE CALL CSTS ;IF KEY PRESSED THEN ABORT RET NZ LD HL,(DISTRT) ;TEMP = START LD (DISTMP),HL LD DE,($MEMRY) ;OUTPUT OFFSET ADDR SBC HL,DE CALL PRT2H LD HL,(DISTRT) ;RESTORE PTR DISP5: PUSH HL CALL SPACE POP HL LD A,(HL) ;PRINT DATA INC HL PUSH HL CALL PRTHEX POP HL CALL CKDONE ;IF PTR = END THEN DONE JR C,DISP6 LD A,L AND 0FH JR NZ,DISP5 DISP6: LD (DISTRT),HL ;UPDATE TO NEW START CALL SPACE LD HL,(DISTMP) ;START OVER DISP7: LD A,(HL) ;IF CHAR = PRINTING THEN OK AS IS CP 7FH JR NC,OUTDEC CP ' ' JR NC,DISPOK OUTDEC: LD A,'.' ;ELSE OUTPUT '.' DISPOK: PUSH HL LD C,A CALL CONOUT POP HL INC HL ;PTR = PTR + 1 LD DE,(DISTRT) ;IF PTR = NEXT START THEN DONE LD A,E SUB L JR NZ,DISP7 LD A,D SUB H JR NZ,DISP7 EX DE,HL CALL CKDONE ;IF NOT DONE THEN DO NEXT LINE RET C JR DISP4 ;****************************** ; ; SUBSTITUTE MEMORY COMMAND ;SUBSTITUTE ADDR ; ;****************************** SUBSC: JP Z,SYNERR ;IF NO OPERANDS THEN ERR LD HL,(CMDPTR) CALL GETNUM JP C,SYNERR CALL CPBNDS ;IF ADDR OUT OF BOUNDS THEN ERR JP C,BNDSM SUBS2: LD (DISTMP),HL ;SAVE ADDR PUSH HL ;OUTPUT ADDR CALL CRLF POP HL PUSH HL OR A ;ADD OFFSET LD DE,($MEMRY) SBC HL,DE CALL PRT2H CALL SPACE ;OUTPUT SPACE POP DE LD A,(DE) ;OUTPUT CURRENT DATA CALL PRTHEX CALL SPACE ;OUTPUT ANOTHER SPACE CALL RDCON ;GET USER INPUT LD HL,(CMDPTR) ;IF NULL LINE THEN SKIP LOCATION CALL SKIP LD DE,(DISTMP) JR Z,SUBS1 LD A,(HL) ;IF CHAR = '.' THEN ABORT CP '.' RET Z CALL GETNUM ;ELSE CONVERT TO NUMBER LD A,H ;IF NUMBER > 255 THEN ERR OR A JP NZ,SYNERR LD A,L ;LOAD DATA INTO LOCATION LD DE,(DISTMP) LD (DE),A SUBS1: INC DE ;NEXT LOCATION EX DE,HL JR SUBS2 SUBTTL SUBROUTINES ;******************************************************* ; ; OUTPUT MEMORY ERROR & RETURN TO SYSTEM ; EXIT - FLAGS SAVED FOR CALLER'S CONDITIONAL ; ;******************************************************* MEMER: PUSH AF LD DE,MEMERM CALL EDITOR POP AF RET ;****************************************************** ; ; BYTE SUBR CONVERTS 2 ASCII-HEX CHARS TO BINARY ; ENTRY- D= CHECKSUM ; EXIT - A= BYTE FROM ASCII-HEX ; D= NEW CHECKSUM ; CF= ERROR ; ;****************************************************** BYTE: CALL GETCH ;GET 1ST DIGIT RET C CALL ASC2HX ;TRY TO CONVERT BUT IF DIGIT NOT HEX THEN ERR JR C,CVERR SLA A ;MOVE OVER 1 NIBBLE SLA A SLA A SLA A LD B,A CALL GETCH ;GET 2ND DIGIT RET C CALL ASC2HX ;CONVERT 2ND NIBBLE JR C,CVERR ADD B ;ADD 2ND TO 1ST LD B,A ;COMPUTE CHECKSUM ADD D LD D,A LD A,B OR A ;CLEAR FLAG RET ; HEX CONVERSION ERROR MESSAGE OUTPUT CVERR: LD DE,CVERM CALL EDITOR SCF ;ALSO RETURN ERROR FLAG RET ;************************************************ ; ; CONVERT ASCII-HEX CHAR TO BINARY SUBR ; ENTRY- A= CHAR ; EXIT - A= BINARY VALUE ; CF= ERROR ; ;************************************************ ASC2HX: SUB '0' ;REMOVE ASCII BIAS RET C CP 10 ;IF 0..9 THEN OK CCF RET NC SUB 7 ;CONVERT 'A'..'F' TO 10..15 CP 10 RET C CP 16 CCF RET ;*************************************************************************** ; ; GET CHARACTER SUBR RETURNS NEXT CHAR OF INPUT FILE ; ENTRY- FCB FORMATTED & FILE OPENED, (BFRCNT INIT'D FOR NEW FILE) ; EXIT - CHAR FROM FILE IN A ; CF = EOF ; ;*************************************************************************** GETCH: PUSH HL LD A,(BFRCNT) ;IF BUFFER EMPTY THEN READ OR A JP NZ,GC1 PUSH DE ;READ SECTOR INTO DEFAULT BUFFER PUSH BC LD DE,DEFBFR CALL SETDMA LD DE,FCB CALL RDSEC OR A ;IF FILE EMPTY THEN RETURN CF SCF JR NZ,FEMPTY LD HL,DEFBFR LD (DBPTR),HL ;SET VARS LD A,SECSIZ LD (BFRCNT),A POP BC POP DE GC1: LD HL,(DBPTR) ;GET CHAR FROM BFR LD A,(HL) INC HL LD (DBPTR),HL LD HL,BFRCNT ;COUNT = COUNT - 1 DEC (HL) POP HL OR A ;INSURE NC RET ; FEMPTY: POP BC ;RESTORE STACK POP DE POP HL RET ;*************************************************************************** ; ; PUT CHAR ROUTINE OUTPUTS CHAR TO OUTPUT BUFFER & WRITES TO OUTPUT ; FILE WHEN FULL. BUFFER IS DEFAULT BUFFER AT 80H ; ENTRY- C= CHAR TO OUTPUT ; FCB= OPENED FILE ; EXIT - CF= ERROR ; ;*************************************************************************** PCHAR: PUSH HL ;SAVE REGS PUSH DE PUSH BC LD HL,(DBPTR) ;GET BUFFER PTR LD (HL),C ;ADD CHAR TO BUFFER INC HL ;PTR += 1 LD (DBPTR),HL LD A,(BFRCNT) ;IF BUFFER FULL THEN WRITE IT INC A LD (BFRCNT),A CP SECSIZ CCF ;SWAP FLAG SENSE TO RETURN NC JP NC,NOWRT CLOS1: LD DE,DEFBFR ;SET WRITE PTR LD (DBPTR),DE CALL SETDMA LD DE,FCB ;WRITE IT LD C,21 CALL BDOS OR A ;IF WRITE ERR THEN RETURN CF JR NZ,WRERR LD (BFRCNT),A ;ELSE BUFFER COUNT = 0 NOWRT: POP BC POP DE POP HL RET WRERR: SCF JR NOWRT ;************************************ ; ; CLOSE OUTPUT BUFFER SUBR ; EXIT - CF= ERROR ; ;************************************ CLOSE: LD C,CPMEOF ;OUTPUT EOF CHAR CALL PCHAR RET C LD A,(BFRCNT) ;IF BUFFER EMPTY THEN NO NEED TO WRITE OR A JR Z,CLOSRET PUSH HL ;PUT NEW RETURN ADDRESS ON STACK LD HL,CLOSRET EX (SP),HL PUSH HL ;THEN SAVE REGS FOR STACK BALANCE PUSH DE PUSH BC JR CLOS1 ;WRITE REMAINDER OF BUFFER CLOSRET: RET C ;IF WRITE ERROR THEN RETURN CF LD DE,FCB ;THEN CLOSE FILE LD C,16 CALL BDOS OR A ;CLEAR POSSIBLE CARRY INC A ;IF NO ERROR THEN RETURN NC RET NZ SCF ;ELSE RETURN CF RET ;*************************************************************************** ; ; PUT BYTE SUBR CONVERTS BYTE TO ASCII-HEX & OUTPUTS IT TO FILE BUFFER ; ENTRY- A= BYTE ; EXIT - CF= WRITE ERROR ; ;*************************************************************************** PBYTE: PUSH BC LD B,A ;SAVE BYTE IN B ADD D ;ADD TO CHECKSUM LD D,A LD A,B ;RESTORE BYTE SRL A ;DO HIGH NIBBLE 1ST SRL A SRL A SRL A CALL HX2ASC ;CONVERT IT TO ASCII LD C,A ;THEN OUTPUT IT CALL PCHAR JR C,PBYT1 LD A,B ;THEN DO LO NIBBLE CALL HX2ASC LD C,A ;& OUTPUT IT CALL PCHAR PBYT1: POP BC RET ;******************************************** ; ; OUTPUT A SPACE TO CONSOLE SUBR ; EXIT - ALL REGS ? ; ;******************************************** SPACE: LD C,' ' ;******************************************** ; ; CONSOLE OUTPUT SUBR ; ENTRY- C= CHAR TO OUTPUT ; EXIT - ALL REGS ? ; ;******************************************** CONOUT: LD E,C ;SETUP LD C,2 JP BDOS ;************************************* ; ; CONSOLE INPUT SUBR ; EXIT - A= CHAR ; ALL OTHER REGS ? ; ;************************************ CONIN: LD C,1 JP BDOS ;************************************************************ ; ; CONSOLE STATUS SUBR ; EXIT - A= 0 : NONE READY, A= NOT 0 : CHAR READY ; ZF = REFLECTS CONDITION OF A ; ;************************************************************ CSTS: LD C,11 CALL BDOS OR A RET ;********************************************************************* ; ; SEARCH SUBR SEARCHES A NUL TERMINATED STRING FOR A CHAR THE SAME ; AS CONTENTS OF C. ; ENTRY- HL= STRING PTR ; C= CHAR TO SEARCH FOR ; EXIT - HL= CHAR PTR ; Z= NOT FOUND (END OF TEXT) ; ;********************************************************************* SEARCH: LD A,(HL) ;IF CHAR = NUL THEN RETURN Z OR A RET Z CP C ;ELSE IF CHAR=C THEN RETURN NZ JR Z,FOUND INC HL ;ELSE TRY NEXT CHAR JR SEARCH FOUND: OR A RET ;*********************************************************************** ; ; SKIP BYPASSES TRAILING SPACES UNTIL NON-SPACE CHAR IS FOUND ; ENTRY- HL= STRING PTR ; EXIT - HL= 1ST NON-SPACE CHAR PTR ; A= NON-SPACE CHAR ; ZF= NOT FOUND ; ;*********************************************************************** SKIP: LD A,(HL) CP ' ' ;IF CHAR <> ' ' THEN RETURN JR NZ,FOUND INC HL JR SKIP ;************************************* ; ; CARRIAGE RETURN/LINE FEED SUBR ; EXIT - ALL REGS ? ; ;************************************* CRLF: LD C,CR CALL CONOUT LD C,LF JP CONOUT ;******************************************************* ; ; MESSAGE EDITOR SUBR OUTPUTS TEXT STRING ; ; ENTRY- DE= STRING PTR ; EXIT - ALL REGS ? ; ;****************************************************** EDITOR: LD C,9 JP BDOS ;*************************************************** ; ; CP/M FILE CONTROL BLOCK FORMATTER ; ENTRY- (CMDPTR) = ASCII STRING PTR OF FILE ; HL = FCB TO FORMAT ; EXIT - ZF = NO WILDCARDS USED ; NZ = WILDCARD(S) USED, A REG. = NUMBER OF ; WILDCARDS ; ;********************************************************* ; ; CONSTANTS ; ZROSIZ: EQU 22 NAMCNT: EQU 8 ;FILENAME CHAR CNT TYPCNT: EQU 3 ;FILE EXTENSION CHAR CNT ; FORMAT: PUSH HL ;SAVE FPB PTR IN IY POP IY LD HL,(CMDPTR) CALL SKIP ;SKIP LEADING SPACES LD (CMDPTR),HL ;SAVE NEW PTR EX DE,HL PUSH IY POP HL ;FPB PTR LD A,(DE) ;IF CHAR = 0 THEN DONE OR A JR Z,L89 SBC 'A' - 1 ;MAKE 'A'-'P' = 1-15 FOR DRIVE # LD B,A INC DE ;IF NEXT CHAR = ':' THEN MUST BE DRIVE LD A,(DE) CP ':' JR Z,L90 DEC DE ;ELSE BACK UP & USE DEFAULT DRIVE L89: LD A,(DFLTDK) LD (HL),A ;FCB(DISK) := DRIVE # JR L96 L90: LD A,B ;USE SPECIFIED DISK LD (HL),B INC DE ;SKIP OVER ':' L96: LD B,NAMCNT ;B := MAX CHARS L98: CALL RSVP JR Z,LB9 INC HL ;PTR := PTR +1 CP '*' ;IF WILDCARD THEN ENTER '?' JR NZ,LA9 LD (HL),'?' JR LAB LA9: LD (HL),A ;ADD CHAR TO FCB FILENAME INC DE LAB: DJNZ L98 ;IF NOT DONE THEN LOOP LAF: CALL RSVP ;IF RESERVED CHAR THEN EXIT JR Z,LC0 INC DE ;SKIP OVER EXTRA CHARS TO '.' JR LAF LB9: INC HL ;FILL REMAINDER OF FCB WITH SPACES LD (HL),' ' DJNZ LB9 LC0: LD B,TYPCNT ;COUNT := TYPE CHAR COUNT CP '.' ;IF RESERVED CHAR NOT '.' THEN FILL ; WITH SPACES JR NZ,LE9 INC DE ;GET NEXT CHAR AFTER '.' LC8: CALL RSVP ;IF RESERVED CHAR THEN EXIT JR Z,LE9 INC HL ;IF CHAR <> '*' THEN EXIT CP '*' JR NZ,LD9 LD (HL),'?' JR LDB LD9: LD (HL),A ;ADD CHAR TO FCB INC DE LDB: DJNZ LC8 LDF: CALL RSVP ;IF RESERVED CHAR THEN EXIT JR Z,LF0 INC DE ;WASTE CHARS JR LDF LE9: INC HL ;FILL REMAINING TYPE WITH SPACES LD (HL),' ' DJNZ LE9 LF0: LD B,ZROSIZ ;ZERO EXTENT, S1, S2, RECORD COUNT LF2: INC HL LD (HL),0 DJNZ LF2 LD (CMDPTR),DE PUSH IY POP HL ;COUNT WILDCARDS LD BC,NAMCNT + TYPCNT L01: INC HL LD A,(HL) CP '?' JR NZ,L09 INC B L09: DEC C JR NZ,L01 LD A,B OR A RET ;************************************ ; ; RESERVED CHAR TEST SUBR ; ENTRY- (DE) = CHAR TO TEST ; EXIT - ZF = FOUND ; ;************************************ RSVP: LD A,(DE) ;IF CHAR <= ' ' THEN RETURN Z CP ' ' + 1 JR NC,RSVP1 XOR A RET RSVP1: PUSH BC PUSH HL LD B,TABCNT ;B := ENTRY COUNT LD HL,RSVPT RSVPL: CP (HL) JR Z,RSVPX ;IF FOUND THEN EXIT INC HL DJNZ RSVPL INC B ;RETURN NZ RSVPX: POP HL POP BC RET RSVPT: DB '=_.:;,<>' TABCNT: EQU $ - RSVPT ;************************************** ; ; OPEN FILE SUBR ; ENTRY- DE= FCB PTR ; EXIT - A= -1 IF NOT ON DISK ; ;************************************** OPEN: LD C,OPENF JP BDOS ;************************************** ; ; READ SECTOR SUBR ; ENTRY- DE= FCB PTR ; EXIT - A= 0 IF OK ; ;************************************** RDSEC: LD C,RDF JP BDOS ;************************************** ; ; SET DISK READ ADDRESS SUBR ; ENTRY- DE= ADDRESS ; ;************************************** SETDMA: LD C,26D JP BDOS ;************************************** ; ; READ CONSOLE BUFFER SUBR ; EXIT - CMDBFR = CONSOLE INPUT ; ;************************************** RDCON: LD DE,CMDBFR ;INIT BUFFER LD A,CBFRSZ ;CMDBFR[0] = CBFRSZ LD (DE),A PUSH DE ;BUFFER PTR LD C,RDCF ;GET INPUT FROM USER CALL BDOS POP HL ;BUFFER PTR INC HL ;COUNT = CMDBFR[1] AND 7FH LD C,(HL) LD B,0 RES 7,C ;MAKE SURE < 128 INC HL ;CMDPTR = .CMDBFR[2] LD (CMDPTR),HL ADD HL,BC ;CMDBFR[COUNT + 2] = 0 /* END OF LINE = NUL */ LD (HL),0 RET ;********************************************************** ; ; READ FILE SKIPS ANY HEADER BLOCK & ONLY LOADS CODE ; ENTRY- BC = LOAD BUFFER PTR ; EXIT - Z = SUCCESSFUL READ ; NZ = READ ERROR ; ;********************************************************** RDFILE: LD (RDPTR),BC ;SAVE ACTUAL READ PTR LD DE,DEFBFR ;READ 1ST SECTOR INTO DEFAULT BUFFER FOR NOW CALL SETDMA LD DE,FCB CALL RDSEC OR A ;IF BAD READ THEN RETURN ERR RET NZ ; IF FILE STARTS WITH JP 0000H THEN ASSUME IT IS A HEADER BLOCK LD A,(DEFBFR) CP 0C3H JR NZ,RESCAN ;ELSE ASSUME THE 1ST SECTOR IS GOOD & LOAD IT LD HL,(DEFBFR+1) LD A,H OR L JR Z,READ1 ; NOT HEADER IF HERE, MOVE INTO LOAD AREA & CONTINUE WITH NEXT SECTOR RESCAN: LD HL,DEFBFR LD DE,(RDPTR) LD BC,SECSIZ LDIR LD (RDPTR),DE ;UPDATE READ PTR ; IF HERE, EITHER WE ARE DISCARDING THE 1ST SECTOR OR READING THE 2ND READ1: LD DE,(RDPTR) ;READ INTO LOAD AREA DIRECTLY CALL SETDMA LD DE,FCB CALL RDSEC OR A ;IF END OF FILE THEN DONE JR NZ,RDDONE LD DE,SECSIZ ;PTR = PTR + SECTOR$SIZE LD HL,(RDPTR) ADD HL,DE LD (RDPTR),HL LD A,(MEMTOP+1) ;IF NEW PAGE > RESERVED_PAGE - 1 THEN DEC A ; GOING INTO RSVP SO ABORT BY RETURNING CF CP H RET C JP READ1 ; RDDONE: XOR A ;RETURN Z RET ;********************************************************** ; ; FETCH PARAMETERS SUBR ; ENTRY- CMDPTR= PTR TO 1ST NUMERICAL PARAMETER ; EXIT - CF= ERR ; ;********************************************************** GET3PAR: LD HL,(CMDPTR) ;IF NO PARAMETERS THEN RETURN CF CALL SKIP SCF RET Z LD (CMDPTR),HL ;SAVE NEW PTR CALL GETNUM ;GET 1ST PARAMETER RET C LD DE,PARAM1 ;INIT PTR TO PARAM STORAGE CALL LOAD16 LD HL,(CMDPTR) ;SEARCH FOR 2ND PARAMETER (AFTER ',') LD C,',' CALL SEARCH SCF ;IF NO 2ND PARAMETER THEN RETURN CF RET Z INC HL ;PASS OVER ',' ;********************************************************** ; ; GET 2 PARAMETERS SUBR. SAME AS ABOVE EXCEPT: ; ENTRY- DE= PARAMETER STORAGE PTR ; HL= CMDPTR ; EXIT - CF= ERR ; ;********************************************************** GET2PAR: CALL SKIP ;SKIP ANY TRAILING SPACES SCF ;IF END OF LINE THEN RETURN CF RET Z LD (CMDPTR),HL ;SAVE NEW PTR CALL GETNUM ;GET 2ND PARAM. RET C CALL LOAD16 ;************************************** ; ; THIS ENTRY IS SAME AS ABOVE ; EXCEPT DOES SEARCH FOR COMMA. ; ;************************************** GET1PC: LD HL,(CMDPTR) LD C,',' CALL SEARCH SCF RET Z INC HL ;************************************************ ; ; GET 1 PARAMETER SUBR SAME AS ABOVE ; ;************************************************ GET1PAR: CALL SKIP ;SKIP ANY SPACES SCF RET Z LD (CMDPTR),HL ;SAVE NEW PTR CALL GETNUM ;GET PARAMETER RET C CALL LOAD16 OR A ;RETURN NO ERR RET ;********************************************************** ; ; LOAD WORD SUBR ; ENTRY- DE= WORD PTR ; HL= VALUE ; EXIT - DE= WORD PTR + 2 (NEXT WORD IN SEQUENCE) ; ;********************************************************** LOAD16: EX DE,HL ;STORE IT LD (HL),E INC HL LD (HL),D INC HL EX DE,HL RET ;*************************************************************************** ; ; CHECK BOUNDS SUBR TESTS PARAM 1 & 2 FOR OUT OF BOUNDS VALUES & IF OK, ; STORES THE OFFSETTED VALUES BACK INTO THE RESPECTIVE LOCATIONS. ; EXIT - CF= OUT OF BOUNDS ; ;*************************************************************************** CKBNDS: LD HL,(PARAM1) ;START WITH PARAM 1 CALL CPBNDS ;IF OUT THEN RETURN CF RET C LD (PARAM1),HL ;ELSE STORE NEW VALUE LD HL,(PARAM2) ;THEN CHECK PARAM 2 CALL CPBNDS LD (PARAM2),HL ;STORE IT & RETURN RESULT RET ;*************************************************************************** ; ; COMPARE BOUNDS SUBR ADDS VALUE TO THE BASE OF THE BUFFER & CHECKS FOR ; OVERFLOW. THEN CHECKS NEW VALUE'S PAGE AGAINST THE TOP OF AVAILABLE ; MEMORY'S PAGE & RETURNS CARRY IF EITHER IS OUT. ; ENTRY- HL = VALUE TO COMPARE ; EXIT - HL = CORRECTED VALUE ; CF = BOUNDS ERROR ; ;*************************************************************************** CPBNDS: LD A,(MEMTOP+1) ;SET UP MEMTOP PAGE -1 DEC A LD DE,($MEMRY) ;ADD VALUE TO BUFFER START (FROM OVERLAY) ADD HL,DE RET C ;IF OVERFLOW THEN RETURN CF CP H ;ELSE COMPARE PAGE VALUE TO MEMTOP PAGE -1 RET ;RETURN CF IF PAGE > MEMTOP PAGE -1 ;**************************************************************************** ; ; GET NUMBER SUBR CONVERTS ASCII-HEX CHARS IN STRING TO BINARY WORDS UNTIL ; A NON- HEX CHAR IS ENCOUNTERED. IF NOT SPACE OR COMMA THEN RETURNS CARRY. ; ENTRY- HL = TEXT POINTER ; EXIT - HL = NUMBER ; CF= NON HEX CHAR (BUT NOT SPACE OR COMMA) ; BC= PTR TO NON-HEX CHAR ; A= NON-HEX CHAR ; ;*************************************************************************** GETNUM: PUSH HL ;HL -> BC POP BC LD A,0 ;FLAG = 0 LD (GETFLG),A LD HL,0 ;ACCUM = 0 GETLP: LD A,(BC) ;CHAR = (PTR) CALL ASC2HX ;ATTEMPT TO CONVERT CHAR JR C,GETCHK ;IF ILLEGAL THEN CHECK FOR DELIMITER ADD HL,HL ;ACCUM = ACCUM * 16 ADD HL,HL ADD HL,HL ADD HL,HL ADD L ;ACCUM = ACCUM + DATA LD L,A INC BC ;PTR = PTR + 1 LD A,(GETFLG) ;BUMP FLAG TO NOT 0 INC A LD (GETFLG),A JR GETLP GETCHK: LD A,(GETFLG) ;IF FLAG = 0 THEN RETURN CF SUB 1 RET C LD A,(BC) ;GE-GET CHAR CP ' ' ;IF SPACE THEN RETURN NC RET Z CP ',' ;IF COMMA THEN RETURN NC RET Z OR A ;IF END OF LINE THEN RETURN NC RET Z SCF ;ELSE RETURN CF RET ;********************************* ; ; PRINT HEX VALUE SUBR ; ENTRY- HL = VALUE ; ;******************************** PRT2H: LD A,H CALL PRTHEX LD A,L ;************************************************ ; ; PRINT BYTE HEX VALUE TO CONSOLE SUBR ; ENTRY- A= VALUE ; ;************************************************ PRTHEX: OR A RLA CALL PRHXDG PRHXDG: RLA RLA RLA RLA PUSH AF CALL HX2ASC ;CONVERT NIBBLE TO ASCII-HEX PUSH HL LD C,A CALL CONOUT POP HL POP AF RET ;*************************************************************************** ; ; HEX TO ASCII-HEX CONVERTER CONVERTS BINARY NIBBLE TO ASCII-HEX ; ENTRY- A= BINARY NIBBLE ; EXIT - A= ASCII REPRESENTATION OF THE NIBBLE ; ;*************************************************************************** HX2ASC: AND 0FH ;ONLY NIBBLE PLEASE CP 10 ;IF 0..9 THEN OK JR C,NTALPH ADD 7 ;ELSE OFFSET 10..15 TO 'A'..'F' NTALPH: ADD '0' ;CONVERT TO ASCII RET ;**************************** ; ; CHECK FOR DONE SUBR ; ;**************************** CKDONE: EX DE,HL LD HL,(DISEND) OR A SBC HL,DE EX DE,HL RET SUBTTL MESSAGES RDERRM: DB CR,LF,'Disk Read Error$' WRERRM: DB CR,LF,'Disk Write Error, Probably Full!$' DELMSG: DB CR,LF,'File Exists, Delete?..(Y/N) - $' CMDERM: SYNERM: DB CR,LF,'*** Syntax Error ***...Type ''HElp'' For List$' FNFM: DB CR,LF,'File Not Found$' BOUNDM: DB CR,LF,'Value Out of Bounds$' MEMERM: DB CR,LF,'Memory Error$' HXERM: DB CR,LF,'Hex File Error$' CVERM: DB CR,LF,'Hex Conversion Error$' HXADRM: DB CR,LF,'Hex Load Address Lower Than 1st Record Load Address!$' HXTYPM: DB CR,LF,'Illegal Hex Record Type!$' HXEMTM: DB CR,LF,'Hex File Empty!$' HELPM: DB CR,LF,'HElp' DB CR,LF,'REad addr d:file (MUST BE ''HEX''FORMAT)' DB CR,LF,'SAve first, last d:file (WILL BE ''HEX'' FORMAT)' DB CR,LF,'FIll first, last, value' DB CR,LF,'MOve first, last, dest' DB CR,LF,'DIsplay <, last>' DB CR,LF,'SUbstitute addr' DB CR,LF,'CTRL-C To Exit to CP/M' DB CR,LF,'$' SUBTTL COMMAND TABLE ;------------------ ; ; COMMAND TABLE ; ;------------------ CMDTBL: DB 2,'RE' ;READ DW READC DB 2,'FI' ;FILL DW FILLC DB 2,'MO' ;MOVE DW MOVEC DB 2,'DI' ;DISPLAY DW DISPC DB 2,'SU' ;SUBSTITUTE DW SUBSC DB 2,'SA' ;SAVE DW SAVEC DB 2,'HE' ;HELP DW HELPC DB 2,'?',0 ; " DW HELPC DB 2,'H',0 ; " DW HELPC DB 0 ;TABLE TERMINATOR SUBTTL VARIABLES CMDPTR: DW CMDBFR CMDBFR: EQU $ ;COMMAND INPUT TEXT BUFFER REPT 130 LIST OFF DB 0 LIST ON ENDM CBFRSZ: EQU $-CMDBFR-2 ; ; INPUT PARAMETER STORAGE ; PARAM1: DW 0 PARAM2: DW 0 PARAM3: DW 0 ; MEMTOP: DW 0 ;TOP OF AVAILABLE MEMORY PTR RDPTR: DW 0 ;FILE READ PTR DISTRT: DW 0 ;DISPLAY COMMAND LAST START PTR DISEND: DW 0 ;DISPLAY COMMAND LAST END PTR DISTMP: DW 0 ;DISPLAY COMMAND TEMP STORAGE TEMP: DW 0 ;TEMPORARY STORAGE GETFLG: DB 0 ;FLAG FOR GETNUM BFRCNT: DB 0 ;FILE READ COMMAND BUFFER COUNT DBPTR: DW DEFBFR BFRPTR: DW 0 ;HEX FILE READ PTR HEXBASE: DW 0 ;HEX FILE BASE LOAD ADDR HEXPTR: DW 0 ;HEX FILE READ PTR FCB: EQU $ ;FILE CONTROL BLOCK STORAGE REPT 33 LIST OFF DB 0 LIST ON ENDM DS STKSIZ STACK: EQU $ IF ($ AND 000FH) = 0 PGMEND: EQU $ ELSE PGMEND EQU ($ AND 0FFF0H) + 0100H ENDIF END