masterspammer (masterspammer) wrote,
masterspammer
masterspammer

Category:

"Матрица" для Z80

Дано - компьютер на Z80, память которого разделена на банки, переключаемые записью в порты (и это не 128-й "Спектрум", там переключаемый банк только один). Хочется реализовать многозадачность, вынести как минимум "гипервизор" из адресного пространства прикладных программ, полностью защитить его от них и их - друг от друга, закрыть им доступ к "железу" и т.д. Пример типового применение - запуск нескольких CP/M, каждой - в своём окошке на графическом экране, при этом, даже программа, стирающая памяти или пишущая в порты мусор ничего не испортит.

В общем - то, чего на Спектруме, Радио-86РК и даже на PC под DOS не хватало совсем.

Далее будут

использоваться термины - системный и обычный режимы, это аппаратно один бит - признак "системности" - выход триггера. Кроме описанного ниже - важно, что системный режим принудительно включает специальный "нулевой" банк ОЗУ для начала (первых 8 или 16 килобайт) памяти, а порты переключения страниц недоступны для записи в обычном режиме. Без владения началом ОЗУ "гипервизором" невозможна обработка им программных прерываний RST XX и немаскируемого прерывания. Реализация с переключением единственного банка в начала ОЗУ возможна, но тогда переключение задач будет требовать замены содержимого всего остального ОЗУ и будет неприлично медленной, а размер "гипервизора" будет ограничен размером переключаемого окна.

Флаг перехода в системный режим - вход другого триггера, влекущий установку признака системности и сброс самого себя после двух записей процессором в память (после сохранения адреса возврата в стэк).
Возврат в обычный режим - немедленный (сразу же после окончания цикла M1), адрес возврата из стэка читается уже после переключения банков.
Кроме детектора команд флаг перехода в системный режим устанавливается при поступлении аппаратных прерываний. При немаскируемом прерывании факт обработки необходимо проверять по сочетанию !M1 и !IORQ, для немаскируемого достаточно наличия сигнала собственно прерывания, но следует уделить внимание моменту начала обработки.


При входе в системный режим "гипервизор" сохраняет прошлое значение стэка и другие регистры (контекст программы), делает свои действия и либо восстанавливает контекст и возвращается по RETI/RETN, либо переключает банки ОЗУ и восстанавливает __другой__ контекст, осуществляя переключение задач. Возможно и более интересное поведение, но пока оно не обдумывалось.

Цель - отследить выполнение "особых" команд для следующих целей:
1. выполнения посторонних действий (перехода из/в сис. режим и т.д.)
2. запрета выполнения команды путём замены на другую (есть шанс, что достаточно замены на 00).

Реализация - автомат, обрабатывающий байты на шине данных после буфера (со стороны памяти) при наступлении сигнала !M1.

Поскольку по внешним сигналам многобайтную команду от нескольких команд (без операндом между ними) подряд отличить невозможно, то необходимо отслеживать команды по их кодам и сбрасывать автомат по окончании команды. Автомат в некотором смысле отражает эволюцию системы команд i8080 -> Z80 :-)

Взаимоисключающие состояния автомата:
0. нейтральное - ожидание RST XX и EI/DI
1. был байт (префикс) CB - обработка дальнейших байт блокирована (по факту будет ровно один байт команды)
2. был байт (префикс) ED - ожидание команд режимов прерываний (по факту будет ещё ровно один байт)

Дополнительные флаги, устанавливаемые из нейтрального состояния:
a. был префикс DD
б. был префикс FD

Обработка в нейтральном состоянии:
* При поступлении 11 11X 011  (F3/FB) - команда DI/EI соответственно в НЕ системном режиме поступающий байт заменяется на 00 (NOP), следом сброс.
* При поступлении 11 XXX 111 - команда  RST N  в НЕ системном режиме устанавливается флаг перехода в системный режим (см. примечание ниже про префиксы DD/FD), следом сброс.
* При поступлении 11 101 001 (E9) - команда JP [HL] - в системном режиме при наличии двух префиксов - И DD И FD - выход из системного режима - для вызова несистемного кода впервые - как для запуска новых программ, так и для callbacks, выход из которых по RST N, не по RET; следом сброс.
* При поступлении 11 001 011  CB - переход в состояние 1. "был CB" - TODO - возможно, сброс при наличии любого из префиксов DD/FD - похоже, что всё после DD CB (FD CB) считается операндом и линия !M1 неактивна;
* При поступлении 11 101 101  ED - переход в состояние 2. "был ED"
* При поступлении 11 011 101  DD - установка флага a. "был префикс DD", состояние остаётся нейтральным.
* При поступлении 11 111 101  FD - установка флага б. "был префикс FD", состояние остаётся нейтральным.

обозначим b0 & b6 & b7 &!CB &!ED = t0

* При поступлении 11 11X 011 DI/EI - t0 & b1 & !b2 & b4 & b5 & !sys = nop, b3 = ei # DI/EI
* При поступлении 11 XXX 111 RST   - t0 & b1 & b2 & !sys & PFX? = tosys # RST
подадим b1, b2, b4 и b5 - на дешифратор, включать его будем по  t0 & b3;
* При поступлении __ 10_ 00_ (!0x08) JP[HL] (E9) - [HL] - & sys & & DD & FD -> outsys
* При поступлении __ 00_ 01_ (!0x01) CB - переход в состояние 1. "был CB" (возможно, просто сброс при наличии любого из префиксов DD/FD - похоже, что всё после DD CB (FD CB) считается операндом и линия !M1 неактивна);
* При поступлении __ 10_ 10_ (!0x0a) ED - переход в состояние 2. "был ED"
* При поступлении __ 01_ 10_ (!0x06) DD - установка флага a. "был префикс DD"
* При поступлении __ 11_ 10_ (!0x0е) FD - установка флага б. "был префикс FD"
при !0x01 & !0x0a & !0x06 & !0x0е == 1 не сбрасывается автомат (многобайтная команда)

Обработка в состоянии 1. "был CB":
// тут нужно смотреть - активен ли вообще сигнал !M1 при чтении команды после префикса CB в зависимости от других префиксов
* ничего не делать, следом сброс.


Обработка в состоянии 2. "был ED":
* При поступлении 01 XXX 101 (команды RETI/RETN, включая недокументированные) в системном режиме - выход из системного режима (в обычном режиме - возможно заменить на RET ничего не делать), следом сброс.
* При поступлении 01 XXX 110 (команды переключения прерываний, включая недокументированные) в НЕ системном режиме поступающий байт заменяется на 00 - см. примечание про регистры I,R и прерывания, следом сброс.
* При поступлении 01 000 111 (47) - команда LD I, A
                      и/или 01 001 111 (4F) - команда LD R, A в НЕ системном режиме поступающий байт возможно - см. примечание про регистры I,R и прерывания - заменяется на 00 (даже с учётом префикса - по действию NOP), следом сброс.
обозначим b2 & b6 & !b7 & ED = t2

при t2 & b0 &! b1 &sys & PFX? = outsys #  RETI/RETN, включая недокументированные)
при t2 & !b0 & b1 &!sys = nop # IM N
LD_FLAG = 1 (запрещены и LD I, A и LD R, A), LD_FLAG = 0 (разрешены и LD I, A и LD R, A), LD_FLAG = b3 для запрета только LD R, A
и LD_FLAG = !b3 для запрета только LD I, A - jumper на 3 положения, снят - четвёртое
при t2 & b0 &b1 &LD_FLAG &!b4 &! b5 &!sys = nop # LD I/R, A

Некоторые действия могут конфигурироваться битами в соответствующем системном порте (физически это будет провод, с сигналом, запрещающим или разрешающим, например, запрет прерываний или требующим префикс DD для перехода в системный режим по RST N) и устанавливаться при переключении контекста.
---
* примечание про префиксы DD/FD - простая реализация может всегда обрабатывать RST XXX в обычном режиме с переходом в системный режим, но может захотеться разделить системные и несистемные вызовы, например, считая вызов с префиксом DD (или FD) системным, а без него - обычным или наоборот. При простой реализации программа обычного режима позволяет использовать начальную область памяти как угодно, а не для хранения векторов (хотя никто ей не помешает делать CALL вместо RST), а во втором - сохраняется обратная совместимость с существующим кодом - если такая нужна.
* примечание про регистры I,R и прерывания - режим прерывания вообще "прибит гвоздями" к аппаратуре и имеет смысл запретить его менять в принципе; если в системном режиме используется режим 2, то в обычном следует запретить изменение регистра I (иначе программа обычного режима может переключить таблицу векторов для системного); кроме того при использовании встроенного в процессор механизма регенерации динамической памяти следует запретить изменение в обычном режиме регистра R.

Схема выходит проще, чем описание, на фоне буферов, дешифраторов и портов она не особо заметна. Схема будет после.

-----
UPD: есть ещё вопрос про вызов несистемного кода из системы - например, начать чтение с клавиатуры (или UART), передав обработчик символа - тут нужен CALL (пусть безусловный) с каким-то префиксом (выключающий системный режим через две операции чтения - адреса перехода) и аналогичный RET (только из стека читаться будет адрес возврата).

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

Тут же надо подумать - а что будет, если посреди такой весёлой позы прилетит прерывание - и или доказать, что всё нормально или сделать, чтоб не прилетело.

Основная печаль, что передать управление косвенно можно только безвозвратно (JP), но вызвать процедуру - callback можно только по адресу в коде, следовательно у нас тут самомодифицирующийся код выходит, из ПЗУ не запустить, так что казалось бы можно ограничиться только командой CALL; с другой стороны - разрешать передачу управления в произвольный фрагмент системного кода (PUSH хрень; RET в систему) недопустимо, так что возврат по RET невозможен в принципе, нужно использовать системный вызов типа RST N для выхода, так что CALL становится ненужен и остаётся обработать только JP; учитывая, что проще всего обработать JP[IX/IY], а для отличия его от обычного требовать сразу два префикса DD и FD - (их порядок определит - какой регистр реально будет использован).

Tags: КомпьютерноеЖелезо, Самоделки, ФиксикиПокусали, ХочетсяСтранного
Subscribe

  • Бегаю по магазинам

    Поскольку мои подвиги в основном строительные, все остальные занятия сводятся в основном к покупкам и разведке. Для колонок ничего не купил - там…

  • На выходных

    (уже скоро новые, да, я знаю) Кроме выбора красивых плинтусов с кабельканалами в соседнем городе, нашёл в подвале здания рынка (кажется, такие…

  • Просто кучка мыслей

    Если задуматься, то события, не выглядящие сюжетом, как сюжет (сюрприз, блин!) не воспринимаются и не получают признак "вкусно" при восприятии. Как…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 9 comments