В данной статье разобран оригинальный метод выполнения компрессии и декомпрессии экранной области "СПЕКТРУМА". Подробные комментарии позволяют использовать этот материал не только как полезную процедуру, но и как пособие тем, кто самостоятельно изучает программирование в машинных кодах и на языке АССЕМБЛЕРА.
ИНФОРКОМ выражает глубокую признательность И. Девятко из г. Нальчика, разработавшему и прокомментировавшему эти процедуры.
Техническое редактирование выполнено ИНФОРКОМом.
Экранная область в памяти компьютера начинается с адреса 16384 и занимает 6912 байтов. Если ваша программа содержит несколько картинок (экранов), то хранить их в памяти - дело довольно расточительное, поэтому желательно провести компрессию (архивацию) экрана перед отправлением его в память на хранение и, соответственно надо выполнять декомпрессию перед выдачей картинки на экран.
Если внимательно проанализировать содержимое экранной области памяти, то можно увидеть, что некоторые значения повторяются многократно. Предлагаемый здесь алгоритм основан на том, что если соседние ячейки не повторяются, то их значения просто переписываются в новую область, иначе переписываются первые два числа из группы повторяющихся, а за ними ставится двухбайтное число, показывающее количество повторений.
LD HL, 16364 LD ВС, 6911 LD DE, ADDR
1. ПРОГРАММА КОМПРЕССИИ
В HL -счетчик в экранной области.
- в BC -количество байтов экрана.
- в DE - адрес, в котором организуется хранение экрана.
Величину ADDR выберите сами. Экран содержит 6912 байтов, тем не менее мы
поместили в BC число 6911, и было это сделано вот почему. В дальнейшем мы организуем в BC счетчик повторений одинаковых байтов, т.е. эта регистровая пара будет работать "за двоих". Возникает необходимость как-то сообщить программе о том, что экранная область кончилась, для этого с последним 6912-ым байтом мы сделаем несложную манипуляцию. Мы сохраним его на стеке, а вместо него поставим инвертированную копию предпоследнего байта. Теперь мы можем компрессировать 6911 байтов, твердо зная, что 6912-ый снимем со стека. На АССЕМБЛЕРе это выглядят так:
ADD HL,BC ; HL = HL+BC, теперь в HL находится адрес последнего байта.
LD A,(HL) ; Значение последнего байта засылается в аккумулятор и
PUSH AF ; отправляется на стек.
DEC HL ; HL=HL-1. теперь в HL находится адрес предпоследнего байта.
LD A, (HL) ; Его значение поступает в аккумулятор.
CPL ; инвертируется и
INC HL ; копируется в последний
LD (HL),A ; байт.
После этого последний байт равен 255-предпоследний.
XOR А ; Сброс флага переноса.
Это необходимо потому, что далее мы применяем команду SBC, ведь простого вычитания SUB для двухбайтных чисел не бывает.
SBC HL,BC ;HL=HL-BC - установка в HL адреса начала компрессируемой области.
Мы могли бы эти операции и упростить, если бы пользовались готовыми значениями адресов, но мы этого не делали, чтобы процедура была универсальной и могла бы применяться и для компрессирования других областей памяти.
L0 LD A,(HL) LDI DEC C INC C JR HZ,L1 DEC B
В акк-р идет байт из исходной области
и переносится по адресу, на который указывает DE.
это проверка BC
на 0 и переход на
метку LK (конец),
если 0
INC B JR Z,LKON
Обычно проверку на ноль делают не так, а проще:
LD A, B OR C
В нашем случае этого делать нельзя, т. к. регистр А уже занят и пришлось бы его сохранять на стеке:
PUSH AF |
|
LD A, B |
|
OR C |
|
POP AF |
|
но это уже работает не так быстро, как приведенный нами метод. |
CP (HL) |
Следующий байт равен предыдущему? |
JR NZ, L0 |
Если нет, то на L0 (копирование). |
LDI |
Иначе копируем |
PUSH BC |
еще раз и запоминаем BC на стеке. |
LD ВС, 0 |
организуем в BC счетчик повторов. |
CP (HL) |
следующий байт равен предыдущему? |
JR NZ,L2 |
Если нет, то на L2 (восстановление прежнего состояния) |
INC ВС |
Наращиваем счетчик |
INC HL |
и переходим к следующему байту. |
JR L3 |
Повторяем проверку |
LD A, C |
записываем в |
LD (DE), А |
приемник количество |
INC (DE) |
повторений, |
LD A, B |
взятое из ВС. |
LD (DE), A |
|
INC DE |
DE=DE+1. После этого в DE содержится адрес первого пустого места в приемнике. |
EX (SP),HL |
HL coxp. на стеке и из стека установленное значение BC восстанавл. в HL, |
XOR A |
Сброс флага C. |
SBC HL^ |
HL=HL-BC Теперь мы имеем в HL количество байтов, которое осталось компрессировать. |
EX (SP,BC) |
двухходовая переброска |
POP BC |
из HL в BC через стек с восстановлением HL. |
LD A,B |
проверка BC на 0 |
OR C |
(на конец работы) |
JR Z, L0 |
|
POP AF |
Восстановление последнего байта и |
LD (DE),A |
напр. в приемник. |
INC DE |
DE=DE+1 |
PUSH DE |
Переброска из DE в |
POP BC |
BC через стек. |
RET |
Выход. |
Длина сжатого блока равна содержимому BC минус начальный адрес. Мы специально поместили результат в BC, чтобы из БЕЙСИКа по команде PRINT USR его можно было бы легко выдать на экран.
ПРИМЕР
Рассмотрим конкретный пример. Адрес трансляции программы примем 30000, а адрес хранения компрессированного экрана - 32766.
Адрес Маш |
.код |
Метка Мнемоника |
7530 |
21 |
00 40 |
LD HL,16384 |
7533 |
01 |
FF 1A |
LD ВС,6911 |
7536 |
11 |
00 80 |
LD DE,32768 |
7539 |
09 |
|
ADD HL,ВС |
753А |
7Е |
|
LD A, (HL) |
753В |
F5 |
|
PUSH HL |
753C |
2B |
|
DEC HL |
753D |
7Е |
|
LD A,(HL) |
753E |
2F |
|
CPL |
753F |
23 |
|
|
INC HL |
7540 |
77 |
|
|
LD (HL), А |
7541 |
AF |
|
|
XOR A |
7542 |
ED |
42 |
|
SBC HL,BC |
7544 |
7E |
|
L0 |
LD A,(HL) |
7546 |
ED |
A0 |
|
LDI |
7540 |
0D |
|
|
DEC C |
7549 |
0С |
|
|
INC C |
754A |
20 |
04 |
|
JR NZ,L1 |
754C |
05 |
|
|
DEC B |
754D |
04 |
|
|
INC B |
754E |
28 |
20 |
|
JR Z, LK |
7550 |
BE |
|
L1 |
CP (HL) |
7551 |
20 |
F2 |
|
JR NZ,L0 |
7553 |
ED |
A0 |
|
LDI |
7554 |
C5 |
|
|
PUSH ВС |
7555 |
01 |
00 00 |
|
LD ВС, 00 |
7556 |
BE |
|
L3 |
CP (HL) |
7559 |
20 |
04 |
|
JR HZ,L2 |
755B |
03 |
|
|
INC ВС |
755C |
23 |
|
|
INC HL |
755D |
18 |
F9 |
|
JR L3 |
755F |
79 |
|
L2 |
LD A, С |
7560 |
12 |
|
|
LD (DE),A |
7561 |
13 |
|
|
INC DE |
7562 |
78 |
|
|
LD A, B |
7563 |
12 |
|
|
LD (DE),A |
7564 |
13 |
|
|
INC DE |
7565 |
E3 |
|
|
EX (SP),HL |
7566 |
AF |
|
|
XOR A |
7567 |
ED |
42 |
|
SBC HL, ВС |
7569 |
E3 |
|
|
EX (SP),HL |
756A |
C1 |
|
|
POP ВС |
756B |
78 |
|
|
LD A, B |
756C |
B1 |
|
|
OR C |
756D |
2O |
D5 |
|
JR NZ,L0 |
756F |
F1 |
|
LK |
POP AF |
7570 |
12 |
|
|
LD (DE), A |
7571 |
13 |
|
|
INC DE |
7572 |
C9 |
|
|
RET |
Загрузите в экран какую-либо картинку, вызовите из БЕЙСИКа компрессирующую программу PRINT USR 30000 и запомните число, которое получили, его надо будет указывать при декомпрессии.
Зная конечный адрес и начало, можно вычислить и длину: длина = результат-32768
Теперь можно выгрузить блок кодов: SAVE"имя"CODE 32768,длина. Процедуру декомпрессии мы дадим в следующем выпуске.