Last active
August 29, 2015 14:01
-
-
Save P4/744718323c080cb7df9f to your computer and use it in GitHub Desktop.
Implementacja Run-Length-Encoding (RLE)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Pawel Maniecki | |
; Asemblery, IET AGH | |
.186 ; pusha, popa | |
BUFSIZE = 4000h ; rozmiar bufora: 4*16*256 = 16*1024 = 16 KiB | |
_Dane segment | |
; Argumenty linii komend | |
argc db 0 ; ilosc argumentow ( dlugosc args ) | |
args dw 10h dup(?) ; offsety na poczatki argumentow w argv | |
argv db 80h dup('$') ; wartosci argumentow rozdz $ | |
; Plik wejsciowy: | |
iname db 40h dup(0) ; sciezka, zakonczona zerem | |
ifile dw ? ; Uchwyt do pliku | |
isize dw 0 ; aktualny rozmiar bufora | |
ipos dw 0 ; aktualna pozycja w buforze | |
ibuffer db BUFSIZE dup(?) ; bufor | |
ifinal db 0 ; czy bufor zawiera finalny fragment pliku? | |
; Plik wyjsciowy | |
oname db 40h dup(0) ; sciezka, zakonczona zerem | |
ofile dw ? ; Uchwyt do pliku | |
osize dw 0 ; aktualny rozmiar bufora | |
obuffer db BUFSIZE dup(?) ; bufor | |
; ** Komunikaty bledow ** | |
ArgumentErrMsg db "Bledne argumenty", 13, 10, "Uzycie: rle [-d] PLIK_WEJSCIOWY PLIK_WYJSCIOWY",13,10,'$' | |
InputFileErrMsg db "Wystapil blad przy otwieraniu pliku wejsciowego",13,10,'$' | |
OutputFileErrMsg db "Wystapil blad przy tworzeniu lub otwieraniu pliku wyjsciowego",13,10,'$' | |
ReadErrMsg db "W trakcie odczytu pliku wystapil blad.",13,10,'$' | |
WriteErrMsg db "Nie udalo sie zapisac danych do pliku.",13,10,'$' | |
CloseErrMsg db "Przy zamykaniu pliku wystapil blad",13,10,'$' | |
DecodeErrMsg db "Blad przy dekompresji pliku, wyjsciowy plik moze zawierac bledy.",13,10,'$' | |
_Dane ends | |
_Kod segment | |
start: | |
; 0) Inicjowanie stosu | |
mov ax, seg wstosu | |
mov ss, ax | |
mov sp, offset wstosu | |
; 1) Wczytanie argumentow | |
; DS na segment kodu zaw PSP | |
call ParseArgs ; wypelnia (argc,args,argv) argumentami z DS:[80h] | |
; 2) Weryfikacja argumentow | |
; Od teraz DS i ES na segment danych | |
mov ax, seg argv | |
mov ds, ax | |
mov es, ax | |
; 2.1) Ilosc argumentow: 2 lub 3? | |
call GetArgNum ; ax <- ilosc argumentow | |
cmp ax, 3 ; argumenty: -d IN OUT | |
je validArgs | |
cmp ax, 2 ; tylko IN OUT | |
jne ArgumentError ; inna ilosc niedozwolona | |
; 2.2) Sprawdzenie flagi | |
validArgs: | |
mov dl, 0 ; Rozpocznij przetwarzanie od 0. argumentu | |
sub ax,2 ; Czy jest opcja -d? 2->0, 3->1 | |
push ax ; odloz obecnosc flagi (push nie zmienia flag) | |
jz noFlag ; wynik sub byl zerem, to brak flagi | |
; sprawdz czy podana opcja jest rowna '-d' | |
call GetArgLen ; AX <- dlugosc argumentu | |
cmp ax, 2 ; dlugosc rowna 2? | |
jne ArgumentError ; nie, na pewno rozne od '-d' | |
call GetArgPos ; AX <- pozycja argumentu | |
mov bx, ax | |
cmp byte ptr ds:[bx], '-' | |
jne ArgumentError | |
cmp byte ptr ds:[bx+1], 'd' | |
jne ArgumentError | |
; opcja ma wartosc '-d' | |
inc dl ; kolejny argument | |
; 2.3) Kopiowanie dwoch argumentow do zmiennych w pamieci | |
noFlag: | |
; Kopiuj PLIK_WEJSCIOWY do iname | |
mov di, offset iname | |
call copyfilename | |
inc dl | |
; Kopiuj PLIK_WYJSCIOWY do oname | |
mov di, offset oname | |
call copyfilename | |
; 3) Otwarcie plikow | |
call OpenFiles ; Otworz pliki w odpow trybach | |
; lub wyjdz z programu z komunikatem o bledzie | |
; 3.1) Wybor trybu pracy | |
pop ax ; ax <- tryb_pracy (0 lub 1) | |
or ax,ax | |
jnz decompression ; 1 oznacza dekompresje | |
; 4) Praca: kompresja lub dekompresja | |
compression: | |
call Encode | |
jmp exit | |
decompression: | |
call Decode | |
;jmp exit | |
; 5) Wyjscie z programu | |
exit: | |
call Flush ; reszta buforu wyjscia do pliku | |
call CloseFiles ; zamykamy pliki | |
mov ax, 4C00h ; exit(0) | |
int 21h | |
; ** OBSLUGA BLEDOW ** | |
; Po komunikacie o bledzie natychmiast wychodzimy z programu, | |
; na stosie mogly zostac rejestry odlozone przez procedury | |
ArgumentError: | |
mov dx, offset ArgumentErrMsg | |
mov al, 0FFh ; exit(-1) | |
jmp errorExit | |
errorExit: | |
mov ah, 4Ch ; exit( AL ) - zachowujemy kod bledu w AL | |
push ax | |
; odkomentowac jesli zmieniamy segment | |
;mov ax, seg ArgumentErrMsg ; segment wspolny dla wszystkich bledow | |
;mov ds, ax ; ustaw segment ds na bledy | |
mov ah, 9 ; wypisanie bledu na ekran | |
int 21h | |
pop ax ; pobranie kodu przerwania+bledu ze stosu | |
int 21h | |
; ** Procedury ** | |
;Encode | |
; spakuj bajty z wejscia przy pomocy kompresjii RLE | |
; - znaki wystepujace wiecej niz 3 razy zamieniane sa | |
; na trojke 0IZ: \x00 <ilosc> <znak> | |
; - pojedynczy znak \x00 jest podwajany, aby odroznic go od poczatku trojki 0IZ | |
; - jakiekolwiek powtorzenia dla 0 oplaca sie zmienic na trojke 0I0 | |
Encode proc | |
push cx | |
push dx | |
call GetChar ; AL <- znak wejscia, AH niszczony | |
jc return ; nic nie kompresujemy, plik pusty | |
xor dx,dx ; dx = 0, przyda sie | |
mov dh,al ; $PREV: znak porownywany z kolejnym | |
mov cx, 1 ; $COUNT: conajmniej pojedyncze wystapienie znaku | |
encodeLoop: ; koduj kolejne porcje danych | |
call GetChar ; AL <- kolejny znak ($CURR), AH niszczony | |
jc loopEnded ; koniec pliku, wypisz $PREV znak. | |
cmp dh,al ; | |
jne nextChar ; $CURR != $PREV | |
cmp cx,255 ; || | |
je nextChar ; $COUNT == 255 | |
;else | |
inc cx ; $COUNT++ | |
jmp encodeLoop | |
nextChar: | |
xchg dh,al ; stary $PREV do wypisania, $PREV = $CURR | |
call emitChar ; wypisz $PREV (AL) $COUNT (CX) razy | |
mov cx,1 ; resetuj $COUNT dla nowego $PREV | |
jmp encodeLoop | |
loopEnded: | |
xchg dh,al | |
call emitChar ; wypisz ostatni znak | |
return: | |
pop dx | |
pop cx | |
RET | |
emitChar: ; emituje znak(i) lub sekwencje kodujaca ciag znakow | |
; AL - $PREV, znak do wypisania, CL - $COUNT, DL - 0, DH - $CURR, nie ruszany. | |
or al,al | |
jz emitZero ; specjalne traktowanie zera. | |
cmp cl,3 | |
jbe emitPlain | |
emitSequence: ; sekwencja 0, $COUNT, $PREV | |
xchg al,dl ; AL = 0, DL = $PREV | |
call PutChar ; 0x00 -> bufor | |
xchg al,dl ; przywroc | |
xchg al,cl ; AL = $COUNT, CL = $CURR | |
call PutChar ; $COUNT -> bufor | |
xchg al,cl ; przywroc | |
call PutChar ; $PREV -> bufor | |
RET | |
emitPlain: ; wypisz znak $PREV $COUNT razy | |
call PutChar ; AL -> bufor | |
loop emitPlain | |
RET | |
emitZero: ; korekta $COUNT dla 0 i wybor metody | |
cmp cl,1 ; dla 2+ zer oplaca sie bardziej kodowac jako trojke. | |
ja emitSequence | |
shl cl,1 ; podwajamy pojedyncze zero | |
jmp emitPlain | |
Encode endp | |
; Decode | |
; rozpakuj bajty z bufora wejsciowego do wyjsciowego | |
; \x00 \x00 => konwertuj na \x00 | |
; \x00 \xCC \xAA => konwertuj na \xAA powtorzone CC razy | |
; inne bajty przepisywane bezposrednio. | |
Decode proc | |
push cx | |
decodeLoop: | |
call GetChar ; AL <- bajt z bufora, CF=1 gdy brak | |
jc decodeReturn ; koniec pliku, wyjdz | |
or al,al ; znak \0, escape character | |
jz escapeCharacter | |
; pisz znak bezposrednio | |
writeCharacter: | |
call PutChar; AL -> wyjscie | |
jmp decodeLoop | |
; sekwencja ze znakiem modyfikacji | |
escapeCharacter: | |
call GetChar ; AL <- Pobranie ilosci | |
jc DecodeError | |
or al,al ; gdy dwa zera | |
jz writeCharacter ; wypisz pojedyncze zero | |
; sekwencja (00, ilosc, znak) | |
xor cx,cx | |
mov cl,al | |
call GetChar ; AL <- faktyczny znak w kodowaniu | |
jc DecodeError | |
repeatChar: | |
call PutChar ; AL -> wyjscie | |
loop repeatChar ; cx--. wiemy, ze cx > 0, bo dla =0 robimy co innego | |
; powtorz dla kolejnej porcji danych: | |
jmp decodeLoop | |
decodeReturn: | |
pop cx | |
RET | |
; Blad przy dekompresji, plik nie jest RLE. | |
DecodeError: | |
mov dx, offset DecodeErrMsg | |
mov al, 0FEh ; exit(-2) | |
jmp errorExit | |
Decode endp | |
; Kopiuj argument wskazywany przez DL do adresu wskaz przez DI | |
copyfilename: | |
call GetArgLen ; AX <- dlugosc argumentu | |
mov cx,ax ; ilosc danych do skopiowania | |
call GetArgPos ; AX <- pozycja argumentu | |
mov si,ax ; kopiuj z argv | |
rep movsb ; kopiowanie DS:SI -> ES:DI | |
RET | |
; OpenFiles | |
; Otworz plik z iname do odczytu, plik z oname do nadpisywania | |
; (DS wskazuje na _Dane) | |
OpenFiles proc | |
push dx | |
push ax | |
; wejsciowy do odczytu | |
mov dx, offset iname ; Nazwa pliku w DS:DX, jako string z zerem | |
mov ax, 3D00h ; AH=3Dh - otwarcie, AL=00 - odczyt | |
int 21h ; AX <- uchwyt pliku lub kod bledu | |
jc InputFileError ; if(CF) blad przy otwarciu, else: | |
mov word ptr ds:[ifile], ax ; zapisz uchwyt z AX w pamieci | |
;stworz lub wymaz wyjsciowy | |
mov dx, offset oname | |
mov ax, 3C00h ; utworz plik lub usun jego zawartosc | |
int 21h ; AX <- uchwyt/kod bledu, uchwyt ignorujemy | |
jc OutputFileError | |
; wyjsciowy do zapisu | |
mov ax, 3D01h ; AL=01 zapis | |
int 21h ; AX <- uchwyt / kod bledu | |
jc OutputFileError | |
mov word ptr ds:[ofile], ax ; zapisz uchwyt w pamieci | |
pop ax | |
pop dx | |
RET | |
; Bledy: przerwanie 21h zwraca kody w AL, errorExit wychodzi ze satusem z AL. | |
InputFileError: | |
mov dx, offset InputFileErrMsg | |
jmp errorExit | |
OutputFileError: | |
mov dx, offset OutputFileErrMsg | |
jmp errorExit | |
OpenFiles endp | |
; GetChar | |
; Czytaj znak z pliku wejsciowego do AL. CF=0 jesli Wczytanie sie udalo | |
; Uwaga! Wartość AH jest niszczona | |
GetChar proc | |
mov ax, word ptr ds:[ipos] ; wczytaj pozycje w buforze | |
cmp word ptr ds:[isize], ax ; czy bufor sie skonczyl? | |
je reloadBuffer ; tak, wczytaj dane | |
fetch: ; Wczytaj znak normalnie | |
push bx | |
mov bx, word ptr ds:[ipos] ; pozycja w buforze | |
mov al, byte ptr ds:[ibuffer+bx] ; wczytaj znak | |
inc word ptr ds:[ipos] ; przesun na kolejny znak | |
pop bx | |
clc ; sukces -> CF=0 | |
RET ; kod znaku w AL | |
reloadBuffer: ; Wypelnij bufor nowa porcja danych: | |
cmp byte ptr ds:[ifinal], 0 | |
; czy poprzedni bufor to finalna porcja danych? | |
jnz fileEnded ; nie ma wiecej w pliku, zakoncz | |
push bx | |
push cx | |
push dx | |
mov ah, 3Fh ; READ, odczyt danych z pilku | |
mov bx, word ptr ds:[ifile] ; uchwyt w ifile | |
mov cx, BUFSIZE ; czytaj tyle bajtow | |
mov dx, offset ibuffer ; do bufora wejsciowego | |
int 21h ; AX <- ilosc faktycznie odczytanych bajtow | |
jc ReadError ; CF=1, to bledy | |
or ax,ax ; koniec pliku jesli wczytalismy zero bajtow | |
jz fileEnded | |
mov word ptr ds:[isize], ax ; nowy rozm bufora | |
mov word ptr ds:[ipos], 0 ; pozycja na poczatek | |
cmp cx, ax ; czy wczytalismy koniec pliku? | |
pop dx | |
pop cx | |
pop bx | |
je fetch ; jesli CX==AX, to plik sie nie skonczyl, można czytac | |
inc byte ptr ds:[ifinal] ; to ostatni fragment pliku. | |
jmp fetch | |
fileEnded: ; plik sie skonczyl nie ma co czytac | |
stc ; ustaw carry, CF=1 | |
RET | |
ReadError: ; blad przy odczycie, kod bledu w AL | |
mov dx, offset ReadErrMsg | |
jmp errorExit | |
GetChar endp | |
; PutChar | |
; zapisz znak z AL do pliku wyjsciowego | |
PutChar proc | |
cmp word ptr ds:[osize], BUFSIZE; czy bufor jest pelen? | |
jne put | |
call Flush ; oproznij bufor | |
put: | |
push bx | |
mov bx, offset obuffer ; pozycja bufora + rozmiar | |
add bx, word ptr ds:[osize] ; daje pierwsze wolne miejsce | |
mov byte ptr ds:[bx], al ; wpisz znak | |
inc word ptr ds:[osize] ; zwieksz rozmiar | |
pop bx | |
RET | |
PutChar endp | |
; Flush | |
; Zapisz bufor wyjscia do pliku | |
Flush proc | |
push ax | |
push bx | |
push cx | |
push dx | |
mov ah, 40h; WRITE zapis danych | |
mov bx, word ptr ds:[ofile] ; uchwyt pliku wyjsciowego | |
mov cx, word ptr ds:[osize] ; rozmiar danych do zapisu | |
mov dx, offset obuffer ; bufor wyjscia | |
int 21h ; AX <- ilosc danych zapisanych | |
jc WriteError ; CF=1 bledy zapisu | |
cmp cx,ax ; zapisalismy wszystko? | |
jnz WriteError ; tez zle, moze dysk jest pelen. | |
mov word ptr ds:[osize], 0 ; Oproznij bufor | |
pop dx | |
pop cx | |
pop bx | |
pop ax | |
RET | |
WriteError: | |
mov dx, offset WriteErrMsg | |
jmp errorExit | |
Flush endp | |
; CloseFiles | |
; Zamyka pliki wejscia i wyjscia | |
CloseFiles proc | |
push ax | |
push bx | |
mov bx, word ptr ds:[ifile] ; Uchwyt do zamkniecia | |
mov ah, 3Eh ; CLOSE zamyka plik | |
int 21h ; AX <- niszczony / kod bledu | |
jc CloseError ; CF=1 jesli bledy | |
mov bx, word ptr ds:[ofile] ; to samo dla drugiego pliku | |
mov ah, 3Eh ; CLOSE zamyka plik | |
int 21h ; AX <- niszczony / kod bledu | |
jc CloseError ; CF=1 jesli bledy | |
pop bx | |
pop ax | |
RET | |
CloseError: | |
mov dx, offset CloseErrMsg | |
jmp errorExit | |
CloseFiles endp | |
; ParseArgs ------------------------------------------------- | |
; wczytaj argumenty z [80h] do struktury (argc,args,argv) | |
; wykorzystujac EatWS i CopyWord. | |
; przy wywolaniu rejestr DS powinien wskazywac na segment PSP | |
ParseArgs proc | |
pusha ; push AX, CX, DX, BX, SP, BP, SI, DI | |
xor cx,cx ; CX = 0 | |
mov cl, byte ptr ds:[80h] ; cx na ilosc bajtow lini komend | |
mov si, 81h ; ds:[si] na poczatek bufora argumentow w PSP | |
mov ax, seg argv ; | |
mov es, ax ; es na segment danych | |
mov di, offset argv ; es:[di] na poczatek argv | |
iter: | |
call EatWS ; zmniejsz cx, zwieksz si | |
and cx,cx ; cmp cx,0 | |
jz return ; Bufor byl pusty / zawieral same biale znaki | |
; dodaj nowy parametr do args | |
xor bh, bh ; Ustaw bx na | |
mov bl, byte ptr es:[argc] ; ilosc argumentow | |
shl bx, 1 ; bx *= 2, adresujemy word | |
mov word ptr es:[args+bx], di ; aktualne di jako adr tego parametru | |
inc byte ptr es:[argc] ; argc++ | |
call CopyWord ; zmniejsz cx, zwieksz si+di | |
jmp iter | |
return: | |
popa ; odwrotnie do PUSHA, nie kopiuje SP, pomija go | |
ret | |
ParseArgs endp | |
;MAKRO: Jump if Equal -- jesli Reg ma wartosc Val, skocz do Label | |
jmpEq macro Reg, Val, Label | |
cmp Reg, Val | |
je Label | |
endm | |
; Eat WhiteSpace ------------------------- | |
; przesun SI na pierwszy niebialy bajt | |
; zmienia: si, cx | |
EatWS proc | |
iter: ; wyjdz gdy bufor sie skonczyl | |
and cx,cx ; cx == 0 ? | |
jz return ; tak -> break | |
dec cx ; cx-- | |
lodsb ; al = ds:[si] ; si++ | |
; if ( bialy ) continue | |
jmpEq al, 20h, iter ; spacja | |
jmpEq al, 09h, iter ; \t tab | |
jmpEq al, 0Ah, iter ; \n line-feed | |
jmpEq al, 0Ch, iter ; \f form-feed | |
jmpEq al, 0Dh, iter ; \r carriage-return | |
; gdy niebialy: | |
inc cx ; "oddajemy" pobrany znak, | |
dec si ; cofajac sie w buforze | |
return: | |
ret | |
EatWS endp | |
; CopyWord -------------------------------------------------------- | |
; kopiuj bajty z ds:[si] do es:[di], az do pierwszego bialego znaku | |
; przesun SI na pierwszy bialy, DI na miejsce na kolejny argument | |
; zmienia: si, di, cx | |
CopyWord proc | |
iter: ; wyjdz gdy bufor sie skonczyl | |
and cx,cx ; cx == 0 ? | |
jz return ; tak -> break | |
dec cx ; pobranie znaku: | |
lodsb ; al = ds:[si] ; si++ | |
; if ( bialy ) break | |
jmpEq al, 20h, done ; spacja | |
jmpEq al, 09h, done ; \t tab | |
jmpEq al, 0Ah, done ; \n line-feed | |
jmpEq al, 0Ch, done ; \f form-feed | |
jmpEq al, 0Dh, done ; \r carriage-return | |
; gdy niebialy | |
stosb ; es:[di] = al, di++ | |
jmp iter | |
done: ; petla trafila na bialy znak, koryguj indeksy | |
inc cx ; oddaj pobrany znak | |
dec si ; wroc w buforze | |
inc di ; zostaw 1 bajt (dolar) przerwy w argv | |
return: | |
ret | |
CopyWord endp | |
; Ponizsze procedury zakladaja, ze ds wskazuje na segment danych: | |
; GetArgNum -------------------- | |
; zaladuj do AL ilosc argumentow | |
; (zaklada, ze ds wskazuje na segment danych) | |
GetArgNum proc | |
xor ax,ax ; AX = 0 | |
mov al, byte ptr ds:[argc] ; kopiuj | |
ret | |
GetArgNum endp | |
; GetArgPos ----------------------------------- | |
; zaladuj do AX adres argumentu o indeksie z DL | |
; (zaklada, ze ds wskazuje na segment danych) | |
GetArgPos proc | |
push bx | |
xor ax,ax ; AX = 0 | |
mov al, dl ; AX = DL | |
shl ax, 1 ; AX*2, word ptr | |
mov bx, offset args | |
add bx, ax ; BX na args[DL] | |
; AX = adres DL-tego argumentu | |
mov ax, word ptr ds:[bx] | |
pop bx | |
ret | |
GetArgPos endp | |
; GetArgLen ------------------------------------- | |
; zaladuj do AX dlugosc argumentu o indeksie z DL | |
; (zaklada, ze ds wskazuje na segment danych) | |
GetArgLen proc | |
push bx | |
call GetArgPos ; znajdz poczatek DL-tego argumentu | |
mov bx, ax | |
xor ax,ax ; ax = 0 | |
lenLoop: | |
cmp byte ptr ds:[bx], '$' | |
je return | |
inc ax | |
inc bx | |
jmp lenLoop | |
return: | |
pop bx | |
ret | |
GetArgLen endp | |
_Kod ends | |
_Stos segment stack | |
; Rozmiar 80h w slowach (256d bajtow) | |
dw 7Fh dup(?) | |
wstosu dw ? | |
_Stos ends | |
end start |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment