! r.d.eager contains code for output accounts in comm control, ! lp adaptor and cp adaptor ! version of 13/06/80 ! dec 79: include an oper adaptor ! have only one service number, x'37' ! other possible changes ! make initialisation call of cc initialise adaptors as well ! make it possible to deallocate/allocate all devices !* communications record format - extant from chopsupe 20a onwards * record format comf(integer ocptype, ipldev, sblks, sepgs, ndiscs, ddtaddr, gpctabsize, gpca, sfctabsize, sfca, sfck, dirsite, dcodeda, suplvn, wasklokcorrect, date0, date1, date2, time0, time1, time2, epagesize, users, cattad, dqaddr, byte integer nsacs, resv1, sacport1, sacport0, nocps, resv2, ocpport1, ocpport0, integer itint, contypea, gpcconfa, fpcconfa, sfcconfa, blkaddr, dptaddr, smacs, trans, long integer kmon, integer ditaddr, smacpos, supvsn, pstva, secsfrmn, secstocd, sync1dest, sync2dest, asyncdest, maxprocs, inspersec, elaphead, commsreca, storeaad, procaad, sfcctad, drumtad, tslice, sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7, sp8, lstl, lstb, pstl, pstb, hkeys, hoot, sim, clkx, clky, clkz, hbit, slaveoff, inhssr, sdr1, sdr2, sdr3, sdr4, sesr, hoffbit, s2, s3, s4, end) record format pe(integer dserv, sserv, p1, p2, p3, p4, p5, p6) record format parmf(integer dserv, sserv, p1, p2, p3, p4, p5, p6, link) !*********************************************************************** !* service numbers and activity numbers * !*********************************************************************** !* global constants const integer virtual= x'81000000' const integer not allocated= x'F0F0F0F0' const integer empty= x'F0F0' const integer attention= 1 const integer normal termination= 8 const integer auto= x'00008400' const integer ebc nl= 21 const integer ebc lf= 37 const integer ebc ff= 12 const integer ebc cr= 13 const integer ebc vp= 34 const integer ebc ms= 32 const integer ebc mnl= 33 const integer ebc sp= 64 const string (1) snl= " " !* comms controller activities const integer init= 0 const integer connect= 1 const integer connect reply= 13 const integer enable= 2 const integer claim amt index reply= 14 const integer enable reply= 15 const integer disable= 4 const integer disable reply= 16 const integer disconnect= 5 const integer disconnect reply= 17 const integer control msg6= 6 const integer control msg6 reply= 18 const integer control msg7= 7 const integer control msg7 reply= 19 const integer transfer requested= 10 const integer page here= 11 const integer transfer completed= 12 !* comms controller requests and reply service numbers const integer comms command= x'00370000' const integer claim block= x'00080001' const integer block claimed= comms command!claim amt index reply const integer free block= x'00080002' const integer claim page= x'00040001' const integer page claimed= comms command!page here const integer free page= x'00040002' const integer request transfer= comms command!transfer requested const integer transfer complete= comms command!transfer completed !* adaptor activities const integer deallocated= x'00000001' const integer allocated= x'00000002' const integer execute fails= x'00000003' const integer initialise= x'00000004' const integer interrupt= x'00000005' const integer go ahead= x'00000006' const integer send control= x'00000007' const integer clock tick= x'00000008' const integer allocate request= x'00000009' const integer oper com reply= x'0000000A' const integer deallocate request= x'0000000B' !* adaptor service numbers const integer op service= x'32' const integer lp service= x'33' if cr fitted=yes start const integer cr service= x'34' finish if cp fitted=yes start const integer cp service= x'35' finish const integer mk1 fe service= x'39' const integer op command= op service<<16 const integer lp command= lp service<<16 if cr fitted=yes start const integer cr command= cr service<<16 else const integer cr command= not allocated finish if cp fitted=yes start const integer cp command= cp service<<16 else const integer cp command= not allocated finish const integer mk1 fe command= mk1 fe service<<16 !* adaptor requests and replies service numbers const integer gpc command= x'00300000' const integer allocate device= gpc command!x'0000000B' const integer lp allocated= lp command!allocated if cr fitted=yes start const integer cr allocated= cr command!allocated finish if cp fitted=yes start const integer cp allocated= cp command!allocated finish const integer mk1 fe allocated= mk1 fe command!allocated const integer deallocate device= gpc command!x'00000005' const integer lp deallocated= lp command!deallocated if cr fitted=yes start const integer cr deallocated= cr command!deallocated finish if cp fitted=yes start const integer cp deallocated= cp command!deallocated finish const integer mk1 fe deallocated= mk1 fe command!deallocated const integer execute chain= gpc command!x'0000000C' const integer lp executed= lp command!execute fails if cr fitted=yes start const integer cr executed= cr command!execute fails finish if cp fitted=yes start const integer cp executed= cp command!execute fails finish const integer mk1 fe executed= mk1 fe command!execute fails const integer lp interrupt= lp command!interrupt if cr fitted=yes start const integer cr interrupt= cr command!interrupt finish if cp fitted=yes start const integer cp interrupt= cp command!interrupt finish const integer mk1 fe interrupt= mk1 fe command!interrupt const integer elapsed int command= x'000A0001'; !TICK EVERY N SECS const integer mk1 fe clock tick=mk1 fe command!clock tick const integer mk1 fe oper com reply= mk1 fe command!oper com reply const integer oper command req= x'0032000E' const integer secs per tick= 15 if mon level&2#0 start extrinsic long integer kmon; !bit 2**sno is set when service sno is to be monitored finish external routine spec dump table(integer table, address, length) external routine spec dpon(record (pe) name mess, integer secs) external routine spec pon(record (pe) name mess) external routine spec gdc(record (pe) name mess) external routine spec pkmon rec(string (20) text, record (pe) name mess) external routine spec op mess(string (63) s) external routine spec monitor(string (63) s) external routine spec move alias "S#MOVE"(integer length, from, to) external routine spec etoi alias "S#ETOI"(integer address, length) external routine spec i to e alias "S#ITOE"(integer address, length) external integer fn spec realise(integer virtual address) external integer fn spec new pp cell external routine spec elapsed int(record (pe) name p) external string fn spec strint(integer i) if multi ocp=no start routine spec comms control(record (pe) name mess) routine spec mk1 fe adaptor(record (pe) name mess) else external routine spec reserve log external routine spec release log finish external routine spec return pp cell(integer cell) if mon level&256#0 start ; !include harvesting? routine spec harvest(integer event, process, len, a, b, c, d, e) external integer trace= no; !initially no trace external integer trace events= 0; !initially no events to trace external integer trace process= 0; !initially no process to trace finish !*********************************************************************** !* * !* stream types. * !* * !* even streams: input streams * !* odd streams: output streams * !* * !* stream states. * !* * !* from state title to state * !* 1-> 0: unused ->2 * !* 3-> 1: disconnecting ->0 * !* 0-> 2: connecting ->3 * !* 2,4,5-> 3: connected ->1,6 * !* 8-11-> 4: suspending ->3 * !* 8-11-> 5: aborting ->3 * !* 3-> 6: claiming -> 7 * !* 6-> 7: enabling ->8 * !* 7-> 8: enabled ->4,5,9-11 * !* 8-> 9: queued ->4,5,10 * !* 8,9-> 10: paging in ->4,5,11 * !* 8,10-> 11: active ->4,5,8,10 * !* * !* * !* stream buffer modes. * !* * !* bits 2**0 to 2**3 * !* 0: sequential 2: sequential continuation * !* 1: circular * !* * !* bits 2**4 to 2**7 * !* 0: iso 2: binary * !* 1: ebcidic 3: control * !* * !* adaptor types. * !* * !* device type device mnemonic adaptor name * !* 1 pt not available * !* 2 pr not available * !* 3 cp cp adaptor * !* 4 cr cr adaptor * !* 5 m0 not available * !* 6 lp lp adaptor * !* 7 gp not available * !* 8 op oper adaptor * !* 9 gu not available * !* 10 dr not available * !* 11 na not available * !* 12 ct not available * !* 13 su not available * !* 14 fe mk1 fe adaptor * !* 15 lk not available * !* * !* device no. * !* * !* can currently be in the range 0 - 9. * !* when combined with an adaptor type a device mnemonic is produced * !* i.e. lp0 to lp9 or fe0 to fe9. * !* * !*********************************************************************** record format sr(half integer stream no, external stream no, byte integer state, mode, adaptor no, device no, integer length, owner, caller, amt index, start, cursor, link) record format br(integer stream no, external stream no, amt index, offset, length, real address, p5, p6, link) record format comms inff(integer index addr, next free buffer, queued stream head, queued stream tail) const integer max stream= maxprocs*3-1 own integer array stream index(0:max stream)= c empty(max stream+1) own record (sr) array format sarf(0:4095); !mapped onto parm table own record (sr) array name stream tab; !stream table array own record (br) array format barf(0:4095); !mapped onto the parm table own record (br) array name buffer tab; !buffer table array own record (parmf) array format parf(0:4095); !mapped onto parm table own record (parmf) array name parm tab; !parm table array own record (comf) name com area own record (comms inff) comms if sfc fitted=yes start const integer drum update= 5; !drum update and recapture else const integer drum update= 1; !only recapture finish const integer no drum update= 0; !no drum update and no recapture const integer byte mask= x'FF' const integer short mask= x'FFFF' const integer top short mask= x'FFFF0000' const integer top bit mask=x'80000000' const integer cc activity mask=31 const integer queued flag=x'40' !* stream states const integer disconnecting= 1; !waiting on adaptor action const integer connecting= 2; !waiting on adaptor action const integer connected= 3; !waiting on user action const integer suspending= 4; !waiting on adaptor action const integer aborting= 5; !waiting on adaptor action const integer claiming= 6; !waiting on global controller action const integer enabling= 7; !waiting on adaptor action const integer enabled= 8; !waiting on user action const integer queued= 9; !waiting on a free epage (buffer) const integer paging in= 10; !waiting on global controller action const integer active= 11; !waiting on adaptor action !* stream buffer modes const integer sequential= 0; !first enable on a stream possible special adaptor action const integer circular= 1 const integer sequential continuation= 2; !a subsequent sequential enable no special action const integer iso= 0 const integer ebcidic= 1 const integer binary= 2 !* control = 3 !* adaptor types const integer cp= 3 const integer cr= 4 const integer lp= 6 const integer op= 8 const integer fe= 14 !*********************************************************************** !* the next array is a mapping from adaptor types to service numbers * !*********************************************************************** const integer array adapt(0:15)= c not allocated(3),cp command,cr command,not allocated,lp command, not allocated,op command, not allocated(5),mk1 fe command,not allocated !*********************************************************************** !* the next array is a mapping from states to reply activities * !*********************************************************************** const integer array sub ident(0:active)= c -1,disconnect reply<<16,connect reply<<16,-1,disable reply<<16(2), -1,enable reply<<16,-1(4) external routine comms control(record (pe) name mess) !*********************************************************************** !* * !* After initialization, all free buffer records are held on a * !* linked list, the first element of which is pointed at by "next * !* free buffer". the maximum buffer (e page) allocation is * !* given by the global controller at initialisation. the comms * !* controller can then own up to this allocation of epages. * !* if there are no free buffers, streams requiring a buffer are * !* held on a linked list, the first element of which is pointed at * !* by "queued stream head" and the last element of which is pointed * !* at by "queued stream tail". * !* if the list is empty, both the head and tail pointers will * !* be set to "empty" (x'f0f0'). * !* space for stream descriptors and buffer descriptors is allocated * !* dynamically by claiming param cells from the dynamically extendable* !* param table via a call on "new pp cell". stream descriptors are * !* accessed indirectly through the array "stream index" but buffer * !* descriptors are accessed directly. when descriptors are no longer * !* required the space is returned to the param table via a call on * !* "return pp cell". * !* note: all page sharing and page recapture is handled by the * !* global controller. the comms controller only requests "page ins" * !* and "page outs". it is up to the global controller to supply the * !* latest copy of the page and to update the relevant copies. * !* * !*********************************************************************** ! %if mon level&256 # 0 %start; !include harvesting? integer proc no ! %finish integer status integer call proc const integer successful= 0, unsuccessful = 1 record (sr) name stream; integer stream no record (br) name buffer; integer buffer no integer temp, dserv, dact, old state, link if monlevel&4#0 start own integer array pool size(-19:comms epages) own integer queued store, queued disc, not queued store, not queued disc, free buffers integer i, l, t finish switch com(init:control msg7 reply) routine spec release page(integer drum up) routine spec release buffer(integer drum up) integer fn spec unlink stream(integer stream no) routine spec get page(integer stream no) routine spec release block !=================================== const integer check ftp streams=yes if check ftp streams=yes start integer device no, ext stream finish !=================================== !*********************************************************************** !* main program * !*********************************************************************** !** monitor if mon level&2#0 and (kmon>>(comms command>>16))&1#0 then pkmon rec("Comms Control: ", mess) dserv = mess_dserv dact = dserv&cc activity mask ->com(dact) if dact<=connect; !connect or init? stream no = mess_p1; !pull out stream number first always in p1 if 0<stream no<=max stream start ; !within range? temp = stream index(stream no) if temp#empty start ; !allocated? stream == stream tab(temp) proc no = ((stream_owner>>16)-64)&(max procs-1) if dact<transfer requested start call proc = ((mess_sserv>>16)-64)&(max procs-1) if call proc#proc no start unless proc no<=2 or (proc no<=5 and mess_sserv>>16=x'32') start !This is primarily (the <=5... bit) to allow OPER ADAPTOR to !muck about with comms streams owned by executives. op mess("Comms: illegal use of stream ".strint(stream no)) op mess("Comms: caller ".strint(call proc).", owner ".strint(proc no)) printstring("Comms control: offending POFF -".snl) pkmon rec("Comms control: ", mess) return finish finish stream_caller = mess_sserv finish else status = mess_p2 ->com(dact) finish finish no stream: print string("Comms Control: Stream not allocated ".snl) pkmon rec("Comms Control: ", mess) return unless dact<transfer requested mess_dserv = mess_sserv; !reply to caller mess_sserv = dserv mess_p2 = unsuccessful pon(mess) return !*********************************************************************** !* * !* this section handles: * !* 1). communications controller initialisation * !* * !*********************************************************************** com(init): !** command: initialise (or print monitoring) if mess_p2>0 start stream tab == array(mess_p3, sarf); !address supplied by global controller buffer tab == array(mess_p3, barf); !address of parm array supplied by global controller parm tab == array(mess_p3, parf); !parm table array com area == record(x'80000000'+48<<18) comms_index addr = addr(stream index(0)) comms_queued stream head = empty comms_queued stream tail = empty com area_commsreca = addr(comms) link = new pp cell; !get a buffer entry from param table comms_next free buffer = link; !head of list for temp = 1, 1, mess_p2 cycle ; !get other buffers buffer == buffer tab(link); !map buffer buffer = 0 buffer_stream no = empty if temp=mess_p2 then link = empty else link = new pp cell !last buffer? buffer_link = link; !link buffers together repeat if monlevel&4#0 then free buffers = comms epages else ; !print monitoring if monlevel&4#0 start l = 0 l = l+pool size(i) for i = 1, 1, comms epages t = l t = t+pool size(i) for i = -19, 1, 0 if multiocp=yes then reserve log newlines(2) printstring("Buffer use (on transfer request) - 100% =") write(t, 1); newline printstring("buffer pool: availability =") write(l*100//t, 1); printstring("%"); newline write(i, 4) for i = 1, 1, comms epages newline; space for i = 1, 1, comms epages cycle write(pool size(i)*100//t, 3) printstring("%") repeat newline printstring("buffer queue: requests queued =") write(100-(l*100//t), 1); printstring("%"); newline write(i, 4) for i = 1, 1, 20 printstring("+"); newline; space for i = 0, -1, -19 cycle write(pool size(i)*100//t, 3) printstring("%") repeat newline l = queued store+queued disc if l#0 then l = queued store*100//l printstring("Pagein for queued buffers: ") write(l, 1); printstring("% store, ") write(100-l, 1); printstring("% disc") newline l = not queued store*100//(not queued store+not queued disc) printstring("Pagein for available buffers: ") write(l, 1); printstring("% store, ") write(100-l, 1); printstring("% disc") newlines(2) if multiocp=yes then release log pool size(i) = 0 for i = -19, 1, comms epages queued store = 0; queued disc = 0 not queued store = 0; not queued disc = 0 finish finish return !*********************************************************************** !* * !* this section handles: * !* 1). commands from communications users. * !* 2). replies from the global controller in response * !* to calls generated by commands. * !* 3). replies from an adaptor in response * !* to calls generated by a command. * !* * !*********************************************************************** com(connect): !** command: connect stream to communications user !=================================== if check ftp streams=yes start ext stream = mess_p3&short mask if (mess_p3>>24)&15=fe and 8<ext stream<350 start stream no = mess_p1&1 if stream no#ext stream&1 start op mess("Comms: I/O mismatch in connect".tostring(17)) ->no stream finish device no = (mess_p3>>16)&byte mask for stream no = stream no+2, 2, max stream-(1-stream no) cycle if stream index(stream no)=empty then continue if stream tab(stream index(stream no))_external stream no=ext stream and c stream tab(stream index(stream no))_device no=device no and c stream tab(stream index(stream no))_adaptor no=fe start op mess("Comms: duplicate FEP stream ".strint(ext stream).tostring(17)) if stream tab(stream index(stream no))_owner#mess_p2 then ->no stream op mess("Comms: old str ".strint(stream no)." discarded") return pp cell(stream index(stream no)) stream index(stream no) = empty exit finish repeat finish finish !====================================== stream no = mess_p1&1; !make even or odd i.e. input or output for stream no = stream no+2, 2, max stream-(1-stream no) cycle if stream index(stream no)=empty start ; !found a free stream stream index(stream no) = new pp cell; !get a stream table entry stream == stream tab(stream index(stream no)) stream = 0 stream_stream no = stream no stream_caller = mess_sserv stream_owner = mess_p2; !high level control messages sent on this service if mon level&256#0 start ; !include harvesting? proc no = ((stream_owner>>16)-64)&(max procs-1) harvest(14, proc no, 8, stream no, mess_p3, 0, 0, 0) if c trace=yes and trace events&(1<<14)#0 and (trace process=-1 or trace process=proc no) finish stream_length = stream no; !set for replies from front end stream_cursor = mess_p5; !set for reply to connect only stream_mode = mess_p6; !set to pass unit size into the adaptor stream_adaptor no = (mess_p3>>24)&15 if adapt(stream_adaptor no)=not allocated start status = unsuccessful ->return cell finish stream_device no = (mess_p3>>16)&byte mask stream_external stream no = mess_p3&short mask stream_amt index = not allocated stream_link = empty stream_state = connecting ->update adaptor finish repeat ->no stream com(connect reply): !** adaptor reply: stream connected if mon level&256#0 start ; !include harvesting? harvest(15, proc no, 4, stream no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<15)#0 and (trace process=-1 or trace process=proc no) finish if status=successful start stream_state = connected ->reply to caller; !note, p3 = nsi or x25 indication from fep finish else ->return cell com(enable): !** command: enable stream ! p1 p2 p3 p4 p5 p6 !Old format: stream da lim mode cursor<<16!start length !Newformat: stream 1<<31!da lim<<16!mode cursor start length ! Formats are distinguished by top bit in da. This change is for multi-console ! printer. Uses a (nominally) circular buffer (as required by fep), hence length ! is limited to '7fff'. So to access last 32k of a 128k block, halfwords start ! and length are insufficient. if stream_state=connected and mess_p6>0 start stream_state = claiming stream_length = mess_p6-1 if mess_p2&top bit mask#0 start ; !new format mess_p2 = mess_p2&(¬(top bit mask)) stream_mode = mess_p3&bytemask mess_p3 = mess_p3>>16 stream_start = mess_p5 if stream_mode&15=circular then stream_cursor = mess_p4 else stream_cursor = 0 else stream_mode = mess_p4&bytemask if stream_mode&15=circular start stream_start = mess_p5&short mask stream_cursor = mess_p5>>16 else stream_start = mess_p5 stream_cursor = 0 finish finish if mon level&256#0 start ; !include harvesting? harvest(16, proc no, 12, stream no, stream_mode, mess_p6, 0, 0) if c trace=yes and trace events&(1<<16)#0 and (trace process=-1 or trace process=proc no) finish !** claim block from the global controller mess_dserv = claim block mess_sserv = block claimed !** p1=stream no, p2=disc address, p3=flags/epages. user supplied cannot check pon(mess) return finish else status = unsuccessful ->reply to caller com(claim amt index reply): !** global controller reply: block claimed if mon level&256#0 start ; !include harvesting? harvest(17, proc no, 8, stream no, status, 0, 0, 0) if c trace=yes and trace events&(1<<17)#0 and (trace process=-1 or trace process=proc no) finish if status<=0 start ; !block unsuccessful !* 0 change block size in situ !* -1 no amt cells !* -2 not enough garbage !* -3 change block size while still in use stream_state = connected status = status-1; !zero normally successful ->reply to caller finish stream_amt index = status<<16 stream_state = enabling ->update adaptor com(enable reply): !** adaptor reply: stream enabled if mon level&256#0 start ; !include harvesting? harvest(18, proc no, 4, stream no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<18)#0 and (trace process=-1 or trace process=proc no) finish if status=successful start stream_state = enabled return if stream_mode&15#circular else stream_state = connected release block finish ->reply to caller com(disable): !** command: disable stream (suspend or abort) if mon level&256#0 start ; !include harvesting? harvest(19, proc no, 12, stream no, mess_p2, stream_state, 0, 0) if c trace=yes and trace events&(1<<19)#0 and (trace process=-1 or trace process=proc no) finish if stream_state>=enabled and suspending<=mess_p2<=aborting start old state = stream_state; !remember state stream_state = mess_p2; !set to suspending or aborting return if old state>=paging in; !wait for completion of page in or transfer stream no = unlink stream(stream no) if old state=queued release block ->update adaptor finish else status = unsuccessful ->reply to caller com(disable reply): !** adaptor reply: disabled if mon level&256#0 start ; !include harvesting? harvest(20, proc no, 4, stream no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<20)#0 and (trace process=-1 or trace process=proc no) finish stream_state = connected status = successful; !always successful ->reply to caller; !note, p3 = accounting info from adaptor com(disconnect): !** command: disconnect stream if mon level&256#0 start ; !include harvesting? harvest(21, proc no, 4, stream no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<21)#0 and (trace process=-1 or trace process=proc no) finish if stream_state=connected start stream_state = disconnecting ->update adaptor finish else status = unsuccessful ->reply to caller com(disconnect reply): !** adaptor reply: stream disconnected if mon level&256#0 start ; !include harvesting? harvest(22, proc no, 4, stream no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<22)#0 and (trace process=-1 or trace process=proc no) finish status = successful; !always successful ->return cell; !note, p3 = disconnect reason from fep com(control msg6): !** command: send high level control message which generates no reply if mon level&256#0 start ; !include harvesting? harvest(23, proc no, 12, stream no, dact, stream_state, 0, 0) if c trace=yes and trace events&(1<<23)#0 and (trace process=-1 or trace process=proc no) finish mess_sserv = comms command!control msg6 reply ->pon adaptor com(control msg7): !** command: send high level control message which generates a reply if mon level&256#0 start ; !include harvesting? harvest(23, proc no, 12, stream no, dact, stream_state, 0, 0) if c trace=yes and trace events&(1<<23)#0 and (trace process=-1 or trace process=proc no) finish status = mess_sserv&x'80000000'; !called or ponned? mess_dserv = send control!adapt(stream_adaptor no) mess_sserv = comms command!control msg7 reply if multiocp=no and stream_adaptor no=fe then mk1 fe adaptor(mess) else pon(mess) if status=0 then ->reply to caller; !ponned, so pon a reply mess_p2 = 0; !status mess_p5 = stream_cursor return ; !back to caller !*********************************************************************** !* * !* this section handles: * !* 1). transfer requests from an adaptor. * !* 2). replies from the global controller in response * !* to calls generated by transfer requests. * !* 3). transfer complete replies from an adaptor. * !* * !*********************************************************************** com(transfer requested): !** adaptor request: transfer request if stream_state=enabled start ; !ignore if suspending or aborting stream_cursor = status&short mask if c status#0 and stream_mode&15=circular and 0<=status&short mask<=stream_length if comms_next free buffer=empty start ; !no buffer available if mon level&256#0 start ; !include harvesting? harvest(27, proc no, 4, stream no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<27)#0 and (trace process=-1 or trace process=proc no) finish stream_state = queued; !so wait. stream_link = empty if comms_queued stream head=empty then comms_queued stream head = stream no else c stream tab(stream index(comms_queued stream tail))_link = stream no comms_queued stream tail = stream no else ; !buffer available buffer no = comms_next free buffer buffer == buffer tab(buffer no) comms_next free buffer = buffer_link buffer = 0 buffer_link = empty stream_link = buffer no get page(stream no) finish if monlevel&4#0 start if free buffers<-19 then pool size(-19) = pool size(-19)+1 else c pool size(free buffers) = pool size(free buffers)+1 free buffers = free buffers-1 finish finish return com(page here): !** global controller reply: current owner's page here if stream_state=paging in start ; !check buffer not disabled (suspend or abort) if mon level&256#0 start ; !include harvesting? harvest(30, proc no, 8, stream no, (mess_sserv&255)<<24!(mess_p5&255)<<16!(mess_p6&short mask), 0, 0, 0) if trace=yes and trace events&(1<<30)#0 and (trace process=-1 or trace process=proc no) finish op mess("Comms page in fails") if mess_p3#0 !* should deal with transfer failures here? !* p_p3 = 0 ok !* p_p3 = 1 parity error !* p_p3 > 1 zero page if status=-1 start op mess("Comms: deadlock page recovery") mess = 0 mess_dserv = claim page mess_sserv = page claimed mess_p1 = buffer tab(stream_link)_amt index mess_p2 = stream no dpon(mess, 2); !2 secs delay return finish if monlevel&4#0 start t = mess_sserv&255 if dserv&queued flag#0 start if t=1 then queued store = queued store+1 else queued disc = queued disc+1 else if t=1 then not queued store = not queued store+1 else not queued disc = not queued disc+1 finish finish buffer tab(stream_link)_real address = status; !real page address stream_state = active mess_sserv = transfer complete mess_dserv = go ahead!adapt(stream_adaptor no) mess_p2 = stream_link mess_p5 = stream_device no; ! for use by OPER if multi ocp=no and stream_adaptor no=fe then mk1 fe adaptor(mess) else pon(mess) return else ; !buffer disabled buffer no = stream_link buffer == buffer tab(buffer no) release buffer(no drum update) release block ->update adaptor finish com(transfer completed): !** adaptor reply: transfer complete if mon level&256#0 start ; !include harvesting? harvest(31, proc no, 12, stream no, mess_p2, mess_p3, 0, 0) if c trace=yes and trace events&(1<<31)#0 and (trace process=-1 or trace process=proc no) finish !* p2 has bits set with significance 2**0 next page required !* 2**1 page not eligible for recapture !* 2**2 update users cursor !* p3 has number of bytes transfered buffer no = stream_link buffer == buffer tab(buffer no) stream_cursor = stream_cursor+mess_p3; !add in length transfered if mess_p3>=buffer_length or stream_state#active start !end of buffer or buffer disabled if stream_cursor>=(stream_length+1) start ; !end of section (wrap round or suspend) if stream_mode&15=circular then stream_cursor = mess_p3-buffer_length { only OPERs can wrap round } else c stream_state = suspending finish if stream_state=active start ; !check not disabled (suspend or abort) if status&1#0 start ; !get the next page. release page(no drum update); !release page to get another get page(stream no) return finish else ; !disabled (suspend or abort) release buffer(no drum update) release block ->update adaptor finish finish if status&2=0 then temp = drum update else temp = no drum update release buffer(temp) stream_state = enabled if status&4#0 start ; !update users cursor mess_dserv = stream_owner mess_sserv = dserv mess_p2 = stream_cursor mess_p3 = 0; !must be zero otherwise signifies an abort pon(mess) finish return update adaptor: !send low level control info to the adaptor mess = 0 mess_p1 = sub ident(stream_state)!stream no mess_p2 = stream_state<<24!stream_mode<<16!(stream_length&x'FFFF') mess_p3 = stream_cursor mess_p4 = stream index(stream no); ! when connecting, set to cell mess_p5 = stream_device no; ! for OPER mess_p6 = stream_length mess_sserv = dserv pon adaptor: mess_dserv = send control!adapt(stream_adaptor no) if multi ocp=no and stream_adaptor no=fe then mk1 fe adaptor(mess) else pon(mess) return com(control msg6 reply): !** adaptor reply: control message 6 reply if mon level&256#0 start ; !include harvesting? harvest(24, proc no, 12, stream no, dact-12, stream_state, 0, 0) if c trace=yes and trace events&(1<<24)#0 and (trace process=-1 or trace process=proc no) finish if status=successful then return else ->reply to caller; !?????? com(control msg7 reply): !** adaptor reply: control message 7 reply if mon level&256#0 start ; !include harvesting? harvest(24, proc no, 12, stream no, dact-12, stream_state, 0, 0) if c trace=yes and trace events&(1<<24)#0 and (trace process=-1 or trace process=proc no) finish return reply to caller: stream no = -1 return cell: mess_sserv = dserv mess_dserv = stream_caller mess_p2 = status mess_p6 = mess_p3; !see connect reply, disable reply, disconnect reply mess_p3 = stream_owner mess_p4 = stream_state mess_p5 = stream_cursor pon(mess) unless stream no<0 start return pp cell(stream index(stream no)) stream index(stream no) = empty finish return routine release page(integer drum up) !*********************************************************************** !* * !* send a page out request to the global controller. specifying * !* whether the drum copy is to be updated, whether the page has been * !* written to and whether the page is eligible for recapture. * !* no reply is given by the global controller * !* bit significance: * !* 2**0 - recapturable * !* 2**1 - make new * !* 2**2 - drum update * !* 2**3 - written to * !* * !*********************************************************************** record (pe) mess mess = 0 mess_dserv = free page mess_sserv = dserv mess_p1 = buffer_amt index; !index<<16!epage mess_p2 = (1-(stream no&1))<<3!drum up if mon level&256#0 start ; !include harvesting? harvest(25, proc no, 12, stream no, mess_p1, mess_p2, 0, 0) if c trace=yes and trace events&(1<<25)#0 and (trace process=-1 or trace process=proc no) finish pon(mess) end ; !of routine release page routine release buffer(integer drum up) !*********************************************************************** !* * !* release a comms page from its allocation. if a transfer request is * !* queued request another page. * !* * !*********************************************************************** integer tempst no release page(drum up) buffer_stream no = empty stream_link = empty if comms_queued stream head#empty start ; !page frame required tempst no = unlink stream(comms_queued stream head) stream tab(stream index(tempst no))_link = buffer no if monlevel&4#0 then tempst no = tempst no!queued flag<<24 get page(tempst no) else ; !return buffer to free list buffer_link = comms_next free buffer comms_next free buffer = buffer no if monlevel&4#0 then free buffers = free buffers+1 finish end ; !of routine release buffer integer fn unlink stream(integer tempst no) !*********************************************************************** !* * !* remove a queued transfer from the linked list * !* * !*********************************************************************** integer stream id, stream link, link record (sr) name tempst if mon level&256#0 start ; !include harvesting? harvest(28, proc no, 4, tempst no, 0, 0, 0, 0) if c trace=yes and trace events&(1<<28)#0 and (trace process=-1 or trace process=proc no) finish if monlevel&4#0 then free buffers = free buffers+1 link = stream tab(stream index(tempst no))_link stream tab(stream index(tempst no))_link = empty if comms_queued stream head=tempst no start comms_queued stream head = link comms_queued stream tail = empty if comms_queued stream head=empty else stream link = comms_queued stream head until stream link=tempst no cycle stream id = stream link tempst == stream tab(stream index(stream id)) stream link = tempst_link repeat tempst_link = link comms_queued stream tail = stream id if comms_queued stream tail=tempst no finish result = tempst no end ; !of integerfn unlink stream routine get page(integer tempst no) !*********************************************************************** !* * !* calculate the next page required and request it from the global * !* controller. * !* * !*********************************************************************** record (pe) mess record (sr) name tempst integer epage, length, offset, esize if monlevel&4#0 start integer queued flag finish if monlevel&4#0 then queued flag = tempst no>>24 and tempst no = tempst no&short mask tempst == stream tab(stream index(tempst no)) tempst_state = paging in esize = com area_epagesize<<10; !calculate the epage size in bytes offset = tempst_cursor+tempst_start; !offset in the file section epage = offset//esize; !calculate in which page offset = offset-epage*esize; !offset in page length = (tempst_length+1)-tempst_cursor; !calculate length length = esize-offset if offset+length>esize !length within page buffer_stream no = tempst no buffer_external stream no = tempst_external stream no buffer_amt index = tempst_amt index!epage buffer_offset = offset buffer_length = length mess = 0 mess_dserv = claim page if monlevel&4#0 then mess_sserv = page claimed!queued flag else mess_sserv = page claimed mess_p1 = buffer_amt index mess_p2 = tempst no if mon level&256#0 start ; !include harvesting? harvest(29, proc no, 12, stream no, mess_p1, buffer_length, 0, 0) if c trace=yes and trace events&(1<<29)#0 and (trace process=-1 or trace process=proc no) finish pon(mess) end ; !of routine get page routine release block !*********************************************************************** !* * !* release the page and buffer if the stream has one and then release * !* the file section from the active memory table * !* * !*********************************************************************** record (pe) mess mess = 0 mess_dserv = free block mess_sserv = dserv mess_p1 = stream no mess_p2 = stream_amt index>>16 if mon level&256#0 start ; !include harvesting? harvest(26, proc no, 8, stream no, mess_p2, 0, 0, 0) if c trace=yes and trace events&(1<<26)#0 and (trace process=-1 or trace process=proc no) finish pon(mess) stream_amt index = not allocated end ; !of routine release block end ; !of routine comms control external routine mk1 fe adaptor(record (pe) name mess) !*********************************************************************** !* service 57 (x39) * !* drives the mk1 front end to comms controller spec * !* can manage up to 10 front ends (fe0-fe9) using a 512 byte area * !* for each which is provided on allocation * !*********************************************************************** if sseries=no start record format daf(integer lst0, lst1, rcb0, rcb1, rcb2, rcb3, rcb4, rcb5, rcb6, rcb7, lb0, lb1, al00, al01, al10, al11, stream, name, cob start, cob index, head, tail, ident, co head, co tail, max qd transfers, max qd control msgs, total qd transfers, total qd control msgs, bytes of control input, bytes of control output, bytes of data input, bytes of data output, control input transfers, control output transfers, data input transfers, data output transfers, output updates, update overwrites, control input xbit set, control output xbit set, data input xbit set, data output xbit set, byte integer attn set, spare, device no, failures, byte integer array cib, cob(0:167), integer array buffqsize(0:20)) else record format daf(integer tcb0 command, tcb0 ste, tcb0 length, tcb0 address, tcb0 next tcb, tcb0 response, integer array tcb0 preamble, tcb0 postamble(0:3), integer tcb1 command, tcb1 ste, tcb1 length, tcb1 address, tcb1 next tcb, tcb1 response, integer array tcb1 preamble, tcb1 postamble(0:3), integer stream, name, cob start, cob index, head, tail, ident, co head, co tail, max qd transfers, max qd control msgs, total qd transfers, total qd control msgs, bytes of control input, bytes of control output, bytes of data input, bytes of data output, control input transfers, control output transfers, data input transfers, data output transfers, output updates, update overwrites, control input xbit set, control output xbit set, data input xbit set, data output xbit set, byte integer attn set, spare, device no, failures, byte integer array cib, cob(0:167)) finish record format gpcf(integer ser, gptsm, propaddr, ticks, caa, grcb ad, lba, ala, state, resp0, resp1, sense1, sense2, sense3, sense4, repsno, base, id, dlvn, mnemonic, entsize, paw, usaw0, urcb ad, sense ad, logmask, trt ad, ua size, ua ad, timeout, props0, props1) record format cr(integer message ident, p2, p3, p4, p5, p6) record format oper command f(integer dest, srce, string (23) txt) own integer array device to da ad(0:9)= c not allocated(10) own integer timer started const integer cib length= 168; !7 control messages const integer cob length= 168 const integer max failures= 5 const byte integer array retry delay(0:max failures)= c 0,1,0,2,0,3 const integer control msg length= 24 const integer control msg descriptor= control msg length!x'18000000' const integer control in msg descriptor= control msg descriptor - 4 const integer idle= x'F0F0F0F0' const integer control in=-2 const integer control out=-1 const integer data stream= 0 if sseries=no start const integer long block mask= x'0400' const integer x mask= x'0200' const integer y mask= x'0100' else const integer long block mask= x'04000000' const integer x mask= x'02000000' const integer y mask= x'01000000' finish integer stream no, buffer no, device no integer temp, length, message ident, s ident, search index, cib index, dact, control addr, l, f, t, x, interrupt analysis flags, xbit set record (pe) gpc req record (pe) cmes record (gpcf) name gpc entry record (daf) name device area record (sr) name stream record (br) name buffer record (cr) name control record (parmf) name parm cell record (oper command f) oper command record (pe) name oc switch act(0:deallocate request) const integer testing=yes routine start device(integer stream no, ext stream no, real address, start, length) if sseries=no start const integer array lb mask(0:1)= c x'00F00202', x'80F00302' else const integer array lb mask(0:1)= c x'2F004002', x'2F004083' finish if sseries=no start device area_lst1 = x'80000001'!real address device area_lb1 = lb mask(stream no&1) device area_al10 = length device area_al11 = start else device area_tcb0 response = 0 device area_tcb1 command = lb mask(stream no&1) if real address=0 then device area_tcb1 ste = device area_tcb0 ste else c device area_tcb1 ste = real address!1 device area_tcb1 length = length device area_tcb1 address = start device area_tcb1 response = 0 finish device area_stream = stream no device area_name = ext stream no<<16!length gpc req = 0 gpc req_dserv = execute chain gpc req_sserv = mk1 fe executed if sseries=no start gpc req_p1 = addr(device area_rcb0) else gpc req_p1 = addr(device area_tcb0 command) finish gpc req_p2 = device area_ident if sseries=no start gpc req_p3 = x'11'; !paw function<<4!saw flags finish !! %if monlevel&2#0 %and (kmon>>mk1 fe service)&1#0 %then !! printstring("FE: start device, st=".strint(stream no)." xst=".strint(ext stream no)." len=".strint(length).snl) if multi ocp=yes then pon(gpc req) else gdc(gpc req) end ; !of routine start device routine queue !* queue a high or low level control message for output as the cob is full integer temp temp = new pp cell parm cell == parm tab(temp) control addr = addr(parm cell_p1) if device area_co head=empty then device area_co head = temp else parm tab(device area_co tail)_link = temp parm cell_link = empty device area_co tail = temp if mon level&4#0 start device area_total qd control msgs = device area_total qd control msgs+1 l = device area_co head f = 1 while l#device area_co tail cycle f = f+1 l = parm tab(l)_link repeat if f>device area_max qd control msgs then device area_max qd control msgs = f finish end ; !of routine queue routine log event(string (63) text, record (pe) p) op mess("FE".tostring(device no+'0').": ".text) pkmon rec("Mk1 FE Adaptor:",p) dumptable(80,mess_p3,512) end ; !of log event !*********************************************************************** !* main program * !*********************************************************************** if mon level&2#0 and (kmon>>mk1 fe service)&1#0 then pkmon rec("Mk1 FE Adaptor: ", mess) dact = mess_dserv&short mask !***** ! nothing from DCU will make any sense 'till I redo DCU!!!! !****** if dact=interrupt start ; !from gpc mess_p3=gpc area adrress interrupt analysis flags = mess_p1>>20&15 gpc entry == record(mess_p3); !map device area device area == record(gpc entry_ua ad); !map private device area device no = device area_device no stream no = device area_stream else if go ahead<=dact<=send control start ; !from comms mess_p1=strm&short mask stream no = mess_p1&short mask temp = stream index(stream no) if temp=empty then return ; !can happen on fepdown stream == stream tab(temp) device no = stream_device no if device to da ad(device no)=not allocated start !send failure replies cmes = 0 if mess_p1&top short mask#0 then cmes_dserv = comms command!mess_p1>>16 else cmes_dserv = mess_sserv cmes_sserv = mess_dserv cmes_p1 = stream no cmes_p2 = 1 pon(cmes) return finish else device area == record(device to da ad(device no)) finish finish ->act(dact) act(0): !** print monitoring if collected if mon level&4#0 start if multi ocp=yes then reserve log for device no = 0, 1, 9 cycle if device to da ad(device no)#not allocated start device area == record(device to da ad(device no)) newlines(2) print string("FE".to string(device no+'0'). c " Log Bytes Transfers XBIT Set Bytes/Transfer") newline print string("Data Input ") f = device area_bytes of data input write(device area_bytes of data input, 10) t = device area_data input transfers write(device area_data input transfers, 10) x = device area_data input xbit set write(device area_data input xbit set, 10) write(device area_bytes of data input//device area_data input transfers, 10) if c device area_data input transfers#0 newline print string("Data Output ") f = f+device area_bytes of data output write(device area_bytes of data output, 10) t = t+device area_data output transfers write(device area_data output transfers, 10) x = x+device area_data output xbit set write(device area_data output xbit set, 10) write(device area_bytes of data output//device area_data output transfers, 10) if c device area_data output transfers#0 newline print string("Control Input ") f = f+device area_bytes of control input write(device area_bytes of control input, 10) t = t+device area_control input transfers write(device area_control input transfers, 10) x = x+device area_control input xbit set write(device area_control input xbit set, 10) write(device area_bytes of control input//device area_control input transfers, 10) if c device area_control input transfers#0 newline print string("Control Output ") f = f+device area_bytes of control output write(device area_bytes of control output, 10) t = t+device area_control output transfers write(device area_control output transfers, 10) x = x+device area_control output xbit set write(device area_control output xbit set, 10) write(device area_bytes of control output//device area_control output transfers, 10) if c device area_control output transfers#0 newline print string("Totals ") write(f, 10) write(t, 10) write(x, 10) write(f//t, 10) if t#0 newline print string("Output Updates ") write(device area_output updates, 10) newline print string("Update Overwrites") write(device area_update overwrites, 8) newline print string("Overwrite Ratio") if device area_update overwrites>0 then c write(device area_output updates//device area_update overwrites, 10) else write(0, 10) print string(":1") newline print string("Queued Transfers") write(device area_total qd transfers, 9) newline print string("Queued Control ") write(device area_total qd control msgs, 10) newline print string("Max Queued Trans") write(device area_max qd transfers, 9) newline print string("Max Queued Control") write(device area_max qd control msgs, 7) newlines(2) if sseries=no start t = 0 t = t+device area_buffqsize(l) for l = 0, 1, 20 printstring("Transfer queue sizes (idle =") if t=0 then printstring(" 100%)") else start write(device area_buffqsize(0)*100//t, 1) printstring("%)") newline write(f, 4) for f = 1, 1, 20 printstring("+") newline; space for f = 1, 1, 20 cycle write(device area_buffqsize(f)*100//t, 3) printstring("%") repeat finish newlines(2) device area_buffqsize(f) = 0 for f = 0, 1, 20 finish device area_max qd transfers = 0 device area_max qd control msgs = 0 device area_total qd transfers = 0 device area_total qd control msgs = 0 device area_bytes of control input = 0 device area_bytes of control output = 0 device area_bytes of data input = 0 device area_bytes of data output = 0 device area_control input transfers = 0 device area_control output transfers = 0 device area_data input transfers = 0 device area_data output transfers = 0 device area_output updates = 0 device area_update overwrites = 0 device area_control input xbit set = 0 device area_control output xbit set = 0 device area_data input xbit set = 0 device area_data output xbit set = 0 finish repeat if multi ocp=yes then release log finish return act(interrupt): !** device interrupt (normal, abnormal or attention) ->atten if interrupt analysis flags=attention ->abnormal termination unless interrupt analysis flags&normal termination=normal termination !** normal termination interrupt if device area_failures#0 start op mess("FE".to string(device no+'0')." Transfer Recovered") device area_failures = 0 finish if sseries=no start length = device area_al10-gpc entry_resp1&short mask !number of bytes transfered xbit set = gpc entry_resp0&x mask else length = device area_tcb1 length-device area_tcb1 response&short mask !number of bytes transfered xbit set = device area_tcb1 response&x mask finish !! %if monlevel&2#0 %and (kmon>>mk1 fe service)&1#0 %then !! printstring("FE: termination, st=".strint(stream no)." bytes=".strint(length)." xbit=".strint(xbit set).snl) if stream no=control out start ; !control output termination if length#(length//control msg length)*control msg length start if length#1 start ; !due to a quirk of the gpc 1 is not an error op mess("FE".tostring(device no+'0')." partial control msg output".tostring(17)) dump table(80, mess_p3, gpc entry_entsize) finish length = (length//control msg length)*control msg length finish if mon level&4#0 start device area_bytes of control output = device area_bytes of control output+length device area_control output transfers = device area_control output transfers+1 device area_control output xbit set = device area_control output xbit set+1 if xbit set#0 finish device area_cob index = device area_cob index-length !*mc move(device area_cob index,addr(device area_cob(length)), %c addr(device area_cob(0))) if device area_cob index > 0 !shuffle down? if device area_cob index>0 start l = device area_cob index f = addr(device area_cob(length)) t = addr(device area_cob(0)) *ldtb_x'18000000' *ldb_l *lda_f *cyd_0 *lda_t *mv_ l = dr finish device area_cob start = 0 while device area_co head#empty and device area_cob index<cob length cycle parm cell == parm tab(device area_co head) !*mc move(control msg length,addr(parm cell_p1),addr( %c device area_cob(device area_cob index))) t = addr(device area_cob(device area_cob index)) *lsd_parm cell *iad_8 *ldtb_control msg descriptor *lda_t *mv_ l = dr device area_cob index = device area_cob index+control msg length temp = parm cell_link return pp cell(device area_co head) device area_co head = temp device area_co tail = empty if temp=empty repeat ->make idle finish if stream no=control in start ; !control input termination if length#(length//control msg length)*control msg length start if length#1 start ; !due to a quirk of the gpc not an error if 1 op mess("FE".tostring(device no+'0')." partial control msg input".tostring(17)) dump table(80, mess_p3, gpc entry_entsize) finish length = (length//control msg length)*control msg length finish if mon level&4#0 start device area_bytes of control input = device area_bytes of control input+length device area_control input transfers = device area_control input transfers+1 device area_control input xbit set = device area_control input xbit set+1 if xbit set#0 finish cib index = 0 cmes_sserv = mess_dserv while cib index<length cycle control == record(addr(device area_cib(cib index))) temp = control_message ident s ident = temp&short mask stream no = temp>>16 if 0<=stream no<=max stream and stream index(stream no)#empty start !valid stream? cmes_p1 = stream no !*mc move(control msg length-4,addr(control_p2),addr(cmes_p2)) t = addr(cmes_p2) *lsd_control *iad_4 *ldtb_control in msg descriptor *lda_t *mv_ l = dr if s ident=0 then cmes_dserv = stream tab(stream index(stream no))_owner else c cmes_dserv = comms command!s ident !! %if monlevel&2#0 %and (kmon>>mk1 fe service)&1#0 %then pkmon rec("FE pons:", cmes) if testing=yes start stream == stream tab(stream index(stream no)) if stream_adaptor no#fe or stream_device no#device no or (0#s ident#transfer requested and c sub ident(stream_state)#s ident<<16) start pkmonrec("Comms: stream ", record(addr(stream))) log event("bad low level con".tostring(17), cmes) finish else pon(cmes) finish else pon(cmes) else print string("Mk1 FE Adaptor: FE".to string(device no+'0')." Bad stream number ".strint(stream no).snl) finish cib index = cib index+control msg length repeat ->make idle finish if mon level&4#0 start if stream no&1=1 start ; !output device area_bytes of data output = device area_bytes of data output+length device area_data output transfers = device area_data output transfers+1 device area_data output xbit set = device area_data output xbit set+1 if xbit set#0 else device area_bytes of data input = device area_bytes of data input+length device area_data input transfers = device area_data input transfers+1 device area_data input xbit set = device area_data input xbit set+1 if xbit set#0 finish finish cmes = 0 cmes_sserv = mess_dserv cmes_dserv = transfer complete cmes_p1 = stream no if sseries=no start cmes_p2 = 3 if gpc entry_resp0&long block mask#0 !next page required and dont recapture else cmes_p2 = 3 if device area_tcb1 response&long block mask#0 !next page required and dont recapture finish cmes_p3 = length if sseries=no start temp = gpc entry_resp0&y mask; !send high level control message to signal end of message else temp = device area_tcb1 response&y mask; !send high level control message to signal end of message finish cmes_p2 = cmes_p2!4 if temp#0 pon(cmes) ->make idle abnormal termination: !** abnormal termination interrupt print string("Mk1 FE Adaptor: Abterm FE".to string(device no+'0')." ".strint(device area_failures).snl) pkmon rec("Mk1 FE Adaptor: ", mess) if device area_failures<=max failures start !try again? gpc req = 0 gpc req_dserv = execute chain gpc req_sserv = mk1 fe executed if sseries=no start gpc req_p1 = addr(device area_rcb0) else gpc req_p1 = addr(device area_tcb0 command) finish gpc req_p2 = device area_ident if sseries=no start gpc req_p3 = x'11'; !paw function<<4!saw flags finish dpon(gpc req, retry delay(device area_failures)) !try again in ? seconds device area_failures = device area_failures+1 else down: op mess("FE".to string('0'+device no)." DOWN".to string(17)); !flash dump table(80,mess_p3,gpc entry_ent size) if stream no>=data stream start ; !fail transfer in progress cmes = 0 cmes_dserv = transfer complete cmes_sserv = mess_dserv cmes_p1 = stream no cmes_p2 = 2; !dont recapture pon(cmes) finish while device area_head#empty cycle ; !fail all queued transfers buffer == buffer tab(device area_head) device area_head = buffer_link device area_tail = empty if device area_head=empty cmes = 0 cmes_dserv = transfer complete cmes_sserv = mess_dserv cmes_p1 = buffer_stream no cmes_p2 = 2; !dont recapture pon(cmes) repeat for stream no = 0, 1, max stream cycle if stream index(stream no)#empty start stream == stream tab(stream index(stream no)) if stream_adaptor no=fe and stream_device no=device no start !connected thru this front end cmes = 0 cmes_sserv = mess_dserv cmes_p1 = stream no if stream no&1=0 and sub ident(stream_state)=-1 start !input stream not waiting on a reply cmes_dserv = stream_owner; !then send a high level control msg cmes_p2 = stream_cursor cmes_p3 = x'01590000'; !control (e o t) y pon(cmes) else ; !waiting on a reply if sub ident(stream_state)#-1 start cmes_dserv = comms command!sub ident(stream_state)>>16 !simulate one cmes_p2 = 1; !failure pon(cmes) finish finish finish finish repeat device area_stream = idle device area_cob start = 0 device area_cob index = 0 device area_failures = 0 gpc req = 0 gpc req_dserv = deallocate device gpc req_sserv = mk1 fe deallocated gpc req_p1 = m'FE0'!device no pon(gpc req) device to da ad(device no) = not allocated if com area_ipldev<0 start oper command_dest = oper command req oper command_srce = mk1 fe oper com reply oper command_txt = "FEPUP 0" oc == oper command oc_p2 = oc_p2!device no dpon(oper command, 600); !after 10 mins finish finish return atten: !** attention interrupt !!%if monlevel&2#0 %and (kmon>>mk1 fe service)&1#0 %then printstring("FE: atten from device".snl) if mess_p1&x'1FF00'=x'08000' start ; !expected attn ? if stream no=idle start if device to da ad(device no)=not allocated start ! mess_dserv = x'32000b' !flash off ! pon(mess) op mess("FE".to string(device no+'0')." UP") device to da ad(device no) = addr(device area) finish make idle: device area_stream = idle if interrupt analysis flags=attention or xbit set#0 or device area_attn set#0 start !control input pending device area_attn set = 0 start device(control in, control in, 0, addr(device area_cib(0)), cib length) return finish if device area_cob index>0 start ; !control output pending. start device(control out, control out, 0, addr(device area_cob(0)), device area_cob index) device area_cob start = device area_cob index return finish if device area_head#empty start ; !data transfer queued buffer == buffer tab(device area_head) device area_head = buffer_link device area_tail = empty if device area_head=empty start device(buffer_stream no, buffer_external stream no, buffer_real address, buffer_offset, buffer_length) return finish finish else device area_attn set = 1 else op mess("FE".to string(device no+'0')." Unexpected attention") pkmon rec("Mk1 FE Adaptor: ", mess) finish return act(deallocate request): !** operator can now invoke reallocation following ! a disconnect/reconnect sequence by gpc device no = mess_p1 if 0<=device no<=9 and device to da ad(device no)#not allocated start device area == record(device to da ad(device no)) stream no = device area_stream ->down finish opmess("FE".tostring(device no+'0')." not allocated") return act(deallocated): !** reply from gpc if mess_p1#0 then opmess("FE: Dealloc fails ".strint(mess_p1&short mask)) return act(allocate request): !** operator request to recover fe device no = mess_p1 if device to da ad(device no)=not allocated start gpc req = 0 gpc req_dserv = allocate device gpc req_sserv = mk1 fe allocated gpc req_p1 = m'FE0'!device no gpc req_p2 = mk1 fe interrupt pon(gpc req) finish else opmess("FE".tostring(device no+'0')." Already allocated") return act(allocated): !** device allocated by gpc if mess_p1=0 start ; !allocated gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) device no = gpc entry_mnemonic&byte mask-'0' device to da ad(device no) = gpc entry_ua ad if sseries=no start device area_lst0 = x'02201000'; !local segment table entry r2, w2, max len 1 epage device area_lst1 = x'00000000' device area_rcb0 = x'00000002'; !rcb flags etc device area_rcb1 = realise(addr(device area_lst0)) !segment table (real address) device area_rcb2 = 8; !logic block byte count device area_rcb3 = addr(device area_lb0); !logic block address (virtual) device area_rcb4 = 16; !address list byte count device area_rcb5 = addr(device area_al00) !address list address (virtual) device area_rcb6 = 0; !unused device area_rcb7 = 0; !unused device area_lb0 = x'84300500'; !write control (ext stream no)<<16!length device area_lb1 = x'00000000'; !read or write data device area_al00 = 4; !length of ext stream no and length field device area_al01 = addr(device area_name) !address of stream field device area_al10 = 0; !length of read / write area device area_al11 = 0; !address of read / write area else com area == record(x'80000000'!48<<18) device area_tcb0 command = x'2F404085'; !write control chained device area_tcb0 ste = integer(addr(device area)<<1>>19<<3+com area_pstva+4)!1 device area_tcb0 length = 4 device area_tcb0 address = addr(device area_name) device area_tcb0 next tcb = addr(device area_tcb1 command) device area_tcb0 response = 0 device area_tcb1 command = 0 device area_tcb1 ste = 0 device area_tcb1 length = 0 device area_tcb1 address = 0 device area_tcb1 next tcb = 0 device area_tcb1 response = 0 finish device area_ident = mess_p2 device area_name = 0; !control information sent written here device area_stream = idle device area_cob start = 0 device area_cob index = 0 device area_head = empty device area_tail = empty if mon level&4#0 start device area_max qd transfers = 0 device area_max qd control msgs = 0 device area_total qd transfers = 0 device area_total qd control msgs = 0 device area_bytes of control input = 0 device area_bytes of control output = 0 device area_bytes of data input = 0 device area_bytes of data output = 0 device area_control input transfers = 0 device area_control output transfers = 0 device area_data input transfers = 0 device area_data output transfers = 0 device area_output updates = 0 device area_update overwrites = 0 device area_control input xbit set = 0 device area_control output xbit set = 0 device area_data input xbit set = 0 device area_data output xbit set = 0 finish device area_co head = empty device area_co tail = empty device area_attn set = 0 device area_device no = device no device area_failures = 0 finish if timer started=no start mess = 0 mess_dserv = elapsed int command; !tick every n secs mess_sserv = mk1 fe clock tick mess_p1 = mk1 fe clock tick mess_p2 = secs per tick if multi ocp=no then elapsed int(mess) else pon(mess) timer started = yes finish return act(execute fails): !** execute chain fails gpc entry == record(mess_p3) dump table(80, mess_p3, gpc entry_ent size) device area == record(gpc entry_ua ad) device no = device area_device no stream no = device area_stream ->abnormal termination; ! treat as abterm for better diagnostics !* above entries are called by gpc !* below entries are called by comms control act(go ahead): !** transfer go ahead buffer no = mess_p2 buffer == buffer tab(buffer no) if device area_stream#idle start ; !queue a transfer buffer_link = empty if device area_head=empty then device area_head = buffer no else c buffer tab(device area_tail)_link = buffer no device area_tail = buffer no if mon level&4#0 start device area_total qd transfers = device area_total qd transfers+1 l = device area_head f = 1 while l#device area_tail cycle f = f+1 l = buffer tab(l)_link repeat if f>device area_max qd transfers then device area_max qd transfers = f f = 20 if f>20 finish else start device(stream no, buffer_external stream no, buffer_real address, buffer_offset, buffer_length) if monlevel&4#0 then f = 0 finish if monlevel&4#0 and sseries=no then device area_buffqsize(f) = device area_buffqsize(f)+1 return act(send control): !** control message message ident = stream_external stream no<<16!mess_p1>>16 mess_p1 = message ident !!%if monlevel&2#0 %and (kmon>>mk1 fe service)&1#0 %then pkmon rec("FE co msg:", mess) if message ident&short mask=0 and mess_sserv&short mask=control msg7 reply start !high level and update message device area_output updates = device area_output updates+1 if mon level&4#0 search index = device area_cob start cycle control addr = addr(device area_cob(0))+search index if search index>=device area_cob index start !end of whats in buffer? if device area_cob index>=cob length start !cob buffer full temp = device area_co head while temp#empty cycle ; !scan down existing queue parm cell == parm tab(temp) if message ident=parm cell_p1 start !update? device area_update overwrites = device area_update overwrites+1 if mon level&4#0 control addr = addr(parm cell_p1) ->out finish else temp = parm cell_link repeat queue out: finish else device area_cob index = device area_cob index+control msg length exit finish if integer(control addr)=message ident start device area_update overwrites = device area_update overwrites+1 if mon level&4#0 exit finish search index = search index+control msg length repeat ! cmes = 0 ! cmes_dserv = mess_sserv ! cmes_sserv = mess_dserv ! cmes_p1 = stream no ! %if multi ocp = no %then comms control(cmes) %else pon(cmes) !reply message updated else if device area_cob index<cob length start !cob buffer full? control addr = addr(device area_cob(device area_cob index)) device area_cob index = device area_cob index+control msg length finish else queue finish !*mcmove(control msg length,addr(mess_p1),control addr) *lsd_mess *iad_8 *ldtb_control msg descriptor *lda_control addr *mv_ l = dr start device(control out, control out, 0, addr(device area_cob(0)), device area_cob index) and c device area_cob start = device area_cob index if device area_stream=idle return act(clock tick): !send control msg every n secs to confirm fes are up for device no = 0, 1, 9 cycle unless device to da ad(device no)=not allocated start device area == record(device to da ad(device no)) if device area_cob index=0 and device area_stream=idle start mess = 0 mess_p1 = -1; !dummy stream no for fe to ignore control addr = addr(device area_cob(0)) device area_cob index = control msg length !*mcmove(control msg length,addr(mess_p1),control addr) *lsd_mess *iad_8 *ldtb_control msg descriptor *lda_control addr *mv_ l = dr start device(control out, control out, 0, addr(device area_cob(0)), control msg length) device area_cob start = control msg length finish finish repeat return act(oper com reply): !ignore reply to fepup message !! end ; !of routine mk1 fe adaptor external routine lp adaptor(record (pe) name mess) !*********************************************************************** !* service 51 (x33) * !* drives the line printer to comms controller spec * !* accepts the data as iso, ebcidic or binary. iso is translated to * !* ebcidic using the master translate tables. ebcidic data is * !* translated to a subset of ebcidic depending on the rep in the * !* printer to avoid non printing characters fouling things up. * !* binary data is left alone. * !* can manage up to 10 printers (lp0-lp9) using a 128 byte area for * !* each which is provided on allocation * !*********************************************************************** record format gpcf(integer last dest, gptsm, propaddr, ticks, caa, grcb ad, lba, (integer last tcb addr or integer ala), integer state, resp0, resp1, sense1, sense2, sense3, sense4, repsno, base, id, dlvn, mnemonic, entsize, paw, usaw0, urcb ad, sense ad, logmask, trt ad, ua size, ua ad, timeout, props0, props1) if sseries=no start record format daf(integer lst0, lst1, rcb0, rcb1, rcb2, rcb3, rcb4, rcb5, rcb6, rcb7, lb0, lb1, lb2, al00, al01, stream no, mode, buffer no, trt ad, ident) else record format daf(half integer stream no, buffer no, integer trt ad, addr tcb, byte integer mode, ident, device no, s, integer tcb0 command, tcb0 ste, tcb0 length, tcb0 address, tcb0 next tcb, tcb0 response, (integer tcb0 fragment length, tcb0 nulls or integer array tcb0 preamble, tcb0 postamble(0:3)), integer tcb1 command, tcb1 ste, tcb1 length, tcb1 address, tcb1 next tcb, tcb1 response, integer array tcb1 preamble(0:3), tcb1 postamble(0:2), byte integer array chars(0:131)) finish routine spec reply to comms control(integer act, stream no, flag, count) routine spec get next buffer own integer array device to da ad(0:9)= c not allocated(10) own byte integer array pagesize(0:9); ! set on connect own byte integer array countsw(0:9); ! guards against counting same epage twice own integer array pagecount(0:9); ! count of pages since last connect own byte integer array linecount(0:9); ! count of lines on current page own integer array fefftab(0:7)= x'00080400',x'E0000000',0(6) ! ff,nl,msp,mnl,vp ! %owninteger last allocated to other = -1; !who last had device record (pe) req record (pe) rep record (gpcf) name gpc entry record (daf) name device area record (sr) name stream record (br) name buffer integer stream no, buffer no, state, start, sstart, p, l, s integer buffer end, sent, device no, act integer i, char, line, long line, line begin, trt ad, interrupt analysis flags long integer savedr, saveacc switch dact(0:deallocate request) switch states(0:active) if mon level&2#0 and (kmon>>lp service)&1#0 start pkmon rec("LP Adaptor: ", mess) finish act = mess_dserv&short mask if act=interrupt start ; !from gpc mess_p3=gpc area adrress interrupt analysis flags = mess_p1>>20&15 gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) stream no = device area_stream no else if go ahead<=act<=send control start ; !from comms mess_p1=strm&short mask stream no = mess_p1&short mask stream == stream tab(stream index(stream no)) device no = stream_device no finish finish ->dact(act) dact(interrupt): !** device interrupt (normal, abnormal, attention) unless interrupt analysis flags=attention start if sseries=no start i = (device area_rcb3-addr(device area_lb0))>>2+gpc entry_resp0&byte mask else if interrupt analysis flags&normal termination=0 and c gpc entry_last tcb addr=addr(device area_tcb0 command) then i = 0 else i = 2 finish if interrupt analysis flags&normal termination#normal termination and c mess_p4&x'FF'=normal termination<<4 and integer(gpc entry_sense ad)=x'20000004' then c long line = yes else long line = no if i=2 and mess_p2#-1 start ; !on the write data and not a time out if sseries=no start sent = device area_al00-gpc entry_resp1&short mask else sent = device area_tcb1 length-device area_tcb1 response&short mask+device area_tcb0 fragment length+dev c ice area_tcb0 nulls finish if long line=no start ; !was it a long line then no need to scan back if sent>0 start buffer == buffer tab(device area_buffer no) start = virtual+buffer_real address+buffer_offset buffer end = start+sent-1 if sent>133 then line begin = buffer end-132 else line begin = start line = buffer end-line begin+1; !in case preceeding line was long for i = buffer end, -1, line begin cycle char = byte integer(i) if (sseries=yes and char=x'07') or char=ebc nl or char=ebc cr or char=ebc ff then c line = buffer end-i and exit repeat !set up chain to merge the end of the line onto the head if line=0 start if sseries=no start device area_rcb3 = addr(device area_lb2) device area_lb2 = x'80700300' else device area_addr tcb = addr(device area_tcb1 command) finish else if sseries=no start device area_rcb3 = addr(device area_lb0) device area_lb1 = x'8A700000'!line device area_lb2 = x'80700000' else device area_addr tcb = addr(device area_tcb0 command) device area_tcb0 length = line for i = 0, 1, line cycle ; ! protem - try MSP device area_chars(i) = ebc sp repeat finish finish finish else ; !tag on a new line if sseries=no start device area_rcb3 = addr(device area_lb1) device area_lb1 = x'8A700300'!ebc nl device area_lb2 = x'80700000' else device area_addr tcb = addr(device area_tcb0 command) device area_tcb0 length = 1 device area_chars(0) = ebc nl finish stream == stream tab(stream index(stream no)) device no = stream_device no if pagesize(device no)#0 then start linecount(device no) = linecount(device no)+1 while linecount(device no)>=pagesize(device no) cycle pagecount(device no) = pagecount(device no)+1 linecount(device no) = linecount(device no)-pagesize(device no) repeat countsw(device no) = yes; ! avoid counting the buffer again finish sent = sent-1; !send the char that caused the long line again finish finish else sent = 0 !send reply to comms control req = 0 req_sserv = mess_dserv req_dserv = transfer complete req_p1 = stream no req_p2 = 3; !get next page and dont recapture if interrupt analysis flags&normal termination=0 and long line=no start req_p2 = req_p2!4; !inform user req_p5 = integer(gpc entry_sense ad) req_p6 = integer(gpc entry_sense ad+4) finish req_p3 = sent pon(req) get next buffer if long line=yes and interrupt analysis flags&normal termination=0 else !** attention interrupt if mess_p1&auto=auto then start if stream index(stream no)#empty start stream == stream tab(stream index(stream no)) device no = stream_device no countsw(device no) = yes; ! remember, to avoid counting buffer twice finish get next buffer finish finish return dact(execute fails): !** execute chain fails gpc entry == record(mess_p3) dump table(81, mess_p3, gpc entry_ent size) return dact(allocated): !** device allocated by gpc if mess_p1=0 start ; !allocated gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) device no = gpc entry_mnemonic&byte mask-'0' device to da ad(device no) = gpc entry_ua ad ! %if gpc entry_last dest # lp interrupt %then last allocated to other = %c ! last allocated to other ! 1<<device no device area_ident = mess_p2; !save gpc identifier device area_mode = 0; !iso by default device area_stream no = mess_p5 device area_buffer no = 0 device area_trt ad = gpc entry_trt ad if sseries=no start device area_lst0 = x'00F01000'; !local segment table entry r15 max len 1 epage device area_lst1 = 0 device area_rcb0 = x'0000000F'; !rcb flags etc device area_rcb1 = realise(addr(device area_lst0)) device area_rcb2 = 12; !logic block byte count device area_rcb3 = addr(device area_lb2); !logic block address device area_rcb4 = 8; !address_list byte count device area_rcb5 = addr(device area_al00) !address list address device area_rcb6 = 0; !unused device area_rcb7 = 0; !unused device area_lb0 = x'8A700300'!ebc ms device area_lb1 = x'8A700000'; !number of spaces device area_lb2 = x'80700300'; !write buffer device area_al00 = 0; !buffer length device area_al01 = 0; !buffer offset else device area_tcb0 command = x'27404083' device area_tcb0 ste = integer(addr(device area)<<1>>19<<3+com area_pstva+4)!1 device area_tcb0 length = 0 device area_tcb0 address = addr(device area_chars(0)) device area_tcb0 next tcb = addr(device area_tcb1 command) device area_tcb1 command = x'27004083' device area_tcb1 ste = 0 device area_tcb1 length = 0 device area_tcb1 address = 0 device area_tcb1 next tcb = 0 device area_tcb0 response = 0 device area_addr tcb = addr(device area_tcb1 command) for i = 0, 1, 131 cycle device area_chars(i) = ebc sp repeat finish finish reply to comms control(mess_p4, mess_p5, mess_p1, 0) return dact(deallocate request): !** operator request to force deallocation device no = mess_p1 if 0<=device no<=9 and device to da ad(device no)#not allocated start device area == record(device to da ad(device no)) stream no = device area_stream no stream == stream tab(stream index(stream no)) if stream_state>=enabled start mess_dserv = stream_caller reply to comms control(disable, stream no, aborting, 0) finish if stream_state=connected start mess_dserv = stream_caller mess_p1 = stream no mess_p2 = 1; !fail the connect request pon(mess) reply to comms control(disconnect, stream no, 0, 0) finish mess_dserv = lp command!deallocate request dpon(mess, 4) finish else op mess("LP Adapt: device free") return dact(deallocated): !** device deallocated by gpc device no = mess_p6 device to da ad(device no) = not allocated reply to comms control(mess_p4, mess_p5, mess_p1, 0) return dact(go ahead): !** transfer go ahead buffer no = mess_p2 buffer == buffer tab(buffer no) device area == record(device to da ad(device no)) device area_buffer no = buffer no start = virtual+buffer_real address+buffer_offset sstart = start i to e(start, buffer_length) if device area_mode=iso if device area_mode#binary start ; !translate it to the ebcidic characters in the current rep i = buffer_length trt ad = device area_trtad if byteinteger(start)=ebc vp and device area_mode=ebcidic and i>1 start !do not translate vp and qualifier i = i-2 start = start+2 finish *lb_i; *jat_14, <l99> *ldtb_x'18000000'; *ldb_ b ; *lda_start *lss_trt ad; *luh_x'18000100' *ttr_ l = dr l99: finish s = pagesize(device no); ! make local copies for efficiency ->trans if s=0 if countsw(device no)=yes then start ; ! this epage already counted countsw(device no) = no ->trans finish p = pagecount(device no) l = linecount(device no) *mpsr_x'0480'; ! set bound check inhibit so that ! modd will work sensibly i = buffer_length *lss_fefftab+4; ! address of format effector table *luh_0; ! form bit descriptor *st_saveacc; ! for subsequent scans *lda_sstart; ! form descriptor to buffer *ldtb_x'18000000' *ldb_i next: !* start of main loop *jat_11, <done>; ! jzdl - end of buffer *lsd_saveacc; ! descriptor to bitmap *tch_ l = dr ; ! scan for format effector *jat_11, <done>; ! jzdl - not found *std_savedr; ! for next scan *lss_(dr ); ! examine character *icp_ebc nl *jcc_6, <notnl> *lss_1 *j_<add> notnl: *icp_ebc ff *jcc_6, <notff> p = p+1; ! increment page count l = 0; ! reset line count ->loop notff: *modd_1; ! advance to qualifier *jat_11, <done>; ! unlikely - no qualifier *std_savedr *icp_ebc mnl *jcc_6, <notmnl> *lss_(dr ); ! number of lines *j_<add> notmnl: *icp_ebc ms; ! multiple space - process but ignore *jcc_8, <loop>; ! j if is ms notms: *icp_ebc vp *jcc_6, <notvp> *lss_(dr ); ! get destination line number *st_i if i>l then l = i if i=0 then p = p+1 and l = 0; ! treat as formfeed ->loop notvp: monitor("BAD FORMAT EFFECTOR") add:*iad_l *st_l while l>=s cycle l = l-s; ! decrement line count p = p+1; ! increment page count repeat loop: *ld_savedr *modd_1; ! advance to next character *j_<next> done: pagecount(device no) = p; ! copy back the counts linecount(device no) = l *mpsr_x'FB40'; ! clear bound check inhibit trans: !and initiate the transfer if sseries=no start device area_lst1 = x'80000001'!buffer_real address device area_al00 = buffer_length device area_al01 = buffer_offset else l = 0 p = 0 if device area_addr tcb=addr(device area_tcb0 command) and device area_chars(0)=ebc sp start ! fragment to tack on l = device area_tcb0 length i = l while i<132 and i-l+p<buffer_length cycle char = byteinteger(sstart) exit if char=ebc nl or char=ebc cr or char=ebc ff device area_chars(i) = char sstart = sstart+1 if char=x'07' then p = p+1 else i = i+1; ! omit nulls repeat l = i-l device area_tcb0 length = i device area_tcb1 length = buffer_length-l-p if device area_tcb1 length<=0 start ; ! file must be corrupt opmess("Corrupt print file!!!") if multi ocp=yes then reserve log printstring("LP adaptor - corrupt print file:") dumptable(0, buffer_real address+x'81000000', buffer_length) if multi ocp=yes then release log req = 0; ! tell Comms Controller req_sserv = mess_dserv req_dserv = transfer complete req_p1 = device area_stream no pon(req) return finish device area_tcb1 address = buffer_offset+l+p else device area_tcb1 length = buffer_length device area_tcb1 address = buffer_offset finish device area_tcb0 fragment length = l device area_tcb0 nulls = p device area_tcb0 response = 0 device area_tcb1 response = 0 device area_tcb1 ste = buffer_real address!1 finish req = 0 req_dserv = execute chain req_sserv = lp executed if sseries=no start req_p1 = addr(device area_rcb0) else req_p1 = device area_addr tcb finish req_p2 = device area_ident if sseries=no start req_p3 = 1<<4!1; !(paw function)<<4!saw flags finish pon(req) return dact(send control): !** control message state = mess_p2>>24 ->states(state) states(connecting): !** allocate device pagecount(device no) = 0; ! initialise output accounting linecount(device no) = 0 pagesize(device no) = (mess_p2>>16)&x'FF' ! page size passed from caller countsw(device no) = no req = 0 req_dserv = allocate device req_sserv = lp allocated req_p1 = m'LP0'!device no req_p2 = lp interrupt req_p4 = mess_p1>>16 req_p5 = stream no pon(req) return states(enabling): !** request first page reply to comms control(mess_p1>>16, stream no, 0, 0) device area == record(device to da ad(device no)) device area_mode = mess_p2>>20&3; !iso ebcidic or binary ! %if (last allocated to other>>device no)&1 = 1 %start; !insert form feed? ! last allocated to other = last allocated to other !! (1<<device no) if mess_p2>>16&3=sequential start ; ! insert ff if not sequential continuation if sseries=no start device area_rcb3 = addr(device area_lb1) device area_lb1 = x'8A700300'!ebc ff else device area_addr tcb = addr(device area_tcb0 command) device area_tcb0 length = 1 device area_chars(0) = ebc ff finish finish get next buffer return states(disconnecting): !** deallocate device req = 0 req_dserv = deallocate device req_sserv = lp deallocated req_p1 = m'LP0'!device no req_p4 = mess_p1>>16 req_p5 = stream no req_p6 = device no pon(req) return states(aborting): ! last allocated to other = last allocated to other ! (1<<device no) states(suspending): reply to comms control(mess_p1>>16, stream no, 0, (pagecount(device no)<<16)!linecount(device no)) !always reply return routine reply to comms control(integer act, stream no, flag, count) rep = 0 rep_dserv = comms command!act rep_sserv = mess_dserv rep_p1 = stream no rep_p2 = flag rep_p3 = count pon(rep) end ; !of routine reply to comms control routine get next buffer req = 0 req_dserv = request transfer req_sserv = lp command req_p1 = stream no pon(req) end ; !of routine get next buffer end ; !of routine lp adaptor if cr fitted=yes start external routine cr adaptor(record (pe) name mess) !*********************************************************************** !* service 52 (x34) * !* drives the card reader to comms controller spec with some * !* nasty fiddles when a card wont fit into remains of an epage * !* can manage up to 10 readers (cr0-cr9) using a 512 byte area for * !* each which is provided on allocation * !*********************************************************************** integer i, j, stream no, device no, state, sent, act, buffer no, interrupt analysis flags, start record format gpcf(integer ser, gptsm, propaddr, ticks, caa, grcb ad, lba, ala, state, resp0, resp1, sense1, sense2, sense3, sense4, repsno, base, id, dlvn, mnemonic, entsize, paw, usaw0, urcb ad, sense ad, logmask, trt ad, ua size, ua ad, timeout, props0, props1) if sseries=no start const integer max trans=36 record format daf(integer lst0, lst1, rcb0, rcb1, rcb2, rcb3, rcb4, rcb5, rcb6, rcb7, stream no, mode, goah ad, goah len, curtrlen, buffer no, ident, blocked, integer array lbe(0:maxtrans-1), ale(0:max trans*2-1)) else const integer max trans=8 record format tcbf(integer cmd, ste, len, datad, ntcb, resp, integer array pre, post(0:3)) record format daf(record (tcbf) array tcb(0:max trans-1), integer stream no, mode, goah ad, goah len, curtrlen, buffer no, ident, blocked) finish own integer array device to da ad(0:9)= c not allocated(10) routine spec translate and shuffle(integer address, integer name len) routine spec reply to comms control(integer act, stream no, flag) routine spec get next buffer record (gpcf) name gpc entry record (daf) name device area record (sr) name stream record (br) name buffer record (pe) req, rep const integer array card bytes(iso:binary)= c 80, 80, 160 const integer array card size(iso:binary)= c 81, 80, 160 switch dact(0:send control) switch states(0:active) if mon level&2#0 and (kmon>>cr service)&1#0 start pkmon rec("CR Adaptor: ", mess) finish act = mess_dserv&short mask if act=interrupt start ; !from gpc mess_p3=gpc area adrress interrupt analysis flags = mess_p1>>20&15 gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) stream no = device area_stream no else if act>=go ahead start ; !from comms mess_p1=strm&short mask stream no = mess_p1&short mask stream == stream tab(stream index(stream no)) device no = stream_device no finish finish ->dact(act) dact(interrupt): !** device interrupt (normal, abnormal, attention) unless interrupt analysis flags=attention start buffer == buffer tab(device area_buffer no) start = virtual+buffer_real address+device area_goah ad if interrupt analysis flags&normal termination=normal termination start translate and shuffle(start, device area_cur tr len) if device area_mode=iso device area_goah len = device area_goah len-device area_curtrlen device area_goah ad = device area_goah ad+device area_curtrlen ->read; !read more if room else i = mess_p1&255; ! failing CCB entry sent = card size(device area_mode)*i; !bytes read ok translate and shuffle(start, sent) if sent>0 and device area_mode=iso device area_goah len = device area_goah len-sent !tell comms control by a call to update cursor req = 0 req_dserv = transfer complete req_sserv = mess_dserv req_p1 = stream no req_p2 = 2; !next page not reqd and dont recapture if sent>0 start req_p2 = req_p2!4 req_p5 = integer(gpc entry_sense ad) req_p6 = integer(gpc entry_sense ad+4) device area_blocked = 1 finish req_p3 = buffer_length-device area_goah len pon(req) finish else !** attention interrupt get next buffer if mess_p1&auto=auto and device area_blocked=0 finish return dact(execute fails): !** execute chain fails gpc entry == record(mess_p3) dump table(82, mess_p3, gpc entry_ent size) return dact(allocated): !** device allocated by gpc if mess_p1=0 start ; !allocated gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) device no = gpc entry_mnemonic&byte mask-'0' device to da ad(device no) = gpc entry_ua ad device area_ident = mess_p2; !save gpc identifier device area_stream no = mess_p5 device area_mode = 0; !read in non binary mode device area_blocked = 0 device area_goah len = 0 device area_goah ad = 0 device area_curtr len = 0 device area_buffer no = 0 if sseries=no start device area_lst0 = x'0FF01000' device area_lst1 = x'00000000' device area_rcb0 = x'0000800F' device area_rcb1 = realise(addr(device area_lst0)) device area_rcb2 = 144; !bytes of logic block device area_rcb3 = addr(device area_lbe(0)) device area_rcb4 = 288; !bytes of address list device area_rcb5 = addr(device area_ale(0)) device area_rcb6 = x'FC01'; !set nr mode device area_rcb7 = 0 for i = 0, 2, 70 cycle device area_ale(i) = x'58000050'; !80 byte entry repeat finish finish reply to comms control(mess_p4, mess_p5, mess_p1) return dact(deallocated): !** device deallocated by gpc device no = mess_p6 device to da ad(device no) = not allocated reply to comms control(mess_p4, mess_p5, mess_p1) return dact(go ahead): !** transfer go ahead !ie comms has paged in buffer buffer no = mess_p2 buffer == buffer tab(buffer no) device area == record(device to da ad(device no)) if sseries=no then device area_lst1 = x'80000001'!buffer_real address device area_goah len = buffer_length device area_goah ad = buffer_offset device area_buffer no = buffer no read: !try to read cards if device area_goah len<card size(device area_mode) start !not room for even 1 card if device area_goah len>0 start j = virtual+buffer_real address+device area_goah ad for i = 0, 1, device area_goah len-1 cycle byte integer(i+j) = 0; !fill partcard with nulls repeat finish req = 0 req_dserv = transfer complete req_sserv = cr command req_p1 = stream no req_p2 = 3; !please provide next page and dont recapture this one req_p3 = buffer_length pon(req) return finish !there is room for at least one card. set up chain i = 0 cycle exit if i>=max trans or device area_goah len-card size(device area_mode)*(i+1)<0 if sseries=no start device area_lbe(i) = x'04400200'+2*i; !chain ignre longblk&read device area_ale(2*i) = x'58000000'!card bytes(device area_mode) device area_ale(2*i+1) = device area_goah ad+card size(device area_mode)*i else device area_tcb(i) = 0 device area_tcb(i)_cmd = x'2440C002' device area_tcb(i)_ste = buffer_real address!1 device area_tcb(i)_len = card bytes(device area_mode) device area_tcb(i)_datad = device area_goah ad+card size(device area_mode)*i device area_tcb(i)_ntcb = addr(device area_tcb(i))+14*4 if device area_mode#binary then device area_tcb(i)_pre(0) = x'FC01' else c device area_tcb(i)_pre(0) = x'FC02' finish i = i+1 repeat if sseries=no start device area_lbe(i-1) = device area_lbe(i-1)&x'F3FFFFFF'; ! dechain if device area_mode#binary then device area_rcb6 = x'FC01' else device area_rcb6 = x'FC02' else device area_tcb(i-1)_cmd = device area_tcb(i-1)_cmd&x'FFBFFFFF' device area_tcb(i-1)_ntcb = 0 finish device area_cur tr len = card size(device area_mode)*i req = 0 req_dserv = execute chain req_sserv = cr executed if sseries=no start req_p1 = addr(device area_rcb0) req_p3 = 1<<4!1; ! PAW fn & SAW flags finish else req_p1 = addr(device area_tcb(0)) req_p2 = device area_ident pon(req) return dact(send control): !** control message state = mess_p2>>24 ->states(state) states(connecting): !** allocate device req = 0 req_dserv = allocate device req_sserv = cr allocated req_p1 = m'CR0'!device no req_p2 = cr interrupt req_p4 = mess_p1>>16 req_p5 = stream no pon(req); !try to allocate return states(disconnecting): !** deallocate device req = 0 req_dserv = deallocate device req_sserv = cr deallocated req_p1 = m'CR0'!device no req_p4 = mess_p1>>16 req_p5 = stream no req_p6 = device no pon(req) return states(enabling): !** request first page reply to comms control(mess_p1>>16, stream no, 0) device area == record(device to da ad(device no)) device area_mode = mess_p2>>20&3; !iso ebcidic or binary? if device area_blocked=0 then get next buffer else device area_blocked = 0 return states(suspending): states(aborting): reply to comms control(mess_p1>>16, stream no, 0) return routine reply to comms control(integer act, stream no, flag) rep = 0 rep_dserv = comms command!act rep_sserv = mess_dserv rep_p1 = stream no rep_p2 = flag pon(rep) end ; !of routine reply to comms control routine get next buffer req = 0 req_dserv = request transfer req_sserv = cr command req_p1 = stream no pon(req) end ; !of routine get next buffer routine translate and shuffle(integer start, integer name len) integer card addr, l, to e to i(start, len); !translate cards to iso to = start; !place card is to be shuffled to for card addr = start, 81, start+len-81 cycle ; !cycle up each card for l = 79, -1, 0 cycle ; !cycle down each character ->m if byteinteger(card addr+l)#' ' !char not a space repeat l = -1; !card all spaces m: byteinteger(card addr+l+1) = nl l = l+2; !length of card move(l, card addr, to) if card addr#to to = to+l repeat len = to-start; !number of chars remaining end ; !of routine translate and shuffle end ; !of routine cr adaptor finish ; !of conditional compilation of cr adaptor if cp fitted=yes start external routine cp adaptor(record (pe) name mess) !*********************************************************************** !* service 53 (x35) * !* drives the card punch to comms controller spec with some * !* nasty fiddles when a card wont fit into remains of an epage * !* cards are just punched from disc in ebcdic and spooler informed * !* can manage up to 10 punches (cp0-cp9) using a 160 byte area for * !* each which is provided on allocation * !*********************************************************************** integer i, j, stream no, device no, state, act, buffer no, sym, interrupt analysis flags record format gpcf(integer ser, gptsm, propaddr, ticks, caa, grcb ad, lba, ala, state, resp0, resp1, sense1, sense2, sense3, sense4, repsno, base, id, dlvn, mnemonic, entsize, paw, usaw0, urcb ad, sense ad, logmask, trt ad, ua size, ua ad, timeout, props0, props1) if sseries=no start record format daf(integer lst0, lst1, rcb0, rcb1, rcb2, rcb3, rcb4, rcb5, rcb6, rcb7, lbe0, ale0, ale1, stream no, buffer no, goah ad, goah len, posn, mode, ident, byte integer array card(0:79)) else record format daf(integer tcb cmd, tcb ste, tcb len, tcb datad, tcb ntcb, tcb resp, tcb pre0, tcb pre1, tcb pre2, tcb pre3, tcb post0, tcb post1, tcb post2, tcb post3, stream no, buffer no, goah ad, goah len, posn, mode, ident, byte integer array card(0:79)) finish own integer array device to da ad(0:9)= c not allocated(10) own byte integer array countsw(0:9); ! guards against counting same epage twice own integer array cardcount(0:9); ! count of cards punched routine spec reply to comms control(integer act, stream no, flag, count) routine spec get next buffer record (gpcf) name gpc entry record (daf) name device area record (sr) name stream record (br) name buffer record (pe) req, rep switch dact(0:send control) switch states(0:active) if mon level&2#0 and (kmon>>cp service)&1#0 start pkmon rec("CP Adaptor: ", mess) finish act = mess_dserv&short mask if act=interrupt start ; !from gpc mess_p3=gpc area adrress interrupt analysis flags = mess_p1>>20&15 gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) stream no = device area_stream no else if act>=go ahead start ; !from comms mess_p1=strm&short mask stream no = mess_p1&short mask stream == stream tab(stream index(stream no)) device no = stream_device no finish finish ->dact(act) dact(interrupt): !** device interrupt (normal, abnormal, attention) unless interrupt analysis flags=attention start buffer == buffer tab(device area_buffer no) if interrupt analysis flags&normal termination=normal termination start device area_posn = 0; !no unpunched card in buffer i = 0 and ->epage used if device area_goah len=0 !finished buffer empty stream == stream tab(stream index(stream no)) device no = stream_device no ->punch; !punch more if room else !tell comms control by a call to update cursor req = 0 req_dserv = transfer complete req_sserv = cp command req_p1 = stream no req_p2 = 6; !next page not reqd and dont recapture this one req_p3 = buffer_length-device area_goah len req_p5 = integer(gpc entry_sense ad) req_p6 = integer(gpc entry_sense ad+4) pon(req) finish else !** attention interrupt if mess_p1&auto=auto then start stream == stream tab(stream index(stream no)) device no = stream_device no countsw(device no) = yes; !remember this, so that epage not counted twice get next buffer finish finish return dact(execute fails): !** execute chain fails gpc entry == record(mess_p3) dump table(83, mess_p3, gpc entry_ent size) return dact(allocated): !** device allocated by gpc if mess_p1=0 start ; !allocated gpc entry == record(mess_p3) device area == record(gpc entry_ua ad) device no = gpc entry_mnemonic&byte mask-'0' device to da ad(device no) = gpc entry_ua ad device area_ident = mess_p2; !save gpc identifier device area_stream no = mess_p5 device area_mode = 0; !read in non binary mode device area_buffer no = 0 device area_goah ad = 0 device area_goah len = 0 device area_posn = 0 if sseries=no start device area_lst0 = 0; ! not used device area_lst1 = 0; ! not used device area_rcb0 = x'00008002' device area_rcb1 = 0; ! not used device area_rcb2 = 4; !bytes of logic block device area_rcb3 = addr(device area_lbe0) device area_rcb4 = 8; !bytes of address list device area_rcb5 = addr(device area_ale0) device area_rcb6 = x'FC01'; !set nr mode device area_rcb7 = 0 device area_lbe0 = x'80000300' device area_ale0 = x'58000050' device area_ale1 = addr(device area_card(0)) else device area_tcb cmd = x'2000C083' device area_tcb ste = realise(addr(device area)&x'FFFC0000')!1 device area_tcb len = 80 device area_tcb datad = addr(device area_card(0)) device area_tcb ntcb = 0 device area_tcb pre0 = x'FC01' finish finish reply to comms control(mess_p4, mess_p5, mess_p1, 0) return dact(deallocated): !** device deallocated by gpc device no = mess_p6 device to da ad(device no) = not allocated reply to comms control(mess_p4, mess_p5, mess_p1, 0) return dact(go ahead): !transfer go ahead !ie comms has paged in buffer buffer no = mess_p2 buffer == buffer tab(buffer no) device area == record(device to da ad(device no)) device area_goah len = buffer_length device area_goah ad = buffer_offset device area_buffer no = buffer no punch: !try to punch cards !copy a card into device area_card and spacefill. spanned cards have !1st part in buffer and device area_posn indicates next free byte. !device area_posn=80 indicates complete card in buffer in ebcdic !as happens after an abnormal termination i = device area_posn ->fire if i=80; !card all ready to fire io j = 0 cycle sym = byte integer(virtual+device area_goah ad+j+buffer_real address) if device area_mode#binary start if device area_mode=iso start exit if sym=nl or sym=12; !control char else exit if sym=ebc nl or sym=ebc lf or sym=12 finish finish !if buffer full then treat as newline except arrange for the last char !to become the first char on the next(overflow) card unlike the nl char !which is discarded. nb care needed not to get blank card unnecessarily if i>=80 then j = j-1 and exit device area_card(i) = sym i = i+1 j = j+1 ->epage used if j>=device area_goah len repeat !control char found device area_goah len = device area_goah len-(j+1) device area_goah ad = device area_goah ad+j+1 if device area_mode#binary start if device area_mode=iso then sym = ' ' else sym = ebc sp device area_card(i) = sym and i = i+1 while i<80 i to e(addr(device area_card(0)), 80) if device area_mode=iso finish device area_posn = 80; !card read in ebcdic if countsw(device no)=yes then countsw(device no) = no else cardcount(device no) = cardcount(device no)+1 fire: req = 0 req_dserv = execute chain req_sserv = cp executed if sseries=no start req_p1 = addr(device area_rcb0) req_p3 = 1<<4!1 finish else req_p1 = addr(device area_tcb cmd) req_p2 = device area_ident req_p3 = 1<<4!1 pon(req) return epage used: !await next go ahead device area_posn = i; !some iso chars in buffer req = 0 req_dserv = transfer complete req_sserv = cp command req_p1 = stream no req_p2 = 3; !please provide next page and dont recapture this one req_p3 = buffer_length pon(req) return dact(send control): !** control message state = mess_p2>>24 ->states(state) states(connecting): !** allocate device cardcount(device no) = 0; !initialise output accounting countsw(device no) = no req = 0 req_dserv = allocate device req_sserv = cp allocated req_p1 = m'CP0'!device no req_p2 = cp interrupt req_p4 = mess_p1>>16 req_p5 = stream no pon(req); !try to allocate return states(disconnecting): !** deallocate device req = 0 req_dserv = deallocate device req_sserv = cp deallocated req_p1 = m'CP0'!device no req_p4 = mess_p1>>16 req_p5 = stream no req_p6 = device no pon(req) return states(enabling): !** request first page reply to comms control(mess_p1>>16, stream no, 0, 0) device area == record(device to da ad(device no)) device area_mode = (mess_p2>>20)&3; !iso ebc or binary get next buffer return states(suspending): states(aborting): reply to comms control(mess_p1>>16, stream no, 0, cardcount(device no)) return routine reply to comms control(integer act, stream no, flag, count) rep = 0 rep_dserv = comms command!act rep_sserv = mess_dserv rep_p1 = stream no rep_p2 = flag rep_p3 = count; ! accounting info pon(rep) end ; !of routine reply to comms control routine get next buffer req = 0 req_dserv = request transfer req_sserv = cp command req_p1 = stream no pon(req) end ; !of routine get next buffer end ; !of routine cp adaptor finish if monlevel&256#0 start ; !include monitoring? if multi ocp=yes start external routine spec semaloop(integer name sema) own integer trace sema= -1 finish external string fn spec h to s(integer i, p) external integer fn spec s to i(string (255) name s) const integer emas page size= epagesize*1024 const integer start page= x'500'; !first available page on fsys to trace to const integer nbuffs= 2; !maybe more for fast machines & multi ocps const integer combine sno= x'380000' const integer page out reply= 1; !activity numbers in combine const integer start harvest= 2 const integer page got= 3 const integer stop harvest= 4 own integer trace fsys= -1; !trace output to this fsys initially not set own integer end page= 0; !last available e page on fsys to trace to own integer current page= 0; !initially not set own integer events lost= 0; !count of events lost when no buffer available own integer buffer pointer= 0; !next free hole in current buffer own integer current buffer= 0; !current buffer if # 0 own integer next buffer= 0; !next buffer if # 0 own integer count= 0; !count of pages claimed record format bufferf(integer virt address, store index, link) own record (bufferf) array buffers(1:nbuffs) record format ddtform(integer ser, pts, propaddr, stick, stats, rqa, lba, ala, state, iw1, iw2, sense1, sense2, sense3, sense4, repsno, base, id, dlvn, mnemonic, string (6) lab, byte integer mech) external routine tracer(string (63) s) !* This routine handles text commands from the operator to set up !* and control the trace procedure. The commands handled are !* !* TRACE TO vollabel !Nominates disc volume trace to be logged to !* TRACE event process !specifies which event is to be traced (-1 all) !* and which process is to be traced (-1 all). !* !Events can be added to but not processes. !* TRACE ON !initiates tracing (above commands must have !* previously been given) !* TRACE OFF !terminates tracing. Not final trace may be !* added to by setting trace on again. record (pe) p record (ddtform) name disc string (63) label, temp integer i, j ->set fsys if s->temp.("TO ").label and temp="" ->set on if s="ON" ->set off if s="OFF" if trace fsys#-1 and trace=no start ; !trace fsys must first be set !and trace not started i = s to i(s); !get events to trace from string if -1<=i<=31 start ; !within range j = s to i(s); !get process to trace from string if -1<=j<=max procs and j#0 start !within range if i<=0 then trace events = i else trace events = trace events!1<<i trace process = j return finish finish finish err: op mess("TRACE ".s." ?") return set on: !start tracing check trace fsys set and events set first ->err if trace fsys=-1 or trace events=0 or trace=yes if current page<end page start p = 0 p_dserv = combine sno!start harvest pon(p); !request combine to get buffers and start tracing finish else op mess("TRACE AREA FULL") return set off: !stop tracing check tracing was on ->err if trace#yes p = 0 p_dserv = combine sno!stop harvest pon(p) return set fsys: !set the trace fsys checking it is there !* CHECKS !* WHETHER THE LABEL IS A VALID LENGTH !* TRACING IS NOT ALREADY IN PROGRESS !* WHETHER SPECIFIED DISC IS ON-LINE !* THAT ITS CONSISTENCY CHECK HAS BEEN DONE !* THAT IT IS NOT THE IPL DISC !* THAT THE TRACE AREA DOES NOT OVERLAP THE FILE SYSTEM !* SETS (if above checks succeed) !* TRACE FSYS !* END PAGE OF TRACE AREA !* START PAGE OF TRACE AREA if length(label)=6 and trace=no and com area_ndiscs>0 start for i = 0, 1, com area_ndiscs-1 cycle disc == record(integer(com area_ditaddr+4*i)) if disc_lab=label and disc_dlvn>=0 and disc_dlvn#com area_suplvn and disc_base>start page start trace fsys = disc_dlvn; !set fsys no end page = disc_base-1; !page before start of file system current page = start page; !set to start of trace area op mess("TRACE AREA ".label." X".h to s(start page, 3)." TO X".h to s(end page, 3)) exit finish repeat return if trace fsys#-1 finish ->err end ; !of routine trace routine fill(integer length, from, filler) !*********************************************************************** !* * !* FILL "LENGTH" BYTES "FROM" WITH CHARACTER "FILLER" * !* * !*********************************************************************** *lb_length *ldtb_x'18000000' *ldb_ b *lda_from *lb_filler *mvl_ l = dr end ; !of routine fill routine page out record (pe) p fill(emas page size-buffer pointer, buffers(current buffer)_virt address+buffer pointer, 0); !clear rest of buffer p = 0 p_dserv = x'210002'; !write out to disc p_sserv = combine sno!page out reply; !reply to combine p_p1 = current buffer p_p2 = trace fsys<<24!current page p_p3 = buffers(current buffer)_virt address pon(p) current page = current page+1 if trace=yes start ; !ignore if trace over if current page>=end page start ; !end of trace area p = 0 p_dserv = combine sno!stop harvest pon(p); !notify combine to stop op mess("TRACE AREA FULL") finish current buffer = next buffer; !get next buffer if current buffer#0 start ; !there was one next buffer = buffers(current buffer)_link !relink chain buffer pointer = 0; !set to start of buffer finish finish end ; !of routine page out routine return buffer(integer buff no) record (pe) p p = 0 p_dserv = x'60000'; !return page p_p1 = buff no p_p2 = buffers(buff no)_store index pon(p) end ; !of routine return buffer external routine harvest(integer event, proc, len, a, b, c, d, e) integer bytes, address, ocp, i record format dataf(byte integer event, proc, ocp, len, long integer clock, integer array a(0:4)) long integer clock record (dataf) name data bytes = len+12 *rrtc_0 *st_clock if multi ocp=yes start *lss_(3); *ush_-26 *and_3; *st_ocp *inct_trace sema *jcc_8, <tgot1> semaloop(trace sema) tgot1: finish else ocp = 0 if current buffer#0 start ; !buffer available? address = buffers(current buffer)_virt address+buffer pointer data == record(address) data_event = event; !log data in buffer data_proc = proc data_ocp = ocp data_len = len data_clock = clock ! move(len,addr(a),address+12) %if len > 0 if len>0 start address = address+12 i = addr(a) *ldtb_x'18000000' *ldb_len *lda_i *cyd_0 *lda_address *mv_ l = dr finish buffer pointer = buffer pointer+bytes page out if buffer pointer+32>=emas page size or trace=no !buffer nearly full or trace stops op mess("END OF TRACE X".h to s(current page-1, 3)) if trace=no finish else events lost = events lost+1 trace sema = -1 if multi ocp=yes end ; !of routine harvest external routine combine(record (pe) name p) switch act(page out reply:stop harvest) integer buff no buff no = p_p1; !normally in p1 if multi ocp=yes start *inct_trace sema *jcc_8, <tgot2> semaloop(trace sema) tgot2: finish ->act(p_dserv&x'FFFF') act(page out reply): op mess("TRACE PAGE OUT FAILS") if p_p2#0 if trace=yes start ; !still tracing if current buffer=0 start ; !buffer urgently required current buffer = buff no buffer pointer = 0 else ; !link to free list buffers(buff no)_link = next buffer next buffer = buff no finish finish else return buffer(buff no); !tracing has stopped trace sema = -1 if multi ocp=yes return act(start harvest): p = 0 p_dserv = x'50000'; !get page p_sserv = combine sno!page got; !reply here p_p2 = 1; !do not zero for buff no = 1, 1, n buffs cycle p_p1 = buff no pon(p) repeat next buffer = 0; !head of free list count = 0; !of buffers which have arrived trace sema = -1 if multi ocp=yes return act(page got): buffers(buff no)_virt address = p_p4 buffers(buff no)_store index = p_p2 buffers(buff no)_link = next buffer; !link in to free list next buffer = buff no count = count+1; !count arrivals if count=nbuffs start ; !all have arrived start harvest current buffer = next buffer; !get first buffer from free list next buffer = buffers(current buffer)_link buffer pointer = 32; !leave room for a file header events lost = 0 op mess("START OF TRACE X".h to s(current page, 3)) trace = yes trace sema = -1 if multi ocp=yes harvest(64, 0, 12, nbuffs, trace events, trace process, 0, 0) !trace starts return finish trace sema = -1 if multi ocp=yes return act(stop harvest): while next buffer#0 cycle ; !release queued buffers return buffer(next buffer) next buffer = buffers(next buffer)_link repeat trace = no trace sema = -1 if multi ocp=yes harvest(65, 0, 4, events lost, 0, 0, 0, 0); !trace ends return end ; !of routine combine finish end of file