; Copyright (c) 1981 Harvard-Radcliffe Student Timesharing System ; Science Center, Harvard University .title system code .page ; the major system code file for lisp ; forrest howard. .if eq,multiseg .psect startc con,shr .if ne,nilas0 .psect nil shr .endc .if ne,onepage .psect onepage con,prv .endc .psect uswdda con,prv .psect usport con,prv .psect usbyda con,prv .psect shbydat con,shr .psect shrwddat con,shr .psect shrcode con,shr .psect dsubr con,shr .psect usbyda con,prv .psect ddtpr con,prv .psect datom con,prv .psect initcd con,prv ;initialization stuff--goes away .psect errorm con,prv ;error mssage psect--goes away .if ne,xfer .psect bcdmap con,prv ;also goes away .endc .iff .psect nil con,prv,dat .if ne,prvispace .psect startc con,prv,ins .psect shrcode con,prv,ins .iff .psect startc con,shr,ins .psect shrcode con,shr,ins .endc .psect shrwddat con,prv,dat .psect shbydat con,prv,dat .psect uswdda con,prv,dat .psect usport con,prv,dat .psect usbyda con,prv,dat .psect dsubr con,prv,dat .psect ddtpr con,prv,dat .psect datom con,prv,dat .psect initcd con,shr,ins ;goes away .psect errorm con,prv,dat ;ditto .if ne,xfer .psect bcdmap con,prv,dat ;as does this... .endc .endc .psect startc con frstcl: jmp @where ;start things off .mcall $exit,$indir,$read,$write,$open,$close,$create,$switch,$sig .if ne,xfer .mcall $fork,$exec,$kill,$ptrace,$wait .endc .psect shrcode ; ;register restore routines-- the following routines are also ;snags for various amount of registers on the cstack r4rres: mov sp,j3 tst (j3)+ mov (j3)+,b mov (j3)+,j1 mov (j3)+,j2 mov (j3),j3 add #12,sp ret r3rres: mov sp,j3 tst (j3)+ mov (j3)+,j1 mov (j3)+,j2 mov (j3),j3 add #10,sp ret r2rres: mov 2(sp),j2 mov 4(sp),j3 add #6,sp ret r1rres: mov 2(sp),j3 add #4,sp ret .rsect shrcode con progsnag: halt brksna: halt .if ne,xfer .globl eexit1 eexit1: halt .endc eexit: .if df,noeval tstb tracfl beq 1$ npush #anil propush a call printr outstr linefeed .if df,width clrb poport+1 .endc unpropop a 1$: .endc tst (sp)+ ;flush func mov ltop,np ;;fix up name stack and pop ltop tst (sp)+ ;and c stack for real return ret ;and take it .rsect shrcode con ;evalquote makes lisp appear as an evalquote ;machine .if eq,xfer ;evalquote needs a little more smarts ;in xfer case evalquote: call readr ;get token cmp a,#atme ;if e, then call readr directly beq readr propush a call readr jmpifnil a,4$ ;see if rest of list nil cmptype a,j1,#ndtpr ;if not nil,better be bne 2$ propush a ;save to protect it mov a,j2 ;and get it in right place 5$: jmpifnil j2,3$ car j2,a ;get first thing needing quoting consbnil ;make (foo) mov #aquote,a ;get quote and consa ;make (quote foo) mov a,(j2)+ ;smash old list, and get ready for mov (j2),j2 ; cdr in this instruction br 5$ ;and loop 4$: mov a,b ;set up for nil case br 6$ 3$: unpropop b 6$: unpropop a consa ret 2$: error .iff evalquote: call readr ;get a form cmp a,#atme ;is it the magic e bne 1$ ;no, skip around call readr ret ;this is necessary so we know we've seen e 1$: propush a ;stick a away call readr ;and get form jmpifnil a,10$ ;if nil, then just cons cmptype a,j1,#ndtpr ;in not nil, gotta be dtpr bne 40$ ;so scream propush a ;we have to copy here, so save it call 20$ ;this gets quoted list in a tst (sp)+ ;flush protecting form 10$: mov a,b ;get form unpropop a ;and future car consa ;melt them together ret ;and quit 20$: cmptype a,j1,#ndtpr ;end of list??? bne 23$ mov (a)+,-(sp) ;push car (already protected) and get cdrp mov (a),a ;change cdrp to cdr call 20$ ;do rest pop j1 ;old form to j1 propush a ;gotta protect this mov j1,a ;get form consbnil ;make (form) mov #aquote,a ;move in quote consa ;and make 'form unprop b ;get back rest consa ;and cons the world 23$: ret ;and return 40$: error .endc .rsect shrcode con ;readr takes port on top of name stack ;and returns form in a ;ratomr clobers j1-j4,therefore so does readr readr: clr rbktf ;set flag for super right paren(]) call 29$ ;call routine mov b,a ;leave result in right place ret ;and go home 29$: call xratm2 ;xratm1 knows about ]'s becomming )^n 30$: cmp a,#lpara ;is (? beq 1$ ;yes, then... cmp a,#rpara ;is ) beq 32$ ;yes, then error cmp a,#lbkta ;is [ beq 31$ ;if so, go same a ( via a little extra code cmp a,#perda ;is period beq 32$ ;yes, error cmp a,#asquote ;is it "'"??? bne 40$ ;if not, skip around call readr ;do recursive call consbnil ;make (form) mov #aquote,a ;get quote atom consa ;make (quote form) 40$: mov a,b ;and return it in right place ret 1$: call xratm2 ;now get rest of list cmp a,#rpara ;is the list ()? bne 2$ ;if not br ahead loadnil b ;yes, return nil ret 2$: call 30$ ;read a list as a car of the list we're on propush b ;save it call xratm1 ;get a token cmp a,#rpara ;are we done? beq 3$ ;yes, goto 3 cmp a,#perda ;is this explicit dotted pair beq 4$ ;yes, goto 4 call 2$ ;now get rest of list unpropop a consb ;cons car and rest of list together ret ;send home a good list 3$: unpropop a ;here if we see ) consbnil ;provide last nil ret ;and go home 4$: call 29$ ;we read period,need new token unpropop a consb propush a ;save it (for sake of b and xfer) call xratm1 ;next thing better be ) cmp a,#rpara bne 32$ ;if not, error unpropop a ;and get form back ret 31$: call 1$ ;this takes care of [-pretend clr rbktf ;but flush )^n when back to proper level ret ;and return 32$: error ;read list error ;register save routines...... ;called by macros save1,save2,save3,save4 xsave1: mov #r1rres,-(sp) ;leave pointer to reg save routine mov 2(sp),-(sp) ;and get address to return to mov j3,4(sp) ;restore the register ret ;and go home xsave2: mov (sp),-(sp) ;fill with ok thing for second mov #r2rres,-(sp) ;and put on snag mov j2,2(sp) mov 4(sp),j2 mov j3,4(sp) jmp (j2) ;and go home xsave3: mov (sp),-(sp) mov (sp),-(sp) ;make 3 register slots mov #r3rres,-(sp) ;stack snag mov j3,6(sp) mov sp,j3 tst (j3)+ mov j1,(j3)+ mov (j3),j1 mov j2,(j3) jmp @j1 ;and simulate return xsave4: mov (sp),-(sp) mov (sp),-(sp) mov (sp),-(sp) mov #r4rress,-(sp) mov j3,10(sp) mov sp,j3 tst (j3)+ mov b,(j3)+ mov j1,(j3)+ mov (j3),j1 mov j2,(j3) jmp @j1 ;evalb takes a list for subr or lambda ;and puts it in name stack elements evalb: jmpifnil b,29$ ;if nothing to stack, go home mov (b)+,a ;if some work to do, then get form mov @b,-(sp) ;and save rest (is protected by fun block) call eval npush a ;put it on stack pop b ;get others br evalb ;and do it again 29$: 1$: ret ;stkb takes a list of atom names in b, and ;pairs them with the evaled name stack entrys...uses j3,j4 stkb: mov np,j3 ;get np copy mov ltop,np ;and new np sub np,j3 ;figure out args blos 1$ ;no args..... asr j3 ;stack entrys to words asr j3 3$: jmpifnil b,2$ ;any more args??? tst (np)+ ;yes, so... mov (b)+,(np)+ ;push name and kick mov (b),b ;and get cdr sob j3,3$ ;and loop 1$: jmpifnil b,2$ ;any that we supply args for npush #anil mov (b)+,-2(np) ;push name mov (b),b br 1$ 2$: ret ;lookup uses j3,np, and finds current binding of ;thing in a ;leaves a so that cdr(a)=desired binding lookup: push np ;save np mov np,j3 ;get np and copy sub npbottom,j3 ;figure out i length of name stack blos 1$ ;if name stack is empty, go home asr j3 asr j3 ;make words 2$: cmp -(np),a ;is this ns entry our choice? beq 3$ ;if yes, then go tst -(np) ;get ready for next try sob j3,2$ ;and if anything left, do it again 1$: pop np ret ;return atom 3$: mov np,a br 1$ ;return a pointer to the ns cell ;chas and chanl: mov #4,a ;nlambda's always have one br chas1 ;now go to the common code chas: movb 1(a),a ;a has pointer to header of bcd bic #177700,a ;clear bits asl a ;and get in right form chas1: add ltop,a ;get where ns should be 1$: cmp np,a ;is it? blt 2$ ;it bigger or equal--that's ok mov a,np ;just return the right thing rts %7 ;and go home 2$: npush #anil ;otherwise push nil br 1$ ;and see if that was enough ;stuff to output terminal forms portout:mov #sportsym,b jmp putstr bcdout: mov #sbcdout,b jmp putstr atmout: add #6,a ;point to string mov a,j3 ;move to j3 movb (j3)+,b ;get char beq 40$ ;null atom print as "" cmpb b, #'- ;minus sign?? bne 10$ ;no, go to 10$ movb (j3)+,b ;next char beq 20$ ; atom with one minus is atom cmpb b, #'- ;this one - also??? beq 20$ ;20$ is where we scan string 10$: cmpb ctable(b),#vnum ;numeric otherwise beq 40$ ;yes, so "" out 20$: mov a,j3 ;get fresh atom name 21$: movb (j3)+,b ;get char beq 50$ ;string is clean if we get here bitb #1,ctable(b) ;ok...check for funny out beq 21$ ;not funny, loop 40$: outstr dq ;must be funny mov a,b ;now name call putstr ;dump name mov #dq,b ; and last '"' br 51$ 50$: mov a,b ;get string 51$: jmp putstr xpatom: mov a,b ;print atom without " add #6,b jmp putstr .if eq,fpsim .rsect shrcode con ;numstr takes number in a, and leaves ptr to string in b ;uses ac0,ac1,ac2,ac3,ac4 numstr: mov #,b ;pointer to result left in b mov #2$,-(sp) ;set normal return clrb -(b) ;input in a numga0 ;leaves binary number(in floating formi ; in ac0 cfcc ;copy codes absd ac0 bge 10$ ;fix up if neg. mov #3$,(sp) ;and set negative return 10$: seti modf ac5,ac0 ;mul by .1, int part in ac1 stf ac0,ac2 ;fract in ac0 addf #37114,ac2 ;fudge good enough for bell labs..... modf ac4,ac2 ;mult fract by 10 stcfi ac3,a ;convert int part to integer add #60,a ;convert it to char movb a,-(b) ;and store it ldf ac1,ac0 ;sets float cc vs stf which sets ccs cfcc ;are we done?(i.e. ac1=0) bne 10$ ;no,loop ret 3$: movb #'-,-(b) 2$: setl ;convert back to long integer mode retnil ;and clean up a ;reminder ;ten=41040,0,0,0 ;tenth=037314,146314,146314,146315 .iff .rsect shrcode ;numstr here converts a int to string by using the idiv routine ;only register a+b killed..... numstr: save3 ;save j1-j3 mov #strbuf+30,j3 mov #3$,-(sp) ;use to return with correct sign numga clrb -(j3) tst a bge 1$ com a com b mov #2$,(sp) add #1,b adc a 1$: clr j1 mov #10.,j2 call idiv add #'0,j2 movb j2,-(j3) tst a bne 1$ tst b bne 1$ ret 2$: movb #'-,-(j3) 3$: mov j3,b loadnil a saveret .endc numout: mov #putstr,-(sp) br numstr ;call routines .rsect shrcode con ;sratm1 converts ] to )^n .enabl lsb xratm1: mov rbktf,a bne 2$ xratm2: call ratomr cmp a,#rbkta beq 1$ ret 1$: mov #rpara,a mov a,rbktf 2$: ret .dsabl lsb ;consa,consb,consbnil macros call these routines ;these protect a and b in case of garbage collection .rsect shrcode con xconsa: .if ne,nilas0 tst fdtpr .iff cmp fdtpr,#anil .endc bne 1$ call xconscom 1$: push a mov fdtpr,a car a,fdtpr pop (a) mov b,2(a) ret loadnil b xconsb: .if ne,nilas0 tst fdtpr .iff cmp fdtpr,#anil .endc bne 1$ call xconscom 1$: push b mov fdtpr,b car b,fdtpr pop 2(b) mov a,(b) ret xconscom: propush a propush b call gcol unpropop b unpropop a ret ;dispatch macro calls xdispatch ;call dispatch ;;;; jmp if number ; jmp if#dtpr ; jmp if#atom ; jmp if#bcd ; jmp if#port ;note that jmps must be used ;also note that disastor will befall ;the system if it gets ahold of something other than ;these things .rsect shrcode con xdispatch: push j3 ;be nice to user ldtype a,j3 ash #2,j3 add j3,2(sp) pop j3 ret .if eq,fpsim ;strnum takes a number in strbuf ;and converts it to binary stored in core pointed to by a .rsect shrcode con strnum: mov #b4$,-(sp) ;normal return mov #strbuf,j2 ;string is in strbuf clrd ac0 ;use fac0 seti ;integer mode cmpb (j2),#'- ;is neg? bne b1$ ;no, jmp around inc j2 ;point after - sign mov #b3$,(sp) ;push on negate address b1$: movb (j2)+,j3 ;get the char beq b8$ ;if zero, we're done bicb #177760,j3 ;strip extra info ldcid j3,ac2 muld ac4,ac0 cfcc addd ac2,ac0 bvc b1$ b2$: error ;arithmetic overflow b3$: negd ac0 b4$: b7$: setl numstac0 ;store the number b8$: ret ;and go home .iff .rsect shrcode ;strnum takes a number in strbuf, and converts it to an internal ;int ;this version uses imul routine.... strnum: mov #3$,-(sp) ;store normal exit mov #strbuf,j3 clr a clr b cmpb (j3),#'- bne 1$ inc j3 mov #2$,(sp) ;set negate return 1$: tstb (j3) beq 8$ mov #10.,j2 clr j1 call imul bvs 5$ movb (j3)+,j2 sub #'0,j2 add j2,b adc a bvs 5$ br 1$ 2$: com a com b add #1,b adc a 3$: nmstore 8$: ret 5$: error .endc .rsect shrcode con ;this section of code handles nice things like ports. since ;there are at most at any time numports, where numports is an assembly ;parameter (about 15), and ports 1,2,and 3 are the tty ports, it ;does not make much sense to have an entire page allocated to them. ;except for the tty ports, the ports are 512 bytes long, starting on an ;even word boundary(even a512 word boundry) ;a port for output looks like this ; .byte count,200!!gcbit ; .word nextchar ; .word firstchar ; .word charsleft ; .word bufferlength ;where count is used by chrct and linelength, and the purpose of the rest should be fairly obvious ;a port for input looks like ; .byte savedc,!gcbit ; .word nextchar ; .word firstchar ;start o buffer ; .word charsleft ; .word bufferlength ;where savedc is the character saved by last savec ;************************** ; ;it is up to the using routine to guarantee that the thing on top of ;the np (the arguement to all these things) is either a port or nil!!! ;destruction will result if abused!!!!!! ; ;************************** ;savec saves character for next lex. ;mungs no registers!!!! ;makes no check on port's validity .rsect shrcode con xsavec: save1 mov @np,j3 jmpnnil j3,2$,nl movb char,piport+1 saveret 2$: movb char,1(j3) saveret ;putstr takes a string pointed to by b and ;outputs it on the port pointed to by the top of ;the np ;mungs no registers .rsect shrcode con putstr: save3 mov @np,j2 ;is @np nil? jmpnnil j2,1$,nl ;if nil use poport mov #poport,j2 1$: tstb (j2) ;if not nil, check tosee if output port bge 30$ ;if this byte is positive, then ; not output port 2$: movb (b)+,@2(j2) ;b has pointer to string that we're putting ;and ports are output when full, so always room ;for one more char beq 10$ ;if zero,we're done incb 1(j2) ;update width 3$: inc 2(j2) ;update pointer to buffer dec 6(j2) ;update count .if df,width bgt 20$ ;char ok...see about linefeed .iff bgt 2$ ;if non-zero, we do it again .endc call wrbop ;write-buffer-of-port .if df,width 20$: jmpnnil (np),2$ ;only concerned about poport cmpb 1(j2),lnleng ;are we past right margin?? ble 2$ ;we're ok... movb #12,@2(j2) ;output lf clrb 1(j2) ;and clear port count br 3$ ;and go to middle of loop .iff br 2$ .endc 10$: loadnil b ;return nil in b saveret 30$: erm5p: error ;wrbop outputs a buffer ;it is called either with dmpport or implicitly by putstr ;it should not be used otherwise .rsect shrcode con wrbop: mov 4(j2),$$write+2 ;set up write system call mov 2(j2),j1 ;j2 points to port; put f.c. in r0 sub 4(j2),j1 ;get length blos 1$ ;if less or = zero then don't bother mov j1,$$write+4 mov (j2),j1 ;now the file cookie bic #177701,j1 asr j1 $indir ;trap indirect $$write bcc 10$ ;if error-free, skip a bit cmp j1,#4 ;otherwise, ^c??? bne erm5p ;if is not ^c, scream 10$: tst protocell beq 1$ cmp j2,#poport beq 3$ cmp j2,#erport bne 1$ 3$: mov b,j3 ;save for a minute mov $$write+2,b add $$write+4,b clrb @b mov $$write+2,b npush protocell call putstr cmp -(np),-(np) mov j3,b ;and get back b 1$: mov 4(j2),2(j2) mov 10(j2),6(j2) ret ; note--by rights one should make sure that ^c is only ; allowable on the command port (np)==nil. ; however, it is not clear what to do in the case when ; ^c is gotten on another port. i.e., do we print a ; message, and continue??? or do we ; just let the ^c handeler take care of it??? ;dmpport outputs buffer whether full or not ;saves all registers .rsect shrcode con dmpport: save4 mov @np,j2 jmpnnil j2,2$,nl mov #poport,j2 2$: tstb (j2) bge erm5p call wrbop ;set up j2 with ptr(port), then call wrbop saveret .enabl lsb .globl $death .rsect shrcode con ;getc ;returns in char the next character in the port on np ;this has had so many additions, that it is getting ;kludgy, and should be re-written xgetc: save4 mov @np,j2 jmpnnil j2,10$,nl mov #piport,j2 incb keybin ;say we're in keyboard input br 11$ 10$: bic #1,(j2) ;turn off the gc bit tstb (j2) ble 5$ ;all this does is get valid input port 11$: movb 1(j2),char ;is savec non-zero?? bgt 2$ ;no, then go through mung blt 32$ ;means we got past eof 1$: dec 6(j2) ;is anything left in port? blt 4$ ;no, then get some chars movb @2(j2),char ;get next char in port inc 2(j2) ;kick pointer 2$: clrb 1(j2) ;set savec to zero bicb #177600,char ;clear out high bits 3$: clrb keybin ;turn off flag saveret ;and go home 4$: mov 4(j2),$$read+2 ;set up system call mov 10(j2),$$read+4 mov (j2),j1 ;savec is zero!! asr j1 $indir ;get indirect $$read bcs erm5p ;error? ;prehaps a check should be made ;for ^c here (assuming one opened ; /dev/tty? or something) ;however, this will be left ;for now mov 4(j2),2(j2) ;reset port mov %0,6(j2) ;save number of chars got beq 21$ ;if not zero,all set tst protocell ;protocol? beq 1$ jmpnnil @np,1$ mov #tib,b clrb tib(%0) ;turn into asciz string npush protocell call putstr cmp -(np),-(np) br 1$ 21$: tstb keybin ;if is > zero, we want bgt ssy31$ ;to do funnies on ^d and ^c blt 22$ ;if less than zero, no savec movb #200,1(j2) ;indicate eof in savec 22$: movb #200,char ;if zero, return eof char br 3$ ;and return, clearing keybin 5$: error 32$: error ;past eof ssy31$: jmp $death ;user typed ^d xgetca: call xgetc ;get char clr a bisb char,a ;and the character in a ret ;and go home .dsabl lsb ;fixname is called from opeen and load ; takes two args on nstack and constructs path ;from them. if second (top) arg is non-nil, ;the path is in the system library. ;if nil, the file itself is used .rsect shrcode con fixname: npop a ;get top arg jmpifnil a,fixnm1 ;if nil, just use first mov #strbuf,a ;make name in stringbuf mov #master,j1 1$: movb (j1)+,(a)+ bne 1$ dec a mov @np,j1 cmptype j1,j2,#natom ;make sure this is atom bne filerror ;then complain add #6,j1 ;get pname 2$: movb (j1)+,(a)+ bne 2$ mov #strbuf,a ;strbuf is first ptr instring ret fixnm1: mov @np,a ;here if to use only path cmptype a,j1,#natom bne filerror add #6,a ret filerror: error filer1: $close ;close error ;openc is code that is called by openr and openw ;it gets buffer and sets up common parts of ports openc: bcs filerror cmp %0,# bgt filer1 asl j1 mov j1,a ash #2,a add j1,a add #piport,a mov #400,10(a) mov j1,(a) ;save cookie mov a,-(sp) ;save port tryba: call globalc tst a beq nobuf movb #-2,qmap(a) swab a mov a,j3 mov (sp)+,a mov j3,2(a) mov j3,4(a) ret nobuf: .if ne,xfer mov (sp)+,a clr 2(a) clr 4(a) call @(sp)+ ;co-routine call to openr/openw call noroom call noroom nroomf: .iff error ,tryba .endc ;openr takes ptr to asciz string in a ;and opens the file if possible openr: mov a,$$open+2 $indir $$open call openc clr 6(a) ;peculiars of read open ret ;openw takse string n a and opens file for output openw: mov a,$$create+2 $indir $$create call openc mov #400,6(a) ;peculair to write ports bis #40200,(a) ret .rsect shrcode con ;close closes the (hopefully) port on np close: mov @np,a mov 4(a),j1 swab j1 movb #-3,qmap(j1) ;give back buffer mov (a),j1 asr j1 bic #177700,j1 clr (a) $close ret .rsect shrcode ;ncomp compares the two numbers on ttop of np ;call and return ; call ncomp ; return if not number ; return if number with condition codes set ; clobers all registers (at least in some cases) ; ;type checking is done .if eq,fpsim ncomp: mov @np,a cmptype a,j1,0 bne 12$ numga1 mov -4(np),a cmptype a,j1,0 bne 12$ numga0 cmpd ac0,ac1 add #2,(sp) cfcc 12$: ret .iff ncomp: mov (np),a ;get right arg cmptype a,b,0 ;is int bne 22$ ;no..... numgj1 ;get int mov -4(np),a cmptype a,b,0 bne 22$ numga ;got them add #2,(sp) ;make good return sub j1,a ;subtract the high order sub j2,b ;we don't care about codes of low order sbc a ;and get the borrow bne 22$ ;if result is non-zero, we're cool cmp b,a ;we know a is zero, and gotta set the v bit 22$: ret .endc