#include "../h/const.h"
#include "../h/types.h"
/* hardware constants */
#define NOSEGS 4 /* number of segments in seg. table */
#define PRINT0ADDR (devreg_t *)01520
/* address of printer0's device
registers */
extern void *SSI;
/* nucleus constants the test program needs to know */
#define CLOCKINTERVAL 100000L /* interval to V clock semaphore */
/* constants to allow SYS services and SYS calls */
#define S_CREAPROC 1
#define S_TERMPROC 2
#define S_CREATHREAD 3
#define S_TERMTHREAD 4
#define S_PROGTRAPVEC 5
#define S_MMTRAPVEC 6
#define S_SYSTRAPVEC 7
#define S_WAITIO 8
#define S_WAITCLOCK 9
#define S_GETCPUTIME 10
#define S_NONEXISTENT 11
#define DO_MSGSEND SYS1
#define DO_MSGRECV SYS2
struct ssimsg {
int ssiservice;
void ssip;
};
int s_service(service,p)
void p;
{
register r4,r3,r2;
struct ssimsg msgtossi;
msgtossi.ssiservice=service;
msgtossi.ssip=p;
r4 = SSI;
r3 = &msgtossi;
DO_MSGSEND();
DO_MSGRECV();
return(msgtossi.ssiservice);
}
long s_getcputime(service,p)
void p;
{
register int r4,r3,r2;
double_t msg;
msg.w.whigh = S_GETCPUTIME;
r4 = SSI;
r3 = &(msg.w);
DO_MSGSEND();
DO_MSGRECV();
return msg.l;
}
#define CREATENOGOOD NULL /* to note result of err on CREATEPROC */
/* just to be clear */
#define NOLEAVES 4 /* number of leaves of p8 process tree */
state_t p2state, p3state, /* initial states */
p4state, p5state,
p6state, p7state,
p8rootstate, child1state,
child2state, gchild1state,
gchild2state, gchild3state,
gchild4state,printstate;
state_t pstat_n, mstat_n, /* trap states for p5 */
sstat_n, pstat_o,
mstat_o, sstat_o;
int p1p2synch=0; /* to check on p1/p2 synchronization */
int zero=0; /* to check divide by 0 */
int p8inc; /* p8's incarnation number */
int p4inc=1; /* p4 incarnation number */
devreg_t *p_devrg=PRINT0ADDR; /* address of printer0's device
registers */
char iong[12] = "i/o no good";
int p2(),p3(),p4(),p5(),p5a(),p6(),p7(),p7a(),p5prog(),p5mm();
int p5sys(),p8root(),child1(),child2(),p8leaf();
int printthread();
void *printid,*p2id,*p4id,*p5id,*p8id;
int
strlen(string)
register char *string;
{
register int length=0;
while (*string++)
length++;
return(length);
}
/* a procedure to print on the line printer */
print(msg)
char *msg; /* address of message to print */
{
register int r4, r3, r2; /* registers available */
r4=printid;
r3=msg;
DO_MSGSEND();
DO_MSGRECV();
}
printthread()
{
register int r4,r3,r2;
char *msg;
int sender;
char *initmsg="printer test";
/* set up printer's device registers */
p_devrg->d_amnt = strlen(initmsg);
p_devrg->d_badd = initmsg;
p_devrg->d_op = IOWRITE; /* start i/o */
s_service(S_WAITIO,p_devrg);
while (1)
{
r4 = 0;
DO_MSGRECV();
msg=(char *) r3;
sender=r4;
/* set up printer's device registers */
p_devrg->d_amnt = strlen(msg);
p_devrg->d_badd = msg;
p_devrg->d_op = IOWRITE; /* start i/o */
r3=s_service(S_WAITIO,p_devrg);
if (r3 != NORMAL)
{
p_devrg->d_amnt = strlen(iong);
p_devrg->d_badd = iong;
p_devrg->d_op = IOWRITE; /* print `iong' */
s_service(S_WAITIO,p_devrg);
}
r4=sender;
r3=0;
DO_MSGSEND();
}
}
/* */
/* p1 -- the root process */
/* */
test()
{
register int r4, r3, r2; /* registers available */
STST(&printstate);
printstate.s_sp = printstate.s_stl;
printstate.s_stl -= PAGESIZE/2;
printstate.s_pc = (int)printthread;
printid=s_service(S_CREATHREAD,&printstate);
/* set up states of the other processes */
/* set up p2's state */
STST(&p2state); /* create a state area */
p2state.s_sp = printstate.s_stl;/* stack of p2 should sit above */
p2state.s_stl=p2state.s_sp- PAGESIZE/2; /* current stack and be 1/2 a page */
p2state.s_pc = (int)p2; /* p2 starts executing function p2 */
STST(&p3state);
p3state.s_sp = p2state.s_stl;
p3state.s_stl = p3state.s_sp - PAGESIZE/2;
p3state.s_pc = (int)p3;
STST(&p4state);
p4state.s_sp = p3state.s_stl;
p4state.s_stl = p4state.s_sp - PAGESIZE/2;
p4state.s_pc = (int)p4;
STST(&p5state);
p5state.s_sp = p4state.s_stl - PAGESIZE/2; /* because there will */
p5state.s_stl = p5state.s_sp - PAGESIZE/2; /* be two p4's */
p5state.s_pc = (int)p5;
STST(&p6state);
p6state.s_sp = p5state.s_stl;
p6state.s_stl = p6state.s_sp - PAGESIZE/2;
p6state.s_pc = (int)p6;
STST(&p7state);
p7state.s_sp = p6state.s_stl;
p7state.s_stl = p7state.s_sp - PAGESIZE/2;
p7state.s_pc = (int)p7;
STST(&p8rootstate);
p8rootstate.s_sp = p7state.s_stl;
p8rootstate.s_stl = p8rootstate.s_sp - PAGESIZE/2;
p8rootstate.s_pc = (int)p8root;
STST(&child1state);
child1state.s_sp = p8rootstate.s_stl;
child1state.s_stl = child1state.s_sp - PAGESIZE/2;
child1state.s_pc = (int)child1;
STST(&child2state);
child2state.s_sp = child1state.s_stl;
child2state.s_stl = child2state.s_sp - PAGESIZE/2;
child2state.s_pc = (int)child2;
STST(&gchild1state);
gchild1state.s_sp = child2state.s_stl;
gchild1state.s_stl = gchild1state.s_sp - PAGESIZE/2;
gchild1state.s_pc = (int)p8leaf;
STST(&gchild2state);
gchild2state.s_sp = gchild1state.s_stl;
gchild2state.s_stl = gchild2state.s_sp - PAGESIZE/2;
gchild2state.s_pc = (int)p8leaf;
STST(&gchild3state);
gchild3state.s_sp = gchild2state.s_stl;
gchild3state.s_stl = gchild3state.s_sp - PAGESIZE/2;
gchild3state.s_pc = (int)p8leaf;
STST(&gchild4state);
gchild4state.s_sp = gchild3state.s_stl;
gchild4state.s_stl = gchild4state.s_sp - PAGESIZE/2;
gchild4state.s_pc = (int)p8leaf;
/* create process p2 */
p2id=s_service(S_CREAPROC,&p2state); /* start p2 */
print("p2 was started");
r4=(int) p2id;
r3 = 2;
DO_MSGSEND();
DO_MSGRECV();
/* make sure we really blocked */
if (p1p2synch == 0)
print("p1/p2 synchronization bad");
r4 = s_service(S_CREAPROC,&p3state);
DO_MSGSEND();
print("p3 is started");
DO_MSGRECV();
p4id=r4= s_service(S_CREAPROC,&p4state);
DO_MSGSEND(); /* start p4 */
DO_MSGRECV();
DO_MSGSEND();
p5id=r4= s_service(S_CREAPROC,&p5state);
DO_MSGSEND(); /* start p5 */
r4=p5id;
DO_MSGSEND();
r4= s_service(S_CREAPROC,&p6state);
DO_MSGSEND(); /* start p6 */
r4= s_service(S_CREAPROC,&p7state);
DO_MSGSEND(); /* start p7 */
p8id=r4= s_service(S_CREAPROC,&p8rootstate);
DO_MSGSEND();
r4=p5id;
DO_MSGRECV(); /* P(endp5) */
print("p1 knows p5 ended");
r4 = p4id;
DO_MSGRECV(); /* P(blkp4) */
#ifdef NOTDEF
/* now for a more rigorous check of process termination */
for (p8inc=0; p8inc<4; p8inc++) {
r4 = (int)&p8rootstate;
DO_CREATEPROC();
if (r2 == CREATENOGOOD) {
print("error in process termination");
HALT();
}
r4 = p8id;
DO_MSGRECV(); /* P(blkp4) */
}
#endif
print("p1 finishes OK");
r4 = r4/0; /* terminate p1 */
/* should not reach this point, since p1 just got a program trap */
print("p1 still alive after progtrap & no trap vector");
HALT(); /* HALT !!! */
}
/* p2 -- synch and cputime-SYS test process */
p2()
{
register int r4, r3, r2; /* registers available */
int i,j; /* just to waste time */
long now1,now2; /* times of day */
double_t cpu_t1,cpu_t2; /* cpu time used */
void parent;
r4=0;
r3=0;
DO_MSGRECV();
if (r3 != 2)
print("p2 received a wrong message");
parent=r4;
print("p2 starts");
/* test of SYS6 */
STCK(&now1); /* time of day */
cpu_t1.l = s_getcputime(); /* CPU time used */
/* delay for several milliseconds */
for (i=1; i<=1500; i++)
j = i;
cpu_t2.l = s_getcputime(); /* CPU time used */
STCK(&now2); /* time of day */
if (((now2 - now1) >= (cpu_t2.l - cpu_t1.l)) &&
((cpu_t2.l - cpu_t1.l) >= 5000))
print("p2 is OK");
else {
if ((now2 - now1) < (cpu_t2.l - cpu_t1.l))
print ("more cpu time than real time");
if ((cpu_t2.l - cpu_t1.l) < 5000)
print ("not enough cpu time went by");
print("p2 blew it!");
}
p1p2synch = 1; /* p1 will check this */
r4 = parent;
r3=0;
DO_MSGSEND(); /* notify end of job */
s_service(S_TERMPROC,0); /* terminate p2 */
/* just did a SYS2, so should not get to this point */
print("p2 didn't terminate");
HALT(); /* HALT! */
}
/* p3 -- pseudoclock test process */
p3()
{
register int r4, r3, r2; /* registers available */
long time1, time2;
double_t cpu_t1,cpu_t2; /* cpu time used */
int i;
void parent;
r4=0;
r3=0;
DO_MSGRECV();
parent=r4;
time1 = 0;
time2 = 0;
/* loop until we are delayed at least half of clock V interval */
while (time2-time1 < (CLOCKINTERVAL >> 1)) {
STCK(&time1); /* time of day */
s_service(S_WAITCLOCK,0);
STCK(&time2); /* new time of day */
}
print("p3 - WAITCLOCK OK");
/* now let's check to see if we're really charge for CPU
time correctly */
cpu_t1.l = s_getcputime();
for (i=0; i<500; i++)
s_service(S_WAITCLOCK,0);
cpu_t2.l = s_getcputime();
if (cpu_t2.l - cpu_t1.l < 1000)
print("p3 - CPU time incorrectly maintained");
else
print("p3 - CPU time correctly maintained");
r4 = parent;
r3=0;
DO_MSGSEND(); /* notify enf of job */
s_service(S_TERMPROC,0); /* terminate p3 */
/* just did a SYS2, so should not get to this point */
print("p3 didn't terminate");
HALT(); /* HALT */
}
int p4ok=0;
state_t *p42state;
/* p4 -- termination test process */
p4()
{
register int r4, r3, r2; /* registers available */
void parent;
r4=0;
r3=0;
DO_MSGRECV();
parent=r4;
switch (p4inc) {
case 1:
print("first incarnation of p4 starts");
p4inc++;
break;
case 2:
print("second incarnation of p4 starts");
p4ok=1;
break;
}
r4=parent;
r3=0;
DO_MSGSEND();
DO_MSGRECV();
if(p4inc==2)
p4ok=0;
/* start another incarnation of p4 running, and wait for */
/* a V(synp4). the new process will block at the P(blkp4),*/
/* and eventually, the parent p4 will terminate, killing */
/* off both p4's. */
p4state.s_sp -= PAGESIZE/2; /* give another page */
p4state.s_stl -= PAGESIZE/2; /* to the new process */
r4 = s_service(S_CREAPROC,&p4state);
DO_MSGSEND(); /* start a new p4 */
DO_MSGRECV(); /* wait for it */
print("p4 is OK");
r4 = parent;
r3=0;
DO_MSGSEND(); /* endp4 */
r4 = s_service(S_TERMPROC,0); /* terminate p4 */
/* just did a termproc, so should not get to this point */
print("p4 didn't terminate");
HALT(); /* HALT */
}
void p5parent;
/* p5 -- passup test process */
p5()
{
register int r4, r3, r2; /* registers available */
state_t tstate; /* a state with translation on */
sd_t seg_tab[NOSEGS];/* bad segment table */
int x; /* divided by 0 to test prog traps */
int seg_no; /* to set up segment table */
r4=0;
r3=0;
DO_MSGRECV();
p5parent=r4;
print("p5 starts");
/* set up higher level TRAP handlers (new areas) */
STST(&pstat_n);
pstat_n.s_sp = pstat_n.s_stl + PAGESIZE/4;
pstat_n.s_pc = (int)p5prog;
STST(&mstat_n);
mstat_n.s_sp = mstat_n.s_stl + PAGESIZE/4;
mstat_n.s_pc = (int)p5mm;
STST(&sstat_n);
sstat_n.s_sp = sstat_n.s_stl + PAGESIZE/4;
sstat_n.s_pc = (int)p5sys;
r4=s_service(S_CREATHREAD,&pstat_n);
s_service(S_PROGTRAPVEC,r4);
/* specify trap vectors */
r4=s_service(S_CREATHREAD,&mstat_n);
s_service(S_MMTRAPVEC,r4);
r4=s_service(S_CREATHREAD,&sstat_n);
s_service(S_SYSTRAPVEC,r4);
x = x/zero; /* should cause a program trap */
STST(&tstate); /* cause a memory management trap */
tstate.s_sta = seg_tab; /* by loading a state with trans- */
tstate.s_ps1.ps_m = TRUE; /* lation on with all segments */
/* invalid */
for (seg_no=0; seg_no < NOSEGS; seg_no++)
seg_tab[seg_no].sd_p = FALSE;
LDST(&tstate);
}
/* second part of p5 - should be entered in user mode */
p5a()
{
register int r4, r3, r2; /* registers available */
long time1, time2;
double_t cpu_t2; /* cpu time used */
SYS9(); /* should be passed up */
/* the first time through, we are in user mode */
/* and the call should generate a program trap */
cpu_t2.l = s_getcputime();
/* if p4 and offspring are really dead, this will increment blkp4 */
r4 = (int)p5parent;
DO_MSGRECV(); /* P(blkp4) */
/* do some delay to be reasonably sure p4 and its offspring are dead */
time1 = 0;
time2 = 0;
while (time2-time1 < (CLOCKINTERVAL >> 1)) {
STCK(&time1);
s_service(S_WAITCLOCK,0);
STCK(&time2);
}
r4 = p5parent;
r3=0;
DO_MSGSEND(); /* endp4 */
r4 = s_service(S_TERMPROC,0); /* terminate p5 */
/* should have terminated, so should not get to this point */
print("p5 didn't terminate");
HALT(); /* HALT */
}
/* p5's program trap handler */
p5prog()
{
register int r4,r3,r2;
state_t *stat;
while (1)
{
r4 = 0;
DO_MSGRECV();
stat=(state_t *) r3;
switch(stat->s_ps2.p2_pr.pr_typ) {
case ZERODIV:
print("divide by zero");
zero = 1; /* will not happen next time */
break;
case PRIVINSTR:
print("privileged instruction");
/* return to kernel mode */
stat->s_ps1.ps_ku = 1;
stat->s_pc = (int)p5a; /* retry in kernel mode */
break;
default:
print("other program trap");
}
r3=0;
DO_MSGSEND();
}
}
/* p5's memory management trap handler */
p5mm()
{
register int r4,r3,r2;
state_t *stat;
while (1)
{
r4 = 0;
DO_MSGRECV();
stat=(state_t *) r3;
print("memory management trap");
stat->s_ps1.ps_m = 0; /* no more address translation */
stat->s_ps1.ps_ku = 0; /* but put us in user mode when */
/* we return */
stat->s_pc = (int)p5a; /* return to p5a */
r3=0;
DO_MSGSEND();
}
}
/* p5's SYS trap handler */
p5sys()
{
register int r4,r3,r2;
state_t *stat;
while (1)
{
r4 = 0;
DO_MSGRECV();
stat=(state_t *) r3;
switch (stat->s_ps1.ps_ku) {
case 0:
print("high level SYS call from user mode process");
break;
case 1:
print("high level SYS call from kernel mode process");
break;
}
r3=0;
DO_MSGSEND();
}
}
/*p6 -- high level syscall without initializing trap vector*/
p6()
{
print("p6 starts");
SYS9(); /* should cause termination because p6 has no
trap vector */
print("p6 still alive after SYS9() without specifying trap vector");
HALT();
}
/*p7 -- program trap without initializing passup vector*/
p7()
{
int x; /* to test divide by 0 */
print("p7 starts");
x = x/0;
print("p7 still alive after program trap with no trap vector");
HALT();
}
/* p8root -- test of termination of subtree of processes */
/* create a subtree of processes, wait for the leaves to block, signal*/
/* the root process, and then terminate */
p8root()
{
register int r4,r3,r2;
int grandchild;
void parent;
r4=0;
r3=0;
DO_MSGRECV();
parent=r4;
r3=0;
print("p8root starts");
r4=s_service(S_CREAPROC,&child1state);
DO_MSGSEND();
r4=s_service(S_CREAPROC,&child2state);
DO_MSGSEND();
for (grandchild=0; grandchild<NOLEAVES; grandchild++) {
r4=0;
r3=0;
DO_MSGRECV();
}
r4 = parent;
r3=0;
DO_MSGSEND(); /* endp8 */
r4 = s_service(S_TERMPROC,0); /* terminate p8 */
/* just did a termproc, so should not get to this point */
print("p8 didn't terminate");
HALT(); /* HALT */
}
/*child1 & child2 -- create two sub-processes each*/
child1()
{
register int r4,r3,r2;
r4=0;
r3=0;
DO_MSGRECV();
r3=r4; /* set the message as the parent's id */
print("child1 starts");
r4=s_service(S_CREAPROC,&gchild1state);
DO_MSGSEND();
r4=s_service(S_CREAPROC,&gchild2state);
DO_MSGSEND();
r4=0;
r3=0;
DO_MSGRECV();
}
child2()
{
register int r4,r3,r2;
r4=0;
r3=0;
DO_MSGRECV();
r3=r4; /* set the message as the parent's id */
print("child1 starts");
r4=s_service(S_CREAPROC,&gchild3state);
DO_MSGSEND();
r4=s_service(S_CREAPROC,&gchild4state);
DO_MSGSEND();
r4=0;
r3=0;
DO_MSGRECV();
}
/*p8leaf -- code for leaf processes*/
p8leaf()
{
register int r4,r3,r2;
r4=0;
r3=0;
DO_MSGRECV();
print("leaf process starts");
r4=r3; /* put grandparent address into r4 */
DO_MSGSEND();
r4=0;
r3=0;
DO_MSGRECV();
}