!********************************************************************** ! MAG TAPE INTERFACE ROUTINES 1ST JUNE !********************************************************************** ! THIS CLOD INCORPORATES TWO MAIN SETS OF INTERFACES FOR HANDLING ! MAGNETIC TAPE TRANSFERS: ! (1) SUPPORT FOR HIGH LEVEL LANGUAGE (HLL) I/O FACILITIES ! (2) THE 'MAG' AND 'MT' MAGNETIC TAPE UTILITY ROUTINES, THE ! 'MT' ROUTINES BEING A SPECIALISED APPLICATION OF ! THE 'MAG' ROUTINES. ! THE MAIN PURPOSE OF IMPLEMENTING HLL AND UTILITY TAPE ACCESS ! FACILITIES TOGETHER IS TO COORDINATE THE REQUESTS TO DIRECTOR ! FOR TAPE TRANSFERS. THE DIRECTOR FACILITIES USED ARE THE ! 'DMAG CLAIM' AND 'DMAGIO' INTERFACES. ALL CALLS ON THESE ! DIRECTOR ROUTINES FROM THE HLL AND UTILITY INTERFACES ARE ! ROUTED THROUGH A COMMON SET OF ROUTINES, THE 'TAPE SERVICE ! ROUTINES' ! THESE 'TAPE SERVICE ROUTINES' PERFORM VERY LITTLE CHECKING ! OF THEIR OWN BUT ASSUME THAT MOST OF THE NECESSARY VALIDATION ! OF PARAMETERS ETC HAS BEEN CARRIED OUT AT A HIGHER LEVEL SINCE ! THE DEGREE OF CHECKING IS NOT NECESSARILY THE SAME FOR HLL AND ! UTILITY ACCESS. THESE ROUTINES PROVIDE TAPE HANDLING ! PRIMITIVES THAT ARE FUNCTIONALLY VERY SIMILAR TO THE 'MAG' ! ROUTINES. !?2 TO ASSIST WITH DIAGNOSING FAULTS IN THIS CODE, SOME TRACING !?2 AND DIAGNOSTIC CODE HAS BEEN INCLUDED. MOST OF THE STATEMENTS !?2 INVOLVED IN THIS HAVE BEEN MARKED WITH THE SEQUENCE OF CHARACTERS !?2 !?N WHERE N TAKES VARIOUS VALUES. THIS IS TO ENABLE THE STRIPPING !?2 OUT OF VARIOUS CATEGORIES OF DIAGNOSTIC CODE WHEN THEY ARE !?2 NOT REQUIRED. THE CATEGORIES INDICATED BY THE VARIOUS !?2 VALUES OF N ARE LOOSELY DEFINED AS: !?2 !?2 1 TEMPORARY DIAGS FOR CHASING ERRORS !?2 2 GENERALLY REQUIRED BY OTHER CATEGORIES !?2 3 CHANNEL ALLOCATION AND USE !?2 4 RECORD I/O TRACING !?2 5 UNEXPECTED RESULTS FROM CALLS ON DIRECTOR INTERFACES !?2 !********************************************************************** ! REQUIRED DIRECTOR INTERFACE ROUTINES !********************************************************************** externalintegerfnspec dmag claim (string (6) tsn, integername sno, integer req,mode) externalintegerfnspec dmag io (integername reply flag,control,len, integer type,sno,adr) externalintegerfnspec dsfi (string (6) user, integer fsys,type,set,addr) !********************************************************************** ! REQUIRED SUBSYSTEM ROUTINES !********************************************************************** systemroutinespec outfile (string (31) file,integer c size,hole,protect,integername conadd,flag) systemroutinespec disconnect (string (31) file,integername flag) systemroutinespec destroy (string (31) file,integername flag) externalstring (8)fnspec date systemstringfnspec next temp externalintegerfnspec uinfi(integer n) externalstringfnspec uinfs (integer type) systemroutinespec move (integer len,from addr,to addr) systemroutinespec itoe (integer address,len) systemroutinespec etoi (integer address,len) systemroutinespec fill (integer len,address,filler) systemstringfnspec itos(integer n) {!?2} externalroutinespec prompt (string (15) s) {!?2} systemroutinespec dump (integer start addr,len) systemintegermapspec comreg (integer n) !********************************************************************** ! CONSTANTS !********************************************************************** {!?2} constinteger defop = 99 constinteger ul = 1,sl = 2 ;! TYPES OF LABEL PROCESSING constinteger behind = 1 ;! ( PERMISABLE VALUES constinteger at = 2 ;! ( FOR THE CF_RELPOS constinteger ahead = 3 ;! ( PARAMETER constinteger eof = 153 constbyteinteger yes = 1, c no = 0 constinteger maxint = x'7FFFFFFF' constinteger minint = x'80000000' constinteger tape permit = x'00008000' constinteger tsfo = 2000 ;! TAPE SYSTEM FAULT NUMBER ORIGIN constinteger min tape channel = 1 constinteger max tape channel = 4 constinteger max fault count = 10 ;! MAX CONSECUTIVE FAULTS constinteger rtfudge = 100 ;! FUDGE FACTOR TO ALLOW FOR RED TAPE ! IN BUFFERS constinteger buffsod = 200 ;! OFFSET TO START OF DATA IN BUFFERS constbyteintegerarray format map(1:3,1:4) = c 2,4,1,3,5,0,0,6,0,0,7,0 ! THE DIMENSIONS OF THE 'FORMAT MAP' ABOVE ! CORRESPOND TO THE 'RECTYPE' AND 'BLOCKING' FIELDS ! OF THE FILE DESCRIPTOR RECORDS. ! THE 'DATA format' FIELD OF THE FILE DESCRIPTOR RECORD IS DERIVED ! FROM THE ABOVE TABLE, THE VALUES BEING INTERPRETED AS FOLLOWS: ! 1 UNDEFINED ! 2 FIXED UNBLOCKED ! 3 FIXED BLOCKED ! 4 VARIABLE UNBLOCKED ! 5 VARIABLE BLOCKED ! 6 VARIABLE SPANNED UNBLOCKED ! 7 VARIABLE BLOCKED SPANNED constinteger mindf = 1, c maxdf = 7 ;! LIMITS OF DATA formatS TYPES constbyteintegerarray rsmod (1:7) = 0,0,0,0,1,1,1 constbyteintegerarray bsmod (1:7) = 1,1,1,1,1,0,2 conststring (1)array recfm char(1:7) = "F","V","U"," ","B","S","R" conststring (4) bfn base = "T#MT" conststring (17) nulldsn = "?????????????????" constintegerarray tsfault(0:4) = 0,tsfo+11,tsfo+12,tsfo+13,eof conststring (28)array errmess (1:22) = c { 1} "Invalid tape format ", { 2} "File header error ", { 3} "Label number error ", { 4} "File name error ", { 5} "Failed to create buffer ", { 6} "Record format incorrect ", { 7} "Tape request refused by oper", { 8} "Failed to write label ", { 9} "Failed to position tape ", {10} "Physical end of tape ", {11} "I/O error ", {12} "Tape off-line ", {13} "Unknown flag ", {14} "Max tape channels exceded ", {15} "Tape already in use ", {16} "Logical end of tape ", {17} "Invalid format or blocking ", {18} "Record too long ", {19} "Invalid unlabelled access ", {20} "Failed to write tape mark ", {21} "Missing/misplaced tape mark ", {22} "Too many tapes requested " !********************************************************************** ! DECLARATIONS CONNECTED WITH PROCESSING IBM STANDARD TAPE LABELS !********************************************************************** ! THE RECORD formatS BELOW ARE TAKEN FROM THE IBM PUBLICATION ! 'OS/VS TAPE LABELS', REF GC26-3795-3. ! THE CONST ARRAYS DESCRIBE THE THE STRING ELEMENTS IN EACH RECORD, ! THE ZEROTH ELEMENT BEING THE NUMBER OF SUCH STRINGS AND EACH ! SUBSEQUENT ELEMENT GIVING THE LENGTH OF EACH STRING. ! THESE ITEMS ARE USED MAINLY IN THE ROUTINES 'DECODE LABEL' ! AND 'ENCODE LABEL' recordformat ibm vol1 format ( c string ( 4) id, string ( 6) volser, string (31) resa, string (10) owner, string (29) resb) constbyteintegerarray ibm vol1 field (0:5) = c 5,4,6,31,10,29 recordformat ibm file1 format ( c string ( 4) id, string (17) file name, string ( 6) file ser num, string ( 4) vol seq num, string ( 4) file seq num, string ( 4) generation, string ( 2) version, string ( 6) create date, string ( 6) expiry date, string ( 1) security, string ( 6) block count, string (13) sys code, string ( 7) resa) constbyteintegerarray ibm file1 field (0:13) = c 13,4,17,6,4,4,4,2,6,6,1,6,13,7 recordformat ibm file2 format ( c string ( 4) id, string ( 1) record format, string ( 5) block length, string ( 5) record length, string ( 1) density, string ( 1) volume switch, string (17) job id, string ( 2) rec tec, string ( 1) control chars, string ( 1) resa, string ( 1) block attribute, string ( 3) resb, string ( 5) tape deck id, string (33) resc) constbyteintegerarray ibm file2 field (0:14) = c 14,4,1,5,5,1,1,17,2,1,1,1,3,5,33 !********************************************************************** ! GLOBAL DECLARATIONS !********************************************************************** recordformat file desc format ( c integer level,dsnum, byteinteger status,access route,valid action,cur state, mode of use,mode,file org,dev code,rectype, flags,channel,relpos, integer rel file num,arec,rec size,minrec,maxrec, block ptr,recptr,sob,eob,sod,eod, transfers,block xfers,block length, reclen, byteinteger recrtl,blkrtl,lpmode,spare, blocking,data format,bwf, string (17) dsn, string (6) volume c ) recordformat tape channel format (string (6) tsn, integer use,tm count,mode,sno,term flag,xfers, block no,buffer length,type,format,use sequence,control, err count,et flag,string (4) init fsn,buffer file) ownrecord (tape channel format)array tc (min tape channel:max tape channel) ownstring (6)array held tape (min tape channel:max tape channel + 1) {!?4} recordformat tcx desc (integer before,rtrace,rtchan) {!?4} ownrecord (tcxdesc)array tcx (min tape channel:max tape channel) recordformat buffer header ( c record (ibm file1 format) label1 , record (ibm file2 format) label2 ) owninteger cuc = 0 ;! CURRENT (USER) UTILITY CHANNEL owninteger mtchannel ownbyteintegerarray uchannel(min tape channel:max tape channel) = c 0(max tape channel - min tape channel + 1) !********************************************************************** ! MISCELLANEOUS DECLARATIONS FOR INTERNAL USE !********************************************************************** routinespec skiptmmag (integer channel,n) routinespec skip blocks (integer channel,integername blocks,flag) routinespec skip tapemarks (integer channel,integername marks,flag) routinespec rewind tape (integer channel,control,integername flag) routinespec read block (integer channel,address,integername len,flag) !********************************************************************** ! DIAGNOSTIC DEBUGGING AIDS !********************************************************************** conststring (1)array hexof(0:15) = "0","1","2","3","4","5", "6","7","8","9","A","B","C","D","E","F" {!?2} string (80)fn answer to (string (15) request) {!?2} string (80) reply {!?2} reply = "" {!?2} prompt(request) {!?2} skipsymbol while nextsymbol = 10 {!?2} reply = reply.nextitem and skipsymbol while nextsymbol # 10 {!?2} skipsymbol {!?2} prompt ("DATA: ") {!?2} result = reply {!?2} end ;! OF ANSWER TO {!?2} routine pshex (integer n) {!?2} integer i {!?2} for i = 12,-4,0 cycle {!?2} printstring(hexof((n>>i)&x'0F')) {!?2} repeat {!?2} return {!?2} end ;! OF PSHEX {!?2} routine phex (integer n) {!?2} integer i {!?2} for i = 28,-4,0 cycle {!?2} printstring(hexof((n>>i)&x'0F')) {!?2} repeat {!?2} return {!?2} end ;! OF PHEX {!?4} routine print chan (integer chan) {!?4} record (tape channel format)name c {!?4} ! {!?4} c == tc(chan) {!?4} newline ; printstring("TAPE CHANNEL") ; write(chan,1) {!?4} printstring(" FOR TAPE ".c_tsn) ; newline {!?4} printstring(" USED TMNO MODE DSNO TERM XFRS BLKN BUFL") {!?4} printstring(" TYPE FORM USEQ IFSN BFILE") {!?4} newline {!?4} write(c_use,4) {!?4} write(c_tm count,4) {!?4} write(c_mode,4) {!?4} space ; phex(c_sno) {!?4} write(c_term flag,4) {!?4} write(c_xfers,4) {!?4} write(c_block no,4) {!?4} write(c_buffer length,7) {!?4} write(c_type,4) {!?4} write(c_format,4) {!?4} write(c_use sequence,4) {!?4} printstring(" ".c_init fsn." ".c_buffer file) ; newline {!?4} return {!?4} end ;! OF PRINT CHAN {!?4} routine print mbc heading (string (60) comment) {!?4} newline {!?4} printstring("OP BCF BWF RECLEN RECSIZE RECPTR ") {!?4} printstring("AREC SOD EOD BLKNO") {!?4} spaces(5) ; printstring(comment) {!?4} newline {!?4} return {!?4} end ;! OF PRINT MBC HEADING {!?4} ! {!?4} routine mbc (record (file desc format)name cf) {!?4} ! MONITOR BUFFER CONTROL {!?4} !NOTIMP80 recordspec cf (file desc format) {!?4} write(cf_relpos,2) {!?4} write(cf_bwf,3) {!?4} spaces(4) ; pshex(cf_reclen) {!?4} spaces(3) ; pshex(cf_recsize) {!?4} spaces(2) ; phex(cf_recptr) {!?4} space ; phex(cf_arec) {!?4} space ; phex(cf_sod) {!?4} space ; phex(cf_eod) {!?4} write(tc(cf_channel)_blockno,5) {!?4} newline {!?4} return {!?4} end ;! OF MBC {!?4} routine print file chars (record (file desc format)name fd) {!?4} !NOTIMP80 recordspec fd (file desc format) {!?4} {!?4} newline {!?4} printstring("CHARACTERISTICS OF FILE ".fd_dsn) {!?4} newline {!?4} printstring("RECTYPE BLOCKING format MAXREC BLKLEN BLKRTL RECRTL") {!?4} newline {!?4} printstring(" ".recfm char(fd_rectype)." ") {!?4} printstring(" ".recfm char(fd_blocking + 2)." ") {!?4} write(fd_data format,4) {!?4} write(fd_maxrec,8) {!?4} write(fd_block length,6) {!?4} write(fd_blkrtl,5) {!?4} write(fd_recrtl,5) {!?4} newline {!?4} return {!?4} end ;! OF PRINT FILE CHARS !********************************************************************** ! formatTING AND CONVERSION ROUTINES !********************************************************************** routine plant integer (integer value,address) move(4,addr(value),address) return end integerfn short int(integer address) result = (byteinteger(address)<<8) + byteinteger(address + 1) end ;! OF SHORT INT string (20)fn strof (integer n,l) integer x,a string (20) s s = "" if n < 0 then a = yes and n = n*(-1) else a = no while n > 0 cycle x = n n = n//10 s = tostring(((x-n*10)+48)).s repeat s = "0".s while length(s) < l if a = yes then s = "-".s result = s end ;! OF STROF string (255)fn blanks (integer n) string (255) s length(s) = n fill(n,addr(s)+1,32) result = s end ;! OF BLANKS string (255)fn zeros (integer n) string (255) s length(s) = n fill(n,addr(s)+1,0) result = s end ;! OF ZEROS integerfn intof (string (20) num) integer i,x,a,y ownstring (1) yy = "0" y = byteinteger(addr(yy)+1) a=addr(num) x = 0 num= substring(num,2,length(num)) while substring(num,1,1) = " " for i = 1,1,length(num) cycle x = (x*10) +byteinteger(a+i) - y repeat result = x end ;! OF INTOF string (5)fn ibmdate constintegerarray dinm(1:12) = c 0,31,59,90,120,151,181,212,243,273,304,334 integer y,m,d,x string (8) dd dd = date y = intof(substring(dd,7,8)) m=intof(substring(dd,4,5)) d=intof(substring(dd,1,2)) d=d+dinm(m) x = y//4 if m > 2 and y = x*4 then d = d + 1 ;! LEAP YEAR result = strof(y,2).strof(d,3) end ;! OF IBMDATE !********************************************************************** ! ENCODE LABEL !********************************************************************** ! THIS ROUTINE HAS THE CONVERSE EFFECT TO THAT OF 'DECODE LABEL' ! SEE COMMECNTS UNDER 'DECODE LABEL' FOR DETAILS routine encode label (stringname label, byteintegerarrayname field, integer record address) integer pointer,i,raddr {!?3} integer a raddr = record address - 1 ;! I STARTS FROM 1 pointer = 0 ; label = "" for i = 1,1,field(0) cycle label = label.string(raddr + pointer + i) pointer = pointer + field (i) repeat {!?3} newline {!?3} printstring("LABEL OF") ; write(length(label),1) {!?3} printstring(" BYTES") ; newline {!?3} for i = 1,1,field(0) cycle {!?3} printstring("!") {!?3} spaces(field(i)-1) {!?3} repeat {!?3} printstring("<") {!?3} newline {!?3} a = addr(label)+1 {!?3} for i = a,1,a + length(label) cycle {!?3} if byteinteger(i) = 0 then printsymbol('*') elsestart {!?3} printsymbol(byteinteger(i)) {!?3} finish {!?3} repeat {!?3} printsymbol('<') {!?3} newline itoe(addr(label) + 1,length(label)) return end ;! OF ENCODE LABEL !*********************************************************************** ! DECODE LABEL !********************************************************************** ! THIS ROUTINE IS USED TO DECODE THE FIELDS OF A TAPE LABEL INTO ! FIELDS OF AN IMP RECORD. IT EXTRACTS THE FIELDS FROM A STRING ! OF EBCDIC CHARACTERS ('LABEL') AND INSERTS THEM INTO STRING ! ELEMENTS OF A RECORD (AFTER TRANSLATION TO ISO). THE ARRAY ! 'FIELD' SPECIFIES THE FIELDS, ITS ZEROTH ELEMENT BEING THE ! NUMBER OF FIELDS AND THE SUBSEQUENT ELEMENTS GIVING THE ! LENGTHS OF THE FIELDS. routine decode label (string (255) label, byteintegerarrayname field, integer record addr) integer pointer,i,raddr {!?3} integer a pointer = 1 raddr = record addr - 2 ;! ADJUSTED BECAUSE POINTER AND I ! START FROM 1. etoi(addr(label) + 1,length(label)) {!?3} newline {!?3} printstring("LABEL OF") ; write(length(label),1) {!?3} printstring(" BYTES") ; newline {!?3} for i = 1,1,field(0) cycle {!?3} printstring("!") {!?3} spaces(field(i)-1) {!?3} repeat {!?3} printstring("<") {!?3} newline {!?3} a = addr(label)+1 {!?3} for i = a,1,a + length(label) cycle {!?3} if byteinteger(i) = 0 then printsymbol('*') elsestart {!?3} printsymbol(byteinteger(i)) {!?3} finish {!?3} repeat {!?3} printsymbol('<') {!?3} newline for i = 1,1,field(0) cycle string(raddr + pointer + i) = c substring(label,pointer,pointer + field(i) - 1) pointer = pointer + field(i) repeat end ;! OF DECODE LABEL !<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ! TAPE SERVICE ROUTINES !<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< !********************************************************************* ! HOLD STATUS !********************************************************************** byteintegerfn hold status (string (6) tape) integer i for i = min tape channel,1,max tape channel cycle if held tape(i) = tape then result = yes repeat result = no end ;! OF HOLD STATUS !********************************************************************** ! CLOSE TAPE !********************************************************************** routine close tape (integer channel,integername flag) if hold status(tc(channel)_tsn) = no then start flag = dmag claim(tc(channel)_tsn,tc(channel)_sno,1,1) ;! RELEASE TAPE {!?5} if flag # 0 then monitor tc(channel)_tsn = "" finish tc(channel)_use = 0 ;!NOT ALLOCATED FOR ANY TYPE OF USE return end ;! OF CLOSE TAPE !************************************************************************** ! CLEAR TAPES !************************************************************************** routine clear tapes (integer use mask) integer channel,flag for channel = min tape channel,1,max tape channel cycle if tc(channel)_use & use mask > 0 then close tape(channel,flag) repeat return end ;! OF CLEAR TAPES !********************************************************************** ! HOLDVOL !********************************************************************** externalroutine holdvol (string (6) tape,integername f) integer i,j tape = tape." " while length(tape) < 6 j = -1 for i = max tape channel,-1,min tape channel cycle if held tape(i) = tape then f = 1 and return if held tape(i) = "" then j = i repeat if j = -1 then f = 2 else f = 0 and held tape (j) = tape return end ;! OF HOLDVOL !********************************************************************** ! RELEASEVOL !********************************************************************** routine releasevol (string (6) tape,integername f) integer i,j,flag for i = min tape channel,1,max tape channel cycle if held tape(i) = tape then held tape(i) = "" and exit repeat f = 0 for i = min tape channel,1,max tape channel cycle if tc(i)_tsn = tape then start if tc(i)_use = 0 then close tape(i,flag) exit finish repeat return end ;! OF RELEASEVOL !********************************************************************** ! OPEN TAPE !********************************************************************** ! INPUT TO THIS ROUTINE IS A REQUEST FOR A LOGICAL CHANNEL TO BE ! ALLOCATED TO A TAPE. THE CHANNEL CONCERNED IS ONE OF THE ! RECORD ELEMENTS OF THE GLOBAL RECORD ARRAY `TC`. THE ! LIST OF CHANNELS IS SEARCHED TO SEE IF THE TAPE HAS A CHANNEL ! ALREDY ALLOCATED TO IT AND IF SO, THE CHANNEL IS CHECKED TO SEE ! IF IT IS OK TO BE USED. IF NOT THE NEXT AVAILABLE ! CHANNEL IS ALLOCATED TO THE TAPE AND THE TAPE IS `CLAIMED` ! VIA A CALL ON DIRECTOR. THE VARIOUS FIELDS OF THE ! ALLOCATED CHANNEL ARE INITIALISED. routine open tape (string (6) tsn,integer mode, integername channel,flag) integer i,mark,sno find chan: flag = 0 ; channel = -1 ; mark = -1 for i = max tape channel,-1,min tape channel cycle if tc(i)_tsn = "" then mark = i if tc(i)_tsn = tsn then channel = i repeat unless channel = -1 then start ! TAPE CHANNEL ALREADY ALLOCATED TO THIS TAPE if tc(channel)_use > 0 then flag = tsfo + 15 c and return if tc(channel)_mode # mode then start releasevol(tc(channel)_tsn,flag) -> find chan finish flag = -1 {!?3} if flag = -1 then start {!?3} printstring("TAPE ".tsn." PREALLOCATED ON CHANNEL") {!?3} write(channel,1) ; newline {!?3} finish finish else start ! CHANNEL = -1, IE. TAPE NOT ALREADY ALLOCATED ! NEED TO ALLOCATE A NEW CHANNEL AND CLAIM THE TAPE if mark = -1 then flag = tsfo + 14 and return channel = mark flag = dmag claim(tsn,sno,0,mode) ! A RESPONSE OF 104 IMPLIES OPERATOR HAS REFUSED REQUEST ! TO MOUNT TAPE if flag # 0 then start if flag = 104 then flag = tsfo + 7 {!?5} monitor return finish tc(channel)_tsn = tsn tc(channel)_sno = sno tc(channel)_use = 0 tc(channel)_tm count = -1 tc(channel)_et flag = no tc(channel)_mode = mode tc(channel)_type = 0 tc(channel)_format = 0 tc(channel)_block no = 0 tc(channel)_xfers = 0 {!?3} printstring("TAPE ".tsn." ALLOCATED ON CHANNEL") {!?3} write(channel,1) finish return end ;! OF OPEN TAPE !*********************************************************************** ! HOLDMT !************************************************************************** externalroutine holdmt (string (255) parms) integer f if length(parms) > 6 then length(parms) = 6 holdvol(parms,f) if f > 1 then start select output(0) newline printstring("Too many held tapes") newline finish return end ;! OF HOLDMT !********************************************************************** ! RELEASEMT !********************************************************************** externalroutine releasemt (string (255) parms) integer i,j,flag,f if length(parms) > 6 then length(parms) = 6 if parms = ".ALL" then start for i = max tape channel,-1,min tape channel cycle unless held tape(i) = "" then releasemt (held tape(i)) repeat return finish releasevol(parms,f) if f > 0 then start select output (0) newline printstring("Tape ") printstring(parms) printstring(" not held") newline finish return end ;! OF RELEASEMT !********************************************************************** ! LOCATE TAPE !********************************************************************** routine locate tape (integer channel,type, integername num,flag) integer x,n,tm count,tm skip count,reqd file integer len,direction,count byteintegerarray lbuff (0:80) stringname id record (tapechannel format)name cc switch t(1:10) !%IF (TYPE & X'01') ! COUNT = 0 %THEN %RETURN ;! NULL CALL cc == tc(channel) count = num -> t(type) t(1): ! ABSOLUTE BLOCK MOVE if count < 2 then count = 0 and -> t(8) count = count - cc_block no - 1 t(2): ! RELATIVE BLOCK MOVE unless count = 0 then skip blocks(channel,count,flag) return t(3): ! ABSOLUTE TAPE MARK MOVE if count < 1 then start ! MOVE TO BT REQUESTED rewind tape (channel,0,flag) return finish count = count - cc_tm count if count < 1 then count = count - 1 x = count skip tape marks (channel,x,flag) if count < 1 then start ! WAS MOVING BACKWARDS DOWN TAPE x = 1 skip tape marks(channel,x,flag) finish return t(4): ! RELATIVE TAPE MARK MOVE x = count if count = 0 then x = 1 skip tape marks (channel,x,flag) if count = 0 then start x = -1 skip tape marks (channel,x,flag) finish return t(5): ! NOT USED t(6): ! NOT USED return t(7): ! MOVE TO ABSOLUTE PHYSICAL FILE count = count - 1 -> t(3) ! t(8): ! SKIP N PHYSICAL FILES if count > 0 then x = count else x = count - 1 locate tape (channel,4,count,flag) unless count > 0 c then x = 1 and skip tape marks (channel,x,flag) return !<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ! FLAG VALUES CONNECTED WITH FILE LOCATION ! -2 => LOGICAL END OF TAPE ! -3 => PHYSICAL END OF TAPE ! -4 => BT, I.E. PHYSICAL START OF TAPE ! -1 => FAILED TO POSITION, PROBALLY LOST THE PLACE !<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< t(9): ! ABSOLUTE IBM FILE MOVE reqd file = count -> file pos t(10): ! RELATIVE IBM FILE MOVE reqd file = count + ((cc_tm count//3) + 1) file pos: ! POSITION TAPE AT HDR1 FOR REQUIRED FILE if cc_tm count < 0 then start ! PREVIOUSLY LOST PLACE ON THE TAPE, MUST NOW TRY TO ! FIND OUT WHERE WE ARE rewind tape (channel,0,flag) if flag > 0 then -> badposs finish ! NOTE THAT THE 'TM COUNT' FIELD OF THE TAPE CHANNEL RECORD IS ! SET TO ZERO ONLY BY THE 'REWIND TAPE' ROUTINE tm count = ((reqd file - 1) * 3) if tm count = 0 then start ! FIRST FILE ON TAPE REQUIRED rewind tape (channel,0,flag) if flag # 0 then -> badposs n = 1 skip blocks (channel,n,flag) ;! SKIP VOL1 LABEL if flag > 0 then -> badposs finish else start ! SOME FILE OTHER THAN THE FIRST ON THE TAPE REQUIRED cycle tm skip count = tm count - cc_tm count if tm skip count < 1 then start ! A SKIP COUNT OF ZERO IMPLIES THAT THE TAPE IS ! POSITIONED AHEAD OF THE REQUIRED TAPE MARK ! BUT BEFORE THE NEXT, IMPLYING A BACKWARDS MOVE. A ! SKIP COUNT OF LESS THAN ZERO IMPLIES A BACKWARDS MOVE A ! ALSO. FOR BOTH CASES IT IS SIMPLER TO SKIP BACKWARDS ! OVER THE REQUIRED TAPE MARK AND THEN FORWARDS OVER ! IT AGAIN, SOMETHING THAT IS ACHIEVED BY THE TEST ! IMMEDIATLY FOLLOWING THE CALL ON SKIP TAPE MARKS. direction = -1 tm skip count = tm skip count - 1 finish else direction = 1 skip tape marks(channel,tm skip count,flag) if flag = 0 then start if direction = 1 then exit ! IF DIRECTION = -1 IT WILL BE NECESSARY TO SKIP ! FORWARDS OVER THE REQUIRED TAPE MARK. THIS ! WILL HAPPEN AUTOMATICALLY ON REPEATING THE CYCLE AGAIN finish else start if flag # 4 then -> badposs ! ! FLAG = 4. THIS MEANS A DOUBLE TAPE MARK WHICH ! IMPLIES: ! MOVING IN EITHER DIRECTION ! * EMPTY FILE ! MOVING FORWARD UP THE TAPE ! * LOGICAL END OF TAPE ) ONLY FOR CORRECTLY ! * PHYSICAL END OF TAPE ) formatTED TAPES ! MOVING BACKWARDS ! * B.T. - PHYSICAL START OF TAPE ! ! THE TAPE WILL BE POSITIONED BETWEEN THE TWO MARKS ! ! NOW HAVE TO BACK UP THE TAPE SO THAT THE ! BLOCK PRECEDING THE DOUBLE TAPE MARK CAN ! BE EXAMINED ! n = -1 skip tape marks (channel,n,flag) if flag = 4 and direction = -1 c then flag = -4 and return ;! 4 => BT WHEN IN REVERSE -> badposs unless flag = 0 n = -1 skip blocks (channel,n,flag) -> badposs unless flag = 0 ! len = 80 ;!( GET read block(channel,addr(lbuff(1)),len,flag) ;!( IDENTIFIER -> badposs unless flag = 0 and len = 80 ;!( FIELD id == string(addr(lbuff(0))) ;!( FROM length(id) = 4 ;!( LABEL ! if id = e"EOF2" then flag = -2 and return ;! EOD if id = e"EOV2" then flag = -3 and return ;! EOT unless id = e"HDR2" then -> badposs ! ! LAST BLOCK WAS A HDR2 LABEL, HENCE WE HAVE AN EMPTY FILE if direction = 1 then start ! IF REVERSING BACK DOWN TAPE, THERE ! IS NO NEED TO SKIP THE DTM WHICH THIS ! NEXT BIT DOES. n = 1 skip tape marks (channel,n,flag) return unless flag = 0 n = 1 skip tape marks (channel,n,flag) return unless flag = 0 finish finish repeat finish return badposs: ! FAILED TO POSITION TAPE AT REQUESTED POSITION flag = -1 cc_tmcount = -1 return end ;! OF LOCATE TAPE !********************************************************************** ! UNWIND TAPE !********************************************************************** ! %ROUTINE UNWIND TAPE (%INTEGER CHANNEL,%INTEGERNAME FLAG) ! ????? NOTE THAT THIS ROUTINE CANNOT BE BROUGHT INTO USE ! ????? UNTIL THE RETURN VALUE FROM DMAGIO CAN BE TRUSTED ! %INTEGER LEN ! LEN = MAXINT ! SKIP TAPE MARKS (CHANNEL,LEN,FLAG) ! %UNLESS FLAG = 0 %OR FLAG = 4 %C ! %THEN TC(CHANNEL)_TM COUNT = -1 %C ! %ELSE TC(CHANNEL)_TM COUNT = TC(CHANNEL)_TM COUNT + LEN ! %RETURN ! %END ;! OF UNWIND TAPE !********************************************************************** ! REWIND TAPE !************************************************************************ routine rewind tape (integer channel,control,integername flag) integer x,len len = 0 ; flag = dmagio(x,control,len,6,tc(channel)_sno,0) if flag = 0 then flag = x if flag = 0 start tc(channel)_tm count = 0 tc(channel)_et flag = no tc(channel)_block no = 0 finish else tc(channel)_tm count = -2 ! -2 INDICATES THAT WE'VE LOST THE PLACE AND WITHOUT MUCH HOPE !OF RECOVERY {!?5} if flag # 0 then monitor return end ;! OF REWIND TAPE !********************************************************************** ! REVERSE READ BLOCK !********************************************************************** ! THIS ROUTINE PERFORMS THE SAME FUNCTION AS THE 'READ BLOCK' ! ROUTINE BUT ATTEMPTS TO READ THE REQUIRED BLOCK BACKWARDS, ! IE. IT TRIES TO READ THE PREVIOUS BLOCK. ! WILL TRY TO IMPLEMENT THIS VIA A PROPER BACKWARDS READ CALL ! ON DMAGIO ASAP. routine reverse read block (integer channel,address, integername len,flag) integer x x = -1 skip blocks (channel,x,flag) if flag # 0 then return read block (channel,address,len,flag) if flag # 0 then return x = -1 skip blocks (channel,x,flag) return end ;! OF REVERSE READ BLOCK !********************************************************************** ! WRITE TAPE MARK !********************************************************************** routine write tape mark (integer channel,integername flag) ! THIS ROUTINE SIMPLY WRITES A TAPE MARK ONTO THE TAPE SPECIFIED ! VIA 'CHANNEL' AT THE CURRENT POSITION OF THE TAPE. NO CHECKS ! ARE MADE HERE AND IT IS ASSUMED THAT THE CHANNEL IS VALID AND ! THAT WRITING TAPE MARKS IS A VALID THING TO DO. ! A FLAG OF 4 FROM 'DMAGIO' INDICATES THAT THE WRITE HAS TAKEN PLACE ! PAST THE 'PHYSICAL END OF TAPE' MARKER, A VALID THING TO DO. ! HOWEVER THE 'ET' FLAG IN THE CURRENT CHANNEL IS SET AND IS ! CHECKED ON SUBSEQUENT ENTRIES TO 'MAGIO' integer x,control,len control = 0 ; len = 0 flag = dmagio (x,control,len,10,tc(channel)_sno,0) if flag = 0 then flag = x if flag = 4 then tc(channel)_et flag = yes and flag = 0 if flag = 0 then start tc(channel)_xfers = tc(channel)_xfers + 1 tc(channel)_tm count = tc(channel)_tm count + 1 tc(channel)_block no = 0 finish {!?5} if flag # 0 then monitor return end ;! OF WRITE TAPE MARK !********************************************************************** ! SKIP TAPE MARKS !********************************************************************** routine skip tape marks (integer channel,integername marks,flag) integer x,control,direction control = 1 if marks < 0 then direction = -1 else direction = 1 flag = dmagio(x,control,marks,9,tc(channel)_sno,0) if flag = 0 then flag = x if flag = 0 or flag = 4 then start tc(channel)_tm count = tc(channel)_tm count + (marks * direction) tc(channel)_block no = 0 finish else tc(channel)_tm count = -1 ;! LOST THE PLACE {!?5} unless flag = 0 or flag = 4 then monitor return end ;! OF SKIP TAPE MARKS !********************************************************************** ! SKIP BLOCKS !********************************************************************** routine skip blocks (integer channel,integername blocks,flag) ! IF A TAPE MARK IS ENCOUNTERED WHILE SKIPPING BLOCKS, THE TAPE IS ! LEFT POSITIONED BEFORE THE TAPE MARK, THIS BEING PART OF ! THE FUNCTION OF 'DMAGIO' integer x,control,direction,b b = blocks control = 0 if blocks < 0 then direction = -1 else direction = 1 flag = dmagio(x,control,blocks,8,tc(channel)_sno,0) if flag = 0 then flag = x if flag = 0 then c tc(channel)_block no = tc(channel)_block no + b else start if flag = 4 then tc(channel)_block no = 0 else c tc(channel)_block no = -1 finish {!?5} unless flag = 0 or flag = 4 then monitor return end ;! OF SKIP BLOCKS !********************************************************************** ! WRITE BLOCK !********************************************************************** routine write block(integer channel,address,len,integername flag) ! SEE COMMENTS UNDER 'WRITE TAPE MARK' integer x,control control = 0 flag = dmagio(x,control,len,2,tc(channel)_sno,address) if flag = 0 then flag = x if flag = 4 then tc(channel)_et flag = yes and flag = 0 if flag = 0 then start tc(channel)_term flag = no tc(channel)_xfers = tc(channel)_xfers + 1 tc(channel)_block no = tc(channel)_block no + 1 finish {!?5} if flag # 0 then monitor return end ;! OF WRITE BLOCK !********************************************************************** ! READ BLOCK !************************************************************************ routine read block (integer channel,address,integername len,flag) ! THIS ROUTINE ATTEMPTS TO READ A BLOCK FROM THE TAPE SPECIFIED VIA ! 'CHANNEL'. AN AREA IS PROVIDED BY THE CALLER AND IF THIS IS ! INSUFFICIENT FOR THE TRANSFER, A LONG BLOCK INDICATION IS ! RETURNED. IF A TAPE MARK IS ENCOUNTERED, THE TAPE IS LEFT ! POSITIONED BEFORE THE TAPE MARK AND AN INDICATION IS RETURNED. integer control,x,a control = 4 ;! IGNORE SHORT BLOCKS if address = 0 then a = 4 else a = 1 ! ADDRESS = 0 => READ CHECK => 4 ! 1 => READ WITH DATA TRANSFER, IE. A REAL READ flag = dmagio(x,control,len,a,tc(channel)_sno,address) if flag = 0 then start ! CALL ON DIRECTOR ACCEPTED OK. flag = x if flag < 2 then start ! CALL ON DIRECTOR EXECUTED AND AN ATTEMPT ! HAS BEEN MADE TO READ A BLOCK WHICH WAS ! SUCCESSFUL OR FAILED WITH AN I/O ERROR. ! IN EITHER CASE THE TAPE IS LEFT POSITIONED ! AFTER THE BLOCK IN QUESTION. tc(channel)_xfers = tc(channel)_xfers + 1 tc(channel)_block no = tc(channel)_block no + 1 finish finish {!?5} unless flag = 0 or flag = 4 then monitor return end ;! OF READ BLOCK !>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ! END OF TAPE SERVICE ROUTINES !>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> !********************************************************************** ! >> MAGIO << - MAIN CONTROL ROUTINE FOR HLL TAPE ACCESS !********************************************************************** systemroutine magio (integer fd addr,operation, integername flag) ! THE VALUES OF THE OPERATION PARAMETER ARE AS FOLLOWS: ! 1 PREPARE FOR WRITE ! 2 READ A RECORD ! 3 WRITE A RECORD ! 4 REWIND FILE ! 5 BACKSPACE ONE RECORD ! 6 ENDFILE (TERMINATE FILE ON TAPE) ! 7 CLOSE FILE ! 8 CLOSE FILE AND TAPE ! 9 OPEN FILE FOR READ ! 10 OPEN NEW FILE FOR WRITE ! 11 OPEN FILE FOR APPENDING (MOD WRITE) ! 12 CLEAR ALL TAPES ALLOCATED FOR UTILITY USE routinespec init desc fields routinespec find preceding tape record integerfnspec preceding length routinespec open tape file routinespec rewind file routinespec process eob routinespec write labels routinespec endfile (integer closing) routinespec close file {!?2} byteintegername rectype,fdflags,lpmode,blocking {!?2} integername maxrec {!?4} string (10) reply integer recptr,reclen,sod,eod,recrtl,blkrtl integer limit,blockmod integer rc,n,x,blksize,shiftlen,len,ptr,seglen,channel ptr integer spanbits record (file desc format)name cf ;! CURRENT FILE record (tape channel format)name cc ;! CURRENT CHANNEL record (buffer header)name header switch op (1:12) switch r,w(mindf:maxdf) switch s(0:3) ;! FOR HANDLING THE DIFFERENT SPANNING ! CONDITIONS if operation = 12 then start ! MUST CHECK FOR OP = 12 BEFORE MAPPING CF RECORD ! BECAUSE FD ADDR NOT NECESSARILY VALID FOR OP = 12 clear tapes(2) flag = 0 return finish cf == record(fd addr) ;! MAP SUPPLIED RECORD ONTO RECORD format ! THE FOLLOWING VARIABLES ARE HELD LOCALLY BECAUSE OF THE VERY ! FREQUENT ACCESS MADE TO THEM IN THE CODE, ESPECIALLY AS ONLY ! THREE OF THEM HAVE TO BE COPIED BACK INTO THE CF RECORD AT ! EXIT FROM MAGIO. NOTE THAT ALL CALLS TO MAGIO EXIT VIA ! THE LABEL SOPEND WHERE THE RECPTR, RECLEN AND EOD FIELDS ! ARE COPIED BACK INTO CF. recptr = cf_recptr ;! 65 reclen = cf_reclen ;! 37 sod = cf_sod ;! 42 eod = cf_eod ;! 48 recrtl = cf_recrtl ;! 28 blkrtl = cf_blkrtl ;! 37 {!?2} ! THE FOLLOWING ARE SET UP TO IMPROVE {!?2} ! DIAGNOSTICS IN THE EVENT OF A FAILURE WHEN TESTING BY {!?2} ! MAKING IMPORTANT FIELDS IN THE CF RECORD VISABLE. {!?2} rectype == cf_rectype {!?2} fdflags == cf_flags {!?2} lpmode == cf_lpmode {!?2} blocking == cf_blocking {!?2} maxrec == cf_maxrec {!?2} comreg(25) = 1 ;! SWITCHES ON DIAGS IN SYSTEM ROUTINE ! THIS FIRST PART OF THE CONTROL ROUTINE CHECKS THE FILE DESCRIPTOR ! AND SPECIFIED CHANNEL AGAINST THE REQUESTED OPERATION AND TAKES ! THE APPROPRIATE ACTION. unless operation > 8 then start ! THE FOLLOWING OPERATIONS DO NOT APPLY WHEN OPENING A FILE channel ptr = cf_channel cc == tc(channel ptr) ;! SET UP THE 'CURRENT CHANNEL' DESCRIPTOR. if cc_et flag = yes then start ! HAVE WRITTEN PAST 'END OF TAPE' MARKER if operation <= 5 then flag = tsfo + 10 and -> sopend finish finish flag = 0 {!?4} if operation <= 8 and tcx(channel ptr)_rtrace = yes then start {!?4} select output (tcx(channel ptr)_rtchan) {!?4} write(operation,1) {!?4} mbc(cf) {!?4} select output (defop) {!?4} finish -> op(operation) op(9): ! OPEN FOR READ op(10): ! OPEN FOR NEW WRITE op(11): ! OPEN FOR MOD WRITE open tape file -> sopend op(1): ! PREPARE FOR WRITE PREPARE FOR WRITE PREPARE FOR WRITE ! ??????? MUST CHECK FOR THIS CALL WHEN AT END OF BUFFER ! THIS CALL IS MADE WHENEVER IT IS REQUIRED TO CHANGE FROM READ ! MODE TO WRITE MODE. THIS IS TO ENSURE THAT THE 'AREC' POINTER ! IS SHIFTED TO POINT AT THE POSITION INTO WHICH THE NEXT RECORD ! IS TO BE WRITTEN. if cf_blocking > 2 and cf_relpos = ahead then start ! SPANNED RECORD formatS !THIS CODE IS HERE BECAUSE 'PROCESS EOB' CANNOT BE USED IN ITS ! PRESENT FORM FOR SPANNED RECORDS n = -1 skip blocks (channel ptr,n,flag) if flag > 0 then -> sopend cf_relpos = behind if recptr < cf_blockptr then start ! THE RECORD CURRENTLY INDICATED BY 'RECPTR' SPANNED TWO ! OR MORE BLOCKS ON THE TAPE find preceding tape record if flag > 0 then -> sopend finish else start ! RECORD POINTED AT BY 'RECPTR' IS CONTAINED ENTIRELY WITHIN ! ONE BLOCK ON THE TAPE, IE THE BLOCK AT ! THE TAPE'S CURRENT POSITION, HAVING ALREADY SKIPPED BACK ! ONE BLOCK. ! NOW HAVE TO MOVE THE CURRENT RECORD (OR SEGMENT) BACK ! TO THE START OF DATA POSITION. len = eod - cf_blockptr if len > 0 then start move(len,cf_blockptr,sod + blkrtl) ! ????? IF BLOCKPTR>EOD MAY HAVE TO RE-READ BLOCK ! SINCE IN THE RECONSTRUCTION OF A SPANNED RECORD THE ! SDW IS STRIPPED OUT AND THERE IS NO WAY OF ! RELIABLY RECONSTRUCTING IT. recptr = recptr - len eod = sod + blkrtl + len finish finish finish cf_arec = recptr + recrtl -> sopend op(2): ! READ READ READ READ READ READ READ READ READ READ ! BUFFER CONTROL ! BUFFER CONTROL IS MANAGED BY MANIPULATION OF THE FOLLOWING ! PARAMETERS IN THE FILE DESCRIPTOR ('CF') FOR THE 'CURRENT FILE'. ! SOD START OF DATA - DOESN'T CHANGE ! EOD END OF DATA - UPDATED AS BLOCKS ARE READ INTO BUFFER. ! USED AS CHECK TO DETECT END OF DATA ON READ. ! RECPTR RECORD POINTER. ON ENTRY, POINTS TO THE RECORD TO BE ! MANIPULATED BY THIS CALL OR TO EOD IF NEW BLOCK NEEDS ! READING. ON EXIT IT POINTS TO THE POSITION OF THE NEXT ! RECORD TO BE TRANSFERED OR TO EOD IF THE CURRENTLY READ ! RECORD WAS THE LAST (OR ONLY) RECORD IN THE BLOCK ! RECLEN RECORD LENGTH. ON ENTRY, UNDEFINED FOR THE PURPOSES OF ! READING EXCEPT FOR 'UNDEFINED'. ON EXIT IT HOLDS THE ! LENGTH OF THE RECORD JUST 'READ'. SEE NOTE UNDER ! R(1): FOR EXPLANATION OF EXCEPTION FOR 'U' format. ! AREC ON ENTRY, UNDEFINED. ON EXIT POINTS TO THE DATA PORTION ! OF THE RECORD JUST 'READ'. ! RECSIZE ON ENTRY, UNDEFINED. ON EXIT THIS HOLDS THE LENGTH OF ! THE DATA PORTION OF THE RECORD JUST 'READ'. if recptr >= eod then start ! PREVIOUS RECORD READ WAS LAST IN BUFFER, IE IN CURRENT BLOCK. ! CHECK TO SEE IF WRITE OUT FLAG SET. IF IT IS THEN THIS ! BLOCK HAS BEEN WRITTEN TO AND REACHING THE END OF DATA ! IMPLIES END OF FILE. if cf_bwf = yes then flag = eof and ->sopend if cf_relpos = behind then start ! THIS IMPLIES THAT THE TAPE IS POSITIONED AT THE START ! OF THE BLOCK WHICH IS ALREADY IN THE BUFFER. HENCE ! HAVE TO SKIP ONE BLOCK TO AVOID AN INCORRECT ! RE-READ OF THE BLOCK. ! THE ABOVE SITUATION WOULD OCCURR AFTER USING ! THE 'REVERSE READ BLOCK' INTERFACE n = 1 skip blocks (channel ptr,n,flag) if flag > 0 then -> sopend finish ! MUST NOW TRY TO READ IN THE REQUIRED BLOCK blksize = cf_block length ;! LIMIT READ TO MAXIMUM BLOCK LENGTH read block (channel ptr,sod,blksize,flag) if flag # 0 then start if flag = 4 then cf_recsize = 0 and flag = eof ! RECSIZE MUST BE SET TO ZERO FOR READLSQ -> sopend finish ! MANAGED TO READ IN A NEW BLOCK cf_blockptr = sod cf_bwf = no ;! NO RECORDS WRITTEN TO THIS BLOCK cf_relpos = ahead if cf_data format > 3 then blksize = short int(sod) eod = sod + blksize recptr = sod + blkrtl finish else blksize = 0 ;! SEE NOTE UNDER R(1): ! NOW SWITCH TO DATA format SPECIFIC CODE -> r(cf_data format) r(1): ! UNDEFINED - JUST HAVE TO SET LENGTH IN 'RECLEN' ! NOTE THAT IN ORDER TO BE ABLE TO MAKE USE OF THE ! DATA IN THE BUFFER FOR SINGLE RECORD BACKSPACING IN ! UNDEFINED format FILES, THE LENGTH OF THE PREVIOUS RECORD ! MUST BE REMEMBERED. THIS IS WHY 'BLKSIZE' IS SET TO ZERO ! IF A BLOCK HAS NOT BEEN READ IN BECAUSE IN THE CASE OF ! 'U' format FILES, THIS CAN ONLY HAPPEN IF THE ! PREVIOUS OPERATION WAS A SINGLE BACKSPACE: OTHERWISE ! A BLOCK IS ALWAYS READ IN. THUS THE CHECK ON BLOCKSIZE ! BEING GREATER THAN ZERO PRESERVES THE VALUE IN RECLEN ! AND THE BUFFER CONTENTS ARE PASSED ON AGAIN WITH THE ! CORRECT LENGTH TO THE MAGIO CALLER. if blksize > 0 then reclen = blksize cf_recsize = reclen recptr = eod ! 'CF_AREC' REMAINS UNCHANGED -> srend r(2): ! FIXED UNBLOCKED r(3): ! FIXED BLOCKED - JUST MOVE RECORD ADDRESS ALONG BUFFER ! RECLEN REMAINS CONSTANT cf_arec = recptr recptr = recptr + reclen -> srend r(4): ! VARIABLE UNBLOCKED r(5): ! VARAIBLE BLOCKED ! EACH RECORD HAS A RECORD DESCRIPTOR WORD (RDW) ! PRECEDING IT, THE FIRST TWO BYTES OF WHICH CONTAINS THE RECORD ! LENGTH. x = shortint(recptr) if x = 0 then start ! NULL RECORD OR PADDING FOR SHORT BLOCK. BOTH IMPLY ! END OF DATA IN THIS BLOCK. eod = recptr ;! FORCE END OF BLOCK -> op(2) finish else reclen = x cf_arec = recptr + recrtl ;!( REAL DATA STARTS cf_recsize = reclen - recrtl ;!( BEYOND RDW recptr = recptr + reclen -> srend r(6): ! VARIABLE SPANNED UNBLOCKED ? r(7): ! VARIABLE BLOCKED SPANNED -> s((byteinteger(recptr + 2))&x'03') ! s(0): ! THIS SEGMENT IS A COMPLETE RECORD ON ITS OWN AND ! CAN BE TREATED AS AN ORDINARY VARIABLE RECORD -> r(5) s(2): ! LAST SEGMENT OF A MULTISEGMENT RECORD s(3): ! INTERMEDIATE SEGMENT OF A MULTISEGMENT RECORD ! SHOULD NEVER GET THIS CONDITION HERE, HENCE FAULT flag = tsfo + 6 ;! INCORRECT RECPRD format -> srend s(1): ! FIRST SEGMENT OF A MULTI SEGMENT RECORD WHICH, BY ! DEFINITION MUST BE THE LAST SEGMENT IN THIS BLOCK. NOW ! HAVE TO READ IN NEW BLOCKS UNTIL THE END OF THIS RECORD ! IS FOUND. ! FIRST CHECK TO SEE IF THIS SEGMENT IS AT THE BEGINING OF ! THE BUFFER OR NOT. IF NOT, THEN MOVE IT TO THE START ! POSITION, REMEMBERING TO ALLOW SPACE AT THE BEGINING ! FOR WHERE THE BDW WOULD BE INSERTED SHOULD ! THE BUFFER SUBSEQUENTLY HAVE TO BE WRITTEN OUT. ! reclen = shortint(recptr) if recptr > sod + blkrtl then start ! SEGMENT NOT AT START OF DATA, MUST MOVE IT BACK move(reclen,recptr,sod + blkrtl) recptr = sod + blkrtl eod = recptr + reclen finish ;! OF MOVING RECORD TO START OF DATA ! MUST NOW READ IN SUCCESSIVE BLOCKS UNTIL THE END OF ! THE CURRENT RECORD IS FOUND. EACH BLOCK WITH ITS RED TAPE ! IS READ INTO THE BUFFER AT A POSITION AT THE END OF THE ! RECORD SO FAR RECONSTRUCTED. THE RELEVANT INFORMATION IS ! EXTRACTED FROM THE RED TAPE AND THEN THE DATA PORTION OF ! SEGMENT (PLUS ANY FURTHER DATA IN THE BLOCK) IS MOVED ! DOWN THE BUFFER TO BE ADJACENT TO THE EXISTING PART OF ! THE RECORD. cycle ! READ BLOCK INTO BUFFER STARTING AT 'EOD' blksize = cf_block length ;! LIMIT READ TO MAX BLOCK LENGTH if reclen + blksize > cc_buffer length then -> srend ! THIS LAST CHECK PREVENTS RUNNING OFF THE END OF THE ! BUFFER WITH AN UNEXPECTEDLY LONG SPANNED RECORD. ! THE CHECKS AT SOPEND SHOULD FAULT 'RECORD TOO LONG' read block(channel ptr,eod,blksize,flag) if flag # 0 then start if flag = 4 then flag = tsfo + 6 ! 6 => INCORRECT RECORD format -> srend finish blksize = shortint(eod) ! NOW GET LENGTH OF DATA PORTION OF SEGMENT seglen = short int(eod + blkrtl) - recrtl spanbits = (byteinteger(eod + 6))&x'03' ! MOVE CURRENT SEGMENT PLUS ANY OTHER DATA move(blksize - (blkrtl + recrtl),eod + blkrtl + recrtl,eod) cf_blockptr = eod eod = eod + blksize - (blkrtl + recrtl) reclen = reclen + seglen if reclen > cf_maxrec then flag = tsfo + 6 and -> srend {!?4} if tcx(channel ptr)_rtrace = yes then start {!?4} select output(tcx(channel ptr)_rtchan) {!?4} printstring(" *") ; mbc(cf) {!?4} select output(defop) {!?4} finish if spanbits = 2 then exit ;! LAST SEGMENT FOUND repeat ! MUST NOW INSERT A RDW FOR THE RECONSTRUCTED RECORD IN CASE ! THIS CURRENT READ IS FOLLOWED BY 'BACKSPACE - READ' SEQUENCE ! WHEN THE READ CODE WILL EXPECT TO FIND THE TRUE LENGTH OF ! RECORD IN THE RDW. REMEMBER RECLEN SET TO 0 AFTER BACKSPACE plant integer (reclen << 16,recptr) ;! INSERT RDW cf_arec = recptr + recrtl ;! ( REAL USER DATA STARTS cf_recsize = reclen - recrtl ;! ( BEYOND THE RDW recptr = recptr + reclen -> srend srend: if reclen > cf_maxrec then flag = tsfo + 18 ;! RECORD TOO LONG -> sopend op(3): ! WRITE WRITE WRITE WRITE WRITE WRITE WRITE WRITE ! THE HLL I/O ROUTINES WILL HAVE JUST PUT A RECORD INTO THE ! BUFFER AT THE ADDRESS 'CF_AREC_, THIS ADDRESS HAVING BEEN SET UP ! ON THE PREVIOUS CALL ON THIS CODE. THE LENGTH OF THE DATA ! WILL BE IN 'CF_RECSIZE'. ! BUFFER CONTROL ! ! BUFFER CONTROL IS MANAGED BY THE FOLLOWING FIELDS IN THE FILE ! DESCRIPTOR FOR THE CURRENT FILE ('CF') ! SOD AS FOR READ ! EOD END OF DATA. ON ENTRY THE CONTENTS ARE UNDEFINED. ON ! EXIT THIS POINTS TO THE FIRST FREE BYTE AFTER THE END ! OF THE CURRENTLY WRITTEN RECORD OR TO SOD+BLKRTL ! EMPTYING THE BUFFER. ! RECPTR ON ENTRY, THIS POINTS TO THE POSITION OF THE ! RECORD TO BE MANIPULATED BY THE CURRENT CALL ON THIS ! CODE. ON EXIT THIS POINTS TO THE FIRST FREE BYTE ! AFTER THE RECORD JUST 'WRITTEN' ! RECLEN ON ENTRY, UNDEFINED. ON EXIT THIS CONTAINS THE LENGTH ! OF THE RECORD JUST 'WRITTEN'. ! AREC ON ENTRY, UNDEFINED. ON EXIT THIS POINTS TO THE ! POSITION TO BE OCCUPIED BY THE DATA PORTION OF THE ! NEXT RECORD TO BE WRITTEN INTO THE BUFFER ! RECSIZE ON ENTRY THIS CONTAINS THE LENGTH OF THE RECORD JUST ! WRITTEN INTO THE BUFFER. ON EXIT, UNDEFINED cf_bwf = yes ;! MARK BUFFER AS WRITTEN TO reclen = cf_recsize + recrtl eod = recptr + reclen -> w(cf_data format) w(1): ! UNDEFINED. THESE RECORDS ALWAYS OCCUPY THE BEGINING ! OF THE BUFFER AREA AND ARE ALWAYS WRITTEN FROM THERE. ! HENCE THER IS NO NEED TO ALTER THE 'RECPTR', ONLY THE ! 'RECLEN' AND THEN WRITE OUT THE BLOCK. THERE IS OF COURSE ! NO BDW OR RDW. w(2): ! FIXED UNBLOCKED - WILL TREAT JUST THE SAME AS 'U' process eob if flag > 0 then ->swend ! EOD SET BY 'PROCESS EOB' ! RECPTR AND AREC STAY UNCHANGED POINTING AT SOD -> swend ! w(3): ! FIXED BLOCKED if eod - sod >= cf_block length then process eob if flag > 0 then -> swend ! EOD WILL BE RESET BY 'PROCESS EOB' AS NECESSARY recptr = eod cf_arec = recptr -> swend w(4): ! VARIABLE UNBLOCKED plant integer(reclen<<16,sod+4) ;! SET RDW ! BLOCK DESCRIPTOR WORD (BDW) SET BY 'PROCESS EOB' process eob if flag > 0 then -> swend ! EOD RESET BY 'PROCESS EOB' ! AREC AND RECPTR REMAIN UNCHANGED -> swend w(5): ! VARIABLE BLOCKED plant integer(reclen<<16,recptr) ;! SET RDW blksize = eod - sod if blksize >= cf_block length - (cf_minrec+recrtl) then start ! HAVE SUFFICIENT DATA TO FILL BLOCK OR THE NEXT ! WRITE IS SURE TO OVERFLOW THE CURRENT BLOCK if blksize > cf_block length then start ! CURRENT CONTENTS OF BUFFER LONGER THAN ! 'BLOCK LENGTH'. MUST SHORTEN CONTENTS TO END OF ! PREVIOUS RECORD. ! AT THIS POINT THE RECORD JUST ADDED TO THE ! BUFFER IS COMPLETELY DEFINED BY 'RECPTR' AND 'RECLEN'. ! HENCE IT IS SAFE TO DO AN APPARENT TRUNCATION OF ! THE BUFFER FOR THE BENEFIT OF 'PROCESS EOB' AND PICK ! UP THE "ABANDONED" RECORD LATER. eod = eod - reclen shift len = reclen finish else shift len = 0 ! BDW SET BY 'PROCESS EOB' process eob if flag > 0 then -> swend if shift len > 0 then start ! AFTER SHORTENING CONTENTS OF BUFFER, MUST MOVE ! DANGLING DATA TO START OF DATA POSITION BUT ! ALLOWING FOR BDW. move(shift len,recptr,sod + blkrtl) cf_bwf = yes ;! VALID DATA STILL IN BUFFER finish eod = sod + blkrtl + shift len finish recptr = eod cf_arec = recptr + recrtl ! ????????? OF DOUBTFULL USE RECLEN = 0 -> swend w(6): ! VARIABLE SPANNED UNBLOCKED w(7): ! VARIABLE SPANNED BLOCKED if cf_data format = 6 then limit = recrtl + blkrtl c else limit = cf_block length - blkrtl - 1 ;! ??????? if eod - sod > limit then start ! HAVE SUFFICIENT DATA IN BUFFER TO WRITE OUT A BLOCK. BEING ! SPANNED RECORDS, MAY HAVE ENOUGH DATA FOR SEVERAL BLOCKS ptr = sod blockmod = 0 ;! INDICATES FIRST BLOCK SPANNED BY RECORD while eod - ptr > limit cycle if eod - ptr < cf_block length c then blksize = eod - ptr c else blksize = cf_block length plant integer(blksize<<16,ptr) ;! SET BDW ! NOW WORK OUT SPANNING BITS ! THE POSSIBLE SPANNING BIT SETTINGS FOR EACH ! SEGMENT ARE AS FOLLOWS ! 0 ONLY SEGMENT ) FIRST ! 1 FIRST ) BLOCK ! 2 LAST ( SECOND AND ! 3 INTERMEDIATE ( SUBSEQUENT BLOCKS ! IF END OF RECORD FALLS WITHIN THIS BLOCK, THE ! BOTTOM BIT OF 'SPANBITS' MUST BE ZERO. OTHERWISE ! MORE OF THE SPANNED RECORD IS TO FOLLOW IN ! FURTHER SEGMENTS AND THE BOTTOM BIT MUST BE SET. if ptr + blksize = eod then spanbits = blockmod c else spanbits = blockmod + 1 seglen = ptr + blksize - recptr plant integer((seglen<<16)!(spanbits<<8),recptr) if blksize < 18 then start ! MUST PAD TO 18 BYTES WITH ZEROS fill(18-blksize,eod,0) blksize = 18 finish write block (channel ptr,ptr,blksize,flag) if flag # 0 then -> swend blockmod = 2 ! MOVE 'PTR' UP BUFFER BUT ALLOW SPACE ! FOR BDW AND RDW FOR NEXT BLOCK ptr = ptr + blksize - (blkrtl + recrtl) recptr = ptr + blkrtl repeat if recptr < eod - recrtl then start !VBS FILE WITH A PART RECORD LEFT OVER IN THE BUFFER, IE. ! A LAST SEGMENT ! MUST MOVE THIS TO THE START OF DATA (+ BLKRTL FOR BDW) len = eod - ptr - (blkrtl + recrtl) move(len,ptr + blkrtl + recrtl,sod + blkrtl + recrtl) plant integer(((len + recrtl)<<16)!x'0200',sod + blkrtl) eod = sod + len + blkrtl + recrtl finish else start ! EMPTY BUFFER eod = sod + blkrtl finish finish else start ! NOT ENOUGH DATA TO FILL BLOCK - SET RDW FOR THIS ! RECORD plant integer(reclen<<16,recptr) finish ;! OF BIT FOR WRITING OUT BLOCKS recptr = eod cf_arec = recptr + recrtl ! ??????? OF DOUBTFULL USE RECLEN = 0 -> swend ! swend: ! END OF SWITCH FOR WRITING -> sopend op(4): ! REWIND rewind file -> sopend op(5): ! BACKSPACE if cf_blocking = 1 then start ! UNBLOCKED RECORDS, U,F OR V if recptr > sod + blkrtl then start ! VALID DATA IN BUFFER AND NOT GOING TO BACKSPACE OFF ! THE BEGINING OF THE BUFFER. SINCE ALL UNBLOCKED ! RECORDS START AT THE 'START OF DATA' POSITION ! ONLY NEED TO POINT RECPTR AT SOD recptr = sod + blkrtl finish else start ! BACKSPACING OFF THE START OF DATA. THIS ! IMPLIES SECOND OR SUBSEQUENT BACKSPACE AFTER A READ ! OR ANY BACKSPACE AFTER A WRITE. ! SINCE ALL UNBLOCKED WRITES ARE ACTED UPON ! IMMEDIATELY, NO NEED TO CALL 'PROCESS EOB'. eod = sod + blkrtl ;! NO CHANGE AFTER A WRITE ! BUT WILL FORCE A READ SHOULD ! A READ FOLLOW. ! NOW MUST BACK UP THE TAPE THE CORRECT NUMBER OF BLOCKS ! IF THE 'RELPOS' = AHEAD, TWO BLOCKS ARE IMPLIED if cf_relpos = ahead then n = -2 else n = -1 skip blocks (channel ptr,n,flag) cf_relpos = at finish -> bend finish ! if cf_blocking = 2 then start ! BLOCKED RECORDS IE. FB OR VB if recptr <= sod + blkrtl then start ! BACKSPACING OFF START OF DATA BLOCK - MUST CHECK ! END OF BLOCK CONDITION ! AT THIS POINT MUST WORK OUT WHETHER TO ! BACK UP THE TAPE ONE OR TWO BLOCKS, AND THIS DECISION ! MUST BE MADE BEFORE CALLING 'PROCESS EOB' WHICH CHANGES ! THE FLAGS AND POINTERS. NORMALLY, AFTER 'PROCESS EOB' ! HAS BEEN CALLED, THE TAPE WOULD BE BACKED UP TWO ! BLOCKS. ONLY IN THE PARTICULAR SITUATIONS WHERE ! A PRECEDING WRITE CAUSED A BLOCK TO BE WRITTEN ! AND THE BUFFER EMPTIED OR AFTER A CALL ON 'ENDFILE' ! WOULD THE TAPE BE REVERSED ONLY ONE BLOCK. THIS ! CONDITIUON IS INDICATED BY BOTH 'BWF' BEING 'NO' ! AND RELPOS NOT BEING AHEAD. ! if cf_relpos = ahead or cf_bwf = yes then n = -2 c else n = -1 process eob if flag > 0 then -> bend ! NOW MUST BACK UP TAPE THE REQUIRED NO. OF BLOCKS skip blocks (channel ptr,n,flag) if flag # 0 then -> bend ! NOW TRY TO READ THE 'PREVIOUS' BLOCK blksize = cf_block length read block (channel ptr,sod,blksize,flag) if flag # 0 then -> bend cf_relpos = ahead cf_bwf = no if cf_data format = 5 then blksize = short int(sod) eod = sod + blksize recptr = eod finish ;! OF READING IN 'PRECEDING' BLOCK if reclen = 0 then start ! A VB FILE - THE LAST OPER'N WAS A BACKSPACE OR WRITE ! HAVE TO FIND THE LENGTH OF THE 'PRECEDING' RECORD ptr = sod + blkrtl while ptr < recptr cycle x = short int(ptr) if x = 0 then exit len = x ptr = ptr + len repeat reclen = len finish ;! OF FINDING LENGTH OF THE 'PRECEDING RECORD recptr = recptr - reclen if cf_data format = 5 then reclen = 0 -> bend finish ;! OF BACKSPACE FOR BLOCKED RECORDS ! CAN ONLY REACH THIS POINT IF CF_BLOCKING > 2 ! BACKSPACE FOR SPANNED RECORDS if reclen = 0 then reclen = preceding length if reclen = 0 then start ! BACKSPACING OFF THE START OF THE DATA IN THE BUFFER ! NOTE THAT EVEN FOR SPANNED RECORDS, AT THIS ! POINT THERE CANNOT BE MORE THAN A BLOCK'S WORTH ! OF DATA IN THE BUFFER BECAUSE OF THE WAY IN WHICH ! THE 'WRITE' CODE ALWAYS WRITES OUT BLOCKS WHEN ! WHEN THERE IS A BLOCK'S WORTH AVAILABLE. ! SINCE THE 'PREPARE FOR WRITE' CODE ALSO TAKES ! CARE OF AWKWARD BACKSPACE-WRITE SITUATIONS, THE ! 'PROCESS EOB' ROUTINE CAN BE USED HERE. process eob if flag > 0 then -> bend if cf_relpos = ahead then start ;! ??????? ! ??????? %IF CF_BCF = YES %THEN %START ;! ???????? ! CONTENTS OF THE BUFFER CORRESPOND TO EARLIER BLOCKS ! ON THE TAPE if recptr < cf_blockptr then start ! FIRST RECORD IN BUFFER SPANNED TWO OR MORE BLOCKS n = -1 skip blocks(channel ptr,n,flag) if flag > 0 then -> bend finish find preceding tape record ;! SHOULD FIND THE RECORD ! CORRESPONDING TO THE ! THE LAST RECORD PROCESSED if flag > 0 then -> bend x = preceding length ;! TRY TO LOCATE PRECEDING RECORD ! IN THE CURRENT BUFFER CONTENTS if x = 0 then find preceding tape record c else recptr = recptr - x finish else find preceding tape record finish else recptr = recptr - reclen reclen = 0 bend: ! END OF SWITCH CONTROLLING BACKSPACING cf_arec = recptr + recrtl -> sopend op(6): ! ENDFILE endfile (no) -> sopend op(7): ! CLOSE FILE op(8): ! CLOSE FILE AND TAPE close file flag = 0 if operation = 8 then close tape(channel ptr,rc) -> sopend sopend: cf_recptr = recptr cf_reclen = reclen cf_eod = eod if flag < 5 then flag = tsfault(flag) unless flag = 0 or flag = eof then start ! FAILURE OF SOME SORT select output(0) newline printstring("MAG TAPE PROCESSING FAILURE CHAN VOLUME ") printstring("LABEL FILENAME BLOCK") newline if flag > tsfo then start printstring(errmess(flag - tsfo)) spaces(2) finish else start printstring("SYSTEM ERROR ") write(flag,4) ; spaces(11) finish write(cf_dsnum,2) spaces(2) ; printstring(cf_volume) spaces(6-length(cf_volume)) write(cf_rel file num,4) spaces(2) ; printstring(cf_dsn) spaces(17-length(cf_dsn)) ! THE CHECK ON THE FOLLOWING LINE IS NECESSARY ! BECAUSE IF THE TAPE HAS NOT BEEN OPENED, THE TAPE ! CHANNEL WILL NOT HAVE BEEN MAPPED AND THE ACCESS ! TO CC_BLOCK WOULD CAUSE AN ADDRESS ERROR. ! MUST RECODE THIS NEXT BIT ?????? if flag = tsfo + 6 or flag = tsfo + 11 c or flag = tsfo + 12 or flag = tsfo + 10 c then write(cc_blockno,5) ! SUBSYSTEM WILL ALWAYS CLOSE FILE AND TAPE AFTER ERROR monitor ; stop finish {!?4} if tcx(channel ptr)_rtrace = yes then start {!?4} select output (tcx(channel ptr)_rtchan) {!?4} spaces(2) ; mbc(cf) {!?4} select output (defop) {!?4} finish return ! >>>>>>>>>>>>>>>>>>>>>>>>>>>> PRECEDING LENGTH <<<<<<<<<<<<<<<<< ! integerfn preceding length ! THIS ROUTINE SCANS THAT PART OF THE CURRENT BUFFER WHICH PRECEDES ! 'RECPTR' LOOKING FOR THE IMMEDIATLY PRECEDING RECORD START (IN ! THE CASE OF V OR VB FILES) OR THE IMMEDIATLY PRECEDING START ! OF RECORD SEGMENT (IN THE CASE OF SPANNED FILES) ! IT RETURNS THE LENGTH OF THE RECORD OR SEGMENT AS ITS RESULT. integer ptr,len,x byteinteger mask len = 0 if cf_blocking > 2 then mask = x'02' else mask = 0 ptr = sod + blkrtl while ptr < recptr cycle x = shortint(ptr) exit if x = 0 ;! NULL RECORD OR PADDING if byteinteger(ptr + 2) & mask = 0 then len = x ptr = ptr + x repeat result = len end ;! OF PRECEDING LENGTH ! >>>>>>>>>>>>>>>>>>>> FIND PRECEDING TAPE RECORD <<<<<<<<<<<<<<< routine find preceding tape record ! THIS ROUTINE READS BACKWARDS DOWN THE TAPE FROM ITS CURRENT ! POSITION, REVERSE READING BLOCKS INTO THE BUFFER AND ! SCANNING THEM FOR A RECORD START SEGMENT. ON EXIT, THE ! START SEGMENT NEAREST TO THE TAPE POSITION ON ENTRY WILL ! BE INDICATED BY 'RECPTR'. IN SUMMARY, ON EXIT: ! * LATEST START SEGMENT POINTED AT BY 'RECPTR' ! * 'BCF' SET TO NO ! * TAPE POSITIONED BEFORE CORRESPONDING BLOCK ON TAPE recptr = eod ;! TO TRIGGER FIRST ENTRY INTO FOLLOWING CYCLE cf_blockptr = sod while recptr >= eod cycle blksize = cf_block length reverse read block (channel ptr,sod,blksize,flag) if flag > 0 then return eod = sod + shortint(sod) recptr = eod ;! FOR PURPOSES OF CALLING 'PRECEDING LENGTH' recptr = recptr - preceding length ! THE PRECEDING STATEMENT MAKES SENSE IF IT IS REMEMBERED ! THAT THE SEGMENT BEING SEARCHED FOR MUST BE, BY DEFINITION, ! THE LAST SEGMENT IN A BLOCK. repeat cf_relpos = behind return end ;! OF FIND PRECEDING TAPE RECORD ! >>>>>>>>>>>>>>>>>>>>>>>>> PROCESS EOB <<<<<<<<<<<<<<<<<<<<<<<<<<< routine process eob ! THIS ROUTINE IS CALLED WHEN AN END OF BLOCK CONDITION IS ! REACHED IN A CONTEXT WHERE THE BLOCK MAY NEED WRITING OUT TO ! THE TAPE. THE MAIN PURPOSE OF THIS ROUTINE IS TO CHECK THE ! THE RELATIVE POSITION OF THE TAPE WRT THE BIFFER CONTENTS, ! I.E. CHECK CF_RELPOS AND THEN WRITE OUT THE BLOCK integer n,x if cf_bwf = no then return ;! BUFFER NOT WRITTEN TO ! ALSO CHECK TO SEE IF ANY DATA IN BUFFER unless eod > sod + blkrtl + recrtl then return if cf_relpos = ahead then start ! THE CURRENT BUFFER CONTENTS CORRESPOND TO THE LAST BLOCK ! ON THE TAPE TO BE PROCESSED. THEREFORE, HAVE TO BACK ! UP THE TAPE ONE BLOCK BEFORE WRITING THE BLOCK. ! THE ABOVE SITUATION COULD ARISE WHEN CALLING ! A SEQUENCE SUCH AS READ-WRITE WITHOUT A CALL ON BACKSPACE n = -1 skip blocks (channel ptr,n,flag) if flag = 4 and cc_block no = 0 then flag = 0 ! THE LAST LINE ABOVE IS A CHECK TO GUARD AGAINST A ! FAILURE IN THE EVENT OF A WRITE AFTER REWIND SEQUENCE if flag > 0 then return finish x = eod - sod ;! GET SIZE OF BLOCK TO BE WRITTEN if cf_rectype = 2 then start plant integer(x<<16,sod) ;! BDW FOR VAR if x < 18 then start ! BLOCK SHORTER THAN 18 BYTES, MUST PAD WITH BINARY ZEROS fill(18-x,eod,0) x = 18 finish finish write block (channel ptr,sod,x,flag) if flag > 0 then return cf_relpos = behind ;! THESE THREE VARIABLES MUST BE SET IN ! ??????CF_BWF = NO ;! A MUTUALLY CONSISTANT MANNER. THE eod = sod + blkrtl ;! CURRENT SETTINGS LEAVE THE BUFFER EMPTY ! CAN'T RESET RECPTR HERE - MUST BE DONE IN CONTEXT OF THE ! CALL ON 'PROCESS EOB'. return end ;! OF PROCESS EOB ! >>>>>>>>>>>>>>>>>>>> OPEN TAPE FILE >>>>>>>>>>>>>>>>>>>>>>>>>>>> routine open tape file routinespec check tape (integer writing) routinespec find file routinespec validate for read routinespec validate for write routinespec unwind file routinespec create buffer routinespec create labels integer rc,len string (80) buffer1,buffer2 ;! BUFFERS FOR HANDLING LABELS record (ibm file1 format) hdr1 record (ibm file2 format) hdr2 switch type (9:11) ;! FOR THE VARIOUS TYPES OF OPEN rc = 0 if cf_dsn = nulldsn then start ! FILENAME NOT SUPPLIED - NOT A VALID THING TO DO IF ! WRITING A NEW FILE WITH STANDARD LABEL PROCESSING if operation = 10 and cf_flags & x'08' > 0 c then flag = tsfo + 4 and return finish else start cf_dsn = cf_dsn." " while length(cf_dsn) < 17 finish ! 'FLAG' SET TO ZERO IN 'MAGIO' -> type(operation) ;! PERFORM APPROPRIATE TYPE OF OPEN type(9): ! OPEN FOR READING check tape (operation) find file if flag = tsfo + 100 then flag = tsfo + 16 validate for read create buffer init desc fields {!?4} if flag = 0 and tcx(channel ptr)_rtrace = yes then start {!?4} select output(tcx(channel ptr)_rtchan) {!?4} print chan(channel ptr) {!?4} print file chars(cf) {!?4} print mbc heading ("OPENED FOR READ") {!?4} select output(defop) {!?4} finish return type(10): ! OPEN FOR WRITING NEW FILE if cf_lpmode = ul and cf_rel file num = 1 then start ! NO WRITE ACCESS TO FIRST PHYSICAL FILE flag = tsfo + 19 return finish check tape (operation) find file if cf_lpmode = sl then start ! THE ONLY ACCEPTABLE RESULT CODES FROM FIND FILE IN THIS CONTEXT ! ARE: ! 0 FILE FOUND OK. AND THE INTENTION IS TO OVERWRITE IT WITH A ! FILE OF THE SAME NAME ! TSFO+100 A TAPE MARK WAS FOUND WHERE A "HDR1" WAS EXPECTED ! IMPLYING THAT THE FILE TO BE WRITTEN IS TO BE ADDED ! TO THE "END" OF THE TAPE ! TSFO+4 AS FOR 0 BUT OVERWRITTING WITH A FILE OF A DIFFERENT NAME. ! if flag = 0 or flag = tsfo + 4 then start ! WILL HAVE READ A "HDR1" TO HAVE HAD THESE RESULTS FROM "FIND ! FILE". HAVE TO SKIP BACK OVER EXISTING "HDR1". n = -1 flag = 0 skip blocks (channel ptr,n,flag) finish else start if flag = tsfo + 100 then start ! NO HDR1 AT THE REQUIRED POSITION ON THE TAPE IMPLYING THAT ! THE CURRENT FILE IS TO BE ADDED TO THE "END" OF THE TAPE. ! SINCE THE 'FIND FILE' ROUTINE IS ALWAYS TRYING TO ! READ A 'HDR1' LABEL, IT FOLLOWS THAT IF IT FAILS, THE TAPE ! ! WILL ALWAYS BE IN THE CORRECT POSITION TO WRITE A NEW ! 'HDR1'. SO JUST SET FLAG TO ZERO. flag = 0 finish finish validate for write create labels create buffer write labels if flag = 0 then write tape mark (channel ptr,flag) finish else start ! UNLABELLED PROCESSING validate for write create buffer finish init desc fields if flag = 0 then cc_blockno = 0 {!?4} if flag = 0 and tcx(channel ptr)_rtrace = yes then start {!?4} select output(tcx(channel ptr)_rtchan) {!?4} print chan(channel ptr) {!?4} print file chars(cf) {!?4} print mbc heading ("OPENED FOR NEW WRITE") {!?4} select output(defop) {!?4} finish return type(11): ! OPEN FOR MOD WRITING, IE. APPENDING check tape (operation) find file ;! IS THIS THE RIGHT TYPE OF LOCATE? ;! FOR SEVERAL MODS TO A LARGE FILE, THIS ;! WILL INVOLVE A LOT OF TAPE MOVBEMENT if flag = 100 then start ! HAVE TRIED TO FIND THE FILE TO WHICH TO 'MOD' ! BUT HAVE FOUND END OF TAPE INSTEAD. SO JUST CREATE ! A NEW FILE AS THOUGH WRITING AA NEW FILE. flag = 0 -> type(10) finish validate for read unwind file validate for write create buffer init desc fields {!?4} if flag = 0 and tcx(channel ptr)_rtrace = yes then start {!?4} select output(tcx(channel ptr)_rtchan) {!?4} print chan(channel ptr) {!?4} print file chars(cf) {!?4} print mbc heading ("OPENED FOR MOD WRITE") {!?4} select output(defop) {!?4} finish return ;! FROM OPEN MOD !>>>>>>>>>>>>>>>>>>>>> CHECK TAPE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< routine check tape (integer open type) integer mode,len constinteger read open = 9 ;! OPENING FOR READ constinteger write open = 10 ;! OPENING FOR WRITEING NEW FILE constinteger append open = 11 ;! OPENING FOR APPENDING string (100) buffer ;! ALLOW FOR FUNNY LABELS record (ibm vol1 format) vol label record (ibm file1 format) hdr1 if flag > 0 then return cf_volume = " ".cf_volume while length(cf_volume) < 6 if cf_flags & x'40' > 0 then mode = 2 else mode = 1 open tape(cf_volume,mode,channel ptr,flag) if flag = 73 then flag = tsfo + 22 and return if tc(channel ptr)_et flag = yes then flag = tsfo + 10 if flag > 0 then return cf_channel = channel ptr cc == tc(channel ptr) cc_xfers = 0 {!?4} if flag = -1 then flag = 0 and -> ctend if flag = -1 then flag = 0 and return rewind tape(channel ptr,0,flag) if flag > 0 then return if cf_lpmode = ul then start cc_type = 2 ;! UNLEBELLED cc_format = 2 ;! UNLABELLED cc_init fsn = "" {!?4} if open type = read open then -> ctend if open type = read open then return finish else start cc_type = 1 ;! IBM cc_format = 1 ;! STANDARD LABELLED finish ! AT THIS STAGE THE TAPE IS BEING ALLOCATED FOR THE FIRST TIME ! AND IS BEING PROCESSED IN SL MODE OR WRITING IN UL MODE ! IN EITHER CASE MUST CHECK PRESENCE OF VOLUME LABEL len = 100 ;! 100 ALLOWS FOR FUNNY LABELS read block (channel ptr,addr(buffer)+1,len,flag) ;! READ VOL1 if flag # 0 or len # 80 then start ct: cc_type = 0 ;! UNKNOWN TYPE OF TAPE cc_format = 0 ;! UNRECOGNISABLE format close tape (channel ptr,flag) flag = tsfo + 1 return finish length(buffer) = 80 decode label(buffer,ibm vol1 field,addr(vol label)) if vol label_id # "VOL1" then -> ct if cf_lpmode = ul then start ! WRITING TO TAPE IN UNLABELLED MODE. ! IF WRITING TO FIRST PHYSICAL FILE, REJECT REQUEST if cf_rel file num =1 then flag = tsfo + 19 {!?4} -> ctend return finish else start ! STANDARD LABEL PROCESSING if open type = write open and cf_rel file num = 1 then start ! ABOUT TO WRITE FIRST FILE ON TAPE cc_init fsn = "INIT" ;! MARK FOR CHECKING IN FIND FILE finish else start ! CHECK FIRST HDR1 LABEL len = 100 read block (channel ptr,addr(buffer)+1,len,flag) ;! READ HDR1 if flag = 2 then return if flag = 0 and len = 80 then start ! NOW DECODE HDR1 FOR FIRST FILE IN ORDER TO GET THE FILE ! SEQUENCE NUMBER OF THE FIRST FILE WHICH NEED ! NOT NECESSARILY BE 1. length(buffer) = 80 decode label (buffer,ibm file1 field,addr(hdr1)) if hdr1_id # "HDR1" then -> ct cc_init fsn = hdr1_file seq num finish else start ! THIS MEANS THAT THERE IS NO VALID HDR1 LABEL ! IMMEDIATLY AFTER THE VOL1 LABEL. ! THIS MUST MEAN THAT THE TAPE IS ! NOT IN A STANDARD format -> ct finish finish cc_xfers = 2 ;! FOR VOL1 & HDR1 finish cc_use = 1 {!?4} ctend: {!?4} if tcx(channel ptr)_before = no then start {!?4} tcx(channel ptr)_before = yes {!?4} reply = answer to ("RECORD TRACE ? ") {!?4} if reply = "YES" then start {!?4} tcx(channel ptr)_rtrace = yes {!?4} prompt("TRACE OUTPUT = ") {!?4} read(tcx(channel ptr)_rtchan) {!?4} finish else tcx(channel ptr)_rtrace = no {!?4} finish end ;! OF CHECK TAPE !>>>>>>>>>>>>>>>>>>>>>>>>>>> FIND FILE <<<<<<<<<<<<<<<<<<<<<<<<<< routine find file constintegerarray errtab(-4:-1) = 9,10,16,9 integer len,loctype if flag > 0 then return ! POSITION TAPE AT HDR1 FOR REQUIRED FILE if cf_lpmode = ul then loctype = 7 else loctype = 9 ! 7 => MOVE TO ABSOLUTE PHYSICAL FILE ! 9 => MOVE TO ABSOLUTE LABELED FILE locate tape (channel ptr,loctype,cf_rel file num,flag) if 0> flag > -5 then flag = tsfo + errtab(flag) if flag > 0 then return if cf_lpmode = ul then return ;! NO CHECKING POSSIBLE ! SHOULD NOW BE IN POSITION TO READ HDR1 AND HDR2 OF REQUIRED FILE len = 80 read block (channel ptr,addr(buffer1)+1,len,flag) ;! READ HDR1 if flag = 4 then flag = tsfo + 100 ;! LOGICAL END OF TAPE if flag > 0 then return if len # 80 then flag = tsfo + 2 and return ! 2 => FILE HEADER ERROR length(buffer1) = 80 decode label (buffer1,ibm file1 field,addr(hdr1)) if hdr1_id # "HDR1" then flag = tsfo + 2 and return if cc_init fsn = "INIT" then start ! GOING TO WRITE TO FIRST FILE ON TAPE, SO JUST SET ! THE SEQUENCE NO. FOR FIRST FILE TO ONE cc_init fsn = "0001" finish else start if cf_rel file num # intof(hdr1_file seq num) - c intof(cc_init fsn) + 1 then flag = tsfo + 3 and return ! 3 => LABEL NUMBER ERROR finish if cf_dsn = nulldsn then start ! NO FILE NAME SUPPLIED VIA DEFINEMT - CAN INSERT FILE NAME ! FROM TAPE LABEL SO LONG AS THIS OPEN IS NOT FOR A NEW FILE. if operation # 10 then cf_dsn = hdr1_file name finish else start if cf_dsn # hdr1_file name then flag = tsfo + 4 and return ! 4 => FILE NAME ERROR finish return end ;! OF FIND FILE !>>>>>>>>>>>>>>>>>>>> VALIDATE FOR READ <<<<<<<<<<<<<<<<<<< routine validate for read integer i,n if flag > 0 then return ! SEEMS THAT WE HAVE THE RIGHT FILE - NOW EXTRACT THE FILE ! ATTRIBUTES FROM THE HDR2 LABEL IF USING STANDARD LABEL PROCESSING if cf_lpmode = sl then start len = 80 read block (channel ptr,addr(buffer2)+1,len,flag) ;! READ HDR2 if flag > 0 then return if len # 80 then flag = tsfo + 2 and return ! 2 => FILE HEADER ERROR length(buffer2) = 80 decode label (buffer2,ibm file2 field,addr(hdr2)) if hdr2_id # "HDR2" then flag = tsfo + 2 and return if hdr2_control chars = "A" then cf_flags = cf_flags ! x'10' if cf_rectype > 3 or operation = 11 then start ! RECTYPE <= 3 MEANS VALID INFO IN RECTYPE, IE. USER IS ! OVERIDING (VIA DEFINEMT) THE FILE LABEL INFO. ! OPERATION = 11 MEANS OPENING FOR MOD WRITE. cf_block length = intof(hdr2_block length) cf_maxrec = intof(hdr2_record length) ! CHECK RECORD LENGTH FOR BENEFIT OF format U FILES ? if cf_maxrec = 0 then cf_maxrec = cf_block length cf_rectype = 255 ; cf_blocking = 255 for i = 1,1,7 cycle if hdr2_record format = recfm char(i) then cf_rectype = i if hdr2_block attribute = recfm char(i) then cf_blocking=i-3 repeat finish n = 1 skip tape marks (channel ptr,n,flag) ;! SKIP TO START OF DATA finish ;! OF GETTING INFO FROM HDR2 LABEL if cf_rectype = 255 or cf_blocking = 255 c then flag = tsfo + 17 and return cf_data format = format map (cf_rectype,cf_blocking) cf_flags = cf_flags ! x'20' ;! SET EBCDIC BIT cc_term flag = yes cf_relpos = at ;! CAN'T PUT IN INIT DESC FIELDS - CF REWIND FILE cc_block no = 0 return end ;! OF VALIDATE FOR READ !>>>>>>>>>>>>>>>>> VALIDATE FOR WRITE <<<<<<<<<<<<<<<<<<<< routine validate for write if flag > 0 then return cf_data format = format map(cf_rectype,cf_blocking) cf_relpos = at ;! CAN'T PUT IN INIT DESC FIELDS - SEE REWIND FILE cc_term flag = no ;! ????? PERHAPS SHOULD LEAVE UNTIL A WRITE return end ;! OF VALIDATE FOR WRITE !>>>>>>>>>>>>>>>>>>>>>>>>> CREATE BUFFER <<<<<<<<<<<<<<<<<<<<<<<<< routine create buffer integer rc if flag > 0 then return cc_buffer length = c cf_maxrec * rsmod(cf_data format) + c cf_block length * bsmod(cf_data format) + c buffsod + rtfudge cc_buffer file = next temp outfile(bfn base.cc_buffer file,cc_buffer length,cc_buffer length, 0,cf_sob,rc) if rc # 0 then flag = tsfo + 5 and return ! 5 => FAILED TO CREATE BUFFER cf_eob = cf_sob + cc_buffer length header == record(cf_sob) header_label1 = hdr1 header_label2 = hdr2 end ;! OF CREATE BUFFER !>>>>>>>>>>>>>>>>>>>>>>>> CREATE LABELS <<<<<<<<<<<<<<<<<<<<<<<<< routine create labels if flag > 0 then return hdr1_id = "HDR1" hdr1_file name = cf_dsn hdr1_file ser num = cc_tsn hdr1_vol seq num = "0001" hdr1_file seq num = strof(cf_rel file num,4) hdr1_generation = blanks(4) hdr1_version = blanks(2) hdr1_create date = " ".ibm date hdr1_expiry date = " 00000" hdr1_security = "0" hdr1_block count = "000000" hdr1_syscode = zeros(13) hdr1_resa = blanks(7) hdr2_id = "HDR2" hdr2_record format = recfm char(cf_rectype) hdr2_block length = strof(cf_block length,5) hdr2_record length = strof(cf_maxrec,5) hdr2_density = "3" hdr2_volume switch = "0" hdr2_job id = blanks(17) hdr2_rec tec = blanks(2) hdr2_control chars = blanks(1) hdr2_control chars = "A" if cf_flags & x'10' > 0 hdr2_resa = blanks(1) hdr2_block attribute = recfm char(cf_blocking + 3) hdr2_resb = blanks(3) hdr2_tape deck id = blanks(5) hdr2_resc = blanks(33) return end ;! OF CREATE LABELS !>>>>>>>>>>>>>>>>>>>>>>> UNWIND FILE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< routine unwind file integer n if flag > 0 then return n = maxint skip blocks (channel ptr,n,flag) if flag # 4 then cc_block no = -1 else c cc_block no = cc_block no + n and flag = 0 return end ;! OF UNWIND FILE end ;! OF OPEN TAPE FILE !>>>>>>>>>>>>>>>>>>>>>>> WRITE LABELS <<<<<<<<<<<<<<<<<<<<<<<<< routine write labels string (80) lab if flag > 0 then return length(lab) = 80 encode label(lab,ibm file1 field,addr(header_label1)) write block(channel ptr,addr(lab)+1,80,flag) if flag > 0 then flag = tsfo + 8 and return ! 8 => FAILED TO WRITE LABEL length(lab)=80 encode label(lab,ibm file2 field,addr(header_label2)) write block(channel ptr,addr(lab)+1,80,flag) if flag > 0 then flag = tsfo + 8 return end ;! OF WRITE LABELS !>>>>>>>>>>>>>>>>>>>>>>>>>>>> ENDFILE >>>>>>>>>>>>>>>>>>>>>>>>>>>>> routine endfile (integer closing) ! THIS ROUTINE EFFECTIVELY TRUNCATES THE FILE ON TAPE JUST AFTER ! THE LAST RECORD TRANSFERED. ANY VALID DATA STILL LEFT ! IN THE BUFFER MUST BE WRITTEN OUT FOLLOWED BY TRAILER LABELS ! AND TWO TAPE MARKS. THE TAPE IS THEN REPOSITIONED JUST AFTER ! THE LAST BLOCK , IE. JUST BEFORE THE TAPE MARK INDICATING ! THE END OF THE FILE. record (ibm file1 format)name trailer1 record (ibm file2 format)name trailer2 integer n,bc eod = recptr ;! TRUNCATE BUFFER, AFTER READING OR BACKSPACING ! RECPTR MARKS THE END OF VALID DATA ! IN AN 'ENDFILE' CONTEXT. if eod - sod > blkrtl + recrtl then start ! RESIDUAL DATA IN BUFFER, MUST WRITE IT OUT. ! THE TEST ABOVE HAS TO COPE WITH THE SITUATION OF ! A REWIND FOLLOWED BY A CLOSE, ESPECIALLY FOR A V FILE. cf_bwf = yes ;! TO TRIGGER 'PROCESS EOB' CORRECTLY process eob if flag > 0 then return ! BUFFER GENUINELY EMPTIED, MUST RESET RECPTR recptr = sod + blkrtl cf_arec = recptr + recrtl finish bc = cc_block no ;! BLOCK COUNT WILL BE ZEROISED BY WRITE TAPE MARK if cf_lpmode = sl then start header == record(cf_sob) trailer1 == header_label1 trailer2 == header_label2 if cc_et flag = no then start trailer1_id = "EOF1" trailer2_id = "EOF2" finish else start trailer1_id = "EOV1" trailer2_id = "EOV2" finish bc = cc_block no ;! BLOCK NO ZEROISED BY WRITE TAPE MARK trailer1_block count = strof(bc,6) write tape mark (channel ptr,flag) ;! MARKS END OF FILE if flag > 0 then return write labels ;! WRITE OUT EOF LABELS if flag > 0 then return finish write tape mark (channel ptr,flag) ;!( MARKS END write tape mark (channel ptr,flag) ;!( OF TAPE. if flag > 0 then return cc_term flag = yes unless closing = yes then start ! FORTRAN TYPE ENDFILE - NOT CLOSING THE FILE ! NOW SKIP BACK TO JUST AFTER THE LAST BLOCK IN THE FILE if cf_lpmode = sl then n = -3 else n = -2 skip tape marks (channel ptr,n,flag) if flag = 0 then cc_block no = bc cf_bwf = no ;! MAKES POTENTIAL FOLLOWING BACKSPACE OK finish return end ;! OF ENDFILE ! >>>>>>>>>>>>>>>>>>>>>>>>>>CLOSE FILE <<<<<<<<<<<<<<<<<<<<<<<<<<<<< routine close file if cc_term flag = no then endfile (yes) disconnect(bfn base.cc_buffer file,flag) destroy (bfn base.cc_buffer file,flag) cf_block xfers = cc_xfers cc_block no = 0 cc_use = 0 {!?4} tcx(channel ptr)_before = no return end ;! OF CLOSE FILE !>>>>>>>>>>>>>>>>>>>>>>> REWIND FILE <<<<<<<<<<<<<<<<<<<<<<<<<<< routine rewind file integer n if flag > 0 then return cc_block no = -1 n = -1 skip tape marks(channel ptr,n,flag) if flag > 0 then return n = 1 skip tape marks(channel ptr,n,flag) if flag <= 0 then cc_block no = 0 ! NOW CLEAR DOWN BUFFER TO A STATE AS THOUGH THE FILE ! HAD JUST BEEN OPENED FOR READ init desc fields cf_relpos = at cc_blockno = 0 return end ;! OF REWIND FILE !>>>>>>>>>>>>>>>>>>>>>> INIT DESC FIELDS <<<<<<<<<<<<<<<<<<<<<<<<<<< routine init desc fields if flag > 0 then return if cf_data format > 3 then start cf_blkrtl = 4 cf_recrtl = 4 finish else start cf_blkrtl = 0 cf_recrtl = 0 finish cf_sod = cf_sob + buffsod cf_recptr = cf_sod + cf_blkrtl if cf_rectype = 1 then cf_reclen = cf_maxrec c else cf_reclen = 0 cf_recsize = cf_maxrec cf_eod = cf_recptr cf_arec = cf_recptr + cf_recrtl cf_bwf = no reclen = cf_reclen recptr = cf_recptr eod = cf_eod return end ;! OF INIT DESC FIELDS end ;! OF MAGIO >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> !*********************************************************************** !* !* Magnetic tape support routines for utility programs !* !* R.R. McLeod ERCC MCMLXXVIII !* R.D. Eager UKC MCMLXXX !* !*********************************************************************** !*********************************************************************** !* !* 16/11/79 - Accept tape claims with '?' as the last character of the !* volume name: this means 'load with optional ring'. !* - Different handling of failures: if flag is 2, then a !* catastrophic failure has occurred (deck powered off, etc): !* if flag is 1, then a hardware fault has occurred - allow !* MAXFAULTCOUNT of these on a channel, then abandon. !* 03/01/80 - Additional routines DENSITYMAG, MODEMAG (DENSITYMT, !* MODEMT) to enable use of 800 bpi tapes, and 1900 !* series compress/expand mode tapes. !* - Channel number now given in diagnostics. !* - Corrected code for '*' and '?' checks on volume name. !* - Additional routine ASKMAG (ASKMT) for requesting a tape, !* whilst retaining control if it not available. !* 08/04/80 - Correction to code of ASKMAG, to return zero flag at BT. !* 07/01/80 - Integration with the HLL tape support routines to !* to coordinate channel allocation and tidying up after !* INT:A etc !* Replacement of DENSITYMAG and MODEMAG with a single !* routine SETMAG: previous implementation not compatible !* with the above integration and the scheme for channel !* allocation. !* Flag of 4 returned when writing beyond EOT. !*********************************************************************** ! SINCE THE LIST OF LOGICAL CHANNELS FOR TAPES IS SHARED BETWEEN HLL ! AND UTILITY ACCESS, THE CHANNEL NUMBERS AS PASSED TO THE UTILITY ! INTERFACES BY THE CALLER (USER) CANNOT BE USED DIRECTLY ! AS SUCH CHANNELS MAY HAVE ALREADY BEEN ALLOCATED FOR HLL ACCESS. ! HENCE A LEVEL OF INDIRECTION IS INTRODUCED VIA THE STRING ARRAY ! 'UCHANNEL' AND ALL CHANNEL NUMBERS PASSED TO THE UTILITY ! INTERFACES ARE TRANSPOSED VIA THIS ARRAY BEFORE BEING USED. !*********************************************************************** !* !* Internal routines !* !*********************************************************************** routine mag fail(string (255) s) selectoutput(0) newlines(2) printstring("*** Error - ".s." - channel ".itos(cuc)." ***") newline monitor stop end ; ! of MAG FAIL routine check channel(integername channel) ! NOTE THAT PARTS OF THE CODE FROM THIS ROUTINE ARE REPEATED ! IN 'ASKMAG' AND 'UNLOADMAG' cuc = channel ;! Save current user channel for possible diags unless min tape channel <= channel <= max tape channel c then mag fail("Invalid channel") channel = uchannel(channel) if channel = 0 then mag fail ("Tape not claimed") end ; ! of CHECK CHANNEL routine recordfault(integer channel,integername flag) if flag = 2 then mag fail("Catastrophic tape failure") ;! OFF-LINE if tc(channel)_err count >= maxfaultcount then start mag fail c ("More than ".itos(maxfaultcount)."consecutive tape failures") finish tc(channel)_err count = tc(channel)_err count + 1 if flag = 1 then flag = 2; ! For return to user I/O ERROR ! ONLY OTHER VALUE OF FLAG = 4 => EOT ON WRITE end ; ! of RECORDFAULT !*********************************************************************** !* !* T H E S U P P O R T R O U T I N E S !* !*********************************************************************** externalroutine set mag (integer channel,density,xcmode, integername flag) integer control check channel(channel) if density = 800 then start ! INSERT '800 BPI' CONTROL BIT tc(channel)_control=tc(channel)_control ! x'80' finish else start if density = 1600 then start ! REMOVE '800 BPI' CONTROL BIT tc(channel)_control = tc(channel)_control & x'7F' finish else mag fail("Invalid density") finish if xcmode = 1900 then start ! INSERT COMPRESS/EXPAND BIT tc(channel)_control = tc(channel)_control ! x'40' finish else start if xcmode = 2900 then start ! REMOVE COMPRESS/EXPAND CONTROL BIT tc(channel)_control = tc(channel)_control & x'BF' finish else mag fail("Invalid mode") finish control = tc(channel)_control rewind tape (channel,control,flag) if flag > 7 then mag fail ("DMAGIO FAILS IN SET MAG") if flag = 4 then flag = 0 ;! ADVISORY FLAG ONLY ????? end ; ! of SETMAG externalroutine askmag(integer channel,string (7) vol, integername flag) integer mode,permits cuc = channel flag = dsfi(uinfs(1),-1,38,0,addr(permits)) if flag # 0 then magfail("User check fails") if permits & tape permit # tape permit c and uinfi(2) & 1 = 1 c then mag fail("Interactive access to tapes not permitted") unless min tape channel <= channel <= max tape channel c then mag fail ("Invalid channel") if uchannel(channel) > 0 then mag fail ("CHANNEL ALREADY IN USE") if length(vol) = 0 then vol = " " ;! PROPER CHECK ON VOL LATER if charno(vol,length(vol)) = '?' then start ; ! Select optional ring mode = 3 length(vol) = length(vol) - 1 finish else start if charno(vol,length(vol)) = '*' then start ; ! Select read/write or read only mode = 2 length(vol) = length(vol) - 1 finish else start mode = 1 finish finish unless 1 <= length(vol) <= 6 then mag fail("Invalid volume label") vol = vol." " while length(vol) < 6 open tape (vol,mode,channel,flag) return if flag > 0 comreg(19) = 1 ;! INDICATES THAT A TAPE HAS BEEN ALLOCATED ! FOR UTILITY USE. uchannel(cuc) = channel ! TC(CHANNEL)_MODE = MODE ! THIS FILLED IN BY OPEN TAPE tc(channel)_err count = 0 tc(channel)_use = 2 flag = 0 ;! FLAG CAN BE < 0 IF TAPE WAS HELD end ; ! of ASKMAG externalroutine openmag(integer chan,string (7) s) integer flag askmag(chan,s,flag) if flag # 0 then mag fail("Failure to claim tape") end ; ! of OPENMAG externalroutine unloadmag(integer channel) integer flag flag = 0 check channel (channel) if tc(channel)_tsn # "" c then close tape (channel,flag) uchannel(cuc) = 0 ;! CUC CORRECTLY SET FOR THIS BY CHECK CHANNEL if flag > 0 then mag fail("FAILED IN UNLOAD MAG") return end ; ! of UNLOADMAG externalroutine clear mag clear tapes(2) return end ;! OF CLEAR MAG externalroutine readmag(integer channel,address,integername len,flag) integer n check channel(channel) if len <= 0 and address > 0 then mag fail("Invalid length for read") ! NOTE THAT LEN = 0 AND ADDRESS = 0 => READ CHECK read block (channel,address,len,flag) if flag > 0 then start if flag > 7 then mag fail("DMAG IO fails in READMAG") if flag = 4 then start ; ! Hit tape mark n = 1 skip tape marks(channel,n,flag) flag = 1 finish else recordfault(channel,flag); ! Read failure finish else tc(channel)_err count = 0 end ; ! of READMAG externalroutine writemag(integer channel,address,len, integername flag) check channel(channel) if tc(channel)_mode < 2 then mag fail("Writing not allowed") write block (channel,address,len,flag) if tc(channel)_et flag = yes then flag = 4 ;! END OF TAPE if flag > 0 then start if flag > 7 then mag fail("DMAG IO fails in WRITEMAG") recordfault(channel,flag) finish else tc(channel)_err count = 0 end ; ! of WRITEMAG externalroutine writetmmag(integer channel,integername flag) check channel(channel) if tc(channel)_mode < 2 then mag fail("Writing not allowed") write tape mark (channel,flag) if tc(channel)_et flag = yes then flag = 4 ;! END OF TAPE if flag > 0 then start if flag > 7 then mag fail("DMAG IO fails in WRITETMMAG") recordfault(channel,flag) finish else tc(channel)_err count = 0 end ; ! of WRITETMMAG externalroutine locate mag (integer channel,type, integername count,flag) check channel (channel) locate tape (channel,type,count,flag) if flag > 7 then mag fail ("DMAGIO failure in LOCATE MAG") return end ;! OF LOCATE MAG externalroutine rewindmag (integer channel) integer flag check channel(channel) rewind tape (channel,0,flag) if flag > 7 then mag fail("DMAG IO fails in REWINDMAG") end ; ! of REWINDMAG externalroutine skipmag(integer channel,n) ! Skips N blocks (a tape mark counting as a block) backwards or forwards integer flag,direction,i,len check channel(channel) return if n = 0; ! Null call if n > 0 then direction = 1 else n = -n and direction = -1 for i = 1,1,n cycle len = direction skip blocks (channel,len,flag) if flag > 7 then mag fail("DMAG IO fails in SKIPMAG") if 1 <= flag <= 2 then recordfault(channel,flag) if flag = 4 then start ; ! Found tape mark len = direction skip tape marks (channel,len,flag) if flag > 7 then mag fail("DMAG IO fails in SKIPMAG") if 1 <= flag <= 2 then recordfault(channel,flag) finish repeat end ; ! of SKIPMAG externalroutine skiptmmag (integer channel,n) integer flag,len,direction,i check channel(channel) if n = 0 then return ; ! Null call if n > 0 then direction = 1 else n = -n and direction = -1 for i = 1,1,n cycle len = direction skip tape marks (channel,len,flag) if flag > 7 then mag fail("DMAG IO fails in SKIPTMMAG") if 1 <= flag <= 2 then recordfault(channel,flag) repeat end ; ! of SKIPTMMAG externalroutine fskiptmmag(integer channel,n,integername flag) integer len check channel(channel) if n = 0 then return ; ! Null call len = n; ! Number of tapemarks to skip skip tape marks (channel,len,flag) if flag > 7 then mag fail("DMAG IO fails in SKIPTMMAG") if 1 <= flag <= 2 then recordfault(channel,flag) if flag = 4 then flag = 1; ! Found double tape mark before skipping enough end ; ! of FSKIPTMMAG externalroutine statusmag (integer channel,address) recordformat statusrecf (string (6) vol,integer ring, tmcount,blockcount,eovflag,errorcount) record (statusrecf)name sr sr == record(address) check channel(channel) sr_vol = tc(channel)_tsn sr_ring = tc(channel)_mode sr_tmcount = tc(channel)_tmcount sr_block count = tc(channel)_blockno sr_eovflag = tc(channel)_etflag sr_error count = tc(channel)_err count return end ;! OF STATUSMAG !*********************************************************************** !* !* Routines for back-compatibility !* !*********************************************************************** externalroutine askmt(string (7) vol,integername flag) integer i cuc = 0 mtchannel = 0 for i = max tape channel,-1,min tape channel cycle if uchannel(i) = 0 then mtchannel = i and exit repeat if mtchannel = 0 then magfail("NO AVAILABLE TAPE CHANNELS") askmag(mtchannel,vol,flag) end ; ! of ASKMAG externalroutine openmt(string (7) vol) integer flag askmt(vol,flag) if flag > 0 then mag fail("FAILED TO OPEN TAPE") end ; ! of OPENMT externalroutine unloadmt unloadmag(mtchannel) end ; ! of UNLOADMT externalroutine locatemt (integer type, integername count,flag) locate mag (mtchannel,type,count,flag) return end ;! OF LOCATEMT externalroutine rewindmt rewindmag(mtchannel) end ; ! of REWINDMT externalroutine readmt(integer ad,integername len,flag) readmag(mtchannel,ad,len,flag) end ; ! of READMT externalroutine writemt(integer ad,len,integername flag) writemag(mtchannel,ad,len,flag) end ; ! of WRITEMT externalroutine writetmmt(integername flag) writetmmag(mtchannel,flag) end ; ! of WRITETMMT externalroutine skipmt(integer n) skipmag(mtchannel,n) end ; ! of SKIPTM externalroutine skiptmmt(integer n) skiptmmag(mtchannel,n) end ; ! of SKIPTMMT externalroutine fskiptmmt(integer n,integername flag) fskiptmmag(mtchannel,n,flag) end ; ! of FSKIPTMMT externalroutine statusmt (integer address) statusmag(mtchannel,address) end ;! OF STATUSMT externalroutine set mt(integer density,xcmode) integer flag set mag(mtchannel,density,xcmode,flag) end ; ! of SETMT !*MAGNETIC TAPE INTERFACE ROUTINES !THESE ROUTINES ARE USED FOR HIGH LEVEL PROGRAMMING LANGUAGE ACCESS !TO MAGNETIC TAPE. THEY ARE ACCESSED BY NEWFILEOP FOR FORTRAN !AND BY THE IMP SQ ROUTINES DIRECTLY. THEY IN TURN CALL ROUTINE MAGIO !WHICH CONTAINS THE MAIN MAG TAPE DRIVER ROUTINES. constinteger closed = 0 constinteger pf = 1,slf = 2 constinteger afterread = 2 constinteger afterwrite = 3 constinteger afterendfile = 6 constinteger ebcdicbit = x'20' constinteger ringneeded = x'40' constinteger modflag = x'08' ;! "MOD" OPEN REQUIRED recordformat mtfdf ( c integer level,dsnum, byteinteger status,access route,valid action,cur state, mode of use,mode,file org,dev code,rectype, flags,channel,relpos, integer rel file num,arec,rec size,minrec,maxrec, block ptr,recptr,sob,eob,sod,eod, transfers,block xfers,block length, reclen, byteinteger recrtl,blkrtl,lpmode,spare, blocking,data format,bwf, string (17) dsn, string (6) volume c ) externalroutinespec set return code (integer code) systemintegerfnspec open(integer afd, mode) systemroutinespec psysmes(integer root, flag) systemintegerfnspec pstoi(string (63) s) systemroutinespec setfname(string (40) name) systemroutinespec setpar(string (255) s) systemintegerfnspec parmap systemstringfnspec spar(integer n) systemintegerfnspec fdmap(integer chan) systemroutinespec define(integer chan, string (31) iden, integername afd, flag) externalroutine definemt(string (255) s) constinteger minblocksize = 18 constinteger maxblocksize = 32767 constinteger optblocksize = 4096 switch blockcheck(1 : 6) constinteger maxrecfms = 14 conststring (4)array recfms(1:maxrecfms)=c "F","FA","FB","FBA", "V","VA","VB","VBA","VS","VSA","VBS","VBSA", "U","" constintegerarray asa(1:maxrecfms)=c 0,16,0,16,0,16,0,16,0,16,0,16,0,0 constbyteintegerarray minrec(1:maxrecfms)=18(4),1(8),18,0 constbyteintegerarray blockat(1:maxrecfms)=c 1(2),2(2),1(2),2(2),3(2),4(2),1,0 constbyteintegerarray rectype(1:maxrecfms)=1(4),2(8),3,255 constintegerarray maxrec(1:maxrecfms)=c 32760(4),32752(4),32767(2),99999(2),32760,0 constbyteintegerarray bccode(1:maxrecfms)=1(2),2(2),3(2),4(2),5(4),6(2) string (60) dsn ;! ALLOW FOR LONG NAME TO BE TRUNCATED string (31) vol, srecsize string (10) slabel ;! HAVE TO ALLOW FOR "U9999-MOD" string (8) schan, recfm, srec, sblocksize integer chan, label, lrec, blocksize, afd, flag, i, char, ring, recfmcode,lpmode,openflag integer recsize,permits record (mtfdf)name f integer acr, lnb flag = dsfi(uinfs(1),-1,38,0,addr(permits)) if flag # 0 then -> err if permits & tape permit # tape permit and uinfi(2) & 1 = 1 then start flag = 350 ;! NO INTERACTIVE ACCESS TO TAPES ;! FOR THIS USER printstring( c "DEFINEMT fails - interactive access to tapes not permitted") -> err finish ring = 0; !NO RING BY DEFAULT setpar(s) if parmap&5 # 5 or parmap > x'3F' c then flag = 263 and -> err !WRONG NUMBER OF PARAMETERS chan = pstoi(spar(1)); !CHANNEL NUMBER unless 1 <= chan <= 80 then flag = 223 and -> err !INVALID CHANNEL NUMBER open flag = 0 dsn = spar(2) if length(dsn) > 3 and c substring(dsn,length(dsn)-3,length(dsn)) = "-MOD" then start length(dsn) = length(dsn) - 4 ;! REMOVE"-MOD" open flag = modflag finish ! NEXT CHECK ONLY TEST FRO FILE NAME TOO LONG BECAUSE ! THE FILE NAME IS NOW AN OPTIONAL PARAMETER if length(dsn) > 17 c then dsn = substring(dsn,length(dsn)-16,length(dsn)) ! IF NO FILE NAME GIVEN, INSERT SPECIAL NAME BECAUSE A ! NULL FILE NAME MEANS FREE FILE DESCRIPTOR TO SOME PARTS ! OF THE SUBSYSTEM if dsn = "" then dsn = nulldsn vol = spar(3); !VOLUME LABEL if charno(vol,length(vol)) = '*' start length(vol) = length(vol)-1 ring = ringneeded label = -1 ;! NO DEFAULT LABEL WHILE WRITING finish else start ! RING NOT REQUESTED label = 1 ;! DEFAULT !????? TEST OPEN FLAG. IF SET TO MOD FLAG THEN !????? GENERATE ERROR finish !WITH OR WITHOUT WRITE unless 1 <= length(vol) <= 6 c then setfname(vol) and -> badparam slabel = spar(4) if length(slabel) > 0 and charno(slabel,1) = 'P' then start slabel = substring(slabel,2,length(slabel)) lpmode = pf finish else lpmode = slf if length(slabel) > 3 and c substring(slabel,length(slabel)-3,length(slabel)) = "-MOD" then start length(slabel) = length(slabel) - 4 ;! REMOVE "-MOD" open flag = mod flag finish if slabel # "" then label = pstoi(slabel) unless 1 <= label then setfname(slabel) and -> badparam !INVALID LABEL PARAMETER srec = spar(5) if srec # "" start ; !USER PROVIDES format INFORMATION recfm = "" for i = 1,1,length(srec) cycle char = charno(srec,i) unless 'A' <= char <= 'Z' then exit recfm = recfm.tostring(char) repeat if recfm = srec then setfname(srec) and -> badparam !NO RECORD SIZE SPECIFIED srecsize = substring(srec,length(recfm)+1,length(srec)) recsize = pstoi(srecsize) for recfmcode = 1,1,maxrecfms cycle if recfm = recfms(recfmcode) start ; !RECFM FOUND unless minrec(recfmcode) <= recsize <= maxrec( c recfmcode) then setfname(srec) and -> badparam exit finish if recfmcode = maxrecfms c then setfname(srec) and -> badparam repeat sblocksize = spar(6) if sblocksize # "" then blocksize = pstoi(sblocksize) c else blocksize = 0 -> blockcheck(bccode(recfmcode)) blockcheck(1): !FIXED if blocksize # 0 then start if blocksize # recsize then -> badblocksize finish else blocksize = recsize -> endblock blockcheck(2): !FIXED BLOCKED if blocksize # 0 start unless (blocksize//recsize)*recsize = blocksize c then -> badblocksize finish else start blocksize = recsize blocksize = blocksize+recsize while blocksize < optblocksize finish !CHOOSE A SUITABLE SIZE -> endblock blockcheck(3): !VARIABLE UN-BLOCKED if blocksize # 0 start if blocksize < recsize+8 then -> badblocksize finish else blocksize = recsize+8 -> endblock blockcheck(4): !VARIABLE BLOCKED if blocksize # 0 start if blocksize < recsize+8 then -> badblocksize finish else start if recsize < optblocksize-8 c then blocksize = optblocksize c else blocksize = recsize+8 finish -> endblock blockcheck(5): !SPANNED if blocksize = 0 then blocksize = optblocksize -> endblock blockcheck(6): !UNSTRUCTURED if blocksize # 0 start if blocksize < recsize then -> badblocksize finish else start if blocksize = 0 then blocksize = recsize finish endblock: unless minblocksize <= blocksize <= maxblocksize c then -> badblocksize finish else recsize = 0 and blocksize = 0 and recfmcode = 14 !FORMAT INFO NOT SUPPLIED fillrec: define(chan,".NULL",afd,flag); !GET EMPTY DESCRIPTOR if flag # 0 then -> err f == record(afd) f_accessroute = 5; !MAGNETIC TAPE f_modeofuse = 2; !SEQUENTIAL f_mode = 11; !FOR FORTRAN I/O f_rectype = rectype(recfmcode) f_flags = f_flags!asa(recfmcode)!ring!ebcdicbit!openflag f_relfilenum = label; !FILE ON TAPE if f_rectype = 1 then f_minrec = recsize c else f_minrec = minrec(recfmcode) f_maxrec = recsize f_blocklength = blocksize f_lpmode = lpmode f_blocking = blockat(recfmcode) f_dsn = dsn f_volume = vol flag = 0 !? NEWLINE : PRINTSTRING("CHANNEL =") : WRITE(F_DSNUM,0) !? NEWLINE : PRINTSTRING("FILE NAME = ") : PRINTSTRING(F_DSN) !? %IF F_FLAGS & X'08' > 0 %THEN PRINTSTRING(", MOD OPEN") !? NEWLINE : PRINTSTRING("VOLUME = "): PRINTSTRING(F_VOLUME) !? %IF F_FLAGS & X'40' > 0 %THEN PRINTSTRING(" + RING") !? NEWLINE : PRINTSTRING("LABEL =") : WRITE(F_REL FILE NUM,0) !? %IF F_LPMODE = 1 %THEN PRINTSTRING(" - UNLABELLED") !? NEWLINE : PRINTSTRING("RECTYPE =") : WRITE(F_RECTYPE,0) !? NEWLINE : PRINTSTRING("RECSIZE =") : WRITE(F_MAXREC,0) !? NEWLINE : PRINTSTRING("BLOCKSIZE =") : WRITE(F_BLOCKLENGTH,0) !? NEWLINE : PRINTSTRING("BLOCKING =") : WRITE(F_BLOCKING,0) -> err badblocksize: setfname(sblocksize) badparam: flag = 202 -> err err: set return code (flag) if flag # 0 then psysmes(100,flag) end ; !OF DEFINEMT systemintegerfn newmtfileop(integer afd, act) record (mtfdf)name f integer flag byteintegername curstate f == record(afd) curstate == f_curstate if act = 1 start ; !READ if curstate = afterread then -> ok; !AFTER READ if curstate = closed start flag = open(afd,1); !OPEN FOR READING curstate = afterread result = flag finish if curstate = afterwrite then result = 156 !FAILURE READ AFTER WRITE if curstate = afterendfile then result = 153 !END OF FILE finish !* !* WRITE !* if act = 2 start if curstate = afterwrite then -> ok if curstate = closed start flag = open(afd,2); !OPEN FOR WRITING if flag # 0 then result = flag curstate = afterwrite -> ok finish if curstate = afterread start if f_flags&ringneeded = 0 then result = 319 !NO RING f_validaction = f_validaction!2; !OR IN WRITE BIT magio(afd,1,flag); !PREPARE FOR WRITE curstate = afterwrite if flag # 0 then result = flag -> ok finish finish !* !* REWIND !* if act = 4 start if curstate = closed then -> ok if curstate = afterwrite start ; !AFTER WRITE - NEED TO DO AN ENDFILE magio(afd,6,flag); !ENDFILE if flag # 0 then result = flag curstate = afterread finish if curstate = afterendfile then curstate = afterread if curstate = afterread start magio(afd,4,flag); !THE REWIND ITSELF result = flag finish finish !* !* BACKSPACE !* if act = 8 start if curstate = afterread start magio(afd,5,flag) result = flag finish if curstate = afterwrite start magio(afd,6,flag); !DO AN ENDFILE FIRST if flag # 0 then result = flag magio(afd,5,flag); !THE BACKSPACE ITSELF curstate = afterread result = flag finish if curstate = afterendfile c then curstate = afterread and -> ok finish !* !* ENDFILE !* if act = 16 start if curstate = afterwrite start magio(afd,6,flag) result = flag finish finish !* result = 171; !INVALID OPERATION !* ok: result = 0 end ; !OF NEWMTFILEOP endoffile