mirror of
https://github.com/PDP-10/its.git
synced 2026-04-18 17:08:05 +00:00
1464 lines
30 KiB
Plaintext
Executable File
1464 lines
30 KiB
Plaintext
Executable File
; -*- Midas -*-
|
||
|
||
title NFILE Server
|
||
subttl NFILE Server
|
||
|
||
a=:1 ; First per-process AC
|
||
b=:2
|
||
c=:3
|
||
d=:4
|
||
e=:5
|
||
f=:6
|
||
|
||
in=:7 ; network input
|
||
out=:10 ; network output
|
||
|
||
t=:11 ; T thru Z are temps, caller must save
|
||
tt=:12
|
||
x=:13
|
||
y=:14
|
||
z=:15 ; Last per-process AC
|
||
|
||
st=:16 ; sleep time
|
||
p=:17
|
||
|
||
nchans==:16. ; All 16 channels allocated
|
||
chmask==:<1_nchans>-1
|
||
|
||
%fl==:1,,525252 ; Flags in LH(0)
|
||
%fltcp==:400000 ; TCP, not Chaos (sign bit)
|
||
%fldbg==:200000 ; Debugging mode
|
||
%flsrv==:100000 ; Started as a server
|
||
%fldie==:040000 ; Need to garbage collect a process
|
||
|
||
%fr==:0,,525252 ; Flags in RH(0)
|
||
if2,[
|
||
repeat nchans,[
|
||
conc %fr,\.rpcnt,==:1_.rpcnt ; Set by channel interrupts
|
||
]]
|
||
|
||
define syscall name,args
|
||
.call [setz ? .1stwd sixbit /name/ ? args(400000)]
|
||
termin
|
||
|
||
define calblk name,args
|
||
setz ? .1stwd sixbit /name/ ? args(400000)
|
||
termin
|
||
|
||
define conc foo,bar
|
||
foo!bar!termin
|
||
|
||
call=:pushj p,
|
||
return=:popj p,
|
||
save==:push p,
|
||
rest==:pop p,
|
||
flose=:.lose %lsfil
|
||
slose=:.lose %lssys
|
||
pause=:.break 16,100000
|
||
clkoff=:.suset [.sidf1,,[%pirlt]]
|
||
clkon=:.suset [.sadf1,,[%pirlt]]
|
||
jtcp==:jumpl 0, .see %fltcp
|
||
jchaos==:jumpge 0,
|
||
|
||
; JSP T,LOSE is like .LOSE %LSSYS(TT) or SLOSE (TT)
|
||
lose: trne tt,-100 .see iocint
|
||
jrst losioc
|
||
syscall lose,[movei %lssys(tt) ? movei -2(t)]
|
||
slose
|
||
|
||
losioc: syscall lose,[movei 1+.lz %piioc ? movei -2(t)]
|
||
slose
|
||
|
||
popj3: aos (p)
|
||
popj2: aos (p)
|
||
popj1: aos (p)
|
||
cpopj: return
|
||
|
||
; Protocol definitions, etc.
|
||
|
||
.insrt dsk:syseng;chsdef >
|
||
$cpkbp==:440800,,%cpkdt
|
||
%comrk==:%codat+1 ; Mark opcode
|
||
|
||
chaprt==:440700,,[asciz /NFILE/]
|
||
chatpr==:440700,,[asciz /NFILE-TEST/] ; For testing
|
||
chacnm==:sixbit /NFILE/
|
||
chawin==:8 ; receive window size
|
||
|
||
tcpprt==:73
|
||
tcptpr==:199. ; For testing
|
||
tcpcnm==:sixbit /SYN073/
|
||
tcpmxw==:500. ; tcp buffer size
|
||
tcpmxc==:tcpmxw*4 ; tcp buffer size in bytes
|
||
|
||
genprt==:-1 ; Gensym port (both Chaos and TCP)
|
||
|
||
; Token list codes:
|
||
; 0. - 199. ; short data token of given length (< 200.).
|
||
zshort==:200. ; Largest short data code plus one
|
||
zpad==:200. ; Padding, ignored
|
||
zlong==:201. ; long data token (>= 200.), followed by 4
|
||
; bytes of length (least sig. byte first).
|
||
ztlbeg==:202. ; Start top level list
|
||
ztlend==:203. ; End top level list
|
||
zbegin==:204. ; Start embedded list
|
||
zend==:205. ; End embedded list
|
||
zsint==:206. ; short integer in next byte
|
||
zlint==:207. ; long integer, length in next byte, then
|
||
; magnitude (least sig. byte first).
|
||
; Only integers less that 2^63 are supported?
|
||
zkey==:208. ; keyword, followed by data token of
|
||
; upper-case standard ASCII.
|
||
ztrue==:209. ; Boolean truth
|
||
zmax==:210. ; Largest code plus one
|
||
|
||
; List structure representation:
|
||
; 4.9 0 => Integer: <magnitude>
|
||
; 1 => 4.8 1 => List: -<word length>,,<addr>
|
||
; 0 => 4.7 0 => Data token: SETZ + <byte length>,,<addr>
|
||
; 1 => Other: <code>,,<???>
|
||
|
||
define .data *string*
|
||
<400000+<.length string>,,[
|
||
.byte 8
|
||
irpc ch,,[string]
|
||
"ch
|
||
termin
|
||
]>termin
|
||
|
||
lskey==:500001 ; Keyword, address in RH.
|
||
lsnil==:500002 ; Empty list
|
||
lstrue==:500003 ; Truth
|
||
lserror==:500004 ; Bottom
|
||
|
||
urandom==:776543
|
||
qnil=:lsnil,,urandom
|
||
qtrue=:lstrue,,urandom
|
||
qerror=:lserror,,urandom
|
||
|
||
; Keyword data structure:
|
||
kwdata==:0 ; wd 0: data token
|
||
kwhash==:1 ; wd 1: hash of data
|
||
kwlen==:2 ; wd 2: length of data
|
||
kwnext==:3 ; wd 3: hashtable bucket thread
|
||
kwsubr==:4 ; wd 4: command routine
|
||
kwmax==:5 ; 5 words total
|
||
|
||
.lkey.==0
|
||
define defkey name,&string&,subr=aerror
|
||
q!name=:lskey,,.
|
||
u!name: .data string ; kwdata
|
||
0 ; kwhash
|
||
0 ; kwlen
|
||
.lkey. ; kwnext
|
||
subr ; kwsubr
|
||
.lkey.==q!name
|
||
termin
|
||
|
||
; REPORT macro
|
||
|
||
format"$$pcode==:1
|
||
.insrt dsk:syseng;format >
|
||
|
||
.scalar rprtch ; report channel
|
||
|
||
outstr: syscall siot,[move rprtch ? a ? b]
|
||
slose
|
||
return
|
||
|
||
define report &string&,args
|
||
call [
|
||
call $report
|
||
.zzz.==-1
|
||
irp arg,,[args]
|
||
save arg
|
||
.zzz.==.irpcnt
|
||
termin
|
||
hrroi a,[ascii string]
|
||
movei b,.length string
|
||
movni c,.zzz.+1
|
||
jrst format"format]
|
||
termin
|
||
|
||
$repor: tlnn %fldbg
|
||
jrst pop1j
|
||
save a
|
||
save b
|
||
save c
|
||
call @-3(p)
|
||
rest c
|
||
rest b
|
||
rest a
|
||
pop1j: rest (p)
|
||
return
|
||
|
||
; Initialization
|
||
|
||
usrvar: sixbit /40ADDR/ ? move [twenty,,forty]
|
||
sixbit /TTY/ ? tlo %tbnvr
|
||
sixbit /OPTION/ ? tlo %opint\%opopc
|
||
sixbit /DF1/ ? move [%pirlt]
|
||
sixbit /MASK/ ? move [%pirlt\%pidwn\%pidbg\%pipdl\%piioc]
|
||
sixbit /UNAME/ ? movem uname
|
||
lusrvar==:.-usrvar
|
||
|
||
.scalar uname ; Uname
|
||
.scalar cname ; Contact name: CHACNM or TCPCNM
|
||
.scalar time ; Address of system's TIME variable
|
||
|
||
.vector pdl(lpdl==:20)
|
||
|
||
purify: move p,[-lpdl,,pdl-1]
|
||
call kinit
|
||
movsi t,-npure
|
||
syscall corblk,[movei %cbndr ? movei %jself ? move t]
|
||
slose
|
||
syscall corblk,[movei 0 ? movei %jself ? movei timepg]
|
||
slose
|
||
.value [asciz ":PDUMP DSK:DEVICE;CHAOS NFILE"]
|
||
;; <rubout><alt>P to debug with purity
|
||
go: .close 1,
|
||
movem 0,cname
|
||
setzi 0, ; clear flags
|
||
move p,[-lpdl,,pdl-1]
|
||
|
||
;; Initialize TIME
|
||
move t,[squoze 0,time]
|
||
.eval t,
|
||
.lose
|
||
movem t,time
|
||
ldb t,[121000,,time]
|
||
syscall corblk,[movei %cbndr
|
||
movei %jself ? movei timepg
|
||
movei %jsabs ? move t]
|
||
slose
|
||
movei t,timepg
|
||
dpb t,[121000,,time]
|
||
|
||
;; Build initial process, start scheduler
|
||
call minit
|
||
setzm queue
|
||
setom qlock
|
||
movei a,gogo
|
||
call spawn
|
||
jsp t,lose
|
||
move t,[-lusrvar,,usrvar]
|
||
syscall usrvar,[movei %jself ? move t]
|
||
slose
|
||
move t,[%rlfls\%rlset,,clktim]
|
||
.realt t,
|
||
jrst schedq
|
||
|
||
; Initial process
|
||
|
||
gogo: .call sstatu
|
||
slose
|
||
setom hstcnt
|
||
setom hstlock ; host table ready
|
||
call kinit ; keywords ready (in case not pure)
|
||
movei t,chmask
|
||
movem t,chbits
|
||
setom chlock ; channels ready
|
||
call challoc
|
||
jsp t,lose
|
||
syscall open,[movei (t) ? movsi .uao\%tjdis ? [sixbit /TTY/]]
|
||
jrst [ call chfree ; %TBNVR is set
|
||
jrst gogo1 ]
|
||
hrrzm t,rprtch
|
||
tlo %fldbg ; Debug chaos by default
|
||
.value [asciz ""]
|
||
report "~&Debugging~&"
|
||
gogo1: movei a,serve
|
||
call spawn
|
||
jsp t,lose
|
||
sysjob: tlnn %fldie
|
||
call hang
|
||
call diegc
|
||
jrst sysjob
|
||
|
||
; Server process
|
||
|
||
serve: move t,cname
|
||
camn t,[chacnm]
|
||
tlo %flsrv
|
||
camn t,[tcpcnm]
|
||
tlo %fltcp\%flsrv
|
||
tlnn %flsrv\%fldbg
|
||
.lose
|
||
move a,[tcpprt]
|
||
tlne %fldbg
|
||
move a,[tcptpr]
|
||
jtcp serve5
|
||
move a,[chaprt]
|
||
tlne %fldbg
|
||
move a,[chatpr]
|
||
serve5: call netlsn
|
||
jsp t,lose
|
||
call netopn
|
||
call logout
|
||
report "~&Network open~&"
|
||
call login
|
||
|
||
call die
|
||
|
||
; Logging in and out
|
||
|
||
; CALL LOGOUT: Logout
|
||
logout: tlnn %fldbg
|
||
.logout
|
||
pause
|
||
jrst logout
|
||
|
||
; CALL LOGIN: Login
|
||
login: tlne %fldbg
|
||
return
|
||
save a
|
||
syscall rfname,[movei (out) ? repeat 4,[ ? movem a]]
|
||
slose
|
||
call hstbeg
|
||
jrst login1 ; Perhaps host table trashed...
|
||
call netwrk"hstsix
|
||
.lose
|
||
call hstend
|
||
login1: move x,uname
|
||
hrli x,(sixbit /NFL/) ; NFL017
|
||
syscall usrvar,[movei %jself ? [sixbit /JNAME/] ? [chacnm]]
|
||
jfcl ; OK if you fail...
|
||
syscall login,[moves tt ? move x ? move a]
|
||
jsp t,[caie tt,%eexfl
|
||
jrst lose
|
||
addi x,20000 ; NFL217, NFL417, etc...
|
||
jrst .-1 ]
|
||
movem x,uname
|
||
.suset [.ssname,,a]
|
||
;; System demon for fast response, just like QFILE:
|
||
syscall detach,[movei %jself ? movsi 3]
|
||
slose
|
||
rest a
|
||
return
|
||
|
||
; Generic I/O
|
||
|
||
.vector buffer(nchans) ; Channel buffers
|
||
.vector bufbp(nchans) ; Byte pointer into buffer
|
||
.vector bufbc(nchans) ; Count of data or room in buffer
|
||
; (always non-zero for output)
|
||
.vector bufcall(nchans) ; What to do when count runs out
|
||
.vector bufsys(nchans) ; system buffer status
|
||
.vector bufmkc(nchans) ; count for mark
|
||
.vector bufmark(nchans) ; routine for sending a mark
|
||
|
||
; CALL INB: Read one byte of input
|
||
; Skips unless error, error code in TT
|
||
; A (val): Byte or -1 for EOF or -2 for mark
|
||
inb0: call @bufcall(in)
|
||
return ; error
|
||
jrst inb1 ; eof
|
||
jrst inb2 ; mark
|
||
inb: sosge bufbc(in) ; normal
|
||
jrst inb0
|
||
ildb a,bufbp(in)
|
||
aos (p)
|
||
return
|
||
|
||
inb2: skipa a,[-2] ; mark
|
||
inb1: setoi a, ; eof
|
||
aos (p)
|
||
return
|
||
|
||
; CALL OUTB: Write one byte of output
|
||
; Skips unless error, error code in TT
|
||
; A (a/v): Byte
|
||
outb: idpb a,bufbp(out)
|
||
sosg bufbc(out)
|
||
jrst @bufcall(out)
|
||
aos (p)
|
||
return
|
||
|
||
; Generic network routines
|
||
|
||
; CALL NETLSN: Start listening for network connection
|
||
; Skips if connection opened, else error code in TT
|
||
; A (a/v): port number/contact name
|
||
; Sets up IN and OUT
|
||
netlsn: call challoc
|
||
return
|
||
.call inton
|
||
slose
|
||
movei out,(t)
|
||
call challoc
|
||
jrst outcls
|
||
.call inton
|
||
slose
|
||
movei in,(t)
|
||
jtcp tcplsn
|
||
jrst chalsn
|
||
|
||
; close channels, preserve error code in TT
|
||
iocls: move t,in
|
||
call chfree
|
||
outcls: move t,out
|
||
jrst chfree
|
||
|
||
; CALL NETOPN: Accept network connection
|
||
; Skips if connection opened
|
||
netopn: movei tt,5*30. ; 5 seconds
|
||
tlne %fldbg
|
||
movei tt,60.*60.*30. ; 1 hour
|
||
jtcp tcpopn
|
||
jrst chaopn
|
||
|
||
; CALL NETBLK: Await channel state change
|
||
; CALL CHBLK: Await channel interrupt
|
||
; X (a/v): Channel
|
||
; T (arg): Current state (NETBLK)
|
||
; T (val): New state (NETBLK)
|
||
; TT (arg): Time to wait
|
||
; TT (val): Remaining time
|
||
ntblk1: call chblk
|
||
netblk: tdz bit(x)
|
||
syscall whyint,[movei (x) ? movem z ? movem z]
|
||
slose
|
||
camn t,z
|
||
jrst ntblk1
|
||
move t,z
|
||
return
|
||
|
||
chblk: add tt,@time
|
||
call chblk1
|
||
call hang
|
||
sub tt,@time
|
||
return
|
||
|
||
chblk1: tdnn bit(x)
|
||
tmblk1: camg tt,@time
|
||
jrst popj1
|
||
camle st,tt
|
||
move st,tt
|
||
return
|
||
|
||
; CALL SLEEP: Await the passage of time
|
||
; TT (arg): Time to wait
|
||
sleep: add tt,@time
|
||
call tmblk1
|
||
call hang
|
||
return
|
||
|
||
; TCP routines
|
||
|
||
if2, .err TCP needs to be re-written
|
||
; And you might want to look at the code in ITSDEV first...
|
||
|
||
tcplsn: jfcl ? .lose
|
||
tcpopn: jfcl ? .lose
|
||
|
||
ifn 0,[
|
||
|
||
if2, .err TCP buffer size?
|
||
|
||
ifn genprt+1, .err TCPLSN demands GENPRT = -1
|
||
tcplsn: syscall tcpopn,[movei (in) ? movei (out) ? move a ? [-1] ? [-1]]
|
||
slose
|
||
syscall rfname,[movei (in) ? movem a ? movem a]
|
||
slose
|
||
movei t,tcpmxw
|
||
call malloc
|
||
jrst iocls
|
||
movem t,buffer(in)
|
||
movei t,tcpmxw
|
||
call malloc
|
||
jrst iocls
|
||
movem t,buffer(out)
|
||
report "~&Listening on TCP port ~D~&",a
|
||
aos (p)
|
||
return
|
||
|
||
tcpopn: movei t,%ntlsn
|
||
tcpop1: call outblk
|
||
jumple tt,cpopj
|
||
cain t,%ntsyr
|
||
jrst tcpop1
|
||
caie t,%ntopn
|
||
cain t,%ntwrt
|
||
jrst tcpop2
|
||
jrst iocls
|
||
|
||
tcpop2: skipge sysdbg
|
||
if2, .err Refuse TCP connection more gracefully.
|
||
jrst iocls
|
||
aos (p)
|
||
movei t,tcpin
|
||
movem t,bufcall(in)
|
||
setzm bufbc(in)
|
||
movei t,tcpmrk
|
||
movem t,bufmark(out)
|
||
movei t,tcpout
|
||
movem t,bufcall(out)
|
||
hrrz t,buffer(out)
|
||
hrli t,240800
|
||
movem t,bufbp(out)
|
||
movei t,tcpmxc-3
|
||
movem t,bufbc(out)
|
||
return
|
||
|
||
; CALL TCPIN: BUFCALL routine for TCP input
|
||
tcpin: skiple t,bufmkc(in)
|
||
jrst tcpin8
|
||
movnm t,bufbc(in)
|
||
sosge bufbc(in)
|
||
jsp z,tcpin0
|
||
ildb t,bufbp(in)
|
||
lsh t,8
|
||
movem t,bufmkc(in)
|
||
sosge bufbc(in)
|
||
jsp z,tcpin0
|
||
ildb t,bufbp(in)
|
||
addb t,bufmkc(in)
|
||
jumpg t,tcpin9
|
||
movn t,bufbc(in) ; found a mark
|
||
movem t,bufmkc(in) ; remember stuff in buffer
|
||
setzm bufbc(in) ; make sure he comes back
|
||
report "~&In ~O: Mark (BUFMKC=~D.)~&",[in,bufmkc(in)]
|
||
jrst popj2
|
||
|
||
tcpin9: skipg bufbc(in)
|
||
tcpin8: jsp z,tcpin0
|
||
movn t,bufbc(in)
|
||
addb t,bufmkc(in)
|
||
skipge t
|
||
addm t,bufbc(in)
|
||
report "~&In ~O: ~D. byte~P (BUFMKC=~D.)~&",[in,bufbc(in),bufmkc(in)]
|
||
jrst popj3
|
||
|
||
; JSP Z,TCPIN0: Refill buffer for TCPIN
|
||
; Returns -2(Z) or RETURNs with error code in TT.
|
||
tcpin1: move t,whymap(in)
|
||
.call whyint
|
||
slose
|
||
skipg whyv3(t)
|
||
call hang
|
||
move t,whyv3(t)
|
||
movem t,bufsys(in)
|
||
report "~&In ~O: ~D. byte~P avail.~&",[in,t]
|
||
tcpin0: skipg t,bufsys(in)
|
||
jrst tcpin1
|
||
caige t,tcpmxc
|
||
skipa x,t
|
||
movei x,tcpmxc
|
||
sub t,x
|
||
movem t,bufsys(in)
|
||
movem x,bufbc(in)
|
||
hrrz t,buffer(in)
|
||
hrli t,440800
|
||
movem t,bufbp(in)
|
||
syscall siot,[moves tt ? movei (in) ? move t ? move x]
|
||
return
|
||
report "~&In ~O: ~D. byte~P~&",[in,bufbc(in)]
|
||
jrst -2(z)
|
||
|
||
; CALL TCPOUT: BUFCALL routine for TCP output
|
||
; (Can't reliably avoid blocking on output under ITS TCP!)
|
||
tcpout: movei t,tcpmxc-3
|
||
sub t,bufbc(out) ; T: # bytes
|
||
jumpe t,popj1
|
||
hrrz x,buffer(out) ; X: buffer
|
||
dpb t,[242000,,0(x)]
|
||
tcpou1: hrli x,240800
|
||
movem x,bufbp(out)
|
||
movei tt,tcpmxc-3
|
||
movem tt,bufbc(out)
|
||
hrli x,440800
|
||
addi t,2
|
||
report "~&Out ~O: ~D. byte~P~&",[out,t]
|
||
syscall siot,[moves tt ? movei (out) ? move x ? move t]
|
||
return
|
||
aos (p)
|
||
return
|
||
|
||
; CALL TCPMRK: BUFMARK routine for TCP
|
||
tcpmrk: movei t,tcpmxc-3
|
||
sub t,bufbc(out) ; T: # bytes
|
||
hrrz x,buffer(out) ; X: buffer
|
||
dpb t,[242000,,0(x)]
|
||
jumpe t,tcpou1
|
||
setzi tt,
|
||
idpb tt,bufbp(out)
|
||
idpb tt,bufbp(out)
|
||
addi t,2
|
||
jrst tcpou1
|
||
|
||
] ;IFN 0
|
||
|
||
; Chaosnet routines
|
||
|
||
pktin: calblk pktiot,[moves tt ? movei (in) ? move buffer(in)]
|
||
|
||
pktout: calblk pktiot,[moves tt ? movei (out) ? move buffer(out)]
|
||
|
||
chals3: idivi t,26.
|
||
addi tt,"A
|
||
jumpe t,chals4
|
||
hrlm tt,(p)
|
||
call chals3
|
||
hlrz tt,(p)
|
||
chals4: idpb tt,x
|
||
return
|
||
|
||
chalsn: came a,[genprt]
|
||
jrst chals1
|
||
movei t,5
|
||
call malloc
|
||
return
|
||
hrli t,440700
|
||
move x,t
|
||
move a,t
|
||
skipa y,[chaprt]
|
||
chals2: idpb t,x
|
||
ildb t,y
|
||
jumpn t,chals2
|
||
movei t,"-
|
||
idpb t,x
|
||
.gennum t,
|
||
call chals3
|
||
setzi t,
|
||
idpb t,x
|
||
chals1: syscall chaoso,[movei (in) ? movei (out) ? movei chawin]
|
||
slose
|
||
movei t,%cpmxw
|
||
call malloc
|
||
jrst iocls
|
||
movem t,buffer(in)
|
||
movei t,%cpmxw
|
||
call malloc
|
||
jrst iocls
|
||
movem t,buffer(out)
|
||
movei tt,%colsn
|
||
move t,a
|
||
call pktsnd ; Send LSN
|
||
report "~&Listening on Chaos for ~A~&",a
|
||
aos (p)
|
||
return
|
||
|
||
chaopn: movei x,(in)
|
||
movei t,%cslsn
|
||
call netblk
|
||
caie t,%csrfc
|
||
return
|
||
skipge sysdbg
|
||
jrst [ movei tt,%cocls
|
||
move t,[440700,,[asciz "System not up"]]
|
||
jrst pktsnd ]
|
||
aos (p)
|
||
movei tt,%coopn
|
||
movei t,[440700,,[asciz ""]]
|
||
call pktsnd
|
||
.call pktin ; Flush RFC packet
|
||
jsp t,lose
|
||
movei t,chain
|
||
movem t,bufcall(in)
|
||
setzm bufbc(in)
|
||
movei t,chamrk
|
||
movem t,bufmark(out)
|
||
movei t,chaout
|
||
movem t,bufcall(out)
|
||
movei t,%cpmxc
|
||
movem t,bufbc(out)
|
||
hrrz t,buffer(out)
|
||
add t,[$cpkbp]
|
||
movem t,bufbp(out)
|
||
return
|
||
|
||
; CALL PKTSND: Send packet with given opcode and contents
|
||
; T (arg): bp to null-terminated string
|
||
; TT (arg): Opcode
|
||
pktsnd: hrrz x,buffer(out)
|
||
setzm (x) ; ITS insists...
|
||
dpb tt,[$cpkop(x)]
|
||
add x,[$cpkbp]
|
||
setzi tt,
|
||
pktsn1: ildb y,t
|
||
jumpe y,pktsn2
|
||
idpb y,x
|
||
aoja tt,pktsn1
|
||
|
||
pktsn2: hrrz x,buffer(out)
|
||
dpb tt,[$cpknb(x)]
|
||
.call pktout
|
||
jsp t,lose
|
||
return
|
||
|
||
; CALL CHAIN: BUFCALL routine for Chaos input
|
||
chain0: caie x,%csopn
|
||
jrst enapp
|
||
tdnn bit(in)
|
||
call hang
|
||
chain1: tdz bit(in)
|
||
syscall whyint,[moves tt ? movei (in)
|
||
movem x ? movem x ? movem y]
|
||
return
|
||
tlnn y,-1
|
||
jrst chain0
|
||
hlrzm y,bufsys(in)
|
||
report "~&In ~O: ~D. pkt~P avail.~&",[in,bufsys(in)]
|
||
chain: sosge bufsys(in)
|
||
jrst chain1
|
||
.call pktin
|
||
return
|
||
hrrz x,buffer(in) ; X: buffer
|
||
ldb y,[$cpkop(x)] ; Y: opcode
|
||
ldb z,[$cpknb(x)] ; Z: length
|
||
report "~&In ~O: ~D. byte~P (op=~O)~&",[in,z,y]
|
||
caie y,%codat
|
||
jrst chain2
|
||
movem z,bufbc(in)
|
||
add x,[$cpkbp]
|
||
movem x,bufbp(in)
|
||
jrst popj3
|
||
|
||
chain2: cain y,%comrk
|
||
jrst popj2
|
||
cain y,%coeof
|
||
jrst popj1
|
||
enapp: movei tt,%enapp
|
||
return
|
||
|
||
; CALL CHAOUT: BUFCALL routine for Chaos output
|
||
chaout: movei t,%cpmxc
|
||
sub t,bufbc(out) ; T: bytes in buffer
|
||
jumpe t,popj1 ; Nothing to send => just return
|
||
movei tt,%codat ; TT: opcode
|
||
chaou0: report "~&Out ~O: ~D. byte~P (op=~O)~&",[out,t,tt]
|
||
hrrz x,buffer(out) ; X: buffer
|
||
dpb tt,[$cpkop(x)]
|
||
dpb t,[$cpknb(x)]
|
||
add x,[$cpkbp]
|
||
movem x,bufbp(out)
|
||
movei t,%cpmxc
|
||
movem t,bufbc(out)
|
||
chaou1: sosge bufsys(out)
|
||
jrst chaou2
|
||
.call pktout
|
||
return
|
||
aos (p)
|
||
return
|
||
|
||
chaou3: tdnn bit(out)
|
||
call hang
|
||
chaou2: tdz bit(out)
|
||
syscall whyint,[moves tt ? movei (out)
|
||
movem x ? movem x ? movem y]
|
||
return
|
||
caie x,%csopn
|
||
jrst enapp
|
||
trnn y,-1
|
||
jrst chaou3
|
||
hrrzm y,bufsys(out)
|
||
report "~&Out ~O: ~D. pkt~P room~&",[out,bufsys(out)]
|
||
jrst chaou1
|
||
|
||
; CALL CHAMRK: BUFMARK routine for Chaos
|
||
chamrk: call chaout
|
||
return
|
||
setzi t,
|
||
movei tt,%comrk
|
||
jrst chaou0
|
||
|
||
; Host table routines
|
||
|
||
netwrk"$$look==:1
|
||
netwrk"$$hstmap==:1
|
||
netwrk"$$hstsix==:1
|
||
.insrt dsk:syseng;netwrk >
|
||
|
||
.see hstlock ; Write lock on host table variables
|
||
.scalar hstcnt ; Count of host table users less one
|
||
|
||
; CALL HSTBEG: Start using host table
|
||
; Skips if it won, else error code in TT
|
||
hstbeg: aose hstlock
|
||
call hang
|
||
aose hstcnt ; First one in?
|
||
jrst hstwn1 ; No
|
||
call challoc ; Yes: we get to map it in
|
||
jrst hstlz1
|
||
save t
|
||
syscall open,[moves tt ? movsi .bii ? movei (t) ? [sixbit /DSK/]
|
||
[sixbit /HOSTS3/] ? [sixbit />/] ? [sixbit /SYSBIN/]]
|
||
jrst hstluz
|
||
syscall fillen,[movei (t) ? movem t]
|
||
slose
|
||
jumple t,hstbdf
|
||
movei t,1777(t)
|
||
lsh t,-10.
|
||
call pgalloc ; T: page aobjn
|
||
jrst hstluz
|
||
movem t,netwrk"hstabn
|
||
movei x,(t)
|
||
lsh x,10. ; X: base address
|
||
move tt,(p)
|
||
syscall corblk,[movei %cbndr ? movei %jself ? move t ? movei (tt)]
|
||
slose
|
||
move t,netwrk"hstsid(x)
|
||
camn t,[sixbit /HOSTS3/]
|
||
jrst hstwin
|
||
move t,netwrk"hstabn
|
||
call pgfree
|
||
jrst hstbdf
|
||
|
||
hstwin: movem x,netwrk"hstadr
|
||
rest t
|
||
call chfree
|
||
hstwn1: aos (p)
|
||
setom hstlock
|
||
return
|
||
|
||
hstbdf: movei tt,%ebdfl
|
||
hstluz: rest t
|
||
call chfree
|
||
hstlz1: sos hstcnt
|
||
setom hstlock
|
||
return
|
||
|
||
; CALL HSTEND: Stop using host table
|
||
hstend: aose hstlock
|
||
call hang
|
||
sosl hstcnt ; Last one out?
|
||
jrst hstunx ; No
|
||
skipn netwrk"hstadr ; Yes: we get to map it out
|
||
.lose
|
||
move t,netwrk"hstabn
|
||
call pgfree
|
||
setzm netwrk"hstadr
|
||
hstunx: setom hstlock
|
||
return
|
||
|
||
; Interrupts
|
||
|
||
; The format of an interrupt stack frame:
|
||
intctl==:<a_6>+<z-a+1> ; A through Z saved
|
||
intsiz==:<z-a+1>+5 ; 5 words as follows:
|
||
intrq1==:1-intsiz ; PIRQC bits
|
||
intrq2==:2-intsiz ; IFPIR bits
|
||
intdf1==:3-intsiz ; saved DF1
|
||
intdf2==:4-intsiz ; saved DF2
|
||
intpc==: 5-intsiz ; saved PC
|
||
intac0==:6-intsiz-a ; then come ACs
|
||
|
||
; All interrupts must defer %PIRLT for scheduler to work properly
|
||
tsint: intctl,,p
|
||
%piioc ? 0 ? -1 ? -1 ? iocint
|
||
%pirlt ? 0 ? %pirlt ? 0 ? clkint
|
||
%pidwn\%pidbg ? 0 ? %pirlt\%pidwn\%pidbg ? 0 ? sstint
|
||
0 ? chmask ? %pirlt ? chmask ? chint
|
||
ltsint==:.-tsint
|
||
|
||
dismis: calblk dismis,[movsi intctl ? move p]
|
||
|
||
.scalar shutdn,sysdbg
|
||
|
||
sstatu: calblk sstatu,[movem shutdn ? movem sysdbg]
|
||
|
||
sstint: .call sstatu
|
||
slose
|
||
setzi st,
|
||
.call dismis
|
||
slose
|
||
|
||
chint: tdo intrq2(p)
|
||
setzi st,
|
||
.call dismis
|
||
slose
|
||
|
||
; Handle IOC interrupts:
|
||
; If the instruction was a .CALL that has an error return argument, we
|
||
; make it look like it just failed to skip.
|
||
iocint: .suset [.rbchn,,a]
|
||
syscall status,[movei (a) ? movem b]
|
||
slose
|
||
tlnn b,-100 ; better not look like standard error...
|
||
.lose
|
||
move e,@intpc(p)
|
||
tlc e,(.call)
|
||
move t,[setz]
|
||
tlnn e,-1 ; better be .CALL [ SETZ ? ... ]
|
||
came t,(e)
|
||
jrst iocnt3
|
||
iocnt2: move t,2(e)
|
||
tlc t,(moves)
|
||
tlne t,(7^9 @(17)) ; unindexed, direct, error return?
|
||
aoja e,iocnt1
|
||
trnn t,-20 ; ACs are on stack right now
|
||
addi t,intac0(p)
|
||
hlrzm b,(t)
|
||
aos intpc(p)
|
||
.call dismis
|
||
slose
|
||
|
||
iocnt1: jumpge t,iocnt2
|
||
iocnt3: hrl a,intpc(p)
|
||
hrri a,1+.lz %piioc
|
||
syscall dismis,[movsi intctl ? move p ? move intpc(p)
|
||
move intdf1(p) ? move intdf2(p) ? move a]
|
||
slose
|
||
|
||
; Scheduler
|
||
|
||
.see qlock ; Lock on process queue
|
||
.scalar queue ; Queue of runnable processes
|
||
.scalar user ; Currently running process
|
||
|
||
unext==:0 ; WD 0: Next process in queue
|
||
updl==:1 ; WD 1: Pdl pointer for process
|
||
luser==:2 ; Length of a process
|
||
|
||
; ST always contains the time at which the scheduler must check all PRHANG
|
||
; instructions again. Interrupts set ST to 0. PRHANG instructions that
|
||
; skip after time T set ST to min(ST, T). When any process is run, ST is
|
||
; set to 0 in case the process does something that might make other
|
||
; processes runnable.
|
||
|
||
; The scheduler always runs with %PIRLT interrupts defered, and an
|
||
; interrupt frame on the stack, just as if the current process had been
|
||
; interrupted by a %PIRLT. All interrupts defer %PIRLT, so processes can
|
||
; be swapped just by swapping stack pointers and doing a DISMIS.
|
||
|
||
prhang==:intrq1 ; "FLSINS" (INTRQ1 and INTRQ1 are not used
|
||
; by %PIRLT interrupts.)
|
||
prdf1==:intdf1 ; Must contain 0
|
||
prdf2==:intdf2 ; Must contain 0
|
||
prpc==:intpc ; "UPC"
|
||
prac0==:intac0 ; "AC0S"
|
||
|
||
; CALL HANG: Wait for previous instruction to skip
|
||
; Instruction can only look at T, TT and X, except in its address
|
||
; calculation where it can look at anything except TT and P.
|
||
hang: clkoff ; Simulate occurrence of %PIRLT
|
||
repeat 3, save [0] ; Build frame
|
||
save -3(p)
|
||
repeat z-a+1, save a+.rpcnt
|
||
move tt,prpc(p)
|
||
move tt,-2(tt) ; TT: instruction
|
||
hrri tt,@tt ; Do E.A. calc.
|
||
tlza tt,(@(17))
|
||
clkint: movsi tt,(caia) ; Actual %PIRLT comes here
|
||
movem tt,prhang(p) ; Set condition
|
||
move a,user
|
||
movem p,updl(a)
|
||
schedn: ;; Come here to consider running process after process in A
|
||
skipn a,unext(a)
|
||
jrst schedw
|
||
sched: ;; Come here to consider running process in A
|
||
move p,updl(a)
|
||
move t,prac0+t(p) ; Restore T, TT and X
|
||
move tt,prac0+tt(p)
|
||
move x,prac0+x(p)
|
||
xct prhang(p) ; Might adjust ST
|
||
jrst schedn
|
||
movem a,user
|
||
setzi st,
|
||
.call dismis
|
||
slose
|
||
|
||
schedw: camg st,@time ; Going to wait?
|
||
jrst schedq ; No: then get right to work
|
||
camle st,@time ; Wait for time to pass (or interrupt)
|
||
.hang
|
||
.suset [.sapirqc,,[%pirlt]] ; (since we waited)
|
||
schedq: ;; Come here to consider queue from the beginning
|
||
hrloi st,377777 ; +inf
|
||
skipn a,queue
|
||
.lose
|
||
jrst sched
|
||
|
||
; Locking order
|
||
|
||
; To prevent deadlocks we maintain a partial order on locks.
|
||
; If XLOCK > YLOCK then someone tries to lock YLOCK while holding XLOCK.
|
||
|
||
.scalar klock ; KLOCK > MLOCK
|
||
.scalar qlock ; QLOCK > MLOCK
|
||
.scalar mlock ; MLOCK > PGLOCK
|
||
.scalar hstlock ; HSTLOCK > PGLOCK, HSTLOCK > CHLOCK
|
||
.scalar pglock
|
||
.scalar chlock
|
||
|
||
; Process management
|
||
|
||
lproc==:150.
|
||
|
||
; CALL SPAWN: Spawn a process
|
||
; Fails to skip if no free memory, error code in TT
|
||
; A (a/v): Initial PC
|
||
; A - Z: Passed to process
|
||
spawn: movei t,lproc
|
||
call malloc
|
||
return
|
||
aos (p) ; gonna win
|
||
move x,t ; X: new process
|
||
add t,[luser+intsiz,,luser+intsiz-1] ; T: its pdl
|
||
movem t,updl(x)
|
||
movem a,prpc(t)
|
||
movsi tt,(caia)
|
||
movem tt,prhang(t)
|
||
hrli tt,a
|
||
hrri tt,prac0+a(t)
|
||
blt tt,prac0+z(t)
|
||
setzm prdf1(t)
|
||
setzm prdf2(t)
|
||
aose qlock
|
||
call hang
|
||
move t,queue
|
||
movem t,unext(x)
|
||
movem x,queue
|
||
setom qlock
|
||
return
|
||
|
||
; CALL DIE: Kill current process
|
||
die: tlo %fldie ; Never skips, do a GC
|
||
call hang
|
||
dead: .value ; The PC of a corpse
|
||
|
||
; CALL DIEGC: System job calls this when %FLDIE comes on
|
||
; Clobbers A
|
||
diegc9: setom qlock
|
||
diegc: tlzn %fldie
|
||
return
|
||
aose qlock
|
||
call hang
|
||
movei a,queue
|
||
jrst diegc2
|
||
|
||
diegc1: movei a,unext(t)
|
||
diegc2: skipn t,(a)
|
||
jrst diegc9
|
||
move tt,updl(t)
|
||
hrrz tt,prpc(tt)
|
||
caie tt,dead
|
||
jrst diegc1
|
||
move tt,unext(t)
|
||
movem tt,(a)
|
||
call mfree
|
||
jrst diegc2
|
||
|
||
; Channel management
|
||
|
||
.see chlock ; Lock on allocating channels
|
||
.scalar chbits ; Mask of free channels
|
||
|
||
; CALL CHALLOC: Allocate a channel
|
||
; Skips if it wins, else error code in TT
|
||
; T (val): channel in RH
|
||
challo: aose chlock
|
||
call hang
|
||
move t,chbits
|
||
jffo t,.+2
|
||
jrst [ setom chlock ? movei tt,%ebdch ? return ]
|
||
movni t,-35.(tt)
|
||
move tt,bit(t)
|
||
andcam tt,chbits
|
||
setzm buffer(t)
|
||
setzm bufsys(t)
|
||
setzm bufmkc(t)
|
||
setzm bufmark(t)
|
||
aos (p)
|
||
setom chlock
|
||
return
|
||
|
||
; CALL CHFREE: Free a channel
|
||
; T (arg): channel in RH
|
||
; TT preserved
|
||
chfree: syscall close,[movei (t)]
|
||
slose
|
||
move z,[-6,,[ sixbit /MSK2/ ? tdz bit(t)
|
||
sixbit /IFPIR/ ? tdz bit(t)
|
||
sixbit /DF2/ ? tdz bit(t) ]]
|
||
syscall usrvar,[movei %jself ? move z]
|
||
slose
|
||
save bit(t)
|
||
skipe t,buffer(t)
|
||
call mfree
|
||
rest t
|
||
iorm t,chbits
|
||
return
|
||
|
||
; .CALL INTON: Enable interrupts on a channel
|
||
; T (arg): channel in RH
|
||
inton: calblk usrvar,[movei %jself ? [sixbit /MSK2/] ? [0] ? [tdo bit(t)]]
|
||
|
||
; Memory Management
|
||
|
||
; CALL ALLOC: Allocate from a freelist
|
||
; skips unless it can't find a winner
|
||
; X (arg): addr of head of list
|
||
; Z (a/v): <size>,,0
|
||
; T (val): aobjn to block of required size
|
||
alloc1: camg z,(t) ; contains <size>,,<next>
|
||
jrst alloc2
|
||
movei x,(t) ; X: previous
|
||
alloc: hrrz t,(x) ; T: candidate block
|
||
jumpn t,alloc1 ; loop if non-nil
|
||
return
|
||
|
||
alloc2: aos (p) ; going to win
|
||
sub t,z ; T: aobjn to allocated block
|
||
move y,(t)
|
||
sub y,z ; Y: <new-size>,,<next>
|
||
tlnn y,-1 ; nothing left?
|
||
jrst alloc3 ; Yes: go remove block
|
||
movs tt,z ; No: thread up remainder
|
||
addi tt,(t)
|
||
movem y,(tt)
|
||
hrrm tt,(x)
|
||
return
|
||
|
||
alloc3: hrrm y,(x)
|
||
return
|
||
|
||
; CALL FREE: Return to a freelist
|
||
; X (arg): addr of head of list
|
||
; T (arg): aobjn to block to be freed
|
||
free.b: cail y,(t) ; Correct location for T?
|
||
jrst free.a
|
||
movei x,(y)
|
||
free: hrrz y,(x) ; Y: next block
|
||
jumpn y,free.b ; Loop if not end
|
||
free.a: hllz tt,t ; Splice T in: (X) -> (T) -> (Y)
|
||
movn tt,tt
|
||
hrri tt,(y)
|
||
movem tt,(t)
|
||
hrrm t,(x) ; done
|
||
hlrz tt,(x) ; Can (X) and (T) merge?
|
||
addi tt,(x)
|
||
cain tt,(t)
|
||
jrst [ hllz tt,(x) ; Yes: merge
|
||
add tt,(t)
|
||
movem tt,(x)
|
||
movei t,(x) ; T: so following will work
|
||
jrst .+1 ]
|
||
hlrz tt,(t) ; Can (T) and (Y) merge?
|
||
addi tt,(t)
|
||
caie tt,(y)
|
||
return ; No: return
|
||
hllz tt,(t) ; Yes: merge
|
||
add tt,(y)
|
||
movem tt,(t)
|
||
return
|
||
|
||
.see pglock ; Lock on page freelist
|
||
.scalar pghead ; Head of freelist
|
||
.vector pgtab(400) ; Freelist is threaded through PGTAB
|
||
|
||
; CALL PGINIT: Initialize pages
|
||
pginit: movsi t,400-freepg
|
||
movem t,pgtab+freepg
|
||
movei t,pgtab+freepg
|
||
movem t,pghead
|
||
setom pglock
|
||
return
|
||
|
||
; CALL PGALLOC: Allocate pages
|
||
; Skips if it wins, else error code in TT
|
||
; T (arg): # pages desired
|
||
; T (val): page aobjn to a block of that size
|
||
pgallo: movsi z,(t) ; Z: <size>,,0
|
||
movei x,pghead
|
||
aose pglock
|
||
call hang
|
||
call alloc
|
||
jrst [ setom pglock ? movei tt,%enacr ? return ]
|
||
subi t,pgtab
|
||
aos (p)
|
||
setom pglock
|
||
return
|
||
|
||
; CALL PGFREE: Free pages
|
||
; CALL PGFRE0: Free pages without NXMing them
|
||
; T (arg): page aobjn to block to be freed
|
||
pgfree: move tt,t
|
||
syscal corblk,[movei 0 ? movei %jself ? move tt]
|
||
slose
|
||
pgfre0: addi t,pgtab
|
||
movei x,pghead
|
||
aose pglock
|
||
call hang
|
||
call free
|
||
setom pglock
|
||
return
|
||
|
||
.see mlock ; Lock on memory freelist
|
||
.scalar mhead ; Head of freelist (in free memory)
|
||
|
||
; CALL MINIT: Initialize memory
|
||
minit: call pginit
|
||
movsi t,<freepg_12>-ffaddr
|
||
movem t,ffaddr
|
||
movei t,ffaddr
|
||
movem t,mhead
|
||
setom mlock
|
||
return
|
||
|
||
; CALL MALLOC: Allocate memory
|
||
; Skips if it wins, else error code in TT
|
||
; T (arg): # words desired
|
||
; T (val): aobjn to a block of that size
|
||
malloc: movsi z,(t) ; Z: <size>,,0
|
||
aose mlock
|
||
call hang
|
||
maloc2: movei x,mhead
|
||
call alloc
|
||
jrst maloc1
|
||
setom mlock
|
||
aos (p)
|
||
return
|
||
|
||
maloc1: save z
|
||
movs t,z
|
||
addi t,1777
|
||
lsh t,-12
|
||
call pgalloc
|
||
jrst [ rest z ? setom mlock ? return ] ; Propogate error code
|
||
move tt,t
|
||
syscall corblk,[movei %cbndr ? movei %jself ? move tt ? movei %jsnew]
|
||
slose
|
||
lsh t,12
|
||
movei x,mhead
|
||
call free ; (we already have MLOCK)
|
||
rest z
|
||
jrst maloc2
|
||
|
||
; CALL MFREE: Free memory
|
||
; T (arg): aobjn to block to be freed
|
||
; TT preserved
|
||
mfree: save tt
|
||
movei x,mhead
|
||
aose mlock
|
||
call hang
|
||
call free
|
||
setom mlock
|
||
rest tt
|
||
return
|
||
|
||
.vector keytab(lkeytab==:150.)
|
||
|
||
; CALL KINIT: Initialize keywords
|
||
; Clobbers A
|
||
kinit: skipn a,keys
|
||
return
|
||
kinit1: move t,kwdata(a)
|
||
call hash
|
||
movem x,kwhash(a)
|
||
movem y,kwlen(a)
|
||
skipe a,kwnext(a)
|
||
jrst kinit1
|
||
movei x,<lkeytab/2>
|
||
kinit2: caile x,lkeytab
|
||
.lose ; Barf! No mod works!
|
||
setzm keytab+0
|
||
move t,[keytab+0,,keytab+1]
|
||
blt t,keytab+lkeytab-1
|
||
move a,keys
|
||
kinit3: move t,kwhash(a)
|
||
idivi t,(x)
|
||
skipe keytab(tt)
|
||
aoja x,kinit2 ; Try a larger hashtable
|
||
movem a,keytab(tt)
|
||
skipe a,kwnext(a)
|
||
jrst kinit3
|
||
movem x,keymod ; Won, use this size
|
||
move x,keys
|
||
setzm keys
|
||
kinit4: setzi y,
|
||
exch y,kwnext(x)
|
||
skipe x,y
|
||
jrst kinit4
|
||
setom klock
|
||
return
|
||
|
||
; CALL HASH: Compute hash of data token
|
||
; T (arg): data token
|
||
; X (val): hash (>= 0)
|
||
; Y (val): length of data
|
||
hash: tlc t,400000
|
||
tlne t,700000
|
||
.lose ; better be data token
|
||
setzi x, ; X: accumulate hash
|
||
hlrz y,t ; Y: length
|
||
hlrz z,t ; Z: length
|
||
jumple z,cpopj
|
||
hrli t,440800
|
||
hashlp: ildb tt,t
|
||
addi x,17.(tt)
|
||
rot x,7
|
||
sojg z,hashlp
|
||
skipge x
|
||
setcm x,x
|
||
return
|
||
|
||
.see klock ; Lock on interning
|
||
|
||
; CALL INTERN: Intern a keyword
|
||
; Skips if it made a new token
|
||
; A (a/v): data token
|
||
; Z (val): keyword token
|
||
intern: call lookup
|
||
jumpn z,cpopj
|
||
aose klock
|
||
call hang
|
||
call looklp
|
||
jumpn z,kpopj
|
||
save x
|
||
save y
|
||
movei t,kwmax
|
||
call malloc
|
||
jsp t,lose
|
||
hrri z,(t)
|
||
hrli z,lskey
|
||
movem a,kwdata(z)
|
||
rest kwlen(z)
|
||
rest kwhash(z)
|
||
movei t,aerror
|
||
movem t,kwsubr(z)
|
||
move t,kwhash(z)
|
||
idiv t,keymod
|
||
move t,keytab(tt)
|
||
movem t,kwnext(z)
|
||
movem z,keytab(tt)
|
||
aos (p)
|
||
kpopj: setom klock
|
||
return
|
||
|
||
; CALL LOOKUP: Lookup a keyword
|
||
; A (a/v): data token
|
||
; X (val): hash (>= 0) if token not found
|
||
; Y (val): length of data if token not found
|
||
; Z (val): keyword token or 0 if token not found
|
||
lookup: move t,a
|
||
call hash ; X: hash (>= 0) Y: length
|
||
looklp: move t,x ; CALL LOOKLP
|
||
idiv t,keymod
|
||
skipn z,keytab(tt) ; Z: current keyword
|
||
return
|
||
lokup1: camn x,kwhash(z)
|
||
came y,kwlen(z)
|
||
jrst lokup2
|
||
jumple y,cpopj
|
||
save z
|
||
hrri x,(a)
|
||
hrli x,440800
|
||
hrr z,kwdata(z)
|
||
hrli z,440800
|
||
lokup5: ildb t,x
|
||
ildb tt,z
|
||
came tt,t
|
||
jrst lokup3
|
||
sojg y,lokup5
|
||
rest z
|
||
return
|
||
|
||
lokup3: rest z
|
||
move x,kwhash(z)
|
||
move y,kwlen(z)
|
||
lokup2: skipn z,kwnext(z)
|
||
return
|
||
jrst lokup1
|
||
|
||
; CALL UNCONS: Free object
|
||
; A (arg): Object
|
||
uncons: jumpge a,cpopj ; A number, return
|
||
tlne a,200000
|
||
jrst unlist ; A list
|
||
tlnn a,100000
|
||
jrst undata ; A data token
|
||
return ; Something else, return
|
||
|
||
undata: hlrz t,a
|
||
movei t,400003(t)
|
||
lsh t,-2
|
||
hrloi t,-1(t)
|
||
eqvi t,(a)
|
||
jumpl t,mfree
|
||
return ; Empty data token!
|
||
|
||
unlist: save a
|
||
save b
|
||
move b,a
|
||
|
||
unlst1: move a,(b)
|
||
call uncons
|
||
aobjn b,unlst1
|
||
|
||
rest b
|
||
rest t
|
||
jrst mfree
|
||
|
||
|
||
aerror: jfcl
|
||
.lose
|
||
|
||
defkey abort,"ABORT"
|
||
defkey chprops,"CHANGE-PROPERTIES"
|
||
defkey close,"CLOSE"
|
||
defkey complete,"COMPLETE"
|
||
defkey continue,"CONTINUE"
|
||
defkey crdirectory,"CREATE-DIRECTORY"
|
||
defkey crlink,"CREATE-LINK"
|
||
defkey dconn,"DATA-CONNECTION"
|
||
defkey delete,"DELETE"
|
||
defkey drout,"DIRECT-OUTPUT"
|
||
defkey directory,"DIRECTORY"
|
||
defkey disable,"DISABLE-CAPABILITIES"
|
||
defkey enable,"ENABLE-CAPABILITIES"
|
||
defkey expunge,"EXPUNGE"
|
||
defkey filepos,"FILEPOS"
|
||
defkey finish,"FINISH"
|
||
defkey hdirectory,"HOME-DIRECTORY"
|
||
defkey login,"LOGIN"
|
||
defkey mfplists,"MULTIPLE-FILE-PLISTS"
|
||
defkey open,"OPEN"
|
||
defkey properties,"PROPERTIES"
|
||
defkey read,"READ"
|
||
defkey rename,"RENAME"
|
||
defkey resynch,"RESYNCHRONIZE-DATA-CHANNEL"
|
||
defkey undconn,"UNDATA-CONNECTION"
|
||
|
||
; Constants
|
||
|
||
cnst0:
|
||
constants
|
||
repeat <.-cnst0+77>/100, conc cnst,\.rpcnt,=:cnst0+.rpcnt*100
|
||
|
||
bit: repeat 36., 1_.rpcnt
|
||
|
||
keys: .lkey. ; Cleared by KINIT
|
||
keymod: 0 ; Initialized by KINIT
|
||
|
||
forty: 0
|
||
0
|
||
-ltsint,,tsint
|
||
0
|
||
0
|
||
|
||
clktim: 3*60. ; 3 second clock
|
||
0 ? 0 ? 0 ; for .REALT
|
||
|
||
patch::
|
||
pat: block 100.
|
||
|
||
npure==:<.+1777>_-12 ; End of pure
|
||
timepg==:npure ; Then SYS page containing TIME variable
|
||
loc <timepg+1>_12 ; Then our variables
|
||
|
||
; Variables
|
||
|
||
variables
|
||
|
||
twenty: block 20
|
||
|
||
vpatch: block 100.
|
||
|
||
ffaddr:: -1 ; Make memory exist
|
||
|
||
freepg==:<.+1777>_-12 ; First free page
|
||
|
||
end go
|