Skip to content

Instantly share code, notes, and snippets.

@P4
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save P4/744718323c080cb7df9f to your computer and use it in GitHub Desktop.
Save P4/744718323c080cb7df9f to your computer and use it in GitHub Desktop.
Implementacja Run-Length-Encoding (RLE)
; 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