; ======================================================= ; ; ; 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 jb LAF mov P2,cx 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: mov ax,P1 ;fetch pointer 1 inc ax ;advance by 1 cmp P3,ax jb UAF ;forget it, return FALSE if past limit mov P1,ax ;we can safely store pointer 1 cmp P2,ax ;but it still must not pass pointer 2 jnb UA2 ;but it still must not pass pointer 2 mov P2,ax ;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 mov ax,P2 ;fetch pointer 2 into (HL) sub ax,cx ;calculate difference cmp ax,P0 jb LBF ;low limit passed, abandon the attempt mov P1,ax ;record it jmp SKP ;generate TRUE return for the predicate LBF: ret ; (B) Pointer 1 retreats by one byte, if possible. UCB: mov ax,P1 ;fetch pointer dec ax ;move backwards one byte cmp ax,P0 ; jb UBF mov P1,ax ;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: mov si,P2 ;fetch end of deleted text mov di,P1 ;fetch beginning of deleted text cmp si,di jz UD2 ;null interval to delete mov P2,di ;after deletion, interval is null mov cx,P3 sub cx,si ;length of string to move jz UD1 cld mov ax,ds mov es,ax repnz movs byte [di],[si] UD1: mov P3,di ;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 jb LEF ;skip if increment not greater mov ax,P3 mov P2,cx mov P3,cx mov P1,ax ;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: mov cx,PY ;end of top argument mov si,PX ;beginning of top argument mov 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 cmps byte [di],[si] jnz UEF ;mismatch terminated scan cmp P3,di ;see whether we've run over jb UEF UEN: mov P2,di 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: mov si,PX mov cx,PY sub cx,si mov di,P1 ;load p2 mov dx,P2 ;now load p1 sub dx,di cmp dx,cx ;compare this to p2 jnb LFT jmp UCL ;insert too big, FALSE and erase insert LFT: cld mov ax,ds mov es,ax repnz movs byte [di],[si] mov P1,di ;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: mov dx,PY mov bx,PX mov di,P1 sub dx,bx ;length of comparison object jz UFX ;fast exit for null object mov cx,P3 sub cx,di ;length of search field cmp cx,dx jb 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 lods byte [si] repnz scas byte [di] ;search by increment until limit jnz UFF ;end without even 1st word cmp cx,dx ;are there enough chars left jb UFF ;can't possibly fit it in push cx push di mov cx,dx repz cmps byte [di],[si] 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 mov P1,ax UFX: mov p2,di jmp CUCL UFS: mov al,[bx] repnz scas byte [di] 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: mov ax,P2 ;point where insertion will be made mov P1,ax ;pointer 1 to precede inserted material mov dx,PY ;source origin sub dx,PX ;source end jz UIN ;null insert mov si,P3 mov cx,P4 sub cx,si cmp cx,dx jb 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 mov di,P3 dec si dec di std repnz movs byte [di],[si] UIA: mov cx,dx mov si,PX ;source origin into (DE) mov di,P1 ;new p1 is destination origin, in (HL) cld repnz movs byte [di],[si] UIN: jmp UCL ;I removes its argument upon completion UIE: call RER ; (j) Null interval at p1. Equivalent to ''F or ''E. LCJ: mov ax,P1 ;pointer to beginning of interval mov P2,ax ;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: mov ax,P0 ;fetch pointer to beginning of text mov P1,ax ;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: mov cx,PY mov si,PX sub cx,si push cx push si call UCL mov cx,PY ;pointer to argument to be compared mov si,PX ;pointer to workspace under comparison sub cx,si call UCL mov bp,P1 jcxz UMT ;trivial comparison to be made cld mov ax,ds mov es,ax mov di,bp UML: cmps byte [di],[si] ;(SI) - (DI) sets flags jb UMM ;decided if WS > PD jnz UMF ;failure if WS < PD loop UML ;repeat for WS = PD UMM: xchg di,bp cmp P3,bp jb 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: cmps byte [di],[si] ;set flags from PD - WS jb 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: mov P2,bp jmp SKP UMF: add sp,4 UMG: ret ; (q) Put p1, p2-p1 on PDL. LCQ: mov 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: mov si,P1 ;fetch beginning of interval mov cx,P2 ;fetch end of interval sub cx,si ;length of interval into (cx) call NARG ;close old arg, check space, def new mov di,PX ;fetch destination origin for (HL) cld mov ax,ds mov es,ax repnz movs byte [di],[si] mov PY,di ;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: mov si,PX mov 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: mov cx,10 ;10 bytes required from PDL call NARG ;verify space, new px into (HL) mov si,(offset P0) ;source is pointer block itself cld mov di,bx mov ax,ds mov es,ax repnz movs byte [di],[si] mov PY,di ;final destination is arg end ret LWX: mov di,(offset P0) ;p0 is destination origin cld mov ax,ds mov es,ax repnz movs byte [di],[si] jmp UCL ;erase the argument LWW: mov 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 mov P0,bx ;origin of workspace mov P1,bx ;segment at front of workspace add bx,cx ;add size mov P2,bx ;segment traverses whole workspace mov P3,bx ;which defines end of occupied text mov P4,bx ;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 mov ax,P2 ;fetch end of last interval mov P1,ax ;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] <=== mov ax,P1 ;beginning of found interval mov P2,ax ;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 mov ax,P2 mov P1,ax ;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: mov bx,PX mov cx,PY sub cx,bx cmp cx,4 jz UYI mov bx,[bx] ;fetch low byte of pointer cmp bx,P0 ;check that p1 will be greater or equal jnb UYM ;if less, store p0 instead mov bx,P0 jmp UYZ UYM: cmp P3,bx ;check that p1 will be less or equal jnb UYN ;if not, make p2 = p1 = p3 mov bx,P3 jmp UYY UYN: cmp P2,bx ;check that p1 less or equal to p2 jnb UYZ UYY: mov P2,bx ;record null interval between p1 and p2 UYZ: mov P1,bx ;give p1 whatever value it'll have jmp CUCL ;pop the argument UYI: mov cx,[bx] mov bx,2[bx] cmp cx,P0 jb UYF add bx,cx cmp P3,bx jb UYF mov P1,cx mov P2,bx jmp CUCL UYF: jmp UCL ; (z) Null interval at end of segment. Equivalent to ; ''I, ''U, or 0b. LCZ: mov ax,P2 ;pointer to end of interval mov P1,ax ;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: mov ax,P3 ;pointer to the end of text mov P2,ax ;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: mov di,P4 mov si,P3 mov cx,di sub cx,si sub cx,4 jb BRE mov cx,si sub cx,P2 dec di dec si std mov ax,ds mov es,ax repnz movs byte [di],[si] dec di mov ax,P4 stos word [di] mov P4,di mov ax,P0 stos word [di] mov ax,P2 mov P3,ax mov ax,P1 mov P0,ax ;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: mov bx,P4 ;load the end of the universe mov cx,[bx] jcxz KEE ;zero means opening too many times mov P0,cx ;restore it mov cx,2[bx] mov P4,cx add bx,4 sub cx,bx mov si,bx mov di,P3 ;end of txt is dest to replace old tail cld mov ax,ds mov es,ax repnz movs byte [di],[si] mov P3,di ;destination end is new end of text ret KEE: call RER ;note error and abandon attempt END