;; ; @(#) arcode16.asm - Demo arithmetic byte 16-bit coder/decoder. ; (c) 1998 Ivan Maidanski http://ivmai.chat.ru ; Freeware program source. All rights reserved. ;; ; Language: ASM8086 (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:25:00 GMT+04:00 ;; NAME ArithByteCoder16 ; tasm arcode16.asm ; tlink /t arcode16.obj .MODEL SMALL .CODE .8086 Null LABEL NEAR ; Program constants (may be changed) FileBufSize= 400h ; file i/o buffer size MaxFreq= 0FFh ; maximum frequence table value (1h..0FFh) SymScale= 4h ; frequence model symbol update scale (0..MaxFreq/2h) ; PSP segment ORG 80h PSP@CmdLine DB 80h DUP(?) ; program command line ; Program start point ORG 100h Start: cmp sp,OFFSET StackTop ; check available memory jb Start@Err mov ax,3000h ; check for MS-DOS v3.0 int 21h cmp al,3h jae Main Start@Err: xor ax,ax ; abort program push es push ax retf Main PROC NEAR ; Comment: - ; Expects: ds=es=cs ; Returns: - (not applicable) ; Uses: - (all) mov sp,OFFSET StackTop 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 short 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: ax,bx,cx,dx,bp 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 call FreqModelStart16 xor bx,bx or cx,0FFFFh mov al,0 PackFile@Loop: push ax call ReadBufFile jc PackFile@Finish push si push di call PackByte16 call PackUpdate16 pop di pop si pop ax PackFile@Out: push di call ArithOutput16 pop di jnc PackFile@Loop xchg si,di call WriteBufFile xchg si,di mov al,0 jmp short PackFile@Out PackFile@Finish: pop ax or bp,0FFFFh push si mov si,di PackFile@Tail: call ArithOutput16 jnc PackFile@Flush call WriteBufFile mov al,0 jmp short PackFile@Tail PackFile@Flush: mov di,si pop si call ArithFlushFinish16 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: ax,bx,cx,dx,bp call ReadBufFile jc UnpackFile@Ret mov ch,al call ReadBufFile jc UnpackFile@Ret mov cl,al call ReadBufFile jc UnpackFile@Ret mov bh,al call ReadBufFile jc UnpackFile@Ret mov bl,al sub bx,1h sbb cx,0 jb UnpackFile@Ret call ReadBufFile mov dh,al call ReadBufFile mov dl,al call ReadBufFile mov ah,al push cx push bx call FreqModelStart16 xor bx,bx or cx,0FFFFh mov al,0 UnpackFile@Loop: push ax push dx push si push di call UnpackByte16 call PackUpdate16 pop si pop di pop dx mov al,ah call WriteBufFile xchg si,di pop ax push bp mov bp,sp sub word ptr ss:[bp+2h],1h sbb word ptr ss:[bp+4h],0 pop bp jb UnpackFile@Restore UnpackFile@Advance: push di call ArithOutput16 pop di jnc UnpackFile@Loop call ReadBufFile mov ah,al mov al,0 jmp short UnpackFile@Advance UnpackFile@Restore: pop bx pop cx UnpackFile@Ret: ret UnpackFile ENDP FreqModelStart16 PROC NEAR ; Comment: initiate frequence model sym/freq tables ; Expects: ds:[FreqSymTable] - freq/sym model table ; Returns: ds:[FreqSymTable] - freq/sym model table, ; bp - (cumulative freq sum)-1 ; Uses: bx,cx,bp mov bx,OFFSET FreqSymTable xor cx,cx FreqModelStart16@Loop: mov word ptr ds:[bx],cx inc bx inc bx inc ch jnz FreqModelStart16@Loop mov bp,0FFh ret FreqModelStart16 ENDP PackByte16 PROC NEAR ; Comment: byte arithmetic encoding ; Expects: al - byte value, ds:[FreqSymTable] - freq model table ; Returns: di - cumulative lower value, dx - cumulative upper value, ; ds:[si] - freq/sym pair of the encoded value ; Uses: dx,si,di mov si,OFFSET FreqSymTable xor di,di ; search byte value in the freq/sym table PackByte16@Loop: mov dx,word ptr ds:[si] cmp al,dh mov dh,0 je PackByte16@Ret inc si stc adc di,dx inc si jmp short PackByte16@Loop PackByte16@Ret: add dx,di ret PackByte16 ENDP UnpackByte16 PROC NEAR ; Comment: byte arithmetic decoding ; Expects: dx - input value, bp - (cumulative freq sum)-1h, ; bx - arith low value, cx - arith high value, ; ds:[FreqSymTable] - freq model table ; Returns: di - cumulative lower value, dx - cumulative upper value, ; ds:[si] - freq/sym pair of the encoded value ; Uses: ax,dx,si,di cmp dx,cx ; max input value ? mov ax,bp jae UnpackByte16@Max sub dx,bx ; calculate approx cumulative lower value mov di,dx inc dx mul dx add ax,di adc dx,0 mov di,cx sub di,bx inc di ; max range value ? jz UnpackByte16@Search div di UnpackByte16@Max: xchg ax,dx UnpackByte16@Search: mov si,OFFSET FreqSymTable-2h ; search encoded freq/sym pair mov di,dx mov ah,0 UnpackByte16@Loop: inc si inc si mov al,byte ptr ds:[si] ; get current cumulative value stc sbb di,ax jnb UnpackByte16@Loop sbb dx,di ; cumulative values correction mov di,dx sub di,ax ret UnpackByte16 ENDP PackUpdate16 PROC NEAR ; Comment: update arithmetic coder values ; Expects: bx - arith low value, cx - arith high value, ; di - cumulative lower value, dx - cumulative upper value, ; bp - (cumulative freq sum)-1h, ds:[FreqSymTable] - freq model table, ; ds:[si] - freq/sym pair of the encoded value ; Returns: bx - new arith low value, cx - new arith high value, ; bp - (new cumulative freq sum)-1h, ds:[FreqSymTable] - new freq table, ; ah - encoded value, ds:[si] - freq/sym pair of the encoded value ; Uses: ax,bx,cx,dx,si,di,bp sub cx,bx ; get arith values range cmp dx,bp ; do not update high value ? mov ax,cx jae PackUpdate16@Low inc dx inc ax ; max range ? jz PackUpdate16@Zero mul dx PackUpdate16@Zero: inc bp ; max cumulative sum ? xchg ax,dx jz PackUpdate16@MaxHigh xchg ax,dx div bp PackUpdate16@MaxHigh: dec ax ; update arith high value dec bp xchg ax,cx PackUpdate16@Low: add cx,bx cmp di,bp ; do not update low value ? ja PackUpdate16@Check mul di add ax,di adc dx,0 inc bp ; max cumulative sum ? xchg ax,dx jz PackUpdate16@MaxLow xchg ax,dx div bp PackUpdate16@MaxLow: dec bp ; update arith low value add bx,ax PackUpdate16@Check: cmp bp,NOT SymScale ; cumulative sum overflow ? ja PackUpdate16@Halven cmp byte ptr ds:[si],MaxFreq-SymScale ; no frequence overflow ? jbe PackUpdate16@Search PackUpdate16@Halven: xor ax,ax ; halven frequence table mov di,OFFSET FreqSymTable mov dh,0 xchg ax,bp PackUpdate16@Loop: mov dl,byte ptr ds:[di] shr byte ptr ds:[di],1h stc ; adjust old cumulative sum value sbb ax,dx ; frequence table proceeded ? jb PackUpdate16@Last inc di shr dl,1h ; adjust new cumulative sum value inc di stc adc bp,dx jmp short PackUpdate16@Loop PackUpdate16@Last: shr dl,1h add bp,dx PackUpdate16@Search: mov ax,word ptr ds:[si] ; update cumulative sum value add bp,SymScale add al,SymScale ; search index for the new frequence value PackUpdate16@Before: cmp si,OFFSET FreqSymTable ; table beginning reached ? je PackUpdate16@Found mov dx,word ptr ds:[si-2h] cmp dl,al ; appropriate index found ? jae PackUpdate16@Found mov word ptr ds:[si],dx dec si dec si jmp short PackUpdate16@Before PackUpdate16@Found: mov word ptr ds:[si],ax ; update re-ordered frequence table ret PackUpdate16 ENDP ArithOutput16 PROC NEAR ; Comment: output arith encoded value ; Expects: bx - arith low value, cx - arith high value, ; al - output byte value, bp - (new cumulative freq sum)-1h, ; ah - input byte value, dx - input code value ; Returns: bx - new arith low value, cx - new arith high value, ; al - new output byte value, CF=1 - unfinished output (byte full), ; ah - advanced input byte value, dx - advanced input code value ; Uses: ax,bx,cx,dx,di xor cx,bx ; get code difference sub al,1h ; output byte empty ? adc al,1h ArithOutput16@Check: shl cx,1h ; nothing (more) to output ? jc ArithOutput16@Nothing inc cx ArithOutput16@Out: shl ah,1h ; advance input code rcl dx,1h shl bx,1h ; output one bit rcl al,1h jnc ArithOutput16@Check xor cx,bx ; restore values if not finished stc ArithOutput16@Ret: ret ArithOutput16@Nothing: rcr cx,1h ; calculate new arith values xor cx,bx mov di,cx ; check for arith overflow ? sub di,bx cmp di,bp jae ArithOutput16@Ret or di,0FFFFh ; count tail bit length not cx ArithOutput16@TailCnt: shr di,1h shl bx,1h ; tail end reached ? jns ArithOutput16@FormTail shl cx,1h ; tail end not reached ? js ArithOutput16@TailCnt mov bx,8000h ; prepare for tail outputting mov cx,di jmp short ArithOutput16@Out ArithOutput16@FormTail: shl cx,1h or cx,bx ; short tail possible ? mov bx,di mov cx,di jz ArithOutput16@Short not bx ; prepare for tail outputting shr bx,1h jmp short ArithOutput16@Out ArithOutput16@Short: xor bx,7FFFh ; prepare for tail outputting jmp short ArithOutput16@Check ArithOutput16 ENDP ArithFlushFinish16 PROC NEAR ; Comment: prepare for flushing output remaining code ; Expects: al - output byte value, ; ah - input byte value, dx - input code value ; Returns: al - new output byte value, CF=0 - nothing to prepare & output, ; ah - advanced input byte value, dx - advanced input code value ; Uses: ax,dx cmp al,2h cmc jnc ArithFlushFinish16@Ret ArithFlushFinish16@Loop: shl ah,1h rcl dx,1h shl al,1h jnc ArithFlushFinish16@Loop ArithFlushFinish16@Ret: ret ArithFlushFinish16 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 16-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 FreqSymTable DW 100h DUP(?) ; 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 Start