$pascal '92070-16288 REV.2001 800514'$ $heap 0$ PROGRAM PFORM; { *NAME: PFORM *SOURCE: 92070-18288 *RELOC: 92070-16288 *PGMR: DAVE NEFF **************************************************************** * (C) COPYRIGHT HEWLETT-PACKARD COMPANY, 1979. ALL RIGHTS * * RESERVED. NO PART OF THIS PROGRAM MAY BE PHOTOCOPIED, * * REPRODUCED OR TRANSLATED TO ANOTHER PROGRAM LANGUAGE WITHOUT * * THE PRIOR WRITTEN CONSENT OF HEWLETT-PACKARD COMPANY. * ****************************************************************} {PFORM is a utility for the RTE-L which configures a memory image file which can be burned onto a PROM card. This allows a user to boot his system from PROM, as well as run programs off PROM, thereby achieving a useful memory only system.} {Read in global constants, types, and variables.} $include 'PFGBL'$ {External procedure definitions. Calls to FMP.} PROCEDURE readf(VAR idcb:dcb; VAR ierr:integer; VAR ibuff:buffer; ilen:integer; VAR len,num:integer);external; PROCEDURE writf(VAR idcb:dcb; VAR ierr:integer; VAR ibuff:buffer; ilen,rec_num:integer);external; {Calls to external assembly language routines} {FUNCTION andi is an external assembly language routine which performs a logical and bit by bit with two integers. The assembly source file is named &ANDI, its relocatable is %ANDI.} FUNCTION andi(I1,I2:integer):integer;external; {FUNCTION ori is an assembly routine which performs a bit by bit logical or of two (single word) integers. Its relocatable file name is %ORI.} FUNCTION ori(I1,I2:integer):integer;external; {split_namr is an interface to the relocatable routine namr. The first integer parsed is always returned in security, and name is always an ASCII file name or null. The interface is written in assembly language, with a source name of &PARSE, and a relocatable name of %PARSE.} { iline: The string containing namrs to be parsed. len: The actual length of the namr string. name: The returned file name if one was specified, null otherwise. security: The security code if one was specified. If the namr type was 1 (just an integer, i.e. an lu) then the integer is returned in this parameter. cartridge: The cartridge number if one was specified, zero if not. start_char: The character number in iline where the parse is to begin. This is updated by the routine namr in order to allow repeated calls to split_namr in case iline contains more than 1 namr string. namr_type: The type of the namr string returned by the routine namr. 0 means no string was parsed, 1 means the string was an integer,3 means it was an ASCII file name. See the namr documentation (in the relocatable routine reference manual) for further information.} PROCEDURE split_namr(VAR iline:input_line;VAR len:integer; VAR name:fname; VAR security,cartridge,start_char, namr_type:integer);external; {The remaining external procecures are in the segments.} {See the segment source listings for documentation on the calling and return sequences.} {PFS3 contains rt_conflict,getids,putids,getshort_ids,putshort_ids, cleanup_process,fi_pr_rp_process,get_file_info,com_line_get,complete_directory, and is in memory during the entire stage 2 of PFORM.} {PFS3 also contains calc_checksum,rec_to_ts,rec_extent,put_fde, part3_init,fl_conflict,nam_in_fde,fix_long_ids,and fix_short_ids.} PROCEDURE part3_init;external; FUNCTION fl_conflict(VAR name:fname; VAR idcb:dcb; VAR ibuff:buffer; ffde_rec:integer):boolean; external; FUNCTION rt_conflict(start_addr:integer; VAR idcb:dcb; VAR ibuff:buffer; VAR name:fname; VAR curr_rec:integer; VAR skel_ids: lo_idseg; VAR free_addr:integer):boolean;external; PROCEDURE rec_to_ts(rec:integer; VAR track,sector:integer);external; PROCEDURE putfde(entry_num:integer; VAR idcb:dcb; VAR ibuff:buffer; VAR name:fname; start_rec:integer; VAR fde: file_directory);external; PROCEDURE nam_in_fde(VAR name:fname; VAR fde:file_directory);external; PROCEDURE fix_long_ids;external; PROCEDURE fix_short_ids;external; PROCEDURE cleanup_process;external; PROCEDURE fi_pr_rp_process;external; PROCEDURE get_file_info;external; PROCEDURE com_line_get;external; PROCEDURE complete_directory;external; {PFS2 contains snap_process,lu_process,outfile_process, sys_process, instuct_printout,crn_init_process and handle error, which use other routines contained in PFS2.} PROCEDURE lu_process;external; PROCEDURE snap_process;external; PROCEDURE outfile_process;external; PROCEDURE sys_process;external; PROCEDURE handle_error(err_type: error_type; {Set of error cases} err_number,err_line: integer; VAR err_file: input_line; err_flen:integer);external; PROCEDURE crn_init_process;external; PROCEDURE instruct_printout;external; {PFS1 contains header_printout, def_labls, and file_init.} PROCEDURE header_printout;external; PROCEDURE file_init;external; PROCEDURE def_labls(VAR num_lb:integer; VAR labl:labl_array);external; {This procedure handles the segment loads.} {@SGLD is a PASCAL library routine.} PROCEDURE segload $ALIAS '@SGLD'$ (segment:fname);external; {fmp_error is called whenever FMP returns an error code in ierr.} { ierr: FMP error code, passed by value, file_name: Name of file which had an associated error.} PROCEDURE fmp_error(ierr:integer;VAR file_name:fname); BEGIN io_error:=true; writeln(ofile,'*ERROR - FMGR',ierr:5,' ON FILE: ',file_name); IF echo_write THEN writeln(ifile,'*ERROR - FMGR',ierr:5,' ON FILE: ', file_name); END; {fmp_err} {pascl_errors catches all pascal runtime system generated errors. It is in a segment since the actual error handling routine (handle_error) aborts the program.} PROCEDURE pascl_error $ALIAS '@PREP'$ (err_type: error_type; err_number,err_line: integer; VAR err_file:input_line; err_flen:integer); BEGIN {Overlay any other segment. This is the last segload.} IF seg_num<>2 THEN segload('PFS2 '); {Call actual error handler.} handle_error(err_type,err_number,err_line,err_file,err_flen); END;{pascl_error} {cnvrt_addr accepts an integer address and returns the record number (recnum) and its offset (starting at 1 for address 0) assuming the file concerned is a memory image, with no header.} { addr: integer address, passed by value, recnum: integer record numeer associated with address, returned, so passed by name, offset: integer offset within the record associated with the address, returned, so passed by name. } PROCEDURE cnvrt_addr(addr:integer; VAR recnum,offset:integer); BEGIN recnum:=(addr DIV recd_len)+1; offset:=(addr MOD recd_len)+1; END; {cnvrt_addr} {getword is a function which is passed an address, and some parameters about the memory image file (no header),and returns the value stored at the passed address. The parameter curr_rec is used to prevent needless disk reads (when the current buffer contains the needed word).} { address: Address of word to be retrieved, passed by value, idcb: dcb of the memory image file the word is in, passed by name to achieve efficient parameter passing, ibuff: buffer associated with the above dcb. Type 1 file access bypasses the idcb. Passed by name for efficiency, name: file name where information exists. Used to pass fmp_error the name when errors occur. Passed by name, curr_rec: The current record number which had the information currently in ibuff, used to prevent needless disk reads. Passed by name since it may be modified.} FUNCTION getword(address:integer;VAR idcb:dcb; VAR ibuff:buffer; VAR name:fname;VAR curr_rec:integer):integer; VAR {integer type declarations.} ierr, {FMP error code identifier.} len, {Number of characters actually read by a readf.} offset, {Buffer offset of the desired word in the record.} recd:integer; {Record number which contains the desired word.} BEGIN cnvrt_addr(address,recd,offset); IF recd<>curr_rec THEN BEGIN {Its a miss. Get the correct record.} readf(idcb,ierr,ibuff,recd_len,len,recd); IF ierr<0 THEN fmp_error(ierr,name) ELSE curr_rec:=recd; END; IF ierr>=0 THEN getword:=ibuff[offset]; END; {getword} {putword stores the word at the passed address into the appropriate memory image file (no header). The word is actually stored onto the disk when the boolean ship is true, or when a record boundary is reached.} { word: Integer to be stored at the specified address, passed by value, address: Integer address where word is to be stored, passed by value, idcb: dcb of the memory image file the word is in,passed by name, ibuff: buffer associated with the above dcb. name: File name where the information is to be stored. Used to pass fmp_error the name when errors occur. Passed by name, curr_rec: The current record number which had the information currently in ibuff, used to prevent needless disk reads. ship: A boolean passed by value, when true, ibuff is written to the disk, when false, ibuff is only written to the disk if the offset is a record boundary.} PROCEDURE putword(word,address:integer; VAR idcb:dcb; VAR ibuff:buffer; VAR name:fname; VAR curr_rec:integer; ship:boolean); VAR {integer type declarations.} ierr, {FMP error code returned here.} len, {Actual number of characters read by a readf call.} offset, {Buffer offset of word to store.} recd:integer; {Record number associated with address.} BEGIN cnvrt_addr(address,recd,offset); IF recd<>curr_rec THEN BEGIN {Its a miss. Get the correct record.} readf(idcb,ierr,ibuff,recd_len,len,recd); IF ierr<0 THEN fmp_error(ierr,name) ELSE curr_rec:=recd; END; IF ierr>=0 THEN BEGIN ibuff[offset]:=word; IF ship OR (offset=recd_len) THEN BEGIN writf(idcb,ierr,ibuff,recd_len,recd); IF ierr<0 THEN fmp_error(ierr,name); END; END; END; {putword} {readline gets a line from the ifile device or file, returns the length of the line of text, and echoes the line to terminal or other lu if needed. It always causes a line feed to occure at the output files (for readability.)} { buff: The buffer of the input line to be read. Passed by name since the buffer is returned to the caller. len: The integer number of characters actually read. Passed by name since it is returned.} PROCEDURE readline(VAR buff:input_line; VAR len:integer); VAR {char type declaration.} character:char; {Used to read a character. PASCAL reads can not be done with packed data types.} i:integer; {Used as a counter.} BEGIN len:=0; character:=' '; {Every prompt loop uses readline. So set repeat_prompt false here to save time and code.} repeat_prompt:=false; {Resetting ifile in interactive cases allows recover from the single case, as well as the echoing feature using only 2 file dcb's (rather than 3).} IF interactive THEN reset(ifile); {read first character, set eoln(ifile) false unless its a .} {A here sets eof(ifile) true but not eoln(ifile).} {Suck up any stray spaces as well.} WHILE (character=' ') AND NOT eof(ifile) DO read(ifile,character); IF eof(ifile) AND NOT interactive THEN pform_ended:=true ELSE IF eof(ifile) AND interactive THEN BEGIN repeat_prompt:=true; rewrite(ifile); writeln(ifile); IF echo_write THEN writeln(ofile); {In this next case, (i.e. RU,PFORM,1,FILE) the typed to the terminal won't be automatically echoed to the file, so we really need two writeln calls.} IF NOT terminal_outfile THEN writeln(ofile); END ELSE BEGIN WHILE (len2 THEN BEGIN segload('PFS2 '); seg_num:=2; END; {Prompt for lu and outfile, process those strings.} lu_process; {Prompt for and process cartridge initialization information.} crn_init_process; abort_or_pform_ended:=abort OR pform_ended; {If interactive first pass, output instructions for part3.} IF (interactive AND first_pform AND NOT (abort_or_pform_ended)) THEN BEGIN first_pform:=false; instruct_printout; END; {Now in part 3 of the PFORM process.} IF NOT (abort_or_pform_ended) THEN BEGIN {****} full_directory:=false; make_fill_file:=true; {PFS3 contains all of the non-I/0 procedures used during the third part of the PFORM process.} IF seg_num<>3 THEN BEGIN segload('PFS3 '); seg_num:=3; END; {Initialize file directory entries, output messages and headers.} part3_init; {This loop handles the configuration portion (part 3) of PFORM.} WHILE NOT (next OR abort_or_pform_ended) DO BEGIN {***} {Prompt for, and read the command string.} com_line_get; IF NOT (repeat_prompt OR abort_or_pform_ended) THEN BEGIN {**} istrc:=1; command:=' '; {The split_namr routine is used to seperate commands.} split_namr(inline,len,command,isecu,icrn,istrc, namr_type); {Commands are significant to 2 characters only, so define the abbreviated command.} abrv_cmmd[1]:=command[1]; abrv_cmmd[2]:=command[2]; IF (abrv_cmmd<>'BU') AND (abrv_cmmd<>'RP') AND (abrv_cmmd<>'FI') AND (abrv_cmmd<>'PR') AND (abrv_cmmd<>'NE') THEN BEGIN {No recognized command was realized.} com_error:=true; writeln(ofile,error3); IF echo_write THEN writeln(ifile,error3); END {Otherwise, start processing the seperate cases.} ELSE IF abrv_cmmd='BU' THEN BEGIN com_error:=false; {Is the record currently at a 2K boundary?} IF (((cur_ofile_rec-1) MOD recds_per_2k)<>0) THEN BEGIN {Not at 2K boundary. BUMP counter.} temp:=(recds_per_2k+1) - (cur_ofile_rec MOD recds_per_2k); END ELSE {Already at 2k boundary. BUMP 2k.} temp:=recds_per_2k; {temp now countains the number of records to BUMP forward.} cur_ofile_rec:=cur_ofile_rec+temp; bump:=true; rem_blocks:=fde_recnum-cur_ofile_rec; IF rem_blocks<=0 THEN BEGIN {We BUMPED past the file directories. BUMP backwards, and give overflow of memory warning.} writeln(ofile,error4); IF echo_write THEN writeln(ifile,error4); cur_ofile_rec:=cur_ofile_rec-temp; END ELSE BEGIN {Output the track, sector, information.} prom_num:=((cur_track*words_per_track+ cur_sector*64) DIV 2048) +1; writeln(ofile,' ',command,temp:8,prom_num:8, cur_track:8,cur_sector:8,rem_blocks); IF echo_write THEN writeln(ifile,' ',command, temp:8,prom_num:8,cur_track:8,cur_sector:8, rem_blocks); rec_to_ts(cur_ofile_rec,cur_track,cur_sector); END END ELSE IF abrv_cmmd='NE' THEN BEGIN next:=true; {Make the file directory entry for the cartridge itself, fill cartridge, mark end of cartridge.} IF NOT (first_boot AND no_directory) THEN complete_directory; END ELSE get_file_info; IF NOT (bump OR overflow OR com_error OR next OR dupl_fname OR (ierr<0)) THEN {Process the FI,PR, and RP commands.} fi_pr_rp_process; END; {**} END; {***} END; {****} END; {*****} IF seg_num<>3 THEN segload('PFS3 '); cleanup_process; END. {PFORM}