; ======================================================= ; ; ; Module composed of workspace operators for REC/MARKOV. ; These are specialized subroutines for manipulating a ; "workspace," which is an array together with five ; pointers. The workspace will probably (but not always) ; hold text, subdivisions of which are marked off by the ; pointers. They in turn are: ; ; p0 the beginning of both space and text ; p1 the beginning of a specific segment ; p2 the end of a specific segment ; p3 the end of the entire text ; p4 the physical end of the available space ; ; The uniform method to designate intervals is that the ; beginning is inclusive, while the end is exclusive. In ; that way a null interval can be represented through two ; equal pointers. The workspace operators and predicates ; move data around and compare it with the help of these ; pointers. Other operators move data between the work- ; space and the pushdown list. ; ; The complete list of operators and predicates is: ; ; A advance pointer 1 if possible ; B retract pointer 1 if possible ; D delete interval between p1 and p2 ; E test equality between workspace and PDL ; F find a specified segment in the workspace ; I insert in the workspacve following p2 ; J move p1 to beginning ; M test workspace for lexicographically larger ; Q move interval between p1 and p2 to PDL ; U find segment between delimiters ; V find segment including delimiters ; Y restore pointer 1 to previous value ; Z move p2 to end ; a extend interval from p1 if possible ; b extend interval to p2 if possiblle ; e extend limit of text if possible ; f fill if space is available ; h fetch workspace header for PDL ; j null interval at front of segment ; q place p1, p2-p1 on PDL ; w store (or possibly exchange) workspace header ; z null interval at end of segment ; < restrict workspace to segment p1-p2 ; > open workspace to previous span ; ; Version released during the Summer School, 1980. ; ; MKV86 - Copyright (C) 1980 ; Universidad Autonoma de Puebla ; All Rights Reserved ; ; [Harold V. McIntosh, 25 April 1982] ; ; May 1, 1982 - M tests for an interval rather than making ; a simple comparison. should be used ; where was formerly written. In general, ; tests for a .LE. WS .LE. b and leaves ; P1 and P2 surrounding such a segment. Both a ; and b are always lifted. ; May 29, 1983 - h discontinued; use ''w instead ; ======================================================= ; ======================================================= ; Workspace subroutines for REC/MARKOV. ; ======================================================= ; (a) Generate a segment of length n forward from p1 if ; possible. A predicate which, when FALSE, leaves the ; deficit by which the segment could not be formed on the ; pushdown list. LCA: call CXLD ;load n into (cx) add cx,P1 cmp P3,cx jc LAF sto cx,P2 jmp SKP ;return with the value TRUE LAF: sub cx,P3 push cx ;argument to putw must be on 8080 stack call PUTW ;record deficit ret ;FALSE return: won't do ; (A) Pointer 1 advances by one byte, if possible. UCA: ld ax,P1 ;fetch pointer 1 inc ax ;advance by 1 cmp P3,ax jc UAF ;forget it, return FALSE if past limit sto ax,P1 ;we can safely store pointer 1 cmp P2,ax ;but it still must not pass pointer 2 jnc UA2 ;but it still must not pass pointer 2 sto ax,P2 ;if it did, pointer 2 = pointer 1 UA2: jmp SKP ;but in either event, return TRUE UAF: ret ; (b) Generate a segment of length n backward from p2, ; if possible. LCB: call CXLD ;load top argument into (cx), erase it ld ax,P2 ;fetch pointer 2 into (HL) sub ax,cx ;calculate difference cmp ax,P0 jc LBF ;low limit passed, abandon the attempt sto ax,P1 ;record it jmp SKP ;generate TRUE return for the predicate LBF: ret ; (B) Pointer 1 retreats by one byte, if possible. UCB: ld ax,P1 ;fetch pointer dec ax ;move backwards one byte cmp ax,P0 ; jc UBF sto ax,P1 ;otherwise store new position jmp SKP ;and return with value TRUE UBF: ret ;return false as NOP if passed ; (D) Delete text between pointers 1 and 2. UCD: ld si,P2 ;fetch end of deleted text ld di,P1 ;fetch beginning of deleted text cmp si,di jz UD2 ;null interval to delete sto di,P2 ;after deletion, interval is null ld cx,P3 sub cx,si ;length of string to move jz UD1 cld mov ax,ds mov es,ax rep movsb UD1: sto di,P3 ;destination limit is new end of text UD2: ret ; (e) Attempt to move pointer 3 forward. If insufficient ; space remains, this predicate is false and places the ; amount of remaining space [p4-p3] on the pushdown list. ; If the extension is possible, P1 and P2 will surround it. LCE: call CXLD ;load argument into (cx), pop it add cx,P3 cmp P4,cx jc LEF ;skip if increment not greater ld ax,P3 sto cx,P2 sto cx,P3 sto ax,P1 ;make it the beginning of new interval jmp SKP ;TRUE return LEF: sub cx,P4 push cx ;putw requires argument on 8080 stack call PUTW ;insert balance on pushdown list ret ;TRUE return: won't work ; (E) Check equality between the pushdown list and the ; workspace. The top argument will be erased whatever ; the outcome of the test, a characteristic common to ; E, F, M, U, and V. If there is a segment originating ; at p1 which is equal to the argument, p2 will move to ; delimit it, otherwise p2 remains unchanged. UCE: ld cx,PY ;end of top argument ld si,PX ;beginning of top argument ld di,P1 ;beginning of workspace segment in (HL) sub cx,si jcxz UEN ;''E is TRUE, sets P2=P1 cld mov ax,ds mov es,ax repz cmpsb jnz UEF ;mismatch terminated scan cmp P3,di ;see whether we've run over jc UEF UEN: sto di,P2 jmp CUCL ;skip for TRUE UEF: jmp UCL ;FALSE for one reason or another ; (f) Fill. <'XXX' f> will replace the text following ; pointer 1 by XXX if pointer 2 does not conflict, ; whereupon pointers 1 and 2 delimit what remains of ; the interval they originally encompassed, making this ; predicate TRUE. Otherwise it will be FALSE, with the ; pointers unaltered. When f fails, its argument is ; erased, otherwise the insert is conserved for repeated ; use in a block fill. f is true even if its argument ; completely fills the available space, since it reports ; whether or not an insertion took place rather than ; whether any space remains. LCF: ld si,PX ld cx,PY sub cx,si ld di,P1 ;load p2 ld dx,P2 ;now load p1 sub dx,di cmp dx,cx ;compare this to p2 jnc LFT jmp UCL ;insert too big, FALSE and erase insert LFT: cld mov ax,ds mov es,ax rep movsb sto di,P1 ;final destination location is new p1 jmp SKP ;keep insert, generate TRUE return ; (F) Search for text. The text in the workspace is ; examined from left to right beginning at pointer 1 to ; see if the object of comparison on the pushdown list ; can be found. If so, its replica in the workspace is ; bracketed by pointers 1 and 2, while the model itself ; is discarded. Even if no replica is found, the model ; is still discarded, but then the value is FALSE and the ; pointers 1 and 2 retain their original sites. Should ; several replicas exist, only the first is taken; if ; consecutive searches for the same object are made, the ; same replica will be found repeatedly. This permits ; nested fragments of the same object to be found on ; successive searches, but requires an intermediate ; collapse of the interval p1-p2 in favor of p2 [say by ; using ''I] to scan multiple occurrences of the same ; object. UCF: ld dx,PY ld bx,PX ld di,P1 sub dx,bx ;length of comparison object jz UFX ;fast exit for null object ld cx,P3 sub cx,di ;length of search field cmp cx,dx jc UFF ;useless to even try cld ;searches go forward mov ax,ds mov es,ax ;CP/M might derange ES dec dx jz UFS ;look for single byte UF1: mov si,bx lodsb repnz ;search by increment until limit scasb jnz UFF ;end without even 1st word cmp cx,dx ;are there enough chars left jc UFF ;can't possibly fit it in push cx push di mov cx,dx repz cmpsb jz UFT pop di ;comparison failed pop cx jmp UF1 ;go on where we left off UFT: pop ax ;comparison accomplished pop cx UFV: dec ax sto ax,P1 UFX: sto di,p2 jmp CUCL UFS: ld al,[bx] repnz scasb jnz UFF mov ax,di jmp UFV UFF: jmp UCL ; (I) Insert text following pointer 2; upon completion ; pointers bracket inserted material. If the proposed ; insertion will not fit, an error indicator is generated ; and the attempt is abandoned. An F followed by an I ; will insert material after the found text, but if an ; intermediate ''F is given, insertion will be made at ; the front of the text. A ''I may be used to form null ; intervals, if they are desired at the end of a segment. UCI: ld ax,P2 ;point where insertion will be made sto ax,P1 ;pointer 1 to precede inserted material ld dx,PY ;source origin sub dx,PX ;source end jz UIN ;null insert ld si,P3 ld cx,P4 sub cx,si cmp cx,dx jc UIE ;insufficient space for insert mov cx,si sub cx,P2 add P2,dx add P3,dx mov ax,ds mov es,ax jcxz UIA ld di,P3 dec si dec di std rep movsb UIA: mov cx,dx ld si,PX ;source origin into (DE) ld di,P1 ;new p1 is destination origin, in (HL) cld rep movsb UIN: jmp UCL ;I removes its argument upon completion UIE: call RER ; (j) Null interval at p1. Equivalent to ''F or ''E. LCJ: ld ax,P1 ;pointer to beginning of interval sto ax,P2 ;make end of interval the same ret ; (J) Back to beginning of workspace. Extends whatsoever ; interval back to the beginning of the entire text. UCJ: ld ax,P0 ;fetch pointer to beginning of text sto ax,P1 ;make it the beginning of the interval ret ; (M) Compare inequality between the pushdown list and ; the workspace. M is TRUE if there is a segment in the ; workspace beginning at p1 which is lexicographically ; larger than the argument on the pushdown list. In that ; event, p2 marks the "larger" interval, which terminates ; after the first byte in the workspace which is larger - ; by unsigned integer comparison - than the argument. M ; is also TRUE if an equal segment lies in the workspace. ; According to the common style of E, F, I, M, U, and V, ; the argument is erased whatever might be the outcome of ; the comparison. Likewise, FALSE results in no pointer ; changes. UCM: ld cx,PY ld si,PX sub cx,si push cx push si call UCL ld cx,PY ;pointer to argument to be compared ld si,PX ;pointer to workspace under comparison sub cx,si call UCL ld bp,P1 jcxz UMT ;trivial comparison to be made cld mov ax,ds mov es,ax mov di,bp UML: cmpsb ;(SI) - (DI) sets flags jc UMM ;decided if WS > PD jnz UMF ;failure if WS < PD loop UML ;repeat for WS = PD UMM: xchg di,bp cmp P3,bp jc UMF ;fail if we run over pop si pop cx jcxz UMT ;treat '' as 'no comparison' mov ax,cx mov cx,bp sub cx,di cmp ax,cx ;set flags by l(PD) - l(WS) sbb dx,dx ;(nc) when l(WS) .LE. l(PD) jz UMN ;(z): workspace is shorter, equal xchg ax,cx ;check only common segment UMN: cmpsb ;set flags from PD - WS jc UMG ;WS > PD means outside inclusive bound jnz UMT ;WS < PD means bound is satisfied loop UMN ;WS = PD means keep testing or dx,dx jnz UMG ;(z): workspace fulfils 'short before long' UMT: sto bp,P2 jmp SKP UMF: add sp,#4 UMG: ret ; (q) Put p1, p2-p1 on PDL. LCQ: ld ax,P2 sub ax,P1 push ax ;stash P2-P1 on 8086's PDK push P1 ;and then P1 on top of that call PUTW ;one argument from top of 8080 stack call PUTW ;then another so we've got both ret ;cannot use jmp putw for call putw, ret ; (Q) Copy workspace to pushdown. The interval between ; p1 and p2 is placed on the pushdown list. UCQ: ld si,P1 ;fetch beginning of interval ld cx,P2 ;fetch end of interval sub cx,si ;length of interval into (cx) call NARG ;close old arg, check space, def new ld di,PX ;fetch destination origin for (HL) cld mov ax,ds mov es,ax rep movsb sto di,PY ;destination end is argument end ret ; (w) Store workspace header. There are two forms of ; this operator which are available: the argument may be ; either a single block of ten bytes, or else the pair ; . The distinction is implicit, according to ; whether the top argument has two bytes or ten bytes. ; The first case arises from a previous usage of h (or ; perhaps deliberate construction), while the second is ; more likely in the initial allocation of a workspace. A ; single block is erased after use, while it is supposed ; that a previous definition existed when the pair form ; is used. In such a case, the old block of bytes would ; be substituted for the two arguments in case it needed ; to be restored later. When a workspace is generated ; from its specifications, pointers 1 and 2 as well as 3 ; are set to encompass the entire space. Many other forms ; can be arranged by a subsequent use of j, D, and so on. LCW: ld si,PX ld cx,PY sub cx,si ;determine size of argument cmp cx,#10 ;ten for comparison jnz LWW ;if length not 10, suppose it was 2 cmp cx,#0 jnz LWX call UCL LWH: ld cx,#10 ;10 bytes required from PDL call NARG ;verify space, new px into (HL) ld si,#P0 ;source is pointer block itself cld mov di,bx mov ax,ds mov es,ax rep movsb sto di,PY ;final destination is arg end ret LWX: ld di,#P0 ;p0 is destination origin cld mov ax,ds mov es,ax rep movsb jmp UCL ;erase the argument LWW: ld bx,PX ;create p0...p4 from org,siz call TWOL ;trnsfr two args from PDL to 8080 stack call LWH ;place existing header on PDL pop bx ;recover under arg, namely org pop cx ;recover upper arg, namely siz sto bx,P0 ;origin of workspace sto bx,P1 ;segment at front of workspace add bx,cx ;add size sto bx,P2 ;segment traverses whole workspace sto bx,P3 ;which defines end of occupied text sto bx,P4 ;as well as physical end ret ; (U) Search for interval, excluding limits. The object ; of the search is defined by its delimiters: thus if the ; text YYY is to be found, it must be specified as the ; one sandwiched between XXX and ZZZ. Then by executing ; 'XXX'F followed by 'ZZZ'U pointers 1 and 2 will bracket ; YYY. U erases its argument, whether TRUE or FALSE, by ; a custom common to all search or comparison predicates. ; By the same custom, pointers 1 and 2 remain unmoved if ; the search fails. ''U generates a null interval at the ; end of the last interval that was created. UCU: push P1 ;fetch beginning of last interval ld ax,P2 ;fetch end of last interval sto ax,P1 ;search begins at end of p1-p2 interval push ax call UCF ;use the search subroutine jmp UUF ;search failed [MUST be 3-byte jmp] <=== ld ax,P1 ;beginning of found interval sto ax,P2 ;is end of result interval pop P1 ;recover end of last interval pop ax ;discard beginning of last interval jmp SKP ;TRUE return from predicate UUF: pop ax ;discard end of last interval-it's same pop P1 ;recover beginning of last interval ret ;FALSE return from predicate ; (V) Search for interval, including limits. This ; predicate is similar to U, the difference being that ; after a successful search, p1 and p2 bracket both the ; delimiters as well as the text which they define, ; whereas U merely brackets the intervening text. UCV: push P1 ;pointer to beginning of last interval ld ax,P2 sto ax,P1 ;pointer to end of last interval call UCF ;predicate F always makes the search jmp UVF ;search failed, F was FALSE pop P1 ;recover the old p1 jmp SKP ;TRUE return with old p1, new p2 UVF: pop P1 ;recover original p1 ret ;FALSE return with p1 and p2 unchanged ; (Y) Recover pointer 1. There are those times when it ; is desirable to note a spot in the workspace, through ; qL for example, and then be able to return to it later ; on. However intervening deletions, insertions or even ; workspace openings and closings may have rendered it ; invalid so a check is made to ensure the preservation ; of the relative order of p0, p1, and p3. If p2 lies ; in an acceptable range, it is untouched; otherwise it ; is set to define a null interval at p1. UCY: ld bx,PX ld cx,PY sub cx,bx cmp cx,#4 jz UYI ld bx,[bx] ;fetch low byte of pointer cmp bx,P0 ;check that p1 will be greater or equal jnc UYM ;if less, store p0 instead ld bx,P0 jmp UYZ UYM: cmp P3,bx ;check that p1 will be less or equal jnc UYN ;if not, make p2 = p1 = p3 ld bx,P3 jmp UYY UYN: cmp P2,bx ;check that p1 less or equal to p2 jnc UYZ UYY: sto bx,P2 ;record null interval between p1 and p2 UYZ: sto bx,P1 ;give p1 whatever value it'll have jmp CUCL ;pop the argument UYI: ld cx,[bx] ld bx,[bx+2] cmp cx,P0 jc UYF add bx,cx cmp P3,bx jc UYF sto cx,P1 sto bx,P2 jmp CUCL UYF: jmp UCL ; (z) Null interval at end of segment. Equivalent to ; ''I, ''U, or 0b. LCZ: ld ax,P2 ;pointer to end of interval sto ax,P1 ;make beginning of interval the same ret ; (Z) Move p2 to the end of the workspace, thereby ; extending whatever interval on to the end of the text. UCZ: ld ax,P3 ;pointer to the end of text sto ax,P2 ;make end of interval the same ret ; (<) Close down workspace. The workspace is confined to ; the interval between pointers 1 and 2. The reason for ; this could be to restrict the editing operations to a ; smaller range, or it could be to have absolute freedom ; to work over some material before incorporating it into ; the main text. As a practical matter, the text between ; pointers 2 and 3 is displaced to the far end of the ; workspace and the original values of pointers 0 and 4 ; are recorded before setting up the new values of the ; pointers. Subsequent insertions and deletions then have ; much less material to move. BRA: ld di,P4 ld si,P3 mov cx,di sub cx,si sub cx,#4 jc BRE mov cx,si sub cx,P2 dec di dec si std mov ax,ds mov es,ax rep movsb dec di ld ax,P4 stos sto di,P4 ld ax,P0 stos ld ax,P2 sto ax,P3 ld ax,P1 sto ax,P0 ;store it as new beginning of text ret ;p2 remains at end of newly made text BRE: call RER ; (>) Open up the workspace. This is the complementary ; operator to <, which is used to return the scope of the ; pointers p0 and p4 to their original range. The text ; forming the restricted workspace is incorporated in its ; entirity in place of the material originally lying in ; the interval p1-p2. An error condition can arise from ; opening a workspace that was never closed, but it will ; be anulled if a zero address was placed at the pointer ; 4 during initialization of the workspace. KET: ld bx,P4 ;load the end of the universe ld cx,[bx] jcxz KEE ;zero means opening too many times sto cx,P0 ;restore it ld cx,[bx+2] sto cx,P4 add bx,#4 sub cx,bx mov si,bx ld di,P3 ;end of txt is dest to replace old tail cld mov ax,ds mov es,ax rep movsb sto di,P3 ;destination end is new end of text ret KEE: call RER ;note error and abandon attempt LINK FXT86.ASM