1
0
mirror of https://github.com/PDP-10/its.git synced 2026-03-03 02:08:00 +00:00
Files
PDP-10.its/src/bawden/itsdev.102
Eric Swenson ad686dd778 Added ITSDEV.
Resolves #246.
2016-12-19 09:26:53 +01:00

714 lines
15 KiB
Plaintext
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; -*- Midas -*-
title ITSDEV - ITS device server.
a=:1
b=:2
c=:3
d=:4
cmd=:5
t=:6
tt=:7
t2=:10
e=:11 ; Error code
ip=:14 ; Input block pointer
op=:15 ; Output block pointer
bp=:16 ; Buffer pointer
p=:17
ch==:0,,-1
chneti==:1
chneto==:2
chdev==:3
%fr==:0,,525252
%fl==:1,,525252
%flin==:200000 ; Channel is open for input
%flout==:100000 ; Channel is open for output
%flnet==:040000 ; Network input interrupts clear this flag
%fltcp==:020000 ; Using TCP, not Chaos
%fllos==:010000 ; Losing...
call=:pushj p,
return=:popj p,
save==:push p,
rest==:pop p,
flose=:.lose %lsfil
slose=:.lose %lssys
quit=:call .
$quit: skipe debug
.break 16,100000
.logout 1,
define bltdup org,len
move tt,[<org>,,<org>+1]
blt tt,<org>+<len>-1
termin
define syscall name,args
.call [setz ? .1stwd sixbit /name/ ? args(400000)]
termin
define conc foo,bar
foo!bar!termin
popj1: aos (p)
cpopj: return
itsdvu"$$defs==:1 ; Just get the definitions
.insrt itsdvu >
.vector pdl(lpdl==:100.)
.scalar cname ; contact name
usrvar: sixbit /OPTION/ ? tlo %opint\%opopc
sixbit /MASK/ ? move [%pipdl\%piioc]
sixbit /MSK2/ ? movei 1_chneti
sixbit /HSNAME/ ? move cname
lusrvar==:.-usrvar
dbg: setom debug ; Debugging entry point
move 0,[chname sixbit /,/]
.value [asciz ""]
go: move p,[-lpdl,,pdl-1] ; Normal entry point
.close 1,
movem 0,cname
setzi 0, ; Clear flags
move t,[-lusrvar,,usrvar]
syscall usrvar,[movei %jself ? move t]
slose
move a,cname
camn a,[chname sixbit /,/]
jrst chago
camn a,[tcport sixbit /SYN,/]
jrst tcpgo
.value
gogo: call brecv
jrst @cmdtab(cmd)
cmdtab: offset -.
opdata:: adata
oplast:: alast
opread:: aread
opsync:: async
opopen:: aopen
opclose:: aclose
opscall:: ascall
opxcall:: axcall
oplink:: alink
opmax:: offset 0
repeat 20-<.-cmdtab>, aerror
axcall: move c,6(bp)
camn c,[sixbit /MTAPE/]
jrst amtape
camn c,[sixbit /LOGIN/]
jrst alogin
camn c,[sixbit /RENAME/]
jrst a6call
jrst rerr+%enscl
lbuffer==:2000
.vector buffer(lbuffer+6) ; I/O buffer, with room for extra block
adata: tlne %flout
jrst adata0
movei d,%ensio
movei c,7 ; 7 words (the first!) not written
jrst adata5 ; Go wait for the OPLAST
alast: tlne %flout
jrst alast0
move c,6(bp)
jrst rerr+%ensio
adata0: movei bp,7(bp)
cail bp,buffer+lbuffer ; Still room?
jrst adata1
adata2: call recv
ifn opdata, .err OPDATA must be 0
jumpe cmd,adata0
caie cmd,oplast
jsp t,rlose
alast0: move c,6(bp)
cail c,0
caile c,6
jsp t,rlose
addi bp,(c)
hrloi t,-1-buffer(bp)
eqvi t,buffer
jumpge t,rvals ; Don't trust your luck...
syscall iot,[moves e ? movei chdev ? move t]
caia
jrst rvals
movei c,(bp)
subi c,(t) ; C: # words not written
jrst rerror
adata1: move t,[-lbuffer,,buffer]
syscall iot,[moves d ? movei chdev ? move t]
jrst adata3
movei bp,-lbuffer(bp)
move t,[buffer+lbuffer,,buffer]
blt t,buffer+5 ; Copy down 6 words
jrst adata2
adata3: movei c,(bp)
subi c,(t) ; C: # words not written
jrst adata5
adata4: addi c,7 ; 7 more words not written
adata5: ;; Error code is in D to avoid clobberage.
call brecv
ifn opdata, .err OPDATA must be 0
jumpe cmd,adata4
caie cmd,oplast
jsp t,rlose
add c,6(bp) ; last 0 to 6 words not written
move e,d
jrst rerror
aread: tlnn %flin
jrst rerr+%ensio
skipge d,0(bp) ; D: # words requested
jrst rerr+%ebdrg
movei c,buffer ; C: -> BUFFER
jrst aread1
aread2: movei cmd,rsdata
call send
movei a,7(a)
aread0: caig a,-7(c)
jrst aread2
hrli t,(a)
hrri t,buffer
blt t,buffer+5 ; Copy down 6 words
subi c,-buffer(a)
aread1: movei a,buffer
jumple d,aread7 ; If no more wanted, flush buffer
movei t,lbuffer
camle t,d
movei t,(d)
subi d,(t)
movni t,(t)
hrli c,(t)
syscall iot,[moves e ? movei chdev ? move c]
jrst aread5
jumpge c,aread0 ; Usual case, go empty buffer
tdza d,d ; EOF => D = 0
aread5: setoi d, ; Error => D = -1
jrst aread0 ; Don't ask for more after emptying buffer
aread7: movei t,-buffer(c)
movem t,buffer+6
movei cmd,rslast
call send
jumpe d,rvals
jrst rerror
ascall: hlrz t,5(bp)
trze t,400000
jrst ancall
caige t,1
jrst rerr+%etfrg
caile t,6
jrst rerr+%etmrg
xscall: xct ascltb-1(t)
jrst rerror
jrst rvals
ascltb:
repeat 6,[
syscall call,[moves e ? move 6(bp) ? movei chdev
repeat .rpcnt, move .rpcnt(bp)
repeat 7, movem vals+.rpcnt
movs 5(bp)]
] ; repeat 6
ancall: caige t,0
jrst rerr+%etfrg
caile t,5
jrst rerr+%etmrg
xncall: xct ancltb(t)
jrst rerror
jrst rvals
ancltb:
repeat 6,[
syscall call,[moves e ? move 6(bp)
repeat .rpcnt, move .rpcnt(bp)
repeat 7, movem vals+.rpcnt
movs 5(bp)]
] ; repeat 6
a6call: syscall call,[moves e ? move 6(bp)
move 0(bp) ? move 1(bp) ? move 2(bp) ? move 3(bp)
move 4(bp) ? move 5(bp)]
jrst rerror
jrst rvals
amtape: tlnn %flin\%flout
jrst rerr+%ensch
movei a,0(bp)
hrli a,chdev
xmtape: .mtape a,
jrst rerr+0 ; Non-specific error
movem a,vals+0
jrst rvals
xerror: hlrzm c,e
movei t,rerror
movem t,intpc(p)
.call dismis
slose
aerror: jsp t,rlose
aopen: tlz %flin\%flout ; Now closed
hrrz t,4(bp)
trnn t,%doblk ; Block mode only
jrst rerr+%ensmd
syscall open,[moves e ? movsi (t) ? movei chdev
move 0(bp) ? move 1(bp) ? move 2(bp) ? move 3(bp)]
jrst rerror
tlo %flin ; Now open for input
trne t,%doout
tlc %flin#%flout ; Make that output
jrst rvals
aclose: tlz %flin\%flout
.close chdev,
jrst rvals
alink: syscall mlink,[moves e
move 0(bp) ? move 1(bp) ? move 2(bp) ? move 3(bp)
move 4(bp) ? move 5(bp) ? move 6(bp)]
jrst rerror
jrst rvals
async: movei a,0(bp)
movei cmd,rssync
call send
call flush
call brecv
jrst @cmdtab(cmd)
xuvars: sixbit /XUNAME/ ? move 0(bp)
sixbit /XJNAME/ ? move 1(bp)
sixbit /SNAME/ ? move 2(bp)
sixbit /HSNAME/ ? move cname
lxuvars==:.-xuvars
.scalar clvers,clprog ; Save client version and program name
alogin: move t,4(bp)
movem t,clvers
move t,5(bp)
movem t,clprog
move c,0(bp)
syscall login,[moves e ? move c ? move 3(bp) ? move 0(bp)]
jsp t,alogn7
movem c,vals+0
move c,1(bp)
syscall usrvar,[moves e ? movei %jself ? [sixbit /JNAME/] ? move c]
jsp t,alogn7
movem c,vals+1
move t,[-lxuvars,,xuvars]
syscall usrvar,[movei %jself ? move t]
slose
move t,[.fvers]
movem t,vals+2
jrst rvals
alogn7: cain e,%eexfl
aoja c,-2(t)
skipe debug ; If you are debugging
jrst (t) ; Then don't sweat the small stuff
jrst rerror
.vector vals(7)
; JRST RVALS to return values in VALS.
rvals: movei cmd,rsvals
call vsend
call brecv
jrst @cmdtab(cmd)
; JRST RERR+<n> to report error <n>.
rerr:
repeat 100, jsp e,rerr1
; JRST RERROR to report error in E.
rerr1: movei e,-<rerr+1>(e)
rerror: move a,[c,,vals] ; Send him:
blt a,vals+6 ; C, D, CMD, T, TT, T2, E
movei cmd,rserror
call vsend
call flush ; Make sure he gets it as soon as possible.
call brecv
jrst @cmdtab(cmd)
; JSP T,RLOSE to report fatal lossage.
rlose: tloe %fllos
.lose ; Don't lose recursively
move a,[c,,vals] ; Send him:
blt a,vals+6 ; C, D, CMD, T, TT, T2, E
movei cmd,rslose
call vsend
call flush
syscall finish,[movei chneto]
jfcl
quit
.scalar incnt ; Number of blocks available for reading
; CALL RECV: Receive next 7 words
; BP (a/v): Address of 7 word input block
; CMD (val): 4 bit opcode
brecv: movei bp,buffer
recv: sosge incnt
jrst recv1 ; No blocks available, get more
move cmd,0(ip)
lsh cmd,-4
repeat 7,[
move t,.rpcnt+1(ip)
lshc cmd,-4
movem t,.rpcnt(bp)
] ; repeat 7
movei ip,8(ip)
return
recv2: skiple incnt
jrst recv
call flush ; Don't hang with buffered output
tlne %flnet
.hang
recv1: save [recv2]
tlnn %fltcp
jrst chaget
jrst tcpget
.scalar outcnt
; CALL SEND: Send next 7 words
; CMD (arg): 4 bit opcode
; A (a/v): Address of 7 word output block
vsend: movei a,vals
send:
repeat 7,[
move t,6-.rpcnt(a)
lshc cmd,4
movem t,7-.rpcnt(op)
] ; repeat 7
lsh cmd,4
movem cmd,0(op)
movei op,8(op)
sosle outcnt
return
flush: tlnn %fltcp ; No more blocks empty, send them off
jrst chafls
jrst tcpfls
; Network
.insrt dsk:syseng;chsdef >
chacnt==:<%cpmxw-%cpkdt>/8 ; Chaos: a packet's worth of blocks
tcpcnt==:160. ; TCP: 160. blocks
.see maxcnt ; A look at the ITS TCP code suggests that it might offer
; as much as 4820. bytes (150. blocks). Experiments with
; MAXCNT have seen TCP offer as much as 4768. bytes (149.
; blocks), but usually when things get backed up it offers
; 3856. (120. blocks). (Note that a disk block of 2000
; words fits in 147. blocks.)
; Make buffers big enough for both:
ifge <tcpcnt*8>-%cpmxw, liobuf==:<tcpcnt*8>
.else, liobuf==:%cpmxw
.vector inbuf(liobuf)
.vector outbuf(liobuf)
; Chaosnet
$cpknx==:$cpknb+<050000,,0>-<000500,,0>
ifn $cpkop&777777, .err $CPKOP not in first word?
ifn $cpknb&777777, .err $CPKNB not in first word?
define cpkop (op,nb)
<.dpb <op>,$cpkop,<.dpb <nb>,$cpknb,0>>!termin
pktin: setz ? sixbit /PKTIOT/
moves e
movei chneti
setzi inbuf
pktout: setz ? sixbit /PKTIOT/
moves e
movei chneto
setzi outbuf
chago: syscall chaoso,[movei chneti ? movei chneto ? movei 15]
slose
move t,[cpkop %colsn,<chname .length ",">]
movem t,outbuf+0
move t,[440700,,[chname asciz ","]]
move tt,[440800,,outbuf+%cpkdt]
chago1: ildb t2,t
idpb t2,tt
jumpn t2,chago1
.call pktout
slose
movei tt,5*30. ; 5 seconds
skipe debug
movei tt,60.*60.*30. ; 1 hour
syscall netblk,[movei chneti ? movei %cslsn ? move tt ? movem t]
slose
caie t,%csrfc
quit
.call pktin
slose
move t,[cpkop %coopn,0]
movem t,outbuf+0
.call pktout
slose
move t,[cpkop %codat,0]
movem t,outbuf+0
call chflsi
call chgeti
jrst gogo
chafls: movei t,chacnt
sub t,outcnt
jumpe t,cpopj
dpb t,[$cpknx outbuf]
.call pktout
jrst chalos
chflsi: movei t,chacnt
movem t,outcnt
movei op,outbuf+%cpkdt
return
chalos: caie e,12000 ; CHNL IN ILLEGAL MODE ON IOT?
.lose ; No, can't JSP T,RLOSE
quit ; Yes, well it can happen...
.scalar pktcnt ; # packets we know system has for us
chgeti: setzm incnt
setzm pktcnt
return
chagt1: tlo %flnet
syscall whyint,[movei chneti ? movem tt ? movem tt ? movem t]
slose
caie tt,%csopn
quit ; Not open? He must not care what we do!
tlnn t,-1
return
hlrzm t,pktcnt
chaget: sosge pktcnt
jrst chagt1
.call pktin
jsp t,rlose
ldb tt,[$cpkop inbuf]
ldb t2,[$cpknb inbuf]
caie tt,%cocls ; Close (should be impossible)
cain tt,%coeof ; or EOF
quit ; then quit
cain tt,%codat
trne t2,37
jsp t,rlose
lsh t2,-5
jumple t2,chaget
movem t2,incnt
movei ip,inbuf+%cpkdt
return
; TCP
tcpcnt==:liobuf/8
tcpgo: tlo %fltcp
syscall tcpopn,[movei chneti ? movei chneto
movei <tcport 0,> ? [-1] ? [-1]]
slose
movei tt,5*30. ; 5 seconds
skipe debug
movei tt,60.*60.*30. ; 1 hour
movei t,%ntlsn
tcpgo1: syscall netblk,[movei chneto ? move t ? move tt
movem t ? movem tt]
slose
skipg tt
quit ; Time ran out
cain t,%ntsyr
jrst tcpgo1 ; Can pass though %NTSYR
caie t,%ntopn
cain t,%ntwrt
caia
quit ; Wrong state
call tpflsi
call tpgeti
jrst gogo
tcpfls: movei t,tcpcnt
sub t,outcnt
jumpe t,cpopj
lsh t,5
move tt,[440800,,outbuf]
syscall siot,[moves e ? movei chneto ? move tt ? move t]
.lose ; Can't JSP T,RLOSE
syscall force,[moves e ? movei chneto]
.lose ; Can't JSP T,RLOSE
tpflsi: movei t,tcpcnt
movem t,outcnt
movei op,outbuf
return
; TCP input is coded in this crazy way because there is a bug in WHYINT
; such that you can't trust the results of a WHYINT unless you have read
; every last byte that it told you about the last time you called it.
.scalar bytcnt ; # unread bytes still in system
.scalar maxcnt ; Largest BYTCNT ever seen (for debugging)
.scalar xtrabc ; # bytes at start of INBUF
.scalar xtrabp ; BP to space in INBUF
tpgeti: setzm incnt
setzm bytcnt
setzm maxcnt
jrst tcpgtj
tcpgtx: ;; Screw case: Before we can call WHYINT, we must pull the last
;; few extra bytes out of the system into the beginning of the
;; INBUF. (Note how the SIOT below advances XTRABP and clears
;; BYTCNT correctly!)
movem t,xtrabc
syscall siot,[moves e ? movei chneti ? move xtrabp ? move bytcnt]
jsp t,rlose
tcpgt1: tlo %flnet
syscall whyint,[movei chneti ? movem tt ? movem tt ? movem t]
slose
caie tt,%ntinp ; Input available?
jrst [ caie tt,%ntopn ; No, still open?
quit ; No. Then he must not care what we do!
return ]
movem t,bytcnt
camle t,maxcnt
movem t,maxcnt
tcpget: skipg t,bytcnt
jrst tcpgt1
add t,xtrabc ; T: # bytes available
caige t,40
jrst tcpgtx ; Not enough for a block, screw case
lsh t,-5 ; T: # blocks available
caile t,tcpcnt
movei t,tcpcnt
movem t,incnt ; INCNT: # blocks that will fit this time
lsh t,5
sub t,xtrabc ; T: # bytes we have to read to get there
movni tt,(t)
addm tt,bytcnt ; BYTCNT: # bytes that will leave in system
syscall siot,[moves e ? movei chneti ? move xtrabp ? move t]
jsp t,rlose
movei ip,inbuf
tcpgtj: move t,[440800,,inbuf]
movem t,xtrabp ; When he comes back, there won't
setzm xtrabc ; be any extra.
return
; Interrupts
intsv0==:a ; Save A
intsv9==:t2 ; Through T2
intsvn==:intsv9+1-intsv0
intctl==:400000+intsv0_6+intsvn ; control bits
intpc==:-<3+intsvn> ; INTPC(P) is PC before interrupt.
intdf1==:intpc-2 ; INTDF1(P) is .DF1 before interrupt.
intdf2==:intpc-1 ; INTDF2(P) is .DF2 before interrupt.
intrq1==:intpc-4 ; INTRQ1(P) are first word bits.
intrq2==:intpc-3 ; INTRQ2(P) are second word bits.
intac0==:intpc+1-intsv0 ; INTAC0+C(P) is C before interrupt.
tsint:
loc 42
-ltsint,,tsint
loc tsint
intctl,,p
%piioc ? 0 ? -1 ? -1 ? iocint
0 ? 1_chneti ? 0 ? 1_chneti ? netint
ltsint==:.-tsint
dismis: setz ? sixbit /DISMIS/ ? movsi intctl ? setz p
; JRST LOSINT to dismis interrupt and do .LOSE at interrupting PC.
losint: move t,intrq1(p)
jffo t,.+2
caia ; So just .LOSE 0
addi tt,1
hrl tt,intpc(p)
syscall dismis,[movsi intctl ? move p ? move intpc(p)
move intdf1(p) ? move intdf2(p) ? move tt]
slose
; Network input interrupts just clear %FLNET.
netint: tlz %flnet
.call dismis
slose
; IOC interrupt of .CALL with error return argument just fails to skip and
; returns with IOC error number as error code.
iocint: hrrz a,intpc(p)
.suset [.rbchn,,b]
syscall status,[movei (b) ? movem c]
slose
tlnn c,-100 ; Better not look like standard error
.lose
caie a,xscall ; Special cases for various reasons
cain a,xmtape
jrst xerror
cain a,xncall
jrst xerror
move b,(a)
tlc b,(.call)
movsi t,(setz)
tlnn b,-1 ; Better be .CALL [ SETZ ? ... ]
came t,(b)
jrst losint
iocnt2: move t,2(b)
tlc t,%clerr
tlne t,(7^9 @(17)) ; Unindexed, direct, error return?
aoja b,iocnt1 ; Nope, keep looking
movei t,(t)
caig t,intsv9
caige t,intsv0
caia
addi t,intac0(p) ; Some locations are on the stack now
hlrzm c,(t)
aos intpc(p)
.call dismis
slose
iocnt1: jumpge t,iocnt2
jrst losint
cnst0: ; Today's weird idea...
constants
repeat <.-cnst0+77>/100, conc cnst,\.rpcnt,=:cnst0+.rpcnt*100
variables
debug: 0
patch::
pat: block 100.
epatch: -1 ; Make memory exist, end of patch area
end go