mirror of
				https://github.com/SEPPDROID/Digital-Research-Source-Code.git
				synced 2025-10-25 17:34:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3256 lines
		
	
	
		
			104 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3256 lines
		
	
	
		
			104 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**************************************************************************
 | |
|  *	G E N C C P M -  generates an operating system
 | |
|  *
 | |
|  *	Written by Bill Fitler Nov. 1982
 | |
|  *
 | |
|  * GENCCPM is designed to edit the system image for the operating system
 | |
|  * and construct an initialized, loadable image for execution.
 | |
|  * GENCCPM was originally designed for Concurrent CP/M-86 v2.0, and was
 | |
|  * modelled after the old GENSYS.
 | |
|  *
 | |
|  * Revision history:
 | |
|  *      23 Feb 83 whf: add patch module
 | |
|  *	25 Jan 83 whf: combine all modules for easier maintenance
 | |
|  *	20 Jan 83 whf: handles SYSDAT overflow
 | |
|  *	19 Nov 82 whf: added BDOS buffering generation.
 | |
|  *
 | |
|  ************************************************************************/
 | |
| 
 | |
| 
 | |
| /*******************************/
 | |
| /* for serialization purposes: */
 | |
| char *copynote = "COPYRIGHT (C) 1983, DIGITAL RESEARCH 654321 $";
 | |
| /****************************************************/
 | |
| /* up front for patching: Note the internal EOS (0) */
 | |
| char *clearx = "\033H\033E\0\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
 | |
| /****************************************************/
 | |
| 
 | |
| /****** include files ********/
 | |
| #define MAINMODULE
 | |
| #define CCPM
 | |
| #include <genccpm.h>
 | |
| /*****************************/
 | |
| 
 | |
| #ifdef CCPM
 | |
| BYTE *edsysver = "GENCCPM  v3.0  [3/7/83]\n";
 | |
| #endif
 | |
| #ifdef MPM
 | |
| BYTE *edsysver = "GENMPM  vX.X  [??/??/??]\n";
 | |
| #endif
 | |
| 
 | |
| BYTE mainmtitle[50] = "";		/* room for title of main menu */
 | |
| MENU *mainmenu = NULLPTRI;		/* pointers to main menu items    */
 | |
| MENU *xiosmenu = NULLPTRI;		/* pointers to XIOS menu items */
 | |
| MENU *memmenu=NULLPTRI;			/* memory management menu */
 | |
| MENU *syspmenu=NULLPTRI;		/* system parameters menu */
 | |
| MENU *rspmenu=NULLPTRI;			/* rsp edit menu */
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* main module
 | |
| /**************************************************************************/
 | |
| 
 | |
| main(ac,av)
 | |
| WORD ac;
 | |
| BYTE **av;
 | |
| {
 | |
| 	BYTE cmds[CMDSLEN];		/* input area of commands */
 | |
| 	BOOLEAN sysvalid();		/* is the system data valid? */
 | |
| 
 | |
| 	fprintf(stderr,edsysver);
 | |
| 	if( isatty(1) )			/* standard output to a tty? */
 | |
| 		clearit = clearx;	/* yes, do clear screens */
 | |
| 	else	clearit = "";		/* no, don't do clear screens */
 | |
| 	init_sysdat();			/* initialize system data */
 | |
| 	init_edsys();			/* initialize menus */
 | |
| 	if( ac>1 ) {			/* if any command line arguments */
 | |
| 	    copyargs(ac-1,av+1,cmds);	/* simplify cmd parser */
 | |
| 	    verbose = TRUE;		/* turn on wordiness by default */
 | |
| 	    domenu(cmds,mainmenu);	/* do what they've asked for */
 | |
| 	} 
 | |
| 	FOREVER {			/* work with user for a while */
 | |
| 		if(doit) {		/* does user want to quit? */
 | |
| 		    if( sysvalid() )	/* has evrything been defined? */
 | |
| 			break;		/* let them if everything okay */
 | |
| 		    press_return();	/* let them view the message(s) */
 | |
| 		}
 | |
| 		printf("%s",clearit);	/* clear the screen */
 | |
| 		prtmenu(mainmtitle,mainmenu);	/* display user options */
 | |
| 		printf(PROMPT);		/* prompt user */
 | |
| 		if( gets(cmds)!=cmds ){	/* on end of file */
 | |
| 		    close(0);		/* standard input */
 | |
| 		    open("CON:",AREAD);	/* reopen to the console */
 | |
| 		    close(1);		/* standard output: assume redirection */
 | |
| 		    open("CON:",AWRITE);/* reopen to console */
 | |
| 		    clearit = clearx;	/* do the clear screen routine */
 | |
| 		    continue;		/* force user to input blank line */
 | |
| 		}
 | |
| 		if( *cmds==NULL ) 	/* blank line of input */
 | |
| 		    continue;		/* don't bother with blank lines here */
 | |
| 		domenu(cmds,mainmenu);	/* do what they've asked for */
 | |
| 	    }				/*****************************/
 | |
| 					/* we've got user input */
 | |
| 	make_sysfile();			/* construct system image/use old one*/
 | |
| 	gentables();			/* generate the tables */
 | |
| 	fixups();			/* odds and ends on the system image */
 | |
| 	wrapup();			/* close files, etc. */
 | |
| }
 | |
| 
 | |
| /* copyargs: copies args to cbuf for uniform parsing */
 | |
| copyargs(ac,av,cbuf)
 | |
| WORD ac;
 | |
| BYTE **av;
 | |
| BYTE *cbuf;
 | |
| {
 | |
| 	for( *cbuf=NULL; ac>0; --ac, ++av ) {
 | |
| 		strcat(cbuf,*av);
 | |
| 		strcat(cbuf," ");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* init_sysdat: reads SYStem DATa info from:
 | |
| /*	1. SYSFILE, if it exists
 | |
| /*	2. SYSMOD, o.w. if this is the case, it also checks 
 | |
| /*		for existence of all other required modules
 | |
| /**************************************************************************/
 | |
| VOID init_sysdat()
 | |
| {
 | |
| 	REG WORD sf;			/* file descriptor for SysData read */
 | |
| 	LONG grpseek();			/* seek the data group */
 | |
| 	WORD goffs;			/* loc of group */
 | |
| 	EXTERN DLIST *rsps, *dirsearch(); /* directory search fn */
 | |
| 	BYTE *addmem();			/* for initializing memory */
 | |
| 	BYTE *chkmodules(), *xlist[80];	/* list of missing modules */
 | |
| 
 | |
| 	printf("GENerate SYStem image for %s\n",VERLABEL);
 | |
| 	init_sds(sdat_area);		/* initialize program ptrs */
 | |
| 			/***************************************************/
 | |
| 	if(chkmodules(xlist)==NULLPTR  &&  (sf=BROPEN(SYSMOD)) > 0 ) {
 | |
| 	    printf("Constructing new %s file\n",SYSFILE);
 | |
| 					/*** procedureize this? for cmd use */
 | |
| 	    dogensys=TRUE;		/* no SYSFILE, read from modules */
 | |
| 	    if(grpseek(sf,GTYPDATA)<0)	/* seek start of data group */
 | |
| 		CRASH("no data group in SYSDAT module\n");
 | |
| 	    read(sf,sdat_area,SDLEN);	/* read in the data group */
 | |
| 	    /**** may have to init arbitrarily some MAX values here ****/
 | |
| 	    xtrapds = *sd_pul;		/* default number of extra process descriptors */
 | |
| 	    totqcbs = *sd_qul;		/* default number of qcbs */
 | |
| 	    totopen = 0x40;		/* arbitrary total open files & locked recs */
 | |
| 	    qbuflen = *sd_qmalen;	/* init'ed to bytes */
 | |
| 	    rsps = dirsearch("*.RSP");	/* init RSP list */
 | |
| 	    addmem("400,6000,400");	/* start out with this memory allocation */
 | |
| 	    close(sf);			/* don't need it for a while */
 | |
| #ifdef CCPM
 | |
| 	    sf=BROPEN(XIOSMOD);		/* let's go read the XIOS info */
 | |
| 	    if( grpseek(sf,GTYPDATA) <= 0 ) /* if there is not a data group */
 | |
| 		grpseek(sf,GTYPCODE);	/* use the code group instead */
 | |
| 	    goffs = lseek( sf,0L,1 );	/* where did we end up? */
 | |
| 	    initxios_info(sf,goffs);	/* grab info from there */
 | |
| 	    close(sf);			/* don't need it for a while */
 | |
| #endif
 | |
| 	} else if( FALSE  &&		/****** temporarily disable this option!!! ***/
 | |
| 		(sf=BROPEN(SYSFILE)) >= 0 ){ /* does SYSFILE exist? *******/
 | |
| 	    printf("Editting %s file\n",SYSFILE);
 | |
| 	    dogensys = FALSE;		/* tell them not to gen a new system */
 | |
| 	    grpseek(sf,GTYPDATA);	/* get data module in OS */
 | |
| 	    read(sf,sdat_area,SDLEN);	/* read in the info */
 | |
| 	    /**** may have to trace down some lists for MAX values ****/
 | |
| 	    close(sf);			/* don't need it for a while */
 | |
| 	} else {	/***************************************************/
 | |
| 	    if( xlist == NULLPTR ) {
 | |
| 		fprintf(stderr,"Can't find %s module\n",SYSMOD);
 | |
| 	    } else {
 | |
| 		fprintf(stderr,"Can't find these modules:\n%s\n",xlist);
 | |
| 	    }
 | |
| 	    fprintf(stderr,"Please find the correct modules\n");
 | |
| 	    exit(1);
 | |
| 	}				/**** end module check ****/
 | |
| 	if( VERSION != *sd_mpmvn ){	/* a little version number checking */
 | |
| 	    USERR("%s works on OS version %x\n",edsysver,VERSION);
 | |
| 	    USERR("Sys Data area found was from OS version %x\n",*sd_mpmvn);
 | |
| 	    USERR("Please find correct .SYS or %s files\n",MODEXT);
 | |
| 	    exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| #define RDAC 4				/* Read Access mode for 'access' */
 | |
|     BYTE *
 | |
| chkmodules(badbuf)			/* place to put missing module names */
 | |
| BYTE *badbuf;
 | |
| {
 | |
| 
 | |
| 	*badbuf=0;	
 | |
| 	if( access(SYSMOD,RDAC) ) {strcat(badbuf,SYSMOD);strcat(badbuf," ");}
 | |
| 	if( access(SUPMOD,RDAC) ) {strcat(badbuf,SUPMOD);strcat(badbuf," ");}
 | |
| 	if( access(RTMMOD,RDAC) ) {strcat(badbuf,RTMMOD);strcat(badbuf," ");}
 | |
| 	if( access(MEMMOD,RDAC) ) {strcat(badbuf,MEMMOD);strcat(badbuf," ");}
 | |
| 	if( access(CIOMOD,RDAC) ) {strcat(badbuf,CIOMOD);strcat(badbuf," ");}
 | |
| 	if( access(BDOSMOD,RDAC) ) {strcat(badbuf,BDOSMOD);strcat(badbuf," ");}
 | |
| #ifdef NET
 | |
| 	if( access(NETMOD,RDAC) ) {strcat(badbuf,NETMOD);strcat(badbuf," ");}
 | |
| #endif
 | |
| 	if( access(XIOSMOD,RDAC) ) {strcat(badbuf,XIOSMOD);strcat(badbuf," ");}
 | |
| 	if( *badbuf ) 
 | |
| 		return badbuf;
 | |
| 	else	return NULLPTR;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* init_edsys: initializes program variables and menus
 | |
| /**************************************************************************/
 | |
| 
 | |
| VOID init_edsys()
 | |
| {
 | |
| 	MENU *m;			/* mainmenu pointer */
 | |
| 	MENU *x;			/* xiosmenu pointer */
 | |
| 	MENU *me;			/* memory partitions menu */
 | |
| 	MENU *s;			/* sys params menu */
 | |
| 	MENU *r;			/* rsp menu */
 | |
| 	MENU *bldmenu();		/* func to build the menu list */
 | |
| 	BYTE *drvmsg;			/* destination drive message */
 | |
| 	BYTE *delmsg;			/* delete old SYSFILE msg */
 | |
| 	BYTE getdrive();		/* get default drive */
 | |
| 	BYTE *help();			/* func to help user */
 | |
| 	BYTE *doxios();			/* func to do xios menu */
 | |
| 	BYTE *dosysp();			/* func to do sys param menu */
 | |
| 	BYTE *dorsp();			/* func to display & handle RSPs */
 | |
| 	BYTE *dolbl();			/* func to modify version label */
 | |
| 	BYTE *domem();			/* handle memory partitions */
 | |
| 	BYTE *doxbufs();		/* func to construct bdos buffers */
 | |
| 	BYTE *addmem();			/* add mem part */
 | |
| 	BYTE *delmem();			/* delete mem part */
 | |
| 	BYTE *rspinclude();		/* include a bunch of RSPs */
 | |
| 	BYTE *rspexclude();		/* exclude a bunch of RSPs */
 | |
| 	BYTE *doexit();			/* exit the main menu */
 | |
| 
 | |
| 	sprintf(mainmtitle,"*** %s %s Main Menu ***",VERLABEL,PROGNAME);
 | |
| 	destdrv = getdrive();		/* returns the default drive */
 | |
| 	drvmsg = malloc(50);
 | |
| 	sprintf(drvmsg,"%s Output To (Destination) Drive",SYSFILE);
 | |
| 	delmsg = malloc(55);
 | |
| 	sprintf(delmsg,"Delete (instead of rename) old %s file\n",SYSFILE);
 | |
| 	m=NULLPTR; x=NULLPTR; me=NULLPTR; s=NULLPTR; r=NULLPTR;	/* init menu pointers */
 | |
| 
 | |
| 	/***** initialize menus *****/
 | |
| #ifdef CCPM
 | |
| 	m=bldmenu(m,MPROC,help,"help","GENCCPM Help");
 | |
| 	m=bldmenu(m,MBOOL,&verbose,"verbose","More Verbose GENCCPM Messages");
 | |
| #endif
 | |
| #ifdef MPM
 | |
| 	m=bldmenu(m,MPROC,help,"help","GENMPM Help");
 | |
| 	m=bldmenu(m,MBOOL,&verbose,"verbose","More Verbose GENMPM Messages");
 | |
| #endif
 | |
| 	m=bldmenu(m,MDRIV,&destdrv,"destdrive",drvmsg);
 | |
| 	m=bldmenu(m,MBOOL,&doclean,"deletesys",delmsg);
 | |
| 
 | |
| 
 | |
| 	m=bldmenu(m,MPROC,dosysp,"sysparams","Display/Change System Parameters");
 | |
| 
 | |
| 	s=bldmenu(s,MDRIV,sd_srchdisk,"sysdrive","System Drive");
 | |
| 	s=bldmenu(s,MDRIV,sd_tempdisk,"tmpdrive","Temporary File Drive");
 | |
| 	s=bldmenu(s,MBOOL,sd_dayfile,"cmdlogging","Command Day/File Logging at Console");
 | |
| 	s=bldmenu(s,MBOOL,sd_cmode,"compatmode","CP/M FCB Compatibility Mode");
 | |
| 	s=bldmenu(s,MWORD,sd_mmp,"memmax","Maximum Memory per Process (paragraphs)");
 | |
| 	s=bldmenu(s,MBYTE,sd_popen_max,"openmax","Open Files per Process Maximum");
 | |
| 	s=bldmenu(s,MBYTE,sd_plock_max,"lockmax","Locked Records per Process Maximum\n");
 | |
| 
 | |
| 	s=bldmenu(s,MWORD,sd_mpmseg,"osstart","Starting Paragraph of Operating System");
 | |
| 	s=bldmenu(s,MWORD,&totopen,"nopenfiles","Number of Open File and Locked Record Entries");
 | |
| 	s=bldmenu(s,MBYTE,&xtrapds,"npdescs","Number of Process Descriptors");
 | |
| 	s=bldmenu(s,MBYTE,&totqcbs,"nqcbs","Number of Queue Control Blocks");
 | |
| 	s=bldmenu(s,MWORD,&qbuflen,"qbufsize","Queue Buffer Total Size in bytes");
 | |
| 
 | |
| 
 | |
| 	m=bldmenu(m,MPROC,domem,"memory","Display/Change Memory Allocation Partitions");
 | |
| 	me=bldmenu(me,MPROC,addmem,"add","ADD Memory Partition(s)");
 | |
| 	me=bldmenu(me,MPROC,delmem,"delete","DELETE Memory Partition(s)");
 | |
| 
 | |
| 
 | |
| #ifdef MPM
 | |
| 	m=bldmenu(m,MPROC,doxios,"xios","Display/Change XIOS Dependent Values");
 | |
| 
 | |
| 	x=bldmenu(x,MBYTE,sd_ncns,"nconsoles","Number of System Consoles");
 | |
| 	x=bldmenu(x,MBYTE,sd_nlst,"nprinters","Number of System Printers");
 | |
| 	x=bldmenu(x,MBYTE,sd_nccb,"nccbs","Total Character Control Blocks");
 | |
| 	x=bldmenu(x,MBYTE,sd_nflags,"nflags","Number of Flags");
 | |
| 	x=bldmenu(x,MBYTE,sd_tickspsec,"nticks","Number of Ticks Per Second");
 | |
| #endif
 | |
| #ifdef CCPM
 | |
| 	m=bldmenu(m,MPROC,doxbufs,"diskbuffers","Display/Change Disk Buffer Allocation");
 | |
| #endif
 | |
| 
 | |
| 	if( dogensys )
 | |
| 	    m=bldmenu(m,MPROC,dolbl,"oslabel","Display/Change Operating System Label");
 | |
| 
 | |
| 	m=bldmenu(m,MPROC,dorsp,"rsps","Display/Change RSP list\n");
 | |
| 	r=bldmenu(r,MPROC,rspinclude,"include","Include RSPs");
 | |
| 	r=bldmenu(r,MPROC,rspexclude,"exclude","Exclude RSPs");
 | |
| 
 | |
| 
 | |
| 	m=bldmenu(m,MPROC,doexit,"gensys","I'm finished changing things, go GEN a SYStem\n");
 | |
| 
 | |
| 
 | |
| 	mainmenu=m;			/* assign now to longer name */
 | |
| 	xiosmenu=x;			/* assign to longer name */
 | |
| 	memmenu=me;			/*  yah yah */
 | |
| 	syspmenu=s;			/*   "   "  */
 | |
| 	rspmenu=r;			/*   "   "  */
 | |
| }
 | |
| 
 | |
| 
 | |
|     BYTE *
 | |
| doexit()				/* this is really a boolean... */
 | |
| {
 | |
| 	doit = TRUE;
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* sysvalid: validates system data.
 | |
| /*	also calculates certain SYSDAT variables, in the course of validation.
 | |
| /**************************************************************************/
 | |
| #define BYTE_ADJUST(xx) if( xx > 0xFF ) xx = 0xFF
 | |
| #define ALIGN(xx) (((xx)+0xF) & 0xFFF0)
 | |
| 
 | |
|     BOOLEAN
 | |
| sysvalid()
 | |
| {
 | |
| 	REG BOOLEAN isv;		/* is valid? */
 | |
| #ifdef CCPM
 | |
| 	BOOLEAN xiosvalid();		/* check xios info */
 | |
| #endif
 | |
| 	LOCAL WORD nmparts;		/* number of memory partitions */
 | |
| 
 | |
| 	isv = TRUE;			/* innocent until proven guilty */
 | |
| 	if( totopen < *sd_popen_max ) {
 | |
| 		USERR("'nopenfiles' is less than 'openmax'. Please adjust.\n");
 | |
| 		isv = FALSE;
 | |
| 	}
 | |
| 	if( totopen < *sd_plock_max ) {
 | |
| 		USERR("'nopenfiles' is less than 'lockmax'. Please adjust.\n");
 | |
| 		isv = FALSE;
 | |
| 	}
 | |
| 	if( (nmparts=cntmlist(memroot)) <= 0 ) {
 | |
| 		USERR("Memory Partitions need to be adjusted\n");
 | |
| 		isv = FALSE;
 | |
| 	}
 | |
| #ifdef CCPM
 | |
| 	if( !xiosvalid() ) {		/* xios info needs work? */
 | |
| 		USERR("Disk Performance Buffers need to be adjusted\n");
 | |
| 		isv=FALSE;
 | |
| 	}
 | |
| #endif
 | |
| 	if( !isv )
 | |
| 		USERR("Please correct the System Parameters\n");
 | |
| 	return isv;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* make_sysfile: responsible for preparing SYSfile image for table generation
 | |
| /* and 
 | |
| /*	if( dousesys )
 | |
| /*		doctors up SYSFILE for appropriate table generation and fixups
 | |
| /*	else	constructs a SYSfile from assembled modules
 | |
| /**************************************************************************/
 | |
| VOID make_sysfile()
 | |
| {
 | |
| 	BYTE fbuf[20];			/* file name buffer */
 | |
| 
 | |
| 	PUTDRV(fbuf,NEWSYS);
 | |
| 	unlink(fbuf);			/* this is the file we'll be using */
 | |
| 	if( (fns=BWCREAT(fbuf))<0 ) {	/* try to open for output */
 | |
| 	    USERR("can't open new .SYS file (%s)\n",fbuf);
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	if( dogensys )
 | |
| 		gensys();		/* make a new SYS file from modules */
 | |
| 	else	edsys();		/* edit the old SYS file */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* gensys: generates a new SYS file image from modules
 | |
| /**************************************************************************/
 | |
|     VOID
 | |
| gensys()
 | |
| {
 | |
| 	WORD fxm;			/* file descriptor for xios mod */
 | |
| 	BYTE fbuf[20];
 | |
| 
 | |
| 	if( doclean ) {			/* clean up the SYSFILE first? */
 | |
| 	    PUTDRV(fbuf,SYSFILE);
 | |
| 	    unlink(fbuf);		/* delete if there... */
 | |
| 	}
 | |
| 	if(verbose) printf("Generating new SYS file\n");
 | |
| 	FCHECK(fns);			/* File error check */
 | |
| 	write(fns,cmdhdr,SECSIZ);	/* write out a (dummy) command hdr */
 | |
| 	genfix(); 			/* remember the sysdat & sup fixups */
 | |
| 	clsup = xfergrp(SUPMOD,GTYPCODE);
 | |
| 	clos_label = writelbl();	/* write the OS label (returns 0 if none exists) */
 | |
| 	if( clos_label != 0 )
 | |
| 		*sd_verptr = clsup<<4;	/* fill in offset of label rel sup code seg */
 | |
| 	genfix(); clrtm = xfergrp(RTMMOD,GTYPCODE);
 | |
| 	genfix(); clmem = xfergrp(MEMMOD,GTYPCODE);
 | |
| 	genfix(); clcio = xfergrp(CIOMOD,GTYPCODE);
 | |
| 	genfix(); clbdos= xfergrp(BDOSMOD,GTYPCODE);
 | |
| #ifdef NET
 | |
| 	genfix(); clnet = xfergrp(NETMOD,GTYPCODE);
 | |
| #else
 | |
| 	clnet = 0;
 | |
| #endif
 | |
| 	fxm = BROPEN(XIOSMOD);		/* look at XIOS module */
 | |
| 	if( grpseek(fxm,GTYPDATA) >= 0){	/* is there a data group?*/
 | |
| #ifdef CCPM
 | |
| 		USERR("XIOS has separate code and data (small model)");
 | |
| 		USERR("This is not supported in this O.S.: use 8080 model.");
 | |
| 		exit(1);
 | |
| #else
 | |
| 		pure_xios = TRUE;	/* ifso, it's separate code & data */
 | |
| 		genfix(); clxios = xfergrp(XIOSMOD,GTYPCODE);
 | |
| #endif
 | |
| 	} else {
 | |
| 		pure_xios = FALSE;
 | |
| 		clxios = 0;
 | |
| 	}
 | |
| 	cltotal = clsup+clos_label+clrtm+clmem+clcio+clbdos+clnet+clxios;
 | |
| 	/*** finished writing the code portion of NEWSYS ***/
 | |
| 
 | |
| 	fldstart = lseek(fns,0L,1);	/* where in file SYSDAT gets written */
 | |
| 	dsstart = *sd_mpmseg + cltotal;	/* where in memory SYSDAT gets loaded */
 | |
| 	dlsysdat = xfergrp(SYSMOD,GTYPDATA);	/* system data area, fixed later */
 | |
| 
 | |
| 	if( pure_xios )
 | |
| 		dlxios = xferpart_grp(fxm,GTYPDATA,dlsysdat);
 | |
| 	else { genfix(); dlxios = xferpart_grp(fxm,GTYPCODE,dlsysdat);	}
 | |
| 	close(fxm);			/* don't need this file descriptor */
 | |
| 	dltotal = dlsysdat+dlxios;	/* this will change later */
 | |
| 	/*** finished writing STATIC data portion of NEWSYS ***/
 | |
| 
 | |
| 	/*** fill in sysdat here ***/
 | |
| 	sd_supmod->ep_eseg  = sd_supmod->ep_iseg  = *sd_mpmseg;
 | |
| 	sd_rtmmod->ep_eseg  = sd_rtmmos->ep_iseg  = sd_supmod->ep_eseg+clsup+clos_label;
 | |
| 	sd_memmod->ep_eseg  = sd_memmod->ep_iseg  = sd_rtmmod->ep_eseg+clrtm;
 | |
| 	sd_ciomod->ep_eseg  = sd_ciomod->ep_iseg  = sd_memmod->ep_eseg+clmem;
 | |
| 	sd_bdosmod->ep_eseg = sd_bdosmod->ep_iseg = sd_ciomod->ep_eseg+clcio;
 | |
| 	sd_xiosmod->ep_eseg = sd_xiosmod->ep_iseg = sd_bdosmod->ep_eseg+clbdos;
 | |
| #ifdef NET
 | |
| 	sd_netmod->ep_eseg  = sd_netmod->ep_iseg  = sd_xiosmod->ep_eseg+clxios;
 | |
| #endif
 | |
| 	if( pure_xios )			/* compute xios offsets */
 | |
| 		sd_xiosmod->ep_ioff = 0;	/* separate code */
 | |
| 	else	sd_xiosmod->ep_ioff = dlsysdat<<4;	/* mixed code & data, after sysdat */
 | |
| 	sd_xiosmod->ep_eoff = sd_xiosmod->ep_ioff + 3;
 | |
| }
 | |
| 
 | |
| /****************************************************************************/
 | |
| genfix()		/* subroutine to remember SYSDAT & Super fixups */
 | |
| {
 | |
| 	LONG fa;
 | |
| 
 | |
| 	fa = lseek(fns,0L,1);		/* where are we in this file? */
 | |
| 	fixlater(fa+6,F_PUT,&dsstart);	/* fix the SYSDAT address */
 | |
| 	fixlater(fa+8,F_PUT,&(sd_supmod->ep_eoff));	/* Supervisor entry offset */
 | |
| 	fixlater(fa+10,F_PUT,&(sd_supmod->ep_eseg));	/*     "        "   segment */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* edsys: edit the old SYS file image.
 | |
| /*	- copies old sys file into new
 | |
| /*	- if( donew_xios ) will patch in a new XIOS from module
 | |
| /**************************************************************************/
 | |
|     VOID
 | |
| edsys()
 | |
| {
 | |
| 	if(verbose) printf("Editting old SYS file\n");
 | |
| 	write(fns,cmdhdr,SECSIZ);	/* leave room for command hdr */
 | |
| 	cltotal = xfergrp(SYSFILE,GTYPCODE);
 | |
| 	dltotal = xfergrp(SYSFILE,GTYPDATA);
 | |
| 	if( donew_xios ) {
 | |
| 	    ; /* need to read 'xt' from XIOS module */
 | |
| 	} else {
 | |
| #ifdef CCPM
 | |
| 	    ; /* need to init 'xt' from sysdat */
 | |
| #endif
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* fixups: inserts updated SYSDAT page into NEWSYS file, and does some
 | |
| /*	address fixups
 | |
| /**************************************************************************/
 | |
| VOID fixups()
 | |
| {
 | |
| 	REG WORD ii;			/* counter */
 | |
| 	GROUP *cg, *dg;			/* group ptrs */
 | |
| 	UWORD genmfl();			/* (re)Gen Memory Free List */
 | |
| 
 | |
| 	if(verbose) printf("Doing fixups\n");
 | |
| 	FCHECK(fns);			/* File error check */
 | |
| 					/*** fixup the memory list ***/
 | |
| 	if(verbose) {
 | |
| 	    printf("SYS image load map:\n");
 | |
| 	    printf("        Code starts at %4.4x\n",*sd_mpmseg);
 | |
| 	    printf("        Data starts at %4.4x\n",dsstart);
 | |
| 	    printf("       Tables start at %4.4x\n",(dsotables>>4)+dsstart);
 | |
| 	    printf("         RSPs start at %4.4x\n",(dsotables>>4)+dsstart+dltables);
 | |
| 	    printf(" XIOS buffers start at %4.4x\n",rsvd_seg);
 | |
| 	    printf("          End of OS at %4.4x\n",*sd_endseg);
 | |
| 	}
 | |
| 
 | |
| 	if( trimlist(*sd_mpmseg,*sd_endseg) ) {	/* mem part list trimmed? */
 | |
| 	    printf("Trimming memory partitions. New list:");
 | |
| 	    dspmlist(memroot);
 | |
| 	    lseek( fns, locmfl, 0 );
 | |
| 	    ii = (dlsysdat+dlxios)<<4;
 | |
| 	    genmfl(ii);			/*****/
 | |
| 	}
 | |
| #ifdef CCPM
 | |
| 					/*** fixup XIOS info ***/
 | |
| 	fixupxios_info(); 		/* fixes the buffer info (DPH info) */
 | |
| #endif
 | |
| 					/*** fixup the command header ***/
 | |
| 	for( ii=0; ii<SECSIZ; ii++ )	/* zero out command header */
 | |
| 	    cmdhdr[ii] = '\0';
 | |
| 	cg = cmdhdr;			/* code group first */
 | |
| 	cg->gtype = GTYPCODE;		/* init all code vars */
 | |
| 	cg->glen = cltotal;		/* total length of code groups */
 | |
| 	cg->gmin = cltotal;		/* min length of code group */
 | |
| 	cg->gabase = *sd_mpmseg;	/* loads at absolute address */
 | |
| 	dg = cmdhdr + sizeof(*cg);	/* data group next */
 | |
| 	dg->gtype = GTYPDATA;		/* init all data vars */
 | |
| 	dg->glen = dltotal;		/* total length of data group+tables */
 | |
| 	dg->gmin = dltotal;		/* min length of data group */
 | |
| 	dg->gabase = cg->gabase+cltotal;/* abs addr of start of system data */
 | |
| 	lseek( fns, 0L, 0 );		/* seek the beginning of NEWSYS */
 | |
| 	write(fns,cmdhdr,SECSIZ);	/* write out the command header */
 | |
| 
 | |
| 					/*** fixup the sysdat page ***/
 | |
| 	lseek(fns,fldstart,0);		/* seek there in file */
 | |
| 	write(fns,sdat_area,SDLEN);	/* put it into the file */
 | |
| 
 | |
| 					/*** take care of the little fixes ***/
 | |
| 	fixfile();			/* fixes from the list */
 | |
| }
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* wrapup: closes files
 | |
| /**************************************************************************/
 | |
| VOID wrapup()
 | |
| {
 | |
| 	BYTE buf1[20], buf2[20];
 | |
| 
 | |
| 	if(verbose) printf("Wrapping up\n");
 | |
| 	FCHECK(fns);			/* File error check */
 | |
| 	lseek(fns,0L,2);		/* flush NEWSYS buffers? */
 | |
| 	if(strlen(edsysver) != write(fns,edsysver,strlen(edsysver))) {
 | |
| 	    USERR("WRITE FAILURE - the disk may be too full\n");
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	close(fns);			/* all finished with NEWSYS */
 | |
| 	PUTDRV(buf1,OLDSYS); unlink(buf1);
 | |
| 	PUTDRV(buf2,SYSFILE); rename(buf2,buf1);
 | |
| 	PUTDRV(buf1,NEWSYS); rename(buf1,buf2);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************************************************/
 | |
| /***************************************************************/
 | |
| /* edszzz.c: the rest of edsys.c, broken up for shorter compiles */
 | |
| /***************************************************************/
 | |
| /***************************************************************/
 | |
| #ifndef MAINMODULE
 | |
| #include <genccpm.h>
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* init_sds: points the system data pointers to the right place
 | |
| /**************************************************************************/
 | |
|     VOID
 | |
| init_sds(sysdat)
 | |
|     BYTE *sysdat;			/* ptr to system data buffer */
 | |
| {
 | |
| 	sd_supmod     = sysdat + 0x00;	/* (4) */
 | |
| 	sd_rtmmod     = sysdat + 0x08;	/* (4) */
 | |
| 	sd_memmod     = sysdat + 0x10;	/* (4) */
 | |
| 	sd_ciomod     = sysdat + 0x18;	/* (4) */
 | |
| 	sd_bdosmod    = sysdat + 0x20;	/* (4) */
 | |
| 	sd_xiosmod    = sysdat + 0x28;	/* (4) */
 | |
| 	sd_netmod     = sysdat + 0x30;	/* (4) */
 | |
| 	sd_mpmseg     = sysdat + 0x40;
 | |
| 	sd_rspseg     = sysdat + 0x42;
 | |
| 	sd_endseg     = sysdat + 0x44;
 | |
| 	sd_module_map = sysdat + 0x46;
 | |
| 	sd_ncns       = sysdat + 0x47;
 | |
| 	sd_nlst       = sysdat + 0x48; 
 | |
| 	sd_nccb       = sysdat + 0x49;
 | |
| 	sd_nflags     = sysdat + 0x4A;
 | |
| 	sd_srchdisk   = sysdat + 0x4B;
 | |
| 	sd_mmp        = sysdat + 0x4C;
 | |
| 	sd_nslaves    = sysdat + 0x4E;
 | |
| 	sd_dayfile    = sysdat + 0x4F;
 | |
| 	sd_tempdisk   = sysdat + 0x50;
 | |
| 	sd_tickspsec  = sysdat + 0x51;
 | |
| 	sd_lul        = sysdat + 0x52;
 | |
| 	sd_ccb        = sysdat + 0x54;
 | |
| 	sd_flags      = sysdat + 0x56;
 | |
| 	sd_mdul       = sysdat + 0x58;
 | |
| 	sd_nxmds      = sysdat + 0x58;
 | |
| 	sd_mfl        = sysdat + 0x5A;
 | |
| 	sd_nmparts    = sysdat + 0x5A;
 | |
| 	sd_pul        = sysdat + 0x5C;
 | |
| 	sd_nxpd       = sysdat + 0x5C;
 | |
| 	sd_qul        = sysdat + 0x5E;
 | |
| 	sd_nqds       = sysdat + 0x5D;        
 | |
| 	sd_qmau       = sysdat + 0x60;	/* (4) */
 | |
| 	sd_qmastart   = sysdat + 0x62;
 | |
| 	sd_qmalen     = sysdat + 0x64;
 | |
| 	sd_verptr     = sysdat + 0x78;
 | |
| 	sd_vn         = sysdat + 0x7A;
 | |
| 	sd_mpmvn      = sysdat + 0x7C;
 | |
| 	sd_ncondev    = sysdat + 0x83;
 | |
| 	sd_nlstdev    = sysdat + 0x84;
 | |
| 	sd_nciodev    = sysdat + 0x85;
 | |
| #ifdef MPM
 | |
| 	sd_plock_max  = sysdat + 0x8A;
 | |
| 	sd_popen_max  = sysdat + 0x8C;
 | |
| #endif
 | |
| #ifdef CCPM
 | |
| 	sd_lcb        = sysdat + 0x86;
 | |
| 	sd_popen_max  = sysdat + 0x8A;
 | |
| 	sd_plock_max  = sysdat + 0x8B;
 | |
| #endif
 | |
| 	sd_cmode      = sysdat + 0x90;
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /* doxios: gets miscellaneous XIOS values from user */
 | |
| /************************************************************************/
 | |
|     BYTE *
 | |
| doxios(bb,xmtitle)			/* submenu for XIOS value mods */
 | |
|     BYTE *bb;				/* value passed in from menu */
 | |
|     BYTE *xmtitle;
 | |
| {
 | |
| 	BYTE cmds[CMDSLEN];		/* space for additional commands */
 | |
| 	EXTERN MENU *xiosmenu;		/* list of menu items for xios menu */
 | |
| 	EXTERN BYTE *clearit;
 | |
| 
 | |
| 	if( bb != NULLPTR ) {		/* if any "command line" arguments */
 | |
| 	    domenu(bb,xiosmenu);	/* do what they've asked for */
 | |
| 	} else FOREVER {		/* work with user for a while */
 | |
| 		printf("%s",clearit);	/* clear screen */
 | |
| 		prtmenu(xmtitle,xiosmenu);	/* display user options */
 | |
| 		printf(PROMPT);		/* prompt user */
 | |
| 		if( gets(cmds)!=cmds ||	/* on end of file */
 | |
| 		    *cmds==NULL )	/* or blank line of input */
 | |
| 		    break;		/* exit interaction with user */
 | |
| 		domenu(cmds,xiosmenu);	/* do what they've asked for */
 | |
| 	}				/*****************************/
 | |
| 	return NULLPTR;			/* standard return... */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /* dosysp: gets miscellaneous system paramaters from user */
 | |
| /************************************************************************/
 | |
|     BYTE *
 | |
| dosysp(bb,smtitle)			/* submenu for SYSP value mods */
 | |
|     BYTE *bb;				/* value passed in from menu */
 | |
|     BYTE *smtitle;
 | |
| {
 | |
| 	BYTE cmds[CMDSLEN];		/* space for additional commands */
 | |
| 	EXTERN MENU *syspmenu;		/* list of menu items for sysp menu */
 | |
| 	EXTERN BYTE *clearit;
 | |
| 
 | |
| 	if( bb != NULLPTR ) {		/* if any "command line" arguments */
 | |
| 	    domenu(bb,syspmenu);	/* do what they've asked for */
 | |
| 	} else FOREVER {		/* work with user for a while */
 | |
| 		printf("%s",clearit);	/* clear screen */
 | |
| 		prtmenu(smtitle,syspmenu);	/* display user options */
 | |
| 		printf(PROMPT);		/* prompt user */
 | |
| 		if( gets(cmds)!=cmds ||	/* on end of file */
 | |
| 		    *cmds==NULL )	/* or blank line of input */
 | |
| 		    break;		/* exit interaction with user */
 | |
| 		domenu(cmds,syspmenu);	/* do what they've asked for */
 | |
| 	}				/*****************************/
 | |
| 	return NULLPTR;			/* standard return... */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*************************************************************************/
 | |
| /* dorsp: handles rsp editting */
 | |
| /*************************************************************************/
 | |
|     BYTE *
 | |
| dorsp(parm,xmtitle)			/* handle RSP list editting */
 | |
|     BYTE *parm;				/* command value parameter */
 | |
|     BYTE *xmtitle;
 | |
| {
 | |
| 	BYTE cmds[CMDSLEN];		/* space for additional commands */
 | |
| 	EXTERN MENU *rspmenu;		/* list of menu items for rsp menu */
 | |
| 	EXTERN BYTE *clearit;
 | |
| 
 | |
| 	if( parm != NULLPTR ) {		/* if any "command line" arguments */
 | |
| 	    domenu(parm,rspmenu);	/* do what they've asked for */
 | |
| 	} else FOREVER {		/* work with user for a while */
 | |
| 		printf("%s",clearit);	/* clear screen */
 | |
| 		rsp_display(rsps);	/* display what they have to work with */
 | |
| 		prtmenu(xmtitle,rspmenu);	/* display user options */
 | |
| 		printf(PROMPT);		/* prompt user */
 | |
| 		if( gets(cmds)!=cmds ||	/* on end of file */
 | |
| 		    *cmds==NULL )	/* or blank line of input */
 | |
| 		    break;		/* exit interaction with user */
 | |
| 		domenu(cmds,rspmenu);	/* do what they've asked for */
 | |
| 	}				/*****************************/
 | |
| 	return NULLPTR;			/* standard return... */
 | |
| }
 | |
| 
 | |
| /*************************************************************************/
 | |
|     BYTE *
 | |
| rspinclude(rs)
 | |
|     BYTE *rs;
 | |
| {
 | |
| 	BYTE *bp, *cp, *ep;		/* ptrs to take apart command str */
 | |
| 	DLIST *dd;
 | |
| 
 | |
| 	if( rs==NULLPTR || *rs==NULL ) 
 | |
| 	    return "You need to say 'include=xx.rsp,yy.rsp,zz.rsp'";
 | |
| 	for( ep=rs; *ep; ++ep ) ;	/* end of string pointer */
 | |
| 	for( bp=rs; bp<ep; bp=cp+1 ){	/* process each rsp spec */
 | |
| 	    for( cp=bp; *cp && *cp!=','; ++cp ) ;	/* find eos or comma */
 | |
| 	    if( *cp == ',' ) *cp = '\0'; /* null terminate the spec */
 | |
| 	    if( (dd=dirsearch(bp)) == NULLPTR )
 | |
| 		return "Can't find RSP to include";
 | |
| 	    rspjoin(dd);
 | |
| 	}
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| rspjoin(dd)
 | |
|     DLIST *dd;
 | |
| {
 | |
| 	DLIST *rs;
 | |
| 
 | |
| 	if( rsps == NULLPTR )
 | |
| 	    rsps = dd;
 | |
| 	else {
 | |
| 	    for( rs=rsps; rs->dlnext != NULLPTR; rs=rs->dlnext ) ;
 | |
| 	    rs->dlnext = dd;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*************************************************************************/
 | |
|     BYTE *
 | |
| rspexclude(rs)
 | |
|     BYTE *rs;
 | |
| {
 | |
| 	BYTE *bp, *cp, *ep;
 | |
| 	BOOLEAN rspzap();
 | |
| 
 | |
| 	if( rs==NULLPTR || *rs==NULL )
 | |
| 	    return "You need to say 'exclude=aa.rsp,bb.rsp,cc.rsp'";
 | |
| 	for( ep=rs; *ep; ++ep ) ;	/* find eos */
 | |
| 	for( bp=rs; bp<ep; bp=cp+1 ){	/* process each rsp spec */
 | |
| 	    for( cp=bp; *cp && *cp!=','; ++cp ) ;	/* find eos or comma */
 | |
| 	    if( *cp == ',' ) *cp = '\0'; /* null terminate the spec */
 | |
| 	    if( !rspzap(bp) )
 | |
| 		return "Can't find RSP to exclude";
 | |
| 	}
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
|     BOOLEAN
 | |
| rspzap(nm)
 | |
|     BYTE *nm;
 | |
| {
 | |
| 	DLIST **qs, *ps, *rs;
 | |
| 
 | |
| 	if( *nm == '*' ) {		/* zap entire list? */
 | |
| 	    for( ps=rsps; ps!=NULLPTR; ps=rs ) {
 | |
| 		rs = ps->dlnext;
 | |
| 		free(ps);
 | |
| 	    }
 | |
| 	    rsps = NULLPTR;
 | |
| 	    return TRUE;
 | |
| 	}
 | |
| 	for( qs= &rsps; *qs!=NULLPTR; qs = &rs->dlnext ) {	/* zap part of list */
 | |
| 	    rs = *qs;
 | |
| 	    if( namematch(nm,rs->dlname) ) {
 | |
| 		*qs = rs->dlnext;
 | |
| 		free(rs);
 | |
| 		return TRUE;
 | |
| 	    }
 | |
| 	}
 | |
| 	return FALSE;			/* thru loop, can't find it */
 | |
| }
 | |
| 
 | |
| namematch(s1,s2)
 | |
|     BYTE *s1, *s2;
 | |
| {
 | |
| 	for( ; *s1 && *s2; ++s1, ++s2 ) 
 | |
| 	    if( toupper(*s1) != toupper(*s2) )
 | |
| 		return FALSE;
 | |
| 	if( *s1 || *s2 )
 | |
| 	     return FALSE;
 | |
| 	else return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| #define PAGEWIDTH 72
 | |
| rsp_display(rp)
 | |
| DLIST *rp;
 | |
| {
 | |
| 	WORD ii;
 | |
| 
 | |
| 	printf("\nRSPs to be included are:\n");
 | |
| 	for( ii=0; rp!=NULLPTR; rp = rp->dlnext ) {
 | |
| 	    if( (ii += 15) > PAGEWIDTH ) {
 | |
| 		printf("\n");
 | |
| 		ii=15;
 | |
| 	    }
 | |
| 	    printf("   %12.12s",rp->dlname);
 | |
| 	}
 | |
| 	printf("\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* grpseek: seeks in MOD file 'fd' to the start of a code/data group, */
 | |
| /*	returns length (in bytes) of that group */
 | |
| /*************************************************************************/
 | |
|     LONG
 | |
| grpseek(fde,gt)
 | |
|     WORD fde;				/* file descriptor */
 | |
|     WORD gt;				/* group type to look for */
 | |
| {
 | |
| 	WORD gc;			/* group counter */
 | |
| 	LONG dps;			/* pgphs to group base in file */
 | |
| 	GROUP grp;			/* structure for Cmd Hdr info */
 | |
| 
 | |
| 	if( lseek(fde,0L,0) < 0 )	/* seek to beginning of file */
 | |
| 	    CRASH("grp cmd header seek failed\n");
 | |
| 	dps=8;				/* pgphs to start of load group */
 | |
| 	for( gc=0; gc<8; gc++ ) {	/* search up to 8 groups in cmd hdr */
 | |
| 	    read(fde,&grp,sizeof(grp));	/* read each group in header */
 | |
| 	    if( grp.gtype == gt )	/* the group we want */
 | |
| 		    break;
 | |
| 	    dps += grp.glen;		/* add in size of previous group */
 | |
| 	}
 | |
| 	if( grp.gtype != gt )		/* did we find the group? */
 | |
| 	    return ((LONG) FAILURE);	/* nope, so fail */
 | |
| 	dps = dps << 4;			/* pgphs xfm to bytes */
 | |
| 	if( lseek(fde,dps,0) < 0 )	/* seek to start of data group */
 | |
| 	    CRASH("grp group seek failed\n");
 | |
| 	return ((LONG)((grp.glen)<<4));	/* return group size in bytes */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* xfer rtns: transfer a group from file 'module' to file NEWSYS (fns) 
 | |
| /*	returns number of paragraphs in the group
 | |
| /**************************************************************************/
 | |
|     WORD
 | |
| xfergrp(module,group_type)
 | |
|     BYTE *module;			/* module name */
 | |
|     WORD group_type;			/* code? or data? */
 | |
| {
 | |
| 	REG WORD fm;			/* file descriptor for module */
 | |
| 	REG WORD rv;			/* return value */
 | |
| 
 | |
| 	if( (fm=BROPEN(module)) < 0 ) 	/* open the module for binary reading */
 | |
| 		CRASH("can't find a system module\n");
 | |
| 	rv = xferpart_grp(fm,group_type,0);
 | |
| 	close(fm);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /**************************************************************************/
 | |
|     WORD
 | |
| xferpart_grp(fm,gt,skip)
 | |
|     WORD fm;			/* file descriptor for module */
 | |
|     WORD gt;				/* group type: code or data */
 | |
|     UWORD skip;				/* num pgphs to skip in this group */
 | |
| {
 | |
| 	REG WORD ii;			/* counter */
 | |
| 	REG LONG xx, yy;		/* long counters */
 | |
| 	BYTE xferbuf[BUFSIZ];		/* a transfer buffer */
 | |
| 	GROUP grp;			/* a group area */
 | |
| 
 | |
| 	if( lseek(fm,0L,0) < 0 )	/* seek to beginning of file */
 | |
| 	    CRASH("grp cmd header seek failed\n");
 | |
| 	yy = skip+8;			/* num pgphs to skip + in cmd hdr */
 | |
| 	for( ii=0; ii<8; ++ii ) {	/* read group hdrs in cmd hdr */
 | |
| 	    read(fm,&grp,sizeof(grp));
 | |
| 	    if( grp.gtype == gt )
 | |
| 		break;
 | |
| 	    yy += grp.glen;		/* count #pgphs in this group to skip */
 | |
| 	}
 | |
| 	if( grp.gtype!=gt ) CRASH("module doesn't have group\n");
 | |
| 	xx = yy << 4;			/* cvt to bytes */
 | |
| 	if( lseek(fm,xx,0)<0 ) CRASH("couldn't seek in module\n");
 | |
| 	xx = grp.glen; xx -= skip; 	/* pgphs to xfer */
 | |
| 	for( yy=xx<<4; yy>0; yy-=ii ){	/* xfer bytes to NEWSYS */
 | |
| 	    ii = ( yy > BUFSIZ ? BUFSIZ : yy );	/* num bytes per xfer */
 | |
| 	    if( read(fm,xferbuf,ii) != ii ) CRASH("not enough bytes in module\n");
 | |
| 	    write(fns,xferbuf,ii);
 | |
| 	}
 | |
| 	return (WORD)(xx);		/* convert to pgphs */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* help: prints out a help message */
 | |
| /**************************************************************************/
 | |
| 
 | |
| BYTE *helpm1[] = {
 | |
| 	"\n\n",
 | |
| 	"\t\t*** GENCCPM Help Function ***",
 | |
| 	"\t\t============================",
 | |
| 	"     GENCCPM lets you edit and/or generate a system image from",
 | |
| 	"operating system modules on the default drive.  A detailed",
 | |
| 	"explanation of each parameter may be found in the Concurrent CP/M-86",
 | |
| 	"System Guide, Section 2.\n",
 | |
| 	"     GENCCPM assumes the default values shown within square",
 | |
| 	"brackets.  All numbers are Hexadecimal.  To change a parameter,",
 | |
| 	"enter the parameter name followed by '=' and the new value.  Type",
 | |
| 	"<CR> (a carriage return) to enter the assignment.  You can make",
 | |
| 	"multiple assignments if you separate them by a space.  No spaces",
 | |
| 	"are allowed within an assignment.  Example:\n",
 | |
| 	"CHANGES?  verbose=N sysdrive=A: openmax=1A <CR>\n",
 | |
| 	"Parameter names may be shortened to the minimum combination of",
 | |
| 	"letters unique to the currently displayed menu.  Example:\n",
 | |
| 	"CHANGES?  v=N sysd=A: op=1a <CR>\n",
 | |
| 	NULLPTRI
 | |
| };
 | |
| BYTE *helpm2[] = {
 | |
| 	"\n\n",
 | |
| 	"Sub-menus (the last few options without default values) are accessed",
 | |
| 	"by typing the sub-menu name followed by <CR>.  You may enter",
 | |
| 	"multiple sub-menus, in which case each sub-menu will be displayed",
 | |
| 	"in order.  Example:\n",
 | |
| 	"CHANGES?  help sysparams rsps <CR>\n",
 | |
| 	"Enter <CR> alone to exit a menu, or a parameter name, '=' and the",
 | |
| 	"new value to assign a parameter.  Multiple assignments may be",
 | |
| 	"entered, as in response to the Main Menu prompt.\n",
 | |
| 	NULLPTRI
 | |
| };
 | |
| 
 | |
| 
 | |
|     BYTE *
 | |
| help(tx)				/* print a helpful message to user */
 | |
|     BYTE *tx;
 | |
| {
 | |
| 	REG BYTE **cpp;		/* pointer to help strings */
 | |
| 
 | |
| 	for( cpp=helpm1; *cpp != NULLPTR; cpp++ )
 | |
| 		printf("%s\n",*cpp);
 | |
| 	press_return();
 | |
| 	for( cpp=helpm2; *cpp != NULLPTR; cpp++ )
 | |
| 		printf("%s\n",*cpp);
 | |
| 	press_return();
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| /************************************************************************/
 | |
| /* memory list allocation */
 | |
| /**************************************************************************/
 | |
| 
 | |
| 
 | |
|     BYTE *
 | |
| domem(cmdval,memtitle)
 | |
|     BYTE *cmdval;
 | |
|     BYTE *memtitle;
 | |
| {
 | |
| 	WORD cntmlist();		/* does list validation */
 | |
| 	BOOLEAN okl;			/* okay list */
 | |
| 	BYTE cmds[CMDSLEN];		/* place to put input */
 | |
| 	EXTERN MLIST *memroot;		/* memory list root */
 | |
| 	EXTERN MENU *memmenu;		/* memory edit menu */
 | |
| 	EXTERN BYTE *clearit;
 | |
| 
 | |
| 	okl = (cntmlist(memroot) > 0);	/* overlap chk & condense */
 | |
| 	if(cmdval!=NULLPTR) {
 | |
| 	    domenu(cmdval,memmenu);	/* do the commands */
 | |
| 	    okl = (cntmlist(memroot)>0);/* check the results */
 | |
| 	}
 | |
| 	if( !okl || cmdval==NULLPTR ) 	/* do we need to interact with user? */
 | |
| 	    FOREVER {			/* display, the edit */
 | |
| 		printf("%s",clearit);	/* clear the screen (maybe) */
 | |
| 		dspmlist(memroot);	/* here's what it looks like */
 | |
| 		prtmenu(memtitle,memmenu);	/* here's how to edit */
 | |
| 		printf(PROMPT);		/* what to do? */
 | |
| 		if( gets(cmds)!=cmds ||	*cmds==NULL ) {	/* see if the want to get out */
 | |
| 		    if(okl)
 | |
| 			 break;
 | |
| 		    else {
 | |
| 			printf("Please adjust memory partitions\n");
 | |
| 			press_return();
 | |
| 			continue;
 | |
| 		    }
 | |
| 		}
 | |
| 		domenu(cmds,memmenu);	/* edit the memory list */
 | |
| 		okl = (cntmlist(memroot)>0);/* validate the memory list */
 | |
| 	    }
 | |
| 	return NULLPTR;			/* standard return */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
|     BYTE *
 | |
| addmem(ms)				/* add memory partitions */
 | |
|     BYTE *ms;				/* memory partition spec */
 | |
| {
 | |
| 	REG BYTE *pl;			/* pointer to last addr spec */
 | |
| 	REG BYTE *ps;			/* pointer to size spec */
 | |
| 	UWORD f, l, s;			/* values of spec */
 | |
| 	MLIST *intomlist(), *mnew;	/* where to put them */
 | |
| 	EXTERN MLIST *memroot;		/* memory partitions list */
 | |
| 	BYTE *badspec = "Add memory partition spec should look like:\n\tadd=first,last,size";
 | |
| 	BYTE *badval = "Spec: add=first,last,size\n\twhere last>first, size>80";
 | |
| 	BYTE *index();
 | |
| 
 | |
| 	if( (pl=index(ms,','))==0)	/* look for ptr to last addr */
 | |
| 		return badspec;
 | |
| 	else	*pl++ = NULL;		/* terminate first addr spec */
 | |
| 	if( (ps=index(pl,','))==0)	/* look for ptr to size */
 | |
| 		return badspec;
 | |
| 	else	*ps++ = NULL;
 | |
| 	f=atoih(ms);			/* convert first addr spec */
 | |
| 	s=atoih(ps);			/* convert size spec */
 | |
| 	if( *pl=='+' )			/* last addr relative to first? */
 | |
| 		l=atoih(pl+1)+f;	/* yes, so calculate */
 | |
| 	else if( *pl==NULL  ||  *pl=='*' ) {
 | |
| 	    if( *pl=='*' )		/* last addr multiple of size? */
 | |
| 		 l=atoih(pl+1);		/* spec multiple */
 | |
| 	    else l=1;			/* default multiple=1 */
 | |
| 	    l = f+l*s;			/* s in paragraphs */
 | |
| 	} else	l=atoih(pl);		/* no, it's absolute address */
 | |
| 	if( l<=f  ||  s==0 )		/* 1st validation check */
 | |
| 		return badval;		
 | |
| 	if( s<MINKMEM )			/* 2nd validation check */
 | |
| 		return "Memory partition must be at least 80 paragraphs";
 | |
| 	mnew = (MLIST *)malloc(sizeof(*mnew));	/* make room */
 | |
| 	mnew->mlfirst = f;
 | |
| 	mnew->mllast = l;
 | |
| 	mnew->mlsize = s;
 | |
| 	memroot=intomlist(memroot,mnew);	/* sorted insertion into list */
 | |
| 	if( s > l-f )			/* check: size_partition > mem_region */
 | |
| 		return "Warning: partition size larger than memory region";
 | |
| 	return NULLPTR;			/* everything okay */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
|     BYTE *
 | |
| delmem(ms)				/* delete memory partition */
 | |
|     BYTE *ms;
 | |
| {
 | |
| 	REG WORD df, dl;		/* delete first, last */
 | |
| 	MLIST **mm, **mn, *mo, *mp, *mq;	/* necessary list ptrs */
 | |
| 	EXTERN MLIST *memroot;		/* memory partitions list */
 | |
| 	BYTE *pl;
 | |
| 	WORD i;
 | |
| 	BYTE *badspec="To delete a memory partition, say\n\tdelete=1  or  delete=1-3\n";
 | |
| 
 | |
| 	if( *ms == '*' ) {		/* delete all? */
 | |
| 	    for( mp=memroot; mp!=NULLPTR; mp=mq ) {
 | |
| 		mq = mp->mlnext;	/* save the pointer before we free it */
 | |
| 		free(mp);
 | |
| 	    }
 | |
| 	    memroot = NULLPTR;
 | |
| 	    return NULLPTR;		/* everything okay */
 | |
| 	}
 | |
| 	if( (pl=index(ms,'-')) == 0 )
 | |
| 	       	pl=ms;			/* point last to first */
 | |
| 	else 	*pl++ = NULL; 		/* else point to after 'dash' */
 | |
| 	df=atoi(ms);			/* decimal conversion */
 | |
| 	dl=atoi(pl);
 | |
| 	for( i=1, mm=&memroot, mn=NULLPTR, mo=memroot;
 | |
| 	    i<=dl && mo!=NULLPTR;
 | |
| 	    ++i ) {
 | |
| 		if( i==df ) mn=mm;	/* found first, pt to ptr to */
 | |
| 		mm = &mo->mlnext;	/* ptr to ptr */
 | |
| 		mo = mo->mlnext;
 | |
| 	}
 | |
| 	if( mn==NULLPTR || i<=dl )	/* check range of deletions */
 | |
| 	    return badspec;
 | |
| 	mp = *mn;			/* save ptr to deleted list */
 | |
| 	*mn = mo;			/* cut them suckers out of the list */
 | |
| 	for( ; mp!=mo; mp=mq ) {
 | |
| 	    mq = mp->mlnext;
 | |
| 	    free(mp);	
 | |
| 	}
 | |
| 	return NULLPTR;			/* everything ok */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| dspmlist(mroot)
 | |
|     MLIST *mroot;
 | |
| {
 | |
| 	MLIST *mn, *mo;
 | |
| 	REG WORD i, nump;
 | |
| 
 | |
| 	printf("\n\n");
 | |
| 	printf("          Addresses          Partitions    (in paragraphs)\n");
 | |
| 	printf(" #      Start    Last       Size      Qty \n");
 | |
| 	for( i=1, mn=mroot; mn!=NULLPTR; ++i, mn=mo ) {
 | |
| 	    nump = (mn->mllast-mn->mlfirst) / mn->mlsize;
 | |
| 	    printf("%2.2d.     %4.4xh    %4.4xh      %4.4xh   %4.4xh ",
 | |
| 		    i,mn->mlfirst,mn->mllast,mn->mlsize,nump);
 | |
| 	    if( (mo=mn->mlnext) != NULLPTR ) {	/* do some checking */
 | |
| 		if( mn->mllast > mo->mlfirst ) 
 | |
| 		    printf("**overlaps** ");
 | |
| 		if( mn->mlsize > mn->mllast-mn->mlfirst )
 | |
| 		    printf("**partition too big** ");
 | |
| 	    }
 | |
| 	    printf("\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
|     WORD
 | |
| cntmlist(mroot)			/* check for overlaps, condense if possible */
 | |
|     MLIST *mroot;		/* returns number mem partitions, 0 if bad list */
 | |
| {
 | |
| 	MLIST *mn, *mo;			/* ptr within list */
 | |
| 	REG BOOLEAN olap;		/* partitions overlap or bad size */
 | |
| 	REG WORD nump;			/* number of partitions this block */
 | |
| 	REG WORD totnump;		/* keep the count going */
 | |
| 
 | |
| 	totnump = 0;			/* total number partitions */
 | |
| 	olap = FALSE;			/* innocent until proven guilty */
 | |
| 	for( mn=mroot; mn!=NULLPTR && (mo=mn->mlnext)!=NULLPTR; ) {
 | |
| 	    if( mn->mlsize==mo->mlsize ){ 	/* partitions same size? */
 | |
| 	      if( mn->mlfirst==mo->mlfirst && 	/* same partitions? */
 | |
| 		  mn->mllast==mo->mllast ) {
 | |
| 			mn->mlnext=mo->mlnext;
 | |
| 			free(mo);	/* mo is redundant, zap it */
 | |
| 			continue;
 | |
| 	      } else 
 | |
| 	      if( mn->mllast==mo->mlfirst  ||	/* adjacent partition? */
 | |
| 		 mn->mllast==(mo->mlfirst-1) ) {
 | |
| 			mn->mllast=mo->mllast;	/* make 1st partition bigger */
 | |
| 			mn->mlnext=mo->mlnext;	/* zap out mo from list */
 | |
| 			free(mo);	/* we don't need it any more */
 | |
| 			continue;
 | |
| 	      }
 | |
| 	    }
 | |
| 	    if( mn->mllast > mo->mlfirst ) 
 | |
| 		olap=TRUE;		/* partitions overlap */
 | |
| 	    nump = (mn->mllast-mn->mlfirst) / mn->mlsize;
 | |
| 	    if( nump<=0 ) 
 | |
| 		olap=TRUE;		/* size > partition */
 | |
| 	    else totnump += nump;	/* incr this count */
 | |
| 	    mn=mo;			/* continue through loop */
 | |
| 	}
 | |
| 	if( mn!=NULLPTR ) {		/* catch the last in the loop */
 | |
| 	    nump = (mn->mllast-mn->mlfirst) / mn->mlsize;
 | |
| 	    if( nump<=0 ) 
 | |
| 		olap=TRUE;		/* size > partition */
 | |
| 	    else totnump += nump;	/* incr this count */
 | |
| 	}
 | |
| 	if( olap ) 
 | |
| 	     return 0;			/* boo boo, return 0 */
 | |
| 	else return totnump;		/* return num partitions */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
|     MLIST *
 | |
| intomlist(mroot,mi)		/* insert mi into list mroot sorted */
 | |
|     MLIST *mroot;
 | |
|     MLIST *mi;
 | |
| {
 | |
| 	MLIST **mm;			/* ptr to ptr to list */
 | |
| 	MLIST *mn;			/* ptr to list */
 | |
| 
 | |
| 	for( mm=&mroot, mn=mroot;
 | |
| 	    mn != NULLPTR  &&  mi->mlfirst > mn->mlfirst; ){
 | |
| 		mm = &mn->mlnext;
 | |
| 		mn = mn->mlnext;
 | |
| 	}
 | |
| 	mi->mlnext = mn;
 | |
| 	*mm = mi;
 | |
| 	return mroot;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* trimlist: trims the memory list according to OS bounds */
 | |
| /**************************************************************************/
 | |
| 
 | |
|     BOOLEAN				/* returns TRUE iff any adjustments */
 | |
| trimlist(osstart,osend)
 | |
|     WORD osstart;			/* starting pgph */
 | |
|     WORD osend;				/* ending pgph */
 | |
| {
 | |
| 	EXTERN MLIST *memroot;
 | |
| 	REG MLIST **mm, *mn, *mo;	/* ptrs into memlist */
 | |
| 	REG WORD ss, zz;		/* temp vars */
 | |
| 	LOCAL WORD adjusts;		/* number of collisions */
 | |
| 
 | |
| 	adjusts = 0;			/* we haven't adjusted anything yet */
 | |
| 	for( mm=&memroot, mn=memroot; mn!=NULLPTR; mn=*mm ) {
 | |
| 	    if( mn->mlfirst < osstart ) { 
 | |
| 		if( mn->mllast >= osstart ) { /** collision **/
 | |
| 		    ++adjusts;		/* we're doing some adjusting */
 | |
| 		    if( mn->mllast > osend ) {	/* real big mem part? */
 | |
| 			mo = (MLIST *)malloc(sizeof *mo); /* set it up now, adjust it later */
 | |
| 			mo->mlfirst = onbounds(mn->mlfirst,mn->mlsize,osstart);
 | |
| 			mo->mllast = mn->mllast;
 | |
| 			mo->mlsize = mn->mlsize;
 | |
| 			mo->mlnext = mn->mlnext;
 | |
| 			mn->mlnext = mo;
 | |
| 		    }
 | |
| 		    if( (zz=trimit(mn->mlfirst,osstart-1,mn->mlsize)) > 0 ) {
 | |
| 			mn->mllast = osstart-1;
 | |
| 			mn->mlsize = zz;	/* in case it was trimmed */
 | |
| 		    } else {		/* partitions too small, delete it */
 | |
| 			*mm = mn->mlnext;
 | |
| 			free(mn);
 | |
| 			continue;	/* don't increment list ptr */
 | |
| 		    }
 | |
| 		}			/**** end collision ****/
 | |
| 
 | |
| 	    } else if( mn->mlfirst < osend ) { /** collision **/
 | |
| 		++adjusts;		/* we're doing some adjusting */
 | |
| 		if( mn->mllast <= osend ) {	/* does part cross past os? */
 | |
| 		    *mm = mn->mlnext;	/* no, delete it */
 | |
| 		    free(mn);
 | |
| 		    continue;
 | |
| 		}
 | |
| 		ss = onbounds(mn->mlfirst,mn->mlsize,osend);
 | |
| 		if( (zz=trimit(osend,ss,mn->mlsize)) > 0 ){/* set up a smaller mem part */
 | |
| 		    mo = (MLIST *)malloc(sizeof *mo);
 | |
| 		    mo->mlfirst = osend+1;
 | |
| 		    mo->mllast = ss;
 | |
| 		    mo->mlsize = zz;
 | |
| 		    mo->mlnext = mn;
 | |
| 		    *mm = mo;
 | |
| 		    mm = &mo->mlnext;
 | |
| 		}
 | |
| 		if( (zz=trimit(ss,mn->mllast,mn->mlsize)) > 0 ){
 | |
| 		    mn->mlfirst = ss;
 | |
| 		    mn->mlsize = zz;
 | |
| 		} else {		/* partition too small, delete */
 | |
| 		    *mm = mn->mlnext;
 | |
| 		    free(mn);
 | |
| 		    continue;		/* don't bottom out on this loop */
 | |
| 		}
 | |
| 	    }				/**** end collision ****/
 | |
| 
 | |
| 	    mm = &mn->mlnext;		/* set up for next partition check */
 | |
| 	}				/**** end FOR loop ***/
 | |
| 	return adjusts>0;		/* returns TRUE if we adjusted */
 | |
| }
 | |
| 
 | |
|     WORD		/* returns new size of partition, 0 if not enough room */
 | |
| trimit(start,last,olds)
 | |
|     WORD start;				/* start address of partition */
 | |
|     WORD last;				/* end address of partition */
 | |
|     WORD olds;				/* old partition size */
 | |
| {
 | |
| 	REG WORD ss;
 | |
| 
 | |
| 	ss = last-start;		/* size is in paragraphs */
 | |
| 	if( ss< 0 ) return 0;		/* negative size */
 | |
| 	if( ss<MINKMEM ) return 0;	/* sorry, too small */
 | |
| 	else if( ss<olds ) return ss-1;	/* needs to be smaller */
 | |
| 	else return olds;		/* fine just the way it is */
 | |
| }
 | |
| 
 | |
|     WORD
 | |
| onbounds(start,size,nst)
 | |
|     WORD start;				/* original starting boundary */
 | |
|     WORD size;				/* partition size */
 | |
|     WORD nst;				/* new starting address */
 | |
| {					/* returns addr>nst multiple of size from start */
 | |
| 	REG WORD rr;
 | |
| 
 | |
| 	for( rr=start; rr<nst; rr += size )
 | |
| 		;
 | |
| 	return rr;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| /*********************************************************************/
 | |
| /* edsgen.c
 | |
| /* EXPORTS gentable() - the routine which builds all the tables
 | |
| /*********************************************************************/
 | |
| /*********************************************************************/
 | |
| #ifndef MAINMODULE
 | |
| #include <genccpm.h>
 | |
| #endif
 | |
| 
 | |
| 
 | |
| MLOCAL UWORD rsp_last;		/* link to previous RSP */
 | |
| MLOCAL UWORD rsp_seg;		/* where next RSP seg gets written */
 | |
| 
 | |
| #define ALIGN(xx) (((xx)+0xF)&0xFFF0)
 | |
| #define BYTE_ADJUST(yy) if(yy>0xFF) yy=0xFF
 | |
| 
 | |
| 
 | |
| /*************************************************************************/
 | |
|     UWORD				/* return new offset */
 | |
| segwrite(nbs)				/* pad to paragraph boundary */
 | |
|     UWORD nbs;				/* old offset */
 | |
| {
 | |
| 	REG UWORD nps;
 | |
| 
 | |
| 	if( nbs & 0xF ) {		/* does it need padding? */
 | |
| 		nps = 0x10 - (nbs & 0xF);	/* number pad bytes needed */
 | |
| 		write(fns,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",nps);
 | |
| 		return (nbs+nps);
 | |
| 	} else	return nbs;
 | |
| }
 | |
| 
 | |
|     UWORD				/* return num paragraphs */
 | |
| padwrite(nbs)				/* pad to paragraph boundary */
 | |
|     UWORD nbs;				/* num bytes needing padding */
 | |
| {
 | |
| 	return segwrite(nbs) >> 4;		/* convert to pgphs */
 | |
| }
 | |
| 
 | |
|     UWORD
 | |
| datwrite(from,size)
 | |
|     BYTE *from;
 | |
|     WORD size;
 | |
| {
 | |
| 	if( write(fns,from,size) != size ) {
 | |
| 	    fprintf(stderr,"ERROR: Write to new SYS file failed\n");
 | |
| 	    fprintf(stderr,"Out of room on disk?\n");
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| /**************************************************************************/
 | |
| MLOCAL LONG oldladdr=0;		/* save previous address for comparison */
 | |
| #define BIGWORD 0x0000ffffL
 | |
| chkaddr(uaddr)			/* check for SYSDAT overflow */
 | |
|     UWORD uaddr;
 | |
| {
 | |
| 	LONG laddr;
 | |
| 
 | |
| 	laddr = BIGWORD & uaddr;
 | |
| 	if( laddr < oldladdr ) {
 | |
| 	    USERR("ERROR - System Data Area has grown too large\n");
 | |
| 	    USERR("Try reducing the size of some of the tables\n");
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	oldladdr = laddr;	/* save for wrap around compare */
 | |
| }
 | |
| 	/* this is a kluge: I did it because I couldn't get the LONG cast */
 | |
| 	/* to work without doing sign extension... whf 1/20/83 */
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* EXPORT gentables: generates various tables in system data area,
 | |
| /*	and handles RSPs
 | |
| /**************************************************************************/
 | |
| VOID gentables()
 | |
| {
 | |
| 	LOCAL WORD nmparts;		/* number of memory partitions */
 | |
| 	LOCAL WORD nsat_items;		/* number of SAT items in qmau */
 | |
| 	LOCAL UWORD damds;		/* offset of beginning of tables */
 | |
| 	LOCAL UWORD daqbuf;		/* offset of beginning of qbuf */
 | |
| 	LOCAL LONG daend;		/* sys data offset: end of data area */
 | |
| 	LOCAL UWORD doffset;		/* current offset into data area */
 | |
| 	LOCAL UWORD lastlink;		/* helps link lists together */
 | |
| 	LOCAL WORD count;		/* counts num list items being generated */
 | |
| 	LOCAL UWORD ucount;		/* unsigned counter */
 | |
| 	LOCAL MLIST *mn;		/* for trapsing down memroot */
 | |
| 	LOCAL WORD nump;		/* local number partitions */
 | |
| 	LOCAL UWORD saddr;		/* local starting addr */
 | |
| 	LOCAL WORD length;		/* local partition length */
 | |
| 	BYTE buf[SECSIZ];		/* an area for writing */
 | |
| 	LOCAL LONG dmagic;		/* fixup location for xt_alloc info */
 | |
| 	UWORD genmfl();			/* gen a memory free list */
 | |
| 	UWORD genxios();		/* go gen the xios disk buffers */
 | |
| 	DLIST *rs;			/* ptr to rsp list */
 | |
| 	MD md;				/* sample memory descriptor */
 | |
| 	PD pd;				/*   "    process descriptor */
 | |
| 	QCB qcb;			/*   "    queue control block */
 | |
| 	CCB ccb;			/*   "    channel control block */
 | |
| 	LOCK lock;			/*   "    lock list item */
 | |
| 	FLAG flg;			/*   "    flag item */
 | |
| 	SATITEM sat;			/*   "    sub allocation table item */
 | |
| 
 | |
| 	if(verbose) printf("Generating tables\n");
 | |
| 	FCHECK(fns);			/* File error check */
 | |
| 
 | |
| 	/* pre-calculate certain vars */
 | |
| 	bufs_seg = 0;			/* total buf segs allocated in rsvd_seg */
 | |
| 	if( (nmparts=cntmlist(memroot)) <= 0 ) 
 | |
| 		CRASH("invalid memory list, aborting\n");
 | |
| 	totpds = xtrapds + nmparts;	/* 1 pd per memory partition, plus xtras */
 | |
| 	BYTE_ADJUST(totpds);
 | |
| 	totmds = (3*totpds) + nmparts + (nmparts>>1); /* 3 mds per process, plus */
 | |
| 					/* 1 per partition, plus extra for */
 | |
| 					/* memory allocator worst case */
 | |
| 	BYTE_ADJUST(totmds);
 | |
| 	nsat_items = (1+ (1+2*totqcbs)); /* 1 for header entry plus worst case */
 | |
| 
 | |
| 	dltotal = dlsysdat+dlxios;	/* keep track of total data length in pgphs */
 | |
| 	doffset = dltotal << 4;		/* calculate current data offset */
 | |
| 	dsotables = doffset;		/* mark the start of tables */
 | |
| 	chkaddr(doffset);		/* init the function */
 | |
| 
 | |
| 					/* MEMORY FREE LIST */
 | |
| 	locmfl = lseek(fns,0L,1);	/* remember where we are writing this */
 | |
| 	doffset = genmfl(doffset);	/* gen the mem free list */
 | |
| 
 | |
| 	lastlink = 0;			/* UNUSED MEMORY DESCRIPTOR LIST */
 | |
| 	zfill(&md,sizeof md);		/* init the mem descriptor */
 | |
| 	for( count=totmds-nmparts; count>0; --count ) {	/* generate rest of mds */
 | |
| 	    md.md_link = lastlink;
 | |
| 	    lastlink = doffset;
 | |
| 	    doffset += datwrite(&md,sizeof(md));
 | |
| 	}
 | |
| 	*sd_mdul = lastlink;		/* store ptr to mds unused */
 | |
| 
 | |
| 	lastlink = 0;			/* UNUSED PROCESS DESCRIPTOR LIST */
 | |
| 	zfill(&pd,sizeof(pd));
 | |
| 	for( count=totpds; count>0; --count ) {
 | |
| 	    pd.pd_link = lastlink;
 | |
| 	    lastlink = doffset;
 | |
| 	    doffset += datwrite(&pd,sizeof(pd));
 | |
| 	}
 | |
| 	*sd_pul = lastlink;		/* store ptr to pds unused */
 | |
| 
 | |
| 	lastlink = 0;			/* UNUSED QUEUE CONTROL BLOCKS */
 | |
| 	zfill(&qcb,sizeof(qcb));
 | |
| 	for( count=totqcbs; count>0; --count ) {
 | |
| 	    qcb.qc_link = lastlink;
 | |
| 	    lastlink = doffset;
 | |
| 	    doffset += datwrite(&qcb,sizeof(qcb));
 | |
| 	}
 | |
| 	*sd_qul = lastlink;		/* store ptr to unused qcb list */
 | |
| 
 | |
| 	lastlink = 0;			/* UNUSED LOCKED ITEMS LIST */
 | |
| 	zfill(&lock,sizeof(lock));
 | |
| 	for( count=totopen; count>0; --count ) {
 | |
| 	    lock.lo_link = lastlink;
 | |
| 	    lastlink = doffset;
 | |
| 	    doffset += datwrite(&lock,sizeof(lock));
 | |
| 	}
 | |
| 	*sd_lul = lastlink;		/* store ptr to unused lock items list */
 | |
| 
 | |
| #ifdef MPM
 | |
| 	*sd_ccb = doffset;		/* CHANNEL CONTROL BLOCK TABLE */
 | |
| 	zfill(&ccb,sizeof(ccb));
 | |
| 	ccb.cc_mimic = ccb.cc_msource = 0xFF;
 | |
| 	for( count= *sd_nccb; count>0; --count ) {
 | |
| 	    doffset += datwrite(&ccb,sizeof(ccb));
 | |
| 	}
 | |
| #endif
 | |
| #ifdef CCPM
 | |
| 	*sd_ncns = xt.xt_nvcns;
 | |
| 	*sd_ncondev = xt.xt_nccbs;
 | |
| 	*sd_ccb = xt.xt_ccb;
 | |
| 	*sd_nlst = xt.xt_nlcbs;
 | |
| 	*sd_nlstdev = xt.xt_nlcbs;
 | |
| 	*sd_lcb = xt.xt_lcb;
 | |
| 	*sd_nciodev = *sd_ncondev + *sd_nlstdev;
 | |
| 	*sd_tickspsec = xt.xt_ticks_sec;
 | |
| #endif
 | |
| 
 | |
| 	*sd_flags = doffset;		/* FLAG TABLE */
 | |
| 	flg.fl_status = -1;
 | |
| 	flg.fl_pd = -1;
 | |
| 	for( count= *sd_nflags; count>0; --count ) {
 | |
| 	    doffset += datwrite(&flg,sizeof(flg));
 | |
| 	}
 | |
| 
 | |
| 
 | |
| #ifdef CCPM
 | |
| 	doffset = genxios(doffset);	/* go gen the xios buffering info */
 | |
| #endif
 | |
| 	chkaddr(doffset);		/* is table space too big? */
 | |
| 
 | |
| 	doffset = segwrite(doffset);	/* Q Sub Alloc Tab HEADER */
 | |
| 	*sd_qmastart = dsstart + (doffset>>4);	/* fill in seg addr */
 | |
| 	daqbuf = doffset + sizeof(sat)*nsat_items;
 | |
| 	zfill(&sat,sizeof(sat));
 | |
| 	sat.sa_start = --nsat_items;	/* decr before write */
 | |
| 	doffset += datwrite(&sat,sizeof(sat));	/* write the header */
 | |
| 					/* Q Sub Alloc Tab ENTRIES */
 | |
| 	sat.sa_start = daqbuf;
 | |
| 	sat.sa_length = qbuflen;	/* byte value */
 | |
| 	*sd_qmalen = qbuflen>>4;	/* pgph value */
 | |
| 	doffset += datwrite(&sat,sizeof(sat));	/* write the first sat entry */
 | |
| 
 | |
| 	zfill(&sat,sizeof(sat));
 | |
| 	for( count= --nsat_items; count>0; --count ) {
 | |
| 	    doffset += datwrite(&sat,sizeof(sat));/* write the rest of the sat entries */
 | |
| 	}
 | |
| 
 | |
| 	zfill(buf,SECSIZ);		/* Q BUFFER AREA - may go past 64K */
 | |
| 	daend = doffset;		/* abandon doffset */
 | |
| 	for( ucount=qbuflen; ucount>0; ucount -= length ) {
 | |
| 	    length = (ucount>SECSIZ ? SECSIZ : ucount);
 | |
| 	    daend += datwrite(buf,length); 
 | |
| 	}
 | |
| 	doffset = daend & 0xF;		/* get low nibble */
 | |
| 	daend = (daend >> 4) + padwrite(doffset); /* cvt to pgphs */
 | |
| 
 | |
| 	dltables = daend - dltotal;	/* compute length of tables in pgphs */
 | |
| 	dltotal = daend;		/* keep track of total data length */
 | |
| 
 | |
| 	/*** now fill in the rsps ***/
 | |
| 	if(verbose) printf("Appending RSPs to system file\n");
 | |
| 	FCHECK(fns);			/* File error check */
 | |
| 	rsp_last = 0;
 | |
| 	rsp_seg = dsstart + daend;
 | |
| 	for( rs=rsps; rs!=NULLPTR; rs=rs->dlnext ) /* follow the rsp list */
 | |
| 	    xfer_rsp(rs->dlname);
 | |
| 	*sd_rspseg = rsp_last;
 | |
| 	dlrsps = rsp_seg - (dsstart + daend);
 | |
| 	dltotal += dlrsps;
 | |
| 	rsvd_seg = rsp_seg;
 | |
| #ifdef CCPM
 | |
| 	length = xt.xt_alloc;		/* how much memory is XIOS requesting */
 | |
| 	xt.xt_alloc = rsvd_seg+bufs_seg; /* this is the place they can use */
 | |
| 	dmagic = fldstart;		/* location of SYSDAT in SYS file */
 | |
| 	dmagic += XTABALLOC;		/* add in where xt_alloc goes to */
 | |
| 	fixlater(dmagic,F_PUT,&(xt.xt_alloc));	/* fix this number later on */
 | |
| 	bufs_seg += length;		/* allocate requested space */
 | |
| #endif
 | |
| 	*sd_endseg = rsvd_seg+bufs_seg;	/* this is where we reserve to */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* genmfl: writes out a memory free list, assuming file 'fns' is seeking
 | |
| /*	correctly into file.
 | |
| /**************************************************************************/
 | |
|     UWORD
 | |
| genmfl(doffset)
 | |
|     UWORD doffset;
 | |
| {
 | |
| 	REG MLIST *mn;			/* ptr into EDSYS mem list */
 | |
| 	LOCAL WORD nump;
 | |
| 	LOCAL UWORD lastlink, length, saddr; /* counter vars */
 | |
| 	MD md;				/* mem desc struct */
 | |
| 
 | |
| 	*sd_mfl = doffset;		/* gen this list forward */
 | |
| 	zfill(&md,sizeof(md));		/* init the data structure */
 | |
| 	for( mn=memroot; mn!=NULLPTR; mn=mn->mlnext ) {
 | |
| 	    nump = (mn->mllast - mn->mlfirst) / mn->mlsize;
 | |
| 	    saddr = mn->mlfirst;	/* use this starting address */
 | |
| 	    length = md.md_length = mn->mlsize; /* size in pgphs */
 | |
| 	    for( ; nump>0; --nump ) {	/* for each partition */
 | |
| 		doffset += sizeof(md);	/* incr by sizeof object */
 | |
| 		lastlink = doffset;	/* record address of where this will go */
 | |
| 		if( nump == 1 )	{	/* on this group's last partition */
 | |
| 		    length = md.md_length = mn->mllast-saddr; /* stick in rmdr */
 | |
| 		    if( mn->mlnext == NULLPTR ) /* on the very last partition */
 | |
| 			lastlink=0;	/* set end of list */
 | |
| 		}
 | |
| 		md.md_link = lastlink;	/* thread the list */
 | |
| 		md.md_start = saddr;	/* set the starting addr */
 | |
| 		write(fns,&md,sizeof(md));	/* put it into the NEWSYS file */
 | |
| 		saddr += length;	/* next md starts here */
 | |
| 	    }
 | |
| 	}				/* finished with memory partitions */
 | |
| 	return doffset;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
|     VOID
 | |
| zfill(addr,num)
 | |
|     BYTE *addr;
 | |
|     WORD num;
 | |
| {
 | |
| 	for( ; num>0; --num )
 | |
| 		*addr++ = '\0';
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /**************************************************************************/
 | |
| #define RSPHDR struct rsp_header
 | |
| RSPHDR {
 | |
| 	UWORD rs_link;			/* link to other RSPs */
 | |
| 	WORD rs_sdatvar;		/* SYSDAT offset: num copies */
 | |
| 	BYTE rs_ncopies;		/* local: num copies */
 | |
| 	BYTE rs_space[11];		/* fill up rest of header */
 | |
| };
 | |
| 
 | |
| RSPHDR *rhdr;				/* RSP's header */
 | |
| PD *rpd;				/* RSP's process descriptor area */
 | |
| UDA *ruda;				/* RSP's uda area */
 | |
| #define RHDRSIZE (sizeof(RSPHDR)+sizeof(PD)+sizeof(UDA))
 | |
| #define RHDRPGPHS (RHDRSIZE>>4)
 | |
| MLOCAL BYTE rh[RHDRSIZE];		/* where to read the RSP header */
 | |
| MLOCAL BYTE rspnbuf[9];			/* room for copy of pd name */
 | |
| MLOCAL BYTE *rspname;			/* name of RSP */
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| xfer_rsp(rspnm)
 | |
|     BYTE *rspnm;			/* name of rsp */
 | |
| {
 | |
| 	LOCAL WORD fr;			/* rsp file descriptor */
 | |
| 	LOCAL WORD ncs;			/* number of copies */
 | |
| 
 | |
| 	rhdr = rh;			/* init these vars */
 | |
| 	rpd = rh+sizeof(*rhdr);		/* points to process descriptor */
 | |
| 	ruda = rh+sizeof(*rhdr)+sizeof(*rpd);	/* points to user data area */
 | |
| 	if( (fr=BROPEN(rspnm)) < 0 ) {
 | |
| 	    printf("can't open RSP %s\n",rspnm);
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	if( grpseek(fr,GTYPDATA) > 0L){	/* is there a data group? */
 | |
| 	    ncs = gwab_hdr(fr);
 | |
| 	    if( rpd->pd_mem == 0 )
 | |
| 		 xfer_separate_rsp(fr,ncs);
 | |
| 	    else xfer_pure_rsp(fr,ncs);
 | |
| 	} else {
 | |
| 	    grpseek(fr,GTYPCODE);
 | |
| 	    ncs = gwab_hdr(fr);
 | |
| 	    if( rpd->pd_mem == 0 )
 | |
| 		 xfer_mixed_rsp(fr,ncs);
 | |
| 	    else {
 | |
| 		USERR("RSP %s has non zero MEM field and no data group\n",
 | |
| 			rspnm);
 | |
| 	    }
 | |
| 	}
 | |
| 	close(fr);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
|     WORD
 | |
| gwab_hdr(fr)
 | |
|     WORD fr;
 | |
| {
 | |
| 	REG WORD rv;			/* return val */
 | |
| 	EXTERN BYTE sdat_area[];
 | |
| 
 | |
| 	rspname = NULLPTR;		/* blank out this name */
 | |
| 	read(fr,rh,RHDRSIZE);
 | |
| 	strncpy(rspnbuf,rpd->pd_name,8);/* store this in case we need it */
 | |
| 	if( rhdr->rs_sdatvar != 0 ) {
 | |
| 		rspname = rspnbuf;
 | |
| 		rv = sdat_area[rhdr->rs_sdatvar] - 1;
 | |
| 	} else {
 | |
| 	    if( rhdr->rs_ncopies > 0 )
 | |
| 		rspname = rspnbuf;
 | |
| 	    rv = rhdr->rs_ncopies;
 | |
| 	}
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| xfer_separate_rsp(fr,ncs)		/* (many) data and (many) code segs */
 | |
|     WORD fr;				/* file desc of RSP file */
 | |
|     WORD ncs;				/* number of copies */
 | |
| {
 | |
| 	WORD ii;			/* counter */
 | |
| 
 | |
| 	for( ii=0; ii<=ncs; ++ii) {
 | |
| 	    ruda->ud_dsinit = ruda->ud_esinit = ruda->ud_ssinit = rsp_seg;
 | |
| 	    ruda->ud_csinit = rsp_seg + (grpseek(fr,GTYPDATA)>>4);
 | |
| 	    rhdr->rs_ncopies = ii;
 | |
| 	    if( rspname ) 		/* do we need to insert a modfied name? */
 | |
| 		putname(rpd->pd_name,rspname,ii);
 | |
| 	    rhdr->rs_link = rsp_last;	/* link together the list */
 | |
| 	    rsp_last = rsp_seg;		/* remember this for next time */
 | |
| 	    write(fns,rh,RHDRSIZE);	/* write out the RSP header */
 | |
| 	    rsp_seg += RHDRPGPHS + xferpart_grp(fr,GTYPDATA,RHDRPGPHS);
 | |
| 	    rsp_seg += xferpart_grp(fr,GTYPCODE,0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| xfer_pure_rsp(fr,ncs)			/* (many) data segs and one code segs */
 | |
|     WORD fr;				/* file desc of RSP file */
 | |
|     WORD ncs;				/* number of copies */
 | |
| {
 | |
| 	WORD ii;			/* counter */
 | |
| 
 | |
| 	ruda->ud_csinit = rsp_seg + (1+ncs) * (grpseek(fr,GTYPDATA) >> 4);
 | |
| 	for( ii=0; ii<=ncs; ++ii) {
 | |
| 	    ruda->ud_dsinit = ruda->ud_esinit = ruda->ud_ssinit = rsp_seg;
 | |
| 	    rhdr->rs_ncopies = ii;
 | |
| 	    if( rspname ) 		/* do we need to insert a modfied name? */
 | |
| 		putname(rpd->pd_name,rspname,ii);
 | |
| 	    rpd->pd_mem = 8; 		/* magic ptr to md in rsphdr */
 | |
| 	    rhdr->rs_link = rsp_last;	/* link together the list */
 | |
| 	    rsp_last = rsp_seg;		/* remember this for next time */
 | |
| 	    write(fns,rh,RHDRSIZE);	/* write out the RSP header */
 | |
| 	    rsp_seg += RHDRPGPHS + xferpart_grp(fr,GTYPDATA,RHDRPGPHS);
 | |
| 	}
 | |
| 	rsp_seg += xferpart_grp(fr,GTYPCODE,0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| xfer_mixed_rsp(fr,ncs)			/* (many) mixed code/data segs */
 | |
|     WORD fr;				/* file desc of RSP file */
 | |
|     WORD ncs;				/* number of copies */
 | |
| {
 | |
| 	WORD ii;			/* counter */
 | |
| 
 | |
| 	for( ii=0; ii<=ncs; ++ii) {
 | |
| 	    ruda->ud_csinit = ruda->ud_dsinit = ruda->ud_esinit = 
 | |
| 		ruda->ud_ssinit = rsp_seg;
 | |
| 	    rhdr->rs_ncopies = ii;
 | |
| 	    if( rspname ) 		/* do we need to insert a modfied name? */
 | |
| 		putname(rpd->pd_name,rspname,ii);
 | |
| 	    rhdr->rs_link = rsp_last;	/* link together the list */
 | |
| 	    rsp_last = rsp_seg;		/* remember this for next time */
 | |
| 	    write(fns,rh,RHDRSIZE);	/* write out the RSP header */
 | |
| 	    rsp_seg += RHDRPGPHS + xferpart_grp(fr,GTYPCODE,RHDRPGPHS);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| putname(to,from,num)
 | |
|     BYTE *to;
 | |
|     BYTE *from;
 | |
|     WORD num;
 | |
| {
 | |
| 	BYTE buf[15];
 | |
| 	BYTE *bp;
 | |
| 	WORD jj;
 | |
| 
 | |
| 	for( bp=buf, jj=0; *from  &&  *from != ' '  &&  jj<8; ++jj )
 | |
| 		*bp++ = *from++;
 | |
| 	if( jj>6 )
 | |
| 		bp = buf+7;
 | |
| 	sprintf(bp,"%x      ",num);
 | |
| 	for( bp=buf, jj=0; jj<8; jj++ )
 | |
| 		*to++ = *bp++;
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /* dolbl: modify operating system version label */
 | |
| /************************************************************************/
 | |
| #define MAXLBL 80
 | |
| 
 | |
|     BYTE *
 | |
| dolbl(txt,lbtitle)
 | |
|     BYTE *txt;				/* cmd val */
 | |
|     BYTE *lbtitle;
 | |
| {
 | |
| 	DLIST *sl, *psl;		/* ptrs to system labels */
 | |
| 	BYTE mb[MAXLBL];		/* max size of each line */
 | |
| 	EXTERN BYTE *clearit;
 | |
| 
 | |
| 	sl = psl = syslbls;		/* init ptrs */
 | |
| 	printf("%s\n%s\n",clearit,lbtitle);
 | |
| 	printf("Current message is:\n");
 | |
| 	if( syslbls==NULLPTR )
 | |
| 		printf("<null>\n");
 | |
| 	else for( ; sl != NULLPTR; sl = (psl=sl)->dlnext ) 
 | |
| 		printf("%s\n",sl->dlname);
 | |
| 	printf("\nAdd lines to message.  Terminate by entering only RETURN:\n");
 | |
| 	FOREVER {
 | |
| 	    if( gets(mb) != mb  ||  *mb==NULL )
 | |
| 		break;
 | |
| 	    sl = (DLIST *)malloc(sizeof(*sl));	/* alloc a list item */
 | |
| 	    sl->dlname = malloc(sizeof(mb));
 | |
| 	    strcpy(sl->dlname,mb);	/* copy message to new space */
 | |
| 	    sl->dlnext = NULLPTR;
 | |
| 	    if( psl==NULLPTR )		/* new list? */
 | |
| 		syslbls = psl = sl;	/* this will be the first line */
 | |
| 	    else {
 | |
| 		psl->dlnext = sl; 
 | |
| 		psl = psl->dlnext; 
 | |
| 	    }
 | |
| 	}
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| /************************************************************************/
 | |
| /* writelbl: writes the OS label to the NEWSYS file, returning the length
 | |
| /*	of the label (in pgphs)
 | |
| /************************************************************************/
 | |
|     WORD
 | |
| _wrlbl(str)				/* subroutine to writelbl */
 | |
|     BYTE *str;
 | |
| {
 | |
| 	REG WORD slen;
 | |
| 
 | |
| 	slen=strlen(str);		/* length of string to be written */
 | |
| 	write(fns,str,slen);		/* write string to file NEWSYS */
 | |
| 	write(fns,"\015\012",2);	/* cr,lf */
 | |
| 	return slen+2;
 | |
| }
 | |
| 
 | |
|     WORD
 | |
| writelbl()				/* returns num pgphs written */
 | |
| {
 | |
| 	REG WORD cc;			/* count of chars output */
 | |
| 	DLIST *sl;			/* list ptr */
 | |
| 
 | |
| 	cc = 0;
 | |
| 
 | |
| 	cc += _wrlbl("");		/* crlf */
 | |
| 	cc += _wrlbl(version);		/* write out the version label */
 | |
| 	cc += _wrlbl(copyright);	/* followed by copyright notice */
 | |
| 	for( sl=syslbls; sl!=NULLPTR; sl=sl->dlnext )
 | |
| 	    cc += _wrlbl(sl->dlname);	/* followed by all lines in OS label */
 | |
| 	cc += _wrlbl("$");		/* terminate message for printstr fn */
 | |
| 	return padwrite(cc);		/* writes enough zeroes to pad to pgph bdry */
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* fixup routines: remembers and posthumously patches the SYS file
 | |
| /***************************************************************************/
 | |
| 
 | |
| FIX *fixroot = NULLPTRI;		/* list of fixes */
 | |
| 
 | |
| fixlater(fa,ft,fp)			/* records the fix in the list */
 | |
|     LONG fa;				/* addr within NEWSYS to fix */
 | |
|     WORD ft;				/* type of fix: F_ADD or F_PUT */
 | |
|     WORD *fp;				/* ptr to val used in fixing */
 | |
| {
 | |
| 	FIX *fx;
 | |
| 
 | |
| 	fx = (FIX *)malloc(sizeof *fx);	/* alloc a list item */
 | |
| 	fx->f_addr = fa;
 | |
| 	fx->f_type = ft;
 | |
| 	fx->f_ptr = fp;
 | |
| 	fx->f_next = fixroot;		/* insert at head of list */
 | |
| 	fixroot = fx;
 | |
| }
 | |
| 
 | |
| fixfile()				/* does the fixes in the file */
 | |
| {
 | |
| 	FIX *fx;
 | |
| 	WORD fval;			/* the value to stuff */
 | |
| 	LONG lastval;
 | |
| 
 | |
| 	lastval=lseek(fns,0L,2);
 | |
| 
 | |
| 	for( fx=fixroot; fx!=NULLPTR; fx = fx->f_next ) {
 | |
| 	    if(fx->f_addr > lastval) {
 | |
| 		printf("fixfile: %X %x %x\n",fx->f_addr,fx->f_type,fx->f_ptr);
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    if( lseek(fns,fx->f_addr,0) < 0L )
 | |
| 		CRASH("fixup seek failure\n");
 | |
| 	    if( fx->f_type == F_ADD ) {	/* F_ADD the value to what's there */
 | |
| 		read(fns,&fval,2);
 | |
| 		fval += *(fx->f_ptr);
 | |
| 		lseek(fns,-2L,1);	/* backup to write */
 | |
| 	    } else			/* must be a F_PUT */
 | |
| 		fval = *(fx->f_ptr);
 | |
| 	    write(fns,&fval,2);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /************************************************************************/
 | |
| /* edsdisk - This module exports the following routines:
 | |
| /*
 | |
| /* initxios_info(xf,goff)	'xf' is XIOSMOD file descriptor, 'goff'
 | |
| /*	is offset of data group in XIOSMOD (including header);  
 | |
| /*	this module inits the buffering info questions.
 | |
| /* doxbufs(txt,title)	'txt' is a command, 'title' is the prompt for this fn;
 | |
| /*	this routine is called within the MENU module, and prompts user
 | |
| /*	for xios info.
 | |
| /* xiosvalid()  returns a BOOLEAN value indicating whether the xios info
 | |
| /*	is valid.
 | |
| /* genxios(doffset)  generates the buffers for the xios, starting at 'doffset'
 | |
| /*	in the system data area.
 | |
| /* fixupxios_info()	writes DPH fixups required by genxios
 | |
| /**************************************************************************/
 | |
| /**************************************************************************/
 | |
| #ifndef MAINMODULE
 | |
| #include <genccpm.h>
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* edsdisk.h : defines the data structures used in this module
 | |
| /**************************************************************************/
 | |
| 
 | |
| #define ADDR UWORD			/* Address (offset) within SysDat */
 | |
| #define SEG  UWORD			/* Segment address (may be outside SysDat) */
 | |
| 
 | |
| #define DPH struct dph_item
 | |
| DPH {					/* Disk Parameter Header structure */
 | |
| 	ADDR dph_xlt;			/* Translation Table Address */
 | |
| 	BYTE dph_space[6];		/* reserved & Media Flag */
 | |
| 	ADDR dph_dpb;			/* Disk Parameter Block Address */
 | |
| 	ADDR dph_csv;			/* CheckSum Vector Address */
 | |
| 	ADDR dph_alv;			/* Allocation Vector Address */
 | |
| 	ADDR dph_dirbcb;		/* Dir Buffer Control Block Header Address */
 | |
| 	ADDR dph_datbcb;		/* Data Buffer Control Block Header Address */
 | |
| 	SEG  dph_hstbl;			/* Hash Table Segment */
 | |
| };
 | |
| 
 | |
| #define DPB struct dpb_item
 | |
| DPB {					/* Disk Parameter Block structure */
 | |
| 	WORD dpb_spt;			/* Sectors Per Track */
 | |
| 	BYTE dpb_bsh;			/* Allocation Block Shift Factor */
 | |
| 	BYTE dpb_blm;			/* Allocation Block Mask */
 | |
| 	BYTE dpb_exm;			/* Extent Mask */
 | |
| 	WORD dpb_dsm;			/* Disk Storage Maximum */
 | |
| 	WORD dpb_drm;			/* Directory Maximum */
 | |
| 	WORD dpb_dav;			/* Directory Allocation Vector */
 | |
| 	WORD dpb_cks;			/* Checksum Vector Size */
 | |
| 	WORD dpb_off;			/* Track Offset */
 | |
| 	WORD dpb_psh;			/* Physical Record Shift Factor */
 | |
| 	WORD dpb_phm;			/* Physical Record Mask */
 | |
| };
 | |
| 
 | |
| #define BCBH struct bcbh_item
 | |
| BCBH {					/* Buffer Control Block Header structure */
 | |
| 	ADDR bcbh_lr;			/* BCB List Root */
 | |
| 	BYTE bcbh_pm;			/* BCB Process Max */
 | |
| };
 | |
| 
 | |
| #define BCB struct bcb_item
 | |
| BCB {					/* Buffer Control Block structure */
 | |
| 	BYTE bcb_drv;			/* Drive */
 | |
| 	BYTE bcb_record[3];		/* Record Number */
 | |
| 	BYTE bcb_wflg;			/* Write Pending Flag */
 | |
| 	BYTE bcb_seq;			/* Sequential Access Counter */
 | |
| 	WORD bcb_track;			/* Logical Track Number */
 | |
| 	WORD bcb_sector;		/* Logical Translated Sector Number */
 | |
| 	UWORD bcb_bufptr;		/* Buffer Offset (for DIRBCB) / Segment (for DATBCB) */
 | |
| 	ADDR bcb_link;			/* Link to next BCB */
 | |
| 	WORD bcb_pdadr;			/* Process Descriptor Address */
 | |
| };
 | |
| 
 | |
| 
 | |
| #define DINFO struct dirbuf_information
 | |
| DINFO {
 | |
| 	BOOLEAN d_modified;		/* DPH was modified? */
 | |
| 	BOOLEAN d_hashing;		/* user wants hashing? */
 | |
| 	WORD d_ndirbs;			/* Num dir buffers */
 | |
| 	WORD d_sdirbs;			/* Size dir buffers */
 | |
| 	WORD d_pmdirbs;			/* Process Max Number Dir Buffs */
 | |
| 	WORD d_ndatbs;			/* Num data buffers */
 | |
| 	WORD d_sdatbs;			/* Size data buffers */
 | |
| 	WORD d_pmdatbs;			/* Process Max Number Dir Buffs */
 | |
| 	WORD d_shash;			/* Size of hash table */
 | |
| 	WORD d_alvsize;			/* size of Alloc Vector area (in bytes ) */
 | |
| 	WORD d_blksize;			/* size of disk Allocation Block */
 | |
| 	WORD d_csvsize;			/* size of Checksum Vector area */
 | |
| };
 | |
| 
 | |
| /* end of edsdisk.h ********************************************************/
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| WORD csfd;				/* command file descriptor */
 | |
| WORD csgoff;				/* group offset within command file */
 | |
| 
 | |
| cseek(offs)				/* seek offset within Command File */
 | |
|     ADDR offs;
 | |
| {
 | |
| 	lseek(csfd,(LONG) offs+csgoff, 0);	/* compensate for cmd header */
 | |
| }
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* following is temporary!! change to zero after debugging */
 | |
| #define ZBYTE (doffset&0xFF)		/* the byte usually passed in */
 | |
|     WORD
 | |
| bufwrite(size,byt)
 | |
|     WORD size;
 | |
|     BYTE byt;
 | |
| {
 | |
| 	WORD ss;
 | |
| 
 | |
| 	for( ss=size; ss>0; --ss )
 | |
| 		write(fns,&byt,1);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* Module local variables */
 | |
| #define DPHNUM 16			/* number of possible disks */
 | |
| ADDR *dphtab;				/* table of offsets of Disk Parm Hdrs */
 | |
| DPH *dh[DPHNUM];			/* table of Disk Parm Hdrs */
 | |
| DINFO *di[DPHNUM];			/* table of info on DPHs */
 | |
| 
 | |
| 
 | |
| /* Module local predicates */
 | |
| #define ASKFOR(aa) ((aa) == 0xFFFF)	/* Is this one we're sposed to ask for? */
 | |
| #define SHRMSK 0x8000			/* Is this shared with another drive? */
 | |
| #define SHARED(bb) ((bb)!=0xFFFF  &&  (bb) & SHRMSK)	/* Is this nd??bs field shared with another disk? */
 | |
| #define GETSHARED(cc) ((cc) & ~SHRMSK)	/* Get the drive we share with */
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* EXPORT
 | |
| /* initxios_info: gets whatever information GENSYS needs from xios
 | |
| /***************************************************************************/
 | |
| initxios_info(xf,goff)			/* get info from xios file */
 | |
|     WORD xf;				/* xios file descriptor */
 | |
|     WORD goff;				/* group offset within xios file */
 | |
| {
 | |
| 	REG WORD ii;			/* counter */
 | |
| 	DPB dd;				/* a place to keep this info */
 | |
| 	DPH *dph;
 | |
| 	DINFO *dinf;
 | |
| 
 | |
| 	csfd = xf;			/* assign to global: set up cseek */
 | |
| 	csgoff = goff;			/* assign to global */
 | |
| 	cseek(XTABLOC);			/* go to where the XIOSTAB info is */
 | |
| 	read(xf,&xt,sizeof xt);		/* read in the xios info */
 | |
| 	dphtab = xt.xt_dphtab;		/* point to disk parm header table */
 | |
| 
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) {	/* for each dph pointer */
 | |
| 	    if( dphtab[ii] == 0 ) {	/* is there a dph? */
 | |
| 		dh[ii]=NULLPTR;
 | |
| 		di[ii]=NULLPTR;		/* no tickee, no washee */
 | |
| 	    } else {			/* aha! let's set up this info */
 | |
| 		dh[ii] = (DPH *)malloc(sizeof(DPH));
 | |
| 		di[ii] = (DINFO *)malloc(sizeof(DINFO));	
 | |
| 		zfill(di[ii],sizeof(DINFO));
 | |
| 		cseek(dphtab[ii]);
 | |
| 		read(xf,dh[ii],sizeof(DPH));
 | |
| 	    }
 | |
| 	}				/* end of reading dph */
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) 	/* let's go thru it again */
 | |
| 	    if( dh[ii] ) {		/* if there's something there */
 | |
| 		dph = dh[ii];
 | |
| 		dinf = di[ii];
 | |
| 		cseek(dph->dph_dpb);	/* point to the disk parm block */
 | |
| 		read(xf,&dd,sizeof(DPB));
 | |
| 		dinf->d_blksize = 128 << dd.dpb_psh;
 | |
| 		dinf->d_csvsize = 0x7FFF & dd.dpb_cks;
 | |
| 		dinf->d_alvsize = (dd.dpb_dsm/8 + 1) * 2;
 | |
| 		dinf->d_shash = 4 * (1 + dd.dpb_drm);
 | |
| 		dinf->d_ndirbs = -1;	/* ask for these by name! */
 | |
| 		dinf->d_ndatbs = -1;
 | |
| 		dinf->d_hashing = TRUE;	/* default to hashing */
 | |
| 	    }				/* end of drive info init */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /* EXPORT
 | |
| /* doxbufs: queries user for BDOS buffering info;
 | |
| /*	exits only when all data valid
 | |
| /************************************************************************/
 | |
|     BYTE *
 | |
| doxbufs(txt,bdtitle)
 | |
|     BYTE *txt;
 | |
|     BYTE *bdtitle;
 | |
| {
 | |
| 	BYTE cmds[CMDSLEN];
 | |
| 	BOOLEAN xiosvalid();
 | |
| 	EXTERN BYTE *clearit;
 | |
| 
 | |
| 	FOREVER {
 | |
| 	    printf("%s",clearit);	/* clear screen */
 | |
| 	    printf("%s\n",bdtitle);
 | |
| 	    dbufs_display();			/* display */
 | |
| 	    printf("Drive (<CR> to exit) ? ");
 | |
| 	    if( gets(cmds)!=cmds || *cmds==NULL ) {	/* they hit <CR>? */
 | |
| 		if( xiosvalid() ) break;
 | |
| 		USERR("Please correct drive buffers information\n");
 | |
| 		press_return();
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    askabout(cmds);		/* go do what they want */
 | |
| 	}
 | |
| 	return NULLPTR;			/* error msg for menu handler */
 | |
| }
 | |
| 
 | |
| /************************************************************************/
 | |
| /* dbufs_display: display disk buffering info 
 | |
| /************************************************************************/
 | |
| dbufs_display()
 | |
| {
 | |
| 	REG WORD ii, jj, tot;		/* counters */
 | |
| 	DPH *dph;
 | |
| 	DINFO *dinf;
 | |
| 
 | |
| 	tot = 0;			/* init this */
 | |
| 	dcalcsizes();			/* check all the sizes */
 | |
| 	printf("\n\t*** Disk Buffering Information ***\n");
 | |
| 	printf("      Dir  Max/Proc   Data Max/Proc   Hash   Specified\n");
 | |
| 	printf("Drv   Bufs Dir Bufs   Bufs Dat Bufs   -ing   Buf Pgphs\n");
 | |
| 	printf("===   ==== ========   ==== ========   ====   =========\n");
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) {	/*** for each drive ***/
 | |
| 	    if( dphtab[ii]==0 ) continue; /* no DP Hdr for this drive */
 | |
| 	    dph = dh[ii]; dinf = di[ii];
 | |
| 	    printf(" %c:   ",'A'+ii);	/* print which drive */
 | |
| 	    dspdnums(dph->dph_dirbcb,dinf->d_ndirbs,dinf->d_pmdirbs);
 | |
| 	    dspdnums(dph->dph_datbcb,dinf->d_ndatbs,dinf->d_pmdatbs);
 | |
| 	    if( ! ASKFOR(dph->dph_hstbl) )
 | |
| 		 printf(" fixed  ");
 | |
| 	    else if( dinf->d_hashing )
 | |
| 		 printf(" yes    ");
 | |
| 	    else printf(" no     ");
 | |
| 	    jj = dspdsize(dph,dinf);
 | |
| 	    if( jj == -2 )
 | |
| 		 printf(" fixed   ");
 | |
| 	    else if( jj == -1 )
 | |
| 		 printf(" ??      ");
 | |
| 	    else {
 | |
| 		printf(" %4.4x   ",jj >> 4);
 | |
| 		tot += jj;
 | |
| 	    }
 | |
| 	    printf("\n");
 | |
| 	}				/*** end for each drive ***/
 | |
| 	printf("Total paragraphs allocated to buffers: %x\n",tot >> 4);
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* dspdsize: calculates a drive's user-requestable buffer size, in bytes
 | |
| /***************************************************************************/
 | |
|     WORD
 | |
| dspdsize(dph,dinf)			/* calculate size requested */
 | |
|     DPH *dph;				/* returns -1 if still needs info */
 | |
|     DINFO *dinf;			/* returns -2 if pre-allocated */
 | |
| {
 | |
| 	REG WORD siz;
 | |
| 
 | |
| 	siz=0;
 | |
| 	if( !ASKFOR(dph->dph_dirbcb)  &&
 | |
| 	    !ASKFOR(dph->dph_datbcb)  &&
 | |
| 	    !ASKFOR(dph->dph_hstbl) )
 | |
| 		return -2;		/* nothing to say here */
 | |
| 	if( ASKFOR(dph->dph_dirbcb) ){
 | |
| 	    if( ASKFOR(dinf->d_ndirbs) )
 | |
| 		return -1;
 | |
| 	    if( !SHARED(dinf->d_ndirbs) )
 | |
| 		siz += dinf->d_sdirbs * dinf->d_ndirbs;
 | |
| 	}
 | |
| 	if( ASKFOR(dph->dph_datbcb) ){
 | |
| 	    if( ASKFOR(dinf->d_ndatbs) )
 | |
| 		return -1;
 | |
| 	    if( !SHARED(dinf->d_ndatbs) )
 | |
| 		siz += dinf->d_sdatbs * dinf->d_ndatbs;
 | |
| 	}
 | |
| 	if( ASKFOR(dph->dph_hstbl) ){
 | |
| 	    if( dinf->d_hashing )
 | |
| 		siz += dinf->d_shash;
 | |
| 	}
 | |
| 	return siz;
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* dspdnums: displays the data/dir buf numbers
 | |
| /***************************************************************************/
 | |
| dspdnums(nfix,ninput,npm)		/* display subroutine */
 | |
|     WORD nfix;
 | |
|     WORD ninput;
 | |
|     WORD npm;
 | |
| {
 | |
| 	if( !ASKFOR(nfix) )
 | |
| 	    printf("fixed           ");
 | |
| 	else if( SHARED(ninput) )
 | |
| 	    printf("shares %c:       ",'A'+GETSHARED(ninput));
 | |
| 	else {
 | |
| 	    if( ASKFOR(ninput) )
 | |
| 		 printf(" ??  ");
 | |
| 	    else printf(" %2.2x  ",ninput);
 | |
| 	    if( ASKFOR(npm) )
 | |
| 		 printf("  ??       ");
 | |
| 	    else printf("  %2.2x       ",npm);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* dcalcsizes: calculates buffer sizes across shared buffers
 | |
| /***************************************************************************/
 | |
| dcalcsizes()			/* make sure all the sizes are correct */
 | |
| {
 | |
| 	REG WORD ii, jj;
 | |
| 	REG WORD *pkk;
 | |
| 	DPH *dph;
 | |
| 	DINFO *dinf;
 | |
| 
 | |
| 	for( ii=0; ii<DPHNUM; ++ii )	/* init all sizes to zero */
 | |
| 	    if(dphtab[ii]) {
 | |
| 		dinf = di[ii];
 | |
| 		dinf->d_sdirbs = dinf->d_sdatbs = 0;
 | |
| 	    }
 | |
| 	for( ii=0; ii<DPHNUM; ++ii )		/* for each drive */
 | |
| 	    if(dphtab[ii]) {
 | |
| 		dph = dh[ii];
 | |
| 		dinf = di[ii];
 | |
| 		if( ASKFOR(dph->dph_dirbcb) ) 	/* max out the size of a dirbuf */
 | |
| 		    if( !ASKFOR(dinf->d_ndirbs) ) { /* if it's real data there */
 | |
| 			if( SHARED(dinf->d_ndirbs) ) {
 | |
| 			    jj = GETSHARED(dinf->d_ndirbs);
 | |
| 			    pkk = & ((di[jj])->d_sdirbs);
 | |
| 			} else 
 | |
| 			    pkk = & dinf->d_sdirbs;
 | |
| 			if( dinf->d_blksize > *pkk ) /* assign the maximum size found */
 | |
| 			    *pkk = dinf->d_blksize;
 | |
| 		    }
 | |
| 		if( ASKFOR(dph->dph_datbcb) ) 	/* max out the size of a data buf */
 | |
| 		    if( !ASKFOR(dinf->d_ndatbs) ) {
 | |
| 			if( SHARED(dinf->d_ndatbs) ) {
 | |
| 			    jj = GETSHARED(dinf->d_ndatbs);
 | |
| 			    pkk = & ((di[jj])->d_sdatbs);
 | |
| 			} else 
 | |
| 			    pkk = & dinf->d_sdatbs;
 | |
| 			if( dinf->d_blksize > *pkk )
 | |
| 			    *pkk = dinf->d_blksize;
 | |
| 		    }
 | |
| 	    }
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* ask user about a drive
 | |
| /***************************************************************************/
 | |
| askabout(drspec)			/* ask user about a drive */
 | |
|     BYTE *drspec;
 | |
| {
 | |
| 	REG WORD dr;
 | |
| 	DPH *dph;
 | |
| 	DINFO *dinf;
 | |
| 	BOOLEAN askhash();
 | |
| 
 | |
| 	if( *drspec == '*' ) {
 | |
| 	    askstar();			/* get info, apply to all disks */
 | |
| 	    return;
 | |
| 	}
 | |
| 	dr = toupper(*drspec) - 'A';	/* which drive do they want to fix? */
 | |
| 	if( dr<0  ||  dr>=DPHNUM  ||  dphtab[dr]==0  ||  drspec[1] != ':') {
 | |
| 	    USERR("Problem with '%s':\n",drspec);
 | |
| 	    USERR("Please specify an existing drive between 'A:' and 'P:'\n");
 | |
| 	    press_return();
 | |
| 	    return;
 | |
| 	}
 | |
| 	dph = dh[dr];
 | |
| 	dinf = di[dr];
 | |
| 	if( dspdsize(dph,dinf) == -2 ) { /* is there anything to modify? */
 | |
| 	    USERR("All buffers for %c: are fixed within the XIOS module.\n",
 | |
| 		dr+'A');
 | |
| 	    USERR("You can't modify this fixed information in GENSYS.\n");
 | |
| 	    press_return();
 | |
| 	    return;
 | |
| 	}
 | |
| 	if( ASKFOR(dph->dph_dirbcb) ){
 | |
| 	    dinf->d_ndirbs = asknd(1,FALSE);	/* get directory info */
 | |
| 	    dinf->d_pmdirbs = askpm(1,dinf->d_ndirbs);
 | |
| 	}
 | |
| 	if( ASKFOR(dph->dph_datbcb) ){
 | |
| 	    dinf->d_ndatbs = asknd(2,(128==dinf->d_blksize)); /* get data info */
 | |
| 	    dinf->d_pmdatbs = askpm(2,dinf->d_ndatbs);
 | |
| 	}
 | |
| 	if( ASKFOR(dph->dph_hstbl) ){
 | |
| 	    dinf->d_hashing = askhash();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /****************************************************************************/
 | |
| /* askstar: asks buffering questions & applies to all ASKFOR/unanswered drives
 | |
| /****************************************************************************/
 | |
| askstar()
 | |
| {
 | |
| 	REG WORD dr;
 | |
| 	DPH *dph;
 | |
| 	DINFO *dinf;
 | |
| 	WORD andirbs, andirpms, andatbs, andatpms;
 | |
| 	BOOLEAN ahash, askhash();
 | |
| 
 | |
| 	andirbs = asknd(1,FALSE);	/* Number of directory buffers? */
 | |
| 	andirpms = askpm(1,andirbs);	/* Process Max Dir Bufs? */
 | |
| 	andatbs = asknd(2,FALSE);	/* Number of data buffers? */
 | |
| 	andatpms = askpm(2,andatbs);	/* Process Max Data Bufs? */
 | |
| 	ahash = askhash();		/* Hashing? */
 | |
| 	for( dr=0; dr<DPHNUM; ++dr )
 | |
| 	    if( dphtab[dr] ) {		/* for all existing drives */
 | |
| 		dph = dh[dr]; dinf = di[dr];
 | |
| 		if( ASKFOR(dph->dph_dirbcb) )
 | |
| 		    if( ASKFOR(dinf->d_ndirbs) ) {
 | |
| 			dinf->d_ndirbs = andirbs;
 | |
| 			dinf->d_pmdirbs = andirpms;
 | |
| 		    }
 | |
| 		if( ASKFOR(dph->dph_datbcb) )
 | |
| 		    if( ASKFOR(dinf->d_ndatbs) ) {
 | |
| 			dinf->d_ndatbs = andatbs;
 | |
| 			dinf->d_pmdatbs = andatpms;
 | |
| 		    }
 | |
| 		if( ASKFOR(dph->dph_hstbl) )
 | |
| 		    dinf->d_hashing = ahash;
 | |
| 	    }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* asknd: ask the user for the number of dir/data buffers
 | |
| /***************************************************************************/
 | |
|     WORD
 | |
| asknd(type,zero_ok)
 | |
|     WORD type;				/* 1=dir, 2=data */
 | |
|     BOOLEAN zero_ok;
 | |
| {
 | |
| 	REG WORD val, jj;
 | |
| 	BYTE *name;
 | |
| 	BYTE cmds[CMDSLEN];
 | |
| 	DPH *dph;
 | |
| 
 | |
| 	if( type==1 ) name="directory"; else name="data";
 | |
| 	FOREVER {
 | |
| 	    printf("Number of %s buffers, or drive to share with ? ",name);
 | |
| 	    gets( cmds );
 | |
| 	    if( cmds[1] != ':' ) {
 | |
| 		val = atoih(cmds);
 | |
| 		if( val > 0  &&  val <= 127 ) break;
 | |
| 		    if(*cmds == '0') {
 | |
| 			if( zero_ok ) break;
 | |
| 			USERR("Number of buffers must be greater than 0\n");
 | |
| 			press_return();
 | |
| 			continue;
 | |
| 		    }
 | |
| 	    } else {
 | |
| 		val = toupper(*cmds) - 'A';	/* which drive do they want? */
 | |
| 		if( val>=0 && val<DPHNUM && dphtab[val]!=0) { /* a drive maybe? */
 | |
| 		    dph = dh[val];
 | |
| 		    if( type==1 ) jj = dph->dph_dirbcb; 
 | |
| 		    else          jj = dph->dph_datbcb;
 | |
| 		    if( ASKFOR(jj) ) {
 | |
| 		    	if( type==1 ) jj = di[val]->d_ndirbs; 
 | |
| 		    	else	      jj = di[val]->d_ndatbs;
 | |
| 		    	if( !SHARED(jj) ) { 
 | |
| 			    val |= SHRMSK;	/* turn on sharing */
 | |
| 			    break;
 | |
| 		        }
 | |
| 		    }
 | |
| 		    USERR("Drive %c: is not available for sharing\n",'A'+val);
 | |
| 		    press_return();
 | |
| 		    continue;
 | |
| 		}
 | |
| 	    }
 | |
| 	    USERR("Please input a number, or an existing drive from 'A:' to 'P:'\n");
 | |
| 	    press_return();
 | |
| 	}
 | |
| 	return val;
 | |
| }
 | |
| 
 | |
| /************************************/
 | |
| /* askpm: ask about process maximum
 | |
| /************************************/
 | |
|     WORD
 | |
| askpm(type,pmmax)
 | |
|     WORD type;
 | |
|     WORD pmmax;
 | |
| {
 | |
| 	REG WORD val;
 | |
| 	BYTE *cmds[CMDSLEN];
 | |
| 	BYTE *name;
 | |
| 
 | |
| 	switch(type) { case 1: name="directory"; break; case 2: name="data"; }
 | |
| 	if( SHARED(pmmax) || pmmax<=0 )
 | |
| 	    return 0;
 | |
| 	FOREVER {			/* get input from user */
 | |
| 	    printf("Maximum %s buffers per process [%x]? ",name,pmmax);
 | |
| 	    gets( cmds );
 | |
| 	    if( *cmds==NULL )
 | |
| 		 val = pmmax;		/* default value */
 | |
| 	    else val = atoih(cmds);
 | |
| 	    if( val > 0  &&  val <= pmmax ) break;
 | |
| 	    USERR("Maximum must be > zero and <= %x\n",pmmax);
 | |
| 	    press_return();
 | |
| 	}
 | |
| 	return val;
 | |
| }
 | |
| 
 | |
| /*****************************************/
 | |
| /* askhash: ask user if they'd like to hash
 | |
| /*****************************************/
 | |
|     BOOLEAN
 | |
| askhash()
 | |
| {
 | |
| 	REG WORD val;
 | |
| 	BYTE *cmds[CMDSLEN], *cp;
 | |
| 
 | |
| 	FOREVER {
 | |
| 	    printf("Hashing [yes] ? ");
 | |
| 	    gets( cmds );
 | |
| 	    if( *cmds == NULL )
 | |
| 		return TRUE;
 | |
| 	    for( cp=cmds; *cp; ++cp ) {
 | |
| 		if( *cp==' '  ||  *cp=='\t' ) {
 | |
| 		    *cp = NULL;
 | |
| 		    break;
 | |
| 		}
 | |
| 		if( isupper(*cp) )
 | |
| 		    *cp = tolower(*cp);
 | |
| 	    }
 | |
| 	    if( (val=whichof(cmds,",yes,true,on,hashing,no,false,off")) > 0 )
 | |
| 		break;
 | |
| 	    USERR("Please answer 'hashing', 'yes', or 'no'.\n");
 | |
| 	    press_return();
 | |
| 	}
 | |
| 	return val<=4;			/* TRUE iff answer was one of 1st four */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| /* EXPORT
 | |
| /* xiosvalid: validates that all the user-requestable info has been filled in
 | |
| /************************************************************************/
 | |
|     BOOLEAN
 | |
| xiosvalid()
 | |
| {
 | |
| 	REG WORD ii;
 | |
| 	REG WORD ndph;
 | |
| 
 | |
| 	ndph = 0;
 | |
| 	for( ii=0; ii<DPHNUM; ++ii )
 | |
| 	    if( dphtab[ii]!=0 ) {
 | |
| 		if( dspdsize(dh[ii],di[ii]) == -1 )
 | |
| 		    return FALSE;
 | |
| 		++ndph;
 | |
| 	    }
 | |
| 	if( ndph==0 ) {
 | |
| 	    USERR("no dph information in xios header...");
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* EXPORT
 | |
| /* genxios: generates disk buffers (to be placed after the rest of the
 | |
| /*	system tables.
 | |
| /*************************************************************************/
 | |
| #define OFF_BCBPTR 0xA
 | |
| #define OFF_DPHHTAB 0x12
 | |
| ADDR locrsvd_off;			/* local reserved offset, for DIRBUF fix */
 | |
| 
 | |
|     ADDR				/* returns final offset */
 | |
| genxios(doffset)			/* generate the disk buffers */
 | |
|     ADDR doffset;			/* offset in data file */
 | |
| {
 | |
| 	REG WORD ii, jj;		/* counter */
 | |
| 	REG BOOLEAN dm;			/* modified flag */
 | |
| 	REG ADDR lastlink;		/* used for threading lists */
 | |
| 	ADDR locbufs_off;		/* local buffer offset for DIRBUF grouping */
 | |
| 	LONG dmagic;			/* where word needing fix goes into file */
 | |
| 	BCB bcb;			/* place for organizing, calc for fixups */
 | |
| 	BCBH bcbh;
 | |
| 	DPH *dph;
 | |
| 	DINFO *dinf;
 | |
| 	BOOLEAN xiosvalid();
 | |
| 
 | |
| 	if( !xiosvalid() ) CRASH("invalid disk buffer information");
 | |
| 
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) 	/* allocate CHECK SUM VECTORS */
 | |
| 	    if( dh[ii] ) {		/* if there's something to look at */
 | |
| 		dph = dh[ii];		/* convenient ptr */
 | |
| 		dinf = di[ii];
 | |
| 		if( ASKFOR(dph->dph_csv) ){
 | |
| 		    dinf->d_modified = TRUE;
 | |
| 		    if( dinf->d_csvsize == 0 )
 | |
| 			dph->dph_csv=0;
 | |
| 		    else {
 | |
| 			dph->dph_csv = doffset;
 | |
| 			doffset += bufwrite(dinf->d_csvsize,ZBYTE);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) 	/* allocate ALLOCATION TABLE VECTORS */
 | |
| 	    if( dh[ii] ) {		/* if there's something to look at */
 | |
| 		dph = dh[ii];		/* convenient ptr */
 | |
| 		dinf = di[ii];
 | |
| 		if( ASKFOR(dph->dph_alv) ){
 | |
| 		    dinf->d_modified = TRUE;
 | |
| 		    dph->dph_alv = doffset;
 | |
| 		    doffset += bufwrite(dinf->d_alvsize,ZBYTE);
 | |
| 		}
 | |
| 	    }
 | |
| 	locbufs_off = 0;		/* amount (bytes) of local dir buffer*/
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) 	/* allocate DIR BCBs & BUFFERS */
 | |
| 	    if( dh[ii] ) {		/* if there's something to look at */
 | |
| 		dph = dh[ii];		/* convenient ptr */
 | |
| 		dinf = di[ii];
 | |
| 		if( ASKFOR(dph->dph_dirbcb) ){	/* DIR BCBs */
 | |
| 		    dinf->d_modified = TRUE;
 | |
| 		    if( ! SHARED(dinf->d_ndirbs) ) { /* take care of sharing later */
 | |
| 			lastlink=0;		     /* linker */
 | |
| 			zfill(&bcb,sizeof(BCB));     /* init to zeroes */
 | |
| 			bcb.bcb_drv = 0xFF;
 | |
| 			for( jj=dinf->d_ndirbs; jj>0; --jj ){
 | |
| 			    bcb.bcb_link = lastlink;
 | |
| 			    /* pts to loc of buffer relative to locrsvd_off */
 | |
| 			    bcb.bcb_bufptr = locbufs_off;
 | |
| 			    locbufs_off += dinf->d_sdirbs; /* increase buf size */
 | |
| 			    /* calc where bcb will be written to, for fixlater */
 | |
| 			    dmagic = lseek(fns,0L,1) + OFF_BCBPTR;
 | |
| 			    fixlater(dmagic,F_ADD,&locrsvd_off);
 | |
| 			    /* finally, write out the bcb info */
 | |
| 			    lastlink = doffset;	     /* point to the bcb */
 | |
| 			    doffset += datwrite(&bcb,sizeof bcb);
 | |
| 			}
 | |
| 			bcbh.bcbh_lr = lastlink; /* fill in the bcb hdr */
 | |
| 			bcbh.bcbh_pm = dinf->d_pmdirbs;
 | |
| 			dph->dph_dirbcb = doffset; /* link to head of list */
 | |
| 			/* write out a buffer control block hdr */
 | |
| 			doffset += datwrite(&bcbh,sizeof bcbh);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) 	/* allocate DATA BCBs & BUFFERS */
 | |
| 	    if( dh[ii] ) {		/* if there's something to look at */
 | |
| 		dph = dh[ii];		/* convenient ptr */
 | |
| 		dinf = di[ii];
 | |
| 		/* the bufs can go out of the system page, but the bcbs need to be in */
 | |
| 		if( ASKFOR(dph->dph_datbcb) ){	/* DATA BCBs: !!temporary!!  */
 | |
| 		    dinf->d_modified = TRUE;
 | |
| 		    if( ! SHARED(dinf->d_ndatbs) ) { /* do sharing later */
 | |
| 			lastlink=0;		     /* linker */
 | |
| 			zfill(&bcb,sizeof(BCB));     /* init to zeroes */
 | |
| 			bcb.bcb_drv = 0xFF;
 | |
| 			for( jj=dinf->d_ndatbs; jj>0; --jj ){
 | |
| 			    bcb.bcb_link = lastlink;
 | |
| 			    bcb.bcb_bufptr = bufs_seg; /* point into reserved area */
 | |
| 			    bufs_seg += (dinf->d_sdatbs) >>4; /* cvt to pgphs */
 | |
| 			    /* calculate where the bcb.bcb_bufptr will be written in file */
 | |
| 			    dmagic = lseek(fns,0L,1) + OFF_BCBPTR;
 | |
| 			    fixlater(dmagic,F_ADD,&rsvd_seg); /* fix the bufptr */
 | |
| 			    lastlink = doffset;
 | |
| 			    /* write out a buffer control block */
 | |
| 			    doffset += datwrite(&bcb,sizeof bcb);
 | |
| 			}
 | |
| 			bcbh.bcbh_lr = lastlink; /* fill in the bcb hdr */
 | |
| 			bcbh.bcbh_pm = dinf->d_pmdatbs;
 | |
| 			dph->dph_datbcb = doffset; /* link to head of list */
 | |
| 			/* write out a buffer control block hdr */
 | |
| 			doffset += datwrite(&bcbh,sizeof bcbh);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	/* now write the local directory buffers */
 | |
| 	locrsvd_off = doffset;	/* address of directory buffer area */
 | |
| 	doffset += bufwrite(locbufs_off,ZBYTE); /* write out the buffers */
 | |
| 
 | |
| 	/* dmagic: magic number to add to dphtab[ii] */
 | |
| 	dmagic = OFF_DPHHTAB + fldstart;/* where in file does SYSDAT start */
 | |
| 
 | |
| 	for( ii=0; ii<DPHNUM; ++ii ) 	/* allocate HASH TABLES */
 | |
| 	    if( dh[ii] ) {		/* if there's something to look at */
 | |
| 		dph = dh[ii];		/* convenient ptr */
 | |
| 		dinf = di[ii];
 | |
| 		if( ASKFOR(dph->dph_hstbl) ) {	/* HASH TABLE ALLOC */
 | |
| 		    dinf->d_modified = TRUE;
 | |
| 		    if( dinf->d_hashing ) {
 | |
| 			dph->dph_hstbl = bufs_seg; /* point into reserved area */
 | |
| 			bufs_seg += (dinf->d_shash) >>4; /* cvt bytes to pgphs */
 | |
| 			/* remember where the dph_hstbl word will be stored */
 | |
| 			/* so we can adjust bufs_seg by starting loc of rsvd_seg */
 | |
| 			fixlater(dmagic+dphtab[ii],F_ADD,&rsvd_seg);
 | |
| 		    } else dph->dph_hstbl = 0;
 | |
| 		}
 | |
| 	    }				/**** end of processing ****/
 | |
| 
 | |
| 	for( ii=0; ii<DPHNUM; ++ii )	/* resolve shared buffers */
 | |
| 	    if( dh[ii] ) {
 | |
| 		if( ASKFOR(dh[ii]->dph_dirbcb) ) {
 | |
| 		    jj = GETSHARED(di[ii]->d_ndirbs);
 | |
| 		    dh[ii]->dph_dirbcb = dh[jj]->dph_dirbcb;
 | |
| 		}
 | |
| 		if( ASKFOR(dh[ii]->dph_datbcb) ) {
 | |
| 		    jj = GETSHARED(di[ii]->d_ndatbs);
 | |
| 		    dh[ii]->dph_datbcb = dh[jj]->dph_datbcb;
 | |
| 		}
 | |
| 	    }				/* end resolve shared buffers */
 | |
| 
 | |
| 	return doffset;
 | |
| }
 | |
| 
 | |
| 
 | |
| /***************************************************************************/
 | |
| /* EXPORT
 | |
| /* fixupxios_info: does the fixup for the dph info in the xios
 | |
| /*************************************************************************/
 | |
| fixupxios_info()		/* call from fixups */
 | |
| {
 | |
| 	REG WORD ii;			/* counter */
 | |
| 	REG LONG dmagic;		/* addr var */
 | |
| 
 | |
| 	for( ii=0; ii<DPHNUM; ++ii )
 | |
| 	    if( di[ii]->d_modified ) {
 | |
| 		dmagic = fldstart + dphtab[ii];
 | |
| 		lseek(fns,dmagic,0); /* where to write to */
 | |
| 		write(fns,dh[ii],sizeof(DPH));	/* what to write */
 | |
| 	    }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /**************************************************************************/
 | |
| /*	M E N U  handling routines
 | |
| /*
 | |
| /*	Written by Bill Fitler
 | |
| /**************************************************************************/
 | |
| /**************************************************************************/
 | |
| #ifndef MAINMODULE
 | |
| #include <portab.h>
 | |
| #include <stdio.h>
 | |
| #include <menu.h>
 | |
| EXTERN BOOLEAN verbose;			/* flag: be wordy */
 | |
| #endif
 | |
| 
 | |
| MLOCAL BOOLEAN eflag = FALSE;		/* user error */
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* bldmenu: builds a menu list from successive calls
 | |
| /**************************************************************************/
 | |
|     MENU *
 | |
| bldmenu(muroot,typ,ptr,name,description)
 | |
|     MENU *muroot;			/* root of menu list (or NULLPTR) */
 | |
|     WORD typ;				/* type of menu item */
 | |
|     BYTE *ptr;				/* pointer to something... */
 | |
|     BYTE *name;				/* command name for this menu item */
 | |
|     BYTE *description;			/* long label for this item */
 | |
| {
 | |
| 	REG MENU *mu;			/* ptr to created item */
 | |
| 	REG MENU *tmu;			/* temp ptr for linked list scan */
 | |
| 
 | |
| 	mu = (MENU *)malloc(sizeof(*mu));	/* allocate memory for item */
 | |
| 	mu->mutype = typ;
 | |
| 	mu->muiptr = ptr;
 | |
| 	mu->muname = name;
 | |
| 	mu->mudesc = description;
 | |
| 	mu->munext = NULLPTR;
 | |
| 	if( muroot == NULLPTR )		/* if NULL list */
 | |
| 		return mu;		/* return something to start with */
 | |
| 	for( tmu=muroot; tmu->munext != NULLPTR; tmu = tmu->munext )
 | |
| 		;			/* append to end of list */
 | |
| 	tmu->munext = mu;	
 | |
| 	return muroot;			/* keep list in order */
 | |
| }
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* domenu: executes the specified commands (in cbuf) from the menu (mnu)
 | |
| /**************************************************************************/
 | |
| #define CMDLEN 80
 | |
| #define USERR ++eflag;printf
 | |
| #define ECHK(fn) _errchk(fn,cmd,cmdval)
 | |
| 
 | |
|     VOID
 | |
| domenu(cbuf,mnu)
 | |
|     BYTE *cbuf;				/* command buffer */
 | |
|     MENU *mnu;				/* table of menu specs */
 | |
| {
 | |
| 	REG MENU *mi;			/* ptr to menu item */
 | |
| 	LOCAL BYTE cmd[CMDLEN];		/* place for command & value */
 | |
| 	LOCAL BYTE *cmdval;		/* ptr within cmd */
 | |
| 	LOCAL BYTE *cp;			/* temp char ptr */
 | |
| 	BYTE *msetbool();		/* set a boolean value */
 | |
| 	BYTE *msetbyte();		/* set a numeric byte value */
 | |
| 	BYTE *msetword();		/* set an integer pointer */
 | |
| 	BYTE *msettxt();		/* set a text value */
 | |
| 	BYTE *(*proc)();		/* procedure pointer */
 | |
| 	BYTE *msetdrv();		/* set a drive value */
 | |
| 	BYTE *_nxtcmd();		/* command parser */
 | |
| 	MENU *_fndcmd();		/* command lookup */
 | |
| 
 | |
| 	eflag = FALSE;			/* no errors yet */
 | |
| 	while((cbuf=_nxtcmd(cbuf,cmd))){ /* while there are commands in cbuf */
 | |
| 	    cmdval = NULLPTR;		/* init value ptr for command */
 | |
| 	    for( cp=cmd; *cp; cp++ ) {	/* scan command for assign op */
 | |
| 		if( isupper(*cp) )	/* convert all commands to LOWER CASE */
 | |
| 		    *cp = tolower(*cp);
 | |
| 		if( *cp == '=' ) {	/* found? */
 | |
| 		    *cp++ = NULL;	/* terminate command */
 | |
| 		    cmdval = cp;	/* point value to after assign op */
 | |
| 		    break;		/* and terminate loop */
 | |
| 		}
 | |
| 	    }
 | |
| 	    if( (mi=_fndcmd(cmd,mnu)) == NULLPTR ){ /* scan menu commands */
 | |
| 		USERR("'%s' is not a command for this menu\n",cmd); 
 | |
| 	    } else {			/******** cmd loop ********/
 | |
| 		switch(mi->mutype) {	/* handle cmd type */
 | |
| 		case MBOOL:		/** BOOLEAN command **/
 | |
| 		    ECHK(msetbool(mi->muiptr,cmdval));
 | |
| 		    break;
 | |
| 		case MBYTE:		/** BYTE command **/
 | |
| 		    ECHK(msetbyte(mi->muiptr,cmdval));
 | |
| 		    break;
 | |
| 		case MWORD:		/** INTEGER command **/
 | |
| 		    ECHK(msetword(mi->muiptr,cmdval));
 | |
| 		    break;
 | |
| 		case MTEXT:		/** TEXT command **/
 | |
| 		    ECHK(msettxt(mi->muiptr,cmdval));
 | |
| 		    break;
 | |
| 		case MPROC:		/** PROCEDURAL command **/
 | |
| 		    proc = mi->muiptr;	/* assign to function ptr */
 | |
| 		    ECHK(((*proc)(cmdval,mi->mudesc)));
 | |
| 		    break;
 | |
| 		case MDRIV:		/** DRIVE letter command **/
 | |
| 		    ECHK(msetdrv(mi->muiptr,cmdval));
 | |
| 		    break;
 | |
| 		}			/* end switch */
 | |
| 	    }				/********* end cmd loop *********/
 | |
| 	    if(eflag) {			/* user error? */
 | |
| 		press_return();
 | |
| 		*cbuf = NULL;
 | |
| 	    } else if(verbose)		/* are we being wordy? */
 | |
| 		prtmival(mi);		/* let the user know what happened */
 | |
| 	}				/**** end while loop ****/
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* _nxtcmd: parses cin into cout */
 | |
|     BYTE *
 | |
| _nxtcmd(cin,cout)			/* returns ptr to after next cmd */
 | |
|     BYTE *cin;				/* command in buf */
 | |
|     BYTE *cout;				/* command out buf */
 | |
| {
 | |
| 	/***** handle quoted strings??? *****/
 | |
| 	while( isspace(*cin) )		/* skip leading spaces */
 | |
| 		++cin;
 | |
| 	if( *cin == NULL )		/* check for EOS */
 | |
| 		return NULLPTR;
 | |
| 	while( *cin && !isspace(*cin) )	/* scan to eos or whitespace */
 | |
| 		*cout++ = *cin++;	/* xfer to command out buf */
 | |
| 	*cout = NULL;			/* null terminate command out buf */
 | |
| 	return cin;			/* return ptr to after command */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* _fndcmd: scans thru menu structure 'mu' looking for command name 'cn' */
 | |
| /*		returns ptr to menu item or NULLPTR */
 | |
|     MENU *
 | |
| _fndcmd(cn,mus)
 | |
|     BYTE *cn;				/* command name */
 | |
|     MENU *mus;				/* ptr to menu list */
 | |
| {
 | |
| 	REG WORD cnl;			/* cmd name len */
 | |
| 	MENU *musave;			/* extra ptr to menu item */
 | |
| 
 | |
| 	cnl = strlen(cn);
 | |
| 	for( ; ; mus = mus->munext) {	/* scan menu list */
 | |
| 	    if( mus==NULLPTR )		/* if we hit the end then fail */
 | |
| 		return NULLPTR;
 | |
| 	    if( strncmp(cn,mus->muname,cnl) == 0 )	/* cn prefix of name?*/
 | |
| 		break;			/* yes, we have a candidate */
 | |
| 	}
 | |
| 	musave = mus;
 | |
| 	for( mus=mus->munext; ; mus=mus->munext ) { /* finish scanning list */
 | |
| 	    if( mus==NULLPTR )		/* if we hit the end then succeed */
 | |
| 		return musave;		/* we reached end of list: found it! */
 | |
| 	    if( strncmp(cn,mus->muname,cnl) == 0 )
 | |
| 		return NULLPTR;		/* another match: ambiguous command */
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* _errchk: checks for error returns on functions */
 | |
|     VOID
 | |
| _errchk(emsg,cm,cmv)
 | |
|     BYTE *emsg;				/* error message to check */
 | |
|     BYTE *cm;				/* command generating the error */
 | |
|     BYTE *cmv;				/* value generating the error */
 | |
| {
 | |
| 	if( emsg ) {			/* non NULL error message return? */
 | |
| 		USERR("Error on command '%s",cm);
 | |
| 		if(cmv)
 | |
| 			USERR("=%s",cmv);
 | |
| 		USERR("': %s\n",emsg);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* msetbool: sets the BOOLEAN pointed to by 'bp' according to value in 'cvp' */
 | |
|     BYTE *				/* return NULLPTR, or ptr to err msg */
 | |
| msetbool(bp,cvp)
 | |
|     BYTE *bp;				/* pointer to boolean to set */
 | |
|     BYTE *cvp;				/* pointer to value to set to */
 | |
| {
 | |
| 	REG WORD bv;
 | |
| 	REG BYTE *tp;
 | |
| 
 | |
| 	if( cvp==NULLPTR )		/* no val: toggle boolean */
 | |
| 		bv = (*bp ? 4 : 1);	/* toggle by setting bv index */
 | |
| 	else {
 | |
| 	    for( tp=cvp; *tp; ++tp ) 
 | |
| 		if( isupper(*tp) ) *tp=tolower(*tp);
 | |
| 	    if( (bv=whichof(cvp,",on,yes,true,off,no,false"))==0 ) 
 | |
| 		return "value must be 'yes' or 'no'";
 | |
| 	}
 | |
| 	if( bv < 4 )
 | |
| 		*bp = -1;		/* zap to 0xFFFF */
 | |
| 	else 	*bp = 0;		/* zap to zero */
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| 
 | |
|     BYTE *
 | |
| msetbyte(bp,cvp)			/* set numeric BYTE value */
 | |
|     BYTE *bp;				/* ptr to BYTE to set */
 | |
|     BYTE *cvp;				/* value to set BYTE to */
 | |
| {
 | |
| 	REG WORD val;			/* temp holding place */
 | |
| 
 | |
| 	val=0;
 | |
| 	if(cvp != NULLPTR) val=atoih(cvp);	/* assume numbers are in HEX!! */
 | |
| 	if( cvp==NULLPTR || (val==0  &&  !isdigit(*cvp)) )
 | |
| 	    return "value must be a number";
 | |
| 	if( val > 255 )
 | |
| 	    return "value must be less than FF hex (255 decimal)";
 | |
| 	*bp = val;
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| 
 | |
|     BYTE *
 | |
| msetword(ip,cvp)
 | |
|     WORD *ip;				/* ptr to WORD to set */
 | |
|     BYTE *cvp;				/* value to set WORD to */
 | |
| {
 | |
| 	REG WORD val;			/* temp holding place */
 | |
| 
 | |
| 	val=0;
 | |
| 	if(cvp != NULLPTR) val=atoih(cvp);	/* assume numbers are in HEX!! */
 | |
| 	if( cvp==NULLPTR || (val==0  &&  !isdigit(*cvp)) )
 | |
| 	    return "value must be an unsigned hex number between 0 and FFFFh";
 | |
| 	*ip = val;
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
| 
 | |
|     WORD
 | |
| atoih(cp)				/* conver ascii hex to word */
 | |
|     BYTE *cp;				/* buffer number */
 | |
| {
 | |
| 	REG WORD v;			/* resulting value */
 | |
| 	REG BYTE cv;			/* character value */
 | |
| 	BYTE *savcp;			/* save orig ptr */
 | |
| 
 | |
| 	v = 0; savcp = cp;		/* init value */
 | |
| 	for( cv = *cp; ; cv = *++cp ){	/* for each char in cp */
 | |
| 	    if( isdigit(cv) )
 | |
| 		v = (v<<4) + (cv-'0');	/* convert digit and add */
 | |
| 	    else {			/* maybe it's a letter */
 | |
| 		if( isupper(cv) )	/* upper case letter? */
 | |
| 		    cv = tolower(cv);	/* convert it */
 | |
| 		if( cv<'a' || cv>'f')	/* range check */
 | |
| 		    break;		/* terminate loop if failed */
 | |
| 		v = (v<<4)+(10+cv-'a');	/* convert letter and add */
 | |
| 	    }
 | |
| 	}
 | |
| 	if( cp-savcp <= 4 )
 | |
| 	     return v;			/* return the value */
 | |
| 	else { 
 | |
| 	     *savcp |= 0x80; 
 | |
| 	     return 0; 		/* turn on hi bit, make isdigit test fail */
 | |
| 	}
 | |
| }
 | |
| 
 | |
|     BYTE *
 | |
| msettxt(tp,cvp)
 | |
|     BYTE **tp;				/* place to store ptr to value */
 | |
|     BYTE *cvp;				/* value to set BYTEs to */
 | |
| {
 | |
| 	REG BYTE *tmp;			/* temp ptr for allocated storage */
 | |
| 
 | |
| 	if( cvp==NULLPTR )	
 | |
| 	    cvp="";			/* point to null string, instead */
 | |
| 	tmp = malloc(1+strlen(cvp));	/* get space for string */
 | |
| 	strcpy(tmp,cvp);		/* copy to allocated space */
 | |
| 	*tp = tmp;			/* assign ptr */
 | |
| 	return NULLPTR;
 | |
| }
 | |
| 
 | |
|     BYTE *
 | |
| msetdrv(dp,cvp)
 | |
|     BYTE *dp;				/* place to store ptr to value */
 | |
|     BYTE *cvp;				/* value to set DRIVE to */
 | |
| {
 | |
| 	REG WORD v;			/* work place */
 | |
| 
 | |
| 	if( cvp!=NULLPTR ) {		/* check for drive letter */
 | |
| 	    if( isupper(*cvp) )
 | |
| 		*cvp = tolower(*cvp);
 | |
| 	    v = *cvp - 'a';		/* convert drive spec to nibble */
 | |
| 	}
 | |
| 	if( cvp==NULLPTR  ||  v<0  ||  v>15  || cvp[1] != ':' )
 | |
| 	     return "you must specify a drive 'A:' thru 'P:'";
 | |
| 	*dp = v;			/* looks okay, set it */
 | |
| 	return NULLPTR;			/* no errors */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************************/
 | |
| /* prtmenu: displays the menu labels, values and descriptors
 | |
| /**************************************************************************/
 | |
|     VOID 
 | |
| prtmenu(mtitle,mnu)
 | |
|     BYTE *mtitle;			/* title of menu */
 | |
|     MENU *mnu;				/* table specifying menu */
 | |
| {
 | |
| 	printf("\n\n%s\n",mtitle);	/* give the menu a title */
 | |
| 	for( ; mnu!=NULLPTR; mnu = mnu->munext ) /* travel down list */
 | |
| 	    prtmival(mnu);	
 | |
| }
 | |
| 
 | |
| 
 | |
| prtmival(mnu)				/* return ptr to value of item */
 | |
|     MENU *mnu;				/* item to get value of */
 | |
| {
 | |
| 	REG BYTE *ival;
 | |
| 	BYTE ivbuf[10];			/* local buf for display */
 | |
| 	BYTE *vbp;			/* value byte ptr */
 | |
| 	WORD *vwp;			/* value word ptr */
 | |
| 	WORD v;				/* place to put value */
 | |
| 
 | |
| 	printf("%12.12s ",mnu->muname); /* first the name */
 | |
| 	ival = ivbuf; *ival = NULL;	/* init value */
 | |
| 	switch(mnu->mutype) {	/* now the value */
 | |
| 	    case MBOOL:
 | |
| 		if( *(mnu->muiptr) )
 | |
| 		     ival="[Y]";
 | |
| 		else ival="[N]";
 | |
| 		break;
 | |
| 	    case MBYTE:
 | |
| 		vbp = mnu->muiptr;
 | |
| 		v = (*vbp & 0xFF);
 | |
| 		sprintf(ivbuf,"[%2.2x]",v);	/* display in hex */
 | |
| 		break;
 | |
| 	    case MWORD:
 | |
| 		vwp = mnu->muiptr;
 | |
| 		v = *vwp;
 | |
| 		sprintf(ivbuf,"[%4.4x]",v);	/* display in hex */
 | |
| 		break;
 | |
| 	    case MTEXT:
 | |
| 		vbp = *((BYTE **)mnu->muiptr);	/* go get the char ptr */
 | |
| 		if( strlen(vbp) > 4 )
 | |
| 		     sprintf(ivbuf,"[%-4.4s>",vbp);
 | |
| 		else sprintf(ivbuf,"[%s]",vbp);
 | |
| 		break;
 | |
| 	    case MPROC:
 | |
| 		/* leave blank */
 | |
| 		break;
 | |
| 	    case MDRIV:
 | |
| 		sprintf(ivbuf,"[%c:]",'A'+(*mnu->muiptr));
 | |
| 		break;
 | |
| 	}
 | |
| 	printf("%-6s ",ival);
 | |
| 	printf("%s\n",mnu->mudesc);	/* finally the description */
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************
 | |
| /* whichof: scans the string 'set' (delimited by 1st char in 'set')
 | |
| /*	for 1st occurrence of string 'sample'.
 | |
| /*	Returns 0 if 'sample' not a prefix of any item in set or if
 | |
| /*		'sample' is a prefix of more than one item in set;
 | |
| /*		delimiter# o.w.
 | |
| /* E.G. if set = ",on,yes,true,off,no,false"  then
 | |
| /*	whichof("yes",set) == 2;
 | |
| /*	whichof("y",set) == 2;
 | |
| /*	whichof("o",set) == 0;
 | |
| /*	whichof("x",set) == 0;
 | |
| /* NOTE: results not guaranteed if item delimiter (*set) is in string 'sample',
 | |
| /*	or if null item is in set (adjacent delimiters).
 | |
| /*************************************************************************/
 | |
| 
 | |
| /* first, a subroutine: */
 | |
| /* _wget: returns NULLPTR if sa not prefix of item in st; */
 | |
| /*	  returns end of item (ptr in st) otherwise */
 | |
| BYTE *_wget(sa,st,de)
 | |
| BYTE *sa;
 | |
| BYTE *st;
 | |
| BYTE de;
 | |
| {
 | |
| 	BYTE *s2;
 | |
| 
 | |
| 	for( ;; ) {					/* do forever */
 | |
| 		for( s2=sa; *s2 && *s2 == *st; ){
 | |
| 			++s2; ++st;			/* try match */
 | |
| 		}
 | |
| 		if( *s2	== NULL ){	/* success! we've matched all of sa */
 | |
| 			while( *st && *st != de ) ++st;	/* adv to nxt delim */
 | |
| 			return( --st );			/* backup one char */
 | |
| 		}
 | |
| 		while( *st  &&  *st != de ) /* no match, look for next item */
 | |
| 			++st;				/* adv to nxt delim */
 | |
| 		if( *st == NULL )
 | |
| 			return( NULLPTR );		/* end of list */
 | |
| 		++st;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| WORD whichof(sample,set)
 | |
| BYTE *sample;
 | |
| BYTE *set;
 | |
| {
 | |
| 	WORD sx;
 | |
| 	BYTE *sp, *np;
 | |
| 
 | |
| 	if( (sp=_wget(sample,set+1,*set)) == NULLPTR )
 | |
| 		return 0;				/* not found */
 | |
| 	if( _wget(sample,sp+1,*set) != NULLPTR )
 | |
| 		return 0;				/* ambiguous */
 | |
| 	for( sx=0, np=set; np<sp; np++ )
 | |
| 		if( *np == *set ) sx++;		/* count delimiters */
 | |
| 	return sx;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /*******************************************************************/
 | |
| /*******************************************************************/
 | |
| /* misc.c:
 | |
| /* some miscellaneous functions which belong in the system library */
 | |
| /*******************************************************************/
 | |
| /*******************************************************************/
 | |
| #ifndef MAINMODULE
 | |
| #include <portab.h>
 | |
| #include <stdio.h>
 | |
| #include <cpm.h>			/* directory search support */
 | |
| #endif
 | |
| 
 | |
| #define FCB struct fcbstruct
 | |
| FCB {
 | |
| 	BYTE	fcbdr;			/* drive */
 | |
| 	BYTE	fcbname[8];		/* name */
 | |
| 	BYTE	fcbext[3];		/* extension */
 | |
| 	BYTE	fcbrest[24];		/* all the rest */
 | |
| };
 | |
| 
 | |
| 
 | |
| #define FSEARCH(faddr) (bdos(17,faddr)&0xFF)
 | |
| #define NSEARCH(faddr) (bdos(18)&0xFF)	/* doesn't use FCB */
 | |
| #define SETDMA(dma) bdos(26,dma)	/* where to put dir info */
 | |
| #define SECSIZE 128
 | |
| #define AMASK 0x7F
 | |
| 
 | |
| /* dirsearch: returns a DLIST of names matching 'fs' */
 | |
|     DLIST *				/* returns list of names found in */
 | |
| dirsearch(fs)				/* directory search for */
 | |
|     BYTE *fs;				/* file spec */
 | |
| {
 | |
| 	FCB f, *fp;			/* use this FCB */
 | |
| 	REG WORD ret;			/* ptr */
 | |
| 	DLIST *_newdli(), *ls;		/* list */
 | |
| 	BYTE dmabuf[SECSIZE];		/* place for directory info */
 | |
| 
 | |
| 	fp = &f;			/* everything else uses address */
 | |
| 	parsefn(fs,fp);			/* fill fcb with filespec */
 | |
| 	SETDMA(dmabuf);			/* tell em where to put it */
 | |
| 	if( (ret=FSEARCH(fp)) > 3 )	/* look for first occurrence */
 | |
| 	    return NULLPTR;		/* no luck */
 | |
| 	ls=_newdli(dmabuf+(ret<<5),NULLPTR);	/* start off a list */
 | |
| 	while( (ret=NSEARCH(fp)) <= 3 ) { /* do all the search_nexts */
 | |
| 	    ls = _newdli(dmabuf+(ret<<5),ls); /* link them together */
 | |
| 	}
 | |
| 	return ls;
 | |
| }
 | |
| 
 | |
| 
 | |
|     DLIST *
 | |
| _newdli(f,lnk)
 | |
|     FCB *f;
 | |
|     DLIST *lnk;
 | |
| {
 | |
| 	REG WORD i;
 | |
| 	BYTE *np, nb[15];		/* name buffer */
 | |
| 	DLIST *newl;			/* new list item */
 | |
| 
 | |
| 					/* make a pretty filename */
 | |
| 	np = nb;
 | |
| 	for( i=0; i<11; ++i )		/* zap out any high bits in filename */
 | |
| 	    f->fcbname[i] &= AMASK;	/* mask off attribute bits */
 | |
| 	for( i=0; i<8; ++i ) {		/* handle name spec */
 | |
| 	    if( f->fcbname[i] == ' ' )	/* end of name? */
 | |
| 		break;			/* okay */
 | |
| 	    *np++ = f->fcbname[i];	/* o.w. stuff into name buffer */
 | |
| 	}
 | |
| 	*np++ = '.';			/* tack this on */
 | |
| 	for( i=0; i<3; ++i ){		/* handle ext spec */
 | |
| 	    if( f->fcbext[i] == ' ' )	/* end of ext? */
 | |
| 		break;
 | |
| 	    *np++ = f->fcbext[i];
 | |
| 	}
 | |
| 	*np++ = NULL;			/* null terminate string */
 | |
| 	/*************************************************************/
 | |
| 
 | |
| 	np = malloc(np-nb);		/* save it away */
 | |
| 	strcpy(np,nb);			/* do the copy */
 | |
| 	newl = (DLIST *)malloc(sizeof(*newl));	/* get ptr area set up */
 | |
| 	newl->dlname = np;
 | |
| 	newl->dlnext = 	lnk;
 | |
| 	return newl;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| parsefn(fs,f)
 | |
|     BYTE *fs;				/* file spec */
 | |
|     FCB *f;				/* fcb */
 | |
| {
 | |
| 	REG WORD i;
 | |
| 	REG BYTE *cp;
 | |
| 
 | |
| 	for( cp=fs; *cp; cp++ )		/* make everything upper case */
 | |
| 	    if( islower(*cp) )
 | |
| 		*cp = toupper(*cp);
 | |
| 	for( i=0; i<24; ++i )		/* init fcb */
 | |
| 	    f->fcbrest[i] = 0;
 | |
| 	if( *(fs+1) == ':' ) {		/* drive spec? */
 | |
| 	    f->fcbdr = 1 + *fs - 'A';	/* 0=default, 1 = A:,... */
 | |
| 	    fs += 2;			/* skip dr spec */
 | |
| 	} else f->fcbdr = 0;		/* default drive */
 | |
| 	for( i=0; *fs && *fs != '.' && i<8; fs++, ++i ) {	/* NAME. */
 | |
| 	    if( *fs == '*' ) {		/* wildcard? */
 | |
| 		for( ; i<8; ++i )	/* fill up rest of name */
 | |
| 		    f->fcbname[i]='?';	/* with search spec */
 | |
| 		for( ; *fs && *fs!='.'; fs++ )	/* ignore to end or '.' */
 | |
| 		    ;
 | |
| 		break;			/* terminate loop */
 | |
| 	    }
 | |
| 	    f->fcbname[i] = *fs;
 | |
| 	}
 | |
| 	for( ; i<8; ++i )		/* fill up rest of name */
 | |
| 	    f->fcbname[i] = ' ';	/* with blanks */
 | |
| 	for( ; *fs; fs++ )		/* skip rest of name */
 | |
| 	    if( *fs == '.' ) {		/* end of name? */
 | |
| 	        fs++; break;		/* yes, so exit this loop */
 | |
| 	    }
 | |
|   	for( i=0; *fs && i<3; fs++,++i ) {			/* .EXT */
 | |
| 	    if( *fs == '*' ){		/* wildcard */
 | |
| 		for( ; i<3; ++i )
 | |
| 		    f->fcbext[i] = '?';
 | |
| 		break;
 | |
| 	    }
 | |
| 	    f->fcbext[i] = *fs;
 | |
| 	}
 | |
| 	for( ; i<3; ++i )		/* fill up rest of ext */
 | |
| 	    f->fcbext[i] = ' ';		/* with blanks */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* access: tests for existence: return NULL if exist, -1 o.w. */
 | |
|     WORD
 | |
| access(fn)
 | |
|     BYTE *fn;
 | |
| {
 | |
| 	FCB f;				/* use this FCB */
 | |
| 	BYTE dmabuf[SECSIZE];		/* place for directory info */
 | |
| 
 | |
| 	parsefn(fn,&f);			/* fill fcb with filespec */
 | |
| 	SETDMA(dmabuf);			/* tell em where to put it */
 | |
| 	if( FSEARCH(&f) > 3 )		/* look for first occurrence */
 | |
| 	    return -1;			/* no luck */
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
|     BYTE *
 | |
| gets(s)
 | |
|     BYTE *s;
 | |
| {
 | |
| 	BYTE *sav;
 | |
| 
 | |
| 	if( fgets(s,32767,stdin) == NULLPTR )
 | |
| 		return NULLPTR;
 | |
| 	if( !isatty(0) )		/* reading from CON:? */
 | |
| 		fputs(s,stdout);	/* echo if not */
 | |
| 	for( sav=s; *s && *s != '\n'; ++s )
 | |
| 		;
 | |
| 	*s = '\0';
 | |
| 	return sav;
 | |
| }
 | |
| 
 | |
| /************************************************************************/
 | |
|     BYTE
 | |
| getdrive()
 | |
| {
 | |
| 	return bdos(25,0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| 
 | |
| /* press_return: let user look at screen */
 | |
| press_return()
 | |
| {
 | |
| 	BYTE bitbucket[40];		/* leave room */
 | |
| 
 | |
| 	if( !isatty(0) ) {		/* stdin from CON:? */
 | |
| 	    printf("Error in command file: terminating program\n");
 | |
| 	    exit(1);
 | |
| 	}
 | |
| 	printf("\07Press RETURN to continue ");
 | |
| 	if( gets(bitbucket)!=bitbucket )
 | |
| 		exit(1);		/* eof, quit now */
 | |
| }
 | |
| 
 | |
| /* patch: provides a patch area */
 | |
| patch()					/* should never get called */
 | |
| {
 | |
| 	int i;
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	i=i+5; i=i+5; i=i+5; i=i+5; i=i+5; 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*******************************************************************/
 | |
| /*******************************************************************/
 | |
| /*			T H E   E N D
 | |
| /*******************************************************************/
 | |
| /*******************************************************************/
 |