BPRUN: Broadcast Parallel RUN for NadaNet

Michael J. Mahon - May 31, 2008
Revised - February 5, 2009

Introduction

BPRUN is an Applesoft program that runs on a ProDOS 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 capitalizes on the fact that all slaves receive the same program by using a broadcast protocol to send it to all of them simultaneously. BPRUN can be found on the ProDOS boot disk.

The Program

The Applesoft BASIC program, BPRUN, is presented below, with liberal comments:

Two address constants are defined: the start address of CR.BPRUNNER and the address of the copy of the master's IDTBL that is loaded into each slave machine.

 100  REM  Broadcast Parallel RUN BASIC program
 110  REM           MJM - 02/05/09
 120 :
 200  REM  Addresses in slave machines
 210 BPRN = 2 * 256: REM  CR.BPRUNNER at $200
 220 SITBL = BPRN + 8 * 16: REM  IDTBL at $280
 230 :

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. (These actions are usually taken by the STARTUP program at the time the master machine is booted from disk.) 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 1 up to MX (defined by the initialization code at 60070) to determine which ones are serving and, if so, what type of machine they are. AppleCrate slave machines are marked "2" in IDTBL. If the Message Server (ID=2) is running, then that is noted. If the Message Server is running and is not needed, that machine is forced to re-boot NADA.CRATE. The disk prefix for M/L code loads and an abbreviation for D$"bload" are defined for convenience.

 240  INPUT "Is Message Server needed? (y/N)";A$
 250 NEEDMS = A$ = "Y" OR A$ = "y"
 260 :
 270  GOSUB 60000: REM  Initialize AmperNada
 280  GOSUB 61000: REM  Take census
 290 MSRV =  PEEK (ITBL + 2) = 3: REM  Save pre-boot Msg Server status
 300  IF  NOT NEEDMS AND MSRV THEN  POKE 512,0: &  POKE (2,1012,1,512): &  CALL (2,64098,0): POKE ITBL + 2,0: REM  Force re-boot of Message Server
 310 PFX$ = "/ap/merlin/work/nadanet/"
 320 DB$ =  CHR$ (4) + "bload"
 330 :

The next lines define a general purpose buffer, send the boot code to boot any network-bootable machines needing boot service, then call &SERVE(10) to enter the master's service loop with a count such that it will stay in the service loop for about 200ms. (since, if nothing is requested, each RCVPKT call times out in about 20ms, and this specifies 10 iterations).

The purpose of this &SERVE call is to process the GETID requests of any just-booted AppleCrate slave machines, causing them to be assigned unique IDs. When we return from SERVE, all slave machines will have their IDs and will be running their service loops.

 340  REM  Boot unbooted slaves
 350 PBUF = 2 * 4096: REM  Program buffer
 360  PRINT DB$PFX$"nada.crate,A"PBUF
 370 PSRT =  PEEK (PBUF + 2) * 256: REM   Prog start
 380 PLNG =  PEEK (48840) + 256 *  PEEK (48841): REM  Prog length
 390  & BOOT(PSRT,PLNG,PBUF)
 400  & SERVE(10): REM  Handle AppleCrate II GETIDs
 410  PRINT "Boot of slaves completed."
 420 :

Now that booting is completed, we retake a census to determine how many slaves are present. If the Message Server is not already running and is needed, we require that machine #2 be available, then &BRUN NADA.MSERVE on it. IDTBL is updated to reflect that machine #2 is now the Message Server and not a normal slave.

 430  GOSUB 61000: REM  Re-take census
 440  IF MSRV OR  NOT NEEDMS GOTO 550: REM  Already running or not needed
 450 :
 460  REM  Load Message Server
 470  IF  NOT  PEEK (ITBL + 2) THEN  PRINT "No Msg Server (2)": STOP 
 480  PRINT DB$PFX$"nada.mserve,A"PBUF
 490 PSRT =  PEEK (PBUF + 2) * 256: REM  Prog start
 500 PLNG =  PEEK (48840) + 256 *  PEEK (48841): REM  Prog length
 510  & B RUN (2,PSRT,PLNG,PBUF): REM  BRUN Message Server
 520  PRINT "Message Server now running on 2"
 530 MSRV = 1: POKE ITBL + 2,3: REM  Mark Msg Server
 540 :

In preparation for initializing the slave machines, we now pre-load the CR.BPRUNNER code that will run on page 2 to receive the broadcast Applesoft program. The master's IDTBL is poked into that image at what will be $280 so that each slave machine can determine the environment in which it is running.

We then get the name of the Applesoft program to be RUN and pre-load it as well. Note that the program must have been SAVEd from the default Applesoft start address of $801, and will be LOADed and RUN at that location in the slave machines.

 550  REM  Broadcast RUN Applesoft program
 560 :
 570  PRINT DB$PFX$"cr.bprunner,a"PBUF
 580 ASTAG = 14 * 4096: REM  Applesoft program BCAST tag ($E000)
 590 :
 600  FOR D = 0 TO  PEEK (ITBL): REM  Copy IDTBL to slave's $280
 610  POKE PBUF + 128 + D, PEEK (ITBL + D)
 620  NEXT 
 630 :
 640  PRINT "Run what program? ";: INPUT "";P$
 650  IF P$ = "" THEN  PRINT  CHR$ (4)"catalog,tbas": GOTO 640
 660  PRINT DB$P$",a"PBUF + 256",tbas"
 670 PLNG =  PEEK (48840) + 256 *  PEEK (48841): REM  Prog Length
 680 PSRT = (8 * 256 + 1): REM  Prog start = $0801
 690 :

The CR.BPRUNNER program is then &BRUN on each serving AppleCrate slave machine (IDTBL type = 2), and each machine is counted and reported as ready for the broadcast of the BASIC program.

 700  REM  Start BPRUNNER program on slaves
 710  PRINT "Machines ";
 720 C$ = "": REM  no initial comma
 730  FOR D = 2 TO MX
 740  IF  PEEK (ITBL + D) <  > 2 GOTO 800: REM  Only AppleCrate machines
 750  & B RUN #(D,512,256,PBUF): REM  BRUN the 1-page BPRUNNER at $200
 760  IF  PEEK (1) GOTO 800: REM  Skip if not serving
 770 NS = NS + 1: REM  Count machines
 780  PRINT C$D;
 790 C$ = ", ": REM  Commas after first
 800  NEXT D
 810  PRINT 
 820  IF NS = 0 THEN  PRINT "not available.": STOP 
 830  PRINT "awaiting BASIC program."
 840 :

Now we clear the master's SITBL area ($280) in preparation to receive status from the running slaves, then broadcast the Applesoft program by executing a &BCAST command. What each slave does with the data depends entirely on the code of CR.BPRUNNER.

CR.BPRUNNER waits for a broadcast BCASTREQ packet with a "tag" high byte of $E0 (ASTAG), then receives the subsequent data into the fixed address $801. Following the load of the Applesoft program, it coldstarts BASIC, sets up the program pointers, and jumps to the Applesoft "RUN" entry point to start the program.

 850  FOR D = 2 TO MX: POKE SITBL + D,0: NEXT : REM  Clear status
 860  & BCAST(ASTAG,PLNG,PBUF + 256): REM  Broadcast BASIC program

At this point, the Applesoft program should be RUNning in each slave machine, and the purpose of the following code is to verify that status.

The master machine &SERVEs requests from the slave machines. Each slave, by convention, &POKEs a non-zero status byte into the master's SITBL area corresponding to its ID. The master then scans the SITBL area to verify that all slaves are running the desired program, or to report if they are not.

The work of BPRUN is now finished as the slave machines continue the requested computation.

 870  FOR I = 1 TO MX: & SERVE#(5): NEXT : REM  Serve &PEEKs from BASIC progs
 880  FOR D = 2 TO MX
 890  IF  PEEK (ITBL + D) <  > 2 GOTO 920
 900 ST =  PEEK (SITBL + D): REM  Get slave program status
 910  IF  NOT ST THEN  PRINT "Machine "D" ("ST") not running program.":NR = 1
 920  NEXT 
 930  IF  NOT NR THEN  PRINT "All machines running program."
 940 :
 950  END 
 960 :

This is the standard "suffix" for NadaNet applications, added by EXECing the text file NADADEFS. The subroutine at 60000 verifies that NadaNet is initialized and sets up a few variables. 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 2 * 60ms. so that unresponsive machines do not stall the &PEEK for very long. The default timeout is restored when the census is completed.)

Note that as of NadaNet 3.0, the initialization subroutine (60000) no longer defines BUF, and that the census subroutine (61000) has been modified to create a multi-column report to better accommodate a larger number of machines (MX). NadaNet 3.0 supports machine IDs up to 31, so MX can be adjusted to suit the expected NadaNet environment.

 60000  REM NadaNet Definitions for Applesoft
 60010  REM        MJM - 01/13/09
 60020 :
 60030 MX = 20: REM Max machine ID
 60040  IF  PEEK (973) <  > 76 THEN  PRINT "NadaNet not loaded.": STOP 
 60050 SELF =  PEEK (972)
 60060  & IDTBL(ITBL): REM ID table
 60070  RETURN 
 60080 :
 61000  REM   Take census of serving machines
 61010  & TIMEOUT(2): REM   Set short timeout
 61020 QC =  INT ( PEEK (33) / 13): REM Number of columns
 61030 QL =  INT ((MX + QC - 1) / QC): REM Number of lines
 61040  FOR I = 1 TO QL
 61050  FOR D = I TO MX STEP QL
 61060  IF D = SELF THEN J =  PEEK (975): GOTO 61110
 61070 A$ = "        "
 61080  &  PEEK #(D,975,1,512): REM Machine type
 61090  IF  PEEK (1) THEN K = 0: GOTO 61150
 61100 J =  PEEK (512)
 61110  IF J = 184 THEN A$ = "CRATE   ":K = 2
 61120  IF J = 008 THEN A$ = "MSERVER ":K = 3
 61130  IF J = 145 THEN A$ = "PRODOS  ":K = 4
 61140  IF J = 141 THEN A$ = "DOS     ":K = 5
 61150  POKE ITBL + D,K: REM Save type in IDTBL
 61160  IF D = SELF THEN A$ = "==SELF=="
 61170  IF D < 10 THEN  PRINT " ";
 61180  PRINT D":"A$"  ";
 61190  NEXT D
 61200  PRINT 
 61210  NEXT I
 61220  & TIMEOUT(): REM Reset retrys
 61230  RETURN