mov bl, 0 // BL = очистить счетчик повторов
mov al, ah // восстановить в AL текущий байт
//
Put_Byte: //------------------------
//
out Number_of_OutputPort, al // записать байт в выходной поток
jmp Get_from_InputStream // взять из входного потока следующий байт
Итак, регистр BL играет роль счетчика, увеличивающегося в тот момент, когда из входного потока приходят одинаковые байты. Разумеется, каждый следующий одинаковый байт увеличивает счетчик (регистр BL) на 1. Регистр AL принимает в себя тот байт, который только что поступил из входного потока. В регистре AH хранится предыдущий байт входного потока. Поскольку код ориентирован под конструкцию считывающего приемника, то есть входной порт микропроцессора подключен к свето-фотодиодной матрице, а его выходной порт подключен прямо к транспортной магистрали, поэтому и извлечение байт из входного потока и выталкивание байт в выходной поток выполнено при помощи команд чтения/записи портов ввода-вывода (номера требуемых портов заданы константами Number_of_InputPort и Number_of_OutputPort). Кроме того, работа по сжатию входного потока сделана в форме бесконечного цикла, подразумевая тот случай, когда аппаратный приемник без перерыва сканирует некую внешнюю информацию, соответственно, также постоянно эта информация (уже сжатая) поступает в транспортную магистраль.
И ниже то же самое, но уже для программистов в языках высокого уровня. Ну, на всякий случай, вдруг нет желания разбираться в ассемблерном коде.
Счетчик = 0
Предыдущий_байт = Взять_байт_из_входного_потока
Выходной_поток = Предыдущий_байт
Бесконечный цикл
Текущий_байт = Взять_байт_из_входного_потока
Если Текущий_байт = Предыдущий_байт тогда
Если Счетчик = 0 тогда Выходной_поток = Текущий_байт
Счетчик = Счетчик + 1
Если Счетчик = 255 тогда
Выходной_поток = Счетчик – 1
Счетчик = 0
Конец если
Иначе
Если Счетчик > 0 тогда Выходной_поток = Счетчик – 1
Выходной_поток = Текущий_байт
Предыдущий_байт = Текущий_байт
Счетчик = 0
Конец если
Конец цикла
Как работает декодер
Здесь алгоритм еще проще. При декодировании бывший выходной поток является уже входным потоком, а получающийся выходной поток – это декодированная информация. Так вот, извлекая из входного потока байт, сразу же выбрасываем его в выходной поток. Далее сравниваем текущий байт с предыдущим байтом. Как только они одинаковы, извлекаем из входного потока байт, который является счетчиком повторов. Именно столько раз в выходной поток копируем текущий байт (что был перед счетчиком).
Предыдущий_байт = Взять_байт_из_входного_потока
Выходной_поток = Предыдущий_байт
Бесконечный цикл
Текущий_байт = Взять_байт_из_входного_потока
Выходной_поток = Текущий_байт
Если Текущий_байт = Предыдущий_байт тогда
Счетчик = Взять_байт_из_входного_потока
Цикл Счетчик раз Выходной_поток = Текущий_байт
Конец если
Предыдущий_байт = Текущий_байт
Конец цикла
И на всякий случай привожу ассемблерный код алгоритма декодирования. Код, естественно, рассчитан опять под аппаратное устройство, то есть дешевую плату с микропроцессором (нет памяти, используются три регистра, работа через порты ввода-вывода), которое находится на противоположной стороне транспортной магистрали и занимается банальным декодированием приходящего из транспортной магистрали потока. Тут входной порт микропроцессора подключен к транспортной магистрали, а выходной порт - к чему-нибудь еще, куда затем направляется раскодированный поток.
Decode_Unbuffred_RLE: // -----------------------
//
in al, Number_of_InputPort // AL = первый байт из входного потока
out Number_of_OutputPort, al // записать байт в выходной поток
mov ah, al // этот байт теперь становится предыдущим
//
Get_from_InputStream: // -----------------------
//
in al, Number_of_InputPort // AL = следующий байт из входного потока
out Number_of_OutputPort, al // записать байт в выходной поток
cmp al, ah // равен ли он предыдущему байту?
mov ah, al // этот байт теперь становится предыдущим
jnz Get_from_InputStream // если байты не одинаковы, взять следующий байт
//
in al, Number_of_InputPort // AL = следующий байт из входного потока
mov bl, al // BL = счетчик повторов
mov al, ah // восстановить в AL текущий байт
//
Copy_Byte: //------------------------
//
cmp bl, 0 // есть ли еще повторы байта?
jz Get_from_InputStream // если нет, взять следующий байт
out Number_of_OutputPort, al // записать байт в выходной поток
dec bl // BL = еще один байт скопировали
jmp Copy_Byte // копировать байт дальше
Все, рассказ закончен. Будем полагать, вы осведомлены теперь, а значит подготовлены. Возникни у вас похожая задача, решение готово. Напомню только, что алгоритм эффективен с позиций строгих ограничений, накладываемых на исполняющие аппаратные устройства. Никаких привилегий в степени сжатия он не дает, ибо это всего лишь собрат RLE, который используют ту же самую методику сжатия. Если в RLE за счет принудительного введения поля счетчика оказывается по одному лишнему байту на каждую "запись" для разнобоев, то в Unbuffered RLE этот байт не затрачивается ввиду отсутствия "записей" для разнобоев, но зато каждая "запись" для повторов теперь занимает по 3 байта, и вот здесь затрачивается один лишний байт.