Воспроизведение сжатых рисунков
Подпрограмма распаковки строки
Предположим, что в оперативной памяти задача зарезервировала пространство для размещения буфера общего назначения достаточно большого размера. Сегмент, в котором расположен буфер, хранится в переменной GenSeg, а начало свободного пространства в этом сегменте – в переменной GenOffs. Эти переменные должны располагаться в разделе данных программы в следующем порядке.
GenOffs dw 0; адрес (смещение) в буфере общего назначения GenSeg dw 0; сегмент, содержащий буфер общего назначения
Способы резервирования пространства в оперативной памяти описаны в приложении Б данной книги.
Текст подпрограммы распаковки строки приведен в примере 3.24.
Пример 3.24. Распаковка строки рисунка (способ RLE для PCX).
Unpack: PushReg <ax,cx,dx,di,es>; сохранение содержимого регистров les di, Dword ptr GenOffs; смещение и сегмент буфера mov dx, fwidth; логический размер строки Unploop: call nxt sym; читаем в al следующий символ mov ex, 01; количество повторяемых символов cmp al, OCOh; символ содержит счетчик повторов? jbe Unl; › нет, это одиночный символ mov cl, al; копируем содержимое al в cl and cl, 3Fh; и выделяем количество повторов call nxt sym; читаем в al – повторяемый символ Unl: sub dx, ex; уменьшаем остаток строки rep stosb; записываем символы в буфер строки or dx, dx; строка распакована полностью? jnz Unploop; нет, продолжение распаковки PopReg <es, di, dx, ex, ax>; восстанавливаем регистры ret; › возврат из подпрограммы
В примере 3.24 перед началом распаковки в стеке сохраняется содержимое используемых регистров. Затем команда les загружает в регистры es:di адрес для записи распакованной строки. Размер строки в байтах помещается в регистр dx, используемый в качестве счетчика распакованных символов. После этого выполняется цикл Unploop.
Действия при распаковке соответствуют описанному выше алгоритму. Очередной байт считывается в регистр al, а в счетчик повторов сх записывается 1. Если код символа меньше чем сон, то происходит переход на метку ип_1. В противном случае в cl помещается содержимое 6-ти младших разрядов регистра al и читается повторяемый символ.
Команда, имеющая метку un_i, вычитает из счетчика распакованных символов содержимое регистра сх. Следующая команда записывает в буфер строки содержимое регистра al столько раз, сколько указано в регистре сх. После этого проверяется содержимое регистра dx и если оно отлично от нуля, то цикл распаковки продолжается. В противном случае восстанавливается содержимое сохраненных в стеке регистров и выполняется команда возврата.
Подпрограмма Nxtjsym
Для получения кода очередного байта в примере 3.24 вызывается подпрограмма Nxtjsym, текст которой приведен в примере 3.25.
Пример 3.25. Чтение очередного символа из буфера обмена.
Nxt sym: cmp si, incount в буфере есть символы? jb @F › да, можно читать очередной символ push ex сохраняем содержимое сх mov ex, – I указываем размер порции данных call Readf читаем данные из файла mov incount, ax сохраняем размер порции данных xor si, si очищаем указатель адреса pop ex восстанавливаем содержимое сх @@: lods byte ptr fs: [si] чтение очередного байта ret; возврат из подпрограммы
Подпрограмма примера 3.25 сравнивает текущее значение указателя адреса буфера обмена (содержимое регистра si) с переменной incount, значение которой соответствует размеру считанной из файла порции данных, т. е. количеству байтов, находящихся в буфере обмена.
Если в буфере достаточно данных, то происходит переход на локальную метку @@. Для чтения кода символа в регистр al, увеличения указателя адреса на 1 и выхода из подпрограммы.
Если достигнута граница данных, хранящихся в буфере обмена, то надо прочитать новую порцию данных. Для этого сохраняется содержимое регистра сх, в него записывается предельное количество байтов для чтения (-1 имеет код OFFFFh) и происходит обращение к подпрограмме Readf, описанной в примере 3.23.
После чтения в переменную incount записывается количество прочитанных байтов, очищается регистр si и восстанавливается из стека содержимое регистра сх. Теперь можно прочитать очередной символ, увеличить значение сх на единицу и выйти из подпрограммы.
В примере 3.25 отсутствует проверка состояния С-разряда регистра признаков после чтения. Вы можете включить ее в текст примера 3.25., но целесообразнее контролировать правильность чтения непосредственно в подпрограмме Readf. Это упростит структуру всех подпрограмм, которые обращаются к Readf.
Замечание
В литературе встречаются сведения о существовании файлов, частично не соответствующих стандарту фирмы ZSoft – при их сжатии цикл упаковки не прекращается в конце строки. Для распаковки подобных файлов подпрограмму примера 3.24 надо изменить так, чтобы она выдавала заданное количество распакованных кодов независимо от размера строки.