.title RNDYN .ident 'BL1.0' .ifndf RT11 .MCALL EXTK$S .ENDC ; ; The symbol $DEBUG is enabled for complte dynamic subroutine checking ; It is off for normal operation since it slows down the routines. ;$DEBUG=1 ; ; The following routines are designed to handle buffers in dynamic ; pool memory. The data should be accessed via these routines. The offset ; BF.FUL is the index to your data bytes. The BF.ADD always points to the ; current byte (one accessed last by GBYT or PBYT). All data in buffers should ; be accessed via these routines. The FNDBF,BEGBF routines set the ; current data index (BF.FUL) so you may use GBYT or GWRD to get the data. You ; may access the current byte via the offset BF.ADD,when BF.FUL is non zero, ; but no other bytes ; may be accessed directly. The routine RSTBF, and ENDBF set the index and then ; set up the buffers for input via PBYT,PWRD. After BEGBF,FNDBF reading past ; past the end of the data is inhibited. If CARRY is set the routine failed. ; CLRBF resets all pointers, deallocates all dynamic memory and sets the ; buffer chain ready for input. ; BFSIZ == 200 ; 128. BYTES OF DATA (MUST BE POWER OF 2) BFMSK = 177 ; BUFFER SIZE MASK ; ; R3=BUFFER HEADER ; R1=DATA BYTE TO PUT/GET/FIND ; R0=DESTROYED BY THESE ROUTINES ; ; ROUTINE TO CLEAR BUFFER FOR MORE DATA ; EXTRA BUFFERS ARE RETURNED TO POOL ; .psect $CODE,RO,LCL,CON,I CLRBF:: .IFDF $DEBUG TST BF.CNT(R3) ; *** DEBUG BGE 2$ ; *** DEBUG 1$: CALL HLTER ; *** DEBUG 2$: TST BF.FUL(R3) ; *** DEBUG BLT 1$ ; *** DEBUG .ENDC MOV #BFSIZ,BF.CNT(R3) ; SET UP COUNT CLR BF.FUL(R3) ; RESET INDEX CLR BF.MAX(R3) ; RESET DATA RESIDENT CLR BF.SPC(R3) ; clear spacing char count CLR BF.VSP(R3) ; Clear vertical spacing CLR BF.HED(R3) ; Clear header MOV BF.BEG(R3),R0 ; GET HEAD OF CURRENT BUFFER CHAIN MOV R0,BF.CAD(R3) ; INTO CURRENT BUFFER ADDRESS MOV R0,BF.ADD(R3) ; INTO DATA ADDRESS ADD #BFLNK,BF.ADD(R3) ; POINTS TO FIRST BYTE-1 RETBF: MOV BF.CAD(R3),R0 ; Current buffer TSTEQ (R0),10$ ; No more in chain ? MOV LNKHD,@BF.END(R3) ; ADD BUFFER POOL TO END OF CHAIN MOV (R0),LNKHD ; PUT COMPLETE CHAIN INTO POOL MOV R0,BF.END(R3) ; LAST = current BUFFER CLR (R0) ; AND NO FOREWARD LINK 10$: RETURN ; ; RSTBF ; ROUTINE TO SET REQUESTED LOCATION AS END OF BUFFER READY FOR INPUT ; ; ENDBF ; SET TO END OF BUFFER + MADE READY FOR PUTTING TO BUFFER ; ENDBF:: MOV BF.MAX(R3),R1 ; LOCATION TO GO TO .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC CMP R1,BF.FUL(R3) ; is R1 really the end? BHIS RSTBF ; yes MOV BF.FUL(R3),R1 ; no get real end RSTBF:: CALL FNDBF ; GO TO IT CLR BF.MAX(R3) ; CLEAR DATA MAX MOV #BFSIZ,BF.CNT(R3) ; BUFFER SIZE SUB R1,BF.CNT(R3) ; ADJUST COUNT TO BUFFER SIZE .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC BR RETBF ; ; ROUTINE SETS POINTER TO START OF BUFFER FOR GETTING DATA ; IF BUFFER HAS NO DATA MAX SET, MAX=CURRENT INDEX ; BEGBF:: .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC MOV BF.BEG(R3),R0 ; FIRST BUFFER MOV R0,BF.CAD(R3) ; IS CURRENT ONE ADD #BFLNK,R0 ; POINTS TO DATA-1 MOV R0,BF.ADD(R3) ; SAVED MOV BF.MAX(R3),R0 ; MAX DATA SET? CMP R0,BF.FUL(R3) ; GREATER THAN CURRENT BHIS 10$ ; YES MOV BF.FUL(R3),R0 ; TOTAL DATA IN BUFFER MOV R0,BF.MAX(R3) ; SET SIZE TO MAX DATA SIZE 10$: CLR BF.FUL(R3) ; CLEAR INDEX CMP R0,#BFSIZ ; SMALLER THAN BUFFER SIZE? BLOS 20$ ; YES MOV #BFSIZ,R0 20$: MOV R0,(R3) ; SET COUNT .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC RETURN ; ; GETS NEXT BYTE FROM BUFFER ; R1=data ; C = set if no data available ; Z = set if R1 is zero ; N = set if Byte R1 is negative ; V = undefined ; GBYT2:: MOV R2,R1 ; Address of header BR GBYT1 GBYT:: MOV R3,R1 ; FOR GBYT1 ; ; SPECIAL PURPOSE ROUTINE ; R1=HEADER (INPUT) ; R1=DATA BYTE (OUTPUT) ; GBYT1:: DEC (R1)+ ; DECREMENT COUNT BLT 20$ ; Nothing in buffer ? INC (R1)+ ; INCREMENT INDEX INC (R1) ; NEXT BYTE MOVB @(R1),R1 ; GET BYTE CLC RETURN 20$: INC -(R1) ; Restore count to zero BLT 50$ ; Bad CNT CMP BF.FUL(R1),BF.MAX(R1); Compare number of chars with max BEQ 40$ ; At end of buffers ? BHI 50$ ; ERROR ????? MOV BF.CAD(R1),R0 ; GET CURRENT BUFFER MOV (R0),R0 ; GET NEXT BUFFER IN CHAIN BEQ 50$ ; NONE ?? MOV BF.MAX(R1),(R1) ; NUMBER OF BYTES TOTAL SUB BF.FUL(R1),(R1) ; LESS INDEX=REMAINING CMP (R1),#BFSIZ ; IS IT OK BLOS 30$ ; YES MOV #BFSIZ,(R1) ; NO, MAKE IT FULL BUFFER SIZE 30$: MOV R0,BF.CAD(R1) ; NEW CURRENT BUFFER ADD #BFLNK,R0 ; POINTS TO BYTE IN FRONT OF FIRST MOV R0,BF.ADD(R1) ; SAVE ADDRESS .IFDF $DEBUG MOV R3,-(SP) ; *** DEBUG MOV R1,R3 ; *** DEBUG CALL TEST ; *** DEBUG Test the params MOV (SP)+,R3 ; *** DEBUG .ENDC BR GBYT1 ; AND GET THE BYTE FINALLY 40$: CLR R1 SEC RETURN 50$: CALL HLTER ; bad count ; ; PUTS NEXT BYTE INTO BUFFER ; PBYT:: MOV R3,R0 ; BUFFER HEADER .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC DEC (R0)+ ; DECREMENT REMAINDER COUNT BLT 10$ ; YES, TRY TO ALLOCATE ANOTHER INC (R0)+ ; INCREMENT INDEX INC (R0) ; INCREMENT ADDRESS MOVB R1,@(R0) ; SAVE BYTE CLC RETURN 10$: BITEQ #BFMSK,BF.FUL(R3),11$ ; legal end of buffer? CALL HLTER ; NO NO NO 11$: MOV BF.CAD(R3),R0 ; CURRENT ADDRESS MOV (R0),R0 ; ANOTHER IN CHAIN? BNE 20$ ; YES, USE IT MOV LNKHD,R0 ; GET BUFFER FROM POOL? BEQ 30$ ; NONE! MOV (R0),LNKHD ; REMOVE IT FROM POOL CLR (R0) ; NO FOREWARD LINK MOV BF.END(R3),2(R0) ; BACKWARD LINK MOV R0,BF.END(R3) ; MAKE THIS LAST BUFFER MOV R0,@BF.CAD(R3) ; FOREWARD LINK 20$: MOV #BFSIZ,(R3) ; NEW COUNT MOV R0,BF.CAD(R3) ; NEW CURRENT BUFFER ADD #BFLNK,R0 ; POINTS TO BYTE IN FRONT OF FIRST MOV R0,BF.ADD(R3) ; SAVE ADDRESS .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC BR PBYT ; AND GET THE BYTE FINALLY 30$: .ifndf RT11 CALL EXTEND ; try to extend memory BCC 10$ ; success .ENDC MOV #12.,R0 ; No more memory avilable message JMP ILINP ; KILL KILL KILL ; ; ROUTINE GETS 1 WORD FROM BUFFERS ; R1=data ; C = set if no data ; N,Z correspond to value in R1 ; GWRD:: CALL GBYT ; GET LOWER BYTE MOV R1,-(SP) ; SAVE IT CALL GBYT ; NEXT UPPER MOVB R1,1(SP) MOV (SP)+,R1 ; GET RESULT RETURN ; ; ROUTINE TO SAVE 1 WORD ; PWRD:: CALL PBYT ; SAVE LOWER BYTE SWAB R1 ; SWAP BYTES CALL PBYT ; SAVE UPPER SWAB R1 RETURN ; ; extend the available pool ; .ifndf RT11 EXTEND: EXTK$S #5 BCC 10$ RETURN 10$: ADD #500,XTOP ; readjust top for extend .endc ; ; subroutine to set up linked list of dynamic buffers ; LNKSET:: MOV XBOT,R0 ; save value ADD #BFSIZ+6,XBOT ; raise bottom BCS 40$ ; done CMP XBOT,XTOP ; too big BHIS 40$ ; yes MOV LNKHD,(R0) ; Save current linked buffer chain MOV R0,LNKHD ; put current buffer at head BR LNKSET ; try another 40$: SUB #BFSIZ+6,XBOT ; restore CLC RETURN ; ; SETS POINTERS TO BUFFER LOCATION SPECIFIED IN R1 ; C SET if bad location specified ; C CLEAR if good location ; IF MAX IS NOT SET MAX=CURRENT INDEX ; FNDBF:: CMPNE R3,BUFADD,1$ ; Not primary input buffer? ; ; Section for input buffer only ; CMP R1,BF.MAX(R3) ; ADDRESS TOO BIG? BHI 15$ ; Yes ? MOV BF.MAX(R3),BF.CNT(R3) ; MAX SIZE SUB R1,BF.CNT(R3) ; NOW REMAINING SIZE MOV BF.CAD(R3),BF.ADD(R3) ; START OF BUFFER ADD #BFLNK,BF.ADD(R3) ; POINTS TO DATA-1 ADD R1,BF.ADD(R3) ; CURRENT ADDRESS MOV R1,BF.FUL(R3) ; NEW LOCATION CLC ; Success !! RETURN ; ; Section for all other buffers ; 1$: .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC CMP BF.FUL(R3),BF.MAX(R3) ; CURRENT BIGGER THAN MAX? BLOS 10$ ; NO MOV BF.FUL(R3),BF.MAX(R3) ; RESET MAX 10$: CMP R1,BF.MAX(R3) ; GREATER THAN CURRENT BLOS 20$ ; NO 15$: SEC RETURN ; RETURN NO ACTION 20$: MOV BF.ADD(R3),R0 ; current address SUB BF.CAD(R3),R0 ; minus cuurrent buffer address SUB #BFLNK,R0 ; minus header offset=bytes this buff. SUB R0,BF.FUL(R3) ; now set index to start of buffer MOV BF.CAD(R3),R0 ; CURRENT BUFFER SUB BF.FUL(R3),R1 ; go foreward? BHI 40$ ; yes BEQ 60$ ; already there 30$: MOV 2(R0),R0 ; GO BACK 1 BUFFER .IFDF $DEBUG BNE 35$ ; *** DEBUG CALL HLTER 35$: .ENDC SUB #BFSIZ,BF.FUL(R3) ADD #BFSIZ,R1 ; add on buffer size BCC 30$ ; NOT THERE YET BR 60$ ; FOUND CORRECT ONE 40$: SUB #BFSIZ,R1 ; done yet BLOS 50$ ; yes ADD #BFSIZ,BF.FUL(R3) MOV (R0),R0 ; GO FOREWARD .IFDF $DEBUG BNE 45$ ; *** DEBUG CALL HLTER ; *** DEBUG 45$: .ENDC BR 40$ 50$: ADD #BFSIZ,R1 ; ADJUST TO POSITIVE 60$: TSTNE R1,70$ ; index non zero? TSTEQ 2(R0),70$ ; no backward link? MOV 2(R0),R0 ; go back SUB #BFSIZ,BF.FUL(R3) ; move index back ADD #BFSIZ,R1 ; to point to last byte 70$: MOV R0,BF.CAD(R3) ; SAVE CURRENT BUFFER ADD R1,R0 ; ALMOST CURRENT DAT ADD #BFLNK,R0 ; DATA -1 MOV R0,BF.ADD(R3) ; SAVED ADD R1,BF.FUL(R3) ; CURRENT INDEX MOV BF.MAX(R3),BF.CNT(R3) ; MAX SUB BF.FUL(R3),BF.CNT(R3) ; - CURRENT = COUNT? MOV #BFSIZ,R0 ; BUFFER SIZE SUB R1,R0 ; - OFFSET = COUNT CMP R0,BF.CNT(R3) ; WHICH ONE? BGT 80$ ; PICK SMALLER MOV R0,BF.CNT(R3) ; SMALLER ONE 80$: .IFDF $DEBUG CALL TEST ; *** DEBUG .ENDC CLC ; Success !! RETURN ; ; GET TEMPORARY BUFFER ; ; R2= current buffer ; R3= temporary buffer address ; GETTBF::MOV #TMPBF,R3 ; Temporary buffer address SAVTBF::MOV R3,-(SP) ; Save R3 MOV R2,R0 ; Will be input MOV #/2,R1 ; Number of words 10$: MOV (R0)+,(R3)+ ; transfer SOB R1,10$ ; Till done MOV (SP)+,R3 ; Restore buffer address RETURN ; ; CALL TMPIN ; Set up input from temporary buffer ; R2 = String address ; = Last byte address in string+1 ; R0,R1 are destroyed ; CALL TMPINB ; R2 = Buffer header ; ; TMPINB::MOV #-1,R1 ; Indicate buffer header BR TMPIN1 TMPIN:: CLR R1 ; Indicates R2=string TMPIN1: MOVB SUBSTK,R0 ; Get stack pointer CMPB SUBSTK,SUBSTK+1 ; Check on size BGE 40$ ; Too big MOV R3,-(SP) ; Save R3 MOV R1,-(SP) ; Save R1 MOV R2,-(SP) ; Save R2 ADD #2,R0 ; Add 2 MOVB R0,SUBSTK ; New stack MOV BUFADD(R0),R3 ; Get buffer header MOV R3,BUFAD MOV #SUBF0,R2 ; Current end of buffer CALL SAVTBF ; Make current same CALL ENDBF ; go to end of buffer CLR R1 ; Deposit 0 CALL PWRD ; Chock buffer MOV BF.FUL(R3),BF.SPC(R3) ; For traceback MOV (SP)+,R2 ; Restore TSTEQ (SP)+,10$ ; String input ?? 5$: CALL GBYT2 ; Get byte BLOS 20$ ; Done ? CALL PBYT ; Save it BR 5$ ; Continue till done 10$: MOVB (R2)+,R1 ; Get 1 byte BEQ 20$ ; At end of buffer ? CALL PBYT ; Save it BR 10$ 20$: MOVB #LF,R1 ; Put line feed CALL PBYT ; Into buffer MOV BF.SPC(R3),R1 ; Back to start of line CALL FNDBF ; Set for reading buffer MOV BF.END(r3),SUBF0+BF.END ; Reset end of buffer MOV (SP)+,R3 ; Restore CLC 30$: RETURN 40$: SEC RETURN ; ; This checks the values of various parameters ; .IFDF $DEBUG TEST: CMP (R3),#BFSIZ ; too big? BHI 10$ ; yes TST BF.FUL(R3) ; too big? BLT 10$ TSTNE @BF.END(R3),10$ ; Last not the end ? TSTEQ BF.MAX(R3),1$ ; Not input ? CMP BF.FUL(R3),BF.MAX(R3) BHI 10$ ; Not correct ? 1$: MOV BF.CAD(R3),-(SP) ADD #BFLNK,(SP) CMP (SP),BF.ADD(R3) ; current address too small? BHI 10$ ; yes ADD #BFSIZ,(SP) SUB BF.CNT(R3),(SP) CMP (SP),BF.ADD(R3) ; current address too big? BLO 10$ TST (SP)+ return 10$: JMP HLTER .ENDC ; ; The input file is the exception to the buffer access rules. ; Since it is never extended it may be accessed directly. ; BFLNK==3 ; Offset from buffer to data-1 ; ; Macro to create buffer header with data area (sub-buffer) ; .psect TABL,RO,D,LCL ; ; Input buffer stack table ; BUFADD::.WORD IBUF1 ; POINTS TO CURRENT INPUT HEADER .WORD SUBF1,SUBF2,SUBF3,SUBF4,SUBF5,SUBF6 ; subst. buffers .PSECT BUFFER,RW,GBL,D .MACRO BUFF,SIZE .WORD SIZE ; BF.CNT .WORD 0 ; BF.FUL .WORD 10$+BFLNK ; BF.ADD .WORD 10$ ; BF.CAD .WORD 10$ ; BF.BEG .WORD 10$ ; BF.END .BLKW 4 ; BF.MAX,SPC,HED,VSP 10$: .BLKW 2 ; buffer foreward/backward links .BLKB SIZE ; data area .ENDM ; ; extra header macro (no data area) ; .MACRO BUFFHD,ADD .WORD 0 ; BF.CNT .WORD 0 ; BF.FUL .WORD ADD+BFLNK ; BF.ADD .WORD ADD ; BF.CAD .WORD ADD ; BF.BEG .WORD ADD ; BF.END .BLKW 4 ; BF.MAX,SPC,HED,VSP .ENDM ; ; BUFFER DEFINITIONS ; Each buffer consists of a header and a string of ; sub-buffers each of uniform size. While a buffer is being filled ; sub-buffers are automatically added as needed from the pool. ; If FNDBF or BEGBF are called then BF.MAX is generated and this ; serves as a limit on adding more data, or reading past BF.MAX. ; CLRBF and ENDBF reset BF.MAX so more data may be added. ; Each sub-buffer consists of a Foreward link, backward link, ; and data area. ; BF.CNT==0 ; Count of char remaining in sub-buffer BF.FUL==2 ; How many char total in buffer(all sub-buf) BF.ADD==4 ; Current char address BF.CAD==6 ; Current sub-BUFFER address BF.BEG==10 ; First sub-BUFFER address BF.END=12 ; LAST sub-BUFFER address BF.MAX==14 ; MAX # characters (all sub-buffers) BF.SPC==16 ; Spacing chars (current line) BF.HED==20 ; Header (begins current line) BF.VSP==22 ; Vertical spacing (all lines) ; ; THE ACTUAL DATA BUFFER HAS THE FOLLOWING FORMAT ; WORD 1= FOREWARD LINK ; WORD 2= BACKWARD LINK ; 3 - N = N-4 BYTES OF DATA ; ; ; TEXT BUFFER HEADER (PRECEEDS TEXT) TEXT TERMINATED BY A NULL ; APPLIES TO FOOTNOTE + SECONDARY BUFFERS ; ; BYTE 0 LINE SKIP COUNT ; 1 FLAGS 1=CHANGE BAR ; 2 LEFT MARGIN SPACING ; 3 SPACES/EXPANDABLE SPACE ; 4 EXPANDABLE SPACES TO LEFT ; 5 LEFT/RIGHT INDICATOR ; ; ; IMPURE DATA STORAGE FOR INPUT BUFFERS ; SUBLEV==3 BUFAD:: .WORD IBUF1 ; Current buffer pointer SUBSTK:: .BYTE 0,SUBLEV*2 ; current substitute pointer,max ; ; Input buffer (contains 1 line only) ; IBUF1:: BUFF IBFSZ+4 ; ; substitution buffer headers ; SUBF0:: BUFFHD SUBBUF SUBF1: BUFFHD SUBBUF SUBF2: BUFFHD SUBBUF SUBF3: BUFFHD SUBBUF SUBF4: BUFFHD SUBBUF SUBF5: BUFFHD SUBBUF SUBF6: BUFFHD SUBBUF SUBBUF: .BLKB BFSIZ+4 ; Data for substitutions buffers ; ; dynamic memory control locations ; XBOT:: .BLKW 1 ; BEGINNING OF ALLOCATED INDEX AREA XTOP:: .BLKW 1 ; HIGHEST VIRTUAL ADDRESS IN PROGRAM LNKHD:: .BLKW 1 ; POOL BUFFERS HEADER ; ; Footnote buffer ; FOTBF:: BUFF BFSIZ ; ; Deferred text buffer ; TXDBF:: BUFF BFSIZ ; ; IF command buffer ; IFBF:: BUFF BFSIZ ; ; Index entry buffer ; INXBF:: BUFF BFSIZ ; ; Command line input buffer/ message buffer/ general format buffer ; This buffer may be used freely as scratch storage ; TTLIN==BFSIZ TTBF:: BUFF 0 ; Input buffer header TTBUF:: .BLKB BFSIZ+2 ; COMMAND LINE INPUT BUFFER (82 bytes or more) ; ; Final output buffer ; .ifndf RT11 .WORD 0 HFOUT:: .WORD OBFSZ,0,0 OUBUF:: .BLKB OBFSZ .endc ; ; Escape sequence table ; ESCBF:: BUFF BFSIZ ; ; Secondary text buffer ; TX2BF:: BUFF BFSIZ ; ; Temporary buffers just a header/ no buffer ; TMPBF:: BUFFHD 0 ; ; Title buffers ; TTLBF:: BUFF BFSIZ ; ; Subtitle buffer ; STLBF:: BUFF BFSIZ ; ; Backspace buffer ; This buffer contains a line with spaces and underline chars. ; for underlining if not doing it by backspace. ; ULNMAX:: .WORD 0 ; MAX NUMBER OF CHARS IN BUFFER UBUFF:: .BLKB ULNSZ+2 ; ; Traceback buffer ; Format: ; Address of current line number, followed by string ; 1 word line number ; String-file-name ; 1 zero byte ; Line number + string occupies bytes ; .IF DF RT11 TRCBUF::.BLKW 1 ; CURRENT BUFFER ADDRESS .BLKW 1 ; LINE NUMBER .ASCIZ "Main File" ; FILE NAME .BYTE TRCLN-12. ; COMPLETE FIRST BUFFER .BLKB 6*TRCLN ; UP TO 6 NESTED .REQ FILES .IFF TRCBUF::.BLKB 5*TRCLN+2 .ENDC .END .TITLE RNFIO .IDENT /BL1.0/ .end