home *** CD-ROM | disk | FTP | other *** search
- TITLE 'MLOAD MULTI-FILE HEX LOAD UTILITY'
- ;
- ; *********************************
- ; * MLOAD.ASM *
- ; * MULTI-FILE HEX LOAD UTILITY *
- ; * FOR CP/M *
- ; *********************************
- ;
- ; REV 1.4
- ; 05/29/83
- ;
- ;
- ; MODIFICATION HISTORY:
- ;
- ; 1.4 (RGF) REPAIRED BUG WHICH SHOWED UP AS IMPROPER FILE
- ; LOADING IF "N:<FILENAME>" SPECIFIED (I.E., THE
- ; INPUT DRIVE IS NOT THE DEFAULT DRIVE). MODIFIED
- ; TO ALLOW THE OUTPUT FILE TYPE TO BE SPECIFIED.
- ; (THE STRING AT "OUTTYP" IS NOW THE DEFAULT, RATHER
- ; THAN THE FORCED, FILETYPE). A NEW SYNTAX PARSING
- ; OPTION ALLOWS COMMAND LINES OF THE FORM
- ; D:=<FILENAME>
- ; WHICH SENDS THE OUTPUT FILE TO DRIVE D. FINALLY,
- ; THE OUTPUT FILE NOW ALWAYS LANDS ON THE DEFAULT
- ; DRIVE, UNLESS SPECIFICALLY SPECIFIED OTHERWISE IN
- ; THE COMMAND LINE.
- ;
- ; 1.3 (RGF) "UN-CONCEALED" COPYRIGHT NOTICE. I DON'T MIND
- ; PEOPLE ENHANCING, OR FIXING BUGS IN, MY PROGRAMS.
- ; WHAT I *DO* MIND, HOWEVER, IS TAMPERING WITH
- ; THE AUTHOR'S NAME (HIS STOCK IN TRADE). MY INTENT
- ; WAS THAT THE DISTRIBUTION VERSION SIGN ON WITH MY
- ; COPYRIGHT NOTICE -- I DON'T CARE (NOR CAN I CONTROL)
- ; WHAT MODS A USER MAKES TO HIS OWN VERSION OF THE
- ; PROGRAM, BUT I MUST INSIST THAT THE DISTRIBUTION
- ; VERSION CARRY MY COPYRIGHT NOTICE EXACTLY IN THE
- ; WAY I ORIGINALLY INTENDED.
- ; --RON FOWLER
- ; 05/11/83
- ;
- ; 1.2 (FJG) FIXED STACK BUG WHEN LOADING LARGE HEX FILES.
- ; MODIFIED HEXOUT ROUTINE TO USE DAA INSTRUCTION.
- ; CONCEALED COPYRIGHT NOTICE.
- ;
- ; 1.1 (RGF) ADDED WARNING MSG IF FILE LOAD NOT AT 100H (THIS
- ; IS TO ALERT THE USER WHO HAS INADVERTANTLY LEFT
- ; OUT AN 'ORG 100H' FROM THE SOURCE FILE, AND SHOULD
- ; BE IGNORED IF THE LOAD ADDRESS IS PURPOSELY NOT
- ; 100H). FIXED MINOR BUG WITH ONE OF THE ERROR MSGS.
- ;
- ; 1.0 (RGF) 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
- ; (414) 563-9932 (NO RING BACK)
- ;
- ; --RON FOWLER
- ; 02/21/83
- ;
- ;------------------------------------------------------------
- ;
- ; CP/M EQUATES
- ;
- WARMBT EQU 0 ;WARM BOOT
- BDOS 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
- OPENF EQU 15 ;OPEN FILE
- CLOSEF EQU 16 ;CLOSE FILE
- 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
- ;
- ; MISCELLANEOUS EQUATES
- ;
- CR EQU 13
- LF EQU 10
- BEL EQU 7
- ;
- ; WITHOUT FURTHER ADO...
- ;
- ORG TPA
- ;
- JMP BEGIN ;JUMP OVER DEFAULT OUTPUT FILETYPE
- ;
- ; THE OUTPUT FILETYPE IS LOCATED AT 103H FOR EASY PATCHING WITH DDT
- ;
- 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. 1.4 Copyright (C) 1983 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 BDOS+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: XRA A ;GET A ZERO IN A
- STA LODFLG ;FLAG NO FILES LOADED YET
- STA BUFPTR ;FORCE A DISK READ
- LXI H,OUTNAM ;SET OUTPUT NAME DR FIELD DEFAULT DRV
- MOV M,A ;ZERO TYPE
- INX H
- MVI B,11 ;BLANK NAME
- CLOFN: MVI M,' '
- INX H ;INIT OUTPUT NAME
- DCR B
- JNZ CLOFN
- MVI M,' ' ;INIT OUTPUT NAME TO NULL
- LXI H,0 ;NO BIAS YET
- SHLD BIAS
- SHLD RECCNT ;NO RECORDS WRITTEN YET
- SHLD BYTCNT ;NO BYTES LOADED YET
- LXI H,CMDBUF ;GET POINTER TO COMMAND BUFFER
- SHLD CMDPTR ;SAVE
- XCHG ;CMDBUF 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 BDOS+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
- LXI H,CMDBUF-1 ;POINT TO COMMAND BUFFER
- 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
- ;
- ; SET UP NEXT INPUT FILE
- ;
- NXTFIL: LHLD CMDPTR ;GET COMMAND LINE POINTER
- NEXT2: LXI D,DFCB ;DESTINATION FCB
- CALL FPARSE ;PARSE A FILENAME
- CPI '=' ;OUTPUT SPECIFIER?
- JNZ NOTEQ
- LDA OUTNAM+1 ;INSURE NO NAME YET SPECIFIED
- CPI ' '
- JNZ SYNERR ;SYNTAX ERROR IF SPECIFIED TWICE
- INR B ;INSURE NO AMBIGUOUS OUTPUT NAME
- DCR B
- JNZ AFNERR
- INX H ;SKIP OVER '='
- PUSH H ;SAVE CMD LINE POINTER
- LXI H,DFCB ;MOVE THE NAME TO OUTPUT NAME HOLD
- LXI D,OUTNAM
- MVI B,12 ;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
- LXI H,DFCB+FT ;POINT TO FILE TYPE
- MOV A,M ;ANYTHING THERE?
- CPI ' '
- JNZ OPENIT ;JUMP IF SO
- MVI M,'H' ;NOPE, FILL IN 'HEX'
- INX H
- MVI M,'E'
- INX H
- MVI M,'X'
- OPENIT: MVI C,OPENF ;OPEN THE FILE
- CALL BDOS
- INR A ;DID IT OPEN OK?
- JZ FNFERR ;OOPS...
- XRA A ;YEP, RESET CY ("INR A" IGNORES CARRY)
- RET
- ;
- ; LOAD CURRENT FILE
- ;
- LODFIL: LHLD BIAS ;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
- JNZ SKPNIT ;NOT FIRST RECORD, JUMP
- 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+1 ;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
- NAMSKP: POP B ;RESTORE BIAS
- POP H ;LOAD ADDRESS TO HL
- POP D ;RESTORE RECORD LENGTH
- SKPNIT: 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 BDOS+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
- ;
- ; 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
- MVI B,9
- 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
- INR A ;TEST RESULT
- JZ 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
- 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
- LHLD BYTCNT ;ALSO PRINT IN HEX
- CALL HEXOUT
- CALL ILPRNT
- DB 'H - ',0
- LHLD RECCNT ;PRINT # RECORDS
- CALL DECOUT
- CALL ILPRNT
- DB ' records) to file %'
- 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
- 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
- LXI H,DFCB ;POINT TO DR FIELD OF DFCB
- MOV A,M ;FETCH IT
- INX H ;ADVANCE TO NAME FIELD
- ORA A ;DEFAULT DRIVE?
- JNZ PRNDF ;JUMP IF NOT
- PUSH H ;YES, SAVE FCB POINTER
- MVI C,GETDRF ;GET DFLT DRV #
- CALL BDOS
- POP H
- INR A ;MAKE IT ONE-RELATIVE (AS IN FCB)
- PRNDF: ADI 'A'-1 ;MAKE DRIVE NAME PRINTABLE
- CALL TYPE ;PRINT IT
- MVI A,':' ;DRIVE NAMES FOLLOWED BY COLON
- CALL TYPE
- 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 ' (brackets denote optional items)',CR,LF
- DB ' <OUTFIL> is the optional output filename',CR,LF
- DB ' <FILEn> are input file(s)',CR,LF
- DB ' <BIAS> is a load offset (in hex) within the output file'
- 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
- ;
- ;
- ; *********************************
- ; * FILE NAME PARSING SUBROUTINES *
- ; *********************************
- ;
- ; CREDIT WHERE CREDIT'S DUE:
- ; --------------------------
- ; THESE ROUTINES WERE LIFTED FROM BOB VAN VALZAH'S
- ; "FAST" PROGRAM.
- ;
- ;
- ; GETFN GETS A FILE NAME FROM TEXT POINTED TO BY REG HL INTO
- ; AN FCB POINTED TO BY REG DE. LEADING DELIMETERS ARE
- ; IGNORED.
- ; ENTRY DE FIRST BYTE OF FCB
- ; EXIT B=# OF '?' IN NAME
- ;
- ;
- FPARSE: CALL NITFCB ;INIT 1ST HALF OF FCB
- MVI B,0 ;INIT AMBIG REF COUNTER
- CALL GSTART ;SCAN TO FIRST CHARACTER OF NAME
- RZ ;END OF LINE WAS FOUND - LEAVE FCB BLANK
- CALL GETDRV ;GET DRIVE SPEC. IF PRESENT
- 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 D ;SAVE FCB LOC
- XCHG ;MOVE IT TO HL
- MVI M,0 ;ZAP DR FIELD
- INX H ;BUMP TO NAME FIELD
- MVI B,11 ;ZAP ALL OF NAME FLD
- FNITLP: MVI M,' '
- INX H
- DCR B
- JNZ FNITLP
- MVI B,FCBSIZ-12 ;ZERO OTHERS, UP TO NR FIELD
- FNIT2: MVI M,0
- INX H
- DCR B
- JNZ FNIT2
- 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
- RZ ;YUP - RETURN W/FLAG
- INX H ;NOPE - MOVE OVER IT
- JMP GSTART ;AND TRY NEXT CHAR
- ;
- ; GETDRV CHECKS FOR THE PRESENCE OF A DRIVE SPEC AT THE TEXT
- ; POINTER, AND IF PRESENT FORMATS IT INTO THE FCB AND
- ; ADVANCES THE TEXT POINTER OVER IT.
- ; 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
- ;
- GETDRV: INX D ;POINT TO NAME IF SPEC NOT FOUND
- INX H ;LOOK AHEAD TO SEE IF ':' PRESENT
- MOV A,M
- DCX H ;PUT BACK IN CASE NOT PRESENT
- CPI ':' ;IS A DRIVE SPEC PRESENT?
- RNZ ;NOPE - RETURN
- MOV A,M ;YUP - GET THE ASCII DRIVE NAME
- SUI 'A'-1 ;CONVERT TO FCB DRIVE SPEC
- DCX D ;POINT BACK TO DRIVE SPEC BYTE
- STAX D ;STORE SPEC INTO FCB
- INX D ;POINT BACK TO NAME
- INX H ;SKIP OVER DRIVE NAME
- INX H ;AND OVER ':'
- 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
- 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 NOTQ ;SKIP IF NOT
- INR B ;ELSE BUMP AFN COUNT
- NOTQ: 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
- CPI '/'
- RZ
- CPI '.'
- RZ
- CPI ','
- RZ
- CPI ' '
- RZ
- CPI ':'
- RZ
- CPI '='
- RZ
- ORA A ;SET ZERO FLAG ON END OF TEXT
- RET
- ;
- ; VARIABLES
- ;
- SPSAVE: DS 2 ;SYSTEM STACK PNTR SAVE
- 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 12 ;OUTPUT DRIVE+NAME
- RECCNT: DS 2 ;OUTPUT FILE RECORD COUNT
- BYTCNT: DS 2 ;OUTPUT FILE BYTES LOADED COUNT
- ;
- DS 80 ;40-LEVEL STACK
- ;
- STACK EQU $
- CMDBUF EQU $ ;COMMAND BUFFER LOCATION
- ;
- ;
- END