home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
old
/
misc
/
cpmtools
/
mload.asm
< prev
next >
Wrap
Assembly Source File
|
2020-01-01
|
35KB
|
1,453 lines
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 [<OUTNAM=] <FILENAME>[,<FILENAME>...] [BIAS]
;
; WHERE <OUTNAM IS THE (OPTIONAL!;) OUTPUT FILE NAME (ONLY THE DRIVE
; SPEC AND PRIMARY FILENAME MAY BE SPECIFIED; THE OUTPUT FILETYPE IS
; DERIVED EXCLUSIVELY FROM THE 3-BYTE STRING AT 103H WITHIN MLOAD),
; <FILENAME> SPECIFIES FILES TO LOAD AND <BIAS> 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:=<FILENAME>" 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 [<OUTFIL>=] <FILE1>[,<FILE2>...] [<BIAS>]',CR,LF
DB TAB,' (brackets denote optional items)',CR,LF,CR,LF
DB TAB,'<OUTFIL> is the optional output filename',CR,LF
DB TAB,'<FILEn> are input file(s)',CR,LF
DB TAB,'<BIAS> is a hex load offset within the output file'
DB CR,LF,CR,LF
DB TAB,'<FILE1> 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:=<FILENAME>" OR "C4:=<FILENAME>". 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 <DU:> (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