rADs v;v"M BG@&I#.#BI60"I0W$[psI'0&I(dG$`&l MNh"d:sS<,\&BS>(X7r'BYC "A;[(B[n $\Gu&B]U$eLBᒜ$`n&# g2Mi $L$|-BgBMmy$`|B򅭴IhXgS,i@&P + T E iX0D&{Bi&lP,\MsbBiJm(I$p.@.xiJmEV>0KnPiJmysMBiJmi>0MS$` "siJ}K.e@[Sqk#.xiSH$IvY3sjI]?$\`B]P ; -*-PALX-*- ; This module manages the asynchronous lines connected to the system. ; The following asynchronous line interfaces are supported: ; KL11 single line interface ; DL11 single line interface ; DZ11 8-line interface ; Each line is assinged a number. The system console (i.e. the KL11 at ; 177560) is always line 0. The KL11s have the next NKL line no.s, the ; DL11s the next NDL, and finally the DZ11 lines occupy the last NDZ*8 ; line no.s. ; For the MIT system the line no.s are currently as follows: ; Line no. Interface Connection ; 0 KL11(DL11-W) LA120 (system console) ; 1 DL11-E not used (autodialer??) ; 2 DL11-E Tektronix 4662 plotter ; 3 DL11-E HP2645A ; 4 DL11-E Vadic 305 modem (dialup 300) ; 3 DZ11,0 StupidBee ; 4 DZ11,1 VT52 #1 ; 5 DZ11,2 VT52 #2 ; 6 DZ11,3 VT52 #3 ; 7 DZ11,4 HP2645A ; 8 DZ11,5 Vadic 3415 modem (dialup 1200) ; 9 DZ11,6 Line to HP3000 ; 10 DZ11,7 MC-ITS 9600 baud line ; New DL11-E assignments ; Address Vector Speed Options ; 175610 310 1200 No parity,8 bits,1 Stop bit ; 175620 320 1200 Even parity,7 bits,1 Stop bit(Plotter) ; 175630 330 300 No parity,8 bits,1 Stop bit ; 175640 340 4800 No parity,8 bits,1 Stop bit ; For Steve Orszag's system the line no.s are currently as follows: ; Line no. Interface Connection ; 0 DLV11-J VT52 (system console) ; 1 DLV11-J Vadic 305 modem (300 baud) ; 2 DLV11-J Vadic 3415 modem (1200 baud) ; 3 DLV11-J Spinwriter printer ; 4 DLV11 LA180 .sbttl Definitions ; Define queue structure dsect < qsize:: .blkw 1 ; size of queue buffer qbp:: .blkw 1 ; ptr to buffer used as queue qfp:: .blkw 1 ; ptr to front of queue qrp:: .blkw 1 ; ptr to rear of queue qcc:: .blkw 1 ; room left in queue >,lq ; Define asynchronous line data structure dsect < .blkw 1 ; device address aldres:: .blkb 1 ; nonzero if device responding almode:: .blkb 1 ; holds flags for flow control purposes .even alib:: .blkw 1 ; bit to turn on interrupts for line aliq:: .blkb lq ; input queue aloq:: .blkb lq ; output queue >,laltab %alxon==1 ; this bit nonzero in ALMODE means DEC flow ; control protocol enabled for that line %alstp==2 ; this bit nonzero in ALMODE means we've just ; received a ^S and holding output till ^Q nal==1+nkl+ndl+ ; no. of asynchronous lines .macro al addr,ibit .word addr ; device address, .word 0 ; device responding flag .word ibit ; interrupt bit for line .blkb lq ; input queue .blkb lq ; output queue .endm altab: al 177560,100 ; Console terminal ifMIT < .rept nkl al 176500+<.rpcnt*10>,100 ; KL11s .endr > ifSAO < .rept nkl al 177500+<.rpcnt*10>,100 ; KL11s .endr > .rept ndl al 175610+<.rpcnt*10>,100 ; DL11s .endr .rept ndz al dzaddr+<.rpcnt*10>,1 al dzaddr+<.rpcnt*10>,2 al dzaddr+<.rpcnt*10>,4 al dzaddr+<.rpcnt*10>,10 al dzaddr+<.rpcnt*10>,20 al dzaddr+<.rpcnt*10>,40 al dzaddr+<.rpcnt*10>,100 al dzaddr+<.rpcnt*10>,200 .endr altabp: .blkw nal ; array of ptrs to LTAB entries .sbttl Initialization ; Line no. assignments klno==1 dlno==klno+nkl dzno==dlno+ndl ; DINIT is called to initialize the asynchronous lines at system startup. dinit: jsr r5,vecset ; setup console terminal interrupt vector .word 60,pr4+0,dlrint-.,dlxint-. .rept nkl jsr r5,vecset ; setup KL11 interrupt vector .word klv+<.rpcnt*10>,pr4+klno+.rpcnt,dlrint-.,dlxint-. .endr .rept ndl jsr r5,vecset ; setup DL11 interrupt vector .word dlv+<.rpcnt*10>,pr4+dlno+.rpcnt,dlrint-.,dlxint-. .endr .if ne ndz jsr r5,vecset ; setup DZ11 interrupt vector .word dzv,pr5,dzrint-.,dzxint-. clr dzric+0 clr dzric+2 clr dzrcc+0 clr dzrcc+2 clr dzxic+0 clr dzxic+2 clr dzxcc+0 clr dzxcc+2 .endc ; DINIT now falls into DPWRU which turns on interrupt enable bits. ; DPWRU is called when power returns after a power fail. It restarts i/o ; on the asynchronous lines. dpwru: mov pc,r1 ; ptr to ALTAB add #altab-.,r1 ; ... mov #nal,r0 ; no. of lines loop < clrb aldres(r1) ; clear device responding flag word add #laltab,r1 ; move ptr to next entry sorl r0 > mov #100,@#tks ; turn on console terminal interrupts mov #100,@#tps ; ... movb #1,altab+aldres ; indicate device responding .if ne nkl call catchn ; setup NXM catch for testing KLs 1$-. ifMIT < .rept nkl mov #100,@#176500+<.rpcnt*10> ; turn on KL interrupts mov #100,@#176504+<.rpcnt*10> ; ... movb #1,altab+aldres+<*laltab> .endr > ifSAO < .rept nkl mov #100,@#177500+<.rpcnt*10> ; turn on KL interrupts mov #100,@#177504+<.rpcnt*10> ; ... movb #1,altab+aldres+<*laltab> .endr > 1$: .endc .if ne ndl call catchn ; setup NXM catch for testing DLs 2$-. .rept ndl mov #106,@#175610+<.rpcnt*10> ; turn on DL interrupts mov #100,@#175614+<.rpcnt*10> ; ... movb #1,altab+aldres+<*laltab> .endr 2$: .endc .if ne ndz call catchn ; setup NXM catch for testing DZ 3$-. mov #dzaddr,r4 ; address of DZ11 mov #20,(r4) ; initialize clr nxmcat ; we have a DZ11, clear NXM catch loop < bit #20,(r4) ; wait for init to finish rptl ne > mov pc,r3 ; ptr to list of line parameters add #dzlpar-.,r3 ; ... mov #8.,r0 ; no. of DZ11 lines mov pc,r2 ; ptr to ALTAB+ALDRES for 1st DZ line add #altab+aldres+-.,r2 loop < movb #1,(r2) ; set line responding flag add #laltab,r2 ; advance ptr mov (r3)+,2(r4) ; setup line parameter register sorl r0 > mov #177777,4(r4) ; set all data term rdys, set all line enabs mov #040140,(r4) ; turn on ints, start DZ11 3$: .endc return ; VECSET is used to initialize interrupt vectors. vecset: mov (r5)+,r1 ; pick up vector address mov (r5)+,r2 ; pick up priority mov r5,(r1) ; set rcvr interrupt vector PC add (r5)+,(r1)+ ; ... mov r2,(r1)+ ; set rcvr interrupt vector PS mov r5,(r1) ; set xmit interrupt vector PC add (r5)+,(r1)+ ; ... mov r2,(r1)+ ; set xmit interrupt vector PS rts r5 .sbttl Asynchronous Line i/o ; ALINIT initializes the asynchronous line module. alinit: mov pc,r1 ; ptr to line table add #altab-.,r1 ; ... mov #nal,r0 ; no. of lines mov pc,r3 ; ptr to line table ptrs add #altabp-.,r3 ; ... loop < mov r1,(r3)+ ; save ptr to line table entry push #20./2,#60. ; ALOCW args: size, timeout call alocw ; allocate input buffer pop r2,* ; vals: ptr, error code bne 1$ clrb aldres(r2) ; clear line responding flag clrb almode(r2) ; clear other mode bits push r2,#20.,r1 ; QINIT args: buf size, buf ptr, queue ptr add #aliq,(sp) ; ... jsr r5,qinit ; initialize input queue push #60./2,#60. ; ALOCW args: size, timeout call alocw ; allocate output buffer pop r2,* ; vals: ptr, error code bne 1$ push r2,#60.,r1 ; QINIT args: buf size, buf ptr, queue ptr add #aloq,(sp) ; ... jsr r5,qinit ; initialize output queue add #laltab,r1 ; move to next line sorl r0 > return 1$: crash ^"No memory" ; ALREBF reallocates the buffers for asynchronous line input and output ; queues. This allows the no. of characters buffered to be dynamically ; changed. ; ARGS: VALS: ; SP -> output buffer size SP -> error code ; input buffer size ; line no. alrebf: push r1,r2 ; save regs mfps -(sp) ; save priority mov 14(sp),r1 ; get line no. argument asl r1 ; times two for indexing add pc,r1 ; for PICness mov altabp-.(r1),r1 ; get ptr to ALTAB entry cmp 12(sp),aliq+qsize(r1) ; already right size? if ne,< ; no, reallocate push aliq+qbp(r1) ; save ptr to old queue buffer push 12+2(sp) ; ALOCW arg1: size inc (sp) ; increment so odd sizes will win asr (sp) ; divide by two to get no. of words push #60. ; ALOCW arg2: timeout call alocw ; allocate input buffer pop r2,14+2(sp) ; vals: ptr, error code bne 2$ push r2,12+4(sp),r1 ; QINIT args: buf ptr, buf size, queue ptr add #aliq,(sp) ; ... spl 5 ; set high priority while redoing queue jsr r5,qinit ; initialize input queue mtps 2(sp) ; restore priority call free ; free old input queue buffer (arg on stack) > cmp 10(sp),aloq+qsize(r1) ; already the right size? if ne,< push aloq+qbp(r1) ; save ptr to old queue buffer push 10+2(sp) ; ALOCW arg1: size inc (sp) ; increment so odd sizes will win asr (sp) ; divide by two to get no. of words push #60. ; ALOCW arg2: timeout call alocw ; allocate output buffer pop r2,14+2(sp) ; vals: ptr, error code bne 2$ push r2,10+4(sp),r1 ; QINIT args: buf size, buf ptr, queue ptr add #aloq,(sp) ; ... spl 5 ; set high priority while redoing queue jsr r5,qinit ; initialize input queue mtps 2(sp) ; restore priority call free ; free old input queue buffer > 1$: mtps (sp)+ ; restore priority pop r2,r1 ; restore regs pop (sp),(sp) ; remove two args from the stack return 2$: pop * ; remove thing from stack br 1$ ; ALSMOD sets the modes of an asynchronous line ; ARGS: VALS: ; SP -> modes (as in ALMODE) none ; line no. alsmod: push r1 ; save reg mov 6(sp),r1 ; get line no. asl r1 ; times two for indexing add pc,r1 ; convert to offset into line table entry ptr mov altabp-.(r1),r1 ; ... movb 4(sp),almode(r1) ; set the modes pop r1,(sp),(sp) ; restore regs, remove args return ; ALGMOD gets the modes of an asynchronous line ; ARGS: VALS: ; SP -> line no. SP -> modes (as in ALMODE) algmod: push r1 ; save reg mov 4(sp),r1 ; get line no. asl r1 ; times two for indexing add pc,r1 ; convert to offset into line table entry ptr mov altabp-.(r1),r1 ; ... movb almode(r1),4(sp) ; get modes pop r1 ; restore reg return ; ALREAD reads a character from the input queue of an asynchronous line. ; REMW is first called to wait until a character is really in the queue. ; ARGS: VALS: ; SP -> timeout SP -> character ; line no. error code alread: push r1 ; save reg mov 6(sp),r1 ; get line no. asl r1 ; times two for indexing add pc,r1 ; convert to offset into line table entry ptr mov altabp-.(r1),r1 ; ... add #aliq,r1 ; make ptr to input queue structure push r1,4+2(sp) ; REMW args: queue ptr, timeout jsr r5,remw ; wait for a character to arrive in queue pop 6(sp) ; val: error code, pass it along if eq,< push r1 ; REMQ arg: queue ptr jsr r5,remq ; remove character from queue pop 4+2(sp),6(sp) ; vals: character, error code > pop r1 ; restore reg return .if ne asmpek ; ALPEEK reads a character from the input queue of an asynchronous line. ; but doesn't remove it from the input queue. ; REMW is first called to wait until a character is really in the queue. ; ARGS: VALS: ; SP -> timeout SP -> character ; line no. error code alpeek: push r1 ; save reg mov 6(sp),r1 ; get line no. asl r1 ; times two for indexing add pc,r1 ; convert to offset into line table entry ptr mov altabp-.(r1),r1 ; ... add #aliq,r1 ; make ptr to input queue structure push r1,4+2(sp) ; REMW args: queue ptr, timeout jsr r5,remw ; wait for a character to arrive in queue pop 6(sp) ; val: error code, pass it along if eq,< push r1 ; REMQ arg: queue ptr jsr r5,peekq ; remove character from queue pop 4+2(sp),6(sp) ; vals: character, error code > pop r1 ; restore reg return .endc ; ALWRIT outputs a character to an asynchronous line by inserting it in ; its output queue. If necessary ALWRIT waits until there is room in the ; queue. ; ARGS: VALS: ; SP -> character (none) ; line no. alwrit: push r1,r4 ; save regs mov 10(sp),r1 ; get line no. asl r1 ; times two for indexing add pc,r1 ; convert line no. to line table entry ptr mov altabp-.(r1),r1 ; ... tstb aldres(r1) ; device responding? if ne,< mov (r1),r4 ; device address add #aloq,r1 ; make ptr to output queue structure push #1,r1 ; ROOMQ args: no. of characters, queue ptr jsr r5,roomq ; wait for room for 1 character push 6(sp),r1 ; INSQ args: character, queue ptr jsr r5,insq ; insert character into queue pop * ; val: error code sub #aloq,r1 ; restore ptr to line table entry bisb alib(r1),4(r4) ; turn on this line's interrupt enable > pop r4,r1,(sp),(sp) ; restore regs, remove args from stack return ; ALWRIZ outputs an ASCIZ string to the specified line no. The ASCIZ ; string is specified by a relative ptr following the call. ; ARGS: VALS: ; SP -> line no. (none) alwriz: push r1 ; save reg mov r5,r1 ; pick up relative ptr after call add (r5)+,r1 ; make absolute ptr loop < tstb (r1) ; end of string? exitl eq push 4(sp) ; ALWRIT arg1: line no. movb (r1)+,-(sp) ; ALWRIT arg2: character jsr r5,alwrit ; send this character rptl > pop r1,(sp) ; restore reg, remove arg from stack return .sbttl DZ interrupt handlers .if ne ndz ; DZ11 receiver interrupt handler. dzrint: push r0,r1,r4 ; save regs add #1,dzric+0 ; increment DZ11 receiver interrupt count adc dzric+2 ; ... mov #dzaddr,r4 ; address of DZ11 loop < mov 2(r4),r0 ; read RBUF exitl pl ; character not valid, stop reading silo inc intflg ; indicate interrupt has occured add #1,dzrcc+0 ; increment DZ11 receiver character count adc dzrcc+2 ; ... mov r0,r1 ; get DZ11 line no. swab r1 ; ... bic #177770,r1 ; ... add #dzno,r1 ; convert to Trantor line no. bic #103400,r0 ; clear data valid bit and line no. jsr r5,alrint ; call general rcvr interrupt routine rptl > pop r4,r1,r0 ; restore regs rti dzric: .word 0,0 ; no. of DZ11 receiver interrupts dzrcc: .word 0,0 ; no. of DZ11 receiver characters ; DZ11 transmitter interrupt handler. dzxint: push r1,r4 ; save regs add #1,dzxic+0 ; increment DZ11 transmitter interrupts adc dzxic+2 ; ... mov #dzaddr,r4 ; address of DZ11 loop < mov (r4),r1 ; read CSR exitl pl ; no more lines ready inc intflg ; indicate interrupt has occured add #1,dzxcc+0 ; increment DZ11 transmitter character count adc dzxcc+2 ; ... swab r1 ; get DZ11 line no. bic #177770,r1 ; ... add #dzno,r1 ; convert to Trantor line no. jsr r5,alxint ; call general xmtr interrupt routine rptl > pop r4,r1 ; restore regs rti dzxic: .word 0,0 ; no. of DZ11 receiver interrupts dzxcc: .word 0,0 ; no. of DZ11 receiver characters .endc ; ne ndz .sbttl KL and DL interrupt handlers .if ne nkl+ndl ; KL11 and DL11 receiver interrupt handler. dlrint: mfps -(sp) ; save PS to extract unit no. push r0,r1,r4 ; save regs inc intflg ; indicate interrupt has occurred mov 6(sp),r1 ; get PS (unit no. in cc bits) bic #177760,r1 ; ... mov r1,r4 ; copy asl r4 ; times two for indexing add pc,r4 ; for PICness mov @altabp-.(r4),r4 ; get device address mov 2(r4),r0 ; get character jsr r5,alrint ; call general rcvr interrupt routine pop r4,r1,r0,* ; restore regs, remove PS from stack rti ; KL11 and DL11 transmitter interrupt handler. dlxint: mfps -(sp) ; save ps to extract unit no. push r1 ; save reg inc intflg ; indicate interrupt has occured mov 2(sp),r1 ; get PS (unit no. in cc bits) bic #177760,r1 ; ... jsr r5,alxint ; call general xmtr interrupt routine pop r1,* ; restore reg, remove PS from stack rti .endc ; ne ndl ; ALRINT ; ARGS: VALS: ; R0: character (none) ; R1: line no. alrint: ifSAO < ; the break character on SAO's machine ; can be a ^\ or a cmpb #34,r0 ; see if it's a requested break beq 5$ ; yes, break for Steve cmpb #234,r0 ; have to test both parities beq 5$ ; break??? but of course if equal > bit #20000,r0 ; Framing Error on this Character?? if ne,< ; yes, treat as a break 5$: push r2 ; save register mov pc,r2 ; get pointer to TTAB add #ttab-.,r2 ; .... mov #ntty,r0 ; number of TTY lines loop < cmp r1,tlinen(r2) ; is this the right Asynchronous line?? exitl eq ; yes, we found it add #lttab,r2 ; move to next terminal entry sorl r0 ; and look at it br 3$ ; this line is not a TTY so treat as a ; regular character > mov #-1,tbreak(r2) ; a break occurred on this line pop r2 ; restore register rts r5 ; no more processing necessary ; break was not a TTY line. 3$: pop r2 ; restore register > ; We have a good character so put it in the right queue for the line it ; was typed on. asl r1 ; times two for indexing add pc,r1 ; convert from line no. to line table entry ptr mov altabp-.(r1),r1 ; ... bitb #%alstp+%alxon,almode(r1) ; flow control mode on? if ne,< cmpb #23,r0 ; check for ^S for with both parities beq 1$ cmpb #223,r0 ifSAO < ; this is for SAO's LA180 beq 1$ ; which uses ^Q/^Q protocol cmpb #21,r0 ; look for a ^Q, either parity beq 1$ ; just return if its a ^Q cmpb #221,r0 ; .... > if eq,< 1$: bisb #%alstp,almode(r1) ; stop output on a ^S rts r5 > cmpb #21,r0 ; look for a ^Q, either parity beq 8$ ; just return if its a ^Q cmpb #221,r0 ; .... if eq,< 8$: bicb #%alstp,almode(r1) ; resume after a ^Q push r4 ; save r4 mov (r1),r4 ; get device address bicb alib(r1),4(r4) ; black magic bisb alib(r1),4(r4) ; enable that lines output interrupts again pop r4 ; restore regs rts r5 > > push r0,r1 ; INSQ args: character, queue ptr add #aliq,(sp) ; ptr to input queue jsr r5,insq ; insert into input queue pop * ; val: error code 2$: rts r5 ; ALXINT ; ARGS: VALS: ; R1: line no. (none) alxint: push r0,r4 ; save regs asl r1 ; times two for indexing add pc,r1 ; convert line no. to line table entry ptr mov altabp-.(r1),r1 ; ... mov (r1),r4 ; get device address bitb #%alstp,almode(r1) ; is this lines output shut off? bne 1$ ; pretend we're not waiting for anything push r1 ; REMQ arg: queue ptr add #aloq,(sp) ; ... jsr r5,remq ; get character from output queue pop r0,* ; vals: character, error code if eq,< movb r0,6(r4) ; send character out into the world > else < ; no character to send 1$: bicb alib(r1),4(r4) ; turn off line's interrupt enable > pop r4,r0 ; restore reg rts r5 .sbttl Queue management ; QINIT initializes a queue structure. ; ARGS: VALS: ; SP -> ptr to queue (none) ; buffer size ; buffer ptr qinit: push r1 ; save reg mov 4(sp),r1 ; get ptr to queue structure mov 6(sp),qsize(r1) ; save queue buffer size mov 10(sp),qbp(r1) ; set buffer ptr mov 10(sp),qfp(r1) ; set front ptr mov 10(sp),qrp(r1) ; set rear ptr mov 6(sp),qcc(r1) ; set room in queue to size (completely empty) pop r1,4(sp) ; restore reg, move up return r5 cmp (sp)+,(sp)+ ; pop off args return ; ROOMQ waits until a queue has a specified amount of room. ; ARGS: VALS: ; SP -> queue ptr (none) ; no. of chars roomq: push 2(sp) ; TWONHI arg1: address to wait on add #qcc,(sp) ; ... push 4+2(sp) ; TWONHI arg2: value to higher than dec (sp) ; ... call twonhi ; wait for QCC to be ge no. of chars pop (sp),(sp) ; remove args from stack return ; INSQ inserts a character into the rear of a queue. ; ARGS: VALS: ; SP -> queue ptr SP -> error code ; character insq: push r1 ; save reg mfps -(sp) ; save priority mov 6(sp),r1 ; ptr to queue structure spl 5 ; set no interrupt priority tst qcc(r1) ; room in queue? if eq,< mov #%eflq,10(sp) ; return queue full error br 1$ > dec qrp(r1) ; move to next char slot cmp qrp(r1),qbp(r1) ; wrap around to end? if lo,< add qsize(r1),qrp(r1) ; yes, make ptr to end of buffer > movb 10(sp),@qrp(r1) ; queue not full, put in this char dec qcc(r1) ; decrement space left in buffer clr 10(sp) ; zero error code for success return 1$: mtps (sp)+ ; restore priority pop r1,(sp) ; restore reg, pop off arg return ; REMW waits for a queue to be non-empty. After calling REMW a REMQ should ; never fail. ; ARGS: VALS: ; SP -> timeout SP -> error code ; queue ptr remw: push r1 ; save reg mov 6(sp),r1 ; ptr to queue structure clr 6(sp) ; zero error code tst 4(sp) ; zero timeout? if ne,< if pl,< ; no timeout if negative time call uptime ; get system up time add 4+4(sp),2(sp) ; add timeout to lo order uptime adc (sp) ; add carry to hi order uptime call tstot ; set timeout time for wait > push r1 ; TWONNE arg1: address to wait on add #qcc,(sp) ; ... push qsize(r1) ; TWONNE arg2: value to change from call twonne ; wait for room to be different from size clr 6(sp) ; set return error code to zero cmp qcc(r1),qsize(r1) ; timed out? if eq,< mov #%eemq,6(sp) ; yes, return queue empty error > > pop r1,(sp) ; restore reg, remove arg from stack return ; REMQ removes a character from the front of a queue. ; ARGS: VALS: ; SP -> queue pointer SP -> character ; error code remq: push (sp),r1 ; make room for results, save regs mov 6(sp),r1 ; ptr to queue structure cmp qcc(r1),qsize(r1) ; queue empty? if eq,< mov #%eemq,6(sp) ; return queue empty error mov #-1,4(sp) ; also return -1 as character br 1$ > dec qfp(r1) ; move rear ptr to next char cmp qfp(r1),qbp(r1) ; wrap around? if lo,< add qsize(r1),qfp(r1) ; yes, put ptr at end of buffer > clr 4(sp) ; so high byte will be clear movb @qfp(r1),4(sp) ; put character in its result slot inc qcc(r1) ; one more space in queue clr 6(sp) ; zero error code for success 1$: pop r1 ; restore reg return .if ne asmpek ; PEEKQ returns the character in front of the queue ; ARGS: VALS: ; SP -> queue pointer SP -> character ; error code peekq: push (sp),r1 ; make room for results, save regs mov 6(sp),r1 ; ptr to queue structure cmp qcc(r1),qsize(r1) ; queue empty? if eq,< mov #%eemq,6(sp) ; return queue empty error mov #-1,4(sp) ; also return -1 as character br 1$ > push qfp(r1) ; save old queue pointer dec qfp(r1) ; move rear ptr to next char cmp qfp(r1),qbp(r1) ; wrap around? if lo,< add qsize(r1),qfp(r1) ; yes, put ptr at end of buffer > clr 4+2(sp) ; so high byte will be clear movb @qfp(r1),4+2(sp) ; put character in its result slot pop qfp(r1) ; restore queue pointer clr 6(sp) ; zero error code for success 1$: pop r1 ; restore reg return .endc; -*-palx-*- .sbttl console ; console's data structure dsect < .blkb lcs cheigh:: .blkw 1 ; screen height cline:: .blkw 1 ; line number connected to cipid:: .blkw 1 ; input process id copid:: .blkw 1 ; output process id cichar:: .blkw 1 ; Console's intercept character cimage:: .blkw 1 ; using image mode output cvpos:: .blkw 1 ; current horizontal position chpos:: .blkw 1 ; current vertical position ctopt:: .blkw 1 ; terminal's option word cofch:: .blkw 1 ; output file channel id cifch:: .blkw 1 ; input file channel id cwait:: .blkw 1 ; time to wait after each character (60th's) cwtchr:: .blkw 1 ; if non-zero wait after a CR only ceschr:: .blkw 1 ; if non-zero last character received ; was an escape ccecho:: .blkw 1 ; 0 = we don't need to echo, 1 = we do >,lcdat consol: push #lcdat/2 ; ALOCL arg: size call alocl ; allocate space for variables pop r1 ; val: ptr mov #lcs/2,r0 ; length of command structure loop < mov (r4)+,(r1)+ ; copy into trollr data area sorl r0 > sub #lcs,r4 ; get back ptr to command structure sub #lcs,r1 ; ... push r4 ; FREE arg: ptr call free ; free command structure mov r1,r4 ; use Trollr's version mov r4,filep(r4) ; ptr to FILNAM add #filnam,filep(r4) ; ... push uoch(r4) ; ORDER arg: channel id push #10000+tvmax,#0 ; ORDER args: order code, order arg call order ; read TVMAX inc (sp) ; we're one based pop cheigh(r4) ; vals: page height, error code push uoch(r4) ; ORDER arg: channel id push #10000+topt,#0 ; ORDER args: order code, order arg call order ; get terminal's option word pop ctopt(r4) ; val: order result (TOPT word for TTY) mov #',cichar(r4) ; set intercept character to ^^ clr cofch(r4) ; we're not connected to anything yet clr cwait(r4) ; no waiting on output by default clr cwtchr(r4) ; default to waiting after a CR tdtype clr ; clear screen call getpid ; get our process id pop cipid(r4) ; save as input side process id mov #-1,cline(r4) ; no current connection call moroff ; turn off more processing push r0 ; save reg mov pc,r0 ; get PIC address of default file name add #crfiln-.,r0 ; .... tst (r0)+ ; skip over directory mov (r0)+,filnam+2(r4) ; set file name mov (r0)+,filnam+4(r4) ; .... mov (r0)+,filnam+6(r4) ; set extension sub #10,r0 ; get back pointer to file name mov (r0)+,filnam+10(r4) ; set default directory mov (r0)+,filnam+12(r4) ; set filename mov (r0)+,filnam+14(r4) ; .... mov (r0)+,filnam+16(r4) ; set extension pop r0 ; restore reg br consi ; go do input side cr4: .word 0 .sbttl Input process consi: tst cline(r4) ; connected? bmi ccp push uich(r4) ; RCHAR arg: channel id call rchar ; read terminal input pop r0 ; val: character cmp r0,cichar(r4) ; Intercept character? beq ccp ; yes, call console command processor push cline(r4),r0 ; ALWRIT args: line no., character call alwrit ; output to line tst ccecho(r4) ; are we supposed to echo if ne,< push uoch(r4),r0 ; yes send character to terminal call wchar ; .... > br consi ; Console command processor ccp: push uoch(r4) ; ORDER arg: channel id push #10000+tvpos,#0 ; ORDER args: order code, order arg call order ; read vertical position inc (sp) ; we're one based pop cvpos(r4) ; val: vpos push uoch(r4) ; ORDER arg: channel id push #10000+thpos,#0 ; ORDER args: order code, order arg call order ; read horizontal position inc (sp) ; we're one based pop chpos(r4) ; val: hpos tst cimage(r4) ; using image mode output?? if ne,< push uoch(r4),#3 ; ORDER args: channel id, order code, push #%tcio ; order arg (cbit to turn off) call order ; turn off image output mode pop * ; val: return value (meaningless) > push cheigh(r4),#1 ; CPOSIT args: vpos, hpos call cposit ; move cursor to bottom line push uoch(r4) ; IOAC arg: channel call ioac ; output prompt .string ^"Console:" tdtype eol ; clear to end of line call rdcmdl ; read command line into buffer call rdcmdc ; read 1st character pop r0 ; val: character cmp r0,cichar(r4) ; intercept character? if eq,< tst cline(r4) ; connected to anything? bmi ccerr ; no, error push cline(r4),r0 ; ALWRIT args: line no., character call alwrit ; send intercept character br 3$ ; ccdone > call rrcmdc ; reread the 1st character call sarg ; read string argument push pc ; SCASE arg: ptr to list of string names add #cclist-.,(sp) ; ... call scase ; lookup command typed pop r0,* ; vals: ptr, error code bne ccerr jsr pc,@2(r0) 3$: tst errflg(r4) ; error occurred on last command if ne,< push uoch(r4),errflg(r4) ; WRITEZ args:output channel,asciz ptr call writez ; print error message type ^" " clr errflg(r4) ; no error now > jmp ccdone ccerr: push uoch(r4),#%tdbel ; WCHAR args: channel id, character call wchar ; feep a loser jmp ccdone cclist: .word 4 defcom ld, defcom cques, defcom cexit, defcom csic, defcom clink, defcom crecve, defcom cclse, defcom culink, defcom clpt, defcom csend, defcom ccwait, defcom csmart, defcom cdumb, ; defcom cmcftp, commentented out because the code doesn't ; exist. TWITS!!!!! -rll defcom cccCR, defcom cccCHR, defcom cecho, defcom cnecho, .word 0 cccCR: clr cwtchr(r4) ; wait only after a CR rts pc cccCHR: mov #1,cwtchr(r4) ; wait after each character rts pc csmart: jsr pc,cccCHR ; wait after each character mov #2,cwait(r4) ; time to wait rts pc cdumb: clr cwtchr(r4) ; wait after CR mov #21,cwait(r4) ; time for HP to catch up rts pc ccwait: call arg pop * call numarg ; get arg pop cwait(r4) ; got it save as number to wait rts pc cecho: mov pc,ccecho(r4) rts pc cnecho: clr ccecho(r4) rts pc csend: tst cline(r4) ; are we connected bmi ccerr1 ; can't send if we're not connected push filep(r4) ; FILARG arg: ptr tofilename buffers call filarg ; read the filename push pc ; ATTACH arg: device add #dskn-.,(sp) ; .... push filep(r4),diskno(r4),#0 ; ATTACH args:filename,unit #,cbits call attach ; open specified file pop cifch(r4),r0 ; vals: channel id, error code if ne,< jmp comerr ; report error in opening file > mov #-1,r2 ; default to sending linefeeds clr r3 ; default to not echoing call arg ; see if there is another arg pop r0 ; get number if ne,< ; if there is an arg mov #12,r2 ; set linefeed flag > tst cimage(r4) ; using image mode output?? if ne,< push uoch(r4),#2 ; ORDER args: channel id, order code, push #%tcio ; order arg (cbit to turn on) call order ; turn on image output mode pop * ; val: return value (meaningless) > loop < call quit ; see if user wants to quit sending pop * ; val: non-zero if time to quit bne 3$ push cifch(r4) ; RCHAR arg: channel id call rchar ; get next character pop r0 ; val: character exitl mi ; EOF???? cmp r0,r2 ; destroy all linefeeds!!???? if ne,< push cline(r4),r0 ; ALWRIT args: line no., character jsr r5,alwrit ; send character > tst r0 ; is the char we're sending a null rptl eq ; yes tst ccecho(r4) ; should we echo if ne,< ; yes if non-zero push uoch(r4),r0 ; WCHAR args: line no. character call wchar ; send character to screen > tst cwtchr(r4) ; if non-zero then wait after each charcter if ne,< 2$: push cwait(r4) ; TWFOR arg: time to wait (1/60th's of secs) call twfor ; wait for other machine to catch up rptl > else < ; if CWTCHR(R4) is zero then wait after a CR cmp r0,#15 ; did we just send a CR? beq 2$ ; yes, wait rptl ; else do more sending > > 3$: push cifch(r4) ; DETACH arg: channel id call detach ; close file clr cifch(r4) ; nothing being read rts pc clpt: tst cofch(r4) ; anything open bne ccerr1 ; yes. report error jsr pc,gettpl ; get the LPT if possible pop cofch(r4),* ; vals: channel id, error code rts pc ccerr1: push uoch(r4),#%tdbel ; WCHAR args: channel id, character call wchar ; feep a loser rts pc crecve: tst cofch(r4) ; anything open bne ccerr1 ; yes. report error push pc ; ATTACH arg1: device name add #dskn-.,(sp) ; ... push filep(r4) ; FILARG arg: pointer to filename buffer call filarg ; set filename (if any) push filep(r4) ; ATTACH arg2: file name push diskno(r4) ; ATTACH arg3: fdisk unit number push #%icrea+%iupd ; ATTACH arg4: cbits call attach ; open write file pop cofch(r4),* ; vals: channel id, error code rts pc cclse: call cclose rts pc clink: tst cline(r4) ; connected to anything? bge ccerr1 ; yes. error. call numarg ; read numeric argument to link command pop cline(r4) ; val: number push r3,r2 ; smashed by CTOTTY jsr pc,ctotty ; get TTYCH table ptr in R3 tst r3 ; zero R3 means bad link # beq cbadlk ; error take care of it tst pidtb-ttych(r3) ; see if a process owns this terminal bne cbadlk ; not-zero? some other process owns it call getpid ; get our process ID pop pidtb-ttych(r3) ; fill it into process ID table for owning TTY pop r2,r3 push pc ; MAKEP arg: initial PC add #conso-.,(sp) ; ... call makep ; create output side of Console pop copid(r4),* ; vals: process id, error code mov r4,cr4 ; pass R4 to output process push copid(r4) ; STARTP arg: process id call startp ; start output side pop * ; val: error code mov #-1,cimage(r4) ; default is image mode clr ccecho(r4) ; default is to not echo rts pc cbadlk: mov #-1,cline(r4) ; set to unconnected state pop r2,r3 ; restore registers br ccerr1 ; report error ; CTOTTY gets a pointer to TTYCH table in R3 for CLINE ; it uses r3 and r2 ctotty: clr r3 ; just for counting up to NTTY's mov pc,r2 add #ttab-.,r2 ; get PIC address of TTAB loop < cmp cline(r4),tlinen(r2) ; is this the right line #? exitl eq add #lttab,r2 inc r3 cmp #ntty,r3 blo 1$ rptl > asl r3 ; TTY #, convert for word address add pc,r3 ; pointer to TTYCH table add #ttych-.,r3 rts pc 1$: clr r3 rts pc cques: push uoch(r4),#%tdclr ; WCHAR args: channel id, character call wchar ; clear screen push pc ; LISTC arg: ptr to command list add #cclist-.,(sp) ; ... call listc ; typeout Console commands rts pc csic: call arg ; skip over spaces pop * ; val: character call rdcmdc ; read new intercept character pop cichar(r4) ; val: character rts pc ccdone: push cheigh(r4),#1 ; CPOSIT args: vpos, hpos jsr r5,cposit ; move to beginning of bottom line push uoch(r4),#%tdeol ; WCHAR args: channel id, character call wchar ; clear bottom line push cvpos(r4),chpos(r4) ; CPOSIT args: vpos, hpos call cposit ; restore cursor to where it was tst cimage(r4) ; image mode output?? if ne,< push uoch(r4),#2 ; ORDER args: channel id, order code, push #%tcio ; order arg (cbit to turn on) call order ; turn on image output mode pop * ; val: return value (meaningless) > jmp consi cfree: tst copid(r4) ; any output process? if ne,< ; yes, flush it push copid(r4) ; STOPP arg: process id call stopp ; stop output process pop * ; val: error code push copid(r4) ; KILLP arg: process id call killp ; kill output process pop * ; val: error code > call cclose ; close any output file tst cline(r4) if ge,< push r3,r2 ; free the TTY in PIDTB jsr pc,ctotty tst r3 if ne,< clr pidtb-ttych(r3) ; free it > pop r2,r3 > return cexit: tst cline(r4) ; are we connected if ge,< ; yes call cbufrt ; reset buffers > push uoch(r4) ; ORDER arg: channel id push #20000+topt,ctopt(r4) ; ORDER arg: order code, order arg call order ; restore terminal options pop * ; val: meaningless call cfree call moron ; turn on more processing push #lcs/2 ; ALOCL arg: size call alocl ; allocate command structure pop r1 ; val: ptr mov #lcs/2,r0 ; no. of words in command structure loop < mov (r4)+,(r1)+ ; copy TROLLR's command stuff sorl r0 ; ... > sub #lcs,r1 ; get back ptrs to start of data blocks sub #lcs,r4 ; ... push r4 ; FREE arg: ptr call free ; free Trollr's data block mov r1,r4 ; set command structure ptr mov r4,filep(r4) ; ptr to FILNAM add #filnam,filep(r4) ; ... pop * ; remove return PC from stack rts pc .if ne 0 ; don't assemble this code for now cdisp: push uoch(r4),#%tdclr ; WCHAR args: channel id,character call wchar ; clear screen push uoch(r4) ; IOAC arg: output channel tst cimage(r4) ; what type of output processing is happening if eq,< call ioac ; output message .string ^"No image output. " > else < call ioac ; output message .string ^"Image Output. " > cmp #-1,cline(r4) ; are we connected to anything?? if eq,< push uoch(r4) ; IOAC arg: output channel call ioac ; print message .string ^"Not connected. " > else < push cline(r4),uoch(r4) ; IOAC args: argument, output channel call ioac ; output message .string ^"Connected to ^d. " > push uoch(r4) ; IOAC arg: output channel tst cofch(r4) ; output channel open ??? if ne,< call ioac ; output message .string ^"Output file is open. " > else < call ioac ; output message .string ^"No output file is open. " > push uoch(r4) ; IOAC arg: output channel tst ccecho(r4) ; are we echoing???? if ne,< call ioac .string ^"Half-duplex (echoing enabled). " > else < call ioac .string ^"Full-duplex (echoing disabled). " > rts pc .endc culink: tst cline(r4) ; are we connected if ge,< call cfree ; free up output channels and ; kill output process call cbufrt ; reset buffers for cline > mov #-1,cline(r4) ; not connected to anything any more clr cofch(r4) ; no output file either rts pc ; return to console command processor cbufrt: push cline(r4),#20.,#60. ; ALREBF args: line #,input,output size call alrebf ; reset line buffers pop * ; VALS: error code return ; CPOSIT moves the terminal's cursor to the specified place. ; ARGS: VALS: ; SP -> horizontal position (none) ; vertical position cposit: bit #%todis,ctopt(r4) ; display terminal? if eq,< ; no push uoch(r4),#%tdhmv ; WCHAR args: channel id, character call wchar ; send code for horizontal move push uoch(r4),2+2(sp) ; WCHAR args: channel id, character call wchar ; send code for vertical move br 1$ > push uoch(r4),#%tdmov ; WCHAR args: channel id, character call wchar ; send software TTY code for cursor move push uoch(r4),4+2(sp) ; WCHAR args: channel id, character call wchar ; send vertical position push uoch(r4),2+2(sp) ; WCHAR args: channel id, character call wchar ; send horizontal position 1$: pop (sp),(sp) ; remove args from stack rts r5 .sbttl Output process conso: push #0,#80. ; SETSS args: process id, size call setss ; enlarge our stack pop * ; val: error code if ne,< lose 0 > mov cr4,r4 ; get ptr to Console's data area push cline(r4),#750. ; ALREBF args: line no., input buffer size push #20. ; output buffer size call alrebf ; re allocate buffers pop * ; val: error code coloop: push cline(r4),#-1 ; ALREAD args: line no., timeout (none) call alread ; read input from line pop r0,* ; vals: character, error code push uoch(r4),r0 ; WCHAR args: channel id, character call wchar ; output to terminal tst cofch(r4) ; output file open? beq coloop ; no bic #177600,r0 ; hack bits beq coloop ; don't write nulls to output file push cofch(r4),r0 ; WCHAR args: channel id, character call wchar ; output to file br coloop cclose: tst cofch(r4) ; anything open? if ne,< push cofch(r4) ; DETACH arg: channel id call detach ; close file clr cofch(r4) ; indicate nothing open > rts r5 crfiln: .rad50 "CONSOL" .rad50 "OUT" 4; Trantor Command processor -*-PALX-*- dsect < uich:: .blkw 1 ; user input channel uoch:: .blkw 1 ; user output channel cbsize:: .blkw 1 ; buffer size cendp:: .blkw 1 ; ptr to end of input line cbufp:: .blkw 1 ; ptr to next character to read in buffer lcmdb==120. ; length of command buffer cbuf:: .blkb lcmdb ; command buffer errflg:: .blkw 1 ; ptr to error message uname:: .blkw 1 ; user name (rad50) ready:: .blkw 1 ; number of ready messages diskno:: .blkw 1 ; current disk unit number diskn1:: .blkw 1 ; used by move command filep:: .blkw 1 ; ptr to filename buffer (next entry!) filnam:: .blkw 4 ; filename .blkw 4 ; used by rename >,lcs .macro type text push uoch(r4) call writec .string ^~text~ .endm .macro tdtype x push uoch(r4),#%td'x call wchar .endm .macro err text .string ^~text~,%.ttmp mov #%.ttmp,errflg(r4) .endm cp: push #0,#80. ; SETSS args: process id, size call setss ; set our stack size large pop * ; val: error code if ne,< lose 0 > push #lcs/2 ; ALOCL arg: size call alocl ; allocate data area for us pop r4 ; val: ptr mov #lcmdb,cbsize(r4) ; set command buffer size clr ready(r4) ; set number of readys to 0 mov r4,filep(r4) ; set FILEP to FILNAM add #filnam,filep(r4) ; ... ifSAO < mov #70700,uname(r4) mov #70700,filnam(r4) ; set SAO's default dir to RH; > clr diskno(r4) ; use disk number zero by default clr diskn1(r4) ; clear just in case push #0 ; GETUIO arg: process id (zero = self) call getuio ; get user input and user output channels pop uoch(r4),uich(r4),* ; vals: output ch, input ch, error code push uoch(r4),#%tdclr ; WCHAR args: channel id, character call wchar ; clear screen push #tvn ; IOAC arg: Trantor version no. push uoch(r4) ; IOAC arg: channel id call ioac ; print greeting message ifMIT < .if eq sysexp .string ^"Trantor ^d: MIT Math Department, Cambridge Mass. " .endc .else .string ^"Trantor ^dX: MIT Math Department, Cambridge Mass. " .endc > ifSAO < .string ^"Trantor ^x. " > ifMIT < push uoch(r4) ; PTIME arg1: channel id push #%pttime+%ptdate+%ptday+%ptzone ; PTIME arg2: cbits call time ; PTIME args 3 and 4: time lo, time hi call ptime ; print current time type ^| Type "help" for information on using the system. | > cploop: push uoch(r4) ; ORDER arg: channel id push #10000+topt,#0 ; ORDER arg: order code, order arg call order ; get terminal option word pop r0 ; got it bic #%tnech,r0 ; clear no output bit push uoch(r4) ; ORDER arg: channel id push #20000+topt,r0 ; ORDER args: order code, order arg call order ; set terminal option word pop * ; val: order result (meaningless) call rdcmdl ; read command line into buffer call arg ; skip over leading spaces pop * ; val: character beq cploop call sarg ; read a string argument ; SCASE args: ptr, length push pc ; SCASE arg3: ptr to list of strings add #cplist-.,(sp) ; ... call scase ; lookup command name in list pop r1,* ; vals: ptr to entry, error code if ne,< type ^"Illegal command. " br cploop > ifMIT < tst uname(r4) ; logged in? if eq,< cmp r1,#cpls1 ; command allowed when not logged in? if his,< type ^"You must login to use that command. " br cploop > > > push uich(r4) ; ORDER arg: channel id push #20000+tbreak,#0 ; ORDER arg: order code, order arg call order ; set tbreak back to zero pop * jsr pc,@2(r1) ; call routine for this command push uoch(r4) ; ORDER arg: channel id push #10000+topt,#0 ; ORDER arg: order code, order arg call order ; get terminal option word pop r0 ; got it bic #%tnech,r0 ; clear no output bit push uoch(r4) ; ORDER arg: channel id push #20000+topt,r0 ; ORDER args: order code, order arg call order ; set terminal option word pop * ; val: order result (meaningless) tst errflg(r4) ; error message to print? if ne,< push uoch(r4),errflg(r4) ; WRITEZ args: channel, asciz ptr call writez ; print error message jsr pc,crlf ; output CRLF jsr pc,crlf ; output another clr errflg(r4) ; clear error flag > mov filep(r4),r1 ; get pointer to filename add #10,r1 ; get end of filename push -(r1),-(r1) ; IOAC args: extension, 2nd filename push -(r1),-(r1) ; IOAC args: 1st filename, directory name ifMIT < .if eq push diskno(r4) ; get disk number add #71176,(sp) ; add offset to get diskname (RL0) .endc .if eq push diskno(r4) ; get disk number add #71436,(sp) ; add offset to get diskname (RP0) .endc > ifSAO < push diskno(r4) ; get disk number add #72136,(sp) ; add offset to get diskname (RX0) > inc ready(r4) ; another command executed push ready(r4),uoch(r4) ; IOAC args: number and channel id call ioac ; print ready message .string ^"r ^4z ^r:^r;^r^r.^r " br cploop ; JMP here to report expected argument missing. eeam: err ^"Expected argument missing" rts pc ; JMP here with error code in R0. comerr: push r0 ; ERRMSG arg: error code call errmsg ; get error message pop errflg(r4) ; val: ptr to asciz string rts pc .macro defcom a,names .irp n, .string ^"n",%.ctmp .word %.ctmp,a .endm .endm defcom cplist: .word 4 ; no. of bytes per entry defcom login, defcom logout, ifMIT < .lif ne 0 defcom help, > defcom flowon, defcom flowof, defcom cmoron, defcom cmorof, defcom clistc, defcom pstime,