§ Теория

Безусловно, любая программа должна информировать пользователя о том, что она делает. Ну почти любая, конечно, но все же, большинство из них. Первым делом, надо бы научиться выводить символы и строки на экран. Сегодня я попытаюсь рассказать об этом.
Как известно, в спектруме нет текстового режима, есть лишь только графический, и потому, чтобы вывести что-то на экран, это придется нарисовать разными методами. Рассмотрю лишь самый простой, который работает с символами размером 8x8 и полем 32x24.
Первое, что требуется, так это информация о записанных символах где-нибудь в памяти. Если 1 символ кодируется 8 байтами, а как известно, 1 байт равен 8 пикселям, то для хранения 256 символов потребуется 2048 байт (2кб) памяти. А памяти в спектруме не так много, к тому же, я буду полностью опускать все символы, имеющие код от 128 до 255 - это обычно там находится русская кодировка. Плюс к этому, еще буду пропускать символы от 0 до 31 - там находятся спецсимволы. Все ради экономии места. Итак, в распоряжении остаются символы от 32 до 127 включительно, а это 96 символов, которые займут в памяти 768 байт - уже много меньше, чем 2кб, хотя можно и еще ужать до 384 байта, если использовать символы 4x8, но пока я это делать тут не буду, может потом сделаю в другой главе.
Поскольку размер ROM состоит из 16кб, то логично бы хранить таблицу символов в последних 768 байтах, как это делается в ROM Бейсике от официального Синклера, так сказать. А они там находятся по адресу 0x3D00 до 0x3FFF включительно.

§ Вычисление адреса данных для показа

Как вычислить позицию данных, откуда будем считывать символы? Просто, очень просто:
1word get_symbol_address(byte sym) {
2    return 0x3D00 | ((sym - 0x20) << 8);
3}
На языке ассемблера Z80 это будет выглядеть так. Все просто:
1; Вход:  A (символ)
2; Выход: HL
3get_symbol_address:
4
5        sub     0x20        ; A = Sym - 0x20
6        ld      h, 0        ; HL = A
7        ld      l, a
8        add     hl, hl
9        add     hl, hl
10        add     hl, hl      ; HL = A << 3
11        ld      a, h
12        add     0x3d
13        ld      h, a        ; HL += 0x3D00
14        ret

§ Вывод символа на экран

Для вывода одного символа на экран нам потребуется использовать 2 процедуры, которые ранее написаны - это вычисление источника символа, и вычисление адреса, куда писать будем. Ну а дальше просто дело техники.
1; Вход: B(y=0..23), C(x=0..31) A(символ)
2print_char:
3
4        push    bc
5        push    de
6        push    hl
7        call    get_symbol_address      ; HL=Адрес символа в памяти
8        ex      de, hl                  ; DE теперь тут
9        call    get_text_cursor_address ; HL=Адрес видеопамяти
10        ld      b, 8        ; Повторить 8 раз
11.pc1:   ld      a, (de)     ; Прочитать 8 бит
12        ld      (hl), a     ; Записать 8 бит
13        inc     h           ; Y = Y + 1 согласно модели памяти
14        inc     de          ; К следующему байту
15        djnz    .pc1        ; Рисовать 8 строк
16        pop     hl
17        pop     de
18        pop     bc
19        ret

§ Процедура очистки экрана

Я подумал, что не лишним будет привести сюда процедуру, которая очищает экран в атрибут A, в том числе еще и устанавливая BORDER под цвет БУМАГИ.
1; Вход: A - атрибут очистки
2cls:
3        ld      bc, 0x02FF
4        ld      hl, 0x5800  ; Отсюда копировать
5        ld      de, 0x5801  ; Сюда
6        ld      (hl), a     ; Байт инициализации
7        rrca
8        rrca
9        rrca
10        out     (254), a    ; Цвет бордера
11        ldir                ; Копировать из (HL) -> (DE), HL++, DE++
12        xor     a
13        ld      hl, 0x4000
14        ld      de, 0x4001
15        ld      bc, 0x17FF
16        ldir                ; Очистить графическую область
17        ret