DMA

Содержание

Обзор

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Конфигурация направления, операции и порта A0XXXXXAAAA не должно быть 00
WR1Конфигурация порта A0XXXX100
WR2Конфигурация порта B0XXXX000
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


Этот документ неполный и вскоре будет пересоздан