;; ; @(#) arcode32.asm - Demo arithmetic byte 32-bit coder/decoder. ; (c) 1998 Ivan Maidanski http://ivmai.chat.ru ; Freeware program source. All rights reserved. ;; ; Language: ASM80386 (for IBM PC/AT, MS-DOS v3.0+, COM file) ; Tested with: Turbo Assembler v3.1, Turbo Link v5.1 ; Last modified: 1998-08-18 13:45:00 GMT+04:00 ;; NAME ArithByteCoder32 ; tasm /m3 arcode32.asm ; tlink /t arcode32.obj .MODEL SMALL .CODE .386 Null LABEL NEAR ; Program constants (may be changed) FileBufSize= 1000h ; file i/o buffer size MaxFreq= 01FFh ; maximum frequence table value (1h..0FFFFh) SymScale= 5h ; frequence model symbol update scale (0..MaxFreq/2) ; PSP segment ORG 80h PSP@CmdLine DB 80h DUP(?) ; program command line ; Program start point ORG 100h Main PROC NEAR ; Comment: - ; Expects: ds=es=cs ; Returns: - (not applicable) ; Uses: - (all) .8086 cmp sp,OFFSET StackTop ; check available memory (COM file) jae Main@Check Main@Abort: xor ax,ax ; abort program push es push ax retf Main@Check: push sp ; check for CPU286 or higher pop ax cmp ax,sp jne Main@Abort .286 pushf ; check for CPU386 or higher pop ax push ax or ah,70h push ax popf pushf pop ax popf test ah,70h jz Main@Abort .386 mov ax,3000h ; check for MS-DOS v3.0 or higher int 21h cmp al,3h jb Main@Abort mov sp,OFFSET StackTop ; init stack mov ah,4Ah ; shrink memory block mov bx,(ProgramLength+0Fh)/10h ; size of program in paragraphs int 21h call AdjustCMDLine mov si,OFFSET PSP@CmdLine ; check command (E or D) call GetCMDParam jc Main@PrintName cmp byte ptr ds:[si],'E' je Main@GetSrc cmp byte ptr ds:[si],'e' je Main@GetSrc cmp byte ptr ds:[si],'D' je Main@GetSrc cmp byte ptr ds:[si],'d' je Main@GetSrc Main@PrintName: mov ah,9h ; print info message mov dx,OFFSET Main@MsgName int 21h mov ax,4C01h int 21h Main@GetSrc: call GetCMDParam jc Main@BadSrc mov ax,3D00h ; open source file for read mov dx,si int 21h jnc Main@GetDest Main@BadSrc: mov ah,9h ; print error message mov dx,OFFSET Main@MsgSrcErr int 21h mov ax,4C02h int 21h Main@GetDest: mov bx,ax call GetCMDParam jc Main@BadDest mov ah,3Ch ; create destination file for read/write mov cx,20h mov dx,si int 21h jnc Main@Process Main@BadDest: mov ah,3Eh ; close source file int 21h mov ah,9h ; print error message mov dx,OFFSET Main@MsgDestErr int 21h mov ax,4C03h int 21h Main@Process: mov si,OFFSET Main@DestFBuf call PrepareBufFile mov di,si mov ax,bx mov si,OFFSET Main@SrcFBuf call PrepareBufFile push si mov si,OFFSET PSP@CmdLine ; get command (E or D) call GetCMDParam mov al,byte ptr ds:[si] pop si cmp al,'D' je Main@Decompress cmp al,'d' je Main@Decompress call PackFile jmp Main@Close Main@Decompress: call UnpackFile Main@Close: xchg si,di ; close destination file call FlushBufFile call CloseBufFile mov si,di ; close source file call CloseBufFile mov ax,4C00h int 21h Main ENDP AdjustCMDLine PROC NEAR ; Comment: replace spaces with zeros in the CMD line ; Expects: ds - PSP segment ; Returns: - ; Uses: cx,si mov si,OFFSET PSP@CmdLine mov cl,byte ptr ds:[si] mov ch,0 jcxz AdjustCMDLine@Ret AdjustCMDLine@Loop: inc si cmp byte ptr ds:[si],' ' ja AdjustCMDLine@Next mov byte ptr ds:[si],0 AdjustCMDLine@Next: loop AdjustCMDLine@Loop mov byte ptr ds:[si+1h],0 AdjustCMDLine@Ret: ret AdjustCMDLine ENDP GetCMDParam PROC NEAR ; Comment: get next parameter in the CMD line ; Expects: ds - PSP segment, si - previous parameter offset ; Returns: ds:[si] - next parameter, CF=1 - error ; Uses: cx,si push si mov si,OFFSET PSP@CmdLine mov cl,byte ptr ds:[si] mov ch,0 add cx,si pop si sub cx,si jbe GetCMDParam@Err cmp si,OFFSET PSP@CmdLine je GetCMDParam@Next GetCMDParam@Skip: cmp byte ptr ds:[si],' ' jbe GetCMDParam@Next inc si loop GetCMDParam@Skip GetCMDParam@Next: inc si cmp byte ptr ds:[si],' ' ja GetCMDParam@Ret loop GetCMDParam@Next GetCMDParam@Err: stc GetCMDParam@Ret: ret GetCMDParam ENDP PackFile PROC NEAR ; Comment: pack file ; Expects: ds:[si] - source file buffer, ds:[di] - destination file buffer ; Returns: - ; Uses: eax,ebx,ecx,edx,edi_hw,ebp call GetLenBufFile mov cx,ax xchg si,di mov al,dh call WriteBufFile mov al,dl call WriteBufFile mov al,ch call WriteBufFile mov al,cl call WriteBufFile xchg si,di push si mov cx,0FFh mov si,OFFSET SymFreqTbl call FreqModelStart32 pop si xor ebx,ebx or ecx,0FFFFFFFFh mov al,0 PackFile@Loop: push ax call ReadBufFile jc PackFile@Finish push si push di mov ah,0 mov si,OFFSET SymFreqTbl call FreqModelGetCum32 call ArithEncode32 mov di,OFFSET SymFreqTbl call FreqModelUpdate32 pop di pop si pop ax PackFile@Out: push di call ArithOutput32 pop di jnc PackFile@Loop xchg si,di call WriteBufFile xchg si,di mov al,0 jmp PackFile@Out PackFile@Finish: pop ax or ebp,0FFFFFFFFh push si mov si,di PackFile@Tail: call ArithOutput32 jnc PackFile@Flush call WriteBufFile mov al,0 jmp PackFile@Tail PackFile@Flush: mov di,si pop si call ArithFlushFinish32 jnc PackFile@Ret xchg si,di call WriteBufFile xchg si,di PackFile@Ret: ret PackFile ENDP UnpackFile PROC NEAR ; Comment: unpack file ; Expects: ds:[si] - source file buffer, ds:[di] - destination file buffer ; Returns: - ; Uses: eax,ebx,ecx,edx,edi_hw,ebp call ReadBufFile jc UnpackFile@Ret mov ch,al call ReadBufFile jc UnpackFile@Ret mov cl,al shl ecx,10h call ReadBufFile jc UnpackFile@Ret mov ch,al call ReadBufFile jc UnpackFile@Ret mov cl,al jecxz UnpackFile@Ret call ReadBufFile mov dh,al call ReadBufFile mov dl,al shl edx,10h call ReadBufFile mov dh,al call ReadBufFile mov dl,al call ReadBufFile mov ah,al push ecx push si mov cx,0FFh mov si,OFFSET SymFreqTbl call FreqModelStart32 pop si xor ebx,ebx or ecx,0FFFFFFFFh mov al,0 UnpackFile@Loop: push ax push edx call ArithDecode32 push si push di mov si,OFFSET SymFreqTbl call FreqModelIndex32 call ArithEncode32 mov di,OFFSET SymFreqTbl call FreqModelUpdate32 pop si pop di pop edx call WriteBufFile xchg si,di pop ax push bp mov bp,sp dec dword ptr ss:[bp+2h] pop bp jz UnpackFile@Restore UnpackFile@Advance: push di call ArithOutput32 pop di jnc UnpackFile@Loop call ReadBufFile mov ah,al mov al,0 jmp UnpackFile@Advance UnpackFile@Restore: pop ecx UnpackFile@Ret: ret UnpackFile ENDP FreqModelStart32 PROC NEAR ; Comment: initiate frequence model sym/freq table ; Expects: ds:[si] - sym/freq model table, cx - max symbol value ; Returns: ds:[si] - initiated sym/freq model table, ; ebp - (initiated cumulative freq sum)-1 ; Uses: ebp xor ebp,ebp mov bp,cx inc bp shl bp,2h add si,bp mov bp,cx inc bp FreqModelStart32@Loop: sub si,4h dec bp mov dword ptr ds:[si],ebp jnz FreqModelStart32@Loop mov bp,cx ret FreqModelStart32 ENDP ArithDecode32 PROC NEAR ; Comment: approx decode value using arithmetic low/high values ; Expects: edx - input value, ebp - (cumulative freq sum)-1h, ; ebx - arith low value, ecx - arith high value ; Returns: edx - approx cumulative value ; Uses: eax,edx cmp edx,ecx ; max input value ? mov eax,ebp jae ArithDecode32@Ret dec ebx sub edx,ebx inc eax ; max cumulative sum value ? jz ArithDecode32@Max mul edx ArithDecode32@Max: sub eax,1h sbb edx,0 sub ecx,ebx ; max arithmetic range ? xchg eax,edx jz ArithDecode32@Zero xchg eax,edx div ecx ArithDecode32@Zero: add ecx,ebx inc ebx ArithDecode32@Ret: xchg eax,edx ret ArithDecode32 ENDP FreqModelIndex32 PROC NEAR ; Comment: calculate exact cumulative lower/upper values and symbol index ; Expects: edx - approx cumulative value, ds:[si] - sym/freq model table ; Returns: edi - cumulative lower value, edx - cumulative upper value, ; ds:si - pointer to the decoded sym/freq values ; Uses: edx,si,edi sub si,4h mov edi,edx FreqModelIndex32@Loop: add si,4h stc sbb di,word ptr ds:[si+2h] jnb FreqModelIndex32@Loop sub edi,10000h jnb FreqModelIndex32@Loop sbb edx,edi mov edi,edx sub di,word ptr ds:[si+2h] jnb FreqModelIndex32@Ret sub edi,10000h FreqModelIndex32@Ret: ret FreqModelIndex32 ENDP FreqModelGetCum32 PROC NEAR ; Comment: calculate cumulative lower/upper values and encoded symbol index ; Expects: ax - encoded symbol, ds:[si] - sym/freq model table ; Returns: edi - cumulative lower value, edx - cumulative upper value, ; ds:si - pointer to the encoded sym/freq values ; Uses: edx,si,edi xor edx,edx xor edi,edi FreqModelGetCum32@Loop: cmp ax,word ptr ds:[si] mov dx,word ptr ds:[si+2h] je FreqModelGetCum32@Ret stc adc edi,edx add si,4h jmp FreqModelGetCum32@Loop FreqModelGetCum32@Ret: add edx,edi ret FreqModelGetCum32 ENDP ArithEncode32 PROC NEAR ; Comment: encode arithmetic low/high values ; Expects: ebx - arith low value, ecx - arith high value, ; edi - cumulative lower value, edx - cumulative upper value, ; ebp - (cumulative freq sum)-1h ; Returns: ebx - new arith low value, ecx - new arith high value ; Uses: eax,ebx,ecx,edx sub ecx,ebx ; get arith values range cmp edx,ebp ; do not update high value ? mov eax,ecx jae ArithEncode32@Skip inc edx inc eax ; max range ? jz ArithEncode32@Zero mul edx ArithEncode32@Zero: inc ebp ; max cumulative sum ? xchg eax,edx jz ArithEncode32@MaxHigh xchg eax,edx div ebp ArithEncode32@MaxHigh: dec eax ; update arith high value dec ebp xchg eax,ecx ArithEncode32@Skip: add ecx,ebx cmp edi,ebp ; do not update low value ? ja ArithEncode32@Ret mul edi add eax,edi adc edx,0 inc ebp ; max cumulative sum ? xchg eax,edx jz ArithEncode32@MaxLow xchg eax,edx div ebp ArithEncode32@MaxLow: dec ebp ; update arith low value add ebx,eax ArithEncode32@Ret: ret ArithEncode32 ENDP FreqModelUpdate32 PROC NEAR ; Comment: update frequence model table ; Expects: ds:[di] - sym/freq model table, ebp - (cumulative freq sum)-1h, ; ds:si - pointer to the encoded/decoded sym/freq values ; Returns: ds:[di] - updated sym/freq model table, ; ax - encoded/decoded symbol, ebp - (new cumulative freq sum)-1h, ; ds:si - pointer to the encoded/decoded sym/freq values ; Uses: eax,edx,ebp,si cmp ebp,NOT SymScale ; cumulative sum overflow ? ja FreqModelUpdate32@Halven cmp word ptr ds:[si+2h],MaxFreq-SymScale ; symbol frequence overflow ? jbe FreqModelUpdate32@Search FreqModelUpdate32@Halven: push di ; halven frequence table xor eax,eax inc di xor edx,edx inc di xchg edx,ebp FreqModelUpdate32@Loop: mov ax,word ptr ds:[di] shr word ptr ds:[di],1h add di,4h ; update old cumulative sum value stc sbb edx,eax ; all symbols proceeded ? jb FreqModelUpdate32@Last shr ax,1h ; update new cumulative sum value stc adc ebp,eax jmp FreqModelUpdate32@Loop FreqModelUpdate32@Last: pop di shr ax,1h add ebp,eax FreqModelUpdate32@Search: mov eax,dword ptr ds:[si] ; re-order frequence table add ebp,SymScale rol eax,10h add ax,SymScale FreqModelUpdate32@Prev: cmp si,di ; table beginning reached ? je FreqModelUpdate32@Ret cmp ax,word ptr ds:[si-2h] ; appropriate index found ? jbe FreqModelUpdate32@Ret mov edx,dword ptr ds:[si-4h] mov dword ptr ds:[si],edx sub si,4h jmp FreqModelUpdate32@Prev FreqModelUpdate32@Ret: ror eax,10h ; update re-ordered frequence table mov dword ptr ds:[si],eax ret FreqModelUpdate32 ENDP ArithOutput32 PROC NEAR ; Comment: output arith encoded value ; Expects: ebx - arith low value, ecx - arith high value, ; al - output byte value, ebp - (new cumulative freq sum)-1h, ; ah - input byte value, edx - input code value ; Returns: ebx - new arith low value, ecx - new arith high value, ; al - new output byte value, CF=1 - unfinished output (byte full), ; ah - advanced input byte value, edx - advanced input code value ; Uses: ax,ebx,ecx,edx,edi xor ecx,ebx ; get code difference sub al,1h ; output byte empty ? adc al,1h ArithOutput32@Check: shl ecx,1h ; nothing (more) to output ? jc ArithOutput32@Nothing inc ecx ArithOutput32@Out: shl ah,1h ; advance input code rcl edx,1h shl ebx,1h ; output one bit rcl al,1h jnc ArithOutput32@Check xor ecx,ebx ; restore values if not finished stc ArithOutput32@Ret: ret ArithOutput32@Nothing: rcr ecx,1h ; calculate new arith values xor ecx,ebx mov edi,ecx ; check for arith overflow ? sub edi,ebx cmp edi,ebp jae ArithOutput32@Ret or edi,0FFFFFFFFh ; count tail bit length not ecx ArithOutput32@TailCnt: shr edi,1h shl ebx,1h ; tail end reached ? jns ArithOutput32@FormTail shl ecx,1h ; tail end not reached ? js ArithOutput32@TailCnt mov ebx,80000000h ; prepare for tail outputting mov ecx,edi jmp ArithOutput32@Out ArithOutput32@FormTail: shl ecx,1h or ecx,ebx ; short tail possible ? mov ebx,edi mov ecx,edi jz ArithOutput32@Short not ebx ; prepare for tail outputting shr ebx,1h jmp ArithOutput32@Out ArithOutput32@Short: xor ebx,7FFFFFFFh ; prepare for tail outputting jmp ArithOutput32@Check ArithOutput32 ENDP ArithFlushFinish32 PROC NEAR ; Comment: prepare for flushing output remaining code ; Expects: al - output byte value, ah - input byte value, ; edx - input code value ; Returns: al - new output byte value, CF=0 - nothing to prepare & output, ; ah - advanced input byte value, edx - advanced input code value ; Uses: ax,edx cmp al,2h cmc jnc ArithFlushFinish32@Ret ArithFlushFinish32@Loop: shl ah,1h rcl edx,1h shl al,1h jnc ArithFlushFinish32@Loop ArithFlushFinish32@Ret: ret ArithFlushFinish32 ENDP PrepareBufFile PROC NEAR ; Comment: prepare buffered file input/output ; Expects: ds:[si] - file buffer, ax - file handler ; Returns: - ; Uses: - mov word ptr ds:[si],ax mov word ptr ds:[si+2h],0 ; current file position mov word ptr ds:[si+4h],0 ; end position ret PrepareBufFile ENDP GetLenBufFile PROC NEAR ; Comment: get buffered file length ; Expects: ds:[si] - file buffer ; Returns: dx:ax - file length, CF - error flag ; Uses: ax,dx push bx push cx push si push di mov ax,4201h ; get current file pos mov bx,word ptr ds:[si] xor cx,cx xor dx,dx int 21h jc GetLenBufFile@Ret mov si,ax mov di,dx mov ax,4202h ; get file length xor dx,dx int 21h mov cx,di mov di,dx mov dx,si mov si,ax pushf mov ax,4200h ; restore file pos int 21h popf mov dx,di mov ax,si GetLenBufFile@Ret: pop di pop si pop cx pop bx ret GetLenBufFile ENDP ReadBufFile PROC NEAR ; Comment: read byte from buffered file ; Expects: ds:[si] - file buffer ; Returns: al - value, CF - error or EOF flag ; Uses: ax push bx mov bx,word ptr ds:[si+2h] ; check for empty buffer ? cmp bx,word ptr ds:[si+4h] jb ReadBufFile@Get test bx,bx jz ReadBufFile@Read cmp bx,FileBufSize ; check for EOF ? jb ReadBufFile@Ret mov word ptr ds:[si+2h],0 mov word ptr ds:[si+4h],0 ReadBufFile@Read: mov ah,3Fh ; read from file to buffer push cx push dx mov bx,word ptr ds:[si] mov cx,FileBufSize mov dx,si add dx,6h int 21h pop dx pop cx jc ReadBufFile@Ret mov word ptr ds:[si+4h],ax test ax,ax ; check for EOF ? stc jz ReadBufFile@Ret xor bx,bx ReadBufFile@Get: mov al,byte ptr ds:[bx+si+6h] ; get one byte inc word ptr ds:[si+2h] clc ReadBufFile@Ret: pop bx ret ReadBufFile ENDP WriteBufFile PROC NEAR ; Comment: write byte to buffered file ; Expects: ds:[si] - file buffer, al - value ; Returns: CF - error flag ; Uses: ax push bx mov bx,word ptr ds:[si+2h] mov byte ptr ds:[bx+si+6h],al ; put one byte inc bx mov word ptr ds:[si+2h],bx inc word ptr ds:[si+4h] cmp bx,FileBufSize ; buffer not full ? cmc jnc WriteBufFile@Ret mov word ptr ds:[si+2h],0 mov word ptr ds:[si+4h],0 mov ah,40h ; write to file from buffer push cx push dx mov cx,bx mov dx,si mov bx,word ptr ds:[si] add dx,6h int 21h pop dx pop cx jc WriteBufFile@Ret WriteBufFile@Ret: pop bx ret WriteBufFile ENDP FlushBufFile PROC NEAR ; Comment: flush buffered file ; Expects: ds:[si] - file buffer ; Returns: CF - error flag ; Uses: ax push bx mov ah,40h ; write to file from buffer push cx push dx xor cx,cx xchg cx,word ptr ds:[si+2h] jcxz FlushBufFile@Skip ; nothing to write ? mov word ptr ds:[si+4h],0 mov dx,si mov bx,word ptr ds:[si] add dx,6h int 21h FlushBufFile@Skip: pop dx pop cx pop bx ret FlushBufFile ENDP CloseBufFile PROC NEAR ; Comment: close buffered file ; Expects: ds:[si] - file buffer ; Returns: CF - error flag ; Uses: ax push bx mov ah,3Eh ; close file mov bx,word ptr ds:[si] int 21h pop bx ret CloseBufFile ENDP ; Program messages Main@MsgName DB 'Arithmetic byte 32-bit compression routine',0Dh,0Ah DB 'Usage parameters: E|D ' DB 0Dh,0Ah,'$' Main@MsgSrcErr DB 'Error: cannot open source file',0Dh,0Ah,'$' Main@MsgDestErr DB 'Error: cannot open destination file',0Dh,0Ah,'$' ; Compression working area EVEN SymFreqTbl DD 100h DUP(?) ; symbol/frequence table(s) ; Program file i/o buffers Main@SrcFBuf DB 6h+FileBufSize DUP(?) Main@DestFBuf DB 6h+FileBufSize DUP(?) ; Program stack EVEN DW 100h DUP(?) StackTop LABEL WORD ; End of program ProgramLength= $-Null END Main