TSconf: INTs
TSconf имеет расширенную систему прерываний, которые могут быть вызваны при наличии таких состояний как: приход луча в заданную позицию экрана, приход луча в начало строки отображение линии на экране, завершение передачи данных.
Дао говорит: Система имеет три типа маскируемого прерывания, которые могут быть вызваны по адресу, который имеет старшим байтом — адрес в регистре I, а младшим — свой тип:
Обработку этих прерываний можно переключать портом INTMask (#2Aaf), изменяя состояние битов:
- #FF — кадровый (Frame)
- #FD — строчный (Line)
- #FB — DMA.
0 — Frame, 1 — Line, 2 — DMA, что приводит к on/off вызову обработчиков. Состояние битов: 0 — запрещен / 1 — разрешен.
В случае прихода нескольких событий одновременно, сначала обработается прерывание с меньшим номером.
Каким образом мы можем управлять данными возможностями? Рассмотрим по порядку:
Frame
Кадровое прерывание в стандартном спектруме приходит в самом начале отрисовки экрана. Для TSconf этот параметр можно задать с помощью трёх портов:
- VSINT: старший и младший байты этих двух портов (VSINTH — #24af, VSINTL — #23af) позволяют задавать вертикальную позицию луча, что позволяет нам указать всю область строк экрана: 0 — 319;
- HSINT: порт, указывающий горизонтальную позицию луча (позицию в строке): 0-223 в тактах 3.5мгц,
Итак, давайте приведём пример, в котором будет установлен обработчик кадрового прерывания #BE:
im2_init
xor a
ld bc,HSINT
out (c),a
ld bc,VSINTL
out (c),a
ld bc,VSINTH
out (c),a
ld a,#be
ld i,a
ld hl,int
ld (#beff),hl
im 2
ret
В данном случае были установлены положения для вызова обработчика кадрового прерывания в 0 (левый верхний угол экрана, крайняя левое положение).
При приходе луча отрисовки экрана в это положение будет произведён переход на код обработчика кадрового прерывания по метке int.
В данном случае, этот обработчик будет вызываться с частотой кадровых импульсов — 50 гц.
А если нам нужно чаще?
Рассмотрим ситуацию, когда нам нужно вызывать кадровый обработчик в начале экрана, и в начале строки под номером 128.
Зачем? Дабы верхняя часть экрана у нас была окрашена красным цветом!
Для этого нам нужно будет указывать, когда нужно будет вызвать обработчик в каждый следующий раз. В обработчике позиции экрана 0 (первая строка экрана) укажем, в каком месте экрана будет вызвано следующее кадровое прерывание:
int
push af
push bc
push hl
ld a,128
ld bc,VSINTL
out (c),a
xor a
ld bc,VSINTH
out (c),a
ld hl,int128
ld (#beff),hl
ld a,#f2
ld bc,BORDER
out (c),a
pop hl
pop bc
pop af
ei
ret
Здесь мы указываем, что следующее кадровое прерывание должно быть вызвано в вертикальной позиции луча на строке 128, и обработчиком будет подпрограмма int128. Устанавливаем красный цвет из стандартной палитры zx spectrum (палитра #0f, цвет 2 — для цвета бордюра просто указываем нужный цвет из всей палитры в 256 ячеек).
При попадания луча в начало строки 128 вызывается подпрограмма int128:
int128
push af
push bc
push hl
xor a
ld bc,VSINTL
out (c),a
ld bc,VSINTH
out (c),a
ld hl,int
ld (#beff),hl
ld a,#f0
ld bc,BORDER
out (c),a
pop hl
pop bc
pop af
ei
ret
в которой мы указали, что следующее кадровое прерывание будет обработано подпрограммой int в позиции луча 0, и устанавливаем цвет бордюра в 0 стандартной палитры.
Отмечаем, что сейчас порт горизонтального положения HSINT не изменяется и имеет значение 0, поэтому вызов обработчиков всегда в начале строки. Предлагаю поиграть с ним Вам самим :)
Замечательно, в нужное место экрана мы уже можем попасть. Но что делать если необходимо некую подпрограмму выполнять КАЖДУЮ строку?
Для этого у нас есть средство в виде строчного прерывания.
Строчные прерывания
Данный вид прерываний будет вызывать свой обработчик в случае, когда луч будет находится в позиции 0 каждой строки экрана (видимой или нет на экране — для системы неважно).
Для его использования необходимо сделать следующее:
- задать адрес обработчика строчных прерываний
- разрешить обработку портом INTMASK (#2Aaf)
Итак, включаем биты порта INTMASK, разрешающие обработку как кадрового, так и строчного прерывания, устанавливаем адрес обработчика:
ld hl,line_proc
ld (#befd),hl
ld bc,INTMASK
ld a,%00000011
out (c),a
Отметим себе, что сейчас разрешены как кадровые, так и строчные прерывания. Первым будет обработан обработчик с меньшим номером — т.е. сначала кадровый, потом строчный.
Меняем задачу. Пусть каждую строку на бордюре будет видна полоса другого цвета.
Отключим фреймовый обработчик, оставим лишь строчный:
ld hl,line_proc
ld (#befd),hl
ld bc,INTMASK
ld a,%00000010
out (c),a
Добавим обработчик строчных прерываний:
line_proc
exx
ex af, af'
lp1 ld a,#ff
inc a
ld (lp1+1),a
or #f0
ld bc,BORDER
out (c),a
ex af, af'
exx
ei
ret
Так как данный обработчик будет вызываться очень часто, стоит минимизировать используемые такты процессора путём переключения в альтернативный набор регистров. Далее — увеличиваем счётчик номера цвета в палитре и отправляем его в систему (порт бордюра #0Faf).
Особенностью данных систем прерываний является то, что использование такой привычной нам команды «останов процессора» HALT приведёт к ожиданию лишь следующего кадрового/строчного прерывания, то есть для первого примера отрисовка полного экрана у нас занимает 2 HALT, а для строчного — 320 HALT :)
DMA
Обработчик завершения передачи будет вызван после завершения передачи.Ваш К.О. ;)
Включаем его обработку и указываем адрес обработчика. Как обычно:
ld hl,dma_proc
ld (#befb),hl
ld bc,INTMASK
ld a,%00000100
out (c),a
Отметим, что в данном случае у нас полностью отключены кадровые и строчные прерывания, и их обработчики вызываться НЕ БУДУТ.
Вопросы и ответы:
? Чо музыка при строчных портится?
! Увы, стандартный плеер pt3 использует стек в работе, и при вызове строчного обработчика данные по адресу в SP заменяются его адресом вызова. И портят данные музы.
Соответственно, вызываем плеер при выключенных строчных (например за пределами экрана, настраиваем кадровые), либо юзаем psg плеер.
… либо играем плейером не использующим стек.
? А как ты вавки играешь при полноэкранных эффектах?
! 1) Хех, строчные прерывания вызываются у нас каждую строку. Соответственно, получаем строчную частотку 15625 герц, и каждую строку выбрасываем в порт ковокса аудио. Наман? И не забываем предыдущий вопрос ^^^
2) Случай, если только 15625-звук, а кадровое прерывание достаточно продолжительное, то разрешить (вложенные) прерывания в обработчике кадрового. Плюс, можно сдвинуть кадровый инт (например) на середину строки, что бы не приходили одновременно со строчным.
Литература:
F.A.Q.
2 комментария
Порядок событий, связанных с приходом сигнала прерывания выглядит так:
0. включение триггера соответствующим источником, при этом на процессор выставляется !INT (при наличие хотя бы одного активного триггера)
1. процессор выполняет последний машцикл инструкции, цепляет инт
2. процессор генерирует цикл подтверждения им2
3. логика контроллера определяет, какой вектор выставить на шину данных (в зависимости от включенных триггеров и их приоритетов)
4. проц цепляет вектор, читает из памяти адрес, летит на ISR
5. контроллер выключает тот триггер, вектор которого обработан; если остались другие триггеры либо успели прийти новые события, инт не убирается
6. процессор заканчивает обработку ISR командами EI:RET, при этом новый инт (если сигнал активен) обработается на последнем машцикле RET, с п.1.
Нюанс первый.
Имеем следующие события:
— установился триггер источника с приоритетом 2,
— прошло 3 такта,
— установился триггер источника с приоритетом 0,
— прошло 2 такта,
— процессор распознал инт и запросил вектор.
Вопрос: какой вектор обработается?
Ответ: источника с приоритетом 0. Несмотря на то, что по тактам событие источника 2 пришло раньше.
Глубокая мораль: приоритеты обрабатываются не по факту прихода, а по факту наличия на момент подтверждения цикла им2.
Нюанс второй.
Если во время активного триггера погасить соответствующий бит в регистре масок, то данный источник не только не будет обработан, но и сбросится триггер. Это нужно для двух вещей: чтоб не оставались «висящие» необработанные инты после выключения битов маски и чтоб можно было рулить разрешениями низших приоритетов из иср-ов высших.