0FAH: DECLARE BDOS LITERALLY '3206H'; /* TO COMPILE THIS LOADER FOR CP/M 2 USE: */ /*0FAH: DECLARE BDOS LITERALLY '5';*/ /* TRANSIENT COMMAND LOADER PROGRAM AUTHOR UNKNOWN, IT MOST LIKELY WAS WRITTEN BY GARY KILDALL AT NARVAL POSTGRADUATE SCHOOL. OLD FRIEND, THE INDENTATION YOU LEFT FOR US IS A MESS. I HAVE FIXED THAT. NOW LOOK THROUGH MY EYES AT THE BEAUTY OF THE SOURCE, I HOPE THIS IS OK WITH YOU, AND IF NOT... TOO BAD. NOTE TO THOSE WHO MESS WITH THE SOURCES AFTER ME: A FRIEND, J.R.D., USED TO SAY: OK, I FIXED THAT FOR YOU. IF YOU BREAK IT AGAIN, I WILL BREAK YOUR FINGERS. THE ORIGINAL PROGRAM INCLUDES LIBRARY ROUTINES FROM ANOTHER FILE, IOLIB.PLM, WHICH IS LOST. IN A DISCUSSION ON C.O.C JEFFREY W. SHOOK POINTED OUT, THAT THE LIBRARY ROUTINES INCLUDED IN CCP.PLM MAKE A GOOD SUBSTITUTE. THIS ROUTINES ARE USED HERE, THANKS JEFF. THE ORIGINAL PROGRAM LOOPED ENDLESS IF THE READER WAS NOT READY AND RETURNED CTRL-Z (EOF). THIS IS FIXED. IF THE ORIGINAL PROGRAM WAS USED WITHOUT A FILENAME, IT CREATED A BOGUS ONE. THIS IS FIXED, THIS VERSION CREATES THE FILE $$$.COM IN THIS CASE. IF YOU DO NOT HAVE A LOADER THAT CAN LOAD A HEX FILE ORIGINATED AT 0FAH, YOU HAVE A BOOTSTRAP PROBLEM THAT CAN BE SOLVED: - COMPILE FIRST VERSION ORIGINATED AT 0100H - USE DDT AND PATCH FIRST 6 BYTES WITH NOP - COMPILE SECOND VERSION ORIGINATED AT 0FAH - USE FIRST VERSION TO LOAD SECOND ONE OCTOBER 2007, UDO MUNK */ LOADCOM: PROCEDURE BYTE; DECLARE FCBA ADDRESS INITIAL(5CH); DECLARE FCB BASED FCBA (33) BYTE; DECLARE BUFFA ADDRESS INITIAL(80H), /* I/O BUFFER ADDRESS */ BUFFER BASED BUFFA (128) BYTE; DECLARE SFCB(33) BYTE, /* SOURCE FILE CONTROL BLOCK */ BSIZE LITERALLY '1024', EOFILE LITERALLY '1AH', SBUFF(BSIZE) BYTE /* SOURCE FILE BUFFER */ INITIAL(EOFILE), RFLAG BYTE, /* READER FLAG */ SBP ADDRESS; /* SOURCE FILE BUFFER POINTER */ /* LOADCOM LOADS TRANSIENT COMMAND FILES TO THE DISK FROM THE CURRENTLY DEFINED READER PERIPHERAL. THE LOADER PLACES THE MACHINE CODE INTO A FILE WHICH APPEARS IN THE LOADCOM COMMAND */ /* **************** LIBRARY PROCEDURES FOR DISKIO ************* */ MON1: PROCEDURE(F,A); DECLARE F BYTE, A ADDRESS; GO TO BDOS; END MON1; MON2: PROCEDURE(F,A) BYTE; DECLARE F BYTE, A ADDRESS; GO TO BDOS; END MON2; READRDR: PROCEDURE BYTE; /* READ FROM CURRENTLY ASSIGNED READER DEVICE */ RETURN MON2(3,0); END READRDR; READCHAR: PROCEDURE BYTE; RETURN MON2(1,0); END READCHAR; DECLARE TRUE LITERALLY '1', FALSE LITERALLY '0', FOREVER LITERALLY 'WHILE TRUE', CR LITERALLY '13', LF LITERALLY '10', WHAT LITERALLY '63'; PRINTCHAR: PROCEDURE(CHAR); DECLARE CHAR BYTE; CALL MON1(2,CHAR); END PRINTCHAR; CRLF: PROCEDURE; CALL PRINTCHAR(CR); CALL PRINTCHAR(LF); END CRLF; PRINT: PROCEDURE(A); DECLARE A ADDRESS; /* PRINT THE STRING STARTING AT ADDRESS A UNTIL THE NEXT DOLLAR SIGN IS ENCOUNTERED */ CALL CRLF; CALL MON1(9,A); END PRINT; READ: PROCEDURE(A); DECLARE A ADDRESS; /* READ INTO BUFFER AT A+2 */ CALL MON1(10,A); END READ; DECLARE DCNT BYTE; INITIALIZE: PROCEDURE; CALL MON1(13,0); END INITIALIZE; SELECT: PROCEDURE(D); DECLARE D BYTE; CALL MON1(14,D); END SELECT; OPEN: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(15,FCB); END OPEN; CLOSE: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(16,FCB); END CLOSE; SEARCH: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(17,FCB); END SEARCH; SEARCHN: PROCEDURE; DCNT = MON2(18,0); END SEARCHN; DELETE: PROCEDURE(FCB); DECLARE FCB ADDRESS; CALL MON1(19,FCB); END DELETE; DISKREAD: PROCEDURE(FCB) BYTE; DECLARE FCB ADDRESS; RETURN MON2(20,FCB); END DISKREAD; DISKWRITE: PROCEDURE(FCB) BYTE; DECLARE FCB ADDRESS; RETURN MON2(21,FCB); END DISKWRITE; MAKE: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(22,FCB); END MAKE; RENAME: PROCEDURE(FCB); DECLARE FCB ADDRESS; CALL MON1(23,FCB); END RENAME; DECLARE (MAXLEN,COMLEN) BYTE, /* MAXIMUM AND CURRENT LENGTH */ COMBUFF(128) BYTE, /* COMMAND BUFFER */ (TCBP,CBP) BYTE; /* BUFFER POINTERS */ READCOM: PROCEDURE; /* READ INTO COMMAND BUFFER */ MAXLEN = 128; CALL READ(.MAXLEN); END READCOM; /* **************** END OF LIBRARY PROCEDURES ***************** */ MOVE: PROCEDURE(S,D,N); DECLARE (S,D) ADDRESS, N BYTE, A BASED S BYTE, B BASED D BYTE; DO WHILE (N:=N-1) <> 255; B = A; S=S+1; D=D+1; END; END MOVE; GETCHAR: PROCEDURE BYTE; /* GET NEXT CHARACTER */ DECLARE I BYTE; IF RFLAG THEN RETURN READRDR; IF (SBP := SBP+1) <= LAST(SBUFF) THEN RETURN SBUFF(SBP); /* OTHERWISE READ ANOTHER BUFFER FULL */ DO SBP = 0 TO LAST(SBUFF) BY 128; IF (I:=DISKREAD(.SFCB)) = 0 THEN CALL MOVE(80H,.SBUFF(SBP),80H); ELSE DO; IF I<>1 THEN CALL PRINT(.'DISK READ ERROR$'); SBUFF(SBP) = EOFILE; SBP = LAST(SBUFF); END; END; SBP = 0; RETURN SBUFF; END GETCHAR; DECLARE STACKPOINTER LITERALLY 'STACKPTR'; PRINTNIB: PROCEDURE(N); DECLARE N BYTE; IF N > 9 THEN CALL PRINTCHAR(N+'A'-10); ELSE CALL PRINTCHAR(N+'0'); END PRINTNIB; PRINTHEX: PROCEDURE(B); DECLARE B BYTE; CALL PRINTNIB(SHR(B,4)); CALL PRINTNIB(B AND 0FH); END PRINTHEX; PRINTADDR: PROCEDURE(A); DECLARE A ADDRESS; CALL PRINTHEX(HIGH(A)); CALL PRINTHEX(LOW(A)); END PRINTADDR; /* INTEL HEX FORMAT LOADER */ RELOC: PROCEDURE; DECLARE (RL, CS, RT) BYTE; DECLARE LA ADDRESS, /* LOAD ADDRESS */ TA ADDRESS, /* TEMP ADDRESS */ SA ADDRESS, /* START ADDRESS */ FA ADDRESS, /* FINAL ADDRESS */ NB ADDRESS, /* NUMBER OF BYTES LOADED */ SP ADDRESS, /* STACK POINTER UPON ENTRY TO RELOC */ MBUFF(256) BYTE, P BYTE, L ADDRESS, I BYTE; SETMEM: PROCEDURE(B); /* SET MBUFF TO B AT LOCATION LA MOD LENGTH(MBUFF) */ DECLARE (B,I) BYTE; IF LA < L THEN /* MAY BE A RETRY */ RETURN; DO WHILE LA > L + LAST(MBUFF); /* WRITE A PARAGRAPH */ DO I = 0 TO 127; /* COPY INTO BUFFER */ BUFFER(I) = MBUFF(LOW(L)); L = L + 1; END; /* WRITE BUFFER ONTO DISK */ P = P + 1; IF DISKWRITE(FCBA) <> 0 THEN DO; CALL PRINT(.'DISK WRITE ERROR$'); HALT; /* RETRY AFTER INTERRUPT NOP */ L = L - 128; END; END; MBUFF(LOW(LA)) = B; END SETMEM; READHEX: PROCEDURE BYTE; /* READ ONE HEX CHARACTER FROM THE INPUT */ DECLARE H BYTE; IF (H := GETCHAR) - '0' <= 9 THEN RETURN H - '0'; IF H - 'A' > 5 THEN GO TO CHARERR; RETURN H - 'A' + 10; END READHEX; READBYTE: PROCEDURE BYTE; /* READ TWO HEX DIGITS */ RETURN SHL(READHEX,4) OR READHEX; END READBYTE; READCS: PROCEDURE BYTE; /* READ BYTE WHILE COMPUTING CHECKSUM */ DECLARE B BYTE; CS = CS + (B := READBYTE); RETURN B; END READCS; MAKE$DOUBLE: PROCEDURE(H,L) ADDRESS; /* CREATE A BOUBLE BYTE VALUE FROM TWO SINGLE BYTES */ DECLARE (H,L) BYTE; RETURN SHL(DOUBLE(H),8) OR L; END MAKE$DOUBLE; DIAGNOSE: PROCEDURE; DECLARE M BASED TA BYTE; NEWLINE: PROCEDURE; CALL CRLF; CALL PRINTADDR(TA); CALL PRINTCHAR(':'); CALL PRINTCHAR(' '); END NEWLINE; /* PRINT DIAGNOSTIC INFORMATION AT THE CONSOLE */ CALL PRINT(.'LOAD ADDRESS $'); CALL PRINTADDR(TA); CALL PRINT(.'ERROR ADDRESS $'); CALL PRINTADDR(LA); CALL PRINT(.'BYTES READ:$'); CALL NEWLINE; DO WHILE TA < LA; IF (LOW(TA) AND 0FH) = 0 THEN CALL NEWLINE; CALL PRINTHEX(MBUFF(TA-L)); TA=TA+1; CALL PRINTCHAR(' '); END; CALL CRLF; HALT; END DIAGNOSE; /* INITIALIZE */ SA, FA, NB = 0; SP = STACKPOINTER; P = 0; /* PARAGRAPH COUNT */ TA,LA,L = 100H; /* BASE ADDRESS OF TRANSIENT ROUTINES */ IF FALSE THEN CHARERR: /* ARRIVE HERE IF NON-HEX DIGIT IS ENCOUNTERED */ DO; /* RESTORE STACKPOINTER */ STACKPOINTER = SP; CALL PRINT(.'NON-HEXADECIMAL DIGIT ENCOUNTERED $'); CALL DIAGNOSE; END; /* READ RECORDS UNTIL :00XXXX IS ENCOUNTERED */ DO FOREVER; /* SCAN THE : */ DO WHILE (I := GETCHAR) <> ':'; /* BUT ABORT IF EOF */ IF I = EOFILE THEN GO TO FIN; END; /* SET CHECK SUM TO ZERO, AND SAVE THE RECORD LENGTH */ CS = 0; /* MAY BE THE END OF TAPE */ IF (RL := READCS) = 0 THEN GO TO FIN; NB = NB + RL; TA, LA = MAKE$DOUBLE(READCS,READCS); IF SA = 0 THEN SA = LA; /* READ THE RECORD TYPE (NOT CURRENTLY USED) */ RT = READCS; /* PROCESS EACH BYTE */ DO WHILE (RL := RL - 1) <> 255; CALL SETMEM(READCS); LA = LA+1; END; IF LA > FA THEN FA = LA - 1; /* NOW READ CHECKSUM AND COMPARE */ IF CS + READBYTE <> 0 THEN DO; CALL PRINT(.'CHECK SUM ERROR $'); CALL DIAGNOSE; END; END; /* FOREVER */ FIN: /* EMPTY THE BUFFERS */ TA = LA; DO WHILE L < TA; CALL SETMEM(0); LA = LA+1; END; /* PRINT FINAL STATISTICS */ CALL PRINT(.'FIRST ADDRESS $'); CALL PRINTADDR(SA); CALL PRINT(.'LAST ADDRESS $'); CALL PRINTADDR(FA); CALL PRINT(.'BYTES READ $'); CALL PRINTADDR(NB); CALL PRINT(.'RECORDS WRITTEN $'); CALL PRINTHEX(P); CALL CRLF; END RELOC; /* ARRIVE HERE FROM THE SYSTEM MONITOR, READY TO READ THE HEX TAPE */ /* SET UP STACKPOINTER IN THE LOCAL AREA */ DECLARE STACK(16) ADDRESS, SP ADDRESS; SP = STACKPOINTER; STACKPOINTER = .STACK(LENGTH(STACK)); SBP = LENGTH(SBUFF); /* SET UP THE SOURCE FILE */ CALL MOVE(FCBA,.SFCB,33); CALL MOVE(.('HEX',0),.SFCB(9),4); CALL SEARCH(.SFCB); IF (RFLAG := DCNT = 255) THEN CALL PRINT(.'SOURCE IS READER$'); ELSE DO; CALL PRINT(.'SOURCE IS DISK$'); CALL OPEN(.SFCB); IF DCNT = 255 THEN CALL PRINT(.'-CANNOT OPEN SOURCE$'); END; CALL CRLF; CALL MOVE(.'COM',FCBA+9,3); /* IF NO SOURCE FILE NAME USE A TEMPORARY ONE */ IF SFCB(1) = 20H THEN CALL MOVE(.'$$$',FCBA+1,3); /* REMOVE ANY EXISTING FILE BY THIS NAME */ CALL DELETE(FCBA); /* THEN OPEN A NEW FILE */ CALL MAKE(FCBA); CALL OPEN(FCBA); IF DCNT = 255 THEN CALL PRINT(.'NO MORE DIRECTORY SPACE$'); ELSE DO; CALL RELOC; CALL CLOSE(FCBA); IF DCNT = 255 THEN CALL PRINT(.'CANNOT CLOSE FILE$'); END; CALL CRLF; /* RESTORE STACKPOINTER FOR RETURN */ STACKPOINTER = SP; RETURN 0; END LOADCOM; EOF