.iif ndf rsx rsx = 1 ;Assume RSX11M .title fopen C library file opener ; ; The information in this document is subject to change ; without notice and should not be construed as a commitment ; by Digital Equipment Corporation or by DECUS. ; ; Neither Digital Equipment Corporation, DECUS, nor the authors ; assume any responsibility for the use or reliability of this ; document or the described software. ; ; Copyright (C) 1980, DECUS ; ; ; General permission to copy or modify, but not for profit, is ; hereby granted, provided that the above copyright notice is ; included and reference made to the fact that reproduction ; privileges were granted by DECUS. ; .ident /000024/ ; ;+ ; ; Index Open or reopen a file ; ; Usage ; ; FILE * ; fopen(name, mode); ; char *name; /* File to open */ ; char *mode; /* Open modes */ ; ; FILE * ; freopen(name, mode, iop); ; char *name; /* File to open */ ; char *mode; /* Open modes */ ; FILE *iop; /* I/O pointer */ ; ; Internal ; ; mov iov,r4 ;r4 -> iov if any, else r4 := 0 ; call $$flun ;Get a Lun slot, initialize fdb ; ;return, r4 -> iov, r3 := lun ; ;r0, r1 random ; ;error: return to caller via $$fope ; ;Note: available RSX luns start at 2, ; ;while RT11 luns start at zero. ; ; mov iov,r4 ;r4 -> iov ; call $$fopt ;C$PMTR+2(r5) => options string ; ;return: option flags in fdb ; ;r0 random ; ;error: return to caller via $$fope ; ; mov iov,r4 ;r4 -> iov ; call $$fcsi ;C$PMTR+0(r5) => file name ; ;file name is parsed and fdb setup. ; ;r0-r3 random ; ;error: return to caller via $$fope ; ; mov size,r0 ;Memory needed ; call $$falo ;Allocate it ; ;return, r0 -> allocation ; ;no room: return to caller via $$fope ; ; mov iov,r4 ;r4 -> iov ; mov lun,r3 ;r3 := lun ; jmp $$fopa ;RT11 file open (ascii file spec.) ; ;C$PMTR+0(r5) => ASCII file spec. ; ;Parse the file using .CSISPC, open it, ; ;returning to the caller via cret$ or ; ;$$fope if error. ; ; mov iov,r4 ;r4 -> iov ; mov lun,r3 ;r3 := lun ; mov dblk,r1 ;r1 -> Rad50 device descriptor ; jmp $$fopr ;open by rad50 device descriptor ; ;RT11 only -- used to open the device ; ;for directory processing. Exit via ; ;cret$$ or $$fope if error. ; ; mov iov,r4 ;r4 -> iov ; jmp $$fopn ;Open the file and return to the ; ;caller via $$fopx or $$fope if error. ; ;On RSX, fixup append mode if the file ; ;wasn't found. ; ; mov iov,r4 ;r4 -> iov ; call $$fopo ;Normal open, then exit via $$fopx ; ;On RSX, $$fopo returns if append open ; ;and "file not found" -- this is ok ; ;for fopen(), but bad news for fnext(). ; ;on return (RSX), r0 -> fcb ; ;Other errors exit via $$fope ; ; mov iov,r4 ;r4 -> iov, file is open ; jmp $$fopx ;Normal exit from fopen ; ;On RSX, $$fopx allocates the record ; ;buffer. ; ; mov iov,r4 ;r4 -> iov (or r4 == 0) ; mov code,r0 ;r0 := error code (to go to $$ferr) ; jmp $$fope ;Error exit from fopen ; ;$$fope deallocates buffers ; ; Description ; ; Fopen opens a new or existing file in the indicated mode: ; ; r Read the existing file sequentially ; w Create and write the file sequentially ; a Append to the file ; n Don't do newlines on each record. ; u RSX-mode: User does all buffering ; RT11-mode: use .ttyin and .ttyout ; ; ; Either "r", "w", or "a" must be given. "n" and "u" are ; optional. "n" should be given for "binary" files. ; The "u" flag is treated quite differently for RSX and RT11 ; modes. ; ; On RSX, "u" means that the program does all I/O by ; calling fget() and/or fput(). Calling any other function ; (for example fprintf) is an error. ; ; On RSX, "n" or "u" mode files will be created with the ; "variable-length" attribute. On RSTS/RSX emulation, text ; files (neither "n" nor "u" specified) will be created with ; "stream" attribute. ; ; On RSX, if the record type bits in the record attribute byte ; (F.RATT in the FDB) is zero, the file will be read as if ; the "n" was specified. Note that, if the file contains ; carriage-return line-feed sequences, the entire sequence ; will be passed to the user's program. If record attributes ; are understandable, the carriage-return will be deleted ; from sequences. ; ; On RT11, if fopen decides that the file being opened ; is really the user's command terminal, single-character I/O ; will be performed (by calling .ttyin and .ttyout). Note that ; the "special-mode" bits must be set in the JSW by the program ; if it requires true single-character or immediate return input. ; Output to the terminal will be performed without buffering, ; which is useful for screen updating, but otherwise expensive. ; ; Fopen() returns NULL on errors -- $$ferr gets an error code. ; On RT11, this will be a RSTS/E compatible code (described in ; iov), while on RSX, this will be the FCS error code. ; ; On RT11, the file name pointed to by the 'io_name' field of ; the iov is either the file name string as passed to fopen() ; or an ascii string reconstructed from the 4-word Rad50 ; device block if the file was opened as part of a fwild/fnext ; sequence. By saving the ascii string, RSTS/E is able to ; re-parse logical device names and PPN's, which are not present ; on native RT11. ; ; Note that "no buffer space available" (IE.NBF or E$$NSP) and ; "invalid lun" (IE.ILU or E$$NOC) may be generated by fopen. ; On RT11, if the file cannot be opened because the user's program ; has already opened the channel, an E$$ILU error will be returned. ; ; The same file may not be used for both reading and writing ; except if the program writes a disk file, then repositions ; and reads it using ftell()/fseek(). In this case, the program ; should call rewind() or freopen() to reinitialize the file before ; using fseek(). ; ; Except in the one specific case of the RT11 console terminal ; open in "u" mode, an open file must not be used for reading ; and writing at the same time. ; ; Freopen() substitutes the named file in place of the open ; file -- indicated by iop. The file currently open on ; iop is closed. Freopen returns iop. If the open failed, ; iop will be deallocated. Note that freopen loses any pending ; fwild/fnext status. ; ; ; Internal ; ; The following routines/globals are for use by fopen/fwild. ; If any of these routines detect an error, they return to the ; fopen() caller with an appropriate error code in $$ferr. ; ; $$fcsi Parse file name and setup fdb ; $$fopt Scan options word. ; $$flun Get free Lun slot ; $$falo Get memory for fopen ; $$fopa RT11 open with Ascii name ; $$fope Error exit from fopen ; $$fopn Normal open, hack append ; $$fopr RT11 open with Rad50 block ; $$fopx Normal exit from fopen ; $$csib Work block for the mighty csi ; $$dfnb Default file name block ; ; Warning: fopen() in RSX/RSTS mode uses unpublished information ; to obtain the PPN [UIC] of an open file. This code (in $$fcsi) ; may require modification for subsequent releases of RSTS/E. ; ; ; Bugs ; ; The RT11 file system does not support files greater than ; 65535 blocks long. ; ; RT11 does not get the actual keyboard name of the console ; terminal, although this isn't too hard to do on RT11/RSTS/E. ; ; Freopen() cannot be used to assign a file to stderr as there ; is code throughout the i/o package for special treatment of ; stderr. For example, it cannot be closed by a user-written ; program. ; ; ; In RSX modes, the maximum number of files that may be simultaneously ; open is defined at assembly time by a macro (FSRSZ$) which is ; expanded when fopen.mac is assembled. The default FSRSZ$ ; parameter is 8. This will yield a file block buffer area of 4K ; bytes. To obtain a smaller (or larger) buffer area, edit ; the configuration command file (RSX.MAC) to redefine N$$FIL. ; ; The code is general, hence big. RSX users who typically ; overlay everything may find pleasure in breaking the ; code into many tiny modules. ; ;- ; ; Edit history ; 000001 24-Jul-79 MM Initial edit ; 000002 10-Mar-80 MM Conversion for the newer library ; 000003 27-Mar-80 MM Merged libraries ; 000004 23-May-80 MM Added freopen(), changed RT11 stuff ; 000005 10-Jun-80 MM Reorganized for fwild/fnext ; NOTE: because of the reorganization, edit codes ; have been removed from RSX fopen ; 000006 15-Jun-80 MM More reorganization ; 000007 22-Jun-80 MM Reorganized RT11 stuff. NOTE: because of the ; reorganization, edit codes have been removed ; from RT11 fopen. ; 000008 02-Jul-80 MM Fixed dumb bug in $$flun ; 000009 06-Jul-80 MM Added Stream under RSTS/E ; 000010 09-Jul-80 MM Do a handler .fetch on RT11 ; 000011 10-Jul-80 MM Fixed typo in .dstatus call ; 000012 18-Jul-80 MM Fixed up RT11 error status values ; 000013 01-Aug-80 MM Track IOV changes ; 000014 17-Aug-80 RBD Slight mods for RT11 fwild(). Changed stashed file ; spec to the 4 word RAD50 CSI output 'dblk'. This ; is consistent with fwild(), which does the same. ; 000015 22-Aug-80 MM Reverted to storing an ascii file name for RT11. ; This is needed to preserve logical device translation ; for RSTS. There is some unfortunate hackery for ; wild card files, as the 4-word device block must ; be decompiled to ascii. No simple solutions. ; 000016 04-Sep-80 MM Squeeze blanks from RT11 filenames ; 000017 16-Sep-80 MM Get RSX Directory UIC ; 000018 18-Sep-80 MM Bug in .parse call, fixed .psect names, added N$$FIL ; 000019 22-Sep-80 RBD Minor changes (improvements) to uic handling. ; 000020 23-Sep-80 MM Fix to RT11 file scan, removed VF$BAD ; 000021 20-Oct-80 MM Bit by Standard Runoff on VMS -- missing attributes ; 000022 24-Oct-80 MM Bit again. Missing attributes on VMS differnt ; 000023 23-Dec-80 RBD Put code to squeeze blanks from RT-11 names in again. ; 000024 05-Jun-81 RBD Add definition to enable TKB "ACTFIL=" option ; on RSX-11M. ; RMODE = 1. ;Must be 1 WMODE = 2. AMODE = 4. UMODE = 8. NMODE = 16. $$RMOD == 0 ;Open for read Do $$WMOD == 1 ;Open for write not $$AMOD == 2 ;Open for append change ; ; Note the following tests: ; ; reading: (r4) & ($$WMOD | $$AMOD) == 0 (VF$MOD == 0) ; writing: (r4) & ($$WMOD | $$AMOD) != 0 (VF$MOD != 0) ; existing file: (r4) & $$WMOD == 0 ; .PSECT .STRN. ;15/18 OPTSTR: .ASCIZ "rwaun" ;Possible options (keep in this order) OPTVAL: .BYTE RMODE,WMODE,AMODE,UMODE,NMODE OPTCOD: .BYTE -1,$$RMOD,$$WMOD,-1,$$AMOD,-1,-1,-1 .EVEN .if ne rsx .IIF NDF N$$FIL N$$FIL = 8. ;Number of files ;18 .MCALL FDBDF$, FDAT$R, FDOP$R, CSI$, CSI$1, CSI$2 .MCALL FDBK$R, FDRC$R, FSRSZ$, NMBLK$, FDOF$L .MCALL OFNB$A, OFNB$R, OFNB$W FDOF$L R.STM = 4 ;Ascii stream record attribute. ;09 ;NOTE: R.STM is unpublished ; F.BFHD == 20 ;Symbol which enables TKB's ACTFIL ;24 ; option to control $$FSR1 size. ;24 .PSECT .DATA. FNMSIZ = 82. ;Filename size maximum CSI$ $$CSIB:: .BLKB C.SIZE ;Work block for the mighty CSI $$DFNB:: NMBLK$ ,,,SY ;Default Filename block ; ; Since the RSX people do not understand reentrant coding, ; the file system must be defined at assembly time. ; ; Note -- code in subroutine $$falo depends on the undocumented ; fact that a dummy file data block is all zero. ; FSRSZ$ N$$FIL ;Define block buffer area ;18 ; .PSECT .PROG. FREOPE:: JSR R5,CSV$ ;C save sequence MOV C$PMTR+4(R5),R4 ;Get IOV pointer MOV R4,R0 ;Make R0 non-zero CALL $$CLOS ;Close file, keep IOV BR FOPEN1 ;Continue with main sequence ; FOPEN:: JSR R5,CSV$ ;C save sequence CLR R4 ;Clear IOV pointer, too FOPEN1: ;Common code for fopen, freopen CALL $$FLUN ;Get a Lun CALL $$FOPT ;Scan options string CALL $$FCSI ;Parse the CSI string, setup the FDB .PAGE ; ; ** $$FOPN ; ; Open the file. This code is specific to fopen/freopen ; Note: if append and file-not-found, restart for writing ; $$FOPN:: CALL $$FOPO ;Try to open it BIC #VF$MOD,(R4) ;Clear out the mode bits BIS #$$WMOD,(R4) ;Make it a write open BR $$FOPN ;And try, try, try. ; ; ** $$FOPO ; ; Actually open the file which has been setup by $$FCSI (or fnext()) ; If the file opened correctly, there is no return -- the program ; exits to the fopen/fnext caller via $$fopx. In the single, specific ; case of open for append and file not found, $$fopo returns to the ; caller so that the higher-level routine can change the append to ; and open for output. This doesn't seem to make any sense in wild ; card opens, however. ; $$FOPO:: MOV R4,R0 ;Get IOV ADD #V$FDB,R0 ;R0 -> fdb BIT #VF$UBF,(R4) ;User buffered? BEQ 10$ ;No, continue ; ; User-buffering. (The following may have to change) ; FDRC$R R0 ;I/O via GET$ and PUT$ FDBK$R R0 ;No block buffers BR DOOPEN ;Continue main sequence ; ; I/O package does buffering (record buffer in V$RBUF). ; 10$: BIT #VF$NOS,(R4) ;No newlines wanted? BNE 20$ ;Branch if so. FDAT$R R0,,#FD.CR ;Want them, so ask for it. 20$: FDRC$R R0 ;Setup for GET$ or PUT$ FDOP$R R0 ;Initialize file open section ; ; Now for the fun part ; DOOPEN: BIT #$$WMOD!$$AMOD,(R4) ;Reading BNE 10$ ;Nope OFNB$R R0 ;Yep, try for it BR 30$ ;Main sequence ; 10$: BIT #$$WMOD,(R4) ;Writing BNE 20$ ;Yep, go do it OFNB$A R0 ;Try opening for append BCC 30$ ;Gotcha. Onward ; ; If the error was "not found" allow caller to restart for writing ; CMPB F.ERR(R0),#IE.NSF ;File not found? BNE 40$ ;No, sorry. RETURN ;Try again ; 20$: BIT #VF$NOS!VF$UBF,(R4) ;"Binary" file? ;09+ BNE 24$ ;Br if so TST $$RSTS ;Stream, but is it RSTS? BEQ 24$ ;No, do it in RSX style FDAT$R R0,#R.STM ;It's an ascii file BR 26$ ;Rejoin mainstream 24$: ;09- FDAT$R R0,#R.VAR ;Writing, use variable-length output 26$: ;09 OFNB$W R0 ;Go for it ; 30$: BCC $$FOPX ;Any errors -- do final cleanup if not 40$: MOVB F.ERR(R0),R0 ;Error code (sign extended) BR $$FOPE ;Sorry about that .PAGE ; ; ** $$FOPX ; ; Normal exit from fopen(). Enter with r4 -> fdb. Returns to fopen ; caller after allocating buffers and setting flag bits. Note: ; the file has been successfully opened, but record buffers have not ; been allocated. ; ; $$FOPX:: BIS #VF$OPN,(R4) ;Mark file opened for $$clos and fnext() BIC #VF$EOR,(R4) ;Ensure not at end of file MOV V$FDB+F.VBSZ(R4),V$RBSZ(R4) ;Get Virtual record size MOVB V$FDB+F.RCTL(R4),R0 ;R0 := Record flag bits CMP $$OPSY,#5 ;On vms? ;22 BNE 5$ ;No, onward ;22 BITB #7,V$FDB+F.RATT(R4) ;Any attribute bits ;21+ BNE 5$ ;Yes, continue BIS #VF$NOS,(R4) ;No, not stream ;21- 5$: BITB #FD.REC,R0 ;Record oriented device? BEQ 20$ ;No, onward BITB #FD.DIR,R0 ;Yes, but is it a file BEQ 10$ ;No, onward BIS #VF$FIL,(R4) ;A file, set bits BR 20$ ;And continue 10$: BIS #VF$REC,(R4) ;Record, no file. Flag same BITB #FD.TTY,R0 ;True terminal? BEQ 20$ ;No, continue BIS #VF$TTY,(R4) ;Yes, flag it 20$: BIT #VF$UBF,(R4) ;User doing buffering? BNE 30$ ;Yep, don't get buffer MOV V$RBSZ(R4),R0 ;Record buffer size INC R0 ;Just in case 'n' given CALL $$FALO ;Go for it -- note: if this fails, $$fcls ;will close the file. MOV R0,V$RBUF(R4) ;Set the first buffer MOV R0,V$BPTR(R4) ;and set free byte pointer 30$: MOV R4,R0 ;Return FILE pointer JMP CRET$ ;And exit C-style .PAGE ; ; ; ** $$FCSI ; ; Parse the file name and setup the FDB for the open ; ; Calling sequence: ; ; jsr r5,csv$ ;standard setup ; mov iov,r4 ;r4 -> io vector ; call $$fcsi ;parse the file name ; ;return: r0 -> fdb ; ;r0-r3 have been destroyed ; ;(exit via $$fope on error) ; $$FCSI:: ; ; Parse the name using the mighty CSI ; Note: since the mighty CSI overwrites the name, we must get a copy. ; SUB #FNMSIZ,SP ;Get temp area for CSI scan MOV SP,R0 ;R0 -> CSI temp MOV R0,$$CSIB+C.CMLD+2 ;String location MOV C$PMTR+0(R5),R1 ;File name from caller CLR R2 ;Setup for count 10$: MOVB (R1)+,(R0)+ ;Copy a byte BEQ 20$ ;Null byte finishes INC R2 ;Count the data byte CMP R2,#FNMSIZ ;Too big? BLT 10$ ;No, keep trucking BR $$FOPE ;Big names are no good 20$: MOV R2,$$CSIB+C.CMLD ;Stuff size into CSI block CSI$1 #$$CSIB ;Do syntax check BCS 50$ ;Exit on error ;17 CSI$2 ,OUTPUT ;Make name BCS 50$ ;Br if nogood ;17 ; ; On native RSX, parse the directory name (UIC) ;17+ ; Note: .ASCPP on RSTS/E fails if a logical device is passed. For ; example, fopen("c:stdio.h", "r") will not return the correct UIC/PPN ; Also, VMS compatibility mode apparently never returns a usable PPN. ; TST $$RSTS ;Running on RSTS? BNE 30$ ;Skip this if so: RSTS .parse gives uic MOV R4,R3 ;R3 -> IOV ADD #V$UIC,R3 ;R3 -> IOV @ UIC entry MOV $$UIC,(R3) ;Presuppose task default UIC ;19+ MOV #$$CSIB+C.DIRD,R2 ;R2 -> CSI block directory info TST (R2) ;Is there any directory info? BEQ 30$ ;Skip it if so (V$UIC == task default) ;19- CALL .ASCPP ;Parse directory information ;17- ; 30$: MOV R4,R0 ;R0 -> IOV ADD #V$FDB,R0 ;R0 -> FDB ; ; Call .parse directly to move all info. into the FDB ; This is needed for fwild()/fnext() as the .csi info might not be around ; MOV R4,R1 ;Get IOV ADD #V$FDB+F.FNB,R1 ;R1 -> file name block MOV #$$CSIB+C.DSDS,R2 ;R2 -> Device descriptor MOV #$$DFNB,R3 ;R3 -> Default file name block CALL .PARSE ;Load the file name into the FNB BCS 50$ ;Urk ; ; On RSTS/E, the true UIC is hidden away in the file name block ;17+ ; Note: this is "unpublished" information. ; TST $$RSTS ;Running on RSTS/E? BEQ 40$ ;Exit if not MOV V$FDB+F.FNB+N.DID(r4),V$UIC(r4) ;Get PPN if so 40$: ; ;17- ADD #FNMSIZ,SP ;Clear file name from stack ;18 ;NOTE: The CSI Block is invalid ;19 RETURN ;Exit 50$: ADD #FNMSIZ,SP ;Clear file name from stack ;18 MOV #IE.BNM,R0 ;Error "bad file name" BR $$FOPE ;and die .iff ;03+ .mcall .csispc, .dstat, .close, .wait .mcall .fetch ;10 ; ; RSTS/E offsets (for append and device status) ; FIRQB = 402 FQSIZE = 16 ; File size (low-order word) FQDEVN = 32 ; Device unit number (flag in high byte) ; ; Define local data ; .psect .data. defext: .word 0 ; No default extension csierr: .byte E$$ILF, E$$NOD ; Errors possible on .csispc lokerr: ;12+ .byte E$$ILU ; Lookup 0, channel open .byte E$$FNF ; Lookup 1, file not found enterr: .byte E$$ILU ; Enter 0, channel open .byte E$$NOR ; Enter 1, no room .byte E$$FAT ; Enter 2, undefined .byte E$$FND ; Enter 3, file already found ;12- .even ; .psect .prog. ; freope:: jsr r5,csv$ ; Establish linkage mov C$PMTR+4(r5),r4 ; Get IOV pointer mov r4,r0 ; Set r0 non-zero to call $$clos ; close the existing file br fopen1 ; continue at main sequence fopen:: jsr r5,csv$ ; Establish linkage clr r4 ; No buffer yet fopen1: ; Common code for fopen/freopen ;04 call $$flun ; Get a Unit (and IOV) call $$fopt ; Scan options string .page ; ; ** $$fopa ; ; Open the file given an Ascii name ; ; r3 has a LUN, try for a file name ; $$fopa:: ;14 sub #39.*2,sp ; Get a csi block mov C$PMTR+0(r5),r2 ; get the argument string ;20+ mov r2,(sp) ; r0 = strlen(file_name) + 1 call strlen ; inc r0 ; Add a byte for the NULL call $$falo ; Get actual length mov r0,V$NAME(r4) ; and save the buffer pointer 10$: movb (r2)+,(r0) ; Copy the next byte ;16+ beq 20$ ; Exit at the end cmpb (r0)+,#040 ; Not at the end, if it's not a blank, bne 10$ ; Keep on trucking dec r0 ; Ignore blanks br 10$ ; And do the next one ; 20$: ;16/20- mov sp,r1 ; r1 -> csi area .csispc r1,#defext,V$NAME(r4) ;23 bcc 30$ ; So far so good ;20 movb @#52,r0 ; Get error code movb csierr(r0),r0 ; Get our flavor br $$fope ; And exit 30$: mov (sp)+,r0 ; Get switch count beq 40$ ; Can't have switches, br if ok mov #E$$ILF,r0 ; Error code br $$fope ; Bye for now 40$: add #3*5*2,r1 ; r1 -> first input spec. ; ; ** $$fopr ; ; Open the file given the 4-word Rad50 device specification ; Entry: r1 -> device spec (Rad50) area. ; $$fopr:: ;14 sub #8.,sp ; Get a .dstat work area mov sp,r0 ; save the area mov r0,-(sp) ; Return area on the stack mov r1,r0 ; Device spec. in r0 emt 342 ; .dstat -- Get device status mov #VF$FIL,r2 ; Assume it's a file tst (sp) ; 0(sp) has device type ;10 bmi 20$ ; .dstat sets high bit if random-access mov #VF$REC,r2 ; Nope, set "record-oriented" flag 20$: bis r2,(r4) ; Remember it's type tst 4(sp) ; Is it resident now? ;10+ bne 25$ ; Continue if so mov 2(sp),r0 ; Get handler size beq 25$ ; Continue if zero size (RSTS) call $$falo ; Handler memory, never released mov r0,-(sp) ; Handler loaded here mov r1,r0 ; Device spec. in r0 emt 343 ; .fetch 25$: mov (sp),r0 ; Get device type ;10- add #8.,sp ; Clean up the stack cmp r0,#4 ; Terminal? bne 40$ ; Br if not tst $$rsts ; Yes, do console check if RSTS beq 30$ ; Br if RT11 mov r1,r0 ; RSTS, r0 -> csi area emt 360 ; .setfqb tst @#FIRQB+FQDEVN ; Was a unit given? bmi 40$ ; Br if so, it's not the console, then 30$: bis #VF$TTY,(r4) ; Set the flag bit #VF$UBF,(r4) ; Hack for "u" mode? ;08 beq openok ; No, continue ;08 bic #$$AMOD+$$WMOD,(r4) ; Yes, make it "input" ;08/13 br openok ; And exit 40$: clr -(sp) ; No seqnum clr -(sp) ; No size mov r1,-(sp) ; Push device area mov #1*400,-(sp) ; Assume .lookup bit #$$WMOD,(r4) ; Did fopen ask for write? beq 50$ ; Br if not asl (sp) ; Writing, (sp) := 2*400 50$: bis r3,(sp) ; Set lun onto stack mov sp,r0 ; r0 -> arg block emt 375 ; Open the file ror r0 ; Save C-bit add #4*2,sp ; Dump the stack tst r0 ; Open ok? bpl openok ; Yes, continue bit #$$AMOD,(r4) ; No, append? beq 60$ ; Not append, bad trouble bic #$$AMOD,(r4) ; Make append into bis #$$WMOD,(r4) ; write (new file) and br 40$ ; Try again 60$: movb @#52,r0 ; Didn't open, get error code bit #$$WMOD,(r4) ; Opening existing file? beq 70$ ; Br if so movb enterr(r0),r0 ; New file, get the error ;12 br $$fope ; and die. ;12 70$: movb lokerr(r0),r0 ; Get the error code br $$fope ; and die. ; ; File is open. ; openok: bic #VF$EOR,(r4) ; Mark not at end of file ;21 bis #VF$OPN,(r4) ; Mark that it's open ;15+ mov r4,r0 ; Normal exit ;15- jmp cret$ .endc ; ; The following code is (more or less) common to RSX and RT11 I/O ; ; ** $$FALO ; ; Subroutine $$FALO is called with the space needed in ; R0. If it returns, R0 has a pointer to the free buffer. ; ; All other registers are preserved ; $$FALO:: CALL $$ALOC ;Call global allocator TST R0 ;Which returns NULL on error BEQ 10$ ;Yes, sorry. RETURN ;Got it this time. 10$: .if ne rsx MOV #IE.NBF,R0 ;Error code "No buffer space" .iff MOV #E$$NSP,R0 ;Error code "no buffer space" .endc ; ; ** $$FOPE ; ; Error exit from fopen routines. If r4 is non-zero, the iov it points ; to will be deallocated. ; ; Calling sequence: ; ; mov #IE.code,r0 ;Error code ; jmp $$fope ;Jump here to die. ; $$FOPE:: MOV R0,$$FERR ;Save error code TST R4 ;Have we allocated an I/O vector? BEQ 10$ ;No, just die. CLR R0 ;Yes, force "full close" CALL $$FCLS ;Clean out buffers 10$: CLR R0 ;Return NULL JMP CRET$ ;Exit. .PAGE ; ; ** $$FOPT ; ; Scan the user option string -- called by fopen and fwild ; ; Calling sequence: ; ; mov iov,r4 ;r4 -> iov ; jsr r5,csv$ ;Standard C setup ; call $$fopt ;Scan options ; ;r0 is destroyed ; ; If the option string is in error, $$fopt returns NULL to the ; fopen caller with an error code in $$ferr. ; $$FOPT:: MOV R3,-(SP) ;Save temp register MOV R2,-(SP) ;Save temp register CLR R1 ;Option word built here MOV C$PMTR+2(R5),R0 ;R0 -> option string BEQ 30$ ;Ignore null parameter string ; 10$: MOVB (R0)+,R3 ;Get next byte BEQ 30$ ;Exit on null finish BIS #040,R3 ;Force lowercase MOV #OPTSTR,R2 ;Scan possible options string 20$: TSTB (R2) ;End of options? BEQ 80$ ;Yep, sorry CMPB R3,(R2)+ ;Found it? BNE 20$ ;Nope, try another BISB OPTVAL-OPTSTR-1(R2),R1 ;Gotcha, set the bit BR 10$ ;Do another 30$: MOV R1,R0 ;Get a copy of the option bits BIC #^C,R0 ;Just want read/write/append MOVB OPTCOD(R0),R0 ;Get option bits to set BMI 80$ ;Die if illegal .if eq rsx ;13 BEQ 40$ ;Br if reading BIT #$$AMOD,R0 ;Can't append on RT11 BEQ 50$ ;No problem, continue TST $$RSTS ;But, we can on RSTS (??) BNE 50$ ;Ok, continue BR 80$ ;Sorry .endc 40$: ;; BIS #VF$BAD,R0 ;Buffer is bad, set bit ;20 50$: BIT #NMODE,R1 ;No LF stuff? BEQ 60$ ;Nope, BIS #VF$NOS,R0 ;Set the flag 60$: BIT #UMODE,R1 ;User buffered? BEQ 70$ ;Nope .if ne rsx BIS #VF$NOS!VF$UBF,R0 ;Yep, set both flags .iff BIS #VF$UBF,R0 ;Yep, set the flag .endc 70$: MOV R0,(R4) ;Save mode flags MOV (SP)+,R2 ;Restore temp register MOV (SP)+,R3 ;Restore temp register RETURN ;Return to caller ; ; Bad option given ; 80$: .if ne rsx MOV #IE.BAD,R0 ;Get an error code .iff MOV #E$$ILF,R0 ;Illegal call .endc BR $$FOPE ;And die .PAGE ; ; ** $$FLUN ; ; Search for a free LUN and setup IOV/FDB ; ; Calling sequence: ; ; mov options,$$optf ; option bits ; mov iov,r4 ; r4 -> iov (or NULL if none) ; call $$flun ; Get lun slot ; ; Return: ; r3 := lun number ; r4 -> iov ; r0, r1 destroyed ; ; On error, return NULL to fopen() caller ; ; Find a free lun slot and initialize IOV pointers ; ; Note that the lun number may be used as in index into $$luns. ; However, the correspondance between lun number and $$luns offset ; differs for RSX and RT11 I/O: ; ; RSX: $$luns+0 == lun number 2 ; RT11: $$luns+0 == lun number 0 ; ; The reason for this is twofold: On RSX, stderr is opened as lun 1 ; and is not present in $$luns. On RT11, stderr is permanently ; assigned to the console terminal and channels are numbered starting ; at zero. ; ; This correspondance is tested in fclose(). ; $$FLUN:: MOV R4,R0 ;Need IOV pointer in R0 ;08 BEQ 10$ ;Br if no IOV yet MOV V$LUN(R4),R3 ;R3 needs lun number for fopen BR 40$ ;Continue ; 10$: ;Fopen called, find a lun .if ne rsx MOV $$NLUN,R0 ;RSX Highest lun number .iftf MOV #$$LUNS,R1 ; Lun buffer pointer ; ; Scan lun slots ; 20$: .ift DEC R0 ;RSX Any slots left? BGT 30$ ;RSX Yes MOV #IE.ILU,R0 ;RSX No, "illegal logical unit" .iff CMP R1,#$$LUNE ;RT11 is R3 at the end? BLO 30$ ;RT11 No, look at this one MOV #E$$NOC,R0 ;RT11 Yes, sorry about that .iftf BR $$FOPE ; Just exit 30$: TST (R1)+ ; In use now? BNE 20$ ; Yes, try another MOV R1,R3 ; Get the channel number .ift SUB #$$LUNS-2,R3 ;RSX Get lun number * 2 (first = lun 2) .iff SUB #$$LUNS+2,R3 ;RT11 R3 has lun * 2 .iftf ASR R3 ; R3 has lun .iff .WAIT R3 ;RT11 On RT11, we also check if it's in use BCC 20$ ;RT11 .wait returns ok if in use .iftf MOV #V$SIZE,R0 ; Get the extended file data block CALL $$FALO ; Get core MOV R0,-(R1) ; Set in lun table ;08 40$: ; Continue here if freopen MOV R0,R4 ; R4 -> IOV area ;08 .endc MOV R4,R1 ; Another copy of iov pointer ADD #V$SIZE,R1 ; End of it 50$: CLR (R0)+ ; Clean out CMP R0,R1 ; The BLO 50$ ; IOV + FDB MOV #-1,V$UGET(R4) ; Clear Ungetc character MOVB R3,V$LUN(R4) ; Remember the LUN RETURN ; Back to caller .end