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++": 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