Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

в 10:13, , рубрики: ATA, DIY, diy или сделай сам, hdd, ISA, stm32, x86, логические схемы, старое железо, Электроника для начинающих, метки: , , , , , , , ,

Добрый день, уважаемыее. В сегодняшней статье мы предадимся ностальгии, поработаем со старым добрым железом, и постараемся прикрутить к нему не менее доброе новое. А заодно вспомним, как работает шина ISA и как вообще происходит общение х86-процессора с периферией.

Введение

Пару дней назад мне попался на глаза мой самый первый компьютер – старичок 386й, на процессоре от АМД (Am386-DX), с 4 мегабайтами RAM, VGA-видеокартой и мульти-платой, берущей на себя функции контроллера дисковода, харда, параллельного и последовательного портов.
Конечно, он давно уже был избавлен от своего корпуса и от древнего, почившего харда – теперь он представлял собой просто материнскую плату с парой плат расширения. Несколько лет назад я подключал к нему более новый хард, на 10 ГБ (изначально в нем стоял диск всего на 200 мегабайт), на который я поставил FreeDOS.

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Однако на этот раз он грузиться дальше БИОСа отказался – судя по звукам, 10 ГБ хард за несколько лет лежания в шкафу успел отправиться вслед за двухсотметровым.
И тогда у меня проснулось острое желание что-нибудь с этим компьютером сделать, прикоснуться к этой древности, с которой я начинал знакомство с IT, уже как разработчик, а не как пользователь. В идеале хотелось бы, конечно, сделать эмулятор жесткого диска, работающий с SD-карточкой, но к этой цели будем идти постепенно. Начнем с более простой задачи – соберем устройство, висящее параллельно с настоящим контроллером жесткого диска, и логирующее обмен данными, чтобы узнать, как именно старый БИОС детектит жесткие диски. Изначально я предполагал сделать то же самое, но для контроллера дискет, однако, после того как мой последний хард помер, все что у меня осталось работающего – это БИОС, который никак не проверяет наличие флоппи. Зато у него есть пара пунктов относящихся к хардам – детект жестких дисков и средства для их форматирования.
Разумеется, на ПЛИС сие делается очень легко в силу их архитектуры, но будем придерживаться бюджетного варианта и попробуем сделать это на контроллере STM32F103 и нескольких микросхемах дискретной логики. И так, начнем.

Железо

Традиционно пойдем от хардварных низов. Давайте вспомним, что же такое шина ISA, лежащая в основе старых компьютеров, и как к ней можно подключиться. Для тех, кто не очень представляет схемотехнику внутри х86 машин, это поможет пролить свет на архитектуру подобных систем. На самом деле все очень просто – в «чистой» ISA нету никаких средств Plug&Play – они появились только в следующем стандарте – а, следовательно – никаких средств выдачи адреса устройствам.
Таким образом, ISA-карты представляют собой устройства с аппаратно заданным адресом (жестко определенным схемотехникой, в лучшем случае – с возможностью выбрать базовый адрес джамперами). Сама шина содержит 20 линий адреса, 16 линий данных, несколько сигналов питания, несколько линий IRQ, и набор управляющих сигналов.

image

Как все это работает? Предположим, нам нужна возможность зажигать/гасить несколько светодиодов на нашем устройстве. Для этого мы разместим на нашей ISA-плате микросхему регистра, например, такую, как 74HC273.
Это самая обычная 8-битная «защелка», запоминающая то, что ей подали на вход по сигналу. Выходы регистра подключим к светодиодам и забудем о них. С программной точки зрения взаимодействие с устройством на ISA шине может быть реализовано двумя методами.

  1. При помощи мапирования на память – тогда мы будем декодировать сигналы чтения/записи памяти и выдавать результаты на шину вместо контроллера DRAM – так поступает видеокарта, ее видеопамять мапируется на адресное пространство памяти компьютера. Таким образом, запись в память видеокарты для компьютера ничем не отличается от записи в свою оперативную память и выполняется обычной командой MOV.
  2. Для тех устройств, где не требуется передавать большие блоки данных, используется так называемое «пространство ввода-вывода» — отдельное адресное пространство, выделенное для периферийных устройств и ограниченное 16 битами адреса. Обращение к нему выполняется командами IN и OUT (чтение и запись в порты ввода-вывода)

На деле, за словами «отдельное адресное пространство» кроется физически простая сущность: в шине ISA присутствуют 4 сигнала – MEMW, MEMR, IOR, IOW. При исполнении команды чтения/записи в память (MOV) либо чтения/записи в IO (IN, OUT), искомый адрес выставляется на одной и той же шине, линиях A0-A19 ISA. Данные также идут по одним и тем же линиям – D0-D15. Разница состоит лишь в том, что при чтении из памяти выставляется активный уровень на линии MEMR, при записи в память – MEMW, при чтении из порта IO – IOR, при записи в него – IOW.
Таким образом, чтобы сделать простейшее устройство с одним регистром и светодиодами, нам нужно определить, когда на шине выставлен нужный нам адрес (мы ведь помним, адреса нам никто не выдает, мы должны сами выбрать адрес, который не будет конфликтовать с имеющейся периферией), и по сигналу IOW разрешить запись данных с линий D0-D8 в наш регистр.
В более сложных устройствах, содержащих несколько регистров, старшие линии адреса идут на декодер, формируя активный выходной сигнал при совпадении с некоторым «базовым» адресом устройства, младшие же формируют номер регистра, к которому следует обратиться.
Перейдем к более конкретному примеру – нашему контроллеру ATA. Для большего понимания принципов его работы рекомендую ознакомиться со статьей из OSDev wiki.
Управляется он девятью IO регистрами, восемь из которых расположены подряд, начиная с базового адреса 0x1F0. Девятый, к сожалению, расположен по адресу 0x3F6, что несколько усложняет схему декодирования.
Разумеется, не будем заводить на контроллер все линии адреса и делать декодер на нем, иначе мы ничего не успеем – частота тактового сигнала шины 8 МГц, цикл IO, согласно спецификации, длится 4 такта, что при частоте 72 МГц контроллера дает нам всего 36 тактов на раздумья. Поэтому воспользуемся дешевыми микросхемами дискретной логики.
Если бы не было этого девятого регистра, который торчит в 0x3F6, то нам нужно было бы построить схему, которая выдает активный сигнал, когда на линиях A9 и A3 установлен ноль, а на A4-A8 – единица ( то есть, для адресов 0x1F(..) ). Биты старше A9 в ISA картах обычно не декодят, не обращая внимания на возможность доступа к одному и тому же устройству по адресам, расположенным выше.
Обработку трех младших бит уже можно было бы поручить контроллеру. Увы, у нас остается неохваченный регистр 0х3F6.
Исходные условия (активны линии А4-А8 и неактивна А3) выполняются всегда, так как эти биты находятся в указанных состояниях и для 0x1F(..) и для 0x3F6. К ним добавляется условие, которое можно сформулировать так: при активном А9 – должны быть активные уровни на А1 и А2 (адрес 0x3F6)
То есть,

CS0 = A8 & A7 & A6 & A5 & A4 & ~A3
CS1 = A1 & A2 & A9
CS2 = CS0 & (~A9 | CS1)

Воспользовавшись онлайн симулятором логических схем Logic.Ly, я построил эту схему базируясь на микросхемах, которые были у меня в наличии – 74HC04, четверной элемент NOT, 74HC30 — восьмивходовый NAND и 74HC10, тройной трехвходовый NAND.
Так как элемента OR у нас нет, вспоминаем правила Де Моргана – отрицание конъюнкции есть дизъюнкция отрицаний и отрицание дизъюнкции есть конъюнкция отрицаний, или, в виде логических равенств

~(A&B) = ~A | ~B
~(A|B) = ~A & ~B

Этим и воспользуемся:

 ~( ~ (~A9 | CS1))) = ~(A9&~CS1) - = (A9 NAND ~CS1)
CS2 = CS0 & (A9 NAND ~CS1) 

Чистого AND у нас тоже нет, поэтому подадим его составляющие на трехвходовый блок NAND и будем входить в прерывание по спаду.
Как видно, вся логика вмещается ровно в три корпуса.

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

К этим условиям добавляется наличие активного уровня на IOR или IOW (не забываем, что на них, согласно стандарту, активный уровень низкий, то есть нам приходят уже инвертированные сигналы, ~IOR и ~IOW):

CS = CS2 & (IOR | IOW)
(IOR|IOW) = ~(~(IOR & IOW) ) = ~(~IOR & ~ IOW) = (IOR NAND IOW)
CS = CS2 & (IOR NAND IOW)

Итоговая схема выглядит так:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

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

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Внимательно добавим соединим выходные цепи, после чего добавим входные в виде достаточно длинных щупов, которые в последствие воткнем в материнскую плату компьютера:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Для удобства я временно закрепил старшие адресные входы слева (А3-А9), младшие справа (А0-А2), а посередине вывел сигнал CS2.

Давайте временно отвлечемся от сборки и попробуем посмотреть осциллографом, что же у нас вышло. Итак, соединяем адресные входы с шиной – так как ISA это именно шина, нам не нужно пытаться воткнуться щупами в тот же разъем, куда вставлена плата контроллера ATA, выбираем любой нам удобный. Щупы, к сожалению, оказались маловаты для таких отверстий, поэтому сверху я воткнул еще и гребенку прямых пинов – по отдельности и щупы и пины выпадают, а вот вместе держатся довольно неплохо.
Также не забываем подключить землю и питание схемы к ISA, а заодно – землю щупов осциллографа.
Включаем осциллограф и компьютер(я сразу зашел в меню настройки БИОСа) и тыкаемся в сигнал CLK. Мы должны увидеть что-то типа этого:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Это, понятное дело, тактовый сигнал шины, частота которого обычно равна 8 МГц. На моей материнской плате его частота равна 7.19 МГц, что отражено в настройках БИОСа. Видимо, это особенность железа – БИОС не позволил мне понизить эту частоту, или хотя бы выставить ее ровно в 8 МГц, упорно выставляя 7.19 МГц. Ну да ладно.
Проверяем контакты входов нашей схемы – тыкнувшись в любой из них мы получим хаотичный сигнал на экране осциллографа, так как система постоянно обращается к разным адресам и портам. Так что если на входе тишина, это значит, что у вас отошел контакт, и его необходимо перепроверить.
Теперь подключаемся к нашему сигналу CS2 и наблюдаем следующую картину:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Вполне ожидаемо – сигналы IOR и IOW не участвуют в формировании CS2, так что он становится активным при совпадении адреса на шине с заданным нами (0x1F0-0x1F7 и 0x3F6). Система проводит регулярную регенерацию DRAM, поэтому мы получаем красивый периодический сигнал. Сейчас самое время поднастроить развертку и уровни осциллографа, чтобы видеть сигналы во всей красе.
Убедившись, что все работает, обесточиваем схему и дособираем ее доконца, получив адскую мешанину проводов типа этого:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Снова включаем компьютер, заходим в меню настройки БИОСа, включаем осциллограф.
Никаких сигналов! Что ж, пришла пора проверить правильность наших выкладок – выбираем пункт “Autodetect hard drive”. Первый диск определяется быстро, и, скорее всего, на экране осциллографа мы ничего не успеем заметить, если только не включили одиночный режим.
А вот второй диск (по причине его отсутствия) будет детектиться достаточночно долго, чтобы мы увидеть на экране компьютера это:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

А на экране осциллографа – вот это:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Чтобы совсем убедиться в нашей правоте, выходим из детекта дисков, включаем одиночный режим осциллографа и внимательно смотрим на его экран – ничего! Сколько бы мы не ждали, CS не становится активным! Но стоит только зайти в детектирование дисков, как мы снова ловим знакомую картинку, которая вполне соответствует стандартам — цикли I/O длится четыре такта шины.
Что ж, самое время взять плату с STM32 и подключить ее к системе!
Я подключил следующим образом:

Шина данных ISA (D0 – D7) подключены к GPIOD.0 – GPIOD.7,
Три младших линии адреса (A0 – A2) – к GPIOD.8 – GPIOD.10,
Линию адреса A9 – к GPIOD.11 (нам ведь нужен будет этот бит, чтобы понять, что обращение идет не к 0x1F6, а к 0x3F6!)
Линии IOW и IOR к GPIOD.12 и GPIOD.13.
Сигнал CS – к GPIOB.0

Теперь при прерывании на GPIOB.0 нам необходимо будет просто прочесть GPIOD->IDR (Input Data Register), в котором младшие 8 бит будут искомыми данными, следующие четыре – адресом (причем, возможными комбинациями будут 0000 – 0111 и 1011, соответствующие портам 0x1F0 – 0x1F7 и 0x3F6), следующие два – режимом (чтения при 01 либо записи при 10).
Тут важно отметить следующее – если вдруг мы получим результат с битами режима, находящимися в невалидном состоянии – 00 либо 11, это будет сигнализировать нам об ошибке работы – этот факт нам очень скоро пригодится.
Итак, переходим к софту.

Софт

С софтом все предельно просто – мы настраиваем GPIOD на вход, как и GPIOB.0, после чего настраиваем прерывание по спаду на линии EXTI, соединенной с GPIOB.0.
В обработчике прерывания мы будем только читать значение из GPIOD и инкрементировать указатель на буфер. Этот буфер потом можно будет послать на компьютер для анализа по любому интерфейсу, либо вовсе не заморачиваться с этим и посмотреть его прямо в дебаге.
Код настройки представлен ниже:

GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);


    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|
    								GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7
    								|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|
    								 GPIO_Pin_12|GPIO_Pin_13;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);

    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

А вот — код обработчика прерывания:

uint16_t Log[1024];
uint16_t ptr=0;
void EXTI0_IRQHandler()
{
	Log[ptr]=GPIOD->IDR;
	ptr++;
	EXTI_ClearITPendingBit(EXTI_Line0);
}

Тесты, отладка и допиливание

Пришло время проверить, что у нас получилось! Запускаем компьютер, заходим в настройку БИОСа. Запускаем дебаг STMки. Заходим в детектирование дисков, и, после детекта диска C, приостанавливаем выполнение программы контроллера. В окне дебага мы видим, что какие-то данные наловились, и их немало!
Дальше я поступил следующим образом – из окна дебага скопировал содержимое буфера в MS Exel, чтобы разбить на колонки и избавиться от первой, содержащей имя переменной, после чего скопировал столбец со значениями в новый текстовый файл, и получил что-то вроде этого:

58453
54527
42069
38143
42069
38143
...

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

var busData = uint.Parse(entry);
uint data = (busData & 0xFF);
uint address = ((busData & 0xFF00) >> 8);
uint rw = (address & 0x30)>>4;
address = (address & 0x0F);

Однако, запустим программу, я столкнулся с большой проблемой – очень многие записи из файла содержали режим доступа 11, что означало отсутствие сигналов чтения/записи. Так как вход в интеррапт был возможен только при наличии одного из этих сигналов, я сделал вывод, что интеррапт длится дольше, чем идет цикл шины, и я просто не успеваю считать валидные данные.
Для проверки этой гипотезы я решил выставлять пин GPIOB.2 в 1 при входе в интеррапт, и сбрасывать его в 0 при выходе, после чего повесил на него щуп осциллографа.
Результат был удручающий:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Как видно, система входит в интеррапт уже у самого конца цикла I/O, несмотря на обещанные 12 тактов на вход. Не помог даже атрибут (naked), разница была совершенно незначительной.
Это меня огорчило, но я решил попробовать разогнать контроллер – те же AVRки очень хорошо поддавались разгону, почему бы не проверить, как с этим обстоит дело у STM32. Для этого нам необходимо пойти в файл system_stm32f10x.c, в процедуру инициализации системного тактового сигнала SetSysClockTo72, и найти строку

RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

В этой строке следует поменять константу RCC_CFGR_PLLMULL9 на что-нибудь побольше. Скажу сразу – я попробовал все варианты и в итоге остановился на максимальном значении, RCC_CFGR_PLLMULL16. Таким образом, контроллер совершенно спокойно завелся на частоте 128 МГц вместо 72, даже не греясь.
Кстати, совсем хорошо было бы привязать тактовый сигнал контроллера к CLK шины ISA, чтобы работать с ней синхронно, но мне очень не хотелось отпаивать кварц на отладочной плате, поэтому я не стал этого делать.
Посмотрим, что выдает осциллограф теперь:

Трясем стариной: перехват потока данных между i386 и контроллером ATA средствами STM32

Наконец-то мы стали входить в интеррапт достаточно рано для того чтобы успеть его обработать! Давайте снова наберем входных данных и попробуем их проанализировать.
Я несколько дополнил программу, чтобы отчет выдавался форматированным и сразу с именами регистров, к которым идет обращение. В случае с неправильным режимом доступа, в отчет добавляется строка о невалидности данных.
Вот результат работы программы:

WRITE:	Cylinder Low	[0x1F4]		VALUE:	0x55
READ:	Cylinder Low	[0x1F4]		VALUE:	0x55
WRITE:	Cylinder Low	[0x1F4]		VALUE:	0xAA
READ:	Cylinder Low	[0x1F4]		VALUE:	0xAA
WRITE:	Cylinder Low	[0x1F4]		VALUE:	0x0F
READ:	Cylinder Low	[0x1F4]		VALUE:	0x0F
WRITE:	Cylinder Low	[0x1F4]		VALUE:	0x00
READ:	Cylinder Low	[0x1F4]		VALUE:	0x00
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Drive/Head	[0x1F6]		VALUE:	0xA0
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Drive/Head	[0x1F6]		VALUE:	0x04
WRITE:	Drive/Head	[0x1F6]		VALUE:	0x00
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Drive/Head	[0x1F6]		VALUE:	0xA0
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Drive/Head	[0x1F6]		VALUE:	0xA0
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Command		[0x1F7]		VALUE:	0x10
READ:	Status		[0x1F7]		VALUE:	0x50
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Drive/Head	[0x1F6]		VALUE:	0xA0
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Drive/Head	[0x1F6]		VALUE:	0xA0
READ:	Status		[0x1F7]		VALUE:	0x50
WRITE:	Command		[0x1F7]		VALUE:	0xEC
READ:	Status		[0x1F7]		VALUE:	0x58
READ:	Data		[0x1F0]		VALUE:	0x5A
READ:	Data		[0x1F0]		VALUE:	0xFF
READ:	Data		[0x1F0]		VALUE:	0x00
READ:	Data		[0x1F0]		VALUE:	0x10
READ:	Data		[0x1F0]		VALUE:	0x00
READ:	Data		[0x1F0]		VALUE:	0x00
READ:	Data		[0x1F0]		VALUE:	0x3F
READ:	Data		[0x1F0]		VALUE:	0x00
READ:	Data		[0x1F0]		VALUE:	0x00
READ:	Data		[0x1F0]		VALUE:	0x00
READ:	Data		[0x1F0]		VALUE:	0x45
...

Как мы видим, невалидных данных больше нет.
Попробуем разобраться, как БИОС проводит детектирование.
В начале он настойчиво пишет-читает в регистры, задающие адрес – удостоверяясь, что вычитывается то же значение, которое было записано. Если контроллер ATA отсутствует в системе, то БИОС так и будет долго-долго пытаться записать-прочитать этот самый регистр, 0x1F4 – вот пример отчета при вытащенной плате контроллера:

WRITE:	Cylinder Low	[0x1F4]	VALUE:	0x55
READ:	Cylinder Low	[0x1F4]	VALUE:	0xFF
WRITE:	Cylinder Low	[0x1F4]	VALUE:	0x55
READ:	Cylinder Low	[0x1F4]	VALUE:	0xFF
...
WRITE:	Cylinder Low	[0x1F4]	VALUE:	0x55
READ:	Cylinder Low	[0x1F4]	VALUE:	0xFF
WRITE:	Cylinder Low	[0x1F4]	VALUE:	0x55

Потом подает команду 0x10, значение которой я так и не нашел, и на которую хард, похоже, никак не реагирует, и наконец – команду 0xEC, DRIVE IDENTIFY, в ответ на которую хард отдает 256 16-битных слов информации о диске.
Прежде чем начать их считывать из порта 0x1F0, БИОС запрашивает байт статуса из регистра 0x1F7, ожидая готовности диска.
Вот тут, к сожалению, я понял свой промах – я решил, что данные выдаются по 8 бит, так как управляющие регистры 8-битные. Однако, как оказалось, данные выдаются по 16 бит, поэтому я получил только 256 младших байт. Для получения полной информации придется немного переделать схему, отдав весь GPIOD под данные, а адрес и режим доступа выводить на другие пины, что, конечно, увеличит задержку в их обработке.
Поэтому на данный момент я приостановился, хотя, возможно, в ближайшее время я и продолжу работу и попробую сесть на шину уже не как монитор, а как устройство. У ISA шины есть замечательный сигнал IOCHRDY, выставляя неактивный уровень на котором, устройство сигнализирует о необходимости увеличить длительность IO-цикла, а значит, возможно, у меня хватит времени переключить пины на вывод и выдать свое состояние.

На этом пока все, спасибо за внимание.

Автор: Ariman

Источник



https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js