P8086         ;выбор процессора

MODEL SMALL   ;выбор модели

DOSHEAP = 0   ;указывает, будет ли использоваться куча ДОС, или

              ;внутренняя куча; 1- индицирует кучу ДОС; 0-внутреннюю

org 100h;

STACK 200h    ;резервирует пространство стека, необходимое приложению



DATASEG

   Text1 db 'Нажмите Enter и введите имя файла в формате: <имя>.<расширение> или нажмите Esc для выхода.$'

   Text2 db 'Вас приветствует архиватор текстовых файлов!',13,10,' $'

   Text3 db 'Файл в директории запуска имеющий расширение *.rec, будет распакован в файл с $'

   Text4 db 'расширением *.txt; с расширенем *.txt упакован в архив *.rec.',13,10,' $'

   ErrOpenMsg db 'Не удается открыть файл. Возможно неверно введено имя. $';

   ErrCreateMsg db 'Конечный файл уже существует. Перезаписать? [Y/N].$';

   Ent db 13,10,'$'                                     ;перевод строки

   Counter dw 0;

   Repeat  db 0;    счетчик повторов

   LastByte db 0;   последний считанный байт

   CurrentByte db 0;

   SourceLength dw 0;             длина исходного файла

   SourceFile dw 0,0;             описатель исходного файла

   TargetFile dw 0,0;             описатель создаваемого файла

   ;имя исходного файла

   SourcePath db  'source.txt                            '

   ;имя создаваемого файла

   TargetPath db  'target.rec                            '

   Max db 255

   Len db 0

   Bufer db 0;                    буфер чтения







CODESEG

   ;начало исполняемого кода

   STARTUPCODE

   ;EXE программа получает всю доступную ей память



IF DOSHEAP

   ;Освобождается вся память за исключением используемой в текущее время

   ;Конец стека является концом части программы, неразмещенной в куче

   MOV BX,SP

   ADD BX,15    ;выравнивает SP по границе параграфа

   SHR BX,4

   MOV AX,SS    ;вычисляется размер программы, используя ES PSP адрес

   ADD BX,AX

   MOV AX,ES

   SUB BX,AX

   MOV AH,4AH   ;изменяется размер блока памяти вместе с PSP

   INT 21H      ;адресация в ES

ENDIF



;исполняемый код пользователя.



   call StartProgram

   call OpenSource;

   call GetSourceLength;

   call Handler;

   mov BX,SourceFile; закрыть исходный файл

   call Close;

   mov BX,TargetFile; закрыть созданный файл

   call Close;

Exit:

   ;Выход в ДОС по завершению

   MOV AH,4CH

   INT 21H



StartProgram Proc

    mov AH,0; установка режима монитора

    mov AL,3; текстовый 80*25

    int 10h;

    ;вывод общей информации

    lea DX,Text2;

    call WriteLN

    lea DX,Text3;

    call WriteLN

    lea DX,Text4;

    call WriteLN

    ret

StartProgram Endp



OpenSource Proc

    jmp Inp

 ErrOpen:

    lea DX,ErrOpenMsg

    call Writeln

 Inp:

    ;вывод подсказки

    lea DX,Text1;

    call WriteLN 

    mov ah,0

    int 16h

    cmp al,27

    je Exit  

;ввод строки с именем файла

    call ReadLN

    lea DX,Bufer;

    mov AL,0;     открыть для чтения

    mov AH,3Dh;   функция открытия файла

    int 21h;      открываем

    jc ErrOpen;

    mov SourceFile,AX; запоминаем описатель исходного файла

    ret

OpenSource Endp



Handler Proc

   ;определяет расширение обрабатываемого файла

   ;и запускает нужную подпрограмму обработки

   mov BH,0

   mov BL,Len

   sub BX,3

   ; анализируем расширение

Sim1:

   cmp [Bufer+BX],'r';

   jz Sim2

   cmp [Bufer+BX],'R';

   jnz Select2          ;расширение точно не 'rec' значит пакуем файл

Sim2:

   cmp [Bufer+BX+1],'e';

   jz Sim3

   cmp [Bufer+BX+1],'E';

   jnz Select2          ;расширение точно не 'rec' значит пакуем файл

Sim3:

   cmp [Bufer+BX+2],'c';

   jz Select1

   cmp [Bufer+BX+2],'C';

   jnz Select2          ;расширение точно не 'rec' значит пакуем файл



Select1:

   call UnPack    ;запускаем распаковщик

   jmp EndSelect



Select2:

   call Pack      ;запускаем упаковщик



EndSelect:

   ret

Handler Endp



CreateTarget Proc

   ;создать новый файл и открыть его для записи

   lea DX, TargetPath

   mov CX,0;    атрибут файла

   mov AL,1;    открыть для записи

   mov AH,5Bh; функция создания файла,если файл существовал, то

             ; его содержимое не уничтожается, а возводится флаг С

   int 21h   ; создаем файл

   jnc CreateOk ;файл успешно создан существует



   ;файл существует. запрос подтверждения перезаписи

   lea DX,ErrCreateMsg;

   call WriteLN;

   ;анализ клавиатуры

KeyWait:

   mov AH,7; ввод символа с клавиатуры без отображения с ожиданием ввода

   int 21h;

   ;код в AL



   ;подтверждение перезаписи

   cmp AL,121; 'y'

   jz  Owerwrite

   cmp AL,89;  'Y'

   jz  Owerwrite



   ;отрицание перезаписи

   cmp AL,78; 'N'

   jz  ProgramStop;

   cmp AL,110; 'n'

   jz  ProgramStop;



   jmp KeyWait;



ProgramStop:

   ;Выход в ДОС по завершению

   MOV AH,4CH

   INT 21H



Owerwrite:

   ;перезаписать файл и открыть его для записи

   lea DX, TargetPath

   mov CX,0;    атрибут файла

   mov AL,1;    открыть для записи

   mov AH,3Ch; функция создания файла,если файл существовал, то он создается заново

   int 21h;     создаем файл



CreateOk:

   mov TargetFile,AX; запоминаем описатель создаваемого файла



   ret

CreateTarget ENDP



GetSourceLength Proc

  ;определение длины исходного файла

  mov BX,SourceFile;

  mov CX,0;

  mov DX,0;

  mov AH,42h; функция установки указателя в файле

  mov AL,2;   установка в позицию "конец файла + CX:DX"

  int 21h;

  mov SourceLength,AX;

  mov CX,0;

  mov DX,0;

  mov AH,42h; функция установки указателя в файле

  mov AL,0;   установка в позицию "начало файла + CX:DX"

  int 21h;

  ret

GetSourceLength Endp



Pack Proc

   ;формируем имя архива

   mov CH,0

   mov CL,len

   sub CL,4

   mov BX,CX

   mov [TargetPath+BX],'.';

   mov [TargetPath+BX+1],'r';

   mov [TargetPath+BX+2],'e';

   mov [TargetPath+BX+3],'c';

   mov [TargetPath+BX+4],0;

next:

   mov BX,CX

   mov AL,[Bufer+BX-1];

   mov [TargetPath+BX-1],AL;

   loop next;



   ;создаем файл и открывем его

   call CreateTarget;



  ;помещаем в счетчик длину исходного файла

  mov AX,SourceLength;

  mov Counter,AX;





  читаем первый байт

  lea DX,LastByte;

  mov BX,SourceFile;

  mov CX,1;   число байт

  mov AH,3Fh; функция чтения из файла

  int 21h;

  mov Repeat,0; повторов пока нет



NextByte:

  ;читаем байт из исходного файла

  lea DX,CurrentByte;

  mov BX,SourceFile;

  mov CX,1;   число байт

  mov AH,3Fh; функция чтения из файла

  int 21h;

  mov AL,CurrentByte

  cmp LastByte,AL;

  jnz Wri           ; текущий байт отличен от предыдущего

  inc Repeat        ; текущий байт совпадает с предыдущим

  cmp Repeat,255d;

  jz  Wri;

  jmp Continue





Wri:

  cmp Repeat,0;

  jz Wri2         ;предыдущий символ встречался 1 раз



  mov BX,TargetFile

  mov [Bufer],27d ;отметка что символ встречается более 1 раза

  lea DX,Bufer

  mov CX,1;

  mov AH,40h; функция записи в файл

  int 21h;

  lea DX,Repeat

  mov CX,1;

  mov AH,40h; функция записи в файл

  int 21h;





Wri2:



  ;записываем предыдущий байт в создаваемый файл

  mov BX,TargetFile

  lea DX,LastByte;

  mov CX,1;

  mov AH,40h; функция записи в файл

  int 21h;

  ;запоминаем текущий байт

  mov AL,CurrentByte;

  mov LastByte,AL;

  mov Repeat,0;

Continue:

  dec Counter

  jnz NextByte



  ;запись последнего байта

  mov BX,TargetFile

  lea DX,CurrentByte;

  mov CX,1;

  mov AH,40h; функция записи в файл

  int 21h;



  ret

Pack Endp



UnPack Proc

   ;формируем имя распакованого файла

   mov CH,0

   mov CL,Len

   sub CL,4

   mov BX,CX

   mov [TargetPath+BX],'.';

   mov [TargetPath+BX+1],'t';

   mov [TargetPath+BX+2],'x';

   mov [TargetPath+BX+3],'t';

   mov [TargetPath+BX+4],0;

next2:

   mov BX,CX

   mov AL,[Bufer+BX-1];

   mov [TargetPath+BX-1],AL;

   loop next2;



   ;создаем файл и открывем его

   call CreateTarget;



  ;помещаем в счетчик длину исходного файла

  mov AX,SourceLength;

  mov Counter,AX;



NextByte2:

  ;читаем байт из исходного файла

  lea DX,CurrentByte;

  mov BX,SourceFile;

  mov CX,1;   число байт

  mov AH,3Fh; функция чтения из файла

  int 21h;



  cmp CurrentByte,27d;  проверка метки повтора

  jnz One           ;  нет метки повтора

  ;есть метка повтора. читаем следующий байт - число повторов

  lea DX,Repeat;

  mov BX,SourceFile;

  mov CX,1;   число байт

  mov AH,3Fh; функция чтения из файла

  int 21h;

  inc Repeat;



  ;читаем повторяющийся байт из исходного файла

  lea DX,CurrentByte;

  mov BX,SourceFile;

  mov CX,1;   число байт

  mov AH,3Fh; функция чтения из файла

  int 21h;



  jmp Wri3;

One:

  mov Repeat,1;     только один раз



Wri3:

  ;записываем в создаваемый файл

  lea DX,CurrentByte;

  mov BX,TargetFile

  mov CX,1;

  mov AH,40h; функция записи в файл

  int 21h;

  dec Repeat;

  jnz Wri3;

  dec Counter

  jnz NextByte2



  ret

UnPack Endp



WriteLN Proc

  ;адрес выводимой строки в DX

  mov AH,9; функция вывода

  int 21h;

  ;переход на следующую строку

  lea DX,Ent;

  mov AH,09h; функция вывода

  int 21h;

  ret

WriteLN Endp



ReadLN Proc ;  ввод имени файла

  ; ввод имени файла

  lea DX,Max; указываем на буфер ввода

  mov AH,0Ah; буферизованный ввод 