Mr. Roche,
Thank you, This is just what I needed! Short explanation: I got a great deal of pleasure simulating a 50 year old analog computer within a simulation of a 38 year old microcomputer. Long explanation: I have had in my possession since 1976 or so a paper tape of MITS BASIC 4.0, which I decided I wanted to decode, load and run. Yes, I know that this BASIC is available on the Internet, but I wanted to run *my* paper tape. I built a webcam paper tape reader, and successfully created a byte-for-byte image file, which I was able to load and run in the Simh altairz80.exe simulator. Since the simulator has no front panel, I had to load the bootstrap loader from a separate small image file instead of toggling it in with switches. Once the bootstrap loader was in "memory," I redirected the console to the BASIC paper tape image file, so it loaded BASIC just as if it had been coming from a Teletype--first and second-stage loaders, then blocks with checksums. The final part of the project was to run a meaningful program under that BASIC, and you have filled the need with the AMF 665D Analog Computer Simulator.
I had to make some changes to get it to run, but noted those changes in comments. There is one part I do not understand which is in lines 1052-1100. I think that section is only a feedback loop safety check, and does not affect proper operation. It seems like a conditional statement might be missing, but I couldn't figure out what it should be. If you have a chance to check your sources and find a missing statement, I'd be interested to know about it.
I also added some built-in examples, so that the program can do something useful immediately on startup. The commands EX1 and EX2 load the connection lists for first and second-order systems, respectively. The LIST command will show the connection list, and the RUN command will start the show. Be sure to clear the list with the NEW command when changing examples. The comments explain how to enter the parameters. I found it quite easy to produce nice graphs by copying the simulator's output screen, then pasting the values into a spreadsheet.
The program runs under MITS BASIC 4.0 as well as under CP/M
MBASIC.COM (version 5.21) within the Simh altairz80.exe simulator. I hope others can have as much fun playing with this as I have.
NOTE: Some of the source code may be hidden as "quoted text". Click on the link to get the full listing.
AMF665D1.BAS
10 REM AMF665D.BAS by Emmanuel ROCHE
11 REM ADAPTED TO MITS BASIC 4.0, JIM DUNLAP, 02/17/2014
12 REM ORIGINAL CODE AND CORRECTIONS MARKED BY "REM ==="
13 REM EXPLANATION COMMENTS MARKED BY "REM +++"
13 REM === CHANGED SINGLE-QUOTE COMMENTS TO 'REM' FOR MITS BASIC.
20 REM
30 REM ANALOG CLARK BAKER 8/72 DSAA
40 REM
41 REM === LINE 50, ADDED DOUBLE QUOTE BEFORE J5.
42 REM +++ LABELS FOR OUTPUT CONNECTIONS O$
45 REM +++ OUTPUT NODE INDICES
46 REM +++ 1 2 3 4 5 6 7 8 9 10 11 12
50 DATA "P1","P2","P3","N1","N2","N3","I4","I5","I6","J4","J5","J6"
52 REM +++ 13 14 15 16 17 18 19 20 21
60 DATA "A7","A8","A9","B7","B8","B9","C7","C8","C9"
65 REM === LINE 70, ADDED DOUBLE QUOTE AFTER B5.
66 REM +++ LABELS FOR INPUT CONNECTIONS, I$
67 REM +++ INPUT NODE CONNECTION NUMBERS
68 REM +++ 1 2 3 4 5 6 7 8 9 10 11 12
70 DATA "A1","A2","A3","A4","A5","A6","B1","B2","B3","B4","B5","B6"
72 REM +++ 13 14 15 16 17 18 19 20 21 22 23 24
80 DATA "C1","C2","C3","C4","C5","C6","I1","I2","I3","J1","J2","J3"
82 REM +++ 25 26 27
90 DATA "M1","M2","M3"
95 REM +++ LABELS FOR M$
100 DATA "1","2","3"
102 REM === CHANGED INTEGRATION ITERATION LOOPS FROM 100 TO NITER
103 REM === 100 IS MORE ACCURATE BUT *VERY* SLOW, 10 IS A LITTLE
104 REM === LESS ACCURATE, BUT MUCH FASTER. SET NITER TO SUIT YOUR NEEDS.
105 NITER = 10
108 REM +++ LABELS FOR INPUT, OUTPUT AND METER CONNECTIONS
110 DIM I$(27),M$(3),O$(21)
111 REM +++ THE FOLLOWING ARE MOSTLY GUESSES
112 REM +++ A: ADDER INPUT STATES (INSTANTANEOUS VALUES)
113 REM +++ B: ADDER OUTPUT STATES
114 REM +++ C: ADDER MULTIPLIER COEFFICIENTS
115 REM +++ I: INTEGRATOR INPUT STATES, AFTER IMPLIED NEGATION
116 REM +++ J: INTEGRATOR OUTPUT STATES
117 REM +++ M: METER STATES
118 REM +++ O: OUTPUT-INPUT CONNECTION LIST
119 REM +++ ARRAY INDEX IS OUTPUT NODE, VALUE IS INPUT NODE
120 DIM A(18),B(3),C(3),I(6),J(2),M(3),O(21)
128 REM +++ READ OUTPUT, INPUT AND METER LABELS.
130 FOR X=1 TO 21
140 READ O$(X)
150 NEXT X
160 FOR X=1 TO 27
170 READ I$(X)
180 NEXT X
190 FOR X=1 TO 3
200 READ M$(X)
210 NEXT X
220 PRINT "ANALOG SIMULATOR"
230 REM
240 REM ***NEW***
250 FOR X=1 TO 21
260 LET O(X)=0
270 REM === PRINT "ENTER COMMAND";
280 NEXT X
288 REM === MOVED 290 TO 292. MOVED 'PRINT "ENTER COMMAND";' FROM 270 TO 290.
290 PRINT "ENTER COMMAND";
292 PRINT
298 REM +++ GET COMMAND STRING
300 INPUT C$
302 REM +++ SET CONNECTION LIST FOR EXAMPLE 1.
305 IF C$="EX1" THEN GOSUB 6000: GOTO 290
306 REM +++ SET CONNECTION LIST FOR EXAMPLE 1.
307 IF C$="EX2" THEN GOSUB 6200: GOTO 290
309 REM +++ CLEAR CONNECTION LIST FOR NEW PROBLEM.
310 IF C$="NEW" THEN 240
318 REM +++ QUIT AMF665D SIMULATOR, RETURN TO CP/M OR BASIC 4.0.
320 IF C$="STOP" THEN 2190
328 REM +++ ADD CONNECTIONS, OUTPUT, INPUT; E.G., 'N1,I1'
329 REM +++ ENTER 'END,0' TO FINISH
330 IF C$="ENTER" THEN 400
332 REM === LINE 340, CHANGED 'ID' TO 'IF'.
340 IF C$="DELETE" THEN 400
350 IF C$="LIST" THEN 780
360 IF C$="RUN" THEN 880
370 PRINT "ILLEGAL COMMAND: ";C$
380 GOTO 290
390 REM
400 REM ***ENTER*** AND ***DELETE***
410 PRINT "CONNECTION";
420 INPUT O$,I$
430 IF O$="END" THEN 290
440 FOR O=1 TO 21
442 REM === LINE 450, CHANGED 'ID' TO 'IF'.
450 IF O$(O)=O$ THEN 490
460 NEXT O
462 REM === ADDED QUOTE AFTER 'TERMINAL:'.
770 REM
780 REM ***LIST***
782 REM === LINE 790, CHANGED 'PPINT' TO 'PRINT'.
790 PRINT
800 LET P=1
810 FOR X=1 TO 21
820 IF O(X)=0 THEN 850
830 PRINT P,O$(X);"-";I$(O(X))
840 LET P=P+1
850 NEXT X
860 GOTO 290
870 REM
880 REM ***RUN***
890 LET T=0
900 LET C1=0
910 LET F1=0
920 FOR X=1 TO 21
930 IF O(X)>=25 THEN 970
940 NEXT X
950 PRINT "NO METER, NO OUTPUT."
960 GOTO 290
970 FOR X=1 TO 18
980 LET A(X)=0
990 NEXT X
1000 FOR X=1 TO 3
1010 LET E(X)=0
1020 LET I(X)=0
1030 LET I(X+3)=0
1040 LET P(X)=0
1050 LET M(X)=9999
1052 REM *** A CONDITIONAL STATEMENT SEEMS TO BE MISSING FROM THE
1053 REM *** CODE BELOW. LOOKS LIKE IT IS SUPPOSED TO SET THE
1054 REM *** ADDER INPUT STATE TO 1 TO PREVENT RUNAWAY UNDER SOME
1055 REM *** UNKNOWN WIRING CONFIGURATION. I COMMENTED IT OUT
1056 REM *** UNTIL I FIGURE OUT HOW ITS SUPPOSED TO WORK.
1057 REM +++ O(13:15) -> A7, A8, A9, B7,..., C9, ADDER OUTPUTS
1060 REM *** FOR Y=X*3+10 TO X*3+12
1070 REM *** PRINT "FEEDBACK LOOP, SORRY"
1080 REM *** LET A(O(Y))=1
1090 REM *** NEXT Y
1100 NEXT X
1110 FOR X=1 TO 3
1120 FOR Y=1 TO 3
1130 IF E(1)=Y THEN 1240
1140 IF E(2)=Y THEN 1240
1150 FOR Z=1 TO 6
1160 IF A(Y*6-6+Z)=1 THEN 1240
1170 NEXT Z
1180 LET E(X)=Y
1190 FOR Z=Y*3+10 TO Y*3+12
1200 IF O(Z)>18 THEN 1220
1210 LET A(O(Z))=0
1220 NEXT Z
1230 GOTO 1250
1240 NEXT Y
1250 NEXT X
1260 IF E(3)<>0 THEN 1280
1270 GOTO 290
1280 FOR X=1 TO 6
1290 IF O(X)=0 THEN 1330
1292 REM *** LINE 1300 CHANGED 'CONSTANT' TO 'STEP MAGNITUDE'
1300 REM *** PRINT "CONSTANT";
1302 PRINT "STEP MAGNITUDE";
1310 INPUT C
1320 GOTO 1340
1330 NEXT X
1340 FOR X=1 TO 3
1350 FOR Y=1 TO 3
1360 IF O(X*3+9+Y)=0 THEN 1410
1365 REM *** CHANGED 'ADDER CONSTANT' TO 'ADDER COEFFICIENT'.
1370 REM *** PRINT "ADDER CONSTANT #";X;
1372 PRINT "ADDER COEFFICIENT #";X;
1380 INPUT C(X)
1390 LET F1=-1
1400 GOTO 1420
1410 NEXT Y
1420 NEXT X
1430 FOR X=1 TO 2
1440 FOR Y=1 TO 3
1450 IF O(X*3+3+Y)=0 THEN 1490
1455 REM *** LINE 1460, CHANGED 'CONSTANT #"9' TO CONSTANT #";
1456 REM *** CHANGED 'INTEGRATOR CONSTANT #'
1457 REM *** TO 'INITIAL CONDITION, INTEGRATOR #'.
1460 REM PRINT "INTERGRATOR CONSTANT #";X;
1465 PRINT "INITIAL CONDITION, INTEGRATOR #";X;
1470 INPUT J(X)
1480 GOTO 1500
1490 NEXT Y
1495 REM *** LINE 1510, CHANGED NEXT 1540 TO NEXT X
1500 NEXT X
1510 IF F1=0 THEN 1540
1520 PRINT "ADDER NUMBER TO CHANGE";
1530 INPUT C1
1540 FOR X=1 TO 12
1550 IF O(X)=0 THEN 1650
1560 LET O=O(X)
1570 IF X>3 THEN 1600
1580 LET K=C
1590 GOTO 1640
1600 IF X>6 THEN 1630
1610 LET K=-C
1620 GOTO 1640
1630 LET K=J(INT(X-7)/3+1)
1640 GOSUB 1950
1650 NEXT X
1660 PRINT "TIME STEP";
1670 INPUT T1
1680 PRINT "STOP TIME";
1690 INPUT T4
1700 PRINT
1702 REM === LINES 1710, 1720, CHANGED HARD-CODED 100 TO VARIABLE NITER
1703 REM === TO EASILY ALLOW ADJUSTING NUMBER OF LOOPS PER TIME STEP.
1710 LET T2=T1/NITER
1720 FOR T3=1 TO NITER
1730 FOR X=1 TO 3
1740 LET A=E(X)
1742 REM *** LINE 1750 IS TOO LONG FOR MITS BASIC 4.0. BROKE IT UP.
1750 REM *** B(A)=(A(A*6-5)*10+A(A*6-4)+A(A*6-3)-A(A*6-2)-A(A*6-1)-A(A*6)*10)*C(A)
1752 B(A)=A(A*6-5)*10+A(A*6-4)+A(A*6-3)
1754 B(A)=B(A)-A(A*6-2)-A(A*6-1)-A(A*6)*10
1755 B(A)=B(A)*C(A)
1760 LET K=B(A)
1770 FOR Y=1 TO 3
1780 IF O(A*3+9+Y)=0 THEN 1810
1782 REM *** LINE 1790, CHANGED O(A(3+9+Y)) TO O(A*3+9+Y)
1790 REM LET O=O(A(3+9+Y))
1792 LET O=O(A*3+9+Y) : REM CORRECTED
1800 GOSUB 1950
1810 NEXT Y
1820 NEXT X
1830 IF T3=1 THEN 2030
1840 FOR X=1 TO 2
1850 LET J(X)=J(X)-(I(X*3-2)*10+I(X*3-1)+I(X*3))*T2
1860 LET K=J(X)
1870 FOR Y=1 TO 3
1880 IF O(X*3+3+Y)=0 THEN 1910
1890 LET O=O(X*3+3+Y)
1900 GOSUB 1950
1910 NEXT Y
1920 NEXT X
1930 NEXT T3
1932 REM +++ UNCOMMENT LINE BELOW FOR DIAGNOSTIC PRINTOUT.
1935 REM +++ GOSUB 7000
1940 GOTO 1720
1945 REM +++ FILL IN STATE ARRAYS WITH INSTANTANEOUS SIMULATION VALUES.
1950 IF O>18 THEN 1980
1960 LET A(O)=K
1970 RETURN
1980 IF O>24 THEN 2010
1990 LET I(O-18)=K
2000 RETURN
2010 LET M(O-24)=K
2020 RETURN
2030 PRINT T;
2040 LET T=T+T1
2050 FOR X=1 TO 3
2060 IF M(X)=9999 THEN 2080
2070 PRINT "","M";M$(X);" =";INT(M(X)*1000)/1000;
2080 NEXT X
2090 IF C1<>0 THEN 2120
2100 PRINT
2110 GOTO 2140
2120 PRINT TAB(65);
2130 INPUT C(C1)
2140 IF T<T4+T1 THEN 1840
2150 PRINT "NEW STOP TIME";
2160 INPUT T4
2170 IF T4<=T-T1 THEN 290
2180 GOTO 1840
2190 SYSTEM
2192 END
6000 REM +++ EXAMPLE 1, FIRST-ORDER SYSTEM
6001 REM Y/U = 1/(s+1)
6002 REM AT 'STEP MAGNITUDE' ENTER '1'
6003 REM AT 'ADDER COEFFICIENT # 1' ENTER '1'
6004 REM AT 'INITIAL CONDITION, INTEGRATOR # 1' ENTER 0
6005 REM AT 'ADDER NUMBER TO CHANGE?' ENTER 0
6006 REM AT 'TIME STEP' ENTER '0.1'
6007 REM AT 'STOP TIME' ENTER '5'
6008 REM THIS IS AN EXTREMELY SIMPLE SYSTEM, USEFUL FOR TROUBLESHOOTING.
6010 O(2) = 5 : REM P2-A5
6020 O(7) = 27 : REM I4-M3
6030 O(8) = 2 : REM I5-A2
6040 O(13) = 19 : REM A7-I1
6199 RETURN
6200 REM +++ EXAMPLE 2, SECOND ORDER SYSTEM
6201 REM TRANSFER FUNCTION: Y/U = Wn/(s^2 + 2*z*Wn*s + Wn^2)
6202 REM WHERE s IS LAPLACE OPERATOR, Wn IS NATURAL FREQ,
6203 REM AND z IS THE DAMPING RATIO.
6204 REM A CLASSIC EXAMPLE USES Wn: 3.1416, Z: 0.707.
6205 REM LOAD THIS EXAMPLE WITH THE COMMANDS 'EX2', THEN 'RUN'.
6206 REM AT 'STEP MAGNITUDE' ENTER '0.9870', THIS IS Wn^2)/10.
6207 REM AT 'ADDER COEFFICIENT # 1' ENTER 1
6208 REM AT 'ADDER COEFFICIENT # 2' ENTER '4.4429', THIS IS 2*Z*Wn.
6209 REM AT 'ADDER COEFFICIENT # 3' ENTER '9.8696', THIS IS Wn^2.
6210 REM FOR BOTH INITIAL CONDITIONS ENTER 0
6211 REM AT 'ADDER NUMBER TO CHANGE?' ENTER 0
6212 REM AT 'TIME STEP' ENTER '0.1' TO GIVE 0.1 SEC PER STEP.
6213 REM AT 'STOP TIME' ENTER '5' TO GIVE A 5-SECOND RUN.
6214 REM YOU SHOULD BE REWARDED BY A LIST OF TIMES AND METER VALUES.
6215 REM NOTE: 'STEP MAGNITUDE' IS DIVIDED BY 10 BECAUSE
6216 REM THE I1 INPUT HAS 10X BUILT IN.
6230 O(4) = 19 : REM N1-I1
6235 O(7) = 4 : REM I4-A4
6240 O(13) = 23 : REM A7-J2
6245 O(10) = 25 : REM J4-M1
6250 O(11) = 14 : REM J5-C2
6255 O(19) = 20 : REM C7-I2
6260 O(8) = 8 : REM I5-B2
6265 O(16) = 21 : REM B7-I3
6299 RETURN
7000 PRINT "DIAGNOSTIC PRINT"
7050 PRINT "A ARRAY, ADDER INPUT STATES"
7060 FOR U=1 TO 18
7070 PRINT U; A(U)
7080 NEXT U
7090 PRINT
7100 PRINT "B ARRAY"
7110 FOR U=1 TO 3
7120 PRINT U; B(U)
7130 NEXT U
7140 PRINT "C ARRAY"
7150 FOR U=1 TO 3
7160 PRINT U; C(U)
7170 NEXT U
7180 PRINT "I ARRAY"
7190 FOR U=1 TO 6
7200 PRINT U; I(U)
7210 NEXT U
7220 PRINT "J ARRAY"
7230 FOR U=1 TO 2
7240 PRINT U; J(U)
7250 NEXT U
7260 PRINT "M ARRAY"
7270 FOR U=1 TO 2
7280 PRINT U; M(U)
7290 NEXT U
7999 RETURN
8000 END