v07i073: A BASIC Interpreter, Part01/06

sources-request at mirror.UUCP sources-request at mirror.UUCP
Fri Dec 5 01:23:40 AEST 1986


Submitted by: phil at Cs.Ucl.AC.UK
Mod.sources: Volume 7, Issue 73
Archive-name: basic/Part01

[  This code ran fine on my Pyramid98x.  --r$  ]

# Shar file shar01 (of 6)
#
# This is a shell archive containing the following files :-
#	README
#	assist.c
#	bas.h
#	bas1.c
# ------------------------------
# This is a shell archive, shar, format file.
# To unarchive, feed this text into /bin/sh in the directory
# you wish the files to be in.

echo Makeing subdirs 1>&2
mkdir pdp11 cursor vax pyramid docs m68000
echo x - README 1>&2
sed 's/^X//' > README << 'End of README'
XBASIC (an Interpreter)
X----------------------
X
XThis directory contains the source of my BASIC interpreter. 
XIt was originally started when I was a student as a 2ndyr project, I
Xcontinued to work on it afterwards every once in a while, putting
Xvarious extra facilities into it as I went along. 
XThe interpreter is based on a combination of Microsoft level 5 basic and
Xand RT11's MU-Basic with a smattering of Basic Plus in there for good
Xmeasure. The rational behind this was that these were the versions I
Xfirst learned to program in (many years ago). There are some parts of
Xthe system I would redo again (especially the file handling - which is
Xonly just workable) but I don't have the time. I'm sure the
Xdocumentation does not have all the latest facilities in but most of
Xthem can be worked out from the source code.
X
XThis code is being put in the Public Domain since I will soon loose
Xnetwork connectivity (I am leaving my job) and I don't particularly want
Xto sell it. This system does not contain any proprietary software. All
Xthe algorithms are original or come from publicly available sources.
X
XThere are no licensing restrictions on this code or documentation at
Xall. I only ask that you give appropriate credit to the author.
X
XBuilding the system
X-------------------
X
XThis system has been built and tested on a Vax running 4.2 (4.1) on a
Xpdp11 (with and without floating point hardware ) running V6 V7 BSD 2.8 and
XBSD 2.9, a pyramid 98X and on a unisoft 68000 (V7) system. With
Xappropriate convertion of the terminal handling routines (about 20 lines
Xof code) it should run on System V systems as well.
X
XThe system dependent code has been extracted and placed in relevent
Xsubdirectories. Follow one of the current systems for conversion guidance.
XThe only nasty is that it assumes (in print()) that ints and structure
Xpointers are the same size. This can be fixed but I don't want to do it.
X(It also assumes that all pointer types are the same size which I
Xwouldn't like to have to fix)
X
XTo compile the system use the "gen" shell script which will do all the
Xwork.
X
XYou may want to sort out the terminal handling/editing routines as
Xwell.
X
XHave fun.
X
XPhil Cockcroft  Fall, 86
X------------------------
End of README
chmod u=rw-,g=r,o=r README
echo x - assist.c 1>&2
sed 's/^X//' > assist.c << 'End of assist.c'
X/*
X * BASIC by Phil Cockcroft
X */
X#include "bas.h"
X
X/* this file contains all the routines that were originally done in assembler
X * these routines only require a floating point emulator to work.
X * To speed things up some routines could be put into assembler and some
X * could be made into macros. the relevent routines are labeled as such
X */
X
X#ifndef VAX_ASSEM       /* if done in assembler don't bring it in */
X/* AS */
X
X/* get a single character from the line pointed to by getch() */
X
Xgetch()
X{
X	register char   *p;
X
X	p = point;
X	while(*p++ == ' ');
X	point = p;
X	return(*--p & 0377);
X}
X
X/* AS  #define  ELSE 0351 */
X
Xcheck()         /* check to see no garbage at end of command */
X{
X	register char   *p;
X	register char   c;
X
X	p = point;
X	while(*p++ == ' ');
X	if(! (c = *--p) || c == ':' || (c == (char)ELSE && elsecount)){
X		point = p;
X		return;
X	}
X	error(SYNTAX);          /* not a terminator - error */
X}
X#endif
X
X#ifndef SOFTFP
Xfpcrash()
X{
X	error(34);      /* arithmetic overflow */
X}
X#endif
X
Xint     (*fpfunc)();
X
Xstartfp()
X{
X#ifndef SOFTFP
X	fpfunc = fpcrash;       /* will call error(34) on overflow */
X#else
X	fpfunc = 0;
X#endif
X}
X
X/* AS */
X
X/* compare two values. return 0 if equal -1 if first less than second
X * or 1 for vice versa.
X */
X
Xcmp(p,q)
Xregister value  *p,*q;
X{
X	if(vartype){
X		if(p->i == q->i)
X			return(0);
X		else if(p->i < q->i)
X			return(-1);
X		return(1);
X	}
X	if(p->f == q->f)
X		return(0);
X	else if(p->f< q->f )
X		return(-1);
X	return(1);
X}
X
X/* the arithmetic operation jump table */
X
X
X/* all the routines below should be put into AS */
X
Xint     fandor(), andor(), comop(), fads(), ads(),
X	fmdm(), mdm(), fexp(), ex();
X
Xint     (*mbin[])() = {
X	0,0,
X	fandor,
X	andor,
X	comop,
X	comop,
X	fads,
X	ads,
X	fmdm,
X	mdm,
X	fexp,
X	ex,
X	};
X
Xtypedef value   *valp;
X
Xex(p,q,c)               /* integer exponentiation */
Xvalp    p,q;
X{
X	cvt(p);
X	cvt(q);
X	vartype = 0;
X	fexp(p,q,c);
X}
X
Xfmdm(p,q,c)             /* floating * / mod */
Xvalp    p,q;
X{
X	double  floor(),x;
X	if(c == '*'){
X		fmul(p,q);
X		return;
X	}
X	if(q->f == 0)
X		error(25);
X	if(c=='/')
X		fdiv(p,q);
X	else  {         /* floating mod - yeuch */
X		if( (x = p->f/q->f) < 0)
X			q->f = p->f + floor(-x) * q->f;
X		else
X			q->f = p->f - floor(x) * q->f;
X	}
X}
X
Xmdm(p,q,c)              /* integer * / mod */
Xvalp    p,q;
X{
X	register long    l;
X	register short  ll;
X
X	l = p->i;
X	if(c=='*'){
X		l *= q->i;
X#ifdef  VAX_ASSEM
X		ll = l;
X		{ asm("bvc mdmov"); }
X			q->f = l;
X			vartype = 0;
X			{ asm("ret"); }         /* could be 'return' */
X		{ asm("mdmov: "); }
X		q->i = ll;
X#else
X		if(l > 32767 || l < -32768){    /* overflow */
X			q->f = l;
X			vartype = 0;
X		}
X		else q->i = l;
X#endif
X		return;
X	}
X	if(!q->i)                       /* zero divisor error */
X		error(25);
X	ll = p->i % q->i;
X	if(c == '/'){
X		if(ll){
X			q->f = (double)l / q->i;
X			vartype = 0;
X		}
X		else
X			q->i = p->i / q->i;
X	}
X	else
X		q->i = ll;
X}
X
Xfads(p,q,c)             /* floating + - */
Xvalp    p,q;
X{
X	if(c=='+')
X		fadd(p,q);
X	else
X		fsub(p,q);
X}
X
Xads(p,q,c)              /* integer + - */
Xvalp    p,q;
X{
X	register long   l;
X#ifdef  VAX_ASSEM
X	register short  ll;
X#endif
X
X	l = p->i;
X	if(c == '+')
X		l += q->i;
X	else
X		l -= q->i;
X#ifdef  VAX_ASSEM
X		ll = l;
X		{ asm("bvc adsov"); }
X			q->f = l;
X			vartype = 0;
X			{ asm("ret"); }         /* could be 'return' */
X		{ asm("adsov: "); }
X		q->i = ll;
X#else
X	if(l > 32767 || l < -32768){    /* overflow */
X		q->f = l;
X		vartype = 0;
X	}
X	else
X		q->i = l;
X#endif
X}
X
Xcomop(p,q,c)                    /* comparison operations */
Xvalp    p,q;
X{
X	compare(c,cmp(p,q));
X}
X
Xfandor(p,q,c)                   /* floating logical AND/OR/XOR */
Xregister valp    p,q;
X{
X	vartype = 01;
X#ifdef  PORTABLE
X	p->i = ((p->f != 0.0) ? -1 : 0);
X	q->i = ((q->f != 0.0) ? -1 : 0);
X#else
X	p->i = (p->i ? -1 : 0);
X	q->i = (q->i ? -1 : 0);
X#endif
X	andor(p,q,c);
X}
X
Xandor(p,q,c)                    /* integer logical */
Xvalp    p,q;
X{
X	register i,j;
X
X	i = p->i;
X	j = q->i;
X	if(c == ANDD)           /* and */
X		i &= j;
X	else if(c == ORR)       /* or */
X		i |= j;
X	else
X		i ^= j;         /* xor */
X	q->i = i;
X}
X
X/* down to about here */
X
X/* MACRO */
X
Xputin(p,var)            /* convert + put the value in res into p */
Xmemp    p;
Xchar    var;
X{
X	if(vartype != var){
X		if(var){
X			if(conv(&res))
X				error(35);
X		}
X		else
X			cvt(&res);
X	}
X	if(var)
X		((value *)p)->i = res.i;
X	else
X		((value *)p)->f = res.f;
X}
X
X/* MACRO */
X
Xnegate()                /* negate the value in res */
X{
X	if(vartype){
X		if(res.i == -32768){    /* special case */
X			res.f = 32768;
X			vartype = 0;
X		}
X		else
X			res.i = -res.i;
X	}
X	else
X		res.f = -res.f;
X}
X
X/* MACRO */
X
Xnotit()                 /* logical negation */
X{
X	if(vartype){
X		res.i = ~res.i;
X		return;
X	}
X	vartype = 01;
X#ifdef  PORTABLE
X	if(res.f)
X		res.i = 0;
X	else
X		res.i = -1;
X#else
X	if(res.i)
X		res.i = 0;
X	else
X		res.i = -1;
X#endif
X}
X
Xfexp(p,q,c)                     /* floating exponentiation */
Xvalp    p,q;
X{
X	double  x,log(),exp();
X
X	if(p->f < 0)
X		error(41);
X	else if(q->f == 0.0)
X		q->f = 1.0;
X	else if(p->f == 0.0)            /* could use pow - but not on v6 */
X		q->f = 0.0;
X	else {
X		if( (x = log(p->f) * q->f) > 88.02969) /* should be bigger */
X			error(40);
X		q->f = exp(x);
X	}
X}
End of assist.c
chmod u=rw-,g=r,o=r assist.c
echo x - bas.h 1>&2
sed 's/^X//' > bas.h << 'End of bas.h'
X/*
X * BASIC by Phil Cockcroft
X */
X/*
X *      This file contains all the variables and definitions needed by
X *    all the C parts of the interpreter.
X */
X
X/*
X * include the correct include file for the current machine
X */
X
X#ifdef  vax
X#include "vax/conf.h"
X#endif
X#ifdef  pdp11
X#include "pdp11/conf.h"
X#endif
X#ifdef  m68000
X#include "m68000/conf.h"
X#endif
X#ifdef  pyramid
X#include "pyramid/conf.h"
X#endif
X
X#define MASK            0377
X#define SPECIAL         0200            /* top bit set */
X#define SYNTAX          1               /* error code */
X#define MAXLIN          255             /* maximum length of input line */
X#define BUSERR          10              /* bus error */
X#define SEGERR          11              /* segmentation violation */
X#define DEFAULTSTRING   512             /* default size of string space */
X#define VARREQD         2               /* error code */
X#define OUTOFSTRINGSPACE 3              /* ditto */
X#define NORMAL          0               /* normal return from a command */
X#define GTO             1               /* ignore rest of line return */
X#define normret return(NORMAL)
X#define MAXERR          51              /* maximum value of error code */
X#define BADDATA         26              /* error message values */
X#define OUTOFDATA       27
X#define FUNCT           33
X#define FLOATOVER       34
X#define INTOVER         35
X#define REDEFFN         45
X#define UNDEFFN         46
X#define CANTCONT        47
X
X#ifdef  LNAMES                          /* if you want long names... */
X
X#define MAXNAME         16              /* maximum size of a name -1 */
X#define HSHTABSIZ       37              /* size of initial hash table */
X					/* very rule of thumb. */
X#endif
X
X/*
X *      values of constants from the symbol table
X */
X
X#define MAXFUNC         0350            /* maximum allowed function code */
X#define RND             0343            /* rnd function code */
X#define FN              0344
X#define MINFUNC         0311
X#define MAXSTRING       0307
X#define DATE            0310
X#define MAXCOMMAND      0272            /* maximum allowed command code */
X#define MINSTRING       0271            /* the rest are pretty obvious */
X#define DATA            0236
X#define QUOTE           0233
X#define ERROR           0231
X#define GOSUB           0226
X#define FOR             0224
X#define IF              0221
X#define INPUT           0212
X#define RUNN            0201
X#define REM             0203
X#define GOTO            0202
X#define WHILE           0257
X#define WEND            0260
X#define REPEAT          0255
X#define UNTIL           0256
X#define ELSE            0351
X#define THEN            0352
X#define ON              0230
X#define RESUME          0220
X#define RESTORE         0240
X#define TABB            0353            /* tab command */
X#define STEP            0354
X#define TO              0355
X#define AS              0365
X#define OUTPUT          0366
X#define APPEND          0367
X#define TERMINAL        0371
X
X/*      logical operators */
X
X#define MODD            0361
X#define ANDD            0356
X#define ORR             0357
X#define XORR            0360
X#define NOTT            0370
X
X/*      comparison operators */
X
X#define EQL             '='
X#define LTEQ            0362
X#define NEQE            0363
X#define LTTH            '<'
X#define GTEQ            0364
X#define GRTH            '>'
X
X/*      values used for file maintainance */
X
X#define _READ           01
X#define _WRITE          02
X#define _EOF            04
X#define _TERMINAL       010
X
X/*
X   N.B. The value of this (_BLOCKED) controls wether the blockmode file stuff
X	is included. ( comment this constant out if don't want it).
X*/
X#define _BLOCKED        020
X
X#define MAXFILES        9
X
X#define ESCAPE		'\033'
X
X/*      definitions of some simple functions */
X/*      isletter()      - true if character is a letter */
X/*      isnumber()      - true if character is a number */
X/*      istermin()      - true if character is a terminator */
X
X#define isletter(c)  ((c)>='a' && (c)<='z')
X#define isnumber(c)  ((c)>='0' && (c)<='9')
X#define istermin(c)  (!(c)|| (c)==':' ||((char)(c)==(char)ELSE && elsecount))
X
X/*      define the offset to the next line */
X
X#define lenv(p)      ((p)->llen)
X
Xtypedef struct  olin    *lpoint;        /* typedef for pointer to a line */
Xtypedef struct  deffn   *deffnp;        /* pointer to a function definition */
Xtypedef struct  filebuf *filebufp;      /* pointer to a filebuffer */
Xtypedef struct  forst   *forstp;        /* pointer to a for block */
Xtypedef struct  strarr  *strarrp;       /* pointer to an array header */
Xtypedef struct  vardata *vardp;         /* pointer to a variable */
Xtypedef struct  stdata  *stdatap;       /* pointer to a string header */
Xtypedef char    *memp;                  /* a memory pointer */
X
X/*      typedef fo the standard dual type of variable */
X
Xtypedef union {
X		short   i;
X		double  f;
X	   } value;
X
X/*      declarations to stop the C compiler complaining */
X
Xfilebufp getf();
Xlpoint  getline();
Xmemp    xpand(),getname();
Xchar    *printlin(),*strcpy(),*grow(),*getenv();
X
Xint     rnd(),ffn(),pii(),erlin(),erval(),tim();
Xint     sgn(),len(),abs(),val(),ascval(),instr(),eofl(),fposn(),sqrtf(),
X	logf(),expf(),evalu(),intf(),peekf(),sinf(),cosf(),atanf(),
X	mkint(),mkdouble(), ssystem();
Xint     midst(),rightst(),leftst(),strng(),estrng(),chrstr(),nstrng(),
X	space(),getstf(),mkistr(),mkdstr();
Xint     endd(),runn(),gotos(),rem(),lets(),list(),
X	print(),stop(),delete(),editl(),input(),clearl(),
X	save(),old(),neww(),shell(),resume(),iff(),
X	random(),dimensio(),forr(),next(),gosub(),retn(),
X	onn(),doerror(),print(),rem(),dauto(),
X	readd(),dodata(),cls(),restore(),base(),fopen(),
X	fclosef(),merge(),quit(),chain(),deffunc(),cont(),lhmidst(),
X	linput(),poke(),rept(),untilf(),whilef(),wendf(),fseek(),renumb(),
X	dump(),loadd();
X
X/*      all structures must have an exact multiple of the size of an int
X *    to the start of the next structure
X */
X
Xstruct  stdata  {               /* data for the string pointer */
X	unsigned snam;          /* getname() will return the address */
X	char    *stpt;          /* of this structure for a string access */
X	};
X
Xstruct  vardata {               /* storage of a standard non-indexed */
X	unsigned nam;           /* variable */
X	value   dt;
X	};
X
Xtypedef unsigned xlinnumb;      /* the type of linnumbers */
X
Xstruct olin{                    /* structure for a line */
X	unsigned linnumb;
X	unsigned llen;
X	char     lin[1];
X	};
X
Xstruct  strarr {                /* structure for an array */
X	unsigned snm;           /* name */
X	int     hash;           /* index to the next array or the start */
X	short   dimens;         /* of the special numbers */
X	short   dim[3];         /* the dimensions */
X	};
X
X
Xstruct  forst {                 /* for / gosub stack */
X	char    *fnnm;          /* pointer to variable - relative to earray */
X	char    fr,elses;       /* type of structure , elsecount on return */
X	value   final;          /* the start and end values */
X	value   step;
X	lpoint  stolin;         /* pointer to return start of line */
X	char    *pt;            /* return value for point */
X	};
X
X#ifdef  LNAMES
X
Xstruct  entry   {               /* the structure for a long name storage */
X	struct  entry   *link;
X	int     ln_hash;        /* hash value of entry */
X	char    _name[MAXNAME];
X	};
X
X#endif
X
X#ifdef  V7
X
X#include        <setjmp.h>
X#include        <signal.h>
X#include        <sys/types.h>
X#include        <sys/stat.h>
X
X#define setexit()       setjmp(rcall)
X#define reset()         longjmp(rcall,0)
X
X#else
X
Xstruct  stat    {
X	short   st_dev;
X	short   st_ino;
X	short   st_mode;
X	int     _stat[15];
X	};
X
X#define _exit(x)        exit(x)
X
Xint     (*signal())();
X#define SIGINT  2
X#define SIGQUIT 3
X#define SIGFPE  8
X#define SIG_IGN ((int(*)())1)
X#define SIG_DFL ((int(*)())0)
X#define NSIG    16
X
X#endif
X
X#ifndef pdp11           /* don't need it on a VAX system */
X#define checksp()       /* nothing */
X#endif
X
Xstruct  filebuf {               /* the file buffer structure */
X	short   filedes;        /* system file descriptor */
X	short   userfiledes;    /* user name */
X	int     posn;           /* cursor / read positon */
X#ifdef  _BLOCKED
X	short   blocksiz;       /* if want block mode files */
X#endif
X	short   inodnumber;     /* to stop people reading and writing */
X	short   device;         /* to the same file at the same time */
X	short   use;            /* flags */
X	short   nleft;          /* number of characters in buffer */
X	char    buf[BLOCKSIZ];  /* the buffer itself */
X	};
X
Xstruct tabl {                   /* structure for symbol table */
X	char    *string;
X	int     chval;
X	};
X
Xstruct  deffn  {                /* structure for a user definable function */
X	int     dnm;
X	int     offs;
X	char    narg;
X	char    vtys;
X	short   vargs[3];
X	char    exp[1];
X	};
X
X#ifndef SOFTFP
X
X#define fadd(p,q)       ((q)->f += (p)->f)
X#define fsub(p,q)       ((q)->f = (p)->f - (q)->f)
X#define fmul(p,q)       ((q)->f *= (p)->f)
X#define fdiv(p,q)       ((q)->f = (p)->f / (q)->f)
X
X#define conv(p) \
X	( ((p)->f > MAXint || (p)->f < MINint) ? 1 : ( ((p)->i = (p)->f), 0) )
X
X#define cvt(p)  (p)->f = (p)->i
X
X#endif
X
X/*
X * On pdp11's and VAXen the loader is clever about global bss symbols
X * On 68000's this is not true so we have to define the memory pointers
X * to be members of an array.
X */
X#ifdef  MPORTABLE
X#define estring _space_[0]
X#ifdef  LNAMES
X#define enames  _space_[1]
X#define edefns  _space_[2]
X#define estarr  _space_[3]
X#define earray  _space_[4]
X#define vend    _space_[5]
X#define bstk    _space_[6]
X#define vvend   _space_[7]
X#else
X#define edefns  _space_[1]
X#define estarr  _space_[2]
X#define earray  _space_[3]
X#define vend    _space_[4]
X#define bstk    _space_[5]
X#define vvend   _space_[6]
X#endif
X
X#endif
X
X
X/*
X *      PART1 is declared only once and so allocates storage for the
X *    variables only once , otherwise the definiton for the variables
X *    ( in all source files except bas1.c ). is declared as external.
X */
X
X#ifdef  PART1
X
Xint     baseval=1;              /* value of the initial base for arrays */
Xchar    nl[]="\n";              /* a new_line character */
Xchar    line[MAXLIN+2];         /* the input line */
Xchar    nline[MAXLIN];         /* the array used to store the compiled line */
Xunsigned linenumber;            /* linenumber form compile */
X
X/*  pointers to the various sections of the memory map */
X
Xmemp    filestart;      /* end of bss , start of file buffers */
Xmemp    fendcore;       /* end of buffers , start of text */
Xmemp    ecore;          /* end of text , start of string space */
Xmemp    eostring;       /* end of full strings */
Xmemp    estdt;          /* start of string header blocks */
X
X/* all these pointers below must be defined in this order so that xpand
X * will be able to increment them all */
X
X#ifndef MPORTABLE
Xmemp    estring;        /* end of strings , start of func defs */
X#ifdef  LNAMES
Xmemp    enames;         /* end of symbol table. start of def fncs */
X#endif
Xmemp    edefns;         /* end of def fncs , start of arrays */
Xmemp    estarr;         /* end of string array structures */
Xmemp    earray;         /* end of arrays , start of simple variables */
Xmemp    vend;           /* end of simple variables , start of gosub stack */
Xmemp    bstk;
Xmemp    vvend;          /* end of stack , top of memory */
X#else
Xmemp    _space_[8];     /* for use in portable systems */
X#endif
X
X/* up to this point */
X
Xint     cursor;         /* position of cursor on line */
Xunsigned shash;         /* starting value for string arrays */
Xint     mcore();        /* trap functions- keep compiler happy */
Xint     seger();
Xint     trap();
Xlpoint  stocurlin;      /* start of current line */
Xunsigned curline;       /* current line number */
Xint     readfile;       /* input file , file descriptor */
Xchar    *point;         /* pointer to current location */
Xchar    *savepoint;     /* value of point at start of current command */
Xchar    elsecount;      /* flag for enabling ELSEs as terminators */
Xchar    vartype;        /* current type of variable */
Xchar    runmode;        /* run or immeadiate mode */
Xchar    ertrap;         /* are about to call the error trapping routine */
Xchar    intrap;         /* we are in the error trapping routine */
Xchar    trapped;        /* cntrl-c trap has occured */
Xchar    inserted;       /* the line table has been changed, clear variables */
Xchar    eelsecount;     /* variables to save the current state after an */
Xlpoint  estocurlin;     /* error */
Xunsigned elinnumb;      /* ditto */
Xchar    *epoint;        /* ditto */
Xint     ecode;          /* error code */
Xlpoint  errortrap;      /* error trap pointer */
Xlpoint  saveertrap;     /* error trap save location - during trap  */
Xlpoint  datastolin;     /* pointer to start of current data line */
Xchar    *datapoint;     /* pointer into current data line */
Xint     evallock;       /* lock to stop recursive eval function */
Xunsigned autostart=10;  /* values for auto command */
Xunsigned autoincr=10;
Xint     ter_width;      /* set from the terms system call */
X
Xlpoint  constolin;      /* values for 'cont' */
Xunsigned concurlin;
Xlpoint  conerp;
Xchar    *conpoint;
Xchar    contelse;
Xchar    contpos;
Xchar    cancont;
Xchar    noedit;         /* set if noediting is to be done */
X
Xint     pipes[2];       /* pipe structure for chain */
X
Xlong    overfl;         /* value of overflowed integers, converting to real */
X
Xvalue   res;            /* global variable for maths function */
X
Xdouble  pivalue= 3.14159265358979323846;        /* value of pi */
X#ifndef SOFTFP
Xdouble  MAXint= 32767;                          /* for cvt */
Xdouble  MINint= -32768;
X#endif
X
X#ifdef  V7
Xjmp_buf rcall;
X#endif
X#ifdef  BSD42
Xjmp_buf ecall;                  /* for use of cntrl-c in edit */
Xchar    ecalling;
X#endif
X				/* one edit mode , one for normal mode */
Xint     nm;                     /* name of variable being accessed */
X
X#ifdef  LNAMES
Xchar    nam[MAXNAME];                   /* local array for long names */
Xstruct  entry   *hshtab[HSHTABSIZ];     /* hash table pointers */
Xint     varshash[HSHTABSIZ];            /* hashing for variables */
Xint     chained;                /* force full search only after a chain() */
X#endif
X
Xchar    gblock[256];            /* global place for string functions */
Xint     gcursiz;                /* size of string in gblock[] */
X
X/*
X *      definition of the command , function and string function 'jump'
X *    tables.
X */
X
X/*      maths functions that do not want an argument */
X
Xint     (*functs[])()= {
X	rnd,ffn, pii, erlin, erval, tim,
X	};
X
X/*      other maths functions */
X
Xint     (*functb[])()={
X	sgn, len, abs, val, ascval, instr, eofl, fposn, sqrtf, logf, expf,
X	evalu,intf,peekf,sinf,cosf,atanf,mkint,mkdouble, ssystem,
X	};
X
X/*      string function , N.B. date$ is not here. */
X
Xint     (*strngcommand[])()= {
X	midst, rightst, leftst, strng, estrng, chrstr, nstrng, space, getstf,
X	mkistr,mkdstr,
X	};
X
X/*      commands */
X
Xint     (*commandf[])()= {
X	endd,runn,gotos,rem,list,lets,print,stop,delete,editl,input,clearl,
X	save,old,neww,shell,resume,iff,random,dimensio,forr,next,gosub,retn,
X	onn,doerror,print,rem,dauto,readd,dodata,cls,restore,base,fopen,
X	fclosef,merge,quit,quit,quit,chain,deffunc,cont,poke,linput,rept,
X	untilf,whilef,wendf,fseek,renumb,loadd,dump,0,0,0,0,lhmidst,
X	};
X
X/*      table of error messages */
X
Xchar    *ermesg[]= {
X	"syntax error",
X	"variable required",
X	"out of string space",
X	"assignment '=' required",
X	"line number required",
X	"undefined line number",
X	"line number overflow",
X	"illegal command",
X	"string overflow",
X	"illegal string size",
X	"illegal function",
X	"illegal core size",
X	"illegal edit",
X	"cannot creat file",
X	"cannot open file",
X	"dimension error",
X	"subscript error",
X	"next without for",
X	"undefined array",
X	"redimension error",
X	"gosub / return error",
X	"illegal error code",
X	"bad load",
X	"out of core",
X	"zero divisor error",
X	"bad data",
X	"out of data",
X	"bad base",
X	"bad file descriptor",
X	"unexpected eof",
X	"out of files",
X	"line length overflow",
X	"argument error",
X	"floating point overflow",
X	"integer overflow",
X	"bad number",
X	"negative square root",
X	"negative or zero log",
X	"overflow in exp",
X	"overflow in power",
X	"negative power",
X	"no space for chaining",
X	"mutually recursive eval",
X	"expression too complex",
X	"illegal redefinition",
X	"undefined user function",
X	"can't continue",
X	"until without repeat",
X	"wend without while",
X	"no wend statement found",
X	"illegal loop nesting",
X	};
X
X/*      tokenising table */
X
Xstruct  tabl    table[]={
X	"end",0200,             /* commands 0200 - 0300 */
X	"run",0201,
X	"goto",0202,
X	"rem",0203,
X	"list",0204,
X	"let",0205,
X	"print",0206,
X	"stop",0207,
X	"delete",0210,
X	"edit",0211,
X	"input",0212,
X	"clear",0213,
X	"save",0214,
X	"old",0215,
X	"new",0216,
X	"shell",0217,
X	"resume",0220,
X	"if",0221,
X	"random",0222,
X	"dim",0223,
X	"for",0224,
X	"next",0225,
X	"gosub",0226,
X	"return",0227,
X	"on",0230,
X	"error",0231,
X	"?",0232,
X	"'",0233,
X	"auto",0234,
X	"read",0235,
X	"data",0236,
X	"cls",0237,
X	"restore",0240,
X	"base",0241,
X	"open",0242,
X	"close",0243,
X	"merge",0244,
X	"quit",0245,
X	"bye",0246,
X	"exit",0247,
X	"chain",0250,
X	"def",0251,
X	"cont",0252,
X	"poke",0253,
X	"linput",0254,
X	"repeat",0255,
X	"until",0256,
X	"while",0257,
X	"wend",0260,
X	"seek",0261,
X#ifdef  RENUMB
X	"renumber",0262,
X#endif
X	"load",0263,
X	"dump",0264,
X	"mid$",0271,            /* string functions 0271 - 0310 */
X	"right$",0272,
X	"left$",0273,
X	"string$",0274,
X	"ermsg$",0275,
X	"chr$",0276,
X	"str$",0277,
X	"space$",0300,
X	"get$",0301,
X#ifdef  _BLOCKED
X	"mkis$",0302,
X	"mkds$",0303,
X#endif
X	"date$",0310,           /* date must be last string funct */
X	"sgn",0311,             /* maths functions 0311 - 0350 */
X	"len",0312,
X	"abs",0313,
X	"val",0314,
X	"asc",0315,
X	"instr",0316,
X	"eof",0317,
X	"posn",0320,
X	"sqrt",0321,
X	"log",0322,
X	"exp",0323,
X	"eval",0324,
X	"int",0325,
X	"peek",0326,
X	"sin",0327,
X	"cos",0330,
X	"atan",0331,
X#ifdef  _BLOCKED
X	"mksi",0332,
X	"mksd",0333,
X#endif
X	"system", 0334,
X	"rnd",0343,
X	"fn",0344,
X	"pi",0345,
X	"erl",0346,
X	"err",0347,
X	"tim",0350,
X	"else",0351,            /* seperators and others 0351 - 0377 */
X	"then",0352,
X	"tab",0353,
X	"step",0354,
X	"to",0355,
X	"and",0356,
X	"or",0357,
X	"xor",0360,
X	"mod",0361,
X	"<=",0362,
X	"<>",0363,
X	">=",0364,
X	"as",0365,
X	"output",0366,
X	"append",0367,
X	"not",0370,
X	"terminal",0371,
X	0,0
X	};
X
X#else
X
X/*   definition of variables for other source files */
X
Xextern  int     baseval;
Xextern  char    nl[];
Xextern  char    line[];
Xextern  char    nline[];
Xextern  unsigned linenumber;
Xextern  memp    fendcore;
X#ifndef MPORTABLE
Xextern  memp    estring,edefns,estarr,earray,vend,bstk,vvend;
X#else
Xextern  memp    _space_[];
X#endif
Xextern  memp    filestart;
Xextern  memp    ecore,eostring,estdt;
Xextern  int     cursor;
Xextern  unsigned shash;
Xextern  int     mcore(),seger(),trap();
Xextern  lpoint  stocurlin;
Xextern  unsigned curline;
Xextern  int     readfile;
Xextern  char    *point;
Xextern  char    *savepoint;
Xextern  char    elsecount;
Xextern  char    vartype;
Xextern  char    runmode;
Xextern  char    ertrap;
Xextern  char    intrap;
Xextern  char    trapped;
Xextern  char    inserted;
Xextern  char    eelsecount;
Xextern  lpoint  estocurlin;
Xextern  unsigned elinnumb;
Xextern  char    *epoint;
Xextern  int     ecode;
Xextern  lpoint  errortrap;
Xextern  lpoint  saveertrap;
Xextern  lpoint  datastolin;
Xextern  char    *datapoint;
Xextern  int     evallock;
Xextern  unsigned autostart;
Xextern  unsigned autoincr;
Xextern  int     ter_width;
Xextern  lpoint  constolin;
Xextern  unsigned concurlin;
Xextern  lpoint  conerp;
Xextern  char    *conpoint;
Xextern  char    contelse;
Xextern  char    contpos;
Xextern  char    cancont;
Xextern  char    noedit;
X
Xextern  int     pipes[];
X
Xextern  long    overfl;
Xextern  value   res;
X
Xextern  double  pivalue;
Xextern  double  MAXint,MINint;
X#ifdef  V7
Xextern  jmp_buf rcall;
X#endif
X
X#ifdef  BSD42
Xextern  jmp_buf ecall;
Xextern  char    ecalling;
X#endif
X
Xextern  int     nm;
X
X#ifdef  LNAMES
Xextern  struct  entry   *hshtab[];
Xextern  char    nam[];
Xextern  int     varshash[];
Xextern  int     chained;
X#ifndef MPORTABLE
Xextern  memp    enames;
X#endif
X#endif
X
Xextern  char    gblock[];
Xextern  int     gcursiz;
X
Xextern  (*functs[])();
Xextern  (*functb[])();
Xextern  (*strngcommand[])();
Xextern  (*commandf[])();
Xextern  char    *ermesg[];
Xextern  struct  tabl    table[];
X
X#endif
End of bas.h
chmod u=rw-,g=r,o=r bas.h
echo x - bas1.c 1>&2
sed 's/^X//' > bas1.c << 'End of bas1.c'
X/*
X * BASIC by Phil Cockcroft
X */
X/*
X *      This file contains the main routines of the interpreter.
X */
X
X
X/*
X *      the core is arranged as follows: -
X * -------------------------------------------------------------------  - - -
X * | file    |  text   |  string | user  | array |  simple    |  for/ | unused
X * | buffers |   of    |  space  | def   | space |  variables | gosub | memory
X * |         | program |         | fns   |       |            | stack |
X * -------------------------------------------------------------------  - - -
X * ^         ^         ^         ^       ^       ^            ^       ^
X * filestart fendcore  ecore     estring edefns  earray       vend    vvend
X *                        ^eostring           ^estarr
X */
X
X#define         PART1
X#include        "bas.h"
X#undef          PART1
X
X/*
X *      The main program , it sets up all the files, signals,terminal
X *      and pointers and prints the start up message.
X *      It then calls setexit().
X * IMPORTANT NOTE:-
X *              setexit() sets up a point of return for a function
X *      It saves the local environment of the calling routine
X *      and uses that environment for further use.
X *              The function reset() uses the information saved in
X *      setexit() to perform a non-local goto , e.g. poping the stack
X *      until it looks as though it is a return from setexit()
X *      The program then continues as if it has just executed setexit()
X *      This facility is used all over the program as a way of getting
X *      out of functions and returning to command mode.
X *      The one exception to this is during error trapping , The error
X *      routine must pop the stack so that there is not a recursive call
X *      on execute() but if it does then it looks like we are back in
X *      command mode. The flag ertrap is used to signal that we want to
X *      go straight on to execute() the error trapping code. The pointers
X *      must be set up before the execution of the reset() , (see error ).
X *              N.B. reset() NEVER returns , so error() NEVER returns.
X */
X
Xmain(argc,argv)
Xchar    **argv;
X{
X	register i;
X	catchsignal();
X	startfp();              /* start up the floating point hardware */
X	setupfiles(argc,argv);
X	setupterm();            /* set up files after processing files */
X	ecore = fendcore+sizeof(xlinnumb);
X	( (lpoint) fendcore )->linnumb=0;
X	clear(DEFAULTSTRING);
X	prints("Phil's Basic version v1.8\n");
X	setexit();
X	if(ertrap)
X		goto execut;
X	docont();
X	runmode=0;              /* say we are in immeadiate mode */
X	if(cursor)              /* put cursor on a blank line */
X		prints(nl);
X	prints("Ready\n");
X	do{
X		do{
X			trapped=0;
X			*line ='>';
X			edit(1,1,1);
X		}while( trapped || ( !(i=compile(1)) && !linenumber ));
X		if(linenumber)
X			insert(i);
X	}while(linenumber);
X	if(inserted){
X		inserted=0;
X		clear(DEFAULTSTRING);
X		closeall();
X	}
X	vvend=bstk;             /* reset the gosub stack */
X	errortrap=0;            /* disable error traps */
X	intrap=0;               /* say we are not in the error trap */
X	trapped=0;              /* say we haven't got a cntrl-c */
X	cursor=0;               /* cursor is at start of line */
X	elsecount=0;            /* disallow elses as terminators */
X	curline=0;              /* current line is zero */
X	point=nline;            /* start executing at start of input line */
X	stocurlin=0;           /* start of current line is null- see 'next' */
Xexecut: execute();              /* execute the line */
X	return(-1);             /* see note below */
X}
X
X/*
X *      Execute will return by calling reset and so if execute returns then
X *    there is a catastrophic error and we should exit with -1 or something
X */
X
X/*
X *      compile converts the input line (in line[]) into tokenised
X *    form for execution(in nline). If the line starts with a linenumber
X *    then that is converted to binary and is stored in 'linenumber' N.B.
X *    not curline (see evalu() ). A linenumber of zero is assumed to
X *    be non existant and so the line is executed immeadiately.
X *      The parameter to compile() is an index into line that is to be
X *    ignored, e.g. the prompt.
X */
X
Xcompile(fl)
Xint     fl;
X{
X	register char   *p,*q;
X	register struct tabl    *l;
X	unsigned lin=0;
X	char    charac;
X	char    *eql(),*k;
X	p= &line[fl];
X	q=nline;
X	while(*p++ ==' ');
X	p--;
X	while(isnumber(*p)){                    /* get line number */
X		if(lin >= 6553)
X			error(7);
X		lin = lin*10 + (*p++ -'0');
X	}
X	while(*p==' ')
X		*q++ = *p++;
X	if(!*p){
X		linenumber =lin;
X		return(0);      /* no characters on the line */
X	}
X	while(*p){
X		if(*p=='"' || *p=='`'){         /* quoted strings */
X			charac= *p;
X			*q++ = *p++;
X			while(*p && *p != charac)
X				*q++ = *p++;
X			if(*p)
X				*q++= *p++;
X			continue;
X		}
X		if(*p < '<' && *p != '\''){     /* ignore all characters */
X			*q++ = *p++;            /* that couldn't be used */
X			continue;               /* in reserved words */
X		}
X		for(l=table ; l->string ; l++)  /* search the table */
X			if(*p != *(l->string) ) /* for the right entry */
X				continue;
X			else if(k = eql(p,l->string)){  /* if found then */
X#ifdef  LKEYWORDS
X				if( isletter(*p) ){
X					if(p!= &line[fl] && isletter(*(p-1)) )
X						continue;
X					if( isletter(*k) && l->chval != FN)
X						continue;
X				}
X#endif
X				*q++ = l->chval;    /* replace by a token */
X				p = k;
X				if(l->chval== REM || l->chval== QUOTE ||
X							l->chval == DATA)
X					while(*p)
X						*q++ = *p++;
X				goto more;      /* dont compile comments */
X			}                       /* or data */
X		*q++ = *p++;
X	more:   ;
X	}
X	*q='\0';
X	linenumber=lin;
X	return(q-nline);                /* return length of line */
X}
X
X/*
X *      eql() returns true if the strings are the same .
X *    this routine is only called if the first letters are the same.
X *    hence the increment of the pointers , we don't need to compare
X *    the characters they point to.
X *      To increase speed this routine could be put into machine code
X *    the overheads on the function call and return are excessive
X *    for what it accomplishes. (it fails most of the time , and
X *    it can take a long time to load a large program ).
X */
X
Xchar    *
Xeql(p,q)
Xregister char   *p,*q;
X{
X	p++,q++;
X	while(*q)
X		if(*p++ != *q++){
X#ifdef  SCOMMS
X			if(*(p-1) == '.')
X				return(p);
X#endif
X			return(0);
X		}
X	return(p);
X}
X
X/*
X *      Puts a line in the table of lines then sets a flag (inserted) so that
X *    the variables are cleared , since it is very likely to have moved
X *    'ecore' and so the variables will all be corrupted. The clearing
X *    of the variables is not done in this routine since it is only needed
X *    to clear the variables once and that is best accomplished in main
X *    just before it executes the immeadiate mode line.
X *      If the line existed before this routine is called then it is deleted
X *    and then space is made available for the new line, which is then
X *    inserted.
X *      The structure of a line in memory has the following structure:-
X *              struct olin{
X *                      unsigned linnumb;
X *                      unsigned llen;
X *                      char     lin[1];
X *                      }
X *      The linenumber of the line is stored in linnumb , If this is zero
X *    then this is the end of the program (all searches of the line table
X *    terminate if it finds the linenumber is zero.
X *      The variable 'llen' is used to store the length of the line (in
X *    characters including the above structure and any padding needed to
X *    make the line an even length.
X *      To search through the table of lines then:-
X *              start at 'fendcore'
X *              IF linnumb is zero THEN terminate search
X *                ELSE IF linnumb is the required line THEN
X *                      found line , terminate
X *                ELSE
X *                      goto next line ( add llen to the current pointer )
X *                      repeat loop.
X *      The line is in fact stored in lin[] , To the C compiler this
X *    is a one character array but since the lines are more than one
X *    character long (usually) it is fooled into using it as a variable
X *    length array ( impossible in 'pure' C ).
X *      The pointers used by the program storage routines are:-
X *              fendcore = start of text storage segment
X *              ecore = end of text storage
X *                    = start of data segment (string space ).
X *    strings are stored after the text but before the numeric variables
X *    only 512 bytes are allocated at the start of the program for strings
X *    but clear can be called to get more core for the strings.
X */
X
Xinsert(lsize)
Xregister int    lsize;
X{
X	register lpoint p;
X	register unsigned l;
X	inserted=1;                  /* say we want the variables cleared */
X	l= linenumber;
X	for(p= (lpoint) fendcore ; p->linnumb; p=(lpoint)((memp)p+lenv(p)))
X		if(p->linnumb >= l ){
X			if(p->linnumb != l )
X				break;
X			l=lenv(p);      /* delete the old line */
X			bmov( (short *)p, (int)l);
X			ecore -= l;
X			break;
X		}
X	if(!lsize)                      /* line has no length */
X		return;
X	lsize += sizeof(struct olin);
X#ifdef  ALIGN4
X	lsize = (lsize + 03) & ~03;
X#else
X	if(lsize&01)
X		lsize++;                /* make length of line even */
X#endif
X	mtest(ecore+lsize);             /* get the core for it */
X	ecore += lsize;
X	bmovu( (short *)p,lsize);       /* make space for the line */
X	strcpy(nline,p->lin);           /* move the line into the space */
X	p->linnumb=linenumber;          /* give it a linenumber */
X	p->llen=lsize;                  /* give it its offset */
X}
X
X/*      This routine will move the core image down so deleteing a line */
X
Xbmov(a,b)
Xregister short  *a;
Xint     b;
X{
X	register short  *c,*d;
X	c= (short *)ecore;
X	d= (short *)((char *)a  + b );
X	do{
X		*a++ = *d++;
X	}while(d<c);
X}
X
X/*      This will move the text image up so that a new line can be inserted */
X
Xbmovu(a,b)
Xregister short  *a;
Xint     b;
X{
X	register short  *c,*d;
X	c= (short *) ecore;
X	d= (short *) (ecore-b);
X	do{
X		*--c = *--d;
X	}while(a<d);
X}
X
X/*
X *      The interpreter needs three variables to control the flow of the
X *    the program. These are:-
X *              stocurlin : This is the pointer to the start of the current
X *                          line it is used to index the next line.
X *                          If the program is in immeadiate mode then
X *                          this variable is NULL (very important for 'next')
X *              point:      This points to the current location that
X *                          we are executing.
X *              curline:    The current line number ( zero in immeadiate mode)
X *                          this is not needed for program exection ,
X *                          but is used in error etc. It could be made faster
X *                          if this variable is not used....
X */
X
X/*
X *      The main loop of the execution of a program.
X *      It does the following:-
X *              FOR(ever){
X *                      save point so that resume will go to the right place
X *                      IF cntrl-c THEN stop
X *                      IF NOT a reserved word THEN do_assignment
X *                              ELSE IF legal command THEN execute_command
X *                      IF return is NORMAL THEN
X *                              BEGIN
X *                                  IF terminator is ':' THEN continue
X *                                  ELSE IF terminator is '\0' THEN
X *                                         goto next line ; continue
X *                                  ELSE IF terminator is 'ELSE' AND
X *                                              'ELSES' are enabled THEN
X *                                                  goto next line ; continue
X *                              END
X *                      ELSE IF return is < NORMAL THEN continue
X *                                      ( used by goto etc. ).
X *                      ELSE IF return is > NORMAL THEN
X *                           ignore_rest_of_line ; goto next line ; continue
X *                      }
X *      All commands return a value ( if they return ). This value is NORMAL
X *    if the command is standard and does not change the flow of the program.
X *    If the value is greater than zero then the command wants to miss the
X *    rest of the line ( comments and data ).
X *      If the value is less than zero then the program flow has changed
X *    and so we should go back and try to execute the new command ( we are
X *    now at the start of a command ).
X */
X
Xexecute()
X{
X	register int    i,c;
X	register lpoint p;
X
X	ertrap=0;                       /* stop recursive error trapping */
Xagain:
X	savepoint=point;
X	if(trapped)
X		dobreak();
X	if(!((c=getch())&0200)){
X		point--;
X		assign();
X		goto retn;
X	}
X	if(c>=MAXCOMMAND)
X		error(8);
X	if((i=(*commandf[c&0177])())==NORMAL){  /* execute the command */
Xretn:           if((c=getch())==':')
X			goto again;
X		else if(!c){
Xelseret:                if(!runmode)            /* end of immeadiate line */
X				reset();
X			p = stocurlin;
X			p = (lpoint)((memp)p + lenv(p)); /* goto next line */
X			stocurlin=p;
X			point=p->lin;
X			if(!(curline=p->linnumb)) /* end of program */
X				reset();
X			elsecount=0;               /* disable `else`s */
X			goto again;
X		}
X		else  if(c==ELSE && elsecount)  /* `else` is a terminator */
X				goto elseret;
X		error(SYNTAX);
X	}
X	if(i < NORMAL)
X		goto again;     /* changed execution position */
X	else
X		goto elseret;   /* ignore rest of line */
X}
X
X/*
X *      The error routine , this is called whenever there is any error
X *    it does some tidying up of file descriptors and sets the error line
X *    number and the error code. If there is error trapping ( errortrap is
X *    non-zero and in runmode ), then save the old pointers and set up the
X *    new pointers for the error trap routine.
X *    Otherwise print out the error message and the current line if in
X *    runmode.
X *      Finally call reset() ( which DOES NOT return ) to pop
X *    the stack and to return to the main routine.
X */
X
Xerror(i)
Xint     i;                      /* error code */
X{
X	register lpoint p;
X	if(readfile){                   /* close file descriptor */
X		close(readfile);        /* from loading a file */
X		readfile=0;
X	}
X	if(pipes[0]){                   /* close the pipe (from chain ) */
X		close(pipes[0]);        /* if an error while chaining */
X		pipes[0]=0;
X	}
X	evallock=0;                     /* stop the recursive eval message */
X	ecode=i;                        /* set up the error code */
X	if(runmode)
X		elinnumb=curline;       /* set up the error line number */
X	else
X		elinnumb=0;
X	if(runmode && errortrap && !inserted ){ /* we have error trapping */
X		estocurlin=stocurlin;   /* save the various pointers */
X		epoint=savepoint;
X		eelsecount=elsecount;
X		p=errortrap;
X		stocurlin=p;            /* set up to execute code */
X		point=p->lin;
X		curline=p->linnumb;
X		saveertrap=p;           /* save errortrap pointer */
X		errortrap=0;            /* disable further error traps */
X		intrap=1;               /* say we are trapped */
X		ertrap=1;               /* we want to go to execute */
X	}
X	else  {                         /* no error trapping */
X		if(cursor){
X			prints(nl);
X			cursor=0;
X		}
X		prints(ermesg[i-1]);            /* error message */
X		if(runmode){
X			prints(" on line ");
X			prints(printlin(curline));
X		}
X		prints(nl);
X	}
X	reset();                /* no return - goes to main */
X}
X
X/*
X *      This is executed by the ON ERROR construct it checks to see
X *    that we are not executing an error trap then set up the error
X *    trap pointer.
X */
X
Xerrtrap()
X{
X	register lpoint p;
X	p=getline();
X	check();
X	if(intrap)
X		error(8);
X	errortrap=p;
X}
X
X/*
X *      The 'resume' command , checks to see that we are actually
X *    executing an error trap. If there is an optional linenumber then
X *    we resume from there else we resume from where the error was.
X */
X
Xresume()
X{
X	register lpoint p;
X	register unsigned i;
X	if(!intrap)
X		error(8);
X	i= getlin();
X	check();
X	if(i!= (unsigned)(-1) ){
X		for(p=(lpoint)fendcore;p->linnumb;p=(lpoint)((memp)p+lenv(p)))
X			if(p->linnumb==i)
X				goto got;
X		error(6);               /* undefined line */
Xgot:            stocurlin= p;                   /* resume at that line */
X		curline= p->linnumb;
X		point= p->lin;
X		elsecount=0;
X	}
X	else  {
X		stocurlin=estocurlin;          /* resume where we left off */
X		curline=elinnumb;
X		point=epoint;
X		elsecount=eelsecount;
X	}
X	errortrap=saveertrap;                   /* restore error trapping */
X	intrap=0;                               /* get out of the trap */
X	return(-1);                             /* return to re-execute */
X}
X
X/*
X *      The 'error' command , this calls the error routine ( used in testing
X *    an error trapping routine.
X */
X
Xdoerror()
X{
X	register i;
X	i=evalint();
X	check();
X	if(i<1 || i >MAXERR)
X		error(22);      /* illegal error code */
X	error(i);
X}
X
X/*
X *      This routine is used to clear space for strings and to reset all
X *    other pointers so that it effectively clears the variables.
X */
X
Xclear(stringsize)
Xint     stringsize;     /* size of string space */
X{
X#ifdef  LNAMES
X	register struct entry   **p;
X	register int    *ip;
X
X	for(p = hshtab ; p < &hshtab[HSHTABSIZ];)    /* clear the hash table*/
X		*p++ = 0;
X	for(ip = varshash ; ip < &varshash[HSHTABSIZ]; )
X		*ip++ = -1;
X#endif
X#ifdef  ALIGN4
X	estring= &ecore[stringsize& ~03];       /* allocate string space */
X#else
X	estring= &ecore[stringsize& ~01];       /* allocate string space */
X#endif
X	mtest(estring);                         /* get the core */
X	shash=1;                                /* string array "counter" */
X	datapoint=0;                           /* reset the pointer to data */
X	contpos=0;
X#ifdef  LNAMES
X	chained = 0;                            /* reset chained flag */
X	estdt=enames=edefns=earray=vend=bstk=vvend=estarr=estring;
X#else
X	estdt=edefns=earray=vend=bstk=vvend=estarr=estring;
X#endif
X			/* reset variable pointers */
X	eostring=ecore;                         /* string pointer */
X	srand(0);                               /* reset the random number */
X}                                               /* generator */
X
X/*
X *      mtest() is used to set the amount of core for the current program
X *    it uses brk() to ask the system for more core.
X *      The core is allocated in 1K chunks, this is so that the program does
X *    not spend most of is time asking the system for more core and at the
X *    same time does not hog more core than is neccasary ( be friendly to
X *    the system ).
X *      Any test that is less than 'ecore' is though of as an error and
X *    so is any test greater than the size that seven memory management
X *    registers can handle.
X *      If there is this error then a test is done to see if 'ecore' can
X *    be accomodated. If so then that size is allocated and error() is called
X *    otherwise print a message and exit the interpreter.
X *      If the value of the call is less than 'ecore' we have a problem
X *    with the interpreter and we should cry for help. (It doesn't ).
X */
X
Xmtest(l)
Xmemp    l;
X{
X	register memp   m;
X	static   memp   maxmem;                 /* pointer to top of memory */
X
X#ifdef  ALIGN4
X	if( (int)l & 03){
X		prints("Illegal allignment\n");
X		quit();
X	}
X#endif
X	m = (memp)(((int)l+MEMINC)&~MEMINC);    /* round the size up */
X	if(m==maxmem)                           /* if allocated then return */
X		return;
X	if(m < ecore || m > MAXMEM || brk(m) == -1){ /* problems*/
X		m= (memp) (((int)ecore +DEFAULTSTRING+MEMINC )&~MEMINC);
X		if(m <= MAXMEM && brk(m)!= -1){
X			maxmem= m;              /* oh, safe */
X			clear(DEFAULTSTRING);   /* zap all pointers */
X			error(24);              /* call error */
X		}
X		prints("out of core\n");        /* print message */
X		quit();                         /* exit flushing buffers */
X	}
X	maxmem=m;                               /* set new limit */
X}
X
X/*
X *      This routine is called to test to see if there is enough space
X *    for an array. The result is true if there is no space left.
X */
X
Xnospace(l)
Xlong    l;
X{
X#ifndef pdp11
X	if(l< 0 || vvend+l >= MAXMEM)
X#else
X	if(l< 0 || l >65535L || (long)vvend+l >= 0160000L)
X#endif
X		return(1);
X	return(0);      /* we have space */
X}
X
X/*
X *      This routine is called by the routines that define variables
X *    to increase the amount of space that is allocated between the
X *    two end pointers of that 'type'. It uses the fact that all the
X *    variable pointers are in a certain order (see bas.h ). It
X *    increments the relevent pointers and then moves up the rest of
X *    the data to a new position. It also clears the area that it
X *    has just allocated and then returns a pointer to the space.
X */
X
Xmemp xpand(start,size)
Xregister memp   *start;
Xint     size;
X{
X	register short  *p,*q;
X	short   *bottom;
X	bottom = (short *) (*start);
X	p= (short *)vvend;
X	do{
X		*start++ += size;
X	}while( start <= &vvend);
X	mtest(vvend);
X	start= (memp *)bottom;
X	q= (short *)vvend;
X	do{
X		*--q = *--p;
X	}while(p > (short *)start);
X	do{
X		*--q=0;
X	}while(q > (short *)start);
X	return( (memp) start);
X}
X
X/*
X *      This routine tries to set up the system to catch all the signals that
X *    can be produced. (except kill ). and do something sensible if it
X *    gets one. ( There is no way of producing a core image through the
X *    sending of signals).
X */
X
X#ifdef  V6
X#define _exit   exit
X#endif
X
Xcatchsignal()
X{
X	extern  _exit(),quit1(),catchfp();
X#ifdef  SIGTSTP
X	extern  onstop();
X#endif
X	register int    i;
X	static  int     (*traps[NSIG])()={
X		quit,           /* hang up */
X		trap,           /* cntrl-c */
X		quit1,          /* cntrl-\ */
X		_exit,
X		_exit,
X		_exit,
X		_exit,
X		catchfp,        /* fp exception */
X		0,              /* kill    */
X		seger,          /* seg err */
X		mcore,          /* bus err */
X		0,
X		_exit,
X		_exit,
X		_exit,
X		_exit,
X		_exit,
X		};
X
X	for(i=1;i<NSIG;i++)
X		signal(i,traps[i-1]);
X#ifdef  SIGTSTP
X	signal(SIGTSTP,onstop);         /* the stop signal */
X#endif
X}
X
X/*
X *      this routine deals with floating exceptions via fpfunc
X *    this is a function pointer set up in fpstart so that trapping
X *    can be done for floating point exceptions.
X */
X
Xcatchfp()
X{
X	extern  (*fpfunc)();
X
X	signal(SIGFPE,catchfp); /* restart catching */
X	if(fpfunc== 0)          /* this is set up in fpstart() */
X		_exit(1);
X	(*fpfunc)();
X}
X
X/*
X *      we have a segmentation violation and so should print the message and
X *    exit. Either a kill() from another process or an interpreter bug.
X */
X
Xseger()
X{
X	prints("segmentation violation\n");
X	_exit(-1);
X}
X
X/*
X *      This does the same for bus errors as seger() does for segmentation
X *    violations. The interpreter is pretty nieve about the execution
X *    of complex expressions and should really check the stack every time,
X *    to see if there is space left. This is an easy error to fix, but
X *    it was not though worthwhile at the moment. If it runs out of stack
X *    space then there is a vain attempt to call mcore() that fails and
X *    so which produces another bus error and a core image.
X */
X
Xmcore()
X{
X	prints("bus error\n");
X	_exit(-1);
X}
X
X/*
X *      Called by the cntrl-c signal (number 2 ). It sets 'trapped' to
X *    signify that there has been a cntrl-c and then re-enables the trap.
X *      It also bleeps at you.
X */
X
Xtrap()
X{
X	signal(SIGINT, SIG_IGN);/* ignore signal for the bleep */
X	write(1, "\07", 1);     /* bleep */
X	signal(SIGINT, trap);   /* re-enable the trap */
X	trapped=1;              /* say we have had a cntrl-c */
X#ifdef  BSD42
X	if(ecalling){
X		ecalling = 0;
X		longjmp(ecall, 1);
X	}
X#endif
X}
X
X/*
X *      called by cntrl-\ trap , It prints the message and then exits
X *    via quit() so flushing the buffers, and getting the terminal back
X *    in a sensible mode.
X */
X
Xquit1()
X{
X	signal(SIGQUIT,SIG_IGN);/* ignore any more */
X	if(cursor){             /* put cursor on a new line */
X		prints(nl);
X		cursor=0;
X	}
X	prints("quit\n\r");     /* print the message */
X	quit();                 /* exit */
X}
X
X/*
X *      resets the terminal , flushes all files then exits
X *    this is the standard route exit from the interpreter. The seger()
X *    and mcore() traps should not go through these traps since it could
X *    be the access to the files that is causing the error and so this
X *    would produce a core image.
X *      From this it may be gleened that I don't like core images.
X */
X
Xquit()
X{
X	flushall();                     /* flush the files */
X	rset_term(1);
X	if(cursor)
X		prints(nl);
X	exit(0);                       /* goodbye */
X}
X
Xdocont()
X{
X	if(runmode){
X		contpos=0;
X		if(cancont){
X			bstk= vvend;
X			contpos=cancont;
X		}
X		else
X			bstk= vend;
X	}
X	cancont=0;
X}
X
X#ifdef  SIGTSTP
X/*
X * support added for job control
X */
Xonstop()
X{
X	flushall();                     /* flush the files */
X	if(cursor){
X		prints(nl);
X		cursor = 0;
X	}
X#ifdef  BSD42
X	sigsetmask(0);                  /* Urgh !!!!!! */
X#endif
X	signal(SIGTSTP, SIG_DFL);
X	kill(0,SIGTSTP);
X	/* The PC stops here */
X	signal(SIGTSTP,onstop);
X}
X#endif
End of bas1.c
chmod u=rw-,g=r,o=r bas1.c



More information about the Mod.sources mailing list