§ Модель видеопамяти
В этом главе уже начинается рассказ о том, как запрограммировать великий Спектрум (хвала ему), чтобы он мог находить видеоадреса. Я рассмотрю 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[0..4] = X & 31
- Y[0..2] = Y & 7
- Y[3..5] = (Y>>3) & 7
- Y[6..7] = (Y>>6) & 3
1word get_video_address(byte X, byte Y) { 2 3 return (X & 31) // Помещение X в биты 0..4 4 | ((Y & 7) << 8) // Y[0..2] в биты 8..10 5 | (((Y>>3) & 7) << 5) // Y[3..5] в биты 5..7 6 | (((Y>>6) & 3) << 11) // Y[6..7] в биты 11..12 7 | 0x4000; 8}В принципе ничего трудного, просто немного сдвиговой магии.
§ Вычисление курсора текста
Вычисление текстового курсора основывается на том же самом принципе, что и вычисление графического, но с тем отличием, что мы не будем заполнять биты 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 |
1word get_text_cursor_address(byte X, byte Y) { 2 3 return (X & 31) // Помещение X в биты 0..4 4 | ((Y & 7) << 5) // Y[0..2] в биты 5..7 5 | (((Y>>3) & 3) << 11) // Y[3..4] в биты 11..12 6 | 0x4000; 7}Как видно из кода, допустимый диапазон Y=0-31, но, конечно же, он реально равен 0-23,
т.к. превышение диапазона будет грозить артефактами и ошибками в данных.
Пришло время транслировать программу в ассемблер Z80
1; Вход: B(Y=0..23), C(X=0..31) 2; Выход: HL(адрес) 3get_text_cursor_address: 4 5 ld a, c 6 and 0x1F 7 ld l, a ; L = X & 31 8 ld a, b 9 and 0x07 ; Нужно ограничить 3 битами 10 rrca ; Легче дойти с [0..2] до позиции [5..7] 11 rrca ; Если вращать направо 12 rrca ; ... три раза 13 or l ; Объединив с 0..4 уже готовыми ранее 14 ld l, a ; Загрузить новый результат в L 15 ld a, b ; Т.к. Y[3..5] уже на месте 16 and 0x18 ; Его двигать даже не надо 17 or 0x40 ; Ставим видеоадрес $4000 18 ld h, a ; И загружаем результат 19 retАссемблер конечно сложен для понимания, но эта программа не должна быть очень сложной.
§ Код вычисления позиции Y
Этот код почти полностью повторяет выше приведенный, с некоторыми поправками на то, чтоY теперь находится в диапазоне от 0 до 191.
1; Вход: B(Y=0..191), C(X=0..31) 2; Выход: HL(адрес) 3get_graphics_address: 4 5 ld a, c 6 and 0x1F ; L[0..4] = X >> 3 7 ld l, a ; Y[0..2] 8 ld a, b 9 and 0x07 10 ld h, a ; Установка => H[0..2] 11 ld a, b ; Y[3..5] 12 and 0x38 13 rlca 14 rlca 15 or l 16 ld l, a ; Ставится в L[5..7] 17 ld a, b 18 and 0xC0 19 rrca 20 rrca 21 rrca 22 or h 23 or 0x40 ; H устанавливается на видеопамять 24 ld h, a ; Y[6..7] ставится в H[3..4] 25 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, в таблице это видно визуально.