DMA
Содержание
- Обзор
- Доступ к zxnDMA
- Описание
- Режимы работы
- Программирование zxnDMA
- Регистры zxnDMA
- Рабочая скорость
- DMA и прерывания
- Примеры программирования
- Технические детали (core 3.1.3+)
- Технические детали (core 3.0.5)
Обзор
ZX Spectrum Next DMA (zxnDMA) — это одноканальное DMA-устройство, реализующее подмножество функциональности Z80 DMA. Это подмножество достаточно велико, чтобы быть совместимым с распространенными способами использования аналогичного интерфейса Datagear, доступного для стандартных компьютеров ZX Spectrum и совместимых с ними. Он также добавляет возможность пакетного режима, который позволяет передавать звук с программируемой частотой дискретизации на ЦАП.
Доступ к zxnDMA
zxnDMA отображается на один порт ввода/вывода для чтения/записи 0x6B, который совпадает с портом, используемым Datagear, но, в отличие от Datagear, он также не отображается на второй порт 0x0B, как интерфейс MB-02.
Начиная с ядра 3.1.2, zxnDMA отображается на Datagear DMA Port ($xx6B / 107), а режим Zilog-DMA отображается на MB02 DMA Port ($xx0B / 11).
Описание
Нормальный чип Z80 DMA (Z8410) — это конвейерное устройство, и из-за этого у него есть многочисленные идиосинкразии и требования к порядку выполнения определенных команд. Эти проблемы не дублируются в zxnDMA. Вы можете продолжать программировать zxnDMA так, как если бы это было устройство Z8410 DMA, но его также можно запрограммировать более простым способом.
Одиночный канал чипа zxnDMA состоит из двух портов с именами A и B. Передача может происходить в любом направлении между портами A и B, каждый порт может описывать цель в памяти или IO, и каждый может быть настроен на автоматическое увеличение, автоматическое уменьшение или оставаться фиксированным после передачи байта.
Особенностью zxnDMA является возможность принудительной установки фиксированного времени для каждой передачи байта, чтобы zxnDMA можно было использовать для передачи оцифрованного звука.
Режимы работы
zxnDMA может работать в режиме совместимости с z80-DMA.
УДАЛЕНО в ядре 3.1.2: Режим совместимости с z80-DMA выбирается установкой бита 6 в nextreg 0x06. В этом режиме все передачи включают length+1 байтов, что соответствует поведению чипа z80-DMA. В режиме zxn-DMA длина передачи — это ровно запрограммированное количество байтов. Этот режим в основном предназначен для поддержки существующего программного обеспечения Spectrum, использующего z80-DMA, и для программ cp/m, которые могут иметь опцию z80-DMA.
Начиная с ядра 3.1.2: режим DMA выбирается по номеру порта, Datagear DMA Port ($xx6B / 107) работает в режиме zxnDMA, MB02 DMA Port ($xx0B / 11) работает в режиме Zilog. Бит 6 в Peripheral 2 Register ($06) больше не связан с DMA и будет использоваться для чего-то другого.
zxnDMA также может работать в пакетном или непрерывном режимах.
Непрерывный режим означает, что чип DMA работает до завершения, не позволяя ЦП работать. Когда ЦП запускает DMA, операция DMA завершится до того, как ЦП выполнит свою следующую инструкцию.
Пакетный режим номинально означает, что DMA позволяет ЦП работать, если ни один из портов не готов. Это условие не может возникнуть в чипе zxnDMA, за исключением случаев работы в специальном режиме передачи с фиксированным временем. В этом режиме zxnDMA позволит ЦП работать, пока он ждет истечения фиксированного времени между переданными байтами.
Обратите внимание, что режима передачи байтов, как в Z80 DMA, нет.
Программирование zxnDMA
Как и чип Z80 DMA, zxnDMA имеет семь регистров записи с именами WR0-WR6, которые управляют устройством. Каждый регистр WR0-WR6 может иметь ноль или более параметров, связанных с ним.
При первой записи в порт zxnDMA записанное значение сравнивается с битовой маской, чтобы определить, какой из WR0-WR6 является целью. Остальные биты в записанном значении могут содержать данные, а также список связанных битов параметров. Биты параметров определяют, ожидаются ли дальнейшие записи для доставки значений параметров. Если установлено несколько битов параметров, ожидаемый порядок записываемых значений параметров определяется положением бита параметра справа налево (от бита 0 до бита 7). После записи всех параметров zxnDMA снова ожидает обычную запись в регистр, выбирающую WR0-WR6.
В таблице ниже описаны регистры и битовая маска, необходимая для их выбора на zxnDMA.
Группа регистров | Описание функции регистра | Битовая маска | Примечания |
---|---|---|---|
WR0 | Конфигурация направления, операции и порта A | 0XXXXXAA | AA не должно быть 00 |
WR1 | Конфигурация порта A | 0XXXX100 | |
WR2 | Конфигурация порта B | 0XXXX000 | |
WR3 | Активация | 1XXXXX00 | Лучше всего использовать WR6 |
WR4 | Конфигурация порта B, времени и прерываний | 1XXXXX01 | |
WR5 | Конфигурация готовности и останова | 10XXX010 | |
WR6 | Регистр команд | 1XXXXX11 |
Регистры zxnDMA
Они описаны ниже в соответствии с соглашением, используемым Zilog для своего чипа DMA:
WR0 – Группа регистров записи 0
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
0 | | | | | | |
| | | | | 0 0 Не использовать
| | | | | 0 1 Передача (предпочтительно для совместимости с Z80 DMA)
| | | | | 1 0 Не использовать (ведет себя как Transfer, Search на Z80 DMA)
| | | | |
| | | | | 1 1 Не использовать (ведет себя как Transfer, Search/Transfer на Z80 DMA)
| | | | |
| | | | 0 = Порт B -> Порт A (направление передачи байтов)
| | | | 1 = Порт A -> Порт B
| | | V
D7 D6 D5 D4 D3 D2 D1 D0 НАЧАЛЬНЫЙ АДРЕС ПОРТА A (МЛАДШИЙ БАЙТ)
| | V
D7 D6 D5 D4 D3 D2 D1 D0 НАЧАЛЬНЫЙ АДРЕС ПОРТА A (СТАРШИЙ БАЙТ)
| V
D7 D6 D5 D4 D3 D2 D1 D0 ДЛИНА БЛОКА (МЛАДШИЙ БАЙТ)
V
D7 D6 D5 D4 D3 D2 D1 D0 ДЛИНА БЛОКА (СТАРШИЙ БАЙТ)
Несколько регистров доступны из WR0. Первая запись в WR0 — это запись в базовый байт регистра. Биты D6:D3 при необходимости устанавливаются, чтобы указать, что связанные регистры в этой группе будут записаны следующими. Порядок поступления записей идет от D3 к D6 (справа налево). Например, если установлены биты D6 и D3, следующие две записи будут направлены в НАЧАЛЬНЫЙ АДРЕС ПОРТА A (МЛАДШИЙ), за которым следует HIGH ДЛИНЫ БЛОКА.
WR1 – Группа регистров записи 1
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
0 | | | | 1 0 0
| | | |
| | | 0 = Порт A — память
| | | 1 = Порт A — IO
| | |
| 0 0 = Адрес порта A уменьшается
| 0 1 = Адрес порта A увеличивается
| 1 0 = Адрес порта A фиксирован
| 1 1 = Адрес порта A фиксирован
|
V
D7 D6 D5 D4 D3 D2 D1 D0 БАЙТ ПЕРЕМЕННОЙ ХРОНОМЕТРИРОВАНИЯ ПОРТА A
0 0 0 0 0 0 | |
0 0 = Длина цикла = 4
0 1 = Длина цикла = 3
1 0 = Длина цикла = 2
1 1 = Не использовать
Длина цикла — это количество циклов, используемых в операции чтения или записи. Первый цикл утверждает сигналы, а последний цикл отпускает их. Полуцикличного тайминга для управляющих сигналов нет.
WR2 – Группа регистров записи 2
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
0 | | | | 0 0 0
| | | |
| | | 0 = Порт B — память
| | | 1 = Порт B — IO
| | |
| 0 0 = Адрес порта B уменьшается
| 0 1 = Адрес порта B увеличивается
| 1 0 = Адрес порта B фиксирован
| 1 1 = Адрес порта B фиксирован
|
V
D7 D6 D5 D4 D3 D2 D1 D0 БАЙТ ПЕРЕМЕННОЙ ХРОНОМЕТРИРОВАНИЯ ПОРТА B
0 0 | 0 0 0 | |
| 0 0 = Длина цикла = 4
| 0 1 = Длина цикла = 3
| 1 0 = Длина цикла = 2
| 1 1 = Не использовать
|
V
D7 D6 D5 D4 D3 D2 D1 D0 ПРЕДДЕЛИТЕЛЬ ZXN (ПЕРЕДАЧА С ФИКСИРОВАННЫМ ВРЕМЕНЕМ)
ZXN PRESCALAR — это особенность реализации zxnDMA. Если он не равен нулю, после каждой переданной байты будет вставлена задержка, так что общее время, необходимое для каждой передачи, будет определяться предварительным делителем. Это работает как в непрерывном режиме, так и в пакетном режиме. Если DMA работает в пакетном режиме, DMA уступит любое время ожидания ЦП, чтобы ЦП мог работать, пока DMA простаивает.
Скорость передачи определяется формулой «Frate = 875 кГц / prescalar» или, перефразируя, «prescalar = 875 кГц / Frate». Формула сформулирована в терминах частоты дискретизации (Frate), но Frate можно инвертировать, чтобы вместо этого установить время передачи для каждого байта. Константа 875 кГц является номинальным значением, предполагающим тактовую частоту системы 28 МГц; тактовая частота системы фактически отличается от этого в зависимости от выбранного пользователем выбора времени видео (HDMI, VGA0-6), поэтому для полной точности константу следует пропорционально рассчитать в соответствии с документацией для nextreg 0x11.
В настройке DMA audio выбор частоты дискретизации 16 кГц будет означать установку значения предварительного делителя на 55. Этот период дискретизации постоянен при изменениях скорости ЦП.
WR3 – Группа регистров записи 3
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
1 | 0 0 0 0 0 0
|
1 = Включить DMA
Z80 DMA определяет больше полей, но они игнорируются zxnDMA. Два других регистра, определенных Z80 DMA в этой группе на D4 и D3, реализованы zxnDMA, но ничего не делают.
Предпочтительно запускать DMA, записывая команду «Enable DMA» в WR6.
WR4 – Группа регистров записи 4
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
1 | | 0 | | 0 1
| | | |
0 0 = Не использовать (ведет себя как непрерывный режим, байтовый режим на Z80 DMA)
0 1 = Непрерывный режим
1 0 = Пакетный режим
1 1 = Не использовать
| |
| V
D7 D6 D5 D4 D3 D2 D1 D0 НАЧАЛЬНЫЙ АДРЕС ПОРТА B (МЛАДШИЙ БАЙТ)
|
V
D7 D6 D5 D4 D3 D2 D1 D0 НАЧАЛЬНЫЙ АДРЕС ПОРТА B (СТАРШИЙ БАЙТ)
Z80 DMA определяет еще три регистра в этой группе через D4, которые определяют поведение прерываний. Прерывания и генерация импульсов не реализованы в zxnDMA, и эти регистры также недоступны для записи.
DMA
WR5 – Группа регистров записи 5
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
1 0 | | 0 0 1 0
| |
| 0 = только /ce
| 1 = /ce и /wait мультиплексированы
|
0 = Остановить по окончании блока
1 = Автоматический перезапуск по окончании блока
Режим /ce & /wait реализован в zxnDMA, но в настоящее время не используется. В этом режиме внешнее устройство использует вывод /ce DMA для вставки состояний ожидания во время передачи DMA.
Функция автоматического перезапуска заставляет DMA автоматически перезагружать адреса источника и назначения и сбрасывать свой счетчик байтов на ноль, чтобы повторить последнюю передачу при завершении предыдущей.
WR6 – Регистр команд
D7 D6 D5 D4 D3 D2 D1 D0 БАЗОВЫЙ БАЙТ РЕГИСТРА
1 ? ? ? ? ? 1 1
| | | | |
1 0 0 0 0 = 0xC3 = Сброс
1 0 0 0 1 = 0xC7 = Сброс времени порта A
1 0 0 1 0 = 0xCB = Сброс времени порта B
0 1 1 0 0 = 0xB3 = Принудительная готовность (не имеет значения для zxnDMA)
0 1 1 1 1 = 0xBF = Чтение байта состояния
0 0 0 1 0 = 0x8B = Повторная инициализация байта состояния
0 1 0 0 1 = 0xA7 = Инициализация последовательности чтения
1 0 0 1 1 = 0xCF = Загрузка
1 0 1 0 0 = 0xD3 = Продолжить
0 0 0 0 1 = 0x87 = Включить DMA
0 0 0 0 0 = 0x83 = Выключить DMA
+-- 0 1 1 1 0 = 0xBB = Маска чтения следует
|
D7 D6 D5 D4 D3 D2 D1 D0 МАСКА ЧТЕНИЯ
0 | | | | | | |
| | | | | | V
D7 D6 D5 D4 D3 D2 D1 D0 Байт состояния
| | | | | |
| | | | | V
D7 D6 D5 D4 D3 D2 D1 D0 Счетчик байтов (младший) ("Старший" с ядром 3.0.5 = ошибка в ядре)
| | | | |
| | | | V
D7 D6 D5 D4 D3 D2 D1 D0 Счетчик байтов (старший) ("Младший" с ядром 3.0.5 = ошибка в ядре)
| | | |
| | | V
D7 D6 D5 D4 D3 D2 D1 D0 Младший адрес порта A
| | |
| | V
D7 D6 D5 D4 D3 D2 D1 D0 Старший адрес порта A
| |
| V
D7 D6 D5 D4 D3 D2 D1 D0 Младший адрес порта B
|
V
D7 D6 D5 D4 D3 D2 D1 D0 Старший адрес порта B
Нереализованные команды Z80 DMA игнорируются.
Перед запуском DMA необходимо выдать команду LOAD, чтобы скопировать адреса порта A и порта B во внутренние указатели DMA. Затем выдается команда «Enable DMA» для запуска DMA.
Команда «Continue» сбрасывает счетчик байтов DMA, так что следующая команда «Enable DMA» позволяет DMA повторить последнюю передачу, но с использованием текущих указателей внутреннего адреса. Т. е. он продолжает с того места, где остановилась последняя операция копирования.
Регистры можно читать с помощью операции чтения ввода-вывода из порта DMA после установки маски чтения. (При включении питания маска чтения устанавливается на 0x7f). Значения регистров — это текущие значения внутреннего счетчика DMA. Таким образом, «Порт адресов A (младший)» — это младшие 8 бит адреса следующей передачи порта A. После достижения конца маски чтения дальнейшие чтения закольцовываются к первому.
Формат байта состояния DMA выглядит следующим образом:
00E1101T
E устанавливается в 0, если общая длина блока была передана хотя бы один раз.
T устанавливается в 1, если был передан хотя бы один байт.
Рабочая скорость
zxnDMA работает с той же скоростью, что и ЦП, то есть 3,5 МГц, 7 МГц, 14 МГц или 28 МГц. Это спорная тактовая частота, которая изменяется ULA и автоматическим замедлением Layer2 (которое происходило только в ядрах Next 1 и 2, ограничение было снято в ядре 3.0).
Автоматическое замедление (до ядра 3.0) происходит без вмешательства пользователя, если скорость превышает 7 МГц и генерируется активный дисплей Layer2 (более высокая скорость работы возобновляется, когда активный дисплей Layer2 не генерируется). Программистам НЕ нужно учитывать различия в скорости в отношении передач DMA, поскольку это происходит автоматически.
Из-за этого длину цикла для портов A и B можно установить на минимальные значения без каких-либо негативных последствий. Длина цикла, указанная для портов A и B, предназначена для выборочного замедления циклов чтения или записи для оборудования, которое не может работать на полной скорости DMA.
DMA и прерывания
zxnDMA в настоящее время не может генерировать прерывания.
Другая сторона этого заключается в том, что пока DMA управляет шиной, Z80 не может отвечать на прерывания. На Z80 прерывание NMI запускается по фронту сигнала, поэтому, если происходит NMI, факт его возникновения сохраняется внутри Z80, чтобы он ответил, когда проснется. С другой стороны, маскируемые прерывания запускаются по уровню сигнала. То есть Z80 должен быть активным, чтобы регулярно опрашивать линию /INT, чтобы определить, происходит ли маскируемое прерывание. На Spectrum и ZX Next ULA (и линейное прерывание) утверждаются только в течение фиксированного времени ~30 циклов на частоте 3,5 МГц. Если DMA выполняет передачу во время утверждения прерывания, ЦП не сможет этого увидеть и, скорее всего, пропустит прерывание. В пакетном режиме с достаточно большим значением предварительного делителя ЦП никогда не пропустит эти прерывания, хотя это может измениться, если будет реализовано несколько каналов.
Примеры программирования
Простой способ запрограммировать DMA — пройтись по списку регистров WR0-WR5, отправив желаемые настройки каждому. Затем запустите DMA, отправив команду LOAD, за которой следует команда ENABLE_DMA в WR6. После того, как вы лучше познакомитесь с DMA, вы обнаружите, что объем отправляемой информации можно сократить до того, что изменяется между передачами.
Ассемблер
Короткий пример программы для DMA памяти на экран, затем DMA спрайта изображения из памяти в спрайт ОЗУ, а затем отображения этого спрайта, прокручивающегося по экрану.
;------------------------------------------------------------------------------
; sjasmplus extra options to enable Z80N, stricter syntax and Next device
opt --zxnext --syntax=abf : device zxspectrumnext
;------------------------------------------------------------------------------
; DEFINE testing ; uncomment to produce NEX file (instead of DOT)
;------------------------------------------------------------------------------
; DMA (Register 6)
;
;------------------------------------------------------------------------------
;zxnDMA programming example
;------------------------------------------------------------------------------
;(c) Jim Bagley
;------------------------------------------------------------------------------
DMA_RESET equ $c3
DMA_RESET_PORT_A_TIMING equ $c7
DMA_RESET_PORT_B_TIMING equ $cb
DMA_LOAD equ $cf; %11001111
DMA_CONTINUE equ $d3
DMA_DISABLE_INTERUPTS equ $af
DMA_ENABLE_INTERUPTS equ $ab
DMA_RESET_DISABLE_INTERUPTS equ $a3
DMA_ENABLE_AFTER_RETI equ $b7
DMA_READ_STATUS_BYTE equ $bf
DMA_REINIT_STATUS_BYTE equ $8b
DMA_START_READ_SEQUENCE equ $a7
DMA_FORCE_READY equ $b3
DMA_DISABLE equ $83
DMA_ENABLE equ $87
DMA_WRITE_REGISTER_COMMAND equ $bb
DMA_BURST equ %11001101
DMA_CONTINUOUS equ %10101101
ZXN_DMA_PORT equ $6b
SPRITE_STATUS_SLOT_SELECT equ $303B
SPRITE_IMAGE_PORT equ $5b
SPRITE_INFO_PORT equ $57
;------------------------------------------------------------------------------
IFDEF testing
org $5800
block 32*24, $38 ; default ULA attributes
org $6000
ELSE
org $2000
ENDIF
start
ld hl,$0000
ld de,$4000
ld bc,$800
call TransferDMA ; copy some random data to the screen pointing
; to ROM for now, for the purpose of showing
; how to do a DMA copy.
ld a,0 ; sprite image number we want to update
ld bc,SPRITE_STATUS_SLOT_SELECT
out (c),a ; set the sprite image number
ld bc,1*256 ; number to transfer (1)
ld hl,testsprite ; from
call TransferDMASprite ; transfer to sprite ram
nextreg 21,1 ; turn sprite on. for more info on this check
; out https://www.specnext.com/tbblue-io-port-system/
ld de,0
ld (xpos),de ; set initial X position ( doesn't need it for
; this demo, but if you run the .loop again it
; will continue from where it was
ld a,$20
ld (ypos),a ; set initial Y position
.loop
ld a,0 ; sprite number we want to position
ld bc,SPRITE_STATUS_SLOT_SELECT
out (c),a
ld de,(xpos)
ld hl,(ypos) ; ignores H so doing this rather than
; ld a,(ypos):ld l,a
ld bc,(image) ; not flipped or palette shifted
call SetSprite
halt
ld de,(xpos)
inc de
ld (xpos),de
ld a,d
cp $01
jr nz,.loop ; if high byte of xpos is not 1 (right of
; screen )
ld a,e
cp $20+1
jr nz,.loop ; if low byte is not $21 just off the right of
; the screen, $20 is off screen but as the
; INC DE is just above and not updated sprite
; after it, it needs to be $21
xor a
ret ; return back to basic with OK
xpos dw 0 ; x position
ypos db 0 ; y position
; these next two BITS and IMAGE are swapped
; as bits needs to go into B register
image db 0+$80 ; use image 0 (for the image we transfered)
; +$80 to set the sprite to active
bits db 0 ; not flipped or palette shifted
c1 = %11100000
c2 = %11000000
c3 = %10100000
c4 = %10000000
c5 = %01100000
c6 = %01000000
c7 = %00100000
c8 = %00000000
testsprite
db c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1
db c1,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c1
db c1,c2,c3,c3,c3,c3,c3,c3,c3,c3,c3,c3,c3,c3,c2,c1
db c1,c2,c3,c4,c4,c4,c4,c4,c4,c4,c4,c4,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c5,c5,c5,c5,c5,c5,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c6,c6,c6,c6,c6,c6,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c6,c7,c7,c7,c7,c6,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c6,c7,c8,c8,c7,c6,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c6,c7,c8,c8,c7,c6,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c6,c7,c7,c7,c7,c6,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c6,c6,c6,c6,c6,c6,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c5,c5,c5,c5,c5,c5,c5,c5,c4,c3,c2,c1
db c1,c2,c3,c4,c4,c4,c4,c4,c4,c4,c4,c4,c4,c3,c2,c1
db c1,c2,c3,c3,c3,c3,c3,c3,c3,c3,c3,c3,c3,c3,c2,c1
db c1,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c2,c1
db c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1,c1
;-------------------------------------------------
; de = X
; l = Y
; b = bits
; c = sprite image
SetSprite
push bc
ld bc,SPRITE_INFO_PORT
out (c),e ; Xpos
out (c),l ; Ypos
pop hl
ld a,d
and 1
or h
out (c),a
ld a,l:or $80
out (c),a ; image
ret
;--------------------------------
; hl = source
; de = destination
; bc = length
;--------------------------------
TransferDMA
di
ld (DMASource),hl
ld (DMADest),de
ld (DMALength),bc
ld hl,DMACode
ld b,DMACode_Len
ld c,ZXN_DMA_PORT
otir
ei
ret
DMACode db DMA_DISABLE
db %01111101 ; R0-Transfer mode, A -> B, write adress
; + block length
DMASource dw 0 ; R0-Port A, Start address
; (source address)
DMALength dw 0 ; R0
Этот документ неполный и вскоре будет пересоздан