PRUN: A Parallel RUN for NadaNet
Michael J. Mahon - November 29, 2004
Introduction
PRUN is an Applesoft program that runs on the master machine. Its purpose is to boot any AppleCrate machines needing network booting, start a Message Server if needed, and then run a user-selected Applesoft BASIC program on all remaining AppleCrate machines. It could be easily altered to run different programs on various machines or to run BASIC programs on other serving machines on the network.
The Program
The Applesoft BASIC program, PRUN, is presented here, with liberal commentary:
100 REM Run Parallel BASIC program 110 REM MJM - 08/16/04 115 REM Revised 11/08/04 120 :
After finding out whether a Message server should be started, we initialize NadaNet. This initialization presumes that NadaNet, with the AmperNada extensions, has already been installed below the OS, HIMEM has been adjusted, and an initializing call has been made. The following GOSUB merely verifies that NadaNet is installed and sets up some pointer variables for application use. Next, we take a "census" of all machines from 2 up to MX to determine which ones are serving and, if so, what type of machine they are. If the Message Server (ID=2) has been started, then that is noted. The disk prefix for program loads is defined for convenience.
300 INPUT "Is Message Server needed? ";A$ 310 NEEDMS = A$ = "Y" OR A$ = "y" 320 : 500 GOSUB 60000: REM Initialize 510 GOSUB 61000: REM Take census 520 MSRV = PEEK (ITBL + 2) = 3: REM Save pre-boot Msg Server status 530 PFX$ = "/ap/merlin/work/nadanet/" 540 :
The next lines set up the boot code and pointers to it in preparation for booting any network-bootable machines needing boot service, then call &SERVE(100) to enter the master's service loop with a count such that it will stay in the service loop for a couple of seconds (since, if nothing is requested, each RCVPKT call times out in about 20ms, and this specifies 100 iterations).
The purpose of this &SERVE call is to process the BOOT requests of any unbooted AppleCrate slave machines. When we return from SERVE, all slave machines requiring boot will have been booted and will be running their service loops.
550 REM Boot unbooted slaves 560 PRINT CHR$ (4)"bload "PFX$"nada.crate,A"BUF 570 PSRT = PEEK (BUF + 2) * 256: REM Prog start 580 PLNG = PEEK (48840) + 256 * PEEK (48841): REM Prog length 590 & BOOTCODE(PSRT,PLNG,BUF) 600 & SERVE(100): REM For BOOT 610 PRINT "Boot of slaves completed." 620 :
When booting is completed, we retake a census to determine how many slaves are present. If machine #2 is required as the message server, we require that there be at least two slaves, so that there will be a machine to do work.
630 GOSUB 61000: REM Re-take census 640 IF MSRV OR NOT NEEDMS GOTO 1000: REM Already running or not needed 650 : 660 REM Load Message Server 670 IF NOT PEEK (ITBL + 2) THEN PRINT "No Msg Server (2)": STOP 680 PRINT CHR$ (4)"bload "PFX$"nada.mserve,A"BUF 690 PSRT = PEEK (BUF + 2) * 256: REM Prog start 700 PLNG = PEEK (48840) + 256 * PEEK (48841): REM Prog length 710 REM Poke NADA.MSERVE code 720 & POKE (2,PSRT,PLNG,BUF) 730 & CALL (): REM Run Message Server 740 PRINT "Message Server now running on 2" 750 MSRV = 1: POKE ITBL + 2,3: REM Mark Msg Server 760 :
Now we load the "worker" Applesoft program into each slave machine. This is a multi-step process, since Applesoft must first be coldstarted on each slave machine while retaining control, then the program is POKEd into slave memory, then Applesoft is RUN. (A coldstart would not be necessary on a machine running a normal ROM, since BASIC is coldstarted after RESET.)
To begin, we BLOAD the program into a safe area of the master's memory and save its length, then iterate over each of the slave machines, &POKEing it into their memories. Note that while PRUN runs the same program in each slave machine, it would be quite easy to load each slave machine with an arbitrary program for more complex workloads.
1000 REM Load Applesoft program 1010 PRINT "Run what program? ";: INPUT "";P$ 1020 IF P$ = "" THEN PRINT CHR$ (4)"catalog": GOTO 1010 1030 BUF = 3 * 256: REM $300 utility buffer 1040 PBUF = 2 * 4096: REM Program buffer $2000 1050 PRINT CHR$ (4)"bload "P$",a"PBUF",TBAS" 1060 PLNG = PEEK (48840) + 256 * PEEK (48841): REM Prog Length 1070 PSTR = (8 * 256 + 1): REM Prog start = $0801 1080 PGND = PSTR + PLNG: REM Prog end = Start + Length 1090 : 1100 FOR D = 2 TO MX 1110 IF PEEK (ITBL + D) < > 2 GOTO 1370: REM Only AppleCrate machines 1120 :
To coldstart Applesoft and retain control, we patch CSW to print to the local video buffer and KSW to call the SERVER loop. The effect is to display (on a local monitor) the Applesoft carriage returns and prompts, but to return control to the SERVER loop as soon as Applesoft requests keyboard input.
1130 POKE BUF,15 * 16: POKE BUF + 1,15 * 16 + 13: REM COUT1 ($FDF0) 1140 POKE BUF + 2,205: POKE BUF + 3,3: REM Server loop $3CD 1150 CSW = 3 * 16 + 6: REM $0036 1160 & POKE (D,CSW,4,BUF): REM Set CSW = COUT1, KSW = Server loop 1170 : 1180 & CALL (D,14 * 4096): REM Cold start Applesoft ($E000) 1190 : 1200 POKE BUF + 1,184: POKE BUF,0: REM $B800 1210 HMEM = 7 * 16 + 3: REM $0073 1220 & POKE (D,HMEM,2,BUF): REM HIMEM: $B800 1230 : 1240 & POKE (D,PSTR,PLNG,PBUF): REM POKE the BASIC program 1250 :
Then we POKE pointers dependent on the length of the Applesoft program and simulate a "RUN" command:
1260 POKE BUF + 1,PGND / 256: POKE BUF,PGND - 256 * PEEK (BUF + 1) 1270 VARTAB = 6 * 16 + 9: REM $0069 1280 & POKE (D,VARTAB,2,BUF): REM Set VARTAB = PGND 1290 : 1300 PRGND = 10 * 16 + 15: REM $00AF 1310 & POKE (D,PRGND,2,BUF): REM Set PRGEND = PGND 1320 : 1330 RNPROG = 13 * 4096 + 5 * 256 + 6 * 16 + 6: REM $D566 1340 & CALL (D,RNPROG): REM Start slave program 1350 : 1360 PRINT "BASIC prog '"P$"' running on "D 1370 NEXT D 1380 END 1390 :
This is the standard "suffix" for NadaNet applications, added by EXECing the text file NADADEFS. The subroutine at 60000 sets up a few named pointers. The subroutine at 61000 performs a "census" of serving machines on the net and saves the results in a table at ITBL. (Note that because the census loop is probing machines that may not be serving (or even exist), it temporarily sets the &TIMEOUT value down to 3 * 60ms. so that unresponsive machines do not stall the &PEEK for very long. The default timeout is restored when the census is completed.)
60000 REM NadaNet Definitions for Applesoft 60010 REM MJM - 11/08/04 60020 : 60030 NP = 3 * 256 + 12 * 16 + 15: REM NADANET page ($3CF) 60040 IF PEEK (NP - 2) < > 76 THEN PRINT "NadaNet not loaded.": STOP 60050 IF PEEK (NP) < > 145 THEN PRINT "Not ProDOS version of NadaNet.": STOP 60060 : 60070 BUF = 4 * 4096: REM $4000 60080 MX = 10: REM Max machine ID 60090 BSLF = NP - 3: REM Boot ID in $3CC 60100 & IDTBL(ITBL): REM ID table 60120 RETURN 60130 : 61000 REM Take census of serving machines 61010 & TIMEOUT(3) 61020 FOR D = 2 TO MX: REM PEEK to verify service 61030 & PEEK #(D,NP,4,BUF): REM PEEK config bytes 61040 IF PEEK (1) THEN TY$ = "not":TY = 0: GOTO 61100 61050 TY$ = "(unknown)":TY = 16 61060 IF PEEK (BUF) = 184 THEN TY$ = "(Crate)":TY = 2 61070 IF PEEK (BUF) = 8 THEN TY$ = "(Mserve)":TY = 3 61080 IF PEEK (BUF) = 145 THEN TY$ = "(ProDOS)":TY = 4 61090 IF PEEK (BUF) = 141 THEN TY$ = "(DOS)":TY = 3 61100 POKE ITBL + D,TY 61110 PRINT "Machine "D" "TY$" serving." 61120 NEXT D 61130 & TIMEOUT(): REM Reset retrys 61140 PRINT "Census completed." 61150 RETURN