IBM PC своими руками — это очень просто
После того, как я воплотил свою давнишнюю мечту и все-таки (хотя и с опозданием почти на 30 лет) построил Радио 86РК, некоторое время мне казалось, что на этой части моей истории поставлена вполне достойная точка.
Тем не менее, обнаружилось, что болезнь до конца не вылечена, и она вернулась еще более острым рецидивом. Наверное, сказались как неожиданно успешный опыт постройки 86РК, так и то, что у меня в ходе данного процесса образовалось довольно большое количество весьма притягательно выглядящих инструментов, приборов и деталей, которым очень хотелось найти применение.В конце концов ломка стала нестерпимой, и мне пришлось снова взяться за паяльник, а также вспомнить некоторые другие навыки из прошлого. Что из этого получилось, можно увидеть вместе с некоторым количеством картинок и очень (повторяю — ОЧЕНЬ) большим количеством букв (и даже не букв, а страниц) дальше…Основным вопрос был в том, что именно делать. С одной стороны, душа лежала к чему-то совсем раритетному, с другой стороны хотелось также попробовать новые технологии. Находясь в тяжелых раздумьях, я периодически просматривал пару тематических форумов, посвященных старому «железу». В какой-то момент я натолкнулся на долго тянущуюся тему о том, как запустить 8086 процессор с минимумом обвязки. Странно было то, что тема обсуждалась больше года, но в практическую плоскость так и не передвинулась.
На мой неискушенный взгляд казалось, что задача вообще пустяковая, но вдруг там есть что-то, что я вообще не понимаю? В результате я решил попробовать поиграться именно с этим в надежде, что данное развлечение — именно то, что мне нужно для полного счастья.
Схему для себя я даже рисовать не стал — вроде все настолько просто, что вопросов, куда что подключать, не возникало. Оставалось только решить, каким способом это все собирать. С МГТФ я достаточно поигрался раньше, хотелось чего-то новенького. Так как все новое — это забытое старое, то я прикупил инструмент и материалы для монтажа накруткой и взялся за процесс. Который (процесс), к сожалению, особо никуда не пошел… Либо руки у меня совсем из неправильного места растут, либо что-то понял неправильно, но добиться устойчивой скрутки так и не получилось.
Впрочем, мучился я недолго (хотя, может именно в этом и была основная причина ?) — в процессе перекладывания моих сокровищ с места на место обнаружилась нераспечатанная беспаечная макетная плата с кучей очень красивых разноцветных проводков. Сложность (вернее, отсутствие таковой) проекта вполне позволяла без труда разместить все на этой плате, что я довольно оперативно и сделал:
Была написана суперпрограмма размером в десяток байт, реализующая не имеющую мировых аналогов функцию — мигание светодиодом. При первом включении ничего не заработало, но обнаружилось, что проблема только в запуске тактового генератора. Мне было лень ставить указанные в документации конденсаторы/резисторы возле кварца, поэтому иногда для запуска генератора требовалось коснуться корпуса кварца пальцем или отверткой, после чего светодиод начинал исправно мигать.
Кстати, меня немного удивила частота мигания — я уже забыл, насколько этот процессор был медленный. При максимально допустимой для оригинального 8088 тактовой частоте в 5МГц цикл в 65536 «пустых» операций выполнялся порядка секунды…Опубликовав результат своего творчества на соответствующем форуме, я тут же был обвинен в том, что обсуждалось подключение 8086, а у меня же 8088, что намного проще и вообще!
Ладно, практика — критерий истины. Раздобыл 8086, потратил еще чуть-чуть времени (в основном, чтобы разнести вручную программу в две микросхемы ПЗУ, так как 8086 16-разрядный, а ПЗУ у меня были 8-разрядные) и получил очередную мигалку:
Апетит, как известно, приходит во время еды. Вот и у меня, вместо удовлетворения от достигнутого, возникло желание двинуться дальше. Только тут уже совсем настойчиво утвердилась мысль, что нужно также опробовать и современные технологии. В качестве примера современных технологий было принято решение использовать FPGA в комбинации с 8088 процессором. Одним махом можно было убить нескольких зайцев — база (процессор) знакомая, технологии FPGA вполне современные, особо тратить время на монтаж не нужно, так как все творчество можно перенести внутрь FPGA.
У меня уже имелась одна из самых навороченных отладочных FPGA плат — Terasic DE2–115, на которой, кроме довольно большой FPGA Altera Cyclone IV, также установлено немереное количество прибамбасов, свисталок и мигалок:
В частности, кроме штатных GPIO в количестве 36 плюс еще несколько штук, на этой плате также находится разъем расширения, к которому можно подключить небольшую плату и получить в общей сложности около 100 GPIO, что должно хватить для практически любого мыслимого применения (разумеется, в моих скромных целях).
Правда, есть нюанс — процессор 8088 5-вольтовый, а вот FPGA уже давно отказались от поддержки TTL 5V, максимум, что есть — LVTTL 3.3V
Хорошо, что есть широкий выбор преобразователей уровней, что и решено было использовать. Вначале остановился на микросхеме TXB0108 — 8-разрядный двунаправленный преобразователь уровней с автоматическим выбором направления. Автоматический выбор направления был довольно важен, так как позволял не думать о том, что происходит на шине данных — чтение или запись. Кроме того, шина данных в 8088 мультиплексирована с 8 младшими разрядами адресной шины, что еще добавляет сложности в определении того, в каком направлении нужно передавать сигналы — от процессора или к нему.
Так как TXB0108 довольно маленькая, да и возиться с проводами уже не очень хотелось, решил попробовать еще одну новую для меня вещь. Была куплена программа Eagle для разводки печатных плат, и начались мои мучения как с освоением нового программного продукта вообще, так и с весьма специфическим интерфейсом Eagle в частности.
Делать что-то в первый раз в новой для себя области лично для меня очень мучительно — тыкаешься, как слепой котенок, а душа рвется на просторы. Подготовка gerber-файлов для производства платы (да, о ЛУТе думал, и даже материал купил, но не решился) вообще чуть не вызвала нервный срыв — сразу представлялось, как все производство стоит и хохочет, глядя на жалкие результаты моих трудов…
Хорошо, что плата была совсем уж простая, поэтому не прошло и недели (вместе со временем изготовления), как я уже держал в руках свежеспаянное творение:
Кстати, для пайки преобразователей уровней я испробовал жало «микроволна» от Ersa (естественно, есть подобное и у других производителей, да и самому такое можно сделать). Должен заметить, работает довольно неплохо, так что трудоемкость пайки была на порядок меньше, чем можно было бы ожидать при таких размерах выводов.
За время, пока плата изготавливалась, я ускоренными темпами осваивал FPGA. Вообще отладочную плату я изначально купил, чтобы поразвлекаться немного с FPGA технологиями и, в частности, с VHDL, но это у меня конкретно не пошло. Мозг просто отказывался мыслить категориями VHDL, и максимум, что я сделал — повторил в FPGA несколько простых устройств, скопировав их схемы методом схемного дизайна. Изучение же VHDL закончилось на уровне signal3 <= signal1 and signal2; дальше чтение учебников оказывало на меня отличный усыпляющий эффект.
Тут тоже решил все делать с помощью схемного дизайна. И вообще — нафига все эти HDL«ы, когда так просто мышкой нарисовать схему, и все работает? В тот момент мне это казалось и правильнее, и удобнее. Соответственно, нарисовал в Quartus«е все ту же мигалку, только пришлось еще сделать аналог тактового генератора 8284 (на макетке он у меня был в «железном» виде). К счастью, в документации нашлась полная внутренняя структура этой микросхемы, так что с этим проблем не возникло. Хотя один нюанс обнаружился — в документации от Intersil (производитель устаревших микросхем) внутренняя структура 8284 была нарисована с ошибкой (инверсный выход вместо прямого, или наоборот — уже не помню). Правда, ошибку отловил еще при рассмотрении схемы, но сам факт очередной раз натолкнул на мысль, что нужно руководствоваться оригинальными документами.
Далее было сгенерировано ПЗУ с соответствующей программой, моя плата с процессором подключена плоским шлейфом к отладочной плате, и наступил момент истины — загрузка прошивки в FPGA. К сожалению, светодиод после этого не замигал… После короткого разбирательства выяснилось, что я подал 0 на один из входов процессора, тогда как там должна быть 1. Данное недоразумение легко исправилось перерезанием дорожки и пайкой перемычки. После этого светодиод мигнул, но так и застыл в горящем состоянии…
И вот тут пошли настоящие разборки. К сожалению, скажу сразу, что причину нестабильной работы схемы я так и не понял. Почти уверен, что это связано с нечетким определением направленности сигналов через преобразователи уровней (хотя бы потому, что больше там нечему неправильно работать). Светодиод мог помигать какое-то время, а потом вообще в полный отказ, затем снова начать работать безо всякой закономерности.
Все мои тыканья с осциллографом не показали ничего, что могло бы натолкнуть на истинную причину, поэтому мне пришлось задуматься о путях кардинального решения проблемы — останавливаться в этом месте не позволяло самолюбие. Так как «самоопределяющиеся» преобразователи оказались под большим вопросом, решил обратиться к более проверенному решению — 74LVC8T245, преобразователи уровня с «ручным» управлением направления передачи сигнала.
Кроме того, также решил расширить задачу и сделать все по максимуму, в буквальном смысле — запустить процессор в максимальном режиме. В данном режиме процессор не является единоличным владельцем системной шины, и может отдавать ее другим устройствам (типа DMA и т.д.). Именно в таком режиме 8088 работает, в т.ч., и в IBM PC совместимых компьютерах. Для работы в максимальном режиме необходимы определенные сигналы, которые обычно формируются с помощью контроллера шины 8288. Теоретически, эти сигналы можно было сформировать внутри FPGA, но у меня не возникло полной ясности после довольно внимательного чтения документации по 8288, поэтому было принято решение использовать «железный» 8288, наряду с таким же 8284 (гулять так гулять !). В результате получалось гарантированно работающее (как мне казалось) ядро, вокруг которого уже можно было строить все, что угодно.
В процессе обдумывания схемы очередной раз пришел к выводу, что понять направление передачи на шине адреса/данных — не совсем тривиальная задача (именно из-за этого, если помните, изначально пробовал использовать «самоопределяющиеся» преобразователи уровней). Сначала кажется, что все предельно просто, и для этого есть практически готовые сигналы, но при более внимательном рассмотрении выясняется, что все далеко не так (особенно, если принять во внимание возможные временные разбросы сигналов и пытаться четко вписаться в них). Поэтому пошел по не самому элегантному, но зато железно работающему варианту — для младших 8 линий адресов выделил отдельный преобразователь уровня, работающий в одном направлении (от процессора) вместе с защелкой (с ней, защелкой, слегка погорячился — можно было внутри FPGA делать), а параллельно к этому на эти же линии поставил еще один преобразователь, чье направление уже управлялось соответствующим сигналом от 8288 (если говорить только о данных, без оглядки на адрес, то там все однозначно).
Правда, из-за того, что полярность сигнала направления была инверсна к тому, что требовалось для преобразователя, пришлось задействовать еще инвертор из 7400. Зато на схеме появилась знаменитая 7400, она же ЛА3, без которой в свое время не обходилось ни одно цифровое устройство.
Очередной раз, пока плата находилась в изготовлении, я рисовал в Quartus«е схему своего суперкомпьютера. В отличие от предыдущего варианта, тут я уже решил добавить нормальную дешифрацию адресного пространства, разделить память и ввод/вывод, а также задействовать ОЗУ. Мало того, вместо мигающего светодиода я сразу замахнулся на целый 7-сегментый индикатор, который должен был увеличивать свое значение по кругу!
Как раз к моменту завершения рисования схемы платы были доставлены, распайка заняла совсем немного времени, и все было готово к загрузке прошивки:
На этот раз, как ни странно, все заработало практически с «полу-тыка». Единственное, что меня удивило и насторожило, так это температура процессора. Он реально нагревался так, что через некоторое время на нем с трудом можно было удерживать палец. Попытки посмотреть осциллографом, нет ли каких-либо конфликтов на шинах, привели только к появлению дополнительных вопросов, поэтому я обратился к одному из профессиональных форумов по электронике.
Тут нужно сделать некоторое отступление. Я никогда не занимался электроникой профессионально, и никакого образования в этой сфере у меня нет. Мало того, даже с любительской точки зрения у меня в этой области был перерыв около 20 лет (да и до того опыт был довольно узким). Соответственно, очень много я не знал вообще, а многое из того, что знал, то понимал в идеализированном виде. В частности, сигналы в цифровой схеме строго прямоугольные и т.д. Не, я, конечно, понимал, что это не так, но практически такое понимание применить не мог.
Так вот, на том форуме мне открыли глаза на некоторые вещи, которые для многих покажутся прописными истинами, но для меня оказались неожиданностью. Например, мне показалась странной форма тактового сигнала, приходящего на процессор с чудовищным по амплитуде «звоном»:
Оказалось, что этот самый сигнал выглядит совершенно по другому, если всего-навсего «землю» брать не там, где брал ее я (на другом конце отладочной платы, как удобнее), а непосредственно у приемника сигнала (процессора):
Вроде мелочь, но, на мой взгляд, именно из таких мелочей и формируется истинное понимание многих процессов…
Кстати, по поводу оборудования, на котором сделана эта картинка. В ходе возни с 86РК у меня появился совсем неплохой портативный осциллограф Fluke, но почему-то к нему у меня сразу душа не легла. Почему — не знаю, не то, и все… Так что новый проект оказался хорошим поводом к приобретению нового осциллографа. Загоревшись идеей, иногда я принимаю поспешные решения. Так и случилось в этот раз. Вместо детального изучения предмета я заимел первое понравившееся устройство — Tektronix MSO3012. Нет, ничего плохого об аппарате я сказать не могу, наоборот — реально удобный интерфейс, куча полезных функций, практически полноценный 16-канальный анализатор цифровых сигналов, возможность просматривать сигналы в виде логической шины, подключение к компьютеру как напрямую, так и через сеть и т.д. Просто всегда хочется большего, и можно было бы выбрать более современную серию — MDO, которая ко всему прочему предлагает еще и встроенный генератор произвольных сигналов. А так инструмент очень крутой — мне аж завидно тем, кто использует подобное оборудование для решения реальных задач, а не для примитивных поделок, как в моем случае…
Возвращаясь к перегреву процессора — на форуме меня убедили, что для данной микросхемы такая температура совершенно нормальна (просто энергопотребление большое). Окончательно я успокоился после того, как поменял процессор на изготовленный по более современной технологии, и он после этого оставался совершенно холодным в течение всего процесса.
Получив в свое распоряжение 8088 с (потенциальной) кучей периферии, у меня совсем уж зачесались руки попрограммировать что-то посложнее управления 7-сегментым индикатором. Правда, встал вопрос о нормальной интегрированной среде для программирования на ассемблере реального режима (единственный язык, который я знал). И вот тут меня ждал большой облом. Если для 8080/Z80, а также для всех современных контроллеров есть масса как бесплатных, так и коммерческих IDE со всеми мыслимыми и немыслимыми прибамбасами, то для x86 не нашлось вообще ничего приличного. Были какие-то заброшенные любительские проекты, и все. Причин этому можно придумать несколько, но факт остается фактом. В конечном итоге, остановился на WinAsm (первое, что у меня хоть как-то заработало), который полноценным IDE назвать вряд ли можно (в первую очередь, из-за отсутствия отладчика), но хоть что-то (типа компиляции прямо из редактора) он позволял делать. В качестве отладчика на тот момент решил использовать старый заслуженный Turbo Debugger, запускаемый в DosBox.
Первым делом мне захотелось получить для своего устройства нормальный способ отображения информации, т.е. видеоадаптер. Хотя в куче мест (в т.ч. и на Хабре) можно найти статьи на тему «Как самому написать видеоадаптер на HDL за 5 минут», сделать свой модуль для меня было просто недостижимой мечтой. Поэтому я залез на широко известный opencores.org и нашел там самый простой алфавитно-цифровой VGA видеоадаптер, да еще и на VHDL (бОльшая часть проектов на opencores написана на Verilog).
Хотя данный модуль был практически законченным устройством, тем не менее, для работы с моей отладочной платой требовалась некоторая доработка (связанная, в первую очередь, со спецификой цифро-аналоговой части VGA-интерфейса DE2–115). Вооружившись моими зачаточными знаниями отдельных выражений VHDL, а также (в основном) методами научного тыка и последовательных приближений, в конце-концов удалось сделать что-то, что вроде отвечало моим потребностям, и компилировалось без ошибок.
К этому моменту я уже начал более-менее ориентироваться в схемном дизайне, так что преобразовать далее модуль видеоадаптера в символ и вставить в свою схему труда не составило. Сначала в качестве видеобуфера я использовал сгенерированное внутри FPGA ПЗУ с заранее записанным тестовым сообщением. Довольно быстро я увидел это сообщение на экране VGA монитора, после чего можно было менять буфер на ОЗУ. В этот момент в очередной (и далеко не последний) раз я ощутил всю прелесть FPGA. В видеоадаптерах всегда есть конфликт между необходимостью непрерывно читать видеобуфер для вывода его на экран, и потребностью процессора записывать в этот же буфер данные (а иногда и тоже их читать). Решается задача по разному, но, в любом случае, это далеко не самый простой узел (как минимум, для меня). А вот при наличии FPGA все сделалось элементарно — я просто сгенерировал двухпортовое ОЗУ, у которого было два комплекта шин адреса и данных. Процессор, естественно, подключался к одному порту, видеоадаптер — ко второму. Что там происходило внутри ОЗУ, и как убирались конфликты при одновременном обращении к одной и той же ячейке памяти — это были проблемы Altera, но никак не мои.
В качестве основного ОЗУ использовал имеющуюся на плате статическую RAM объемом 16×1M (в смысле, 1024К 16-битных слов). В качестве ПЗУ все так же использовался ROM, сгенерированный внутри FPGA — хотя на отладочной плате есть flash-память более чем достаточного объема, но для отладочных целей намного удобнее использовать встроенную память, тем более, что недостатка ее я не испытывал.
Итак, у меня неожиданно появилась вполне рабочая система на 8088 процессоре с видеадаптером, кучей (относительной) памяти и множеством разъемов для подключения к чему угодно. Все это явно напрашивалось на то, чтобы сделать с ним еще что-нибудь.
В голове очередной раз стала появляться мысль, которую я уже несколько раз упорно отгонял — «Может, DOS?…». И в какой то момент, попав на пик самоуверенности, я сдался… Итак, будем пробовать запускать MS-DOS!
Очевидно, для запуска DOS мне нужно будет реализовать необходимые функции BIOS, но что скрывается под словом «необходимые»? В принципе, я знал, где найти максимальный минимум (или минимальный максимум ?) функций –, а именно в первой версии BIOS«а для IBM PC. Так как уже на этом BIOS«е должны были работать все версии DOS, то в любом случае можно было бы ограничиться только его функциями. Найти исходники BIOS в интернете труда не составило, а беглый просмотр показал, что ничего особо загадочного там нет. Фактически, нужна была работа с клавиатурой INT 16h, видеоадаптер 10h, диск 13h и еще несколько простейших функций типа возврата объема доступной оперативной памяти, которые реализовывались буквально несколькими строчками ассемблера.
Первым делом в глубинах интернета был найден VHDL модуль для работы с PS/2 клавиатурой и внедрен (все тем же схемным дизайном) в мою схему. С контроллером прерываний было решено пока не заморачиваться, так как клавиатура на этот момент планировалась единственным источником прерываний.
Итак, можно было приступать к написанию обработчика INT 09h — прерывания клавиатуры. И тут меня ждала очередная засада. В позапрошлой жизни я довольно серьезно программировал на x86 ассемблере, но это было так давно, что почти все тонкости из головы улетучились вчистую. Нет, ясно, что mov и cmp забыть сложно, но все сложнее этого давалось с огромным трудом. Для меня нет ничего хуже, чем делать то, чего уже когда-то делал, и обучение чему-то не является исключением. Особенно если помнишь, что когда-то был довольно крут в чем-то, а сейчас не можешь сказать ни бе, ни ме… Пришлось, стиснув зубы, скачать какой-то учебник по ассемблеру и в экспресс-режиме его прочитать.
Естественно, вспоминать легче, чем начинать с нуля, но мои программы, особенно вначале, мягко говоря, элегантностью не отличались. Приблизительно как попытка пересказать Шекспира на английском языке, пользуясь словарным запасов в сотню слов и двумя временами…Тем не менее, довольно быстро минимальный набор INT 09/16 заработал, а за ним и была сделана поддержка нескольких основных функций INT 10h для вывода символов на экран. Можно было приступать к намного более сложной вещи — работе с диском.
Естественно, поддерживать реальный жесткий диск я не собирался. Идея была в том, чтобы эмулировать жесткий диск через работу с SD-картой, тем более, что на отладочной плате был разъем для такой карты. С образом диска проблем не возникло — чтобы не ходить далеко, я взял образ диска для уже упоминавшегося здесь проекта zet.aluzina.org
С поддержкой же работы SD-карты возникло сразу два больших вопроса — аппаратная поддержка шины SPI и протокол взаимодействия с самой картой.
В принципе, SPI можно реализовать полностью программно, но мне хотелось поразвлекаться и с «железом» тоже, поэтому я героически принялся за рисование приемо-передатчика байта в схемном дизайне. К моему удивлению, ничего сложного в этом не оказалось, и довольно скоро я уже наблюдал на экране осциллографа резво бегающие 8-битовые пакеты, содержащие именно то, что мне хотелось. Кстати, тут я впервые оценил возможность нового осциллографа не просто показывать кучу сигналов, а еще и объединять их логически в соответствующую шину. Намного приятнее видеть, что осциллограф понял, что передается именно байт A5, а не вручную смотреть, в нужных ли местах находятся переходы с 0 в 1 и наоборот.
С протоколом общения с SD-картой было слегка сложнее, но не намного. В интернете есть куча ресурсов, где все тщательно разжевывается, поэтому поиск необходимой информации не составил большого труда. Для упрощения задачи я не пытался подстраиваться под все типы и разновидности карт, а ограничился оригинальной SD (не SDHC или еще какие-то варианты) картой. Немного программирования, и вот уже на экране стало отображаться содержимое 0-го сектора карты. Сразу после этого привел эти функции в некоторое подобие INT 13h, добавил в зачаточном виде INT 19h (boot load) и увидел на экране следующее:
Так как в тот момент при чтении всегда считывался только 0-ой сектор, то начальный загрузчик (находящийся как раз в этом секторе), не находил ОС для загрузки, о чем и сообщал. Но это уже мелочи — главное, что моя схема потихоньку начала превращаться в настоящий компьютер и уже даже пыталась загрузиться!
Далее пошла борьба с пересчетом физических секторов в логические блоки. Тут я тоже схалявничал и вместо определения параметров (образа) диска просто жестко забил цифры для конкретного экземпляра образа. С этой частью пришлось повозиться — вычисления почему-то приводили к совершенно неожиданным результатам (вообще никогда не любил арифметику на ассемблере). Тем не менее, после некоторых мучений физические сектора/цилинды/головки стали исправно переводиться в логические блоки, и пришло время попробовать загрузиться уже по серьезному.
Естественно, сразу загрузка не прошла, да я и не ожидал этого. Заранее зная, что у меня в BIOS«е не реализована куча функций, я поставил на все прерывания заглушки, и при обращении к нереализованной функции на экран выводилась вся необходимая информация — к какому прерыванию и с какими аргументами обращаются. Далее шел процесс написания обработчика соответсвующей функции (а еще чаще — просто временной заглушки), и процесс продолжался. Неожиданно все остановилось на функции, которая вообще отсутствует в оригинальной PC — одна из функций INT 2F, связанную с обработкой событий. Я видел, что DOS определяет тип PC, и вроде не должна вызывать прерывания, отсутствующие на данном типе, но, тем не менее, это происходило, и процесс останавливался. Простая заглушка не помогла, а всю функцию реализовывать не хотелось из принципа.
Сейчас уже не помню весь ход мыслей (очень много чего смотрел в тот момент в исходниках DOS и в процессе загрузки), но в очередной раз на данном «зависании» я решил вызвать кучу прерываний (в тот момент у меня был отключен таймер на INT 08h) и нажал клавишу Shift. Неожиданно случилось чудо:
Скажу честно, эмоций на меня нахлынуло довольно много — проделать путь от макетки с парой микросхем до загрузки DOS за месяц, да еще и короткими набегами (из-за хронической нехватки времени) вроде довольно круто (извините за хвастовство)!
Кстати, с этим сообщением у меня есть до сих пор неразгаданная загадка. Дело в том, что после доделки прерывания таймера DOS стала загружаться без зависания в данном месте, но вот сообщение о копирайте Microsoft почему-то не выводится. Вроде оно также не выводится и на настоящем компьютере (к сожалению, попробовать не на чем). В чем тут первопричина — тайна, покрытая мраком. Я пытался понять логику по исходным кодам DOS, но сходу не увидел, а много времени тратить не захотел. Тем не менее, вопрос все еще мучает потихоньку…
После запуска DOS пришла очередь позапускать другие программы. Наверное, можно догадаться, чья очередь была первой — естественно, как говорят, старый добрый Norton Commander. Как ни странно, возни с ним было заметно больше, чем с DOS«ом. NC при запуске вызывал дикое количество функций, причем в ряде случаев обойтись простыми заглушками не удавалось, приходилось писать хотя бы минимум функциональности.
Тем не менее, проблемы были больше количественные, чем качественные, и вскоре удалось довести процесс загрузки NC до логического завершения:
Такой «интересный» внешний вид обусловлен несколькими причинами: — видеоадаптер не поддерживал на тот момент атрибуты— у меня не было второй части знакогенератора, в которой содержиться псевдографика, поэтому в соответствующих местах оказались символы из нижней части кодовой таблицы— не были реализованы некоторые функции INT 10h.
Вообще меня периодически удивляло, каким именно образом реализованы те или иные функции в различных программах (и даже в DOS). Например, команда CLS (очистка экрана) вызывала функцию INT 10h, вызывающую сдвиг окна вверх. При этом в качестве окна указывалась вся доступная экранная область, и сдвигалась она на количество строк, равное количеству строк на экране. Так как я не ожидал, что функции работы с окнами вообще кто-то использует, то и не спешил их реализовывать. Результат оказался налицо (вернее, на экране). Впрочем, к странностям некоторых программ еще вернемся немного дальше…
После запуска NC у меня возникло естественное желание привести его в божеский вид. Тем более, что такая часть работы иногда даже более приятна, чем попытки завести вообще мертвое устройство. С псевдографикой особых проблем не было — просто довольно много времени на ручное рисование символов (знакогенератор у меня был прямо в виде VHDL кода). А вот с атрибутами пришлось немного напрячься.
Еще раньше, по ходу процесса, я стал применять некоторые элементы VHDL. Сначала практически насильно — все-таки было желание еще раз попробовать освоить этот язык, а потом и потому, что в определенных случаях это оказывалось удобнее, чем использовать схемный дизайн. Даже в самом видеоадаптере мне пришлось вникнуть в код — изначально поддерживалось 43 (или что-то около этого) строки, мне же нужно было переделать на 25 строк. И поддержку атрибутов я сначала попытался сделать схемным дизайном, но вдруг стал осознавать, что вроде использовать VHDL для этого может оказаться проще. Естественно, все двигалось с большим трудом и использованием самых простых конструкций языка, но я вдруг начал понимать суть VHDL — пока еще совсем чуть-чуть, но уже достаточно, чтобы начать на нем что-то осознано создавать, а не просто модифицировать уже имеющееся.
Моя возня с VHDL не прошла даром, и через некоторое время я смог увидеть что-то давно и хорошо знакомое:
Да, там еще можно было заметить некоторые недоделки (типа сдвинутого на один символ атрибута), но в целом цветной текстовый режим 80×25 заработал так, как должен.
Следующим на очереди стоял контроллер прерываний 8259. Сначала возникла мысль попытаться использовать уже имеющийся из какого-то проекта, но ни один из них мне по разным причинам не понравился (либо слишком примитивные, либо, наоборот — я не понимал, как они работают, а документация отсутствовала). Была даже попытка купить коммерческую IP (в данном случае IP это не Internet Protocol, а Intellectual Property), но производители не хотели заморачиваться с продажей целой одной штуки…
В конечном итоге пришлось взяться за листик бумаги и набросать нечто типа (блок)схемы контроллера, которую потом начал реализовывать на VHDL. За полной совместимостью не гнался — мне нужна была (на данном этапе) поддержка одного основного режима приоритетных прерываний, возможность маскировать прерывания (также читать маску прерываний) и выполнять команду EOI (End Of Interrupt). На мой взгляд, этого должно быть достаточно, чтобы подавляющее большинство программ с этим нормально работали. Забегая вперед, скажу, что и по настоящий день я не обнаружил ни одной программы, которая пыталась бы сделать с контроллером прерываний что-то свыше заложенной мною функциональности.
Наверное, контроллер прерываний был моим первым настоящим (пускай и маленьким) проектом на VHDL — от начала и до конца. Писал я его тщательно, не поленился даже (опять таки впервые в своей жизни) сделать test bench (не уверен, как правильно перевести на русский — фактически, последовательность сигналов для проверки правильности функционирования устройства). Моделирование в симуляторе ModelSim показало вроде полную работоспособность контроллера, после чего из него был сгенерирован очередной графический символ и добавлен в мое устройство.
Нормального таймера 8254 у меня еще не было, для генерации прерываний 18.2 Гц использовался обычный счетчик, который я и подключил к контроллеру прерываний. Поведение компьютера показало, что вроде все работает — DOS загрузился без необходимости нажимать на клавишу, а в NC наконец-то пошли часы. Казалось, пройден очередной этап, и можно смело двигаться дальше.
Как оказалось, рано я радовался — в этот момент обнаружилась, пожалуй, самая большая проблема во всем проекте. Если кто помнит, у NC есть встроенная экранная заставка — «звездное небо». Оставив мой компьютер на некоторое время, после возвращения к нему я обнаружил, что звезды на заставке почему-то застыли, проще говоря, компьютер завис. Хотя я понимаю, что таких случайностей не бывает, мне все-таки хотелось верить в чудо — в то, что это единичный случай. К сожалению, как всегда, чуда не случилось — после полного сброса и перезагрузки компьютер снова подвис после часа или около того работы. Стало однозначно понятно, что где-то есть проблема, причем очень труднонаходимая.
Чтобы максимально сузить круг поиска, я написал простейший тест памяти, который запускался сразу после сброса процессора, без инициализации всех ненужных устройств типа таймера и т.д. В принципе, индикацию ошибки памяти я воспринял с облегчением — по крайней мере, проблема была явно в железе. Осталось дело за малым — понять, в каком именно месте. И вот с этим оказалось в