TSconf: INTs


TSconf имеет расширенную систему прерываний, которые могут быть вызваны при наличии таких состояний как: приход луча в заданную позицию экрана, приход луча в начало строки отображение линии на экране, завершение передачи данных.

Дао говорит: Система имеет три типа маскируемого прерывания, которые могут быть вызваны по адресу, который имеет старшим байтом — адрес в регистре I, а младшим — свой тип:
  1. #FF — кадровый (Frame)
  2. #FD — строчный (Line)
  3. #FB — DMA.
Обработку этих прерываний можно переключать портом INTMask (#2Aaf), изменяя состояние битов:
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 комментария

avatar
о, оказывается, есть one-shot на нужной строке :)
avatar
Добавлю два нюанса.

Порядок событий, связанных с приходом сигнала прерывания выглядит так:
0. включение триггера соответствующим источником, при этом на процессор выставляется !INT (при наличие хотя бы одного активного триггера)
1. процессор выполняет последний машцикл инструкции, цепляет инт
2. процессор генерирует цикл подтверждения им2
3. логика контроллера определяет, какой вектор выставить на шину данных (в зависимости от включенных триггеров и их приоритетов)
4. проц цепляет вектор, читает из памяти адрес, летит на ISR
5. контроллер выключает тот триггер, вектор которого обработан; если остались другие триггеры либо успели прийти новые события, инт не убирается
6. процессор заканчивает обработку ISR командами EI:RET, при этом новый инт (если сигнал активен) обработается на последнем машцикле RET, с п.1.

Нюанс первый.
Имеем следующие события:
— установился триггер источника с приоритетом 2,
— прошло 3 такта,
— установился триггер источника с приоритетом 0,
— прошло 2 такта,
— процессор распознал инт и запросил вектор.
Вопрос: какой вектор обработается?
Ответ: источника с приоритетом 0. Несмотря на то, что по тактам событие источника 2 пришло раньше.
Глубокая мораль: приоритеты обрабатываются не по факту прихода, а по факту наличия на момент подтверждения цикла им2.

Нюанс второй.
Если во время активного триггера погасить соответствующий бит в регистре масок, то данный источник не только не будет обработан, но и сбросится триггер. Это нужно для двух вещей: чтоб не оставались «висящие» необработанные инты после выключения битов маски и чтоб можно было рулить разрешениями низших приоритетов из иср-ов высших.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.