;----------------------------------------------------------------- ; anima.asm ; --------- ; Assembly Anim stuff. ;----------------------------------------------------------------- include asm.inc include anim.inc HEADER anima extrn _printf:far ;----------------------------------------------------------------- DSEG IF 0 extrn _doDump:byte public _TotBytes runMsg db 'run %x',13,0 dumpMsg db 'dump %x',13,0 skipMsg db 'skip %x',13,0 clipMsg db '---dest:%x ',0 finalMsg db '---dest:%x last-row-bytes:%x--- ',0 _TotBytes dw 0 ENDIF ENDDS PSEG anima ;----------------------------------------------------------------- ; void ShortDelay(UWORD delay); ; Does a high-speed delay loop. ;----------------------------------------------------------------- public _ShortDelay STARTPROC _ShortDelay SD_DELAY = ARGB push bp mov bp,sp mov cx,SD_DELAY[bp] sd_loop: loop sd_loop pop bp ret ENDPROC _ShortDelay IF 1 ; RunSkipDump format. ;----------------------------------------------------------------- ; UWORD PlayRunSkipDump(UWORD nBytes, srcSeg, srcAddr, dstSeg, dstAddr); ; Finishes playing the Bitmap body with type==RunSkipDump. ; RETURN final dstAddr (one byte past last pixel written). ; NOTE: since a final skip is omitted, this could be anywhere on the screen. ; If there is no final skip, it will be one past the last pixel on the screen. ; For a screen starting at address (offset) 0, its greatest value is ; the # pixels on the screen. ; NOTE: we're ignoring nBytes. Bad data could cause this to write past ; end of dst buffer, or even to loop forever. ;----------------------------------------------------------------- public _PlayRunSkipDump STARTPROC _PlayRunSkipDump PR_N_BYTES = ARGB PR_SRC_SEG = ARGB+2 PR_SRC_ADDR = ARGB+4 PR_DST_SEG = ARGB+6 PR_DST_ADDR = ARGB+8 push bp mov bp,sp push si push di push es mov si,PR_SRC_ADDR[bp] mov di,PR_DST_ADDR[bp] mov es,PR_DST_SEG[bp] push ds ; Save DS:DGROUP. mov ds,PR_SRC_SEG[bp] ; SET DS:dstSeg. NOT DGROUP. sub ch,ch ; SET CH = 0. jmp short nextOp skip: sub cl,80h ; Strip off sign bit, leaving skip cnt. jz longOp ; cnt==0 indicates a long op. ; --- shortSkip --- add di,cx ; skip # pixels. (CH=0) ; --- variation on NEXTOP inline to minimize jmp's --- nextOp: ; Get and decode next op. mov cl,[si] inc si jcxz run or cl,cl ; Test CL's sign bit. jl skip dump: rep movsb ; copy # pixels. (CH=0) ; --- variation on NEXTOP inline to minimize jmp's --- mov cl,[si] inc si or cl,cl ; Test CL's sign bit. jl skip jg dump run: mov cl,[si] ; 8-bit unsigned count. inc si lodsb ; pixel value. rep stosb ; set # pixels to value. (CH=0) ; --- variation on NEXTOP inline to minimize jmp's --- mov cl,[si] inc si jcxz run or cl,cl ; Test CL's sign bit. jl skip jmp short dump longOp: ; NOTE: if load into CX, must clear CH afterwards. lodsw ; 16-bit unsigned count. or ax,ax ; set flags. jle notLongSkip ;longSkip: add di,ax ; skip # pixels. jmp short nextOp ; longSkip only used for > 2*127, so can't be very many, ; so don't bother saving one jmp with inline NEXTOP. notLongSkip: jz stop ; long count of zero means "stop code". mov cx,ax ; may SET CH non-zero. sub ch,80h ; Clear sign bit. cmp ch,40h jge longRun ; --- For maximum speed on longDump, caller should insure src & dst are ; aligned. To do so, caller must calculate whether ; src DATA will begin on same (odd or even) alignment as dst data. ; If not, first put a 1-byte Dump, which occupies 2 src bytes, thereby ; shifting relative alignment (src moves 2, dst moves 1). ;longDump test si,1 ; Insure src word-aligned. ; In case caller didn't sync src & dst, we chose ; to align src because we know it is of benefit -- ; aligning dst on 8-bit video cards might not be of ; any benefit. jz dumpWordAligned movsb ; get to word boundary. dec cx dumpWordAligned: shr cx,1 ; Convert byte count to word count. jc dumpOddByte rep movsw ; Word-aligned. longOpDone: sub ch,ch ; SET CH = 0. jmp short nextOp dumpOddByte: rep movsw ; Word-aligned. movsb jmp short longOpDone longRun: sub ch,40h ; Clear "longRun" bit. lodsb mov ah,al ; Replicate byte to word value. test di,1 ; Insure dst word-aligned. jz runWordAligned stosb dec cx runWordAligned: shr cx,1 ; Convert byte count to word count. jc runOddByte rep stosw ; Word-aligned. jmp short longOpDone runOddByte: rep stosw ; Word-aligned. stosb jmp short longOpDone stop: pop ds ; Restore DS:DGROUP. mov ax,di ; RETURN final dstAddr. pop es pop di pop si pop bp ret ENDPROC _PlayRunSkipDump ;----------------------------------------------------------------- ; UWORD MakeRunSkipDump(UWORD src1Seg, src2Seg, finalPixelAddr, ; UWORD dstSeg, dstAddr, dstLimit, UWORD *nBytesP, ; UWORD srcWidth, finalRowAddr, finalRowWidth, ; BOOL inFinalRow, skipPermitted); ; Finishes making the Bitmap body with type==RunSkipDump. ; RETURN TRUE if fit in deltaX. ; SIDE_EFFECT: set deltaX.nBytes, store that many bytes at deltaX.(seg:addr). ; ASSUMES srcs start at Addr=0. ;----------------------------------------------------------------- ADDR = 0 ; Field-offsets within a far ptr. 16-bit-offset. SEGM = 2 ; Segment. ; --- registers --- ; AL: pixel. Value of current src2 pixel. ; AH: runPix. Value of pixels in current Run. ; BL: skipInDump. # skippable bytes in Dump or Run. ; BH: runInDump. # runnable bytes in Dump. ; CX: wordCnt. # pixels processed for current op. ; DX: -- temporary. ; DS:SI: src2. ; ES:DI: src1 or dst. -- careful, ES:DI used for two purposes. public _MakeRunSkipDump STARTPROC _MakeRunSkipDump MR_SRC1_SEG = ARGB MR_SRC2_SEG = ARGB+2 MR_FINAL_PIXEL_ADDR = ARGB+4 MR_DST_SEG = ARGB+6 MR_DST_ADDR = ARGB+8 ; dst0 -- save so can calculate size. MR_DST_LIMIT = ARGB+10 ; dstLimit => dstAddr musn't pass this. MR_N_BYTES_PTR = ARGB+12 MR_SRC_WIDTH = ARGB+14 MR_FINAL_ROW_ADDR = ARGB+16 MR_FINAL_ROW_WIDTH = ARGB+18 MR_IN_FINAL_ROW = ARGB+20 MR_SKIP_PERMITTED = ARGB+22 ; --- locals MR_SRC1_FAR = -4 ; ES:DI when addressing src1. MR_DST_FAR = -8 ; ES:DI when addressing dst. MR_MUST_STOP = -9 ; BOOL mustStop. Have reached end of src data. ;unused byte... MR_??? = -10 ; BOOL ??? MR_X = -12 ; current x-coordinate. MR_LARGE_SKIP_LIMIT = -14 ; At or beyond here, don't try to do large skip. MR_WAS_X = -16 ; x-coordinate where op's data started. mLOCALS = 16 ; Total local space to save. push bp mov bp,sp sub sp,mLOCALS push si push di push es IF 0 ; debugging mov _TotBytes,0 ENDIF ; --- Simplify "incompressible" checking. We don't care if we are ; slightly over-conservative. 7 allows 3B for long skip, 4B for any ; other op. This allows us to not check at all on a short skip ; (a >4B series of short skips would never be done -- long skip done instead), ; and to check run or dump overhead without subtracting the size of the ; overhead. sub word ptr MR_DST_LIMIT[bp],7 ; --- Simplify check of limit for "large skip" code. The limit is determined ; by the need to have CLIP_X see the transition to the final scan row. mov dx,MR_FINAL_ROW_ADDR[bp] sub dx,MR_SRC_WIDTH[bp] ; Paranoia -- a whole row before final. sub dx,4 ; Make sure CLIP_X gets a chance. mov MR_LARGE_SKIP_LIMIT[bp],dx ; --- Prepare far ptrs for "les di,...". sub di,di ; SET src1. mov ax,MR_SRC1_SEG[bp] mov MR_SRC1_FAR+ADDR[bp],di mov MR_SRC1_FAR+SEGM[bp],ax mov di,MR_DST_ADDR[bp] ; SET dst. SET DI:dstAddr. mov ax,MR_DST_SEG[bp] mov MR_DST_FAR+ADDR[bp],di mov MR_DST_FAR+SEGM[bp],ax ; --- ASSUME srcs start at Addr=0. sub si,si ; SET (DS:SI):src2. push ds ; Save DS:DGROUP. mov ds,MR_SRC2_SEG[bp] ; SET DS:dstSeg. NOT DGROUP. ; --- DGROUP NOT ACCESSIBLE VIA DS BELOW. USE SS IF NEED DGROUP. --- les di,MR_DST_FAR[bp] ; SET ES:DI:dst. mov ax,BMBODY_RUNSKIPDUMP stosw ; *(UWORD far *)dst++ = ; 2-byte header containing type. mov MR_DST_FAR+ADDR[bp],di ; Save dstAddr, so DI can be re-used. les di,MR_SRC1_FAR[bp] ; *** SET ES:DI:src1 for loops *** sub dx,dx ; 0. mov MR_X[bp],dx ; "x=0;" mov MR_MUST_STOP[bp],dl ; "mustStop = FALSE;" notInASequence: sub cx,cx ; CX:wordCnt=0. ; ----------------------- Dump ------------------------------------------- ; ASSUME ES:DI: src1. ; "pixel" must be set when jump to beDump with "wordCnt != 0". beDump: mov dx,MR_X[bp] ; sub dx,cx ; CX:wordCnt. jge Dxw add dx,MR_SRC_WIDTH[bp] ; started on previous line Dxw: mov MR_WAS_X[bp],dx sub bx,bx ; SET BL:skipInDump=0, BH:runInDump=0. or cx,cx ; test CX:wordCnt. jz DnoCnt inc bh ; BH:runInDump. Any pixel is run==1. DnoCnt: inDump: mov dx,MR_X[bp] cmp dx,MR_SRC_WIDTH[bp] jne DnoClip call ClipX ; "CLIP_X(stopDump);" jnz stopDump DnoClip: mov ah,al ; AH,AL: "runPix = pixel;" lodsb ; "pixel = *src2++;" Get&count byte inc word ptr MR_X[bp] ; "x++;" inc cx ; CX: "wordCnt++;" cmp al,es:[di] ; AL:pixel. ES:DI:src1. je DSkip ; "if (pixel != *src1)" mov bl,0ffh ; "-1" & inc below => "skipInDump=0" DSkip: inc bl ; BL: "skipInDump++;" inc di ; DI: "src1++;" cmp al,ah ; "if (pixel != runPix)" je DRun sub bh,bh ; "0" & inc below => "runInDump=1" ; Any pixel is a run of 1. DRun: inc bh ; BH: "runInDump++;" cmp bl,MIN_SKIP ; "if (skipInDump == MIN_SKIP" jne DnoSkip test byte ptr MR_SKIP_PERMITTED[bp],1 ; " && skipPermitted)" jz DnoSkip ; --- "PUT_DUMP(src2-wordCnt, wordCnt-MIN_SKIP);" push bx push cx mov bx,si ; SET BX:param1. SI:src2Addr. sub bx,cx ; CX:wordCnt. sub cx,MIN_SKIP ; SET CX:param2 = CX:wordCnt-MIN_SKIP. call PutDump pop cx pop bx mov cx,MIN_SKIP ; CX:wordCnt. jmp beSkip DnoSkip: cmp bh,MIN_RUN ; "if (runInDump == MIN_RUN)" jne noRID ; --- "PUT_DUMP(src2-wordCnt, wordCnt-MIN_RUN);" push bx push cx mov bx,si ; SET BX:param1. SI:src2Addr. sub bx,cx ; CX:wordCnt. sub cx,MIN_RUN ; SET CX:param2 = CX:wordCnt-MIN_RUN. call PutDump pop cx pop bx mov cx,MIN_RUN ; CX:wordCnt. jmp short beRun noRID: ; --- Byte still in Dump. cmp cx,MAX_LONG_DUMP ; "if (wordCnt == MAX_LONG_DUMP)" jne inDump stopDump: ; --- "PUT_DUMP(src2-wordCnt, wordCnt);" push bx push cx mov bx,si ; SET BX:param1. SI:src2Addr. sub bx,cx ; CX:wordCnt. ; ; SET CX:param2 = CX:wordCnt. call PutDump pop cx pop bx test byte ptr MR_MUST_STOP[bp],1 jnz Dstop jmp notInASequence Dstop: jmp beStop ; ----------------------- Run ------------------------------------------- ; ASSUME ES:DI: src1. beRun: mov dx,MR_X[bp] ; sub dx,cx ; CX:wordCnt. jge Rxw add dx,MR_SRC_WIDTH[bp] ; started on previous line Rxw: mov MR_WAS_X[bp],dx sub bx,bx ; SET BL:skipInDump=0. mov ah,al ; "runPix = pixel;" inRun: mov dx,MR_X[bp] cmp dx,MR_SRC_WIDTH[bp] jne RnoClip call ClipX ; "CLIP_X(stopRun);" jnz stopRun RnoClip: lodsb ; "pixel = *src2++;" Get&count byte inc word ptr MR_X[bp] ; "x++;" inc cx ; CX: "wordCnt++;" cmp al,es:[di] ; AL:pixel. ES:DI:src1. je RSkip ; "if (pixel != *src1)" mov bl,0ffh ; "-1" & inc below => "skipInDump=0" RSkip: inc bl ; BL: "skipInDump++;" inc di ; DI: "src1++;" ; Note: check for byte runnable comes later. cmp bl,MIN_RUN_SKIP ; "if (skipInDump == MIN_RUN_SKIP" jne RnoSkip test byte ptr MR_SKIP_PERMITTED[bp],1 ; " && skipPermitted)" jz RnoSkip sub cx,MIN_RUN_SKIP ; retract skip bytes from wordCnt. ; --- "PUT_RUN(wordCnt, runPix);" call PutRun ; CX:wordCnt, AH:runPix. Must Preserve. mov cx,MIN_RUN_SKIP ; CX:wordCnt. jmp short beSkip RnoSkip: cmp al,ah ; "if (pixel != runPix)" je Rrun dec cx ; Retract pixel from Run. ; --- "PUT_RUN(wordCnt, runPix);" call PutRun ; CX:wordCnt, AH:runPix. Must Preserve. mov cx,1 ; CX:wordCnt. jmp beDump ; --- Byte runnable. Rrun: cmp cx,MAX_LONG_RUN ; "if (wordCnt == MAX_LONG_RUN)" jne inRun stopRun: ; --- "PUT_RUN(wordCnt, runPix);" call PutRun ; CX:wordCnt, AH:runPix. Must Preserve. test byte ptr MR_MUST_STOP[bp],1 jnz Dstop ; ("jmp beStop" was out of range.) jmp notInASequence ; ----------------------- Skip ------------------------------------------- beSkip: ; ---------------- Attempt to quickly skip a long ways forward. mov dx,MR_LARGE_SKIP_LIMIT[bp] sub dx,si jb inSkip0 ; Too close to end. push cx ; Hold CX:wordCnt. mov cx,dx ; Permit skip up to skipLimit. shr cx,1 ; As # words. mov dx,cx ; SET DX: # words to cnt down. jcxz skippedAll repe cmpsw je skippedAll ; last words eql; cnt stopped us. sub si,2 ; Retract the non-matching word. sub di,2 inc cx skippedAll: sub dx,cx ; # skip words found. shl dx,1 ; # skip bytes found. pop cx ; Restore CX:wordCnt. add cx,dx ; wordCnt now reflects new skip bytes. add dx,MR_X[bp] ; x += bytes found, mod width. SmodX: sub dx,MR_SRC_WIDTH[bp] ; force it to underflow. jnb SmodX add dx,MR_SRC_WIDTH[bp] ; compensate for underflow. SxOk: mov MR_X[bp],dx ; ---------------- jmp short inSkip ; --- Byte skippable. Final Skip is NOT output prior to Stop. skippable: inc di ; DI: "src1++;" inSkip0: mov dx,MR_X[bp] cmp dx,MR_SRC_WIDTH[bp] je Sclip ; --- fall through to "inSkip". inSkip: lodsb ; "pixel = *src2++;" Get&count byte inc word ptr MR_X[bp] ; "x++;" inc cx ; CX: "wordCnt++;" cmp al,es:[di] ; AL:pixel. ES:DI:src1. je skippable ; "if (pixel != *src1)" ; --- Byte not skippable. Put the Skip. *** AX:temporary.*** ; NOTE: Subtracted enough from dstLimit, that we're not bothering to ; check for incompressible (destination buffer overflow) on short skips. inc di ; DI: "src1++;" push ax ; Hold AX. "jmp SOutDone" for POP.*** ; IF CHANGE # regs pushed, must change SOutDone & Sincompressible. mov MR_SRC1_FAR+ADDR[bp],di ; Update src1Addr from DI. les di,MR_DST_FAR[bp] ; SET ES:DI:dst. dec cx ; "wordCnt--;" Retract pixel. IF 0 call DumpPutSkip ENDIF cmp cx,MAX_SHORT_SKIP ja SnotShort ; --- One Short Skip. mov al,OP_SKIP ; "*dst++ = OP_SKIP | wordCnt;" or al,cl stosb jmp short SOutDone SnotShort: cmp cx,2*MAX_SHORT_SKIP ja Slong ; --- Two Short Skips. mov al,OP_SKIP ; "*dst++ = OP_SKIP | MAX_SHORT_SKIP;" or al,MAX_SHORT_SKIP stosb sub cx,MAX_SHORT_SKIP ; "wordCnt-= MAX_SHORT_SKIP;" mov al,OP_SKIP ; "*dst++ = OP_SKIP | wordCnt;" or al,cl stosb jmp short SOutDone Slong: mov dx,cx ; SET DX:wordCnt0 = wordCnt. Slong0: mov cx,dx ; "while (wordCnt0) {..." jcxz SOutDone cmp di,MR_DST_LIMIT[bp] ; Check for incompressible. ja Sincompressible cmp cx,MAX_LONG_SKIP ; "wordCnt = MIN(wordCnt0,MAX_LONG_SKIP);" jbe Sfits mov cx,MAX_LONG_SKIP Sfits: sub dx,cx ; "wordCnt0 -= wordCnt;" mov al,LONG_OP ; "*dst++ = LONG_OP;" stosb mov ax,cx ; "*((UWORD far *)dst++ = wordCnt;" stosw jmp short Slong0 SOutDone: mov MR_DST_FAR+ADDR[bp],di ; Update dstAddr. les di,MR_SRC1_FAR[bp] ; SET ES:DI:src1. pop ax ; Restore AX. *** mov cx,1 ; CX:wordCnt. jmp beDump Sclip: call ClipX ; "CLIP_X(beStop);" jnz beStop jmp short inSkip ; Skip may be any length up to 64KB-1. Sincompressible: pop ax ; Match "push ax". jmp incompressible ; ----------------------- Stop ------------------------------------------- beStop: mov MR_SRC1_FAR+ADDR[bp],di ; Update src1Addr from DI. les di,MR_DST_FAR[bp] ; SET ES:DI:dst. cmp di,MR_DST_LIMIT[bp] ; Check for incompressible. ja incompressible mov al,LONG_OP ; "*dst++ = LONG_OP;" stosb ; Stop is "LongOp #0". sub ax,ax ; 0. stosw ; "*(UWORD far *)dst++ = 0;" mov ax,di ; "*nBytesP = dst - dst0;" sub ax,MR_DST_ADDR[bp] mov bx,MR_N_BYTES_PTR[bp] mov ss:[bx],ax ; ASSUME SS=DGROUP. mov ax,1 ; RETURN TRUE. jmp short done ; --- Body could not be compressed into given buffer size. ; Consider it incompressible. (Actually, client may have given a small ; buffer, and may wish to re-try with a larger buffer.) incompressible: sub ax,ax ; RETURN FALSE. done: pop ds ; Restore DS: DGROUP. pop es pop di pop si mov sp,bp pop bp ret ; FAR back to client. ; ------ CLIP_X() ----------------------------------------------------------- ; No parameters, returns flags according to "test inFinalRow". ; DX: -- temporary. ASSUME caller allows destruction. ; NOTE: moved per-pixel test into each caller, to save time per pixel. ; Therefore, must already know at line end when call ClipX. ClipX: mov word ptr MR_X[bp],0 test byte ptr MR_IN_FINAL_ROW[bp],1 jz notDone mov byte ptr MR_MUST_STOP[bp],1 IF 0 call DumpFinal ENDIF mov dx,1 or dx,dx ; clear zero flag. retn ; NEAR. notDone: cmp si,MR_FINAL_ROW_ADDR[bp] ; "if (src2Addr == finalRowAddr)" jne notStartingLastLine mov dx,MR_FINAL_ROW_WIDTH[bp] ; "w = finalRowWidth;" mov MR_SRC_WIDTH[bp],dx mov byte ptr MR_IN_FINAL_ROW[bp],1 ; "inFinalRow = TRUE;" notStartingLastLine: IF 0 call DumpClipX ENDIF sub dx,dx ; set zero flag. retn ; NEAR. ; ------ Assembly level routines, with same registers & BP as above, except: ; AX: -- temporary. Each routine must SAVE/RESTORE. ; DX: -- temporary. ASSUME caller allows destruction. ; REMEMBER, DS:src2Seg, NOT DGROUP. ; REMEMBER, ES:DI: src1. DI must be updated to MR_SRC1_FAR+ADDR[bp], if ; need to use it for dst. ; ------ PutRun(CX:wordCnt, AH:runPix) -------------------------------------- ; Must Preserve all registers, including parameters. PutRun: jcxz noRun IF 0 call DumpPutRun ENDIF push ax ; Hold AX. Last, so can access runPix. ; IF CHANGE # regs pushed, must change PutRun & PutDump & Rincompressible. mov MR_SRC1_FAR+ADDR[bp],di ; Update src1Addr from DI. les di,MR_DST_FAR[bp] ; SET ES:DI:dst. cmp di,MR_DST_LIMIT[bp] ; Check for incompressible. ja Rincompressible cmp cx,MAX_SHORT_RUN ; ja lRun ; "wordCnt > MAX_SHORT_RUN" mov ax,MR_WAS_X[bp] add ax,cx cmp ax,MR_SRC_WIDTH[bp] ja lRun ; "CROSSED_W(wordCnt)" mov al,OP_RUN ; "*dst++ = OP_RUN;" stosb mov al,cl ; "*dst++ = wordCnt;" stosb jmp short rBody lRun: ; longRun op. mov al,LONG_OP ; "*dst++ = LONG_OP;" stosb mov ax,cx ; "*((UWORD far *)dst++ = " or ah,LONG_RUN ; " (LONG_RUN<<8) | wordCnt);" stosw rBody: pop ax ; ACCESS AH:runPix. push ax mov al,ah stosb ; "*dst++ = runPix;" mov MR_DST_FAR+ADDR[bp],di ; Update dstAddr. les di,MR_SRC1_FAR[bp] ; SET ES:DI:src1. pop ax ; Restore AX. noRun: retn ; NEAR. Dincompressible: ; NOTE: since we're throwing everything away, doesn't matter that ; bx & cx aren't the top two items at this point. pop ax ; Balance CALLER'S PUSH BX & CX. pop ax Rincompressible: pop ax ; Balances "push ax". pop ax ; Remove (near) return address. jmp short incompressible ; --- PutDump(DS:BX:body, CX:cnt). ; BX: param1. ASSUME caller allows destruction. ; CX: param2. ASSUME caller allows destruction. ; Dump's data is at "body" -- a sequence of pixels in src2seg. ; ALL CALLER'S MUST PUSH BX & CX, AND ONLY those registers, so that ; "Dincompressible" will pop correct #words. PutDump: jcxz noDump ; No data to dump. IF 0 call DumpPutDump ENDIF push ax ; Hold AX. ; IF CHANGE # regs pushed, must change PutRun & PutDump & Rincompressible. mov MR_SRC1_FAR+ADDR[bp],di ; Update src1Addr from DI. les di,MR_DST_FAR[bp] ; SET ES:DI:dst. mov dx,MR_DST_LIMIT[bp] ; Check for incompressible. sub dx,cx jb Dincompressible ; (underflow -- "dstLimit < wordCnt") cmp di,dx ja Dincompressible ; "dstAddr > dstLimit-wordCnt" ; "Rinc..." because "incompressible" was out of range. ; It must be same condition test ("ja")! cmp cx,MAX_SHORT_DUMP ; ja lDump ; "cnt > MAX_SHORT_DUMP" mov ax,MR_WAS_X[bp] add ax,cx cmp ax,MR_SRC_WIDTH[bp] ja lDump ; "CROSSED_W(cnt)" mov al,OP_DUMP ; "*dst++ = OP_DUMP | cnt;" add al,cl stosb jmp short dBody lDump: ; longDump op. mov al,LONG_OP ; "*dst++ = LONG_OP;" stosb mov ax,cx ; "*((UWORD far *)dst++ = " or ah,LONG_DUMP ; " (LONG_DUMP<<8) | cnt);" stosw dBody: xchg bx,si ; Hold SI:src2. SET BX:body. rep movsb ; DS:SI:body, ES:DI:dst, CX:cnt. ; Note: dst is ++ past the new data. xchg bx,si ; Restore SI:src2. mov MR_DST_FAR+ADDR[bp],di ; Update dstAddr. les di,MR_SRC1_FAR[bp] ; SET ES:DI:src1. pop ax ; Restore AX. noDump: retn ; NEAR. IF 0 ; debugging DumpPutRun: push ds push es push ax push bx push cx push dx mov al,ah sub ah,ah push ax push cx mov ax,ss mov ds,ax mov es,ax lea ax,DGROUP:runMsg push ax test _doDump,0ffh jz noR add _TotBytes,cx call _printf noR: add sp,6 pop dx pop cx pop bx pop ax pop es pop ds retn DumpPutDump: push ds push es push ax push bx push cx push dx push cx mov ax,ss mov ds,ax mov es,ax lea ax,DGROUP:dumpMsg push ax test _doDump,0ffh jz noD add _TotBytes,cx call _printf noD: add sp,4 pop dx pop cx pop bx pop ax pop es pop ds retn DumpPutSkip: push ds push es push ax push bx push cx push dx push cx mov ax,ss mov ds,ax mov es,ax lea ax,DGROUP:skipMsg push ax test _doDump,0ffh jz noS add _TotBytes,cx call _printf noS: add sp,4 pop dx pop cx pop bx pop ax pop es pop ds retn DumpClipX: push ds push es push ax push bx push cx push dx mov ax,ss mov ds,ax mov es,ax push di ; dest lea ax,DGROUP:clipMsg push ax test _doDump,0ffh jz noC call _printf noC: add sp,4 pop dx pop cx pop bx pop ax pop es pop ds retn DumpFinal: push ds push es push ax push bx push cx push dx mov ax,ss mov ds,ax mov es,ax mov ax,MR_FINAL_ROW_WIDTH[bp] push ax push di ; dest lea ax,DGROUP:finalMsg push ax test _doDump,0ffh jz noF call _printf noF: add sp,6 pop dx pop cx pop bx pop ax pop es pop ds retn ENDIF ; Debugging ENDPROC _MakeRunSkipDump ENDIF ; RunSkipDump format. IF 0 ; simple dump format. ;----------------------------------------------------------------- ; void AsmPlayDelta(UWORD picSeg, deltaSeg, deltaAddr, deltaBytes); ; Finishes playing the current delta-encoding format. ;----------------------------------------------------------------- public _AsmPlayDelta STARTPROC _AsmPlayDelta APD_PIC_SEG = ARGB APD_DELTA_SEG = ARGB+2 APD_DELTA_ADDR = ARGB+4 APD_DELTA_BYTES = ARGB+6 push bp mov bp,sp push si push di push es mov si,APD_DELTA_ADDR[bp] mov dx,APD_DELTA_BYTES[bp] mov es,APD_PIC_SEG[bp] push ds ; Save DS:DGROUP. mov ds,APD_DELTA_SEG[bp] ; SET DS:deltaSeg. NOT DGROUP. jmp short more ; In case no data. runLoop: lodsw mov di,ax ; SET DI:picAddr lodsw mov cx,ax ; SET CX:runCount rep movsb ; far_movmem more: cmp si,dx ; while (deltaAddr < deltaBytes) jb runLoop pop ds ; Restore DS:DGROUP. pop es pop di pop si pop bp ret ENDPROC _AsmPlayDelta ;----------------------------------------------------------------- ; UWORD MakeDeltaSequence(UWORD picSeg, prevSeg, minAddr, maxAddr, ; deltaSeg, deltaAddr, limitAddr); ; ; Given a sequence of bytes in the same position in two images, ; create a delta-description to convert one image to the other. ; ; Puts the delta at (deltaSeg,deltaAddr). ; RETURNS "deltaBytes" -- # bytes to describe the delta, INCLUDING ; the initial "deltaAddr" value. ; "limitAddr" is offset into deltaSeg that may not be overwritten. ; RETURNS "-1" if doesn't fit. ;----------------------------------------------------------------- DOESNT_FIT = -1 O_PIC_SEG = ARGB O_PREV_SEG = ARGB+2 O_MIN_ADDR = ARGB+4 O_MAX_ADDR = ARGB+6 O_DELTA_SEG = ARGB+8 O_DELTA_ADDR = ARGB+10 O_LIMIT_ADDR = ARGB+12 public _MakeDeltaSequence STARTPROC _MakeDeltaSequence push bp mov bp,sp push si push di push es mov si,O_MIN_ADDR[bp] ; SET SI:picAddr = minAddr. mov es,O_PREV_SEG[bp] ; SET ES:prevSeg mov dx,O_DELTA_SEG[bp] ; SET DX:deltaSeg mov di,O_DELTA_ADDR[bp] ; SET DI:deltaAddr push ds ; Save DS:DGROUP. mov ds,O_PIC_SEG[bp] ; SET DS:picSeg. NOT DGROUP. ; --- Not in a run. Look for differences. sub cx,cx ; SET CX:runCount. sub bx,bx ; SET BX:gapSize. jmp short growGap0 gapStarted: growGap: inc bx ; BX:gapSize. inc si growGap0: cmp si,O_MAX_ADDR[bp] ja endWhileInGap ; --- Cmp pic byte to prev byte. mov al,es:[si] ; ES:prevSeg, SI:picAddr cmp al,[si] ; DS:picSeg, SI:picAddr je growGap ; --- A run is starting. Determine whether to append to previous run, ; or to output previous run. jcxz noPreviousRun ; So won't append initial gap to ; non-existent run. cmp bx,4 ; BX:gapSize jb smallGap endWhileInGap: jcxz done ; NO RUN EVER FOUND. ; --- Output previous run. --- mov ax,di ; Test whether would reach limitAddr. add ax,4 ; Run's addr & count. add ax,cx ; runCount. cmp ax,O_LIMIT_ADDR[bp] jb fits mov ax,DOESNT_FIT ; RETURN "DOESNT_FIT". jmp short fail fits: mov ax,es xchg ax,dx mov es,ax ; SET ES:deltaSeg, DX:prevSeg sub si,bx ; Move back past gap. sub si,cx ; SET SI:runAddr = picAddr - runCount mov ax,si ; *(WORD)(deltaSeg,deltaAddr)= runAddr, stosw ; deltaAddr += 2. mov ax,cx ; *(WORD)(deltaSeg,deltaAddr)= runCount, stosw ; deltaAddr += 2. rep movsb ; far_movmem picSeg|Addr => deltaSeg|Addr, ; runCount bytes. When done, SI:picAddr set past run, ; CX:runCount = 0. add si,bx ; Move forward over gap. mov ax,es xchg ax,dx mov es,ax ; SET ES:prevSeg, DX:deltaSeg cmp si,O_MAX_ADDR[bp] ; Extra check saved a few code bytes. ja done jmp short gapConsumed smallGap: add cx,bx ; Append gap to run. ; jmp short gapConsumed noPreviousRun: gapConsumed: sub bx,bx ; SET BX:gapSize ; --- When reach here, have already found one byte of run. ; ASSUME SI:picAddr still pointing at byte, CX:runCount already is zero. continueRun: inc si ; Move SI:picAddr past byte of run. inc cx ; CX:runCount. cmp si,O_MAX_ADDR[bp] ja endWhileInRun mov al,es:[si] ; ES:prevSeg, SI:picAddr cmp al,[si] ; DS:picSeg, SI:picAddr jne continueRun endWhileInRun: jmp short gapStarted ; SI points to first byte of gap. ; BX:gapSize = 0. done: mov ax,di ; RETURN deltaBytes = DI:deltaAddr. fail: pop ds ; Restore DS:DGROUP. pop es pop di pop si pop bp ret ENDPROC _MakeDeltaSequence ENDIF ; simple dump format. ENDPS anima end