Documentation for OUT output package. This file should be MC:KSC;?OUT > (OUT.DOC on TNX). Contents: Brief usage summary Basic intro to OUT OUT items Miscellaneous cautions etc. CONVERSION: How to convert old FWRITE/UUOs stuff to OUT. plus sections of general UUO documentation (new ?UUOS >) WARNING: the OUT package is still in a state of development; not everything in here is guaranteed to be true. In particular, "interpreted mode" may never be implemented - it was an interesting side path that became less interesting. Also, "item" names are liable to change, especially where the name is simply an unthinking copy of the equivalent FWRITE item and a more logical scheme becomes apparent. Comments & suggestions welcomed! Usage: .INSRT KSC;OUT or .INSRT OUT ; to invoke package Syntax: OUT(chan,func(args),(" Literal string "),func(args)...) chan - channel to output these items on. func(args) - function-like "item" that usually outputs its args. Basic channel types: OUT(ch,OPEN) ; Initialize for a "real" channel OUT(ch,OPEN(UC$BPT,[bp],[lim])) ; Writes up to chars using OUT(ch,OPEN(UC$NUL)) ; Outputs to null device sink. Basic operations: OUT(ch,CLS) ; Closes channel, forcing out any remaining output. Basic items: D([num]) ; outputs number in decimal O([num]) ; outputs number in octal F([num]) ; outputs number as floating-point. S([# bytes],[bp]) ; Outputs byte string TZ([asciz]) ; Outputs ASCIZ string General description: These are macros and routines supporting a new output package interface, OUT, which is the successor to FWRITE. The basic idea is quite similar, but the syntax of the invoking macro is different and much more flexible. The intent is to make the OUT macro and supporting routines efficient enough and general enough that no other means of output should be necessary. In particular, none of the individual "item" types should ever be invoked other than via OUT, and use of output UUO's or "direct" system output calls should be avoided. STANDARD OUTPUT: One important concept to remember is that there is always a "current output channel", or "standard output", identified by accumulator "OC". All output, by any means whatever, always goes to the standard output! If no channel argument is given to the OUT, output simply defaults to this standard output, which is quite efficient. When a channel argument IS given, OUT will set the standard output to this new channel, but always restores the previous value at the end of the call; thus OUT never permanently alters the value of OC. The only exception to this is the construct OUT(,CH(chan)) which explicitly requests that the standard output be set to . OUT "ITEMS": Within an OUT call, any reasonable number of "items" or "literal strings" may be specified. (Currently the definition of reasonable is about 16). A literal string is simply a STRUNG-type string enclosed in parentheses; an item consists of an item name, which may or may not have arguments associated with it. If arguments are given, they must be enclosed in parentheses immediately after the item name. The number of arguments an item takes depends on the particular item (some take a variable number). It is one exemplary feature of the OUT macro that furnishing too few or too many arguments will only affect that particular item; it will never destroy the overall functioning of the OUT call. Examples: OUT(TYOC,("This is example "),D(CNT),(" of"),D(TOT,5),EOL) The use of literal strings should be obvious. TYOC - During execution of this OUT, the standard output will be temporarily set to TYOC. D(CNT) - outputs the value at CNT in decimal. D(TOT,5) - outputs the value at TOT in decimal, right adjusting it within a 5-column field. This is an example of how some items can take more than one argument. EOL - simply outputs a CRLF. This demonstrates an item with no arguments at all. !!!!! CAVEAT !!!!!! In order to provoke the right behavior from MIDAS, OUT must be a "parenthesized call"; a "<", "(", or "[" must come immediately after the OUT with no intervening space. There is no "free format"; blanks between items will screw the stupid macro parser. Under the covers: OUT can assemble in three basic ways; each of these can be specifically requested by using the names OUTCOD, OUTCAL, or OUTRID instead of "OUT". OUT for default (normally OUTCOD) OUTCOD forces "inline" OUTCAL forces single instruction CALL OUTRID forces "interpreted" (not implemented) OUTRID - Interpreted (Not implemented yet) PUSHJ P,[JSP U3,OXJSPE ch ? outinstr ? outinstr ? ... ? outinstr ? -1] Each "outinstr" is an interpreted instruction, with an opcode and address. Some of these actually generate output, others merely set parameters, and some can even provide rudimentary flow control. OUTCAL - In-line call PUSHJ P,[HRLM OC,(P) ; Or nothing if std output MOVEI OC,CH ; Or nothing if std output HLRZ OC,(P) ; Or nothing if std output POPJ P, ] OUTCOD - In-line code PUSH P,OC ; Or nothing if std output MOVEI OC,CH ; Or nothing if std output POP P,OC ; Or nothing if std output OUTCOD is the default and is used to get fast inline code, especially when using standard output. OUTCAL has the overhead of a PUSHJ/POPJ, but has the feature of being both skippable and XCT-able, and lends itself to constants optimization, again especially when using standard output. General policy should be to use OUT for everything except where a one-instruction invocation is specifically needed; for these, use OUTCAL. The OUTCAL invocation should not be used for any other reason, in order to clearly identify such places. Similarly, OUTCOD should only be used where it is essential that the code produced be inline. There is no preference implied for one or the other form of OUT invocation; this merely makes sure that nothing will break if the default form used by OUT is changed. Channel Opening, types, & manipulation: OPEN(itype,aval,alim,abytsize) Open a channel. This does NOT imply that the "hard" channel is likewise opened from the OS viewpoint; that must be done by user. abytsize - byte size, only used by UC$BUF. alim - # bytes maximum to output. Attempts to output more will be no-ops. UC$BUF uses different interpretation. Defaults to infinity. itype - as below. Defaults to UC$IOT. UC$IOT - "Hard" OS-opened output channel. aval - [jfn #], on TNX only. eg: OUT(tyoc,OPEN(UC$IOT,[.priou])) on TNX. UC$BUF - Ditto but buffered. aval - as for IOT. alim - buffer to use. If positive, a buffer of that many BYTES is allocated. Zero or default allocates one page's worth. If negative, is taken as AOBJN -<# wds>,, abytsize - byte size to use. Defaults to 7 (standard ASCII) eg: OUT(bc,OPEN(UC$BUF,,[-bflen,,buff])) UC$BPT - Byte pointer. aval - BP to output with. eg: OUT(bpc,OPEN(UC$BPT,[440700,,stuff])) UC$UAR - UUO Area. aval = ARPT to specific area. eg: OUT(ac,OPEN(UC$UAR,omfar)) UC$NUL - Null output sink. UC$SAO - with UUO LISTS, like SAOBEG. aval - pointer to LSE (defaults to current LSE) eg: OUT(lc,OPEN(UC$SAO,$arloc+msgar)) UC$XCT - XCT operation aval - Instr to XCT (takes byte in U1, chan in OC) eg: OUT(xc,OPEN(UC$XCT,[call hacko])) UC$TRN - TRANslated channel aval - channel # to translate output to. Ignores "alim". eg: OUT(dtyoc,OPEN(UC$TRN,[tyoc])) FRC Force out all buffered output. No-op for anything but a UC$BUF type channel. CLS Close the channel. Implies FRC. Unlike OPEN, this actually WILL effect closure of the "hard" channel to the OS. PTV(aret) Returns pointer-value for channel. Generally speaking this tries to be the # of bytes output on the channel since it was opened, but for certain channel types this may be different: BUF - # of bytes currently in buffer. UAR - # of chars in whole area (between beg and $ARWPT) TRN - value for translated channel. CH(ichan) Set standard output to channel specified. This will not last beyond the current OUT invocation unless no channel was specified to the OUT, i.e. the default std output is in use. eg: OUT(,CH(chan)) does the right thing. FMT(ITEM,iwid,iprec,ifill) Hairy field formatting output. Outputs ITEM (can be any item, with args) according to formatting parameters: iwid - Field width in columns. If positive, output is right justified. If negative, output is left justified. Output is never truncated by this parameter. Defaults to zero (no justification) iprec - "precision"; max # of chars to output. This parameter DOES truncate output!! Truncation happens before justification. Defaults to infinity. ifill - "fill character" to use as justification padding. Defaults to blank. LIST OF OUT ITEMS: The following terminology will be used in describing OUT items and the arguments they take: ITEM(ixxx,axxx,{opt1,opt2}...) ITEM - name of item {...} - args in braces are optional. ixxx - "immediate" arg, MOVEI'd. xxx is rest of arg name. axxx - "addressed" arg, MOVE'd. xxx is rest of arg name. Common names: abp - address of a byte pointer. acnt - address of a count. aval - address of a value (implied numerical) a6 - address of a sixbit word. C(ich) Character. Outputs single byte (since this is MOVEI'd it only goes up to 18 bits). ; W(aval) Word. Outputs single byte (MOVE'd, so can be up to 36 bits). ; or B(aval) Byte. ; Not implemented yet; just listed for consideration. S(acnt,abp) String. Outputs cnt bytes from source bp. TS(astr) String variable. Address of [,,cnt ? bp] TA(arpt) Area. Address is of an ARBLK for an area. TZ(asciz) ASCIZ. Address is of an ASCIZ string. TZ$(aasciz) ASCIZ, RH of value is address of ASCIZ. A bit crockish. TC(ascnt) ASCNT. Address is of [cnt,,[ascii /str/]] TPZ(abp) BYTEZ. Outputs from bp until zero byte. TPC(acrock) Ugh. Address is of [cnt,,[bp]]. Obsoleted by "S". $$OHST items: HN(aval) Host number. Outputs value as octal host #, simplifying if possible. HND(aval) Like HN but decimal. HST(aval) Host name. If no name for given number, becomes "HN". $$OERR item: ---------------------------------------- ERR({aerr}) Outputs error message for given error # from system. If none specified, or value is -1, outputs message for last error encountered by program, e.g. as in OUT(,ERR). SIMPLE items: --------------------------------------- CRLF Outputs a CR and LF. EOL End-of-Line, same as CRLF. LPAR,RPAR Parentheses, (Left and Right) LBRK,RBRK Brackets, [Left and Right] LBRC,RBRC Braces, {Left and Right} LABR,RABR Angle-brackets, SIXBIT output: ------------------------------------------- 6W(a6) 6-bit word, all 6 chars. 6F(a6) 6-bit "Filename" - no trailing blanks 6Q(a6) 6-bit Quoted filename - Like 6F, but certain chars special in a filename are quoted with ^Q. These chars are anything not alphanumeric or "-". NUMERICAL OUTPUT: ---------------------------------------------- D(aval,{ifld}) Decimal output O(aval,{ifld}) Octal X(aval,{ifld}) Hexadecimal F(aval,{ifld,iprec}) Floating-point "F" - mmm.nnn E(aval,{ifld,iprec}) Floating-point "E" - m.nnnnnnE+ee G(aval,{ifld,iprec}) Floating-point "G" - F or E as appropriate. aval - number to output ifld - width of the field to output in. If positive, string will be right justified. If negative, string will be left justified. Output will never be truncated by this parameter. iprec - Precision. For floating-point types, specifies # of digits to right of the decimal point. RH(aval) RH as oooooo (6 octal digits) LH(aval) LH similar. H(aval) Halfword as LH,,RH RHV(aval) RH as positive octal value LHV(aval) Similar HV(aval) Similar RHS(aval) RH as Signed octal number (i.e. sign extension) LHS(aval) Similar HS(aval) Similar $$OTIM Time Output items: -------------------------------------- ; All of these take an optional time-word argument. ; If this is not specified, the current time is used. TIM(HMS,{atim}) Time as "hh:mm:ss" TIM(MDY,{atim}) Date as "mm/dd/yy" TIM(MDYT,{atim}) Datime as "mm/dd/yy hh:mm:ss" TIM(MONTH,{atim}) Month as "Month" TIM(MON,{atim}) Month as "MON" TIM(DOW,{atim}) Day of week as "Fooday" TIM(DOW3,{atim}) Day of week as "Foo" TIM(F1,{atim}) Datime as "dd MON yy hhmm-ZON" TIM(F2,{atim}) Datime as "dd Month yyyy hh:mm-ZON" CONVERSION SCHEME: Intent is that all existing programs will continue to work without modification. Of course, they won't be able to take advantage of any new features, and if they make use of any UUO/output package internals, they will require some checking. To convert over to using OUT: (0) Resolve undefined symbols. Because output-related functions have been moved to the OUT package, any references to symbols defined therein will now fail, unless OUT" is prefixed to these references. This is a deliberate incompatibility to help flush out such refs and tag them for easy future checking. (1) Check out any UUO definitions. Programs which define their own UUO's will need some checking to make sure they conform to standards described in the UUO documentation (KSC;?UUOS > on ITS), which has changed. If no UUO's are defined, you already win. (2) Verify operation with $$OUT==1. This switch enables the new OUT package features. No changes to the sources should be necessary, provided steps (0) and (1) have been satisfied. It would be wise to check out any FWRITE-item definitions made in the program. If they don't do anything real hairy or dependent on UUO internals, there should be no problem, though. It is possible that certain FWRITE's will not work, because use of the OUT package also implies that FWRITE items are coded differently; they will no longer expand into UUO's. However, the new code should have exactly the same results, and cases which don't work will be so kludgy that they deserve the attention-getting feature of lossage. For example, code which assumes that FWRITE assembles into a single instruction (and tries to skip over it) will lose grossly, and moreover DESERVE to lose because the doc tells you not to try that. Now the new OUT macro is included and can be used in place of or in combination with FWRITE. Since it incorporates new item types, is cleaner, and faster, its use is preferable for new output statements. Its only disadvantage is that it is slightly more painful to set up literal strings; however, improvements to MIDAS macro parsing may fix this someday. (3) Set things up for $$OUUO==0. This switch controls inclusion of the old output UUO's, including OUTOPN and OUTPTV. It defaults to 1. By setting it 0, output UUO's are DISABLED!!! This is the most significant conversion step since you have to be sure that none of these UUO's remain in the source; all should have been converted to use OUT by then. Since FWRITE still works, this primarily means that any code which directly invokes these UUO's should be changed. The main advantage is that they no longer gobble up most of the space in the UUO table, so that you can use the entries for more useful purposes. (4) ?? Replace instances of FWRITE? It is intended that FWRITE will continue to work under the new OUT package, although it will not be further developed; only OUT will have new features. At some point in the future, it may be that all programs using NUUOs will have abandoned the use of FWRITE, which can then be flushed or recycled. MAKSTR and CONC will be given OUT-style constructions as well. This is basic documentation on writing UUO and OUT code and interfacing with the packages in general. It will eventually live in KSC;?UUOS >. ------------------------------------------------------------------------ The major change to the UUO package is the removal of all output UUO's; their functions are now furnished by the OUT package, which will continue to support these UUO's by default. Other changes relate primarily to a general clean-up of code by defining standard symbols and macros that allow more flexibility; in particular, different methods of UUO dispatching are available. $$UJSR==1 requests traditional JSR dispatching, and is the default. $$UCAL==1 requests dispatch via PUSHJ P, $$UJSP is not defined, but could be someday. In general no changes should be required unless the program explicitly refers to anything in the UUO package besides UUO's. In particular, references to the following will require study: 40, 41, U2, UUOH, UUORET, UUODEF, UUODFA, UUODFE, UUODFN ------------------------------------------------------------ References to UUO package symbols in general: No refs to the UUO package symbols should ever be required unless (A) executing UUO's at interrupt level, or (B) defining new UUO's. Sometimes a program may explicitly set loc 41 (the UUO vector) to remedy loader smashage. For this case, set it to UUOCAL, as in MOVE A,[UUOCAL] ? MOVEM A,41 (A) Executing UUO's at interrupt level: If an interrupt handler executes any UUO's it will have to save and restore at least the following variables to minimize interference with interrupted UUO routines: PUSHAE P,[U40,U1,U2,U3,U4] ; Always IFE $$UCAL,PUSH P,UUORPC ; Only if not using PUSHJ. IFN OC-U2, PUSH P,OC ; Only if OC != U2. (B) DEFINING NEW UUO's: The macros UUODEF, UUODFA, UUODFE, and UUODFN are used to associate a UUO name with a routine address. The routine should whenever possible make use of these definitions: instr ac,U40 - UUO being executed. This is basically just 40, but is a symbol to help keep track of references. LDB ac,UACFLD - Gets AC field of UUO instruction. jump ac,UUORTL - ReTurn Location. When it is desired to jump directly to the UUO return location, use this as the destination, as in JUMPG U1,UUORTL. This replaces most instances of UUORET, which is obsolete. instr ac,UUORPC - Return-PC address. Doing AOS UUORPC is the right way to "skip return". However, this must NOT be used unless the PDL is at the same level as at the start of the UUO routine!!! UUOXRT - eXecute ReTurn. This macro-instruction should be used to return from the UUO routine. The PDL should of course be at entry state. UIOINIT - Macro-instruction which loads OC with channel # and maybe does other stuff. This should be the first thing any output UUO does. Note though that output UUO's are obsolete, so don't create any more unless it's absolutely necessary. Notes on AC usage: U1 and U3 are freely available and can be smashed without fear. There is a convention that "direct" non-UUO calls to the routine should use U3 to pass arguments; this is mostly used by OUT routines. U4 should not be smashed before args are read; by convention this is available for furnishing args to the UUO. Otherwise, it is also freely smashable. OC should NEVER be smashed across a call. **** If U2 == OC, U2 should NEVER be smashed across a UUO call, although it can be saved, used, and restored. If U2 != OC then U2 is freely smashable. Defining "output" UUO's: This is covered in more detail in ?UUOS > and in the rest of the OUT package documentation. Since output UUO's are obsolete, you shouldn't be adding any more. Nevertheless, basic things to note: Use UIOINIT at the very start of the UUO. In this case it smashes OC but that is okay. Thereupon leave OC (which may be U2) alone. Try to avoid using U2 for anything. STDOUT takes a byte in U1 as argument, outputs on channel in OC. STDOBP is same as STDOUT, but should be used whenever there is an input BP (which should be in U3) that risks being bumped by an area shift. Use of UUO's: Rule 0: Never refer to the stack (P) in any UUO argument, unless it is part of the actual effective address computation for the UUO; e.g. UUO AC,-3(P) is valid, but UUO AC,[(P)] is NOT!! The reason for this is that the stack level will probably change owing to AC saving, or CALLs, by the time UUO code tries to read the argument. DEFINING NEW "OUT" ITEMS: Pick some item name of 4 chars or less. Then set up the following definitions: DEFINE OUT"$! (ARG1,ARG2,...) ; Args !! MUST BE BALANCED !!! IFN $$ORID, instructions> IFE $$ORID, > TERMIN OUTDEF O.!:,O!!I ; Defines O. instruction. OI!: OX!: RET ; To return when done. The code must "never" leave OC altered. (exceptions to this should be left to package-internal routines). Note that U2 may be = to OC, so it is good form to avoid using U2 unless saving/restoring is better than using just 3 ACs. Note also the passing of O.-instruction in U3 for interpreted mode, and the common convention (though not restriction) that direct calls pass a single arg in U3. Other than this, ACs U1-U4 are freely available. Actual output mechanism: Within the code for an output UUO or item, there are two fundamental ways to output stuff: Byte: STDOUT - outputs byte in U1 over channel in OC. No ACs are clobbered. STDOUT(arg) - same but does MOVEI U1,arg prior to STDOUT. This saves some coding. Note arg is balanced, so beware of commas or single bracket-chars!!! STDOBP - Same as STDOUT, but assumes that a byte ptr is in U3 and takes care to preserve it should an area-shift happen as a result of the output. Thus U3 may change. String: ; This is how to do string output MOVE U3,[byte ptr] ; U3 takes byte pointer SKIPLE U4,[# of bytes] ; U4 takes # bytes (must be positive!) CALL @USCOPT(OC) ; Invoke string output for channel! ; All of U1, U3, U4 and U2 (if != OC) ; may be clobbered. WRITING "XCT" CHANNEL CODE: (This stuff currently only talks about existing XCT facility, which is slated for flavorful expansion) The argument to the OPEN should be a single instruction which outputs the byte in U1. The channel # is available in OC. Usually this will have to be a CALL to some routine, and the following conditions hold: No ACs should be clobbered!! This includes U1 and OC. In future, arg will be a pointer to a vector block which specifies all the goodies such as string-mode dispatch, overflow dispatch, etc. as well as the unit-mode instruction. ------------------------------------------------------------------------