Google
 

Trailing-Edge - PDP-10 Archives - tops20-v7-ft-dist1-clock - 7-sources/rmsosb.b36
There are 6 other files named rmsosb.b36 in the archive. Click here to see a list.
%TITLE 'O S C A L L   -- OS dependent code'
!<BLF/REQUIRE 'RMSBLF.REQ'>
MODULE oscall (IDENT = '3.0'
		) =
BEGIN

GLOBAL BIND
    oscv = 2^24 + 0^18 + 615;			! Edit date: 14-Apr-86

!+
!
!
!    FUNCT:	THIS MODULE CONTAINS ALL OPERATING-SYSTEM CALLS EXCEPT
!    FOR THOSE MODULES WHOSE OVERALL FLOW IS MONITOR-DEPENDENT,
!    NAMELY RMSOPN AND MORE?.
!
!    AUTHOR:	S. COHEN, A. UDDIN
!
!	COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1979, 1986.
!	ALL RIGHTS RESERVED.
!
!	THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED  AND
!	COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH
!	THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS  SOFTWARE  OR
!	ANY  OTHER  COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE
!	AVAILABLE TO ANY OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE
!	SOFTWARE IS HEREBY TRANSFERRED.
!
!	THE INFORMATION IN THIS SOFTWARE IS  SUBJECT  TO  CHANGE  WITHOUT
!	NOTICE  AND  SHOULD  NOT  BE CONSTRUED AS A COMMITMENT BY DIGITAL
!	EQUIPMENT CORPORATION.
!
!	DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF
!	ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
!
!
!
!    **********	TABLE OF CONTENTS	**************
!
!
!    ROUTINE			FUNCTION
!    =======			========
!    ABORTFILE			ABORTIVELY CLOSES FILE
!    ADJEOF			ADJUST EOF PTR OF FILE
!    ASCREAD			READ STREAM OF BYTES FROM SPEC FILE
!    ASCWRITE			WRITE STREAM OF BYTES TO SPEC FILE
!    CLOSEFILE			CLOSE THE FILE
!    CREPAGE			CREATES A PAGE ON THE TOPS-10(PAGE. UUO)
!    DATOFILE			RETURN DATES (EG. CREATE TIME) OF FILE
!    DEVCHAR			RETURNS DEV CHAR WORD IN TOPS-20 FMT
!    IOERROR			MAP JSYS I/O ERROR TO RMS STATUS CODE
!    KILLPAGE			REMOVE PAGE FROM PROCESS (DO IO ON 10)?
!    LOFFALL			CLEAR ALL THE LOCKS
!    NEXTPAGE			RETS NEXT EXISTING P# IN FILE (IF NONE RETS -1)
!    PAGEXIST			RETS TRUE IF SPECIFIED PAGE EXISTS
!    PAGIN			READS ONE OR MORE PAGES INTO MEMORY
!    PAGOUT			WRITES PAGES TO DISK
!    POSASCFIL			SET THE CURRENT LOC OF FILE TO BEGIN OR END
!    READTTY			READ STREAM OF ASCII BYTES FROM TTY
!    SIZEFILE			RETURNS # OF BYTES IN SPECIFIED FILE
!    TRUNCFILE			DELETE TRAILING PAGES FROM A FILE
!    PSIZEFILE			RETURNS # OF PAGES IN SPECIFIED FILE
!    RLSJFN			RELEASE JFN
!
!    *************************************************
!    *						*
!    *		NEW REVISION HISTORY		*
!    *						*
!    *************************************************
!
!    PRODUCT	MODULE	 SPR
!    EDIT	 EDIT	 QAR		DESCRIPTION
!    ======	======	=====		===========
!
!
!    ***** END OF REVISION HISTORY *****
!
!    ***** Start Version 2 Development *****
!
!    PRODUCT	MODULE	 SPR
!    EDIT	 EDIT	 QAR		DESCRIPTION
!    ======	======	=====		===========
!
!    300	300	XXXXX		Make DBUG=1 work.
!
!    400	400	xxxxx	    Clean up BLISS code (RL,22-Apr-83)
!    403        403                 Make LPT work (AWN, 26-Apr-83)
!    453	453	xxxxx	    Check for "Quota exceeded" error in
!				    IOERROR and return ER$EXT.
! Version 3 development
!    504                            Image Mode
!    614			    Add Psizefile to return file's page count
!    615			    Add RLSJFN
!-

REQUIRE 'RMSREQ';

REQUIRE 'RMSOSD';				! This module is OS-dependent
%SBTTL 'ABORTFILE - close abortively'

GLOBAL ROUTINE abortfile (file_id, user_fab : REF BLOCK) : NOVALUE =
! ABORTFILE
! ======
!
!	CLOSES A  FILE ABORTIVELY
!
! INPUT:	1. VALUE OF JFN.
!		2. ADDR OF USER'S FAB.
!
! OUTPUT:	NONE
    BEGIN

    LOCAL
	keep_jfn,				! Set if user gave us JFN
	close_error,				! Error code from CLOSF, if any
	close_word : monword;			! CLOSF JSYS bits

    TRACE ('ABORTFILE');
    keep_jfn = (.user_fab [fabjfn, 0] NEQ 0);	! If user gave JFN, keep it
    close_word = .file_id OR cz_abt;		! Set abort bit for CLOSF JSYS

    IF .keep_jfn				! He gave us a JFN
    THEN
	close_word [co_nrj] = 1;		! Don't release JFN

    IF NOT closf (.close_word; close_error)	! Close the file
    THEN
	BEGIN					! Oops
	!
	!   If we are here, the CLOSF failed.  If it
	!   failed because the file had not yet been opened,
	!   then we must release the JFN (unless, of course,
	!   the user gave it to us).  Otherwise, we are
	!   in this routine because of an error anyway, so
	!   ignore the CLOSF error.
	!

	IF .close_error EQL clsx1		! File not opened?
	THEN
	    BEGIN				! Release JFN manually...

	    IF NOT .keep_jfn			! ...if the user allows
	    THEN
		rljfn (.file_id);		! NOTE: no error handling

	    END
	ELSE
	    RETURN;				! Ignore error.

	END;

    RETURN;
    END;					! End ABORTFILE
%SBTTL 'ADJEOF - adjust EOF in FDB'

GLOBAL ROUTINE adjeof (file_id, our_eofbyte, trunc_flag) : NOVALUE =
! ADJEOF
! ======
!
! ROUTINE TO ADJUST THE FILE'S EOF POINTER IN THE FILE DESCRIPTOR BLOCK (FDB)
!	THIS ROUTINE IS CALLED ONLY IF WE HAVE WRITTEN INTO THE FILE
!	WITH THE CURRENT STREAM, OR IF THERE WAS A $TRUNCATE DONE ON
!	THE STREAM. THIS ROUTINE MUST CHECK THE FILE EOF POINTER IN
!	THE FDB AND DETERMINE IF OUR NEW EOF IS GREATER THAN THE
!	OLD ONE. IF SO, THE FDB EOF IS CHANGED; IF NOT, NO ACTION
!	IS PERFORMED.
!
! INPUT:
!	FILE_ID		=	FILE ID
!	OUR_EOFBYTE	=	THE EOF BYTE THAT WE HAVE WRITTEN INTO
!	TRUNC_FLAG	=	FLAG TO INDICATE IF TRUNCATE WAS DONE
! OUTPUT:
!	<NO STATUS RETURNED>
!
! NOTES:
!
!	1.	THERE IS CURRENTLY NO SYNCHRONIZATION ON THE OPERATION
!		OF UPDATING THE FILE'S EOF POINTER. THUS, BETWEEN THE
!		TIME THAT WE CHECK THE FDB VALUE AND THEN WE CHANGE IT,
!		SOMEONE ELSE COULD SLIP IN AND CHANGE IT ALSO. HOWEVER,
!		THIS IS NOT REALLY A PROBLEM SINCE THE EOF WILL BECOME
!		CORRECT AGAIN AS SOON AS SOMEONE WRITES INTO THE FILE.
!		ALSO, THE QUEUEING OVERHEAD OF THE LOCK TO ACHIEVE
!		MUTUAL EXCLUSION IS EXCESSIVE AND IS NOT DEEMED SUFFICIENT.
!
    BEGIN

    LOCAL
	eofbyte;				! THE REAL EOF BYTE IN THE FDB

    TRACE ('ADJEOF');
    !
    !   Get the file size word from the FDB into EOFBYTE.
    !

    IF NOT gtfdb (.file_id, $xwd (1, $fbsiz), eofbyte)	! Read size
    THEN
	monerr ();

    !+
    !	Should we update the FDB?
    !-

    IF (.our_eofbyte GTR .eofbyte) OR 		! If file has grown
	(.trunc_flag NEQ 0)			! Or if we did a $TRUNCATE
    THEN
	BEGIN

	LOCAL
	    chfdb_word;

	chfdb_word = cf_nud OR 			! Do not wait for update
	fld ($fbsiz, cf_dsp) OR 		! Update size word
	fld (.file_id, cf_jfn);			! JFN

	IF NOT chfdb (.chfdb_word, allones, .our_eofbyte)	! Change EOF
	THEN
	    monerr ();

	END;

    lookat ('	HYBYTE = ', our_eofbyte);
    lookat ('	EOF # = ', eofbyte);
    RETURN
    END;
%SBTTL 'ASCREAD - read ASCII bucket'

GLOBAL ROUTINE ascread (file_id, bkt_to_use, buf_ptr, ch_in_buf) : =
! ASCREAD
! =======
!	READ IN BUCKET ENCOMPASSED BY SPECIFIED BYTE IN FILE
!
! INPUT:	1. FILE ID
!		2. NUMBER OF BKT WE WANT TO READ
!		3. ADDRESS OF BUFFER TO READ INTO
!		4. LENGTH OF BUFFER IN CHARS
!
! OUTPUT:	1. COUNT OF CHARS READ
!
! NOTES:	1. ASSUMES THAT A FILE IS ALWAYS READ WITH SAME SIZE BUFFER
!
    BEGIN

    LOCAL
        bytes_per_word,                   	! # of bytes per word   !a504
        bytes_per_page,                   	! # of bytes per page   !a504
	input_pointer,				! Input byte pointer
	bytes_to_read,				! Bytes to input
	bytes_we_got,				! Bytes actually input
	byte_in_file,				! Byte number in file
	temp,
	w_in_buf;				! Words in a buffer

    TRACE ('ASCREAD');

    bytes_per_word = %BPVAL / .Fst[Fst$h_Bsz] ;   !  bytes per word       !a504
    bytes_per_page = .bytes_per_word * pagesize;

    ! Get Buff Size in Words
    w_in_buf = ( .ch_in_buf + .bytes_per_word-1 ) / .bytes_per_word ;

    IF .bkt_to_use NEQ .fst [fstseqbkt]		! POSIT TO RIGHT PLACE?
    THEN
	BEGIN					! NO
	byte_in_file = 				! Byte position of bucket
	.bkt_to_use*.cbd [bkdbktsize] * .bytes_per_page;
	!
	!   Use 36-bit bytes for LSA files
	!

	IF sequenced THEN byte_in_file = (.byte_in_file + 4)/5;

	IF NOT tty
	THEN
	    BEGIN

	    IF NOT sfptr (.file_id, .byte_in_file)	! Set pointer
	    THEN
		monerr ();

	    END;

	END;

    IF sequenced
    THEN
	BEGIN
	input_pointer = CH$PTR (.buf_ptr, 0, 36);	! Use 36-bit bytes
	bytes_to_read = -(.ch_in_buf + 4)/5	! Number of words to move
	END
    ELSE
	BEGIN
	input_pointer = CH$PTR (.buf_ptr, 0, .Fst[Fst$h_Bsz] );  !m504
	bytes_to_read = -.ch_in_buf;
	END;

    IF NOT sin (				! Read in the record
	    .file_id, 				! JFN
	    .input_pointer, 			! Destination pointer
	    .bytes_to_read, 			! Read this much
	    0; 					! No ending byte
	    acc$1, 				! Save results
	    acc$2, 				!   in global
	    acc$3)				!     storage
    THEN
	ioerror (.file_id);			! Process JSYS error

    IF sequenced				! For LSA file, we got words...
    THEN
	bytes_we_got = -.acc$3*asciibytperword	! ...convert them to bytes
    ELSE
	bytes_we_got = -.acc$3;			! ...or leave them be.

    bytes_we_got = .ch_in_buf - .bytes_we_got;	! Chars read = max - residue
    currentfilepage = .bkt_to_use*asciibktsize;	! Keep track of current page
    fst [fstseqbkt] = .currentfilepage + asciibktsize;	! Now after it
    RETURN .bytes_we_got
    END;
%SBTTL 'ASCWRITE - write stream to file'
! ASCWRITE
! =========
!	WRITE A STREAM OF BYTES TO THE SPECIFIED FILE
! INPUT:	1. FILE ID
!		2. NUMBER OF BKT WE WANT TO WRITE
!		3. POINTER TO BUFF TO OUTPUT IN CALLERS ADDRESS SPACE
!		3. LENGTH OF STRING TO OUTPUT
! OUTPUT:	<NONE>
!
!
!
!

GLOBAL ROUTINE ascwrite (file_id, bkt_to_use, buf_ptr, ch_in_buf) : NOVALUE =
    BEGIN

    LOCAL
	output_pointer,				! Pointer to stream to output
	bytes_to_write,				! Bytes we should output
	bucket_position,			! Number of bucket's first byte
	n,					! Num of bits of cruft
	lw,					! Ptr to last partial wd in buf
	temp,
	w_in_buf,				! Data words in this buffer
        bytes_per_word,                         	 		  !a504
        bytes_per_page;                         	 		  !a504

    TRACE ('ASCWRITE');

    bytes_per_word = %BPVAL / .Fst[Fst$h_Bsz];
    bytes_per_page = .bytes_per_word * pagesize;        		  !m530

    ! Get words of data (based on actual byte size)		
    w_in_buf = (.ch_in_buf + .bytes_per_word - 1 )/.bytes_per_word;	  !m504

    IF .bkt_to_use NEQ .fst [fstseqbkt]		! Positioned to right place?
    THEN
	BEGIN					! No
	bucket_position = 			! Bucket's first byte
	.bkt_to_use*.cbd [bkdbktsize]*.bytes_per_page;

	IF sequenced THEN bucket_position = (.bucket_position + 4)/5;

	IF NOT (tty OR llpt)                    ![403] If not TTY: or LPT:
	THEN
	    BEGIN

	    IF NOT sfptr (.file_id, .bucket_position)	! Find right place
	    THEN
		monerr ();

	    END;

	END;

    IF sequenced				! LSA file?
    THEN
	BEGIN
	output_pointer = CH$PTR (.buf_ptr, 0, 36);
	bytes_to_write = -(.ch_in_buf + 4)/5
	END
    ELSE
	BEGIN
	output_pointer = CH$PTR (.buf_ptr,
                                 0,
                                 .Fst[Fst$h_Bsz] );
	bytes_to_write = -.ch_in_buf;
	END;

    IF NOT sout (				! Output stream of bytes
	    .file_id, 				! JFN
	    .output_pointer, 			! Source pointer
	    .bytes_to_write, 			! Number of bytes to output
	    0; 					! No special end byte
	    acc$1, 				! Output to
	    acc$2, 				!    global AC
	    acc$3)				!       storage
    THEN
	ioerror (.file_id);

    currentfilepage = .bkt_to_use*asciibktsize;	! Keep track of current page
    fst [fstseqbkt] = .currentfilepage + asciibktsize;	! Now after it
    RETURN
    END;
%SBTTL 'CLOSEFILE - close a file'

GLOBAL ROUTINE closefile (file_id, user_fab : REF BLOCK) : NOVALUE =
! CLOSEFILE
! =========
!	CLOSES A  FILE
! INPUT:	1. FILE ID OF THE FILE TO BE CLOSED (JFN ON 20)
!		2. POINTER TO USER'S FAB.
! OUTPUT:	1. TRUE IF MON SUCCEEDED, ELSE ERROR CODE
!
    BEGIN

    LOCAL
	closf_word : monword;

    TRACE ('CLOSEFILE');

    !+
    !	We will set up the flags and the JFN for the CLOSF JSYS.
    !	Then, we will check to see if the user wants us to keep
    !	his JFN for him when we close the file.
    !-

    closf_word = .file_id OR cz_nud OR co_nrj;	! Set up arg to CLOSF JSYS

    IF .user_fab [fabjfn, 0] EQL 0
    THEN 					! He didnt want to save the JFN
	closf_word [co_nrj] = 0;

    IF NOT closf (.closf_word) THEN monerr ();	! Close the file

    RETURN;
    END;
%SBTTL 'CREPAGE - create page (10 only)'

GLOBAL ROUTINE crepage (page_num, pages) : NOVALUE =
! CREPAGE
!============
!	CREATES A PAGE ON THE TOPS-10. A NO-OP ON THE -20.
!
! INPUT:	1. PAGE NO.OF THE FIRST PAGE OF THE BLOCK TO BE CREATED
!		2. NO OF PAGES IN THE BLOCK
!
!
! OUTPUT:	<NONE>
!
!
    BEGIN
    TRACE ('CREPAGE');
    !WHEREAS THE 10 REQS AN EXPLIC OS CALL TO CRE A PROCESS PAGE,
    !THE 20 DOES IT AUTOMATICALLY WHEN YOU ACCESS A WORD IN THE PAGE
    RETURN
    END;
%SBTTL 'DATOFILE - return file dates'

GLOBAL ROUTINE datofile (fst_jfn, date_blk, date_blk_size) : NOVALUE =
! DATOFILE
! =========
!	RETURNS DATES AND TIMES ASSOCIATED WITH THE SPECIFIED FILE.
! INPUT:	1. FILE ID OF THE FILE IN FST.
!		2. POINTER TO A BLOCK THAT WILL CONTAIN DATES
!		3. SIZE OF THE BLOCK
!
! OUTPUT:	INDIRECTLY, DATES AND TIMES IN THE BLOCK
!
    BEGIN
    TRACE ('DATOFILE');

    IF NOT rftad (.fst_jfn, .date_blk, .date_blk_size)	! Get times and dates
    THEN
	monerr ();

    RETURN
    END;
%SBTTL 'DEVCHAR - get device characteristics'

GLOBAL ROUTINE devchar (file_id) =
! DEVCHAR
! =========
!	RETURNS DEVCHAR FLAGS IN 20 FORMAT
! INPUT:	1. JFN OF THE DESIRED DEVICE
!
! OUTPUT:	THE 20-FORMAT FLAG WORD
!
    BEGIN

    LOCAL
	dummy1,					! Protect against BLISS bug
	dummy3,					! Ditto
	device_characteristics;			! Returned device info

    TRACE ('DEVCHAR');

    IF NOT dvchr (.file_id; dummy1, device_characteristics, dummy3)	!
    THEN
	monerr ();

    RETURN .device_characteristics;
    END;
%SBTTL 'IOERROR - processes SIN/SOUT errors'

GLOBAL ROUTINE ioerror (file_id) : NOVALUE =
! IOERROR
! =========
!
!  THIS ROUTINE PROCESSES AN ERROR RETURN FROM EITHER  THE SIN JSYS
!	OR THE SOUT JSYS. EACH MONITOR ERROR CODE IS MAPPED INTO
!	A CORRESPONDING RMS-20 ERROR CODE. THE MAPPING IS AS FOLLOWS:
!
!		MONITOR CODE	MEANING		RMS-20 CODE
!		============	=======		===========
!
!		IOX4		EOF		<NO MAPPING DONE>
!		IOX5		DATA ERROR	ER$RER, ER$WER
!		ALL OTHERS	INTERNAL BUG	ER$BUG
!
!
! INPUT:
!	1. JFN OF FILE THAT HAD IO ERROR
!
! OUTPUT:
!	<NONE>
! NOTES:
!
!	1.	THE STV FIELD WILL ALWAYS BE SET TO BE THE MONITOR CODE
!		RETURNED.
!
!	2.	THIS ROUTINE EXITS TO THE USER UNLESS THE ERROR WAS ER$EOF
!
    BEGIN

    LOCAL
	last_error;

    TRACE ('IOERROR ');
    !
    !	Get the last error code.
    !

    IF NOT geter ($fhslf; last_error) THEN monerr ();

    !+
    !	Is this an EOF? If so, we can ignore it because the
    !	user probably won't see an EOF until he reads all the
    !	records in the current buffer.
    !-

    IF .last_error<rh> EQL iox4 THEN RETURN;	! Return on EOF

    usrstv = .last_error<rh>;			! Save code in STV

    selectone .usrstv of 			! Handle error		!A453
	set					!			!A453
	[iox5]:					! Device or data error	!A453
	    BEGIN				! Yes
	    usrsts = er$rer;			! Assume read error

	    IF currentjsys EQL c$put 		! Doing output?
	    THEN 				!
		usrsts = er$wer;		! Make it a write error

	    END;
	[iox11,iox34]:				! Quota exceeded	!A453
	    usrsts = er$ext;			! ...
	[otherwise]:				! Unexpected error	!A453
	    BEGIN				! No -- some other bug
	    usrsts = er$bug;			! Show it's a bug

	    IF prichk (.usrstv)			! Displaying internal errs?
	    THEN
		txtout (mf$iom, .usrstv);	! Yes, stream i/o error

	    END;

	tes;					!			!A453

    usrerr ()					! Do failure return to user
    END;					! End IOERROR
%SBTTL 'KILLPAGE - kill mapped pages'

GLOBAL ROUTINE killpage (page_num, pages) : NOVALUE =
! KILLPAGE
! =======
!	UNMAPS (KILLS) A FILE'S PAGES IN A PROCESS.
! INPUT:	1. PAGE NUMBER OF THE FIRST PAGE OF THE BLOCK TO BE KILLED
!		2. NO. OF PAGES TO BE UNMAPPED
! OUTPUT:	<NONE>
!
!
!
!
    BEGIN
    TRACE ('KILLPAGE');

    !+
    !	Set up ACs for PMAP JSYS.
    !-

    IF NOT pmap (				!
	    -1, 				! Kill the pages
	    $xwd ($fhslf, .page_num), 		! Handle,,page number
	    pm_cnt OR .pages)			! Use page count,,page count
    THEN
	monerr ();

    RETURN
    END;					! End KILLPAGE
%SBTTL 'LOFFALL - unlock all records'

GLOBAL ROUTINE loffall (request_id) : NOVALUE =
! LOFFALL
! =======
!	TURNS OFF ALL LOCKS ON RECORDS OF A STREAM
! INPUT:	1. REQUEST ID. THIS IS THE SAME REQUEST ID
!		   USED TO LOCK THE RECORDS
! OUTPUT:	<NONE>
!
!
    BEGIN
    TRACE ('LOFFALL');

    IF NOT deq ($deqid, .request_id) THEN monerr ();

    RETURN;
    END;					! End LOFFALL
%SBTTL 'NEXTPAGE - return next page in file'

GLOBAL ROUTINE nextpage (fst_jfn, page_no) : =
! NEXTPAGE
! ========
!	RETURNS THE NEXT USED PAGE IN THE FILE. APPLIES TO 20 ONLY.
! INPUT:	1. VALUE OF JFN IN FST.
!		2. PAGE NUMBER OF PAGE TO FIND.
! OUTPUT:	1. PAGE NUMBER, IF IT EXISTS
!		2. ELSE, FALSE
!
    BEGIN

    LOCAL
	next_file_page;

    TRACE ('NEXTPAGE');
    !
    !	Get first used page, starting at PAGE_NO
    !

    IF ffufp ($xwd (.fst_jfn, .page_no); next_file_page)
    THEN
	RETURN .next_file_page<0, 18>		! If it worked, return page
    ELSE
	RETURN false;				! If not, return false

    END;					! End NEXTPAGE
%SBTTL 'PAGEXIST - check if file page exists'

GLOBAL ROUTINE pagexist (file_id, p_to_chk) : =
! PAGEXIST
! ========
!	CHECKS TO SEE WHETHER A PARTICULAR PAGE OF A FILE EXISTS.
! INPUT:	1. FILE ID
!		2. PAGE NO. TO CHECK
! OUTPUT:	1. TRUE IF THE PAGE EXISTS.
!		2. FALSE OTHERWISE
!
    BEGIN

    LOCAL
	page_access : monword;

    TRACE ('PAGEXIST');

    IF NOT rpacs ($xwd (.file_id, .p_to_chk); page_access) THEN monerr ();

    IF .page_access [pa_pex] THEN RETURN true ELSE RETURN false;

    END;					! End PAGEXIST
%SBTTL 'PAGIN - read pages'

GLOBAL ROUTINE pagin (file_id, file_page, mem_page, pagacc, count) : NOVALUE =
! PAGIN
! =====
!	READ ONE OR MORE CONSECUTIVE PAGES INTO MEMORY
!	DOES "IN" ON 10 AND "PMAP" ON 20
! INPUT:
!	FILE TO READ (JFN OR CHANNEL)
!	PAGE IN FILE TO READ (0 IS 1ST PAGE)
!	WHERE TO PUT IT (PAGE NUMBER)
!	LEVEL OF ACCESS TO PAGE
!	NUMBER OF PAGES (=512 WORDS) TO READ
! OUTPUT:	(IMPLICITLY)
!	DATA IN MEMORY PAGE(S)
    BEGIN

    LOCAL
	access_bits : monword;

    TRACE ('PAGIN');
    access_bits = pm_rd OR pm_pld OR pm_cnt;	! Read, preload, and use count

    IF (.pagacc AND axwrt) NEQ 0		! Writing?
    THEN
	access_bits [pm_wr] = 1;		! Write access also

    access_bits [pm_rpt] = .count;		! Number of pages to read

    IF NOT pmap (				!
	    $xwd (.file_id, .file_page), 	! JFN,,page number
	    $xwd ($fhslf, .mem_page), 		! Fork handle,,memory page
	    .access_bits)			! Page access
    THEN
	monerr ();

    okcreate (.mem_page^p2w, .file_page);	! Prevent illegal memory read
    RETURN
    END;					! End PAGIN
%SBTTL 'PAGOUT - update file pages'

GLOBAL ROUTINE pagout (file_id, file_page, mempage, count) : NOVALUE =
! PAGOUT
! ======
!
!	UPDATE PAGES OF THE SPECIFIED FILE.
! INPUT:
! -----
!	1. FILE ID
!	2. PAGE NUMBER IN FILE TO BE UPDATED.
!	3. COUNT OF PAGES TO BE UPDATED
! OUTPUT:
! ------
!	<NONE>
!
!
    BEGIN
    TRACE ('PAGOUT');

    IF NOT ufpgs (				!
	    $xwd (.file_id, .file_page), 	! JFN,,page number
	    uf_now OR .count)			! page count, non-blocking bit
    THEN
	monerr ();

    RETURN
    END;					! End PAGOUT
%SBTTL 'POSASCFIL - get page for byte'

GLOBAL ROUTINE posascfil (file_id, byte_no) : NOVALUE =
! POSASCFIL
! =========
!
!	DETERMINE PAGE # OF DESIRED BYTE AND READ IT IN.
!	THEN SETUP BYTE PTR TO IT IN MEMORY.
!	ALSO SETUP COUNT OF CHARACTERS IN BUFFER
! INPUT:	1. FILE ID
!		2. BYTE NO IN FILE TO POSITION TO
! OUTPUT:	<NONE>
    BEGIN

    LOCAL
        bytesperpage,                  		! # of bytes in a page !a504
    	bkt_to_use,				! Bucket containing byte
	ch_per_bkt,				! Number of chars in bucket
	cc_in_buf,				! Offset of byte_no(th)
						! char in buffer
	temp;

    TRACE ('POSASCFIL');

    bytesperpage = (%BPUNIT/.Fst[Fst$h_Bsz])*pagesize; ! bytes  per page  !a504

    ch_per_bkt =
     .cbd [bkdbktsize] * .bytesperpage;    	! Calc buff size in bytes !m504
    cc_in_buf = .byte_no MOD .ch_per_bkt;	! Offset of byte within page
    bkt_to_use = .byte_no/.ch_per_bkt;		! Calc bkt to pos to
    temp = 					! Read bkt & ret chars in it
    ascread (.file_id, .bkt_to_use, .curentbufferadr, .ch_per_bkt);

    IF endoffile				!
    THEN
	BEGIN
	rst [rstnrp] = .bkt_to_use;		! Next I/O will be rewrite
						!   of this bucket
	rst [rstbytecount] = .cc_in_buf;	! Indicate fullness of bucket
	END
    ELSE
	BEGIN
	rst [rstnrp] = .bkt_to_use + 1;		! next I/O will be read
						!   of subsequent bkt
	rst [rstbytecount] = .temp - .cc_in_buf	! Chars left to read
	END;

    rst [rstpagptr] = CH$PTR (.curentbufferadr,	!PT TO DESIRED BYTE
                              .cc_in_buf,
                              .Fst[Fst$h_Bsz] );! with desired byte size  !m504
    RETURN
    END;					! End POSASCIF
%SBTTL 'READTTY - read from TTY'

GLOBAL ROUTINE readtty (buffer_address, size) : =
! READTTY
! =======
!	ROUTINE TO READ A LINE FROM THE TTY.
! INPUT:	1. ADDRESS OF BUFFER FOR STRING
!		2. NO OF BYTES AVAILABLE IN STRING
! OUTPUT:	1. REMAINING BYTE COUNT
!
    BEGIN

    LOCAL
	last_byte,
	updated_pointer,
	bytes_left;

    TRACE ('READTTY');

    IF NOT rdtty (				! Get string from TTY:
	    CH$PTR (.buffer_address), 		! Pointer to buffer
	    rd_top OR .size, 			! Break set, buffer size
	    0; 					! Pointer to prompt (not v2)
	    updated_pointer, 			! Returned pointer
	    bytes_left)				! Unused bytes
    THEN
	monerr ();

    !
    !	Check for control-Z, which will generate an EOF.
    !
    last_byte = CH$RCHAR (CH$PLUS (.updated_pointer, -1));

    IF .last_byte EQL $chcnz THEN usrsts = er$eof;

    RETURN .size - .bytes_left;			! Chars read = max-residue
    END;					! End READTTY
%SBTTL 'SIZEFILE - return file length'

GLOBAL ROUTINE sizefile (file_id) : =
! SIZEFILE
! ======
!	RETURNS THE LENGTH OF AN EXISTING FILE
! INPUT:	1. FILE ID
! OUTPUT:	1. NO OF BYTES IN THE FILE:
!			WORDS FOR RMS FILES
!			CHARS FOR LSA AND ASCII FILES
    BEGIN

    LOCAL
	dummy1,					! Prevent BLISS bug
	dummy3,					! Ditto
	file_size;				! Count of file bytes

    TRACE ('SIZEFILE');

    IF NOT sizef (.file_id; dummy1, file_size, dummy3) THEN monerr ();

    IF sequenced
    THEN
	RETURN .file_size*asciibytperword	! LSA: Convert words to bytes
    ELSE
	RETURN .file_size;			! ASCII: Already in bytes

    END;					! End SIZEFILE
%SBTTL 'PSIZEFILE - return file length in pages'

GLOBAL ROUTINE psizefile (file_id) : =		!614
! PSIZEFILE
! ======
!	RETURNS THE LENGTH OF AN EXISTING FILE
! INPUT:	1. FILE ID
! OUTPUT:	1. NO OF PAGES IN CURRENT FILE

    BEGIN

    LOCAL
	dummy1,					! Prevent BLISS bug
	dummy2,					! Ditto
	file_size;				! Count of file bytes

    TRACE ('PSIZEFILE');

    IF NOT sizef (.file_id; dummy1, dummy2, file_size) THEN monerr ();

    RETURN .file_size;				! RETURN PAGES

    END;					! End PSIZEFILE
%SBTTL 'RLSJFN - release a JFN'

GLOBAL ROUTINE rlsjfn (file_id) :  =			!615
! RLSJFN
! ======
!	RELEASE JFN
! INPUT:	1. FILE ID
! OUTPUT:	1. TRUE if JFN released, else FALSE
!
! 
    BEGIN

    TRACE ('RLSJFN');

    IF NOT Rljfn (.file_id) THEN RETURN false
    ELSE  RETURN true;

    END;					! End RLSJFN
%SBTTL 'TRUNCFILE - delete trailing pages'

GLOBAL ROUTINE truncfile (file_id, page_no, no_of_pages) : NOVALUE =
! TRUNCFILE
! =========
!	DELETE TRAILING PAGES FROM A FILE
! INPUT:	1. FILE ID OF FILE TO BE TRUNCATED
!		2. PAGE NO OF FIRST PAGE OF A BLOCK OF PAGES TO BE KILLED
!		3. NO OF PAGES TO BE KILLED
! OUTPUT:	<NONE>
!
    BEGIN
    TRACE ('TRUNCFILE');
    !
    !	Truncation has two aspects, setting EOF pointer and
    !	physically destroying pages for efficiency's sake.
    !	Unfortunately the 20 only
    !	has facility for destroying pages LSS 777 (Octal)
    !

    IF NOT pmap (				! Remove file pages
	    -1, 				! Kill the pages
	    $xwd (.file_id, .page_no), 		! JFN,,page number
	    pm_cnt OR .no_of_pages)		!
    THEN
	monerr ();

    RETURN
    END;					! End TRUNCFILE

END

ELUDOM