10 ' CPM-PERT Program from Interface Age, Feb 1981
20 ' Written by Richard Parry
30 ' Adapted to Microsoft BASIC by Charles H Strom
40 ' Dressed up & Adapted to Freedom 100 and Decision I 10 Apr 84 [Toad hall]
50 ' Added COUT and LIST location from CP/M and BIOS jump table to alleviate
60 ' problems with reserved locations in Page 0, problems with RST 1 and 3
70 ' (my CP/M didn't have them), having to run any special programs, etc.
80 ' Also changed location in MBASIC of CONOUT routine (different in V5.1).
90 ' David Kirschbaum, Toad Hall
100 ' 7573 Jennings Lane, Fayetteville NC 28303 (919)868-3471/396-6862
110 ' ARPANet ABN.ISCAMS@USC-ISID
120 '
130 '== Locate COUT (conout) and LIST in CP/M BIOS Jump Table ==
140 '
150 ' Location of warm boot in BIOS jump table can be found at bytes 0001
160 ' (Least Significant Byte, LSB), and 0002 (Most Significant Byte, MSB).
170 '
180 COUT1%=PEEK(1) : COUT2%=PEEK(2) 'get LSB and MSB of warm boot jump
190 COUT1%=COUT1%+9 'bump up 9 bytes from warm boot to COUT
200 IF COUT1%<256 THEN 220 'no need to increase MSB
210 COUT2%=COUT2%+256 : COUT1%=COUT1%-256 'inx MSB, dx LSB
220 LST2%=COUT2% : LST1%=COUT1%+3 'bump up 3 bytes from COUT to LIST
230 IF LST1%<256 THEN 280 'no need to increase MSB
240 LST2%=LST2%+256 : LST1%=LST1%-256 'inx MSB, dx LSB
250 '
260 '== Initialize Normal Distribution Constants
270 '
280 RN=15: RS=SQR(3/RN)
290 CONOUT%= &H41B8 'Loc in MBASIC 5.1 of call to CONOUT
300 ' old value for MBASIC 5.21 was &H41E4
310 CLR$=CHR$(27)+"*" ' Home cursor and clear screen for Freedom 100 terminal
320 '
330 ' == Input Data ==
340 '
350 PRINT CLR$;
360 PRINT "CPM or PERT Simulation? (C/P): ";
370 Q$=INKEY$:IF LEN(Q$)<1 THEN 370 ELSE PRINT Q$
380 IF Q$<>"C" AND Q$<>"P" THEN PRINT "ERROR! Try again.":GOTO 360
390 PRINT "Do you want a HARD-COPY record? (Y/N): ";
400 HC$=INKEY$:IF LEN(HC$)<1 THEN 400 ELSE PRINT HC$
410 IF HC$<>"Y" AND HC$<>"N" THEN PRINT "ERROR! Try again.":GOTO 390
420 IF HC$<>"Y" THEN 450
430 PRINT "Note - SETUP.ASM must be loaded before MBASIC"
440 PRINT "or printer will not function!"
450 PRINT:INPUT "Number of Activities: ",N
460 DIM ML(N), MO(N), MP(N), CP(N), ME(N), SD(N), IC(20)
470 DIM S(N), F(N), D(N), E(N), L(N), F1(N)
480 FOR I=1 TO N
490 PRINT CLR$: PRINT "Activity: ",I: PRINT
500 GOSUB 2190 'Go to Input Data routine
510 NEXT I
520 PRINT CLR$;
530 PRINT "Would you like to examine or edit the Input Data? (Y/N): ";
540 Q1$=INKEY$:IF LEN(Q1$)<1 THEN 540 ELSE PRINT Q1$
550 IF Q1$<>"Y" AND Q1$<>"N" THEN PRINT "ERROR! Try again.":GOTO 530
560 IF Q1$="N" THEN 860
570 GOSUB 2350 'Sort Input Data
580 '
590 '== Display Input Data ==
600 '
610 IF HC$<>"Y" THEN 640
620 IF HC$<>"Y" THEN 640
630 POKE CONOUT%,LST1%:POKE CONOUT%+1,LST2%
640 PRINT CLR$:IF Q$<>"C" THEN 690
650 PRINT "ACTIVITY # FROM TO DURATION"
660 FOR I = 1 TO N
670 PRINT TAB(5); I; TAB(15); S(I); TAB(25); F(I); TAB(35); D(I)
680 NEXT I:GOTO 740
690 PRINT "ACTIVITY # FROM TO ML MO MP"
700 FOR I = 1 TO N
710 PRINT TAB(5); I; TAB(15); S(I); TAB(25); F(I);
720 PRINT TAB(35); ML(I); TAB(45); MO(I); TAB(55); MP(I)
730 NEXT I:PRINT
740 POKE CONOUT%,COUT1%:POKE CONOUT%+1,COUT2%
750 PRINT "Would you like to edit an Activity? (Y/N): ";
760 Q1$=INKEY$:IF LEN(Q1$)<1 THEN 760 ELSE PRINT Q1$
770 IF Q1$<>"Y" AND Q1$<>"N" THEN PRINT "ERROR! Try again.":GOTO 750
780 IF Q1$="N" THEN 860
790 '
800 '== Edit Mode ==
810 '
820 PRINT:INPUT "What activity needs alteration? (0 to end): ",I
830 IF I=0 THEN 520
840 GOSUB 2190 'Go to Input Data Routine
850 GOTO 820
860 GOSUB 2350 'Go to Sort Routine
870 IF Q$<>"C" THEN 1210
880 '
890 ' Critical Path Analysis requested. Perform Critical Path
900 ' Analysis once and display results.
910 '
920 GOSUB 2600
930 C2=0
940 IF HC$<>"Y" THEN 960
950 POKE CONOUT%,LST1%:POKE CONOUT%+1,LST2%
960 PRINT CLR$;"CP Analysis is:"
970 PRINT: PRINT: PRINT "FROM","TO","EST","LFT","FLOAT": PRINT
980 FOR I = 1 TO N:PRINT S(I),F(I),E(S(I)),L(F(I)),F1(I):NEXT I
990 PRINT "The Critical Path Length is: ";PL
1000 PRINT: PRINT "The Critical Path is:":PRINT "FROM","TO": PRINT
1010 FOR I = 1 TO N
1020 IF F1(I) = 0 THEN 1040
1030 NEXT I
1040 PRINT S(I),F(I): C2=C2+1: IF I>N THEN 1080
1050 FOR M= 1 TO N
1060 IF S(M)=F(I) AND F1(M) = 0 THEN I=M: GOTO 1040
1070 NEXT M
1080 IF C1<>C2 THEN PRINT: PRINT "There is more than one Critical Path."
1090 PRINT:POKE CONOUT%,COUT1%:POKE CONOUT%+1,COUT2%
1100 PRINT "Would you like to edit an Activity or stop program? (E/S): ";
1110 Q1$=INKEY$:IF LEN(Q1$)<1 THEN 1110 ELSE PRINT Q1$
1120 IF Q1$="E" THEN PRINT:GOTO 640
1130 IF Q1$<>"S" THEN PRINT "ERROR! Try again.":GOTO 1100
1140 END
1150 '
1160 '== PERT Simulation requested. Perform Critical Path Analysis the
1170 ' number of times specified. Store path lengths and increment
1180 ' Activities which appear on Critical Path. Construct Histogram
1190 ' and display results.
1200 '
1210 FOR I = 1 TO N
1220 ME(I) = (MO(I)+4*ML(I)+MP(I))/6 'Compute Mean of each activity.
1230 SD(I) = (MP(I)-MO(I))/6 'Compute Standard Deviation of each activity.
1240 NEXT I
1250 '== Compute Most Optimistic Path Length ==
1260 DU=0: FOR I=1 TO N: CP(I)=0: E(I)=0: L(I)=0: NEXT I
1270 FOR I = 1 TO N: D(I)=MO(I): NEXT I
1280 GOSUB 2600
1290 BC=PL
1300 '== Compute Most Pessimistic Path Length ==
1310 DU=0: FOR I=1 TO N: CP(I)=0: E(I)=0: L(I)=0: NEXT I
1320 FOR I = 1 TO N: D(I)=MP(I): NEXT I
1330 GOSUB 2600
1340 WC=PL
1350 '== Initialize Key Variables ==
1360 DU=0: FOR I = 1 TO N: CP(I)=0: E(I)=0: L(I)=0: NEXT I
1370 LS=0: HS=0: FOR I=1 TO 20: IC(I)=0: NEXT I
1380 '== Initialize Random Number Generator ==
1390 RANDOMIZE
1400 '== Propose # of Transactions as 20 * # of Activities ==
1410 PRINT "Number of transactions should be >= "; 20*N
1420 INPUT "Number of transactions: ",NS
1430 PRINT: PRINT "++SIMULATION IN PROGRESS++"
1440 '
1450 '== Construct Histogram ==
1460 '-- Set Appropriate Interval (i.e., Integer >=1) --
1470 LL=INT(BC)
1480 IF WC-BC<=20 THEN IN=1
1490 IN=INT((WC-BC)/20)+1
1500 '
1510 '-- Perform Simulation --
1520 TC=100
1530 FOR K=1 TO NS
1540 IF K=TC THEN PRINT "++ Simulation in Progress ++";TC: TC=TC+100
1550 FOR J=1 TO N
1560 S=0: E(J)=0: L(J)=0
1570 IF ML(J)=0 THEN D(J)=0: GOTO 1600
1580 FOR I=1 TO RN: S=S+2*RND-1: NEXT I
1590 D(J)=ME(J)+SD(J)*S*RS
1600 NEXT J
1610 GOSUB 2600
1620 '-- Find Interval for This Path Length --
1630 I3=(PL-LL)/IN+2
1640 IF I3<1 THEN LS=LS+1: GOTO 1680
1650 IF I3>20 THEN HS=HS+1: GOTO 1680
1660 I3=INT(I3)
1670 IC(I3)=IC(I3)+1
1680 NEXT K
1690 '
1700 '-- Print Frequency Distribution Table --
1710 IF HC$<>"Y" THEN 1730
1720 POKE CONOUT%,LST1%:POKE CONOUT%+1,LST2%
1730 PRINT CLR$;"++FREQUENCY DISTRIBUTION TABLE++": PRINT
1740 PRINT "Most OPTIMISTIC path length: "; BC
1750 PRINT "Most PESSIMISTIC path length: "; WC
1760 PRINT "Number of transactions LOWER than histogram range: ";LS
1770 PRINT "Number of transactions HIGHER than histogram range: ";HS: PRINT
1780 PRINT " INTERVAL FREQ. PCT."
1790 I1=LL-IN: I2=LL
1800 FOR M=1 TO 20
1810 PRINT"=>";I1;"<";I2;TAB(20);IC(M);TAB(30);INT(.5+100*IC(M)/NS)
1820 I1=I1+IN: I2=I2+IN
1830 NEXT M
1840 '
1850 '== Print Histogram ==
1860 '-- Compute Histogram Scale Factor --
1870 SC=0: LO=18: J=0: LL=INT(BC)
1880 FOR M=1 TO 20
1890 IF IC(M)>SC THEN SC=IC(M)
1900 NEXT M
1910 SC=50/SC: X$="PATH LENGTH"
1920 PRINT: PRINT: PRINT TAB(24); "++ HISTOGRAM ++": PRINT
1930 PRINT TAB(18);"RELATIVE FREQUENCY OF PATH LENGTHS"
1940 PRINT TAB(LO); "+------------------------------------------------+"
1950 FOR M=1 TO 20
1960 HM=IC(M)*SC
1970 FOR K=1 TO 3
1980 J=J+1: PRINT MID$(X$,J,1);TAB(2);
1990 IF K=2 THEN PRINT ">=";LL-IN;"<";LL;: LL=LL+IN
2000 PRINT TAB(LO);
2010 IF IC(M)=0 THEN PRINT: GOTO 2030
2020 FOR I=1 TO HM: PRINT "*";: NEXT I: PRINT
2030 NEXT K
2040 NEXT M
2050 '
2060 '-- Print Activity Analysis --
2070 PRINT: PRINT
2080 PRINT TAB(10); "+++ CP ACTIVITY ANALYSIS TABLE +++": PRINT
2090 PRINT "ACTIVITY # FROM TO CP FREQ. PCT."
2100 FOR I=1 TO N
2110 PRINT TAB(5);I;TAB(15);S(I);TAB(25);F(I);
2120 PRINT TAB(35);CP(I);TAB(45);INT(.5+100*CP(I)/NS)
2130 NEXT I
2140 PRINT: PRINT "DUPLICATE critical paths occurred";DU;"times."
2150 GOTO 1090 'Quit??
2160 '
2170 '== Input Data Routine ==
2180 '
2190 INPUT "FROM: ",S(I)
2200 INPUT " TO: ",F(I)
2210 IF F(I)>N THEN PRINT "++End Node # NOT <= # of Activities++":GOTO 2200
2220 IF S(I)>F(I) THEN PRINT "++Start Node MUST be < End Node++":GOTO 2190
2230 IF LEFT$(Q$,1)="C" THEN INPUT "Duration: ",D(I): GOTO 2310
2240 INPUT " Most Likely: ",ML(I)
2250 '-- Check for Dummy Activity --
2260 IF ML(I)=0 THEN MO(I)=0: MP(I)=0: GOTO 2310
2270 INPUT " Most Optimistic: ", MO(I)
2280 IF MO(I)>ML(I) THEN PRINT "++MO MUST be <= ML++": GOTO 2270
2290 INPUT "Most Pessimistic: ", MP(I)
2300 IF MP(I)<ML(I) THEN PRINT "++MP MUST be >= ML++": GOTO 2290
2310 RETURN
2320 '
2330 '== Sort Data Using Start Node as Key ==
2340 '
2350 PRINT: PRINT "Sorting in Progress": PRINT
2360 SW=0
2370 FOR I=1 TO N-1
2380 J=I+1
2390 IF S(I)<=S(J) THEN 2470
2400 EX=S(I): S(I)=S(J): S(J)=EX
2410 EX=F(I): F(I)=F(J): F(J)=EX
2420 EX=D(I): D(I)=D(J): D(J)=EX
2430 EX=ML(I): ML(I)=ML(J): ML(J)=EX
2440 EX=MO(I): MO(I)=MO(J): MO(J)=EX
2450 EX=MP(I): MP(I)=MP(J): MP(J)=EX
2460 SW=1
2470 NEXT I
2480 IF SW=1 THEN 2360
2490 RETURN
2500 '
2510 '== The following subroutine is used by both the CPM Analysis
2520 ' and the PERT Simulation Analysis. While the CPM Analysis
2530 ' calls the routine only once, the Simulation calls the
2540 ' routine the number of times requested by the user.
2550 ' The Earliest, Latest, and Float Times are computed, and
2560 ' from this data the Critical Path Length and Critical Path
2570 ' are calculated. Duplicate Critical Paths are only counted once.
2580 '
2590 '== Compute Earliest Starting Time ==
2600 C1=0: C2=0: PL=0
2610 FOR I=1 TO N
2620 M1=E(S(I))+D(I)
2630 IF E(F(I))<=M1 THEN E(F(I))=M1
2640 NEXT I
2650 '== Compute Latest Finishing Time ==
2660 L(F(N))=E(F(N))
2670 FOR I=N TO 1 STEP -1
2680 L1=S(I): M2=L(F(I))-D(I)
2690 IF L(L1)>=M2 OR L(L1)=0 THEN L(L1)=M2
2700 NEXT I
2710 '== Compute Float Time ==
2720 FOR I=1 TO N
2730 F1(I)=L(F(I))-E(S(I))-D(I)
2740 IF F1(I)<.0001 THEN F1(I)=0: C1=C1+1
2750 NEXT I
2760 '== Compute Critical Path Length ==
2770 FOR I=1 TO N
2780 IF L(F(I))>PL THEN PL=L(F(I))
2790 NEXT I
2800 '== Compute Critical Path ==
2810 FOR I=1 TO N
2820 IF F1(I)=0 THEN 2840
2830 NEXT I
2840 C2=C2+1: CP(I)=CP(I)+1
2850 IF I>N THEN 2890
2860 FOR M=1 TO N
2870 IF S(M)=F(I) AND F1(M)=0 THEN I=M: GOTO 2840
2880 NEXT M
2890 IF C1<>C2 THEN DU=DU+1
2900 RETURN