Страница Ивана Рощина > Статьи > Вывод чёрно-белых изображений с градациями яркости >

Приложение 4

Здесь приведены тексты процедур для работы с pcx-файлами, содержащими чёрно-белые изображения с 256 градациями яркости. Кратко расскажу о формате таких файлов — это необходимо для понимания логики работы процедур.

Табл. 2
Часть файла Длина в байтах
Заголовок 128
Упакованная информация о пикселах изображения переменная
Тип палитры 1
Палитра 768

Заголовок файла содержит информацию о размерах изображения, количестве цветов и т.п. В процедуре чтения (READ_PCX) заголовок игнорируется, так как эти данные предполагаются уже известными. В процедуре записи (MAKE_PCX) заголовок записывается всегда один и тот же.

Информация о пикселах изображения хранится в упакованном виде. При упаковке последовательность одинаковых байтов заменяется на два байта: счётчик и оригинал. Чтобы можно было распознавать байт-счётчик при распаковке, его старшие два бита устанавливаются в 1, а длина последовательности (от 1 до 63) хранится в младших шести битах. Если длина последовательности больше 63, записывается несколько пар «счётчик-значение». Если при упаковке попадается отдельный байт, старшие два бита которого равны 1, то, чтобы при распаковке он не был перепутан с байтом-счётчиком, перед ним ставится байт-счётчик с количеством повторений, равным 1.

Тип палитры — #0A, если значения байтов палитры находятся в диапазоне 0—63, и #0C, если в диапазоне 0—255. В процедуре чтения тип палитры игнорируется, а в процедуре записи — записывается #0C.

Палитра — в ней содержится информация о компонентах R, G, B каждого из 256 цветов изображения. В процедуре чтения палитра игнорируется, а в процедуре записи — записывается последовательность байтов (0,0,0, 1,1,1, …, 255,255,255).

Исходя из вышесказанного, можно определить максимальную длину файла: 2 байта на пиксел — это 256*192*2=#18000, ещё #80 байтов — заголовок, 1 байт — тип палитры и #300 байтов — сама палитра, итого #18381 байт. Но в TR-DOS длина файла не может превышать #FF00 байтов. Поэтому в процедуре MAKE_PCX, если длина файла получается больше #FF00, на диск записываются два файла: первый длиной #FF00, а во втором — остаток.

Итак, тексты процедур.

1.

READ_PCX — чтение изображения из pcx-файла в три банка памяти.

Вход:

имя и расширение файла указано в FILENAME, номера банков памяти — в N_BANK.

Выход: 

A=1 — OK, A=0 — файл не найден.

;Структуры данных:

BUFER      EQU  #7000 ;256-байтный буфер (адрес кратен 256!).

;Пошла сама процедура:

           ORG  #6000

READ_PCX   LD   HL,BUFER
           LD   (GET_B_0),HL
           LD   HL,N_BANK
           LD   (PUT_B_1),HL
           LD   HL,#C000
           LD   (PUT_B_2),HL

;Ищем файл:

           LD   HL,FILENAME
           LD   C,#13
           CALL #3D13

           LD   C,#0A
           CALL #3D13

           LD   A,C
           INC  A
           RET  Z ;Нет такого файла.

           DEC  A
           LD   C,8   ;Читаем
           CALL #3D13 ;дескриптор.

;Определяем трек/секторный адрес начала
;файла:

           LD   HL,(#5CEB)
           LD   (GET_B_1),HL

;Пропускаем первые 128 байтов файла -
;это заголовок.

           LD   B,128
M1         CALL GET_B
           DJNZ M1

           LD   HL,#C000 ;Кол-во pix.

;Главный цикл:

M3         CALL GET_B ;Читаем байт.

;Если это байт-счётчик, то два его
;старших бита установлены в 1, а в
;младших 6 битах - количество
;повторений.

           LD   B,1
           LD   C,A
           CPL
           AND  %11000000
           LD   A,C
           JR   NZ,M2

;Повторяем следующий байт указанное
;количество раз:

           LD   A,%00111111
           AND  C
           LD   B,A
           CALL GET_B

M2         CALL PUT_B
           DEC  HL
           DJNZ M2

           LD   A,H ;Все #C000 пикселов?
           OR   L
           JR   NZ,M3

           INC  A   ;A:=1.
           RET

;Имя файла:

FILENAME   DB   "picture pcx"

;Номера используемых банков памяти:

N_BANK     DB   #10,#11,#13

;---------------------------------------
;Процедура GET_B - чтение байта из
;файла.

GET_B      PUSH HL
           PUSH BC

           LD   HL,0   ;Адрес в буфере.
GET_B_0    EQU  $-2
           LD   A,L
           AND  A
           JR   NZ,GET_B_3

;Надо прочитать сектор файла:

           PUSH HL

           LD   B,1
           LD   DE,0
GET_B_1    EQU  $-2
           PUSH DE
           LD   HL,BUFER
           LD   C,5
           CALL #3D13

;Увеличиваем трек/секторный адрес:

           POP  DE
           INC  E
           BIT  4,E
           JR   Z,GET_B_2
           LD   E,0
           INC  D
GET_B_2    LD   (GET_B_1),DE

           POP  HL

;Читаем байт из буфера:

GET_B_3    LD   A,(HL)
           INC  L
           LD   (GET_B_0),HL

           POP  BC
           POP  HL
           RET

;---------------------------------------
;Процедура PUT_B - помещение информации
;об очередном пикселе в память.

PUT_B      PUSH AF
           PUSH HL

           PUSH AF
           PUSH BC
           LD   HL,0
PUT_B_1    EQU  $-2
           LD   A,(HL)    ;N банка.
           LD   BC,#7FFD
           OUT  (C),A
           POP  BC
           POP  AF

           LD   HL,0      ;Адрес.
PUT_B_2    EQU  $-2
           LD   (HL),A

           INC  HL       ;Увелич. адреса.
           LD   A,H
           OR   L
           JR   NZ,PUT_B_3

           LD   HL,(PUT_B_1) ;Переход к
           INC  HL           ;следующему
           LD   (PUT_B_1),HL ;банку.
           LD   HL,#C000

PUT_B_3    LD   (PUT_B_2),HL

           POP  HL
           POP  AF
           RET
2.

MAKE_PCX — запись изображения из трёх банков памяти в pcx-файл.

Вход:

имя и расширение файла указано в NAME_EXT, номера банков памяти — в N_BANK.

Выход: 

A=1 — OK, A=0 — записать файл не удалось (нет места на диске или в каталоге диска).

;Структуры данных:

FILE_BUFER EQU  #7000 ;Буфер для записи.
CAT_BUFER  EQU  #7100 ;Каталог диска.

;Пошла сама процедура:

           ORG  #6000

MAKE_PCX   CALL INIT_GET

;Сначала преобразуем "вхолостую", чтобы
;узнать размер получаемого файла. В это
;время на бордюр выводятся цветные
;полосы - чтобы показать, что компьютер
;не завис.

           LD   D,0     ;DHL - длина.
           LD   HL,#381
           LD   BC,1

MAKE_PCX_1 CALL GET_BYTE
           JR   C,MAKE_PCX_2
           AND  7            ;Изменяем
           OUT  (254),A      ;BORDER.
           ADD  HL,BC
           LD   A,D
           ADC  A,0
           LD   D,A
           JR   MAKE_PCX_1

MAKE_PCX_2 LD   (LEN_FILE),HL
           LD   A,D
           LD   (LEN_FILE+2),A

;Длина в байтах определена, теперь
;получаем длину в секторах:

           LD   BC,255
           ADD  HL,BC
           LD   A,D
           ADC  A,0
           LD   L,H
           LD   H,A ;HL - длина.
           PUSH HL  ;Запомнили её...

;Читаем каталог:

           LD   HL,CAT_BUFER
           LD   DE,0
           LD   B,9
           LD   C,5
           CALL #3D13

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

           LD   HL,(CAT_BUFER+#8E5)
           POP  DE ;Длина файла.
           XOR  A  ;A:=0, флаг C сброшен.
           SBC  HL,DE
           RET  C  ;Нет места на диске!

;Коррекция свободного места:

           LD   (CAT_BUFER+#8E5),HL

;Теперь определяем, сколько будет
;файлов: один или два?

           LD   A,1    ;Кол-во файлов.
           EX   DE,HL
           LD   DE,255
           SBC  HL,DE  ;Флаг C сброшен!
           JR   C,MAKE_PCX_7 ;1 файл.
           INC  A            ;2 файла.
MAKE_PCX_7 LD   (FILES),A

;Проверка свободного места в области
;каталога:

           LD   HL,CAT_BUFER+#8E4
           LD   B,(HL)
           ADD  A,B
           LD   (HL),A ;Коррекция.
           CP   128+1
           LD   A,0
           RET  NC     ;Нет места!

;Определяем номер первого сектора файла:

           LD   HL,(CAT_BUFER+#8E1)
           LD   (DISK_ADR),HL

;Помещаем в каталог информацию о файле
;(или о двух файлах, если длина больше
;#FF00):

           LD   H,0
           LD   L,B
           ADD  HL,HL
           ADD  HL,HL
           ADD  HL,HL
           ADD  HL,HL ;*16
           LD   DE,CAT_BUFER
           ADD  HL,DE

           EX   DE,HL
           LD   HL,NAME_EXT  ;Заголовок.
           LD   BC,11
           LDIR

           LD   A,(FILES)
           CP   2
           JR   NZ,MAKE_PCX_A

;Записывается два файла.
;Первый файл:

           EX   DE,HL
           LD   (HL),0   ;Длина:
           INC  HL
           LD   (HL),#FF ;#FF00 байтов,
           INC  HL
           LD   (HL),#FF ;#FF секторов.
           INC  HL

           LD   A,#FF    ;Адрес на
           CALL NEW_TS   ;диске.

;Второй файл:

           EX   DE,HL
           LD   HL,NAME_EXT ;Заголовок.
           LD   BC,8
           LDIR
           LD   A,"0"  ;Первый символ
           LD   (DE),A ;расширения - "0".
           INC  DE
           INC  HL
           LDI
           LDI

           EX   DE,HL        ;Длина:
           LD   A,(LEN_FILE) ;младший
           LD   (HL),A       ;байт...
           INC  HL

           PUSH HL
           LD   HL,(LEN_FILE+1)
           DEC  H
           INC  HL     ;-#FF00
           LD   A,L          ;и старший
           POP  HL           ;байт.

           LD   (HL),A

           PUSH HL
           DEC  HL
           LD   L,(HL)
           LD   H,A
           INC  H
           DEC  HL
           LD   A,H  ;Длина в секторах.
           POP  HL

           INC  HL
           LD   (HL),A

           INC  HL
           CALL NEW_TS
           JR   MAKE_PCX_9

;Записывается один файл:

MAKE_PCX_A EX   DE,HL
           LD   DE,(LEN_FILE)
           LD   (HL),E      ;Длина
           INC  HL          ;в байтах.
           LD   (HL),D
           INC  HL

           INC  D           ;Длина
           DEC  DE          ;в секторах.
           LD   (HL),D
           INC  HL

           LD   A,D
           CALL NEW_TS

;Записываем каталог:

MAKE_PCX_9 LD   HL,CAT_BUFER
           LD   DE,0
           LD   B,9
           LD   C,6
           CALL #3D13

;Записываем содержимое файла:

           CALL OPEN_FILE

;Заголовок (128 байтов):

           LD   HL,HEADER
           LD   B,128

MAKE_PCX_6 LD   A,(HL)
           CALL PUT_BYTE
           INC  HL
           DJNZ MAKE_PCX_6

;Данные о пикселах:

           CALL INIT_GET

MAKE_PCX_3 CALL GET_BYTE
           JR   C,MAKE_PCX_4
           CALL PUT_BYTE
           JR   MAKE_PCX_3

;Тип палитры:

MAKE_PCX_4 LD   A,#0C
           CALL PUT_BYTE

;Палитра (#300 байтов):

           XOR  A
MAKE_PCX_5 CALL PUT_BYTE
           CALL PUT_BYTE
           CALL PUT_BYTE
           INC  A
           JR   NZ,MAKE_PCX_5

;Закрываем файл:

           CALL CLOSE_FILE
           LD   A,1
           RET

NAME_EXT   DB   "picture pcx" ;Имя.
LEN_FILE   DS   3 ;Длина pcx-файла.
FILES      DS   1 ;Сколько будет файлов.

;---------------------------------------
;Процедура NEW_TS помещает по указанному
;в HL адресу сведения о первом
;свободном треке/секторе и обновляет
;эти сведения в системном секторе.
;
;Вход: A - длина файла в секторах.
;Выход: HL:=HL+2.

NEW_TS     LD   DE,(CAT_BUFER+#8E1)
           LD   (HL),E
           INC  HL       ;Адрес на
           LD   (HL),D   ;диске.
           INC  HL

;Увеличиваем номера первых незанятых
;трека/сектора:

           LD   B,A ;Длина в секторах
           AND  15
           ADD  A,E
           BIT  4,A
           RES  4,A
           LD   E,A
           JR   Z,NEW_TS_1
           INC  D
NEW_TS_1   LD   A,B
           RRA
           RRA
           RRA
           RRA
           AND  15
           ADD  A,D
           LD   D,A
           LD   (CAT_BUFER+#8E1),DE

           RET

;---------------------------------------
;Процедура GET_BYTE возвращает в
;аккумуляторе очередной байт упакованных
;данных о пикселах. Если флаг C
;установлен - значит, данные кончились.
;
;BC,DE,HL не изменяются.

STATUS     DS   1 ;N состояния проц.
BYTE_B     DS   1 ;Запомненный байт.

;Инициализация:

INIT_GET   LD   A,1
           LD   (STATUS),A
           CALL INIT_PIX
           RET

;Пошла сама процедура:

GET_BYTE   PUSH BC
           PUSH DE
           PUSH HL

           LD   A,(STATUS)
           CP   2
           JR   Z,GET_BYTE_2

;1-е состояние:

           LD   C,1 ;Счётчик одинаковых.
           CALL GET_PIX
           JR   C,GET_EXIT

           LD   (BYTE_B),A

GET_B_3    LD   A,C ;Уже есть 63
           CP   63  ;одинаковых байта?
           JR   Z,GET_B_1

           CALL GET_PIX  ;Берём ещё байт.
           JR   C,GET_B_4

           LD   HL,BYTE_B ;Сравниваем...
           CP   (HL)
           JR   NZ,GET_B_4

           INC  C
           JR   GET_B_3

GET_B_4    CALL PREV_PIX  ;Сдвиг назад.

GET_B_2    LD   A,(BYTE_B);Байт выглядит
           AND  %11000000 ;как
           CP   %11000000 ;байт-счётчик?
           JR   Z,GET_B_1

           LD   A,C       ;Одно
           DEC  A         ;повторение?
           JR   NZ,GET_B_1

           LD   A,(BYTE_B)
           AND  A         ;Сбрас. флаг C
           JR   GET_EXIT

GET_B_1    LD   A,2
           LD   (STATUS),A
           LD   A,C
           OR   %11000000 ;Сбрас. флаг C
           JR   GET_EXIT

;2-е состояние:

GET_BYTE_2 LD   A,1
           LD   (STATUS),A
           LD   A,(BYTE_B)
           AND  A         ;Сбрас. флаг C

GET_EXIT   POP  HL
           POP  DE
           POP  BC
           RET

;---------------------------------------
;Процедура GET_PIX последовательно
;возвращает данные о пикселах. Если
;флаг C установлен - значит, данные
;кончились.
;
;BC,DE,HL не изменяются.

GET_ADR    DS   2 ;Адрес в банке памяти.
GET_BANK   DS   1 ;Номер банка в таблице.

;Инициализация:

INIT_PIX   LD   HL,#C000-1
           LD   (GET_ADR),HL
           XOR  A
           LD   (GET_BANK),A
           RET

;Пошла сама процедура:

GET_PIX    PUSH HL

           LD   HL,(GET_ADR)
           INC  HL
           LD   (GET_ADR),HL
           LD   A,H
           OR   L
           JR   NZ,GET_PIX_1

           LD   HL,#C000
           LD   (GET_ADR),HL
           LD   A,(GET_BANK)
           INC  A
           LD   (GET_BANK),A
           CP   3
           JR   NZ,GET_PIX_1

           POP  HL
           SCF
           RET

GET_PIX_1  PUSH BC
           PUSH DE
           LD   BC,#7FFD
           LD   HL,N_BANK
           LD   A,(GET_BANK)
           LD   D,0
           LD   E,A
           ADD  HL,DE
           LD   A,(HL)
           OUT  (C),A
           LD   HL,(GET_ADR)
           LD   A,(HL)
           LD   D,#10
           OUT  (C),D
           POP  DE
           POP  BC
           POP  HL

           AND  A         ;Сбрас. флаг C.
           RET

;Сдвиг на пиксел назад:

PREV_PIX   PUSH HL

           LD   HL,(GET_ADR)
           DEC  HL
           LD   A,H
           CP   #BF
           JR   NZ,PREV_PIX_1
           LD   A,L
           CP   #FF
           JR   NZ,PREV_PIX_1

           LD   HL,#FFFF
           LD   A,(GET_BANK)
           DEC  A
           LD   (GET_BANK),A

PREV_PIX_1 LD   (GET_ADR),HL

           POP  HL
           RET

;Банки данных с информацией о пикселах:

N_BANK     DB   #10,#11,#13

;---------------------------------------
;Процедуры для побайтной записи
;содержимого файла с буферизацией.

IN_BUF     DS   1   ;Кол-во байтов в
                    ;буфере.
DISK_ADR   DS   2   ;Адрес сектора.

;OPEN_FILE - открыть файл.

OPEN_FILE  XOR  A
           LD   (IN_BUF),A
           RET

;PUT_BYTE - записать байт в файл.
;Вход: A - записываемое значение.
;Выход: регистры не изменены.

PUT_BYTE   PUSH AF
           PUSH BC
           PUSH DE
           PUSH HL

           LD   B,A
           LD   A,(IN_BUF)
           LD   HL,FILE_BUFER
           LD   D,0
           LD   E,A
           ADD  HL,DE
           LD   (HL),B

           INC  A
           LD   (IN_BUF),A

;Если буфер заполнился - записываем его:

           CALL Z,CLOSE_FILE

           POP  HL
           POP  DE
           POP  BC
           POP  AF
           RET

;CLOSE_FILE - закрыть файл.
;Содержимое буфера записывается на диск.

CLOSE_FILE LD   DE,(DISK_ADR)
           PUSH DE
           LD   B,1
           LD   C,6
           LD   HL,FILE_BUFER
           CALL #3D13
           POP  DE

           INC  E
           LD   A,E
           AND  15
           LD   E,A
           JR   NZ,CLOSE_EXIT
           INC  D
CLOSE_EXIT LD   (DISK_ADR),DE

           RET

;---------------------------------------
;128-байтный заголовок:

HEADER

 db #0A,#05,#01,#08,#00,#00,#00,#00
 db #FF,#00,#BF,#00,#00,#01,#C0,#00
 db #60,#61,#62,#63,#64,#65,#66,#67
 db #68,#69,#6A,#6B,#6C,#6D,#6E,#6F
 db #70,#71,#72,#73,#74,#75,#76,#77
 db #78,#79,#7A,#7B,#3F,#00,#00,#00
 db #50,#D6,#B0,#01,#14,#01,#00,#00
 db #C5,#B9,#F7,#BF,#C4,#30,#92,#81
 db #01,#01,#00,#01,#01,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
 db #00,#00,#00,#00,#00,#00,#00,#00
3.

SCR2BANK — преобразование изображения в спектрумовском формате в чёрно-белое изображение с 256 градациями яркости, расположенное в трёх банках памяти. С помощью предыдущей процедуры полученное изображение можно записать в pcx-файл.

Вход: 

исходное изображение загружено с #4000, номера банков памяти — в N_BANK, яркости градаций — в BRIGHT_0 и BRIGHT_1.

           ORG  #8000

SCR2BANK   LD   HL,N_BANK
           LD   (PUT_B_1),HL
           LD   HL,#C000
           LD   (PUT_B_2),HL

           LD   DE,0
MAIN       CALL GET_PIX

           LD   B,A
           AND  7       ;Индикация
           OUT  (254),A ;на бордюре.
           LD   A,B

           CALL PUT_B
           INC  D
           JR   NZ,MAIN
           INC  E
           LD   A,E
           CP   192
           JR   NZ,MAIN

           RET

;Номера используемых банков памяти:

N_BANK     DB   #10,#11,#13

;---------------------------------------
;Процедура GET_PIX возвращает яркость
;указанного пиксела изображения.
;
;Вход: DE - координаты пиксела.
;Выход: A - яркость (0..255).

GET_PIX    PUSH DE

;Определяем INK, PAPER, BRIGHT:

           LD   H,0
           LD   A,E
           AND  %11111000
           LD   L,A
           ADD  HL,HL
           ADD  HL,HL
           LD   A,D
           RRA
           RRA
           RRA
           AND  %00011111
           LD   E,A
           LD   D,#58
           ADD  HL,DE
           LD   A,(HL)
           RRA
           RRA
           AND  %00010000
           LD   (BRIGHT),A

           LD   A,(HL)
           AND  7
           LD   (INK),A

           LD   A,(HL)
           RRA
           RRA
           RRA
           AND  7
           LD   (PAPER),A

           POP  DE
           PUSH DE

           CALL BYTE
           AND  (HL)
           LD   A,0
PAPER      EQU  $-1
           JR   Z,GET_PIX_1
           LD   A,0
INK        EQU  $-1
GET_PIX_1  ADD  A,A
           ADD  A,0
BRIGHT     EQU  $-1
           LD   E,A
           LD   D,0
           LD   HL,BRIGHT_0
           ADD  HL,DE
           LD   E,(HL)
           INC  HL
           LD   D,(HL)
           LD   HL,#80
           ADD  HL,DE
           LD   A,H

           POP  DE
           RET

BYTE       LD   A,E
           AND  A
           RRA
           SCF
           RRA
           AND  A
           RRA
           XOR  E
           AND  #F8
           XOR  E
           LD   H,A
           LD   A,D
           RLCA
           RLCA
           RLCA
           XOR  E
           AND  #C7
           XOR  E
           RLCA
           RLCA
           LD   L,A
           LD   A,D
           AND  7
           LD   B,A
           INC  B
           LD   A,1
LOOP       RRCA
           DJNZ LOOP
           RET

;---------------------------------------
;Яркости градаций в формате 8.8:

BRIGHT_0   DW   0,1112,2139,4412,6062
           DW   14555,26811,46556

BRIGHT_1   DW   0,1369,3129,6062,8995
           DW   21704,38045,65280

;---------------------------------------
;Процедура PUT_B - запись в память
;информации об очередном пикселе.
;
;Вход: A - значение пиксела.

PUT_B      PUSH AF       ;Устанавливаем
           LD   A,(0)    ;нужный
PUT_B_1    EQU  $-2      ;банк
           LD   BC,#7FFD ;памяти.
           OUT  (C),A
           POP  AF

           LD   HL,0     ;Записываем.
PUT_B_2    EQU  $-2
           LD   (HL),A

           INC  HL       ;Увеличиваем
           LD   A,H      ;адрес.
           OR   L
           JR   NZ,PUT_B_3

           LD   HL,(PUT_B_1) ;Переход к
           INC  HL           ;следующему
           LD   (PUT_B_1),HL ;банку.
           LD   HL,#C000

PUT_B_3    LD   (PUT_B_2),HL
           RET
Страница Ивана Рощина > Статьи > Вывод чёрно-белых изображений с градациями яркости >