Оглавление
§ Модель видеопамяти
В этом главе уже начинается рассказ о том, как запрограммировать великий Спектрум (хвала ему), чтобы он мог находить видеоадреса. Я рассмотрю 2 независимых варианта, а то есть, вычисление видеоадреса для X=0..31, Y=0..23 в текстовом режиме и X=0..255, Y=0..191 в режиме графическом.
Видеопамять у Спекки странная, конечно, но вполне постижимая. Необходимо научиться транслировать сначала Y, поскольку он самый трудный для понимания.
Общий формат 16-битного видеоадреса:
| 15 | 14 | 13 | 12 |
11 | 10 | 9 | 8 |
7 | 6 | 5 | 4 |
3 | 2 | 1 | 0 |
0 |
1 |
0 |
Y[7:6] |
Y[2:0] |
Y[5:3] |
X[4:0] |
Здесь X и Y – это задаваемые параметры. В данном случае X может быть от 0 до 31, поскольку видеоадрес задается по 1 байту, который равен 8 точкам. А вот значение Y задается от 0 до 191 и важно следить, чтобы он не "уплыл" за 191, а то он может. Если это случится, то начнется сначала закрашивание области атрибутов, а потом вообще и данные попортить может.
Как можем заметить, распределена память очень неравномерно. Введем обозначения:
X[0..4] = X & 31
Y[0..2] = Y & 7
Y[3..5] = (Y>>3) & 7
Y[6..7] = (Y>>6) & 3
Если написать это в Си-стиле, то получится примерно следующее
word get_video_address(byte X, byte Y) {
return (X & 31)
| ((Y & 7) << 8)
| (((Y>>3) & 7) << 5)
| (((Y>>6) & 3) << 11)
| 0x4000;
}
В принципе ничего трудного, просто немного сдвиговой магии.
§ Вычисление курсора текста
Вычисление текстового курсора основывается на том же самом принципе, что и вычисление графического, но с тем отличием, что мы не будем заполнять биты 8-10 для видеоадреса, они будут равны 0. Значимыми битами останутся Y[3..7], то есть получается, что для курсора, который двигается по 8 пикселей, нижние 3 бита Y[0..2] просто опускаются. Потому новый вид будет такой:
| 15 | 14 | 13 | 12 |
11 | 10 | 9 | 8 |
7 | 6 | 5 | 4 |
3 | 2 | 1 | 0 |
| 0 |
1 |
0 |
Y[3..4] |
0 |
Y[0..2] |
X[0..4] |
| 7 | 6 | 5 | 4 |
3 | 2 | 1 | 0 |
7 | 6 | 5 | 4 |
3 | 2 | 1 | 0 |
| H |
L |
Приведу текст программы
word get_text_cursor_address(byte X, byte Y) {
return (X & 31)
| ((Y & 7) << 5)
| (((Y>>3) & 3) << 11)
| 0x4000;
}
Как видно из кода, допустимый диапазон Y=0-31, но, конечно же, он реально равен 0-23,
т.к. превышение диапазона будет грозить артефактами и ошибками в данных.
Пришло время транслировать программу в ассемблер Z80
get_text_cursor_address:
ld a, c
and 0x1F
ld l, a
ld a, b
and 0x07
rrca
rrca
rrca
or l
ld l, a
ld a, b
and 0x18
or 0x40
ld h, a
ret
Ассемблер конечно сложен для понимания, но эта программа не должна быть очень сложной.
§ Код вычисления позиции Y
Этот код почти полностью повторяет выше приведенный, с некоторыми поправками на то, что
Y теперь находится в диапазоне от 0 до 191.
get_graphics_address:
ld a, c
and 0x1F
ld l, a
ld a, b
and 0x07
ld h, a
ld a, b
and 0x38
rlca
rlca
or l
ld l, a
ld a, b
and 0xC0
rrca
rrca
rrca
or h
or 0x40
ld h, a
ret
Здесь в этом коде немного непонятно сразу, но в целом он разбит на 5 частей
- Сначала вычисляется
X & 31
- Потом записывается в регистр H (а точнее по смещению 8 адреса) биты
Y[0..2]
- Далее, рассчитываются и устанавливаются биты
Y[3..5] в точку L[5..7] путем их смещения 2 раза влево
- И после устанавливаются биты
Y[6..7] в точку H[3..4] путем их смещения 3 раза вправо (соответствует битам адреса11..13)
- Записывается бит 14 в адрес
И видеоадрес готов!
Единственное, что я тут не рассказал, это как рассчитать точку. Но это делается очень просто. Для X[0..2], если мы берем попиксельно, ставится соответствие следующая таблица
Бит Hex Bin C X
0 = 0x01 = 00000001 = 1<<0 | 7
1 = 0x02 = 00000010 = 1<<1 | 6
2 = 0x04 = 00000100 = 1<<2 | 5
3 = 0x08 = 00001000 = 1<<3 | 4
4 = 0x10 = 00010000 = 1<<4 | 3
5 = 0x20 = 00100000 = 1<<5 | 2
6 = 0x40 = 01000000 = 1<<6 | 1
7 = 0x80 = 10000000 = 1<<7 | 0
Тут уж кому как удобнее воспринимать, но в общем-то, это довольно просто.
В данной таблице, при установке бита 0, будет отображен самый правый пиксель (координата X = 7), при установке бита 7 – самый левый (X = 0). То есть, чтобы вывести пиксель, считаем слева направо – от 7 до 0, в таблице это видно визуально.