perm filename PNTAID.PAL[AID,HE]1 blob sn#375075 filedate 1978-08-24 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00013 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	  FILES, SETNAM
C00004 00003	  Data structures:  Notes, note cells, message buffers
C00008 00004	  GETNOTE, SNDNOTE, SERVER
C00012 00005	  DOGTBUF, DOUSBUF, DORLBUF
C00014 00006	  LINKQUEUE, UNLQUE, SAMEID
C00017 00007	  TREATMESSAGE, GETOFS, DOERR, SNDANS
C00024 00008	  MAKREQ, SNDREQ
C00028 00009	  KTABLE, RTABLE, LOOKUP, RLOOKP, GETOCT, ascie messages
C00037 00010	   TACK, SKIPSP, SKIPOPT
C00039 00011	DODDT:	Service routine
C00041 00012	DOEVAL:
C00046 00013	  Driver for test of communications, ALINIT, ALKILL
C00051 ENDMK
C⊗;
;  FILES, SETNAM


.IFNDF ALAID
    DEBUG == 1
.IFF
    DEBUG == 0
.ENDC

KERNEL == 1
FLOAT == 1

.IFNZ DEBUG

;Set up the necessary mapping for the Zonker
	.INSRT ZONKER.PAL[AL,HE]

.OFFSET -160000		;Put ALAID in the Zonker

    .IF1
        .TITLE  Test of ALAID
        .INSRT ALHEAD.PAL[AL,HE]
        .INSRT K1DEF.PAL[11,SYS]
    .ENDC

. = PATCH
	.BLKW 200	;Patch area

	;If DDT sends us to user I space this will start the Kernel up anyway
. = START
	RESTRT		;EMT gets us into Kernel I space
	RESTRT
	RESTRT		;Kernel INIT entry point

. = INTRP

CODE$ == .		;Interpreter code & data spaces start here
DATA$ == .

    .INSRT ALIO.PAL[AL,HE]
    .INSRT FLOAT.PAL[AL,HE]
	STSW  LBDEBUG,1	;1 => first word of any large block is address of maker.
    .INSRT LARGEB.PAL[AL,HE]
    INSTSZ == 20    ;Size of an interpreter stack
.ENDC


.IFZ DEBUG
CODE
;  Special pseudo-ops

SETNAM:	;Interpreter code
	MOV @IPC(R4),INTNAM(R4)
	BMPIPC		;
	CCC		;Clear Condition Code
	RTS PC		;Done
.ENDC
;  Data structures:  Notes, note cells, message buffers

;  Notes from 10 to 11:
GETBUF == 1	;
USEBUF == 2	;
RELBUF == 3	;

;  Notes from 11 to 10:
BUFALC == 101	;
TAKBUF == 102	;

;  Offsets in notes:
ARG1 == 2
ARG2 == 4

;  Offsets in message buffers:
MESID == 0	;
MESTYP == 2	;
    FROMTEN == 1	;
    FROMELF == 2	;
    REQUEST == 4	;
    ANSWER == 10	;
MESLTH == 4	;
MESBEG == 6	;

;NOTB10  The notebox from 11 to the 10 (byte address) defined in COMTAB
;NOTB11  The notebox from 10 to the 11 (byte address) defined in COMTAB

NOTSIZ == 3		;  In WORDS!
BUFSIZ == 200		;  In WORDS!

DATA
NXTID:	.WORD 0	;Always even
CURNAM:	.WORD 0	;The current ISB for active interpreter.
ALLIVE:	.WORD 0	;AL interpreter alive if non-zero

;  Answer block:
	II == 0
	XX ANSBUF	;Points to a buffer for the return answer
	XX ANPTR	;Initialized to point to the start of the message in ANSBUF
	XX AGBUF	;Start of the request buffer
	XX AGARG	;Start of the arguments in request buffer
	XX AGPTR	;Points to the current place in the request
	XX VALPTR	;The value to be used in the answer
	XX GPHPTR	;The graph node to be used in the answer
	ABKSIZ == II/2	;Size of an answer block, in words.

;  Request block:
	II == 0
	XX REQBUF	;Place where the request will be assembled
	XX REQPTR	;Current end of the assembled request
	XX REQRES	;Where the response is placed
	XX REQEVT	;The event that will signal the return of the response
	XX REQQUE	;The queue node holding our waiting process
	RQBSIZ == II/2	;Size in WORDS.

;  Interlock event
ALDEVT:	.WORD 0

;HN  Halt Switch
HALTSW:	.WORD 0		;HN	  0 = Run  ,   1 = Halt

;  Waitqueue structure:
	II == 0
	XX QNEXT	;Next entry on queue
	XX QPREV	;Previous entry on queue
	   QID == II	;Identifier of this node.  Same field as QEVT.
	XX QEVT		;The event this waiter is expecting
	XX QBUF		;The answer he was waiting for
	QUELTH == II/2	;Length of queue node in WORDS.

WAITQ:	.BLKW QUELTH	;List of processes waiting to hear answers.

CODE
;  GETNOTE, SNDNOTE, SERVER

COMMENT ⊗ Since there is only one server, it is not necessary to put
any interlocks around code in GETNOTE and SNDNOTE.  ⊗

GETNOTE:
COMMENT ⊗ Returns the first note seen in a block pointed to by R0. ⊗
	MOV R2,-(SP)	;Save R2
1$:	TST NOTB11	;Anything there?
	BNE 2$		;Yes
	SLEEP #100	;and sleep a while
	TST ALLIVE	;See if the main interpreter has gone away
	BNE 1$		;if not try again
	DISMIS		;if so we should die
2$:	MOV #NOTSIZ,R0	;
	MOV R0,R2	;R2 ← Count of how many words to transfer
	JSR PC,GTFREE	;R0 ← place to store the note
	MOV #NOTB11,R1	;Transfer the note
3$:	MOV (R1)+,(R0)+
	SOB R2,3$	;Repeat
	SUB #2*NOTSIZ,R0	;Reset R0 to point to front of note.
	CLR NOTB11	;Clear the note, to say we got it.
	MOV (SP)+,R2	;Restore R2
	RTS PC		;Done

SNDNOTE:
COMMENT ⊗ R0 points to a note to send.  Send it and then release the
block. ⊗
	MOV R2,-(SP)	;Sve R2
1$:	TST NOTB10	;Anything there?
	BEQ 2$		;No.
	SLEEP #100	;Yes, so sleep a while
	BR  1$		;And try again
2$:	MOV #NOTSIZ-1,R1	;R1 ← count of words to send
	MOV #NOTB10+2,R2;R2 ← Where to put it.
	TST (R0)+	;Skip the first word; we will put it in last
3$:	MOV (R0)+,(R2)+
	SOB R1,3$	;Repeat
	SUB #2*NOTSIZ,R0	;Reset R0 ← LOC[note]
	MOV (R0),NOTB10	;Activate the note by sending the first word
	JSR PC,RLFREE	;Release the block.
	MOV (SP)+,R2	;Restore R2
	RTS PC		;Done

SERVER:
COMMENT ⊗ Listens for notes from the 10 and acts on them.  Never
returns. Uses R2. ⊗
	JSR  PC,GETNOTE	;R0 ← LOC[note]
	MOV (R0),R1	;R1 ← type of note
	MOV R0,R2 	;R2 ← LOC[note]

	CMP R1,#GETBUF	;GETBUF
	BNE 1$ 
	JSR PC,DOGTBUF	;
	BR 4$		;
1$:
	CMP R1,#USEBUF	;USEBUF
	BNE 2$
	JSR PC,DOUSBUF	;
	BR 4$		;
2$:
	CMP R1,#RELBUF	;RELBUF
	BNE 3$
	JSR PC,DORLBUF	;
	BR 4$		;
3$:
	ALERR SRVMES 	;Illegal code

4$:	MOV R2,R0	;Release the note.
	JSR PC,RLFREE	;
	BR SERVER	;One more river, there's one more river to cross.

DATA
SRVMES:	ASCIE </CAN'T UNDERSTAND NOTE FROM THE 10/>
CODE

;  DOGTBUF, DOUSBUF, DORLBUF

DOGTBUF:
COMMENT ⊗ Called by SERVER.  The 10 wants us to allocate a buffer.
R0 = LOC[note].  The size in bytes is in ARG1(R0).  We should respond
with BUFALC <size> <adr>.  ⊗
	MOV ARG1(R0),R0	;R0 ← size argument
	MOV R0,-(SP)	;Save size argument
	JSR  PC,GTFREE	;Get the buffer out of free storage
	MOV R0,-(SP)	;Save buffer address
	MOV #NOTSIZ,R0	;
	JSR PC,GTFREE	;R0 ← LOC[new note to send]
	MOV #BUFALC,(R0)	;BUFALC
	MOV (SP)+,ARG2(R0) 	;  <adr>
	MOV (SP)+,ARG1(R0) 	;  <size>
	JSR PC,SNDNOTE	;Send the note off. (He will destroy it)
	RTS PC		;Done

DOUSBUF:
COMMENT ⊗ Called by SERVER.  R0 = LOC[note].  The buffer that starts
at address ARG1(R0) is a message.  Look at it, act on it, and then
recycle the message buffer.  ⊗
	MOV ARG1(R0),R0	;R0 ← LOC[message]
	JSR PC,TREATMESSAGE	;Treat it and release it
	RTS PC		;Done

DORLBUF:
COMMENT ⊗ Called by SERVER.  R0 = LOC[note].  The buffer that starts
at ARG1(R0) has been used by the 10, and we may deallocate it now. ⊗
	MOV ARG1(R0),R0	;R0 ← LOC[expended message]
	JSR PC,RLFREE	;
	RTS PC		;Done

;  LINKQUEUE, UNLQUE, SAMEID

LINKQUEUE:
COMMENT ⊗ There is a dummy queue at the start of the chain.  R1
points to the queue header, and R0 is the one we wish to add in.  
Exclusion should be on before this routine is called; it remains
on afterwards.  ⊗
	MOV QNEXT(R1),QNEXT(R0)
	MOV R1,QPREV(R0)
	MOV R0,QNEXT(R1)
	RTS PC

UNLQUE:
COMMENT ⊗ R0 points to a queue node.  It is unlinked from its queue.
R0 is left pointing at the same node.  Exclusion should be on before
this routine is called; it will remain on afterwards.  ⊗
	MOV QPREV(R0),R1	;R1 ← prev(old)
	MOV QNEXT(R0),QNEXT(R1)	;Transfer forward link.
	MOV QNEXT(R0),R1	;R1 ← next(old)
	BEQ 1$			;If any
	MOV QPREV(R0),QPREV(R1)	;Transfer backward link.
1$:	RTS PC			;Done.


SAMEID:
COMMENT ⊗ R0 = header of queue.  R1 = ID to look for.  If there is a
node in the queue with that ID, it is returned in R0.  Otherwise, R0
← 0.  Exclusion should be on before this routine is called; it will
remain on afterwards.  ⊗
1$:	MOV QNEXT(R0),R0	;R0 ← next (real) node in queue
	BEQ 2$			;If any.
	CMP QID(R0),R1		;Match the ID?
	BNE 1$			;No.  Try next one.
	JSR PC,UNLQUE		;R0 ← same node, now unlinked.
2$:	RTS PC			;Done

;  TREATMESSAGE, GETOFS, DOERR, SNDANS

TREATMESSAGE:
COMMENT ⊗ R0 = LOC[buffer from the 10].  Print out its contents and
treat it.  ⊗
	MOV R2,-(SP)	;Save R2
	MOV R0,R2	;R2 ← LOC[buffer]

	;print the message
   .IFZ DEBUG
	EVWAIT CSLEVT	;
   .ENDC
	MOV #CRLFX,R0	;
	JSR PC,TYPSTR	;
	MOV R2,R0	;
	ADD #MESBEG,R0	;R0 ← LOC[start of message itself]
	JSR PC,TYPSTR	;Print it
   .IFZ DEBUG
	EVSIG CSLEVT	;
   .ENDC

	;see what kind of message it is
	MOV R2,R0	;
	MOV MESTYP(R0),R1	;R1 ← MESTYPE;
	BIT #ANSWER,R1	;An  answer?
	BEQ 2$		;No
	

	;got a response.  See if anyone is waiting to hear it.
	MOV MESID(R0),R1;R1 ← MESID
	EVWAIT ALDEVT	;Enter critical section
	MOV #WAITQ,R0	;R0 ← head of wait.
	JSR PC,SAMEID	;R0 ← queue node waiting for this MESID.
	EVSIG ALDEVT	;End of critical section
	TST R0		;Was there a waiting process?
	BNE 1$		;Yes.
	ALERR TRTMMS	;None found.  A bug!
1$:	MOV R2,QBUF(R0)	;Give him his result.
	EVSIG QEVT(R0)	;Give him his signal
	BR 3$		;Prepare to leave
	
	;got a question.  Get someone to look at it.
2$:	JSR PC,RLOOKP	;Start up a process to fulfill the request and
			;delete the message

3$:	MOV (SP)+,R2	;Restore R2
	RTS PC		;

DATA
TRTMMS:	ASCIE </GOT UNEXPECTED ANSWER FROM THE 10./>
CODE

GETOFS:	
COMMENT ⊗ R2 = LOC[answer block].  We want to see (OFFSET n).  If we
do, we put LOC[graph node for n] in GPHPTR(R2); otherwise R0 ← 0.  R2 is
still LOC[answer block], but ARGPTR is properly updated. ⊗
	MOV AGARG(R2),R0;R0 ← LOC[argument string]
	CMPB (R0)+,#'(	;A left paren?
	BNE 1$		;No.  
	JSR PC,LOOKUP	;R0 ← next thing on arg, R1 ← OFSCOD, we hope.
	CMP R1,#OFSCOD	;Was it offset?
	BNE 1$		;No.
	JSR PC,GETOCT	;R0 ← after the arg, R1 ← octal number found.
	MOV R0,AGPTR(R2);Save arg. ptr
	MOV R1,R0	;R0 ← integer offset
	MOV CURNAM,R4	;R4 ← LOC[ISB of active interpreter]
	JSR PC,GETARG	;R0 ← LOC[environment entry for variable]
	MOV R0,GPHPTR(R2)
	BEQ 1$		;If anyone home.  Else will return failure.
	MOV AGPTR(R2),R0;
	JSR PC,SKIPSP	;Skip spaces.
	MOV #'),R1	;
	JSR PC,SKIPOP	;Skip the ), if it is there.
	MOV R0,AGPTR(R2);
	RTS PC		;
1$:	CLR R0		;Failure return
	RTS PC		;

DOERR:	
COMMENT ⊗ There has been an error in parsing some command. R2 =
LOC[answer block].  We will say "ERROR (message)".  R2 will be left
with ANPTR fixed up.  ⊗
	MOV ANPTR(R2),R0;R0 ← answer pointer
	MOV #ERRMES,R1	;
	JSR PC,TACK	;Tack on "ERROR "
	MOV #LPAREN,R1	;
	JSR PC,TACK	;Tack on " ( "
	MOV AGBUF(R2),R1;
	ADD #MESBEG,R1	;
	JSR PC,TACK	;Tack on the original message
	MOV #RPAREN,R1	;
	JSR PC,TACK	;Tack on " ) "
	MOV R0,ANPTR(R2);
	JMP SNDANS	;He will never return.

SNDANS:
COMMENT ⊗ R2 = LOC[answer block]. ANPTR(R2) = end of the message.
ANSBUF(R2) = front of the message.  Compute the message length, send
the message out, reclaim the answer block, including the AGBUF, and
then reclaim the interpreter stack, the PDB of this process and
dismiss. ⊗

	;compute MESLTH
	MOV ANPTR(R2),R1;R1 ← ans. ptr
	MOV ANSBUF(R2),R0	;R0 ← LOC[answer buffer]
	SUB R0,R1	;R1 ← length in bytes of message
	ASR R1		;in words
	BCC 1$		;HN OK if even numbers of bytes
	INC R1		;HN otherwise add 1 to the length in words
	INC ANPTR(R2)	;HN reflct on the ans. ptr
	MOVB #' ,@ANPTR(R2)	;HN and put a blank byte at the end of the mess.
1$:	MOV R1,MESLTH(R0); MESLTH


	;send the result back.  R0 = LOC[message]
	MOV #NOTSIZ,R0	;
	JSR PC,GTFREE	;R0 ← LOC[new note to send]
	MOV #TAKBUF,(R0);TAKBUF
	MOV (SP),R1	;R1 ← LOC[answer block]
	MOV ANSBUF(R2),ARG1(R0)	;  <adr>
	JSR PC,SNDNOTE	;Send the note off. (He will destroy it)

	;reclaim answer block
	MOV R2,R0	;Reclaim the argument message buffer
	MOV AGBUF(R0),R0;
	JSR PC,RLFREE	;
	MOV R2,R0	;Reclaim the answer block itself
	JSR PC,RLFREE	;

	;reclaim interpreter stack
	MOV R3,R0
	SUB #2*INSTSZ,R0
	JSR PC,RLFREE

	;reclaim Processor Desriptor Block
	MOV R5,R0	;
	JSR PC,RLFREE	;
	DISMIS		;Gone!

;  MAKREQ, SNDREQ

MAKREQ:	
COMMENT ⊗ Returns in R3 a pointer to a brand new request block, with
REQBUF and REQPTR initialized to a new area for assembling a request.
The REQBUF is initialized with MESTYP.  ⊗
	MOV #RQBSIZ,R0	;Get a request block
	JSR PC,GTFREE	;
	MOV R0,R3	;R3 ← LOC[request block]
	MOV #BUFSIZ,R0	;
	JSR PC,GTFREE	;R0 ← LOC[request buffer]
	MOV #FROMELF+REQUEST,MESTYP(R0)
	MOV R0,REQBUF(R3)
	ADD #MESBEG,R0	;
	MOV R0,REQPTR(R3)
	RTS PC		;

SNDREQ:
COMMENT ⊗ R3 = LOC[request block]. REQPTR(R3) = end of the message.
REQBUF(R3) = front of the message.  Compute the message length, send
the message out, wait for a reply, and then put the response in
REQRES(R3).  R3 is left pointing to the request block.  ⊗

	;compute MESLTH
	MOV REQPTR(R3),R1	;R1 ← ans. ptr
	MOV REQBUF(R3),R0	;R0 ← LOC[request buffer]
	SUB R0,R1	;R1 ← length in bytes of message
	ASR R1		;in words
	MOV R1,MESLTH(R0); MESLTH

	MOV REQBUF(R3),R0	;R0 ← LOC[message buffer]
	EVMAK		;Get an event that will signal the response to the request.
 	MOV (SP),MESID(R0)	;That will be the MESID.
	MOV (SP)+,REQEVT(R3)	;REQEVT

	MOV #QUELTH,R0	;Enqueue ourselves for the response
	JSR PC,GTFREE	;R0 ← LOC[queue node]
	MOV R0,REQQUE(R3)	;REQQUE
	MOV REQEVT(R3),QEVT(R0)	;QEVT
	EVWAIT ALDEVT	;Enter critical region
	MOV #WAITQ,R1	;
	JSR PC,LINKQUEUE;
	EVSIG ALDEVT	;Leave critical region

	;send the request out.  R0 = LOC[message]
	MOV #NOTSIZ,R0	;
	JSR PC,GTFREE	;R0 ← LOC[new note to send]
	MOV #TAKBUF,(R0);TAKBUF
	MOV REQBUF(R3),ARG1(R0)	;  <adr>
	JSR PC,SNDNOTE	;Send the note off. (He will destroy it)
	EVWAIT REQEVT(R3)	;Wait for the event to happen

        COMMENT ⊗ When the answer comes, the server will unlink the
        queue for us.  We must destroy the event and reclaim the
        queue node ourselves.  ⊗

	;the response has come, and the answer is in QBUF(REQQUE(R3))
	EVKIL REQEVT(R3)	;
	MOV REQQUE(R3),R0	;
	MOV QBUF(R0),REQRES(R3)	;REQRES
	JSR PC,RLFREE		;Release the queue node
	RTS PC		;
;  KTABLE, RTABLE, LOOKUP, RLOOKP, GETOCT, ascie messages

DATA
LPAREN:	.ASCIZ / ( /
RPAREN:	.ASCIZ / ) /
DONEMES:.ASCIZ /DONE /
ERRMES:	.ASCIZ /ERROR /
YTHMES:	.ASCIZ /YOUTHERE /
HLTMSG:	.ASCIZ /ALL ACTIVE INTERPRETERS HALTED/	        ;HN
	.EVEN

	.MACRO KWORD KNAME, KINFO
	II == .
	ASCIE /KNAME/
	. = II + 6	;Truncate to 6 characters
	KINFO		;Either code for this keyword, or service routine address
	.ENDM
	
OFSCOD == 1
SCACOD == 2
VCTCOD == 3
TRACOD == 4
PLCCOD == 5

KTABLE:	;List of keywords.
	KWORD <OFFSET>, OFSCOD
	KWORD <SCALAR>, SCACOD
	KWORD <VECTOR>, VCTCOD
	KWORD <TRANS >, TRACOD
	KWORD <PLACE >, PLCCOD
KTEND:	.WORD 0

RTABLE:	;List of requests.
	KWORD <EVAL  >, DOEVAL		;HN
	KWORD <DDT   >, DODDT
RTEND:	.WORD 0
CODE

COMMENT ⊗ R0 = LOC[string]. Find which keyword heads the string,
using a disgusting linear search, and return R1 ← 0 if no keyword
found, otherwise R1 ← code for that keyword.  R0 ← next entry on
string. ⊗

LOOKUP:	
	MOV R2,-(SP)	;Save R2
	MOV #KTABLE,R1	;R1 ← LOC[current try in KTABLE]
1$:	MOV #6,R2	;R2 ← count of how many characters to match.
2$:	CMPB (R0)+,(R1)+;Match the next letter?
	BEQ 4$		;Yes
3$:	ADD R2,R0	;
	SUB #7,R0	;R0 ← start of given string.
	ADD R2,R1	;R1 ← end of test string
	TSTB (R1)+	;R1 ← start of next test string
	CMP R1,#KTEND	;Off the end?
	BLO 1$		;No.
	BR 6$		;Yes.
4$:	SOB R2,2$	;Try the next, if any.
	;found a match.  R1 = LOC[KINFO]
	JSR PC,SKIPSP 	;Skip spaces (does not hurt R1)
	MOV (R1),R1	;R1 ← KINFO
5$:	MOV (SP)+,R2	;Restore R2
	RTS PC		;Done
6$:	CLR R1		;Did not find anything
	BR 5$		;

COMMENT ⊗ R0 = LOC[message buffer request]. Find which request word
heads the string, using a disgusting linear search, and start a
process to handle the request.  He will see to the deletion of the
message buffer.  ⊗

RLOOKP:	
	MOV R2,-(SP)	;Save R2
	MOV R0,-(SP)	;Save LOC[message buffer request]
	ADD #MESBEG,R0	;R0 ← LOC[request string]
	MOV #RTABLE,R1	;R1 ← LOC[current try in KTABLE]
1$:	MOV #6,R2	;R2 ← count of how many characters to match.
2$:	CMPB (R0)+,(R1)+;Match the next letter?
	BEQ 4$		;Yes
3$:	ADD R2,R0	;
	SUB #7,R0	;R0 ← start of given string.
	ADD R2,R1	;R1 ← end of test string
	TSTB (R1)+	;R1 ← start of next test string
	CMP R1,#RTEND	;Off the end?
	BLO 1$		;No.
	MOV #DOERR,R2	;So what we will do is handle the error.
	BR 5$
4$:	SOB R2,2$	;Try the next, if any.
	;found a match.  R1 = LOC[KINFO]
	MOV (R1),R2	;R2 ← KINFO = address of service routine
5$:	JSR PC,SKIPSP 	;Skip spaces
	MOV R0,-(SP)	;Save AGPTR

	;build the answer block
	MOV #BUFSIZ,R0	
	JSR PC,GTFREE	;R0 ← LOC[answer buffer]
	MOV 2(SP),R1	;R1 ← AGBUF
	MOV MESID(R1),MESID(R0)	;Transfer the MESID to answer from request.
	MOV #FROMELF+ANSWER,MESTYP(R0)	;MESTYP
	MOV R0,-(SP)	;Save ANSBUF
	MOV #ABKSIZ,R0	;Get an answer block
	JSR PC,GTFREE	;R0 ← LOC[answer block]
	MOV (SP)+,R1	;R1 ← ANSBUF
	MOV R1,ANSBUF(R0)
	ADD #MESBEG,R1	;
	MOV R1,ANPTR(R0);
	MOV (SP),AGARG(R0)
	MOV (SP)+,AGPTR(R0)
	MOV (SP)+,AGBUF(R0)
	MOV R0,-(SP)	;Save LOC[answer block]

	;set up a new process with R2 ← LOC[answer block] to fulfil request.
	INSTSZ == 20	;Size of an interpreter stack
	MOV #INSTSZ,R0	;R3 stack space
	JSR PC,GTFREE	;
	ADD #2*INSTSZ,R0	;to end of space
	MOV R0,-(SP)	;Save stack space
	MOV #210,R0	;Room for process descriptor
	JSR PC,GTFREE	;R0 ← LOC[new process descriptor]
	MOV #UFPUSE+UGRSAV+4,PDBSTA(R0);Use floating point, use saved registers.
	MOV R0,USKMIN(R0)	;Set up min pointer for SP
	ADD #UFEC+36,USKMIN(R0)
	MOV R0,USKMAX(R0)	;Set up max pointer for SP
	ADD #420,USKMAX(R0)
	MOV #144100,UPSW(R0)	;Set up psw
	MOV (SP)+,PDBR3(R0)	;Store away the R3 stack pointer.
	MOV (SP)+,PDBR2(R0)	;Store away the R2 = LOC[answer block]
	MOV CURNAM,PDBR4(R0)	;Start out on the current ISB
	MOV R0,PDBR5(R0)	;Store away the R5 = PDB address.
	MOV #USRIM,UIMAP(R0)	;Map instruction space
	FORK R0,R2,#USRDM	;Cause the new process to be started

6$:	MOV (SP)+,R2	;Restore R2
	RTS PC		;Done

GETOCT:
COMMENT ⊗ R0 = string pointer.  Finds an octal number, skipping
spaces to do so, and places its value in R1.  Leaves R0 at end of
spaces following the string. ⊗
	MOV R2,-(SP)	;Save R2
	CLR R1		;R1 is the eventual result
	JSR PC,SKIPSP	;Skip leading spaces
1$:	MOVB (R0)+,R2	;R2 ← Character
	CMP #'0,R2	;Too small?
	BGT 2$		;yes
	CMP #'7,R2	;Too large?
	BGE 3$		;no
2$:	TSTB -(R0)	;Move back one place
	JSR PC,SKIPSP	;skip trailing spaces
	MOV (SP)+,R2	;Restore R2
	RTS PC		;Done
3$:	MOV R2,-(SP)	;Save the character
	ASH #3,R1	;Compute new result
	BIC #60,(SP)	;
	ADD (SP)+,R1	;
	BR  1$		;And repeat
   TACK, SKIPSP, SKIPOPT

TACK:
COMMENT ⊗ R1 = LOC[ascie string to tack on], R0 = LOC[where to put
it].  Returns R0 ← next location available in destination string.  ⊗
	MOVB (R1)+,(R0)+;Copy a byte
	BNE TACK	;Repeat while necessary
	DEC R0		;Go back past the null
	RTS PC		;Done

SKIPSP:
COMMENT ⊗ R0 = LOC[string].  Skip past any spaces, returning R0 ← LOC[next
non-space element of the string.  Leaves R1 unchanged.  ⊗
	CMPB (R0)+,#' 	;
	BEQ SKIPSP	;
	DEC R0		;Go back past the non-space
	RTS PC		;

SKIPOPT:
COMMENT ⊗ R0 = LOC[string].  Skip past the character in R1, if it is
the next character, and in any case, skip past any spaces.  ⊗
	CMPB (R0),R1	;The optional character?
	BNE 1$		;No
	TSTB (R0)+	;Yes.  Skip it.
1$:	JMP SKIPSP	;Skip over spaces, and let SKIPSP return.

DODDT:	;Service routine
COMMENT ⊗ Jump to DDT, so that ↑P will proceed. The answer is of the
form "DONE", unless something goes wrong, in which case the answer
will be "ERROR (DDT arg)".  ⊗

	ALERR DODDTMES	;Here we go to DDT.

	;test stuff.  Current test:  Try the turn-around question YOUTHERE
	;at the ten.
	MOV R3,-(SP)	;Save R3
	JSR PC,MAKREQ	;R3 ← request block.
	MOV REQPTR(R3),R0	;R0 ← REQPTR
	MOV #YTHMES,R1	;Tack on "YOUTHERE"
	JSR PC,TACK	;
	MOV R0,REQPTR(R3)
	JSR PC,SNDREQ	;Send the request on its way, and eventually come back
			;with response in the REQRES(R3)
	MOV REQRES(R3),R0	;
	ADD #MESBEG,R0	;Print out the response
	JSR PC,TYPSTR	;
	MOV REQRES(R3),R0	;Reclaim the response buffer
	JSR PC,RLFREE	;
	MOV R3,R0	;Reclaim request block
	JSR PC,RLFREE	;
	MOV (SP)+,R3	;Restore R3

	;prepare the answer. Note that TACK and TACKVAL take a string pointer
	; in R0 and leave it right afterwords.
	MOV ANPTR(R2),R0	;R0 ← answer pointer
	MOV #DONEMES,R1	;
	JSR PC,TACK	;Tack on "DONE "

	;ANPTR(R2) = end of the message.  ANSBUF(R2) = front of the message
	JMP SNDANS	;Send it winging on its way. Reclaim the answer block.
			;Reclaim the PDB.  Dismiss.

DATA
DODDTMES:  ASCIE </SWITCHING TO DDT/>
CODE
DOEVAL:
Comment ⊗ This service routine causes execution (or evaluation) of a list of pcodes
	sent to the 11 (us !) ⊗

	MOV CURNAM,R4	
	MOV R2,-(SP)	;save the answer block pointer
	MOV AGPTR(R2),R0	; address of the first word of the pcode list
	ADD #2,R0	;first word has the number of variables for the environment
	EVMAK		;-(SP)← event to signal when pcode list processed
	MOV (SP),R1	;prepare for spawn
	JSR PC,SPAWN	;R0 ← PDB[new interpreter process].
	MOV AGPTR(R2),R1	; address of the first word of the pcode list
	MOV R0,R2	;R2 ← new process control block 

	;Set up the new environment
	MOV (R1),R0	;Get number of variables used in this environment
	ASL R0		;Need 2 words/variable
	ADD #ENVSIZ,R0	;Add in header size
	JSR PC,GTFREE	;Allocate from large blocks
	MOV R0,LVARS(R0)
	ADD #2*ENVSIZ,LVARS(R0)	;Initialize where the first free entry should go

	MOV PDBR4(R2),CURNAM	;Set current interpreter to this one.
	MOV ENV(R4),SLINK(R0)	;Not necessary to set up OLEV, etc.
	MOV PDBR4(R2),R1
	MOV R0,ENV(R1)
	INC LEV(R1)

	FORK R2,#INTERP,#USRDM	   ;Cause the new process to be started.
	POP <R1>
	EVWAIT R1	;wait until the pcode list is processed
;	EVKIL R1	;event is not needed any more
	MOV (SP)+,R2	;now restore R2 (the answer-block pointer)
	MOV ANPTR(R2),R0	;R0 ← answer pointer
       	MOV #DONEMES,R1	;HN
	JSR PC,TACK	;HN
	MOV R0,ANPTR(R2);HN
	JMP SNDANS	;HN

;  Driver for test of communications, ALINIT, ALKILL

.IFNZ DEBUG

temp == %OFFSE	;Save the current offset
.OFFSET 0	;We want to use real physical addresses here for the kernel

	PUTLOC JOBDAT, MAINBL
	PUTLOC JOBSA, START
	PUTLOC JOBDM, USRDM

.OFFSET temp	;Restore Offset

DATA
MAINBL:	PDBLK 1,200,S	;Makes a process descriptor for main process
CODE
START:	JSR PC,IOINIT	;
	JSR PC,FRINIT	;
	CLR NOTB10
	CLR NOTB11
	EVMAK		;Create and signal once the AL interlock event.
	MOV (SP),ALDEVT	;
	EVSIG		;
	CLR WAITQ+QNEXT	;
	JMP SERVER	;No, he'll never return


GETARG:	MOV R0,FAKE	;
	MOV #FAKE1,R0	;
	RTS PC

DATA
FAKE:	.BLKW 2	;Long enough for floating
FAKE1:	FAKE
CODE

ROUTINE GETVAL,<GTV.ARG>
	MOV GTV.ARG(RF),R0
	RTS PC

ROUTINE CHANGE,<CHG.ND,CHG.VN>
	RTS PC

GETSCA:	MOV #FAKE,R0	;
	MOV R0,-(R3)	;
	RTS PC		;

GETTRN:	MOV #60,R0	;
	JSR PC,GTFREE	;
	MOV R0,-(R3)	;

TACKVAL:
COMMENT ⊗ R1 = LOC[value], R0 ← where to put it ⊗
	MOV #FAKEMES,R1	;
	JMP TACK	;
DATA
FAKEMES:ASCIE </999.999/>
CODE

.ENDC

DATA
ALPDB:	PDBLK 2,150,S	;Makes a process descriptor for server
CODE

ALINIT:
COMMENT ⊗ Start up one copy of the server as a separate job. ⊗
	EVMAK			;Create and signal once the AL interlock event.
	MOV (SP),ALDEVT
	EVSIG
	CLR WAITQ+QNEXT
	CLR NOTB11
	CLR NOTB10
	MOV #1,ALLIVE		;Indicate that the AL interpreter is alive
	MOV #20,R0		;R3 stack space
	JSR PC,GTFREE
	ADD #40,R0		;to end of space
	MOV #ALPDB,R1		;R1 ← LOC[ALAID process descriptor]
	BIS #UGRSAV+USKSAV,PDBSTA(R1)	;Use saved registers.
	MOV R0,PDBR3(R1)	;Store away the R3 stack pointer.
	MOV USKMAX(R1),USKP(R1)	;Make sure we have a good stack pointer
	SCHEDU R1,#SERVER,#USRDM,#2 ;Cause the new process to be started, suspended
	RTS PC

ALKILL:	CLR ALLIVE		;Indicate that the AL interpreter is dead
	RTS PC