Trailing-Edge
-
PDP-10 Archives
-
tops10_704_monitoranf_bb-x140c-sb
-
10,7/anf10/dncdmc.p11
There are 3 other files named dncdmc.p11 in the archive. Click here to see a list.
.SBTTL DMCDMC - DMC-11 SYNCH LINE INTERFACE 29 APR 86
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
; OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1979,1980,1981,1984,1988.
;ALL RIGHTS RESERVED.
VRCDMC=035 ;FILE EDIT NUMBER
.REPT 0
LOOSE ENDS
.ENDR
.IF NE DMCN
ND DMCIBF,4 ;MAX # OF INPUT BUFFERS TO QUEUE
ND DMCOBF,4 ;MAX # OF OUTPUT BUFFERS.
;DMC DOESN'T WORK IF YOU GIVE
; IT ANY MORE...
SEL0=0 ;FIRST REGISTER
SEL2=2 ;SECOND
SEL4=4 ;THIRD
SEL6=6 ; AND FOURTH
.SBTTL CB - DMC-11 MESSAGE BUFFERS.
.REPT 0
The DMC-11 requires that the messages it transmits and
receives be in contiguous memory. This driver accomplishes
that by copying the data back and forth between chunks and
DMC-11 buffers (CB.???). There is a fixed pool of buffers
that is shared between all DMC-11's on the system.
.ENDR
BLOCK CB ;DEFINE THE "X, XX" MACROS
X LNK,1 ;LINK TO THE NEXT BUFFER
X CNT,1 ;NUMBER OF BYTES IN BUFFER
XX DAT,MSGMAX ;DATA AREA
X LEN,0 ;LENGTH OF A CB BLOCK
;NOW DEFINE THE NUMBER OF BUFFERS FOR THE SYSTEM.
CB.NUM=DMCN*<DMCIBF+DMCOBF> ;TOTAL NUMBER OF BUFFERS
;THE BUFFER FREE LIST CONSISTS OF A COUNT AND POINTER TO FIRST
; LIST AND FREECOUNT ARE SET UP DURING ONCE/ONLY
CBFREC: .BLKW 1 ;COUNT OF FREE DMC-11 BUFFERS
CBFSTF: .BLKW 1 ;FIRST FREE BUFFER
;NOW RESERVE STORAGE FOR THE BUFFERS
.MACRO X Q ;ALSO NAME THE BUFFERS.
CB..'Q: .BLKB CB.LEN ;RESERVE STORAGE FOR BUFFER
.ENDM
CBFRST: ;ADDRESS OF THE FIRST BUFFER
Z=0 ;INIT THE LOOP COUNTER
.REPT CB.NUM ;FOR ALL BUFFERS
X \Z ;RESERVE THE MEMORY
Z=Z+1 ; AND INCREMENT THE COUNTER
.ENDR ;BUFFERS ALL LAYED OUT.
.SBTTL DMCBAS - BASE TABLES FOR THE DMC-11
.REPT 0
Each DMC-11 requires a 256. word base table in which
it stores various line counts. The DMC-11 must be given the
address of this table in a BASE-IN transaction before it will
do anything else (except clobber low memory...)
.ENDR
BLOCK MB ;DMC BASE
XX ,5 ;NOTHING OF INTREST HERE
XX RNK ;RECEIVED NAKS
XX XNB ;SENT NAKS - NO BUFFER
XX XNH ;SENT NAKS - BAD HEADER
XX XND ;SENT NAKS - DATA BCC
XX XRP ;SENT REPS
XX RRP ;RECEIVED REPS
XX XX,255.-MB.RRP ;FILL OUT TO THE END
XX LEN,0 ;LENGTH OF THE BASE TABLE (256)
;NOW A MACRO TO LAY OUT EACH BASE TABLE
.MACRO X Q ;ARG IS DMC-11 NUMBER
DMBAS'Q: .BLKB MB.LEN ;LAY OUT THE BASE
.ENDM
Z=0 ;INIT LOOP COUNTER
.REPT DMCN ;FOR ALL DMC-11'S
X \Z ; LAY OUT THEIR BASE TABLE
Z=Z+1 ;INCREMENT THE DMC NUMBER
.ENDR
.SBTTL QUEUES - DMC-11 MESSAGE BUFFER QUEUES (IN LINE BLOCK)
.REPT 0
Each line-block for a DMC-11 has 3 queues of DMC buffers.
There are:
1) LB.INQ Empty input buffers waiting to be filled
2) LB.IND Full input buffers ready to be emptied at loop.
3) LB.OUT Full output buffers waiting to be sent by the DMC.
The general flow of control for input is:
1) DMCQRB queues input buffers on LB.INQ and gives them to
the DMC (via BA/CC in transaction) to fill.
2) The DMC-11 interrupts when the buffer is filled, the
interrupt code (DMCBIN) transfers the buffer from the
LB.INQ to the LB.IND queue and wakes low level.
3) Loop level removes the buffer from the LB.IND queue, copies
the data to chunks (to form a message), frees the DMC
buffer, and gives the message to the appropriate higher
level protocol (NCL or NSP).
The general flow of control for output is:
1) DNDCMP queues a buffer on LB.OBF and calls the DMC-11
driver at DMCXMT.
2) The driver allocates a DMC-11 buffer, dequeues the
message from LB.OBF and copies it to the DMC-11 buffer.
The DMC-11 buffer is given to the DMC-11 to send and
queued on the LB.OUT queue. The message is returned
to the appropriate higher level protocol as "sent".
3) The DMC-11 will interrupt after the message has been
sent and ACKed by the other side. At this point the
DMC-11 buffer is dequeued from LB.OBF and freed.
.ENDR
;FORMATS FOR THE QUEUES ARE:
BLOCK CBQ ;DEFINE THE DMC-11 QUEUES
X CT ;FIRST IS THE QUEUE LENGTH
X FS ;NEXT IF THE FIRST BUFFER
X LS ; THEN THE LAST BUFFER
X LN,0 ;QUEUES ARE THIS LONG.
.SBTTL LBLK - SPECIAL DMC-11 LINE BLOCK OFFSETS
BLOCK LB ;DEFINE THE X, XX MACROS
XX ,LB.IBF ;GET TO OUR OFFSET AREA
X INQ,CBQ.LN ;BUFFERS GIVEN TO DMC TO FILL
X IND,CBQ.LN ;FILLED BUFFERS RETURNED BY DMC
X OUT,CBQ.LN ;BUFFERS QUEUED TO BE SENT
X TMO,1 ;TIMER TO TIME OUT DMC-11
;DMC-11 DEPENDENT BITS IN LB.STX
LSXMAI=B0 ;WE ARE IN MAINT MODE
LSXWAI=B1 ;WAITING FOR MEMORY OR BUFFERS
LSXRUN=B2 ;WE BELIEVE DMC IS RUNNING
LSXKRK=B3 ;DMC IS KROAKED
LSXDMC=LSXMAI!LSXWAI!LSXRUN!LSXKRK ;CLEAR ON RESTART
DMCLB0: .WORD 0 ;ADDRESS OF FIRST DMC-11 LINE
;FILLED IN BY CKADMC
.SBTTL VECTOR - INTERRUPT VECTOR DEFINITIONS
.MACRO X N ;N := DMC-11 NUMBER
.NLIST ;NO ONE IS INTERESTED
MCVA'N: SAVE <J,#DMCAIN> ;SAVE POINTER & SERVICE ROUTINE
BR MCVC'N ;BRANCH TO COMMON CODE
MCVB'N: SAVE <J,#DMCBIN> ;SAVE POINTER & SERVICE ROUTINE
MCVC'N: MOV DMCLB0,J ;GET ADDR OF FIRST DMC-11 LB
.IIF NE N, ADD #<LB.SIZ*N>,J ;GET ADDRESS OF N'TH
RTS PC ;"CALL" THE SERVICE ROUTINE
.LIST
.ENDM
Z=0 ;LOOP COUNTER STARTS AT ZERO
.REPT DMCN ;FOR ALL DMC-11'S
X \Z ;DEFINE THEIR VECTORS
Z=Z+1 ;ADVANCE TO THE NEXT DMC
.ENDR ;VECTORS ALL DEFINED.
.SBTTL DMCONC - ONCE/ONLY CODE TO SET UP THE BUFFER FREE POOL
;CALLED FROM START UP CODE (JUST AFTER CHUNKS ARE LAYED OUT).
; FIRST INIT THE FREE LIST COUNT AND ADDRESS OF FIRST FREE BUFFER,
; THEN LOOP OVER ALL THE BUFFERS CHAINING THE FREE LIST.
DMCONC: MOV #CB.NUM,R0 ;GET THE NUMBER OF BUFFERS
MOV #CBFRST,R1 ;GET THE ADDRESS OF THE FIRST
MOV R0,CBFREC ;INIT THE FREE COUNT
MOV R1,CBFSTF ;INIT THE FIRST FREE POINTER
10$: MOV R1,R2 ;GET ADDR OF THIS ONE
ADD #CB.LEN,R1 ;GET ADDR OF NEXT ONE
MOV R1,CB.LNK(R2) ;MAKE "THIS" POINT TO "NEXT"
SOB R0,10$ ;LOOP OVER ALL BUFFERS
CLR CB.LNK(R2) ;MAKE LAST POINTER NULL
RTS PC ;FREE LIST INITIALIZED.
.SBTTL DMCINI - ONCE/ONLY CODE TO INIT THE LINE BLOCKS
;CALLED BY ONCE/ONLY AS WHEN IT LOOPS OVER ALL THE LINE BLOCKS
; J := LINE BLOCK ADDRESS
DMCINI: BIC #LSXDMC,LB.STX(J) ;RESET ALL THE DMC-11 BITS
JSR PC,DMCHLT ;STOP THE DMC (CLEAR LSXRUN)
BCS 99$ ;SKIP IF THE DMC IS BUSTED
QUEPUT QI ;TELL LOOP LEVEL TO RESTART IT
99$: RTS PC ;ALL DONE.
;DUMMY ADDRESSES FOR DISPATCH TABLE ENTRIES (ONLY REQUIRED WHEN
; THERE ARE OTHER TYPES OF LINES ON THE SYSTEM)
.IF NE,<NTLINE-DMCN> ;IF MORE THAN JUST DMC LINES
DMCDINI: ;INITIALIZE
DMCRBEG: ;RECEIVER BEGIN
DMCXBEG: ;XMIT BEGIN
TRAP ;SHOULD NEVER GET HERE
.ENDC
.SBTTL DMCTMR - CLOCK SERVICE FOR THE DMC-11
;DMCTMR ROUTINE TO PROCESS THE TIMER FUNCTIONS FOR THE DMC-11
;GENERAL ORDER OF BUSINESS IS:
; 1) SEE IF WE ARE IN MAINT MODE. IF WE ARE, WE'VE BEEN THERE TOO
; LONG. CLEAR MAINT, RESTART THE LINE AND EXIT.
; 2) IF WE'RE WAITING, START LOOP LEVEL AND EXIT. (AFTER ALL, IT
; ISN'T FAIR TO TIME LINES THAT CAN'T GET MEMORY)
; 3) IF NO OUTPUT MESSAGES QUEUES, QUEUE AN NCL ACK (NECESSARY
; BECAUSE STUPID DMC-11 WON'T NOTICE A LINE DOWN BY ITSELF)
; 4) SEE IF THE LINE HAS BEEN OUTPUT ACTIVE TOO LONG. IF SO,
; RESTART IT.
DMCTMR: BIT #LSXRUN,LB.STX(J) ;IF WE'RE NOT RUNNING
BEQ 97$ ;LET'S GET STARTED.
BIT #LSXMAI,LB.STX(J) ;ARE WE IN MAINT MODE
BEQ 10$ ; IF NOT MAINT, SKIP ON
INC LB.TMO(J) ;INCREMENT THE TIMER
CMP LB.TMO(J),#^D120./NRPSEC ;BEEN WAITING LONG ENOUGH?
BLT 99$ ;IF NOT, KEEP WAITING
BIC #LSXMAI,LB.STX(J) ;OTHERWISW CLEAR MAINT
BR 97$ ; AND RESTART THE LINE
10$: BIT #LSXWAI,LB.STX(J) ;WHERE WE WAITING?
BEQ 20$ ; IF NOT, GO TIME THE LINE
BIC #LSXWAI,LB.STX(J) ;IF SO, CLEAR THE FLAG
TWIDDLE ;COUNT NUMBER OF TIMES WE WAIT
BR 98$ ; AND QUEUE THE LINE FOR SERVICE
20$: TST LB.OUT+CBQ.CT(J) ;IS THERE AN OUTPUT MSG QUEUED
BNE 40$ ; IF SO, GO TIME IT
SAVE SB ;WE CLOBBER THIS
MOV LB.SCB(J),SB ;GET OUR NEIGHBOR'S SCB ADDR
BEQ 30$ ;IF NO ONE HOME...
BIT #SBF.IC,@SB ;SEE IF WE ARE ON SPEAKING TERMS
BEQ 30$ ;IF WE DON'T KNOW HIM
INC LB.TMO(J) ;IF IDLE, WAIT A TIME-OUT PERIOD
CMP LB.TMO(J),#REPDWN ; BEFORE WE QUEUE AN ACK
BLE 30$ ;GIVE NCL A CHANCE TO DO OTHER WORK
BIS #SF.XAK,@SB ;LET'S SAY HELLO
; MOVB #1,SB.TIM(SB) ; AND DO IT SOON
JSR PC,NCLQRQ ;PUT A REQUEST IN THE QUEUE
30$: RESTORE SB ;PUT CONTENTS OF R4 BACK
BR 99$ ;RESET REP TIMER AND LEAVE
40$: INC LB.TMO(J) ;IF OUTPUT ACTIVE, COUNT UP
CMP LB.TMO(J),#REPDWN ;HAS IT BEEN TOO LONG?
BLT 99$ ;IF NOT, JUST KEEP GOINT
97$: JSR PC,DMCRST ;RESTART THE DMC
BCS 99$ ;DMC IS ILL
98$: QUEPUT QI ;QUEUE IT FOR SERVICE
99$: MOVB #REPSEC,LB.REP(J) ;RESET THE REP TIMER
RTS PC ; AND WE'RE DONE
.SBTTL DMCHLT - STOP THE DMC-11 IN IT'S TRACKS.
;DMCHLT ROUTINE TO HALT THE DMC-11 AND DEQUEUE ALL DMC-11
; MESSAGE BUFFERS IT MAY OWN
;THIS ROUTINE MAY BE CALLED FROM ANY INTERRUPT LEVEL.
;
;ON RETURN, IF CARRY SET THE DMC IS KROAKED, DON'T USE IT.
DMCHLT: SAVE <R0,R1> ;WE CLOBBER NO REGISTERS
BIC #LSXKRK,LB.STX(J) ;ASSUME THE DMC WILL COOPERATE
MOV LB.SLA(J),R0 ;GET THE DMC-11 ADDRESS
BIS #MC.MCL,@R0 ;MASTER CLEAR THE DMC-11
;NOW FREE ALL MESSAGE BUFFERS
10$: JSR R0,DMCDEQ ;DEQUEUE A BUFFER
.WORD LB.INQ ; FROM THE INPUT WAIT QUEUE
BCC 20$ ;IF WE GOT ONE, GO FREE IT
JSR R0,DMCDEQ ;DEQUEUE A BUFFER
.WORD LB.IND ; FROM THE INPUT DONE QUEUE
BCC 20$ ;IF WE GOT ONE, GO FREE IT
JSR R0,DMCDEQ ;DEQUEUE A BUFFER
.WORD LB.OUT ; FROM THE OUTPUT QUEUE
BCS 30$ ;IF NONE, WE'RE DONE
20$: JSR PC,DMCGVB ;RETURN THE BUFFER
BR 10$ ; AND GO GET THE REST
30$: BIC #LSXRUN,LB.STX(J) ;SAY THE DMC IS NOT RUNNING
MOV #2000,R1 ;INIT A LOOP COUNTER
40$: BIT #MC.MCL,@R0 ;DID MASTER-CLEAR CLEAR YET
BEQ 50$ ;IF SO, WE'RE DONE
SOB R1,40$ ;WAIT TILL IT DOES
BIS #LSXKRK,LB.STX(J) ;FLAG THIS DMC KROAKED OFF
TWIDDLE @LB.SLA(J) ;SAVE DMC CONTROL/STATUS REGISTER
RESTORE <R1,R0> ;PUT THE REGISTERS BACK
SEC ;TAKE ERROR RETURN
RTS PC ; AND WE'RE DONE.
50$: RESTORE <R1,R0> ;PUT THE REGISTERS BACK
CLC ;SUCCESSFUL RETURN
RTS PC ; AND WE'RE DONE.
.SBTTL DMCRST - RESTART THE DMC-11
;ROUTINE TO INITIALIZE THE DMC-11, AND PERFORM THE BASE/CONTROL
; IN. IF IN MAINT STATE, DMC IS PUT IN MAINT MODE, AND THE TIMER
; IS SET FOR "LONG TIME", IF NOT MAINT, DMC IS RESTARTED NORMALLY
; AND THE HIGHER LEVEL PROTOCOL (NCL ! NSP) IS TOLD ABOUT THE LINE.
;
;ON RETURN, IF CARRY SET THEN THE DMC IS ILL, DON'T USE IT.
DMCRST: SAVE <R0,R1,R2,R4> ;WE CLOBBER NO REGISTERS
JSR PC,DMCHLT ;MAKE SURE THE DMC ISN'T RUNNING
JSR PC,L.DOWN ;TELL THE WORLD THE LINE IS DOWN
CLR LB.TMO(J) ;CLEAR THE OUTPUT TIMER
BIT #LSXKRK,LB.STX(J) ;IS THE DMC OK?
BEQ 5$ ;YES, THEN SET IT UPT
SEC ;NO, FLAG ERROR
BR 99$ ;AND GET OUT OF THIS MESS
;DMC IS OK (DMCHLT SAYS SO!), INITIALIZE IT
5$: MOV LB.SLA(J),R4 ;SET R4 := DMC-11 ADDRESS
BIS #MC.IEO,MC.CSO(R4) ;ENABLE OUTPUT INTERRUPTS.
;NOW DO THE BASE IN
MOV #MC.BSI,R0 ;BASE IN CODE
MOV LB.BAS(J),R1 ;ADDRESS OF THE BASE AREA
CLR R2 ;RESUME BIT CLEAR
JSR PC,DMCIN ;DO THE INPUT TRANSACTION
;THE CONTROL IN
MOV #MC.CTI,R0 ;CONTROL IN TRANSACTION CODE
CLR R1 ;NO BUS ADDRESS
CLR R2 ;FULL DUPLEX, NORMAL MODE
BIT #LSXMAI,LB.STX(J) ;IF MAINT MODE,
BNE 10$ ; THE REST IS DONE DIFFERENTLY.
JSR PC,DMCIN ;DO THE CONTROL IN
MOVB #REPSEC,LB.REP(J) ;SET THE REP TIMER
JSR PC,DDDRSS ;TELL THE WORLD WE RESTARTED
BR 95$ ;RESTORE THE REGS AND RETURN
;HERE IF IN MAINT MODE
10$: BIS #MC.MAI,R2 ;SET MAINT MODE
JSR PC,DMCIN ;DO THE CONTROL IN TRANSACTION
MOVB #REPSEC*60.,LB.REP(J) ;START A MUCH LONGER TIMER
95$: BIS #LSXRUN,LB.STX(J) ;TELL EVERYONE THE DMC IS OK
BIC #LS..ST!LS.STK!LS.XRP,@J;FAKE OUT THAT DDCMP IS NOW RUNNING
JSR PC,DMCQRB ;GIVE THE DMC-11 RECEIVE BUFFERS
CLC ;ALL IS WELL HERE
99$: RESTORE <R4,R2,R1,R0> ;PUT THE REGS BACK
RTS PC ; AND WE'RE DONE
.SBTTL DMCINP - SEE WHAT THE DMC-11 HAS FOR US TO DO.
;DMCINP ROUTINE TO PROCESS DMC-11 QUEUE REQUESTS
; CALLED FROM LOOP WHEN THERE IS A REQUEST IN THE QUEUE.
;GENERAL ORDER OF BUSINESS:
; 1) SEE IF THE DMC-11 IS RUNNING, IF NOT, KICK IT TILL IT IS.
; 2) CHECK FOR ANY INCOMING MESSAGES. COPY THEM TO CHUNKS AND
; GIVE THEM TO THE NEXT LEVEL (NCL ! NSP)
; 3) CALL DMCXMT TO TRY TO SEND MESSAGES
DMCINP: BIT #LSXRUN,LB.STX(J) ;IS THE DMC-11 UP
BNE 10$ ;IF SO, LEAVE WELL ENOUGH ALONE
JSR PC,DMCRST ; OTHERWISE WAKE IT UP.
BCS DMCJML ;THE DMC IS DEAD, LONG LIVE THE DMC!
10$: MOV LB.SLA(J),R4 ;GET THE DMC-11 HDW ADDRESS
ASSERT #MC.IEO SET IN 2(R4) ;MAKE SURE INTERRUPT ENABLE SET
;HERE TO PROCESS INPUT MESSAGES
JSR PC,DMCBTC ;TRY TO COPY A MESSAGE
BCS DMCXMT ;IF CAN'T, GO CHECK XMIT
SAVE <J> ;WORRY ABOUT "J"
BIT #LSXMAI,LB.STX(J) ;IF WE'RE IN MAINT MODE
BNE 20$ ;USE DIFFERENT SERVICE ROUTINE
JSR PC,DDDGIV ;GIVE THE MSG TO (NSP ! NCL)
RESTORE <J> ;GET LINE BLOCK ADDR BACK
BR 10$ ; AND GO LOOK FOR MORE WORK.
;HERE IF THIS IS A MAINT MSG
20$: SAVE <R0> ;REMEMBER THE MESSAGE ADDR
JSR PC,DRBOOT ;GIVE IT TO NCL
RESTORE <R0,J> ;GET MSG & LB ADDR BACK
JSR PC,FRECKS ;FREE THE MESSAGE.
JSR PC,DMCRST ;RESTART THE DMC (RESET TIMER)
BCS DMCJML ;IF DMC IS KROAKED, LEAVE IT ALONE
; BR DMCXMT ;SEE IF ANYTHING TO SEND
.SBTTL DMCXMT - GIVE THE DMC-11 SOMETHING TO DO.
;DMCXMT ROUTINE TO GIVE THE DMC-11 FULL BUFFERS TO EMPTY OR EMPTY
; ONES TO FILL. CALLED FROM DMCINP OR DDCMP WHENEVER IT THINKS
; WE HAVE A MESSAGE TO SEND
;GENERAL ORDER OF BUSINESS IS:
; 1) IF IN BOOT MODE, GO TO DMCXMB - OTHERWISE CONTINUE
; 2) SEE IF WE'VE QUEUED TOO MANY BUFFERS. IF SO, EXIT.
; 3) GET A DMC-11 MESSAGE BUFFER, FILL IT, QUEUE IT ON THE
; OUTPUT QUEUE, AND GIVE THE "NOT QUITE SENT" MESSAGE
; BACK TO HIGHER LEVEL (THIS IS OK SINCE WE NOW HAVE OUR
; OWN COPY)
DMCXMT: MOV LB.BOO(J),R0 ;IN BOOT MODE?
BNE DMCXMB ;IF SO, DO IT ALL DIFFERENT.
CMP #DMCOBF,LB.OUT+CBQ.CT(J);HAVE WE SENT OUR LIMIT
ASSERT GE ;JUST CHECKING
BEQ 40$ ;WE HAVE. TRY TO QUEUE INPUT
JSR PC,DMCGTB ;GET A DMC-11 MESSAGE BUFFER
BCS DMCWAI ;IF NONE, SET WAIT & EXIT
PIOFF ;NO INTS WHILE LOOKING AT Q
MOV LB.OBF(J),R0 ;GET NEXT MESSAGE TO GO
BEQ 20$ ;GET OUT IF NONE
MOV CN.MLK(R0),LB.OBF(J) ;MAKE THE SECOND THE NEW FIRST
BNE 10$ ;IF THERE IS NO SECOND, THEN
CLR LB.OBF+2(J) ; CLEAR THE "LAST"
10$: DEC LB.OBF+4(J) ;DEC THE COUNT OF MESSAGES
CLR CN.MLK(R0) ;IT'S NOT POLITE TO POINT
20$: PION ;CONSISTANCY...
TST R0 ;DID WE WIN A MESSAGE?
BEQ 30$ ;IF NOT, RETURN BUFFER & LEAVE
JSR PC,DMCCTB ;COPY TO MESSAGE BUFFER & SEND
JSR PC,COMODN ;RETURN MESSAGE TO SENDER.
BR DMCXMT ; AND TRY FOR ANOTHER
30$: JSR PC,DMCGVB ;RETURN THE MESSAGE BUFFER
40$: JSR PC,DMCQRB ;QUEUE RECEIVE BUFFERS
BCC DMCJML ;ALL DONE UNLESS NO BUFFERS
;HERE TO SET "WAIT" AND RETURN
DMCWAI: BIS #LSXWAI,LB.STX(J) ;INDICATE WE WANT TO RUN
DMCJML: JMP LOOP ; AND WE'RE DONE
;HERE TO TRY TO SEND BOOTSTRAP MESSAGES
;FLOW IS:
; 1) SET MAINT MODE, CLEAN OUT & RESTART THE DMC-11
; 2) MAKE SURE WE HAVE A RESPONSE BUFFER
; 3) COPY & QUEUE THE MAINT MESSAGE.
;
;ENTER WITH MESSAGE IN R0
DMCXMB: CLR LB.BOO(J) ;INDICATE THE MESSAGE HAS GONE
BIS #LSXMAI,LB.STX(J) ;SET MAINT MODE
JSR PC,DMCRST ; AND MAKE SURE THE DMC-11 KNOWS
; BCS 10$ ;DMC IS DEAD, FORGET IT
; JSR PC,DMCQRB ;QUEUE A RESPONSE BUFFER
BCS 10$ ; SET "WAIT" IF NONE AVAILABLE
JSR PC,DMCGTB ;GET A DMC-11 MESSAGE BUFFER
BCS 10$ ; SET "WAIT" IF NONE
JSR PC,DMCCTB ;COPY & QUEUE THE BUFFER
JSR PC,FRECKS ;FREE THE BOOT MESSAGE
BR DMCJML ; ALL DONE
;HERE TO TOSS THE BOOT REQUEST IF NO MEMORY... (OR IF THE DMC IS DEAD)
10$: JSR PC,FRECKS ;FREE THE MESSAGE
BR DMCWAI ; AND SET WAIT
.SBTTL DMCAIN - DMC-11 "A" VECTOR INTERRUPT
;WE DON'T USE THEM
DMCAIN: TRAP ;WASTE A WORD...
.SBTTL DMCBIN - DMC-11 "B" VECTOR INTERRUPT
;DMCBIN ROUTINE TO PROCESS "B" VECTOR INTERRUPTS.
;GENERAL ORDER OF BUSINESS:
; 1) READ THE REGISTERS AND SEE WHAT TYPE OF INTERRUPT IT IS:
; DISPATCH TO
; BUFFER INPUT
; BUFFER OUTPUT
; CONTROL OUT
; 2) PROCESS MESSAGE, IF NECESSARY RESTART THE LINE & EXIT.
.ENABL LSB
DMCBIN: SAVE <R0,R1,R2,R4> ;WE DARE NOT CLOBBER ANYTHING
MOV LB.SLA(J),R4 ;GET THE DMC-11 BASE ADDRESS
MOV SEL2(R4),R0 ;COPY THE DMC REGISTERS
MOV SEL4(R4),R1 ; INTO OUR
MOV SEL6(R4),R2 ; REGISTERS
ASSERT #MC.RDO SET IN R0 ;THIS IS AN OUTPUT TRANSACTION
ASSERT #2 CLEAR IN R0 ;JUST RANDOM CHECKING...
BIC #MC.RDO,SEL2(R4) ;CLEAR REQUEST (WE COPYED INFO)
BIT #1,R0 ;IS THIS A CONTROL OUT?
BNE DMCCOO ; IF SO,GO PROCESS IT
SUB #CB.DAT,R1 ;RELOCATE TO BUFFER BASE
BIT #MC.IOI,R0 ;IS IT INPUT OR OUTPUT
BNE DMCBOI ;BRANCH IF IT'S AN INPUT BFR
DMCBOO: ASSERT R1 EQ LB.OUT+CBQ.FS(J) ;MAKE SURE IT'S THE RIGHT ONE.
JSR R0,DMCDEQ ;DEQUEUE THE OUTPUT BUFFER
.WORD LB.OUT ; FROM THE "OUTPUT" QUEUE
JSR PC,DMCGVB ;RETURN THE BUFFER TO FREE POOL
CLR LB.TMO(J) ;INDICATE THAT OUTPUT IS WORKING
INC LB.OCN(J) ;COUNT UP MESSAGES FOR DDT-11 TO SEE
TST LB.OBF(J) ;IF THERE ARE ANY MORE MSGS
BNE 98$ ; THEN WAKE UP LOW LEVEL
BR 99$ ; OTHERWISE JUST RETURN
;HERE FOR BUFFER IN-INPUT
DMCBOI: ASSERT R1 EQ LB.INQ+CBQ.FS(J) ;MAKE SURE IT'S THE RIGHT ONE
JSR R0,DMCDEQ ;DEQUEUE THE BUFFER FROM THE
.WORD LB.INQ ; "INPUT WAIT" QUEUE
BIC #140000,R2 ;"FIXUP" THE COUNT
MOV R2,CB.CNT(R1) ; AND STORE IT IN THE BUFFER
JSR R0,DMCENQ ;REQUEUE THE BUFFER ON THE
.WORD LB.IND ; "INPUT DONE" QUEUE
INC LB.ICN(J) ;COUNT UP MESSAGES FOR DDT-11 TO SEE
BR 98$ ;WAKE UP LOW LEVEL AND RETURN
;HERE ON A CONTROL OUT TRANSACTION
DMCCOO: BIT #MC.LOS!MC.NXM!MC.ERR,R2;ANY "FATAL" ERROR
;RDH ASSERT EQ ;DIE IF DEBUGGING
;RDH BNE 97$ ;RESTART THE LINE
BEQ 3$ ;HAPPY IF NO ERROR
TWIDDLE ;NOTE ERROR OCCURRED
BR 97$ ;RESTART THE LINE
3$: BIT #MC.RMA,R2 ;IF NOT MAINT RECEIVED
BEQ 10$ ; THEN KEEP CHECKING
TWIDDLE ;COUNT THE MAINT MSGS
BIS #LSXMAI,LB.STX(J) ;ENTER MAINT MODE
BR 97$ ;RESET THE LINE AND EXIT
10$: BIT #MC.RST!MC.DSC,R2 ;IF SOME ONE JUST CAME UP
BEQ 20$ ; (IF NOT SKIP ON)
TWIDDLE ;COUNT THE RESTARTS
; BIT #LSXMAI,LB.STX(J) ;IF WE'RE NOT IN MAINT MODE.
; BEQ 97$ ;JUST QUEUE THE LINE AND EXIT
BIC #LSXMAI,LB.STX(J) ;CLEAR MAINT MODE
BR 97$ ; AND RESET THE LINE
20$: BIT #MC.DCK!MC.TMO!MC.OVR,R2;MAKE SURE A BIT IS SET
ASSERT NE ;OTHERWISE WHAT HAPPENED?
TWIDDLE ;COUNT THE TIMES THIS HAPPENS
BR 99$ ; BUT JUST IGNORE IT.
97$: JSR PC,DMCHLT ;HALT THE DMC (LOOP RESTARTS IT)
BCS 99$ ;IGNORE THE DMC IF IT'S BUSTED
98$: QUEPUT QI ;TELL LOOP TO LOOK AT THIS LINE
99$: RESTORE <R4,R2,R1,R0,J> ;RESTORE THE REGS
RTI ; AND WE'RE DONE
.DSABL LSB
.SBTTL DMCIN - ROUTINE TO PERFORM DMC-11 INPUT TRANSACTIONS
;DMCIN ROUTINE TO REQUEST THE DMC-11 INPUT PORT AND STUFF IT.
;CALL R0 := REQUEST TYPE (LOW 3 BITS)
; R1 := SEL4 DATA (BUFFER ADDRESS)
; R2 := SEL6 DATA (COUNT)
DMCIN: SAVE <R4> ;CLOBBER NO MORE THAN NECESSARY
MOV LB.SLA(J),R4 ;GET THE DMC-11 BASE ADDR
SAVE <R0> ;SAVE THE TRANSACTION FOR A BIT
MOV #2000,R0 ;FIRST WAIT FOR THE LAST TRANSACTION
10$: BIT #MC.RDI,@R4 ; TO BE ACCEPTED (IE. RDYI CLEAR)
BEQ 20$ ;WHEN IT IS, WE CAN PROCEED
SOB R0,10$ ;LOOP TILL IT CLEARS (OR WE GET BORED)
TRAP ;DMC DIDN'T CLEAR RDYI. MUST BE BROKEN
20$: BIS #MC.RQI,@R4 ;REQUEST THE INPUT PORT
MOV #2000,R0 ;GET A NICE ROUND NUMBER
30$: BIT #MC.RDI,@R4 ;INPUT READY YET?
BNE 40$ ; IF SO, GO STUFF IT
SOB R0,30$ ;LOOP FOR A BIT
TRAP ;DMC-11 IS BAD
40$: MOV R1,SEL4(R4) ;STORE THE VARIOUS
MOV R2,SEL6(R4) ; REGISTERS AWAY
RESTORE <R0> ;GET REQUEST BACK
BIC #^C7,R0 ;GET JUST THE TRANSACTION CODE
BIS #MC.RDI,R0 ;DON'T CLEAR RDYI
MOVB R0,SEL0(R4) ;DISMISS THE DMC-11
RESTORE <R4> ;GET REG 4 BACK
RTS PC ; AND WE'RE DONE
.SBTTL DMCQRB - QUEUE RECEIVE BUFFERS FOR THE DMC-11
;DMCQRB ROUTINE TO QUEUE RECEIVE BUFFERS
;CALL JSR PC,DMCQRB
;RETURN CARRY-SET ;NOT ENOUGH FREE BUFFERS
; CARRY-CLEAR ;DMC-11 HAS BEEN FED ADEQUATELY
;IF IN MAINT MODE, ONLY ONE RECEIVE BUFFER WILL BE QUEUED.
;IF IN "NORMAL" MODE, "DMCIBF" BUFFERS WILL BE QUEUED.
DMCQRB: SAVE <R0,R1,R2> ;WE CLOBBER NO REGISTERS
10$: BIT #LSXMAI,LB.STX(J) ;ARE WE IN MAINT MODE?
BEQ 20$ ;IF NOT, GO SEE CHECK COUNT
TST LB.INQ+CBQ.CT(J) ;IF MAINT, SEE IF ANY QUEUED
BNE 98$ ;ONE BUFFER IS ENOUGH IN MAINT.
20$: CMP #DMCIBF,LB.INQ+CBQ.CT(J);SEE HOW MANY BUFFERS QUEUED
ASSERT GE ;MAKE SURE NOT TOO MANY
BEQ 98$ ;IF JUST ENOUGH, WE'RE DONE
JSR PC,DMCGTB ;GET A FREE BUFFER
BCS 99$ ;IF NONE, GO GIVE ERROR RETURN
JSR R0,DMCENQ ;QUEUE THE BUFFER ON THE
.WORD LB.INQ ; THE INPUT WAIT QUEUE
MOV #MC.IOI!MC.BCI,R0 ;DO A BUFFER IN (INPUT)
ADD #CB.DAT,R1 ;ADDRESS OF DATA AREA OF MSG
MOV #MSGMAX,R2 ;AND THIS IS HOW LONG IT IS
JSR PC,DMCIN ;DO THE INPUT TRANSACTION
BR 10$ ;GO TRY ANOTHER
98$: CLC ;INDICATE SUCCESS
99$: RESTORE <R2,R1,R0> ;CLEAN UP
RTS PC ; AND RETURN
.SBTTL DMCCTB - ROUTINE TO COPY FROM CHUNK-MESSAGE TO MESSAGE BUFFER
;DMCCTB - COPY FROM CHUNK-STREAM TO DMC-11 BUFFER
;CALL R0 := POINTER TO THE CHUNKS
; R1 := POINTER TO THE DMC-11 MESSAGE BUFFER
; JSR PC,DMCCTB ;COPY FROM CHUNKS TO BUFFER
;RETURN WITH MESSAGE BUFFER QUEUED ON LB.OUT
DMCCTB: SAVE <R2,R3> ;WE USE THESE IN A BIT
MOV CN.LEN(R0),R2 ;MESSAGE LENGTH (BYTES)
MOV R2,CB.CNT(R1) ;REMEMBER IT IN THE BUFFER
INC R2 ;ROUND UP WHEN CONVERTING
ASR R2 ; BYTE COUNT TO WORDS.
SAVE <R0,R1> ;SAVE THE TWO POINTERS
ADD #CN.NCT,R0 ;MAKE THEM BOTH POINT TO THEIR
ADD #CB.DAT,R1 ; RESPECTIVE DATA AREAS
MOV #CNKLN1/2,R3 ;GET LENGTH OF FIRST CHUNK
BR 20$ ; AND SKIP INTO THE LOOP
10$: MOV #CNKLN2/2,R3 ;GET LENGTH OF SUBSEQUENT CHUNKS
20$: SUB R3,R2 ;ACCOUNT FOR THIS CHUNK'S WORTH
BLE 40$ ;EXIT IF THIS IS LAST CHUNK
30$: MOV (R0)+,(R1)+ ;COPY ONE WORDS WORTH
SOB R3,30$ ;LOOP OVER ENTIRE CHUNK
MOV -CNKSIZ(R0),R0 ;FOLLOW CHUNK LIST
ASSERT CHUNK R0 ;MAKE SURE IT'S REASONABLE
TST (R0)+ ;ADVANCE TO THE DATA PORTION
BR 10$ ; AND GO DO NEXT CHUNK
40$: ADD R2,R3 ;R3 := LENGTH OF LAST FRAGMENT
50$: MOV (R0)+,(R1)+ ;COPY NEXT WORD
SOB R3,50$ ;LOOP OVER ALL WORDS
RESTORE <R1> ;GET OUR MSG POINTER BACK
TST LB.OUT+CBQ.CT(J) ;IF WE ALREADY HAVE OUTPUT BUFFERS
BNE 60$ ; QUEUED, DON'T TOUCH THE REP TIMER
CLR LB.TMO(J) ;OTHERWISE, START TIMER FRESH
60$: JSR R0,DMCENQ ;QUEUE THE BUFFER ON
.WORD LB.OUT ; THE OUTPUT QUEUE
MOV #MC.BCI,R0 ;BA/CC IN -- OUTPUT TRANSACTION
MOV CB.CNT(R1),R2 ;LENGTH OF THE BUFFER
ADD #CB.DAT,R1 ;ADDRESS OF THE BUFFER
JSR PC,DMCIN ;DO THE BUFFER IN TRANSACTION
RESTORE <R0,R3,R2> ;FIXUP THE STACK
RTS PC ; AND WE'RE DONE.
.SBTTL DMCBTC - COPY DMC-11 BUFFER DATA TO CHUNKS.
;DMCBTC ROUTINE TO DEQUEUE INPUT MESSAGES AND COPY THEM TO CHUNKS.
;CALL JSR PC,DMCBTC
;RETURN CARRY-SET ;NO MESSAGE AVAILABLE
; CARRY-CLEAR ;R0 := CHUNK-STREAM
;GENERAL LOGIC:
; 1) FIRST MAKE SURE THERE IS A MESSAGE. RETURN QUICK IF NOT.
; 2) NEXT TRY TO ALLOCATE ENOUGH CHUNKS FOR THE MESSAGE. IF
; NOT AVAILABLE, SET "LSXWAI" AND GIVE ERROR RETURN
; 3) NOW DEQUEUE THE MESSAGE BUFFER (WE DAREN'T DO IT TILL WE
; HAVE THE CHUNKS. NOTE THAT THERE IS THE POSSIBILITY OF
; INTERRUPT LEVEL CLOBBERING THE OUTPUT QUEUE. WE HAVE TO
; CHECK FOR THIS CASE BY COMPARING LENGTH BEFORE AND AFTER)
; 4) COPY THE DATA AND RETURN THE DMC-11 MESSAGE BUFFER.
DMCBTC: TST LB.IND+CBQ.CT(J) ;ANYTHING ON THE OUTPUT QUEUE
BNE 10$ ;SKIP ON IF SO
SEC ;IF NO MESSAGE, SET ERROR FLAG
RTS PC ; AND RETURN
10$: SAVE <R1,R2,R3> ;SAVE THE REGS WE CLOBBER
MOV LB.IND+CBQ.FS(J),R1 ;GET POINTER TO OUTPUT MSG
BEQ 98$ ;RACE WITH INTERRUPT LEVEL
MOV CB.CNT(R1),R1 ;GET THE LENGTH OF THE MESSAGE
MOV R1,R2 ; AND A COPY FOR LATER
ASSERT GT ;MAKE SURE IT'S REASONABLE
JSR PC,GETCNK ;GET THE FIRST CHUNK
BEQ 98$ ;IF NONE, SET "LSXWAI" & RETURN
SUB #CNKLN1,R1 ;DECREMENT REMAINING COUNT
BLE 30$ ;BRANCH IF 1 WAS ENOUGH
20$: MOV R0,R3 ;STORE THIS CHUNK IN R3
JSR PC,GETCNK ;GET THE NEXT CHUNK
BEQ 97$ ;IF NONE, FREE R3'S LIST & EXIT
MOV R3,@R0 ;MAKE R0 POINT TO R3'S LIST
SUB #CNKLN2,R1 ;ACCOUNT FOR 1 MORE CHUNK
BGT 20$ ;LOOP TILL WE'VE GOT EHOUGH
30$: MOV R0,R3 ;SO WE CAN USE "97$" TO "FAIL"
PIOFF ;NOW WE REALLY DEQUEUE THE MSG.
MOV LB.IND+CBQ.FS(J),R1 ;R1 -> DMC-11 BUFFER
BEQ 96$ ;IF CLOBBERED, GET OUT NOW
CMP R2,CB.CNT(R1) ;SEE IF LENGTH IS STILL THE SAME
BNE 96$ ;IF NOT, INT LEVEL WON RACE...
JSR R0,DMCDEQ ;FINALLY DEQUEUE THE MESSAGE
.WORD LB.IND ;FROM THE "INPUT DONE" QUEUE
ASSERT CC ;WE KNOW THAT THERE IS ONE THERE
PION ;ALL'S CLEAR NOW.
;DMCBTC CONTINUED
;AT THIS POINT WE HAVE:
; R0 := POINTER TO THE EMPTY CHUNK STREAM
; R1 := POINTER TO THE NOW DEQUEUED MESSAGE BUFFER
; R2 := THE BYTE COUNT FOR THE TRANSFER.
CLR CN.MLK(R0) ;DON'T POINT...
MOV R2,CN.LEN(R0) ;SET THE MESSAGE LENGTH UP
ASSERT GT ;JUST CHECKING...
INC R2 ;ROUND BYTES UP
ASR R2 ; WHEN CONVERTING TO WORD COUNT
SAVE <R0,R1> ;SAVE THE MESSAGE POINTERS
ADD #CN.NCT,R0 ; AND MAKE THEM BOTH POINT TO
ADD #CB.DAT,R1 ; THEIR RESPECTIVE DATA ARESA
MOV #CNKLN1/2,R3 ;GET # WORDS IN 1ST CHUNK
BR 50$ ; AND SKIP INTO THE CHUNK LOOP
40$: MOV #CNKLN2/2,R3 ;# WORDS IN SUBSEQUENT CHUNKS
50$: SUB R3,R2 ;ACCOUNT FOR THIS CHUNK FULL
BLE 70$ ;SKIP OUT IF THIS IS LAST CHUNK
60$: MOV (R1)+,(R0)+ ;COPY NEXT WORDS WORTH
SOB R3,60$ ;LOOP OVER ENTIRE CHUNK
MOV -CNKSIZ(R0),R0 ;FOLLOW MESSAGE POINTER
ASSERT CHUNK R0 ;MAKE SURE IT'S A CHUNK POINTER
TST (R0)+ ;SKIP OVER THE LINK WORD.
BR 40$ ; AND GO FILL THIS CHUNK
70$: ADD R2,R3 ;R3 := LENGTH OF LAST FRAGMENT
80$: MOV (R1)+,(R0)+ ;MOVE THE NEXT WORD
SOB R3,80$ ;LOOP OVER ALL OF LAST SEGMENT
RESTORE <R1,R0> ;GET OUR POINTERS BACK
JSR PC,DMCGVB ;RETURN THE MESSAGE BUFFER
CLC ;INDICATE SUCCESS
BR 99$ ; AND RETURN
96$: PION ;FIXUP THE STACK
97$: MOV R3,R0 ;GET ADDR OF CHUNKS TO FREE
JSR PC,FRECKS ; AND FREE THE LIST OF THEM
98$: BIS #LSXWAI,LB.STX(J) ;SET "WAITING FOR MEMORY"
SEC ;SAY WE FAILED
99$: RESTORE <R3,R2,R1> ;PUT BACK THE REGS
RTS PC ; AND WE'RE DONE
.SBTTL ENQDEQ - ROUTINES TO QUEUE AND DEQUEUE MESSAGE BUFFERS
.ENABL LSB
;DMCENQ ROUTINE TO ENQUEUE A BUFFER ON A LINE-BLOCK QUEUE
;CALL R1 := BUFFER TO QUEUE
; J := LINE BLOCK ADDRESS
; JSR R0,DMCENQ ;CALL THIS ROUTINE
; .WORD LB.QUE ; NEXT WORD IS QUEUE OFFSET
DMCENQ: SAVE <R2> ;WE CLOBBER NO REGS
PIOFF ;NO RACES WITH INT LEVEL
MOV (R0)+,R2 ;GET OFFSET (FIXUP RET ADDR)
ADD J,R2 ;MAKE RELATIVE OFFSET ABSOLUTE
CLR (R1) ;MAKE SURE MSG DOESN'T POINT
TST CBQ.CT(R2) ;IF THE QUEUE ISN'T EMPTY
BNE 10$ ;JUST PUT THIS MSG ON FRONT
MOV R1,CBQ.FS(R2) ; OTHERWISE MAKE THIS LAST
MOV R1,CBQ.LS(R2) ; AND LAST MSG IN THE LIST
BR 20$ ;GO FIXUP THE COUNT & EXIT
10$: MOV R1,@CBQ.LS(R2) ;MAKE THE LAST ENTRY POINT TO US
MOV R1,CBQ.LS(R2) ; AND MAKE US THE NEW LAST
20$: INC CBQ.CT(R2) ;FIXUP THE COUNT
BR 98$ ;RESTORE R2 AND RETURN
;DMCDEQ ROUTINE TO DEQUEUE A BUFFER FROM A LINE BLOCK QUEUE
;CALL J := LINE BLOCK ADDRESS
; JSR R0,DMCDEQ ;CALL THIS ROUTINE
; .WORD LB.QUE ;RELATIVE QUEUE OFFSET
;RETURN CARRY SET ;QUEUE WAS EMPTY
; CARRY CLEAR ; R1 := MESSAGE BUFFER
DMCDEQ: SAVE R2 ;WE ONLY CLOBBER R1
PIOFF ;NO RACES WITH INTERRUPT LEVELS
MOV (R0)+,R2 ;GET RELATIVE QUEUE OFFSET
ADD J,R2 ; MAKE IT ABSOLUTE
TST CBQ.CT(R2) ;IS THERE ANYTHING IN THE QUEUE
ASSERT GE ;BETTER NOT BE NEGATIVE
BEQ 97$ ;IF EMPTY, GIVE ERROR RETURN
MOV CBQ.FS(R2),R1 ;R1 := ADDR OF THE FIRST MSG
MOV @R1,CBQ.FS(R2) ;MAKE OLD 2ND THE NEW 1ST.
DEC CBQ.CT(R2) ;DECREMENT THE COUNT
BNE 98$ ;IF QUEUE NOT EMPTY, WE'RE DONE
CLR CBQ.FS(R2) ;IF EMPTY, CLEAR THE FIRST
CLR CBQ.LS(R2) ; AND LAST POINTERS
BR 98$ ; AND RETURN
97$: PION ;RESTORE PI'S
SEC ;INDICATE ERROR (NO BUFFERP
BR 99$ ;RESTORE R2 & RETURN
98$: PION ;ENABLE INTERRUPTS
CLC ;INDICATE SUCCESS
99$: RESTORE <R2> ;RESTORE THE REG
RTS R0 ; AND WE'RE DONE
.DSABL LSB
.SBTTL BFRMGT - DMC-11 MESSAGE BUFFER MANAGEMENT.
;DMCGTB ROUTINE TO ALLOCATE A DMC-11 MESSAGE BUFFER
;CALL JSR PC,DMCGTB ;TRY TO GET A BUFFER
;RETURN CARRY-SET ;NO BUFFER
; CARRY-CLEAR ;R1 := BUFFER
DMCGTB: PIOFF ;NO INTS PLEASE
TST CBFREC ;SEE IF WE HAVE ANY
ASSERT GE ;NEGATIVE COUNT ILLEGAL
BEQ 10$ ;FAIL IF NO FREE BUFFERS
MOV CBFSTF,R1 ;GET ADDR OF FIRST
ASSERT NE ;BETTER BE ONE.
MOV @R1,CBFSTF ;MAKE THE OLD SECOND NEW FIRST
DEC CBFREC ;COUNT OFF THE BUFFER
PION ;INTS OK NOW
CLC ;SAY WE WON
RTS PC ; AND RETURN
10$: PION ;INTS OK
SEC ;SAY WE FAILED
RTS PC ; AND RETURN
;DMCGVB ROUTINE TO RETURN DMC-11 MESSAGE BUFFERS
;CALL R1 := ADDR OF BUFFER
; JSR PC,DMCGVB ;RETURN THE BUFFER
DMCGVB: PIOFF ;NO INTERRUPTS
MOV CBFSTF,@R1 ;MAKE THIS POINT TO OLD HEAD
MOV R1,CBFSTF ;MAKE THIS THE NEW HEAD
INC CBFREC ;INCREMENT THE FREE COUNT
PION ;INTS OK NOW
RTS PC ; AND WE'RE DONE
.ENDC; IF NE DMCN (AND OF DNCDMC.P11)