TITLE 'MLOAD MULTI-FILE HEX LOAD UTILITY' ; ; ********************************* ; * MLOAD.ASM * ; * MULTI-FILE HEX LOAD UTILITY * ; * FOR CP/M * ; ********************************* ; ; ; REPLACEMENT FOR THE CP/M "LOAD" PROGRAM: THIS PROGRAM ; FIXES MANY OF THE PROBLEMS ASSOCIATED WITH THE "CP/M" ; LOAD PROGRAM, AND ADDS MANY NEW FEATURES. ; ; ---------------- ; ; REV 2.1 ; 03/08/84 ; WRITTEN BY RON FOWLER, FORT ATKINSON, WI ; ; ---------------- ; ; MODIFICATION HISTORY: ; ; 2.1 (RGF) FIXED PROBLEM ON DISK-FULL WHEN WRITING OUTPUT FILE ; (MLOAD PREVIOUSLY DIDN'T ERROR OUT ON A FULL DISK) ; 2.0 (RGF) ADDED THE ABILITY TO PRE-LOAD A NON-HEX FILE, ALLOWING ; MLOAD TO BE USED TO LOAD HEX FILE PATCHES (OBVIATING ANY ; NEED TO USE DDT). THE NORMAL MLOAD SYNTAX IS PRESERVED. ; THE FIRST (AND ONLY THE FIRST) FILESPEC (AFTER THE "=", ; IF USED) MAY BE NON-HEX; THE FILETYPE MUST BE SPECIFIED. ; EXAMPLES: ; ; 1) MLOAD WS.COM,WSPATCH ; 2) MLOAD MDM7TEST=MDM720.COM,MDM7US ; 3) MLOAD WS.OVR,OVRPATCH ; ; THE FIRST EXAMPLE LOADS WS.COM, OVERLAYS IT WITH ; WSPATCH.HEX, AND WRITES THE OUTPUT TO WS.COM. THE ; SECOND EXAMPLE LOADS MDM720.COM, OVERLAYS IT WITH ; MDM7US.HEX, AND WRITES THE OUTPUT FILE TO MDM7TEST.COM. ; (NOTE THAT THE SECOND EXAMPLE IS THE RECOMMENDED TECHNIQUE, ; SINCE IT PRESERVES THE ORIGINAL FILE). THE THIRD EXAMPLE ; LOADS WS.OVR AND PATCHES IT WITH THE FILE "OVRPATCH.HEX". ; ; ALSO ADDED THIS REV: ZCPR2-STYLE DU SPECS ARE NOW FULLY ; SUPPORTED, FOR BOTH INPUT AND OUTPUT FILES. THUS, THE ; FOLLOWING COMMAND LINES ARE PERMISSABLE: ; ; B3>MLOAD A4:MYFILE.COM=0:BIGFIL,B6:PATCH1,C9:PATCH2 ; A6>MLOAD B5:=C3:MDM717.COM,MDMPATCH ; ; AFTER LOADING, AN ADDITIONAL INFORMATION LINE IS NOW PRINTED ; IN THE STATISTICS REPORT, WHICH DISPLAYS THE TRUE SIZE OF THE ; SAVED IMAGE (THE PREVIOUS REPORT WAS TECHNICALLY CORRECT, BUT ; COULD RESULT IN CONFUSION FOR CERTAIN KINDS OF FILES WITH ; IMBEDDED "DS" AND "ORG" STATEMENTS IN THE ORIGINAL SOURCE CODE). ; ; 1.0 - 1.4 (RGF) CHANGE LOG REMOVED TO CONSERVE SPACE ; ; ORIGINALLY WRITTEN BY RON FOWLER, FORT ATKINSON, WISCONSIN ; ; ; ; FOR ASSEMBLY WITH ASM.COM OR MAC (DELETE ABOVE TITLE LINE IF ; ASSEMBLING WITH ASM.COM) ; ; THIS PROGRAM IS A REPLACEMENT FOR THE CP/M "LOAD" PROGRAM. ; WHY REPLACE "LOAD"? WELL... LOAD.COM HAS A FEW DEFICIENCIES. ; FOR EXAMPLE, IF YOUR HEX FILE'S ORIGIN IS ABOVE 100H, LOAD.COM ; PREPENDS BLANK SPACE TO THE OUTPUT FILE TO INSURE IT WILL WORK ; AS A CP/M TRANSIENT. IT CARES NOT IF THE FILE IS NOT INTENDED ; AS A CP/M TRANSIENT. IT ALSO DOESN'T LIKE HEX RECORDS WITH MIXED ; LOAD ADDRESSES (FOR EXAMPLE, ONE THAT LOADS BELOW A PREVIOUS RECORD -- ; WHICH IS A PERFECTLY LEGITIMATE HAPPENSTANCE). ALSO, LOAD.COM ; CAN LOAD ONLY ONE PROGRAM AT A TIME, AND HAS NO PROVISION FOR ; A LOAD BIAS IN THE COMMAND SPECIFICATION. FINALLY, THERE IS NO ; PROVISION FOR USER SPECIFICATION OF OUTPUT FILE NAME. ; ; ; HENCE, THIS PROGRAM.... ; ;------------------------------------------------------------ ; ; SYNTAX IS AS FOLLOWS: ; ; MLOAD [[,...] [BIAS] ; ; WHERE SPECIFIES FILES TO LOAD AND IS THE OFFSET WITHIN ; THE SAVED IMAGE TO APPLY WHEN LOADING THE FILE. ; ; MLOAD WITH NO ARGUMENTS PRINTS A SMALL HELP MESSAGE -- THIS MESSAGE ; IS ALSO PRINTED WHENEVER A COMMAND LINE SYNTAX ERROR OCCURS. ; ; FILENAMES MAY CONTAIN DRIVE SPECS, AND MUST NOT CONTAIN WILDCARDS. ; INPUT FILENAMES MUST BE SEPARATED BY COMMAS, AND A SPACE IS REQUIRED ; BETWEEN THE LAST FILENAME AND THE OPTIONAL BIAS. ; ; A LOAD INFORMATION SUMMARY IS PRINTED AT THE SUCCESSFUL CONCLUSION ; OF THE LOAD. ANY ERRORS IN LOADING WILL GENERALLY INCLUDE THE NAME ; OF THE FILE IN QUESTION. ; ; IF NO OUTPUT FILENAME IS SPECIFIED, IT WILL BE DERIVED FROM THE FIRST ; INPUT FILENAME, WITH FILETYPE OF 'COM', IF NOT OTHERWISE SPECIFIED ; (THIS DEFAULT FILETYPE MAY BE PATCHED DIRECTLY INTO MLOAD VIA DDT ; -- ITS LOCATION IS AT 103H IN MLOAD.COM). NOTE THAT A COMMAND LINE OF ; THE FORM "C:=" WILL PLACE THE OUTPUT FILE ON THE "C" DRIVE ; WITH THE SAME PRIMARY FILENAME AS THE INPUT FILE. ; ; IN ITS SIMPLEST FORM, MLOAD'S SYNTAX IS IDENTICAL TO LOAD.COM; THUS ; THERE SHOULD BE NO PROBLEM IN LEARNING TO USE THE NEW PROGRAM. THE ; ONLY SIGNIFICANT DIFFERENCE HERE IS THAT, UNDER LOAD.COM, ALL FILES ; ARE OUTPUT STARTING AT 100H, EVEN IF THEY ORIGINATE ELSEWHERE. MLOAD ; OUTPUTS STARTING AT THE HEX FILE ORIGIN (ACTUALLY, THE FIRST HEXT REC- ; ORD SPECIFIES THE OUTPUT LOAD ADDRESS). THE BIAS OPTION MAY BE USED ; TO OVERRIDE THIS. ; ; AN EXAMPLE SHOULD CLARIFY THIS. SUPPOSE YOU HAVE A FILE THAT LOADS ; AT 1000H. LOAD.COM WOULD SAVE AN OUTPUT FILE THAT BEGINS AT 100H AND ; LOADS PAST 1000H (TO WHEREVER THE PROGRAM ENDS). MLOAD WILL SAVE AN ; OUTPUT FILE STARTING FROM 1000H ONLY. IF, FOR SOME REASON YOU NEED THE ; FILE TO START AT 100H IN SPITE OF ITS 1000H ORIGIN (I CAN THINK OF SEV- ; ERAL CIRCUMSTANCES WHERE THIS WOULD BE NECESSARY), YOU'D HAVE TO SPECIFY ; A BIAS TO MLOAD. THUS, USING THIS EXAMPLE, "MLOAD MYFILE 0F00" WOULD DO. ; ; NOTE THAT THIS PROGRAM RE-INITIALIZES ITSELF EACH TIME IT IS RUN. ; THUS, IF YOUR SYSTEM SUPPORTS A DIRECT BRANCH TO THE TPA (VIA A ZERO-LENGTH ; .COM FILE, OR THE ZCPR "GO" COMMAND), YOU MAY SAFELY RE-EXECUTE MLOAD. ; ; PLEASE REPORT ANY BUGS, BUG FIXES, OR ENHANCEMENTS TO ME AT: ; ; "FORT FONE FILE FOLDER" RCPM/CBBS ; FORT ATKINSON, WISCONSIN ; (414) 563-9932 (NO RING BACK) ; ; --RON FOWLER ; 03/08/84 ; ;------------------------------------------------------------ ; ; CP/M EQUATES ; WARMBT EQU 0 ;WARM BOOT SYSTEM EQU 5 ;SYSTEM ENTRY (ALSO TOP OF MEM PNTR) DFCB EQU 5CH ;DEFAULT FILE CONTROL BLOCK FT EQU 9 ;FCB OFFSET TO FILETYPE TBUF EQU 80H ;DEFAULT BUFFER TPA EQU 100H ;TRANSIENT PROGRAM AREA EOF EQU 1AH ;CP/M END-OF-FILE MARK FCBSIZ EQU 33 ;SIZE OF FILE CONTROL BLOCK ; ; CP/M SYSTEM CALLS ; PCHARF EQU 2 ;PRINT CHAR SELDF EQU 14 ;SELECT DISK DRIVE OPENF EQU 15 ;OPEN FILE CLOSEF EQU 16 ;CLOSE FILE FSRCHF EQU 17 ;SEARCH FOR FIRST FSRCHN EQU 18 ;SEARCH FOR NEXT ERASEF EQU 19 ;DELETE FILE READF EQU 20 ;READ RECORD WRITEF EQU 21 ;WRITE RECORD CREATF EQU 22 ;CREATE FILE GETDRF EQU 25 ;RETURN DFLT DRIVE # SDMAF EQU 26 ;SET DMA ADDRESS GSUSER EQU 32 ;GET/SET USER # RRAND EQU 33 ;READ RANDOM WRAND EQU 34 ;WRITE RANDOM FILSZF EQU 35 ;COMPUTE FILE SIZE SRAND EQU 36 ;SET RANDOM ; ; ASCII CHARACTER CONSTANTS ; CR EQU 13 LF EQU 10 BEL EQU 7 TAB EQU 9 ; ; WITHOUT FURTHER ADO... ; ORG TPA ; JMP BEGIN ;JUMP OVER DEFAULT OUTPUT FILETYPE ; ; THE DEFAULT OUTPUT FILETYPE IS LOCATED AT 103H FOR EASY PATCHING ; OUTTYP: DB 'COM' ; BEGIN: LXI H,0 ;SAVE SYSTEM STACKPOINTER DAD SP SHLD SPSAVE LXI SP,STACK ;LOAD LOCAL STACK CALL ILPRNT ;SIGN ON DB 'MLOAD ver. 2.1 Copyright (C) 1983,1984 by Ronald G. Fowler' DB CR,LF,0 CALL SETUP ;INITIALIZE MAIN: CALL NXTFIL ;PARSE AND READ NEXT INPUT FILE JC DONE ;NO MORE... CALL LODFIL ;YEP, LOAD IT CALL CLOSFL ;CLOSE IT (IN CASE MP/M) JMP MAIN ;MAYBE MORE DONE: CALL WRTFIL ;WRITE THE OUTPUT FILE ; ; EXIT TO CP/M ; EXIT: LXI D,TBUF ;RESTORE DMA ADDRESS MVI C,SDMAF CALL BDOS LDA SYSTEM+2 ;GET TOP OF MEMORY POINTER SUI 9 ;ALLOW FOR CCP+SLOP LXI H,HILOAD+1 ;HIGHEST LOAD ADDRESS SUB M ;ABOVE CCP? JC WARMBT ;THEN WARM-BOOT LHLD SPSAVE ;NOPE, CCP STILL IN MEMORY SPHL ;RESTORE ITS STACK RET ;RETURN TO CCP ; ; LOAD PROGRAM INITIALIZATION ; SETUP: LXI H,VARSET ;INITIALIZE VARIABLES LXI D,VARS MVI B,VARLEN ;BY MOVING IN DEFAULT VALUES CALL MOVE LHLD CMDPTR ;GET FIRST FREE MEM POINTER XCHG ;IN DE LXI H,TBUF ;POINT TO COMMAND TAIL BUFR MOV A,M ;GET ITS LENGTH INX H ORA A ;DOES IT HAVE ANY LENGTH? JZ HELP ;NOPE, GO GIVE USAGE HELP MOV B,A ;YEP, GET LENGTH TO B CALL MOVE ;MOVE CMD TAIL TO BUFFER XCHG ;END OF DEST TO HL MVI M,0 ;STUFF A TERMINATOR INX H ;POINT TO FIRST FREE MEMORY SHLD FILBUF ;SET UP FILE BUFFER XCHG ;FILE BUFR ADRS TO DE LHLD SYSTEM+1 ;GET TOP OF MEMORY POINTER MOV A,L ;COMPUTE SIZE OF FILE BUFFER SUB E MOV C,A ;WITH RESULT IN BC MOV A,H SUI 9 ;ALLOW FOR CCP SBB D MOV B,A XCHG ;BUFFER POINTER TO HL NITMEM: MVI M,0 ;CLEAR BUFFER INX H DCX B MOV A,B ORA C JNZ NITMEM ; ; LOOK FOR A BIAS SPECIFICATION IN COMMAND LINE ; LHLD CMDPTR ;POINT TO COMMAND BUFFER-1 DCX H CALL SCANBK ;SCAN PAST BLANKS ORA A ;NO NON-BLANK CHARS? JZ HELP ;THEN GO PRINT HELP TEXT FNDSPC: INX H ;POINT TO NEXT MOV A,M ;FETCH IT ORA A ;TEST IT RZ ;LINE ENDED, RETURN CPI ' ' ;NOPE, TEST FOR BLANK JNZ FNDSPC ;NOT BLANK, CONTINUE CALL SCANBK ;SKIP BLANKS ORA A ;END-OF-LINE? RZ ;RETURN IF SO ; ; HL POINTS TO BIAS IN COMMAND LINE ; LXI D,0 ;INIT BIAS CALL HEXDIG ;INSURE A HEX DIGIT JC SYNERR ;BAD... HEXLP: MOV A,M ;NO. GET NEXT CHAR INX H ;SKIP OVER IT CALL HEXDIG ;TEST FOR HEX DIGIT JNC DIGOK ;JUMP IF GOOD HEX DIGIT ORA A ;MUST END ON NULL TERMINATOR JNZ SYNERR XCHG ;GOOD END, GET BIAS TO HL SHLD BIAS ;STUFF IT RET ;DONE DIGOK: XCHG ;BIAS TO HL DAD H ;SKIFT LEFT 4 TO MAKE ROOM DAD H ; FOR NEW HEX DIGIT DAD H DAD H XCHG ;BACK TO DE ADD E ;ADD IN NEW DIGIT MOV E,A JNC HEXLP ;JUMP IF NO 8-BIT OVFL INR D ;CARRY JMP HEXLP ; ; PARSE NEXT INPUT NAME, AND OPEN RESULTANT FILE ; NXTFIL: LHLD CMDPTR ;GET COMMAND LINE POINTER NEXT2: LXI D,DFCB ;DESTINATION FCB CALL FPARSE ;PARSE A FILENAME CPI '=' ;STOPPED ON OUTPUT SPECIFIER? JNZ NOTEQ LDA OUTNAM+2 ;INSURE NO NAME YET SPECIFIED CPI ' ' JNZ SYNERR ;SYNTAX ERROR IF ALREADY NAMED LDA OUTFLG ;ALREADY BEEN HERE? ORA A JNZ SYNERR ;CAN'T BE HERE TWICE INR A ;FLAG THAT WE'VE BEEN HERE STA OUTFLG INR B ;INSURE NO AMBIGUOUS OUTPUT NAME DCR B JNZ AFNERR INX H ;SKIP OVER '=' PUSH H ;SAVE CMD LINE POINTER LXI H,DFCB-1 ;MOVE THE NAME TO OUTPUT NAME HOLD LXI D,OUTNAM MVI B,13 ;DRIVE SPEC TOO CALL MOVE POP H ;RESTORE COMMAND LINE POINTER JMP NEXT2 ;GO PARSE ANOTHER NOTEQ: CPI ',' ;STOPPED ON COMMA? JZ GCOMMA ;JUMP IF SO MVI M,0 ;NOPE, INSURE END OF INPUT JMP NXT2 ;DON'T ADVANCE OVER FAKE END GCOMMA: INX H ;SKIP OVER COMMA NXT2: SHLD CMDPTR ;SAVE NEW COMMAND LINE PNTR MOV A,B ;GET AMBIG CHAR COUNT ORA A ;TEST IT JNZ AFNERR ;ALLOW NO AMBIG CHARACTERS STA BUFPTR ;FORCE A DISK READ LXI D,DFCB+1 ;LOOK AT PARSED FILENAME LDAX D CPI ' ' ;BLANK? (INPUT ENDED?) STC ;GET CARRY READY IN CASE SO RZ ;RETURN CY IF INPUT GONE DCX D ;NOPE, POINT DE TO START OF FCB OPEN2: PUSH D ;TRY TO OPEN THE FILE MVI C,OPENF CALL BDOS POP D INR A ;RETURN=0FFH? JNZ OPENOK ;JUMP IF NOT ; ; FILE NOT FOUND: IF FILETYPE BLANK, SET TO 'HEX' AND TRY AGAIN ; LXI H,DFCB+FT ;POINT TO FILE TYPE MOV A,M ;ANYTHING THERE? CPI ' ' JNZ FNFERR ;YES, SO FILE NOT FOUND MVI M,'H' ;NOPE, FILL IN 'HEX' INX H MVI M,'E' INX H MVI M,'X' JMP OPEN2 ;GO TRY AGAIN ; ; HERE AFTER A GOOD FILE OPEN ; OPENOK: CALL HEXCHK ;IS THIS A HEX FILE? RZ ;IF SO, ALL DONE LXI H,COMFLG ;NO, GET POINTER TO FLAG MOV A,M ;LOADING FIRST FILE? ORA A RNZ ;IF NOT, IGNORE TYPE, CONSIDER HEX INR M ;ELSE, SET THE FLAG RET ; ; LOAD CURRENT FILE ; LODFIL: LXI H,COMFLG ;LOADING A COM FILE? MOV A,M ;GET FLAG ANI 1 JNZ LODCOM ;JUMP IF SO LHLD BIAS ;ELSE GET BIAS ON TOP OF STACK PUSH H ; ; LOAD A HEX RECORD ; LOADLP: CALL GNB ;GET NEXT FILE BYTE SBI ':' ;LOOK FOR START-RECORD MARK JNZ LOADLP ;SCAN UNTIL FOUND STA CKSUM ;GOT IT, INIT CHECKSUM TO ZERO MOV D,A ;UPPER BYTE OF REC CNT=0 POP B ;RETRIEVE BIAS ADRS PUSH B ;SAVE IT AGAIN CALL GHBCKS ;GET HEX BYTE W/CHECKSUM MOV E,A ;DE NOW HAS RECORD LENGTH ORA A ;TEST IT JNZ NOTEND ;JUMP IF LEN<>0 (NOT EOF REC) POP H ;ALL DONE RET NOTEND: CALL GHBCKS ;HI BYTE OF REC LD ADRS MOV H,A ;ACCUMULATE IN HL CALL GHBCKS ;GET LO BYTE MOV L,A ;PUT LO IN L LDA LODFLG ;TEST LOAD FLAG ORA A CZ LODNIT ;NOT FIRST RECORD, INITIALIZE PUSH H ;SAVE LOAD ADDRESS DAD D ;ADD IN RECORD LENGTH DCX H ;MAKE HIGHEST, NOT NEXT LDA HIPC ;A NEW HIGH? SUB L LDA HIPC+1 SBB H JNC NOTGT ;JUMP IF NOT SHLD HIPC ;YEP, UPDATE HIPC PUSH D ;SAVE RECLEN XCHG ;LOAD ADRS TO DE LHLD OFFSET ;GET OFFSET TO FORM TRUE MEMORY ADRS DAD D ;ADD IN OFFSET DAD B ;AND BIAS SHLD HILOAD ;MARK HIGHEST TRUE MEMORY LOAD ADRS LDA SYSTEM+2 ;VALIDATE AGAINST TOP-MEM POINTER CMP H JC MEMFUL ;JUMP IF OUT OF MEMORY POP D ;RESTORE RECLEN NOTGT: POP H ;RESTORE LOAD ADDRESS DAD B ;ADD BIAS TO LOAD ADRS PUSH D ;SAVE RECORD LENGTH PUSH H LHLD BYTCNT ;ADD RECORD LENGTH TO BYTE COUNT DAD D SHLD BYTCNT POP H XCHG LHLD OFFSET ;CALCULATE TRUE MEMORY ADRS DAD D ;HL=TRUE LOADING ADRS POP D ;RESTORE RECORD LENGTH CALL GHBCKS ;SKIP UNUSED BYTE OF INTEL FORMAT ; ; MOVE THE RECORD INTO MEMORY ; RECLP: CALL GHBCKS ;GET HEX BYTE MOV M,A ;STORE IT IN BUFFER INX H ;POINT TO NEXT DCR E ;COUNT DOWN JNZ RECLP ;UNTIL RECORD ALL READ CALL GHBCKS ;GET CHECKSUM BYTE JNZ CSERR ;FINAL ADD CKSUM SHOULD SUM 0 JMP LOADLP ;GOOD LOAD, GO DO NXT RECORD ; ; GET NEXT HEX BYTE FROM INPUT, AND ; ACCUMULATE A CHECKSUM ; GHBCKS: PUSH B ;SAVE EM ALL PUSH H PUSH D CALL HEXIN ;GET HEX BYTE MOV B,A ;SAVE IN B LXI H,CKSUM ;ADD TO CHECKSUM MOV A,M ADD B MOV M,A MOV A,B ;GET BYTE BACK POP D ;RESTORE CHECKSUM POP H ;RESTORE OTHER REGS POP B RET ; ; ROUTINE TO GET NEXT BYTE FROM INPUT...FORMS ; BYTE FROM TWO ASCII HEX CHARACTERS ; HEXIN: CALL GNB ;GET NEXT INPUT FILE BYTE CALL HEXVAL ;CONVERT TO BINARY W/VALIDATION RLC ;MOVE INTO MS NYBBLE RLC RLC RLC ANI 0F0H ;KILL POSSIBLE GARBAGE PUSH PSW ;SAVE IT CALL GNB ;GET NEXT BYTE CALL HEXVAL ;CONVERT IT, W/VALIDATION POP B ;GET BACK FIRST ORA B ;OR IN SECOND RET ;GOOD BYTE IN A ; ; GNB - UTILITY SUBROUTINE TO GET NEXT ; BYTE FROM DISK FILE GNB: PUSH H ;SAVE ALL REGS PUSH D PUSH B LDA BUFPTR ;GET INPUT BUFR POINTER ANI 7FH ;WOUND BACK TO 0? JZ DISKRD ;GO READ SECTOR IF SO GNB1: MVI D,0 ;ELSE FORM 16 BIT OFFSET MOV E,A LXI H,TBUF ;FROM TBUF DAD D ;ADD IN OFFSET MOV A,M ;GET NEXT BYTE CPI EOF ;END OF FILE? JZ EOFERR ;ERROR IF SO LXI H,BUFPTR ;ELSE BUMP BUF PTR INR M ORA A ;RETURN CARRY CLEAR POP B ;RESTORE AND RETURN POP D POP H RET ; ; READ NEXT SECTOR FROM DISK ; DISKRD: MVI C,READF ;BDOS "READ SEC" FUNCTION LXI D,DFCB CALL BDOS ;READ SECTOR ORA A JNZ EOFERR ;ERROR IF PHYS END OF FILE STA BUFPTR ;STORE 0 AS NEW BUF PTR JMP GNB1 ;GO RE-JOIN GNB CODE ; ; LOAD A COM FILE ; LODCOM: INR M ;BUMP THE COMFILE FLAG LXI H,TPA ;SET ORIGIN CALL LODNIT ;AND INITIALIZE XCHG ;LOAD ADDRESS IN DE LHLD BIAS ;ADD IN BIAS DAD D XCHG LHLD OFFSET ;AND OFFSET DAD D XCHG ;DE HAS ABSOLUTE MEM ADRS OF LOAD ; COMLP: LXI H,128 ;CALCULATE NEXT DMA DAD D LDA SYSTEM+2 ;CHECK FOR SPACE CMP H JC MEMFUL ;JUMP IF NONE PUSH H ;ELSE SAVE NEXT DMA PUSH D ;AND THIS DMA MVI C,SDMAF ;SET THIS DMA CALL BDOS LXI D,DFCB ;READ NEXT RECORD MVI C,READF CALL BDOS POP H ;RECALL THIS DMA POP D ;DE=NEXT DMA ORA A ;END OF READ? JNZ LODEND ;JUMP IF SO LHLD COMSIZ ;NO, ADVANCE COM BYTE COUNT LXI B,128 DAD B SHLD COMSIZ JMP COMLP ;CONTINUE ; LODEND: DCX H ;ONE LESS BYTE IS HIGHEST SHLD HILOAD ;SET A NEW HIGH LHLD COMSIZ ;HI PC=BYTECOUNT+100H LXI D,TPA DAD D XCHG ;TO DE LHLD BIAS ;ADD IN BIAS DAD D SHLD HIPC LXI D,TBUF ;RESET DMA FOR HEX FILES MVI C,SDMAF CALL BDOS RET ; ; WRITE OUTPUT FILE ; WRTFIL: LXI D,DFCB ;POINT TO FCB PUSH D ;SAVE 2 COPIES OF POINTER PUSH D CALL NITFCB ;INITIALIZE OUTPUT FCB LXI H,OUTNAM ;MOVE OUTPUT NAME IN DCX D ;POINT TO USER # (PRIOR TO FCB) MVI B,10 ;MOVE USER, DRIVE, PRIMARY NAME CALL MOVE MOV A,M ;OUTPUT TYPE BLANK? CPI ' ' JNZ WRTNB ;JUMP IF NOT LXI H,OUTTYP ;YES, MOVE DFLT OUTPUT FILETYPE IN WRTNB: MVI B,3 CALL MOVE POP D ;RESTORE FCB POINTER MVI C,ERASEF ;ERASE ANY EXISTING FILE CALL BDOS POP D ;RESTORE FCB POINTER MVI C,CREATF ;CREATE A NEW FILE CALL BDOS INR A ;GOOD CREATE? JZ DIRFUL ;GOTO DIRECTORY FULL ERROR IF NOT LHLD HILOAD ;YEP, GET TOP OF BUFR PNTR XCHG ;IN DE LHLD FILBUF ;GET START OF BUFR ADRS MOV A,E ;CALCULATE OUTPUT FILE SIZE SUB L MOV C,A ;WITH RESULT IN BC MOV A,D SBB H MOV B,A MOV A,B ;TEST LENGTH ORA C JZ LODERR ;NOTHING TO WRITE??? LXI D,DFCB ;GET FCB POINTER WRLP: PUSH B ;SAVE COUNT PUSH D ;AND FCB POINTER XCHG ;GET MEMORY POINTER TO DE LXI H,128 ;ADD IN SECTOR LENGTH FOR NEXT PASS DAD D XTHL ;SAVE NEXT DMA PUSH H ;ABOVE FCB MVI C,SDMAF ;SET TRANSFER ADDRESS CALL BDOS POP D ;FETCH FCB POINTER PUSH D ;SAVE IT AGAIN MVI C,WRITEF ;WRITE A SECTOR CALL BDOS ORA A ;TEST RESULT JNZ DSKFUL ;DISK FULL ERROR... LHLD RECCNT ;NO,INCREMENT COUNT OF RECORDS INX H SHLD RECCNT POP D ;RESTORE FCB POINTER POP H ;AND MEMORY WRITE POINTER POP B ;AND COUNT MOV A,C ;SUBTRACT 128 (SEC SIZE) FROM COUNT SUI 128 MOV C,A JNC WRLP ;JUMP IF SOME LEFT MOV A,B ;HI-ORDER BORROW SUI 1 ;DO IT (CAN'T "DCR B", DOESN'T AFFECT CY) MOV B,A ;RESTORE JNC WRLP ;JUMP IF MORE LEFT CALL CLOSFL ;CLOSE OUTPUT FILE ; ; REPORT STATISTICS TO CONSOLE ; CALL ILPRNT DB 'Loaded ',0 LHLD BYTCNT ;PRINT # BYTES CALL DECOUT CALL ILPRNT DB ' bytes (',0 CALL HEXOUT CALL ILPRNT DB 'H)',0 CALL ILPRNT DB ' to file %',0 LDA COMFLG ;DID WE LOAD A COMFILE TOO? ORA A JZ NOTCOM ;JUMP IF NOT CALL ILPRNT DB CR,LF,'Over a ',0 LHLD COMSIZ CALL DECOUT CALL ILPRNT DB ' byte binary file',0 NOTCOM: CALL ILPRNT DB CR,LF,'Start address: ',0 LHLD LODADR ;PRINT LOADING ADDRESS CALL HEXOUT CALL ILPRNT DB 'H Ending address: ',0 LHLD HIPC ;PRINT ENDING LOAD ADDRESS CALL HEXOUT CALL ILPRNT DB 'H Bias: ',0 LHLD BIAS CALL HEXOUT CALL ILPRNT DB 'H',CR,LF,0 CALL ILPRNT DB 'Saved image size: ',0 LHLD RECCNT ;GET COUNT OF IMAGE RECORDS PUSH H ;SAVE IT MVI B,7 ;CONVERT TO BYTES XLP: DAD H DCR B JNZ XLP CALL DECOUT ;PRINT IT CALL ILPRNT DB ' bytes (',0 CALL HEXOUT ;NOW IN HEX CALL ILPRNT DB 'H, - ',0 POP H ;RECALL RECORD COUNT CALL DECOUT ;PRINT IT CALL ILPRNT DB ' records)',CR,LF,0 LHLD LODADR ;FETCH LOADING ADDRESS MOV A,L ;TEST IF =TPA ORA A JNZ NOTTPA ;TPA ALWAYS ON PAGE BOUNDARY MOV A,H ;LO OK, TEST HI CPI (TPA SHR 8) AND 0FFH RZ ;RETURN IF TPA NOTTPA: CALL ILPRNT ;NOT, SO PRINT WARNING MSG DB CR,LF,BEL DB '++ Warning: program origin NOT at 100H ++' DB CR,LF,0 RET ;DONE ; ; *********************** ; * UTILITY SUBROUTINES * ; *********************** ; ; ; ROUTINE TO CLOSE ANY OPEN FILE ; CLOSFL: LXI D,DFCB MVI C,CLOSEF CALL BDOS INR A ;TEST CLOSE RESULT JZ CLSERR ;JUMP IF ERROR RET ; ; PRINT MESSAGE IN-LINE WITH CODE ; ILPRNT: XTHL ;MESSAGE PNTR TO HL CALL PRATHL ;PRINT IT XTHL ;RESTORE AND RETURN RET ; ; PRINT MSG POINTED TO BY HL UNTIL NULL. EXPAND ; '%' CHAR TO CURRENT FILENAME. ; PRATHL: MOV A,M ;FETCH CHAR INX H ;POINT TO NEXT ORA A ;TERMINATOR? RZ ;THEN DONE CPI '%' ;WANT FILENAME? JZ PRTFN ;GO DO IT IF SO CALL TYPE ;NOPE, JUST PRINT CHAR JMP PRATHL ;CONTINUE ; PRTFN: PUSH H ;SAVE POINTER PUSH B LDA DFCB ;FETCH DR FIELD OF DFCB ORA A ;DEFAULT DRIVE? JNZ PRNDF ;JUMP IF NOT CALL GETDSK ;GET LOGGED-IN DRIVE # INR A ;MAKE IT ONE-RELATIVE (AS IN FCB) PRNDF: ADI 'A'-1 ;MAKE DRIVE NAME PRINTABLE CALL TYPE ;PRINT IT LDA DFCB-1 ;GET USER # CPI 0FFH ;NULL? CZ GETUSR ;IFF SO, GET CURRENT USER MOV L,A ;TO HL MVI H,0 CALL DECOUT ;PRINT IT MVI A,':' ;DRIVE NAMES FOLLOWED BY COLON CALL TYPE LXI H,DFCB+1 ;SETUP FOR NAME MVI B,8 ;PRINT UP TO 8 CALL PRTNAM MVI A,'.' ;PRINT DOT CALL TYPE MVI B,3 ;PRINT FILETYPE FIELD CALL PRTNAM POP B POP H ;RESTORE AND CONTINUE JMP PRATHL ; ; PRINT FILE NAME @HL MAX LENGTH IN B. DON'T PRINT SPACES ; PRTNAM: MOV A,M ;FETCH A CHAR CPI ' ' ;BLANK? JZ PWIND ;GO WIND IF SO INX H ;NOPE, MOVE TO NEXT CALL TYPE ;PRINT IT DCR B ;COUNT DOWN JNZ PRTNAM ;CONTINUE RET PWIND: INX H ;SKIP REMAINDER OF BLANK NAME DCR B JNZ PWIND RET ; ; PRINT HL IN DECIMAL ON CONSOLE ; DECOUT: PUSH H ;SAVE EVERYBODY PUSH D PUSH B LXI B,-10 ;CONVERSION RADIX LXI D,-1 DECLP: DAD B INX D JC DECLP LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOUT ;THIS IS RECURSIVE MOV A,E ADI '0' CALL TYPE POP B POP D POP H RET ; ; NEWLINE ON CONSOLE ; CRLF: MVI A,CR CALL TYPE MVI A,LF JMP TYPE ; ; PRINT HL ON CONSOLE IN HEX ; HEXOUT: MOV A,H ;GET HI CALL HEXBYT ;PRINT IT MOV A,L ;GET LO, FALL INTO HEXBYT ; ; TYPE ACCUMULATOR ON CONSOLE IN HEX ; HEXBYT: PUSH PSW ;SAVE BYTE RAR ;GET MS NYBBLE.. RAR ;..INTO LO 4 BITS RAR RAR CALL NYBBLE POP PSW ;GET BACK BYTE NYBBLE: ANI 0FH ;MASK MS NYBBLE ADI 90H ;ADD OFFSET DAA ;DECIMAL ADJUST A-REG ACI 40H ;ADD OFFSET DAA ;FALL INTO TYPE ; ; TYPE CHAR IN A ON CONSOLE ; TYPE: PUSH H ;SAVE ALL PUSH D PUSH B MOV E,A ;CP/M OUTPUTS FROM E MVI C,PCHARF CALL BDOS POP B POP D POP H RET ; ; MOVE: FROM @HL TO @DE, COUNT IN B ; MOVE: INR B ;UP ONE MOVLP: DCR B ;COUNT DOWN RZ ;RETURN IF DONE MOV A,M ;NOT DONE, CONTINUE STAX D INX H ;POINTERS=POINTERS+1 INX D JMP MOVLP ; ; SCAN TO FIRST NON-BLANK CHAR IN STRING @HL ; SCANBK: INX H ;NEXT MOV A,M ;FETCH IT CPI ' ' JZ SCANBK RET ; ; GET HEX DIGIT AND VALIDATE ; HEXVAL: CALL HEXDIG ;GET HEX DIGIT JC FORMERR ;JUMP IF BAD RET ; ; GET HEX DIGIT, RETURN CY=1 IF BAD DIGIT ; HEXDIG: CPI '0' ;LO BOUNDARY TEST RC ;BAD ALREADY? CPI '9'+1 ;NO, TEST HI JC HEXCVT ;JUMP IF NUMERIC CPI 'A' ;TEST ALPHA RC ;BAD? CPI 'F'+1 ;NO, UPPER ALPHA BOUND CMC ;PERVERT CARRY RC ;BAD? SUI 7 ;NO, ADJUST TO 0-F HEXCVT: ANI 0FH ;MAKE IT BINARY RET ; ; ****************** ; * ERROR HANDLERS * ; ****************** ; SYNERR: CALL CRLF CALL ILPRNT DB ' Command line syntax error',CR,LF,CR,LF,0 JMP HELP ;GIVE HELP MSG TOO ; AFNERR: CALL ERRXIT ;EXIT WITH MESSAGE DB 'Ambiguous file name: % not allowed.',0 ; FNFERR: CALL ERRXIT DB 'File % not found.',0 ; DSKFUL: CALL ERRXIT DB 'Disk full.',0 ; DIRFUL: CALL ERRXIT DB 'Directory full.',0 ; EOFERR: CALL ERRXIT DB 'Premature end-of-file in %',0 ; CSERR: CALL ERRXIT DB 'Checksum error in %',0 ; CLSERR: CALL ERRXIT DB 'Can''t close %',0 ; MEMFUL: CALL ERRXIT DB 'Memory full while loading %',0 ; FORMERR:CALL ERRXIT DB 'Format error in file %',0 ; LODERR: CALL ERRXIT DB 'Writing %, nothing loaded',0 ; HELP: CALL ERRXIT ;PRINT HELP TEXT DB 'MLOAD syntax:',CR,LF,CR,LF DB 'MLOAD [=] [,...] []',CR,LF DB TAB,' (brackets denote optional items)',CR,LF,CR,LF DB TAB,' is the optional output filename',CR,LF DB TAB,' are input file(s)',CR,LF DB TAB,' is a hex load offset within the output file' DB CR,LF,CR,LF DB TAB,' may be an optional non-HEX file to be patched',CR,LF DB TAB,'by subsequently named HEX files (specifying',CR,LF DB TAB,'The filetype enables this function).' DB CR,LF,CR,LF DB 'Note that ZCPR2-style drive/user notation may be used in all' DB CR,LF DB 'file specifications (e.g., "B3:MYFILE.COM, "A14:MDM7.HEX").' DB CR,LF,0 ; ; GENERAL ERROR HANDLER ; ERRXIT: CALL CRLF ;NEW LINE POP H ;FETCH ERROR MSG POINTER CALL PRATHL ;PRINT IT CALL CRLF JMP EXIT ;DONE ; ; INITIALIZE LOAD PARAMETERS ; LODNIT: MVI A,1 ;FIRST RECORD, SET LOAD FLAG STA LODFLG SHLD LODADR ;SAVE LOAD ADDRESS SHLD HIPC ;AND HI LOAD PUSH D ;SAVE RECORD LENGTH XCHG ;DE=LOAD ADDRESS LHLD FILBUF ;GET ADDRESS OF FILE BUFFER MOV A,L ;SUBTRACT LOAD ADRS FROM FILE BUFFER SUB E MOV L,A MOV A,H SBB D MOV H,A SHLD OFFSET ;SAVE AS LOAD OFFSET PUSH D ;SAVE LOAD ADDRESS ON STACK PUSH B ;SAVE BIAS LXI D,OUTNAM+2 ;CHECK OUTPUT FILENAME LDAX D ;(FIRST CHAR) CPI ' ' JNZ NAMSKP ;JUMP IF SO LXI H,DFCB+1 ;GET FIRST NAME POINTER MVI B,8 ;(DON'T INCLUDE DRIVE SPEC) CALL MOVE ; ; CHECK FOR OUTFLG=1 (PRESENCE OF AN "="). NOTE THAT THE ; FILENAME MAY WELL BE BLANK, AND YET OUTFLG <>0, FOR EXAMPLE ; IN THE CASE OF "A:=" OR "C4:=". IN ; THIS CASE, WE WANT TO REMEMBER THE DRIVE/USER SPECIFIED, BUT ; USE THE FIRST INPUT FILE TO FORM THE OUTPUT NAME. OTHERWISE, ; WE USE THE CURRENT DRIVE/USER. ; LDA OUTFLG ;WAS THERE AN "="? ORA A JNZ NAMSKP ;JUMP IF SO LXI H,OUTNAM ;GET DESTINATION POINTER CALL GETUSR ;GET CURRENT USER # MOV M,A INX H ;POINT TO DRIVE CALL GETDSK ;GET IT INR A ;FCB'S DRIVE IS 1-RELATIVE MOV M,A NAMSKP: MVI A,1 ;INSURE "=" CANNOT OCCUR ANYMORE STA OUTFLG POP B ;RESTORE BIAS POP H ;LOAD ADDRESS TO HL POP D ;RESTORE RECORD LENGTH RET ; ; ********************************* ; * FILE NAME PARSING SUBROUTINES * ; ********************************* ; ; CREDIT WHERE CREDIT'S DUE: ; -------------------------- ; THESE ROUTINES WERE LIFTED FROM BOB VAN VALZAH'S ; "FAST" PROGRAM. ; ; ; ; ********************************* ; * FILE NAME PARSING SUBROUTINES * ; ********************************* ; ; ; GETFN GETS A FILE NAME FROM TEXT POINTED TO BY REG HL INTO ; AN FCB POINTED TO BY REG DE. LEADING DELIMETERS ARE ; IGNORED. ALLOWS DRIVE SPEC OF THE FORM (DRIVE/USER). ; THIS ROUTINE FORMATS ALL 33 BYTES OF THE FCB (BUT NOT RAN REC). ; ; ENTRY DE FIRST BYTE OF FCB ; EXIT B=# OF '?' IN NAME ; FCB-1= USER # PARSED (IF SPECIFIED) OR 255 ; ; FPARSE: CALL NITFCB ;INIT 1ST HALF OF FCB CALL GSTART ;SCAN TO FIRST CHARACTER OF NAME CALL GETDRV ;GET DRIVE/USER SPEC. IF PRESENT MOV A,B ;GET USER # OR 255 CPI 0FFH ;255? JZ FPARS1 ;JUMP IF SO DCX D ;BACK UP TO BYTE PRECEEDING FCB DCX D STAX D ;STUFF USER # INX D ;ONWARD INX D FPARS1: CALL GETPS ;GET PRIMARY AND SECONDARY NAME RET ; ; NITFCB FILLS THE FCB WITH DFLT INFO - 0 IN DRIVE FIELD ; ALL-BLANK IN NAME FIELD, AND 0 IN EX,S1,S2,RC, DISK ; ALLOCATION MAP, AND RANDOM RECORD # FIELDS ; NITFCB: PUSH H PUSH D CALL GETUSR ;INIT USER FIELD POP D POP H PUSH D ;SAVE FCB LOC DCX D STAX D ;INIT USER # TO CURRNT USER # INX D XCHG ;MOVE IT TO HL MVI M,0 ;DRIVE=DEFAULT INX H ;BUMP TO NAME FIELD MVI B,11 ;ZAP ALL OF NAME FLD NITLP: MVI M,' ' INX H DCR B JNZ NITLP MVI B,33-11 ;ZERO OTHERS, UP TO NR FIELD ZLP: MVI M,0 INX H DCR B JNZ ZLP XCHG ;RESTORE HL POP D ;RESTORE FCB POINTER RET ; ; GSTART ADVANCES THE TEXT POINTER (REG HL) TO THE FIRST ; NON DELIMITER CHARACTER (I.E. IGNORES BLANKS). RETURNS A ; FLAG IF END OF LINE (00H OR ';') IS FOUND WHILE SCANING. ; EXIT HL POINTING TO FIRST NON DELIMITER ; A CLOBBERED ; ZERO SET IF END OF LINE WAS FOUND ; GSTART: CALL GETCH ;SEE IF POINTING TO DELIM? RNZ ;NOPE - RETURN ORA A ;PHYSICAL END? RZ ;YES - RETURN W/FLAG INX H ;NOPE - MOVE OVER IT JMP GSTART ;AND TRY NEXT CHAR ; ; GETDRV CHECKS FOR THE PRESENCE OF A DU: SPEC AT THE TEXT ; POINTER, AND IF PRESENT FORMATS DRIVE INTO FCB AND RETURNS ; USER # IN B. ; ; ENTRY HL TEXT POINTER ; DE POINTER TO FIRST BYTE OF FCB ; EXIT HL POSSIBLY UPDATED TEXT POINTER ; DE POINTER TO SECOND (PRIMARY NAME) BYTE OF FCB ; B USER # IF SPECIFIED OR 0FFH ; GETDRV: MVI B,0FFH ;DEFAULT NO USER # PUSH H ;SAVE TEXT POINTER DSCAN: CALL GETCH ;GET NEXT CHAR INX H ;SKIP POINTER OVER IT JNZ DSCAN ;SCAN UNTIL DELIMITER CPI ':' ;DELIMITER A COLON? INX D ;SKIP DR FIELD IN FCB IN CASE NOT POP H ;AND RESTORE TEXT POINTER RNZ ;RETURN IF NO DU: SPEC MOV A,M ;GOT ONE, GET FIRST CHAR CALL CVTUC ;MAY BE DRIVE NAME, CVT TO UPPER CASE CPI 'A' ;ALPHA? JC ISNUM ;JUMP TO GET USER # IF NOT SUI 'A'-1 ;YES, CONVERT FROM ASCII TO # DCX D ;BACK UP FCB POINTER TO DR FIELD STAX D ;STORE DRIVE # INTO FCB INX D ;PASS POINTER OVER DRV INX H ;SKIP DRIVE SPEC IN TEXT ISNUM: MOV A,M ;FETCH NEXT INX H CPI ':' ;DU DELIMITER? RZ ;DONE THEN DCX H ;NOPE, BACK UP TEXT POINTER MVI B,0 ;GOT A DIGIT, INIT USER VALUE ULOOP: MOV A,B ;GET ACCUMULATED USER # ADD A ;* 10 FOR NEW DIGIT ADD A ADD B ADD A MOV B,A ;BACK TO B MOV A,M ;GET TEXT CHAR SUI '0' ;MAKE BINARY ADD B ;ADD TO USER # MOV B,A ;UPDATED USER # INX H ;SKIP OVER IT MOV A,M ;GET NEXT CPI ':' ;END OF SPEC? JNZ ULOOP ;JUMP IF NOT INX H ;YEP, RETURN TXT POINTER PAST DU: RET ; ; GETPS GETS THE PRIMARY AND SECONDARY NAMES INTO THE FCB. ; ENTRY HL TEXT POINTER ; EXIT HL CHARACTER FOLLOWING SECONDARY NAME (IF PRESENT) ; GETPS: MVI C,8 ;MAX LENGTH OF PRIMARY NAME MVI B,0 ;INIT COUNT OF '?' CALL GETNAM ;PACK PRIMARY NAME INTO FCB MOV A,M ;SEE IF TERMINATED BY A PERIOD CPI '.' RNZ ;NOPE - SECONDARY NAME NOT GIVEN ;RETURN DEFAULT (BLANKS) INX H ;YUP - MOVE TEXT POINTER OVER PERIOD FTPOINT:MOV A,C ;YUP - UPDATE FCB POINTER TO SECONDARY ORA A JZ GETFT INX D DCR C JMP FTPOINT GETFT: MVI C,3 ;MAX LENGTH OF SECONDARY NAME CALL GETNAM ;PACK SECONDARY NAME INTO FCB RET ; ; GETNAM COPIES A NAME FROM THE TEXT POINTER INTO THE FCB FOR ; A GIVEN MAXIMUM LENGTH OR UNTIL A DELIMITER IS FOUND, WHICH ; EVER OCCURS FIRST. IF MORE THAN THE MAXIMUM NUMBER OF ; CHARACTERS IS PRESENT, CHARACTER ARE IGNORED UNTIL A ; A DELIMITER IS FOUND. ; ENTRY HL FIRST CHARACTER OF NAME TO BE SCANNED ; DE POINTER INTO FCB NAME FIELD ; C MAXIMUM LENGTH ; EXIT HL POINTING TO TERMINATING DELIMITER ; DE NEXT EMPTY BYTE IN FCB NAME FIELD ; C MAX LENGTH - NUMBER OF CHARACTERS TRANSFERED ; GETNAM: CALL GETCH ;ARE WE POINTING TO A DELIMITER YET? RZ ;IF SO, NAME IS TRANSFERED INX H ;IF NOT, MOVE OVER CHARACTER CPI '*' ;AMBIGIOUS FILE REFERENCE? JZ AMBIG ;IF SO, FILL THE REST OF FIELD WITH '?' CPI '?' ;AFN REFERENCE? JNZ NOTQM ;SKIP IF NOT INR B ;ELSE BUMP AFN COUNT NOTQM: CALL CVTUC ;IF NOT, CONVERT TO UPPER CASE STAX D ;AND COPY INTO NAME FIELD INX D ;INCREMENT NAME FIELD POINTER DCR C ;IF NAME FIELD FULL? JNZ GETNAM ;NOPE - KEEP FILLING JMP GETDEL ;YUP - IGNORE UNTIL DELIMITER AMBIG: MVI A,'?' ;FILL CHARACTER FOR WILD CARD MATCH FILLQ: STAX D ;FILL UNTIL FIELD IS FULL INX D INR B ;INCREMENT COUNT OF '?' DCR C JNZ FILLQ ;FALL THRU TO INGORE REST OF NAME GETDEL: CALL GETCH ;POINTING TO A DELIMITER? RZ ;YUP - ALL DONE INX H ;NOPE - IGNORE ANTOHER ONE JMP GETDEL ; ; GETCH GETS THE CHARACTER POINTED TO BY THE TEXT POINTER ; AND SETS THE ZERO FLAG IF IT IS A DELIMITER. ; ENTRY HL TEXT POINTER ; EXIT HL PRESERVED ; A CHARACTER AT TEXT POINTER ; Z SET IF A DELIMITER ; GETCH: MOV A,M ;GET THE CHARACTER, TEST FOR DELIM ; ; GLOBAL ENTRY: TEST CHAR IN A FOR FILENAME DELIMITER ; FNDELM: CPI '/' RZ CPI '.' RZ CPI ',' RZ CPI ' ' RZ CPI ':' RZ CPI '=' RZ ORA A ;SET ZERO FLAG ON END OF TEXT RET ; ; BDOS ENTRY: PRESERVES BC, DE. IF SYSTEM CALL IS A FILE ; FUNCTION, THIS ROUTINE LOGS INTO THE DRIVE/ ; USER AREA SPECIFIED, THEN LOGS BACK AFTER ; THE CALL. ; BDOS: CALL FILFCK ;CHECK FOR A FILE FUNCTION JNZ BDOS1 ;JUMP IF NOT A FILE FUNCTION CALL GETDU ;GET DRIVE/USER SHLD SAVEDU LDAX D ;GET FCB'S DRIVE STA FCBDRV ;SAVE IT DCR A ;MAKE 0-RELATIVE JM BDOS0 ;IF NOT DEFAULT DRIVE, JUMP MOV H,A ;COPY TO H BDOS0: XRA A ;SET FCB TO DEFAULT STAX D DCX D ;GET FCB'S USER # LDAX D MOV L,A INX D ;RESTORE DE CALL SETDU ;SET FCB'S USER ; ; NOTE THAT UNSPECIFIED USER # (VALUE=0FFH) BECOMES ; A GETUSR CALL, PREVENTING AMBIGUITY. ; CALL BDOS1 ;DO USER'S SYSTEM CALL PUSH PSW ;SAVE RESULT PUSH H LDA FCBDRV ;RESTORE FCB'S DRIVE STAX D LHLD SAVEDU ;RESTORE PRIOR DRIVE/USER CALL SETDU POP H ;RESTORE BDOS RESULT REGISTERS POP PSW RET ; ; LOCAL VARIABLES FOR BDOS REPLACEMENT ROUTINE ; SAVEDU: DW 0 ;SAVED DRIVE,USER FCBDRV: DB 0 ;FCB'S DRIVE DMADR: DW 80H ;CURRENT DMA ADRS ; BDOS1: PUSH D PUSH B MOV A,C ;DOING SETDMA? CPI SDMAF JNZ BDOS1A ;JUMP IF NOT XCHG ;YEP, KEEP A RECORD OF DMA ADDRESSES SHLD DMADR XCHG BDOS1A: CALL SYSTEM POP B POP D RET ; ; GET DRIVE, USER: H=DRV, L=USER ; GETDU: PUSH B ;DON'T MODIFY BC PUSH D MVI C,GSUSER ;GET USER # MVI E,0FFH CALL BDOS1 PUSH PSW ;SAVE IT MVI C,GETDRF ;GET DRIVE CALL BDOS1 MOV H,A ;DRIVE RETURNED IN H POP PSW MOV L,A ;USER IN L POP D POP B ;RESTORE CALLER'S BC RET ; ; SET DRIVE, USER: H=DRV, L=USER ; SETDU: PUSH B ;DON'T MODIFY BC PUSH D PUSH H ;SAVE INFO MOV E,H ;DRIVE TO E MVI C,SELDF ;SET IT CALL BDOS1 POP H ;RECALL INFO PUSH H MOV E,L ;USER # TO E MVI C,GSUSER CALL BDOS1 ;SET IT POP H POP D POP B RET ; ; CHECK FOR FILE-FUNCTION: OPEN, CLOSE, READ RANDOM, WRITE ; RANDOM, READ SEQUENTIAL, WRITE SEQUENTIAL. ; FILFCK: MOV A,C ;GET FUNCTION # CPI OPENF RZ RC ;IGNORE LOWER FUNCTION #'S CPI CLOSEF ;(THEY'RE NOT FILE-RELATED) RZ CPI READF RZ CPI WRITEF RZ CPI RRAND RZ CPI WRAND RZ CPI FSRCHF RZ CPI FSRCHN RZ CPI ERASEF RZ CPI CREATF RZ CPI FILSZF RZ CPI SRAND RET ; ; CONVERT CHAR TO UPPER CASE ; CVTUC: CPI 'a' ;CHECK LO BOUND RC CPI 'z'+1 ;CHECK HI RNC SUI 20H ;CONVERT RET ; ; CHECK FOR HEX FILETYPE IN FCB NAME ; HEXCHK: PUSH H PUSH D PUSH B MVI B,3 ;TYPE IS 3 CHARS LXI D,DFCB+9 ;POINT DE TO TYPE FIELD LXI H,HEXTYP ;POINT HL TO "COM" HEXLOP: LDAX D ANI 7FH ;IGNORE ATTRIBUTES CMP M INX H INX D JNZ HEXIT ;JUMP IF NOT COM DCR B JNZ HEXLOP HEXIT: POP B ;Z REG HAS RESULT POP D POP H RET ; HEXTYP: DB 'HEX' ; ; ROUTINE TO RETURN USER # WITHOUT DISTURBING REGISTERS ; GETUSR: PUSH H PUSH D PUSH B MVI C,GSUSER MVI E,0FFH CALL BDOS POP B POP D POP H RET ; ; ROUTINE TO RETURN DRIVE # WITHOUT DISTURBING REGISTERS ; GETDSK: PUSH H PUSH D PUSH B MVI C,GETDRF CALL BDOS POP B POP D POP H RET ; ; THESE ARE THE INITIAL VALUES OF THE VARIABLES, AND ; ARE MOVED INTO THE VARIABLES AREA BY THE SETUP ROUTINE. ; IF YOU ADD VARIABLES, BE SURE TO ADD THEIR INTIAL VALUE ; INTO THIS TABLE IN THE ORDER CORRESPONDING TO THEIR ; OCCURANCE IN THE VARIABLES SECTION. ; VARSET: DW 0 ;BIAS DW 0 ;HILOAD DW 0 ;HIPC DB 0 ;CKSUM DW CMDBUF ;CMDPTR DB 0 ;BUFPTR DB 0 ;LODFLG DW CMDBUF ;FILBUF DW 0 ;OFFSET DW 0 ;LODADR DB 0,0,' ' ;OUTNAM DW 0 ;RECCNT DW 0 ;BYTCNT DB 0 ;COMFLG DW 0 ;COMSIZ DB 0 ;OUTFLG ; VARLEN EQU $-VARSET ;DEFINE LENGTH OF INIT TABLE ; ; WORKING VARIABLES ; VARS EQU $ ;DEFINE VARIABLES AREA START ; BIAS: DS 2 ;LOAD OFFSET HILOAD: DS 2 ;HIGHEST TRUE LOAD ADDRESS HIPC: DS 2 ;HIGHEST PC CKSUM: DS 1 ;RECORD CHECKSUM CMDPTR: DS 2 ;COMMAND LINE POINTER BUFPTR: DS 1 ;INPUT BUFFER POINTER LODFLG: DS 1 ;SOMETHING-LOADED FLAG FILBUF: DS 2 ;FILE BUFFER LOCATION OFFSET: DS 2 ;LOAD OFFSET INTO BUFFER LODADR: DS 2 ;LOAD ADDRESS OUTNAM: DS 13 ;OUTPUT DRIVE+NAME RECCNT: DS 2 ;OUTPUT FILE RECORD COUNT BYTCNT: DS 2 ;OUTPUT FILE BYTES LOADED COUNT COMFLG: DS 1 ;FLAGS COM FILE ENCOUNTERED COMSIZ: DS 2 ;SIZE OF A LOADED COM FILE OUTFLG: DS 1 ;FLAGS AN "=" PRESENT IN CMD LINE ; ; END OF WORKING VARIABLES ; ; ; ; STACK STUFF ; SPSAVE: DS 2 ;SYSTEM STACK PNTR SAVE ; ; DS 100 ;50-LEVEL STACK ; STACK EQU $ CMDBUF EQU $ ;COMMAND BUFFER LOCATION ; ; END