<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[ПЭВМ "Агат" 7-9: Форум &mdash; Мост # 3 - специализированный компьютер для чтения дискет]]></title>
		<link>https://forum.agatcomp.ru//viewtopic.php?id=390</link>
		<atom:link href="https://forum.agatcomp.ru/extern.php?action=feed&amp;tid=390&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние сообщения в теме «Мост # 3 - специализированный компьютер для чтения дискет».]]></description>
		<lastBuildDate>Sun, 19 Jan 2025 11:19:45 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7798#p7798</link>
			<description><![CDATA[<p>Episode 2</p><p>Понемногу тестируем используя два экземпляра моста3.</p><p>Вносятся некоторые правки, в основном касающиеся web-интерфеса и немного затрагивающие программы, непосредственно работающие с дисками.</p><p>Есть пара-тройка больших достижений:</p><p>1) Дописана прога замера скорости 140кб.&nbsp; Её особенность в том, что так как индекса на этом приводе нет, приходится читать магнитную запись и делать измерение по ней. Т.е. декодировать адресные поля, но сохраняя при этом первоначальные тайминги читаемого с поверхности потока. Цепочка декодеров, которая используется в остальном софте, так делать не умеет. Поэтому для 140ки я дописал сложнейший код (как я думал, поэтому долго его откладывал) размеров 80 строк и 3 процедуры. Одна процедура собирает биты из потока, вторая складывает их в байты и если находит пролог адресного поля, сразу декодирует его (там FM-кодирование), вытягивая номер трека и номер сектора. И третья процедура запускает дважды вторую, находя повтор какого нибудь адресного поля, после чего замеряет время между первой и второй копией одного и того же адресного поля.</p><p>2) Вроде бы выстроилась в конкретный код идея о ручном съёме. Это когда пользователь задаёт диапазон дорожек и формат, а дальше софт - уже с этими знаниями, без обзорного прохода - пытается ковырять отдельные дорожки в соответствии с заданным форматом.<br />Сложность тут была в том, чтобы переиспользовать код дочитывания диска без кода обзорного прохода. Проще говоря: декодеры используют много параметров (около 10) и надо как-то получить их от пользователя. Если с форматами вроде агатовских всё понятно: указал 140 или 840 и дальше ясно чего ожидать, то с PC нужна и плотность записи и битрейт и количество секторов и т.д. Ещё можно задать сложность автоподбора таблиц фазовой коррекции и подстройку скорости (битрейта). Это позволяет выбирать между более высокой скоростью и более надежным чтением.</p><p>3) Я покопался в коде линухового ядра, который умеет изображать USB-device. К сожалению, код, который умеет изображать накопитель данных (mass storage) завязан на понятие драйвера блочного устройства, т.е. по простому пробросить флопик через USB наружу не получится. Это надо, фактически, писать собственный блочный драйвер, который будет работать внутри ядра.<br />Зато удалось разобрать вопрос о пробросе IP over USB. Там куча протоколов и стандартов, которые заменялись в течение последних лет. В чём полезно для моста3: это позволит протащить web-интерфейс и его настройку через USB, минуя физический Ethernet и упростив, таким образом, подключение моста напрямую к компу (например, к ноуту на выезде). Естесно, с сохранением web-интерфейса.</p><p>2 и 3 пункты пока в процессе, но некоторые проведённые тесты обнадеживают.</p><p>==</p><p>Ошибок в коде читалок пока не находилось. До вчерашнего дня.<br />Ошибка интересная и я решил её описать.</p><p>Итак, мне передали коробку дисков с эпловскими игрушками с просьбой прочитать. Первый диск зашел успешно, с небольшим дочитыванием и без ошибок в итоге.</p><p>Второй читался тоже, но как-то неуверенно. Третий шел совсем плохо, выбранный наугад пятый - то же самое.<br />Интересно, что, судя по спектрам, там была вполне чистая GCR, может быть с некоторыми незначительными косячками (возможно, диск форматировали на одном дисководе, а потом записывали на другом). При этом проги не могли даже определить формат некоторых дорожек (т.е. им не удавалось декодировать ни одного сектора).</p><p>Я залез в поток глазками и обнаружил интересное: там были адресные поля обычного агатовского формата, но с какими-то нестандартными эпилогами. Сперва я предположил, что это какой-то вид защиты, но потом, полазив по остальным снятым образам, стал находить странные сочетания: часть треков имеют стандартные эпилоги, часть чередуются между стандартными и нестандартными, а часть имеют только стандартные сектора.</p><p>И, наконец, я заметил совсем странное: в потоке, сразу за эпилогом шел интересный байт со значением $80.</p><p>Такой байт не может возникнуть &quot;естественным&quot; путём, особенно у Моста3: больше двух нулей подряд запрещено по ограничению спектра дисковода и Мост3 даже не пытается сохранить такие тайминги.</p><p>Я сперва подумал о какой-то своей ошибке, но, покопавшись в коде, нашел свою же закладку: код декодера байтового потока умеет вставлять значение $80 в поток если к нему прилетел интервал, превышающий примерно 15 мкс. Фактически, это подсказка о том, что от дисковода слишком долго не было импульсов и получилось что-то вроде сбоя синхронизации.</p><p>Почему их не было ? Вероятно потому, что либо форматтер этих дисков выключал запись сразу после записи адресного поля и дисковод затирал несколько бит &quot;по инерции&quot; либо, в момент начала записи поля данных, также возникала пауза в битовом потоке</p><p>А почему это не поймали при отладке авторы драйвера или форматтера ? А потому что, очевидно, стандартные драйвера при чтении таких дисков не сталкивались с этой ошибкой.</p><p>Я залез в собственные статьи по работе 140-го контроллера и обнаружил, в чём тут дело:<br />стандартный контроллер 140ки имеет две фазы чтения байта: чтение первых двух бит (D7 и D6) и остальных бит.<br />Пока он ожидает прихода D7 очередного байта он игнорирует все входящие нули (независимо от их количества).<br />Но когда единичка D7 пришла, остальные биты он будет хватать с дисковода независимо от их количества и значения.</p><p>В моём коде же приход любого семпла с больше чем 2 нулями подряд ломал уже накопленные данные.<br />Когда я писал этот код я рассуждал так: если пришло больше двух нулей - то данные тут сломаны, байт нет смысла сохранять, нужно синхронизиваться заного.</p><p>Но именно при записи эпилога поля происходит следующее: записываются байт $DE, затем $AA, после чего в контроллер закидывается ещё один байт, после чего драйвер выключает режим записи. При этом запись последнего байта не гарантируется (а точнее - от него едва ли успеет записаться хотя бы один бит).</p><p>Второй байт ($AA) выглядит как 1010 1010 - дальше идёт неопределённость. Может быть дисковод при выключении записи дёрнет ток в записывающей головке, может быть нет. Может раньше может позже. Это не регламентируется.<br />Если после 1010 1010 мелькнёт ещё 11 или 01 или 10 - то мой код будет считать его нормальным новым байтом и байт $AA перейдёт от декодера 3 к декодеру 4 (который собирает из отдельных байт поля адреса и данных). Но если после 1010 1010 наступит тишина, то декодер уровня 3 увидит бесконечный ноль и просто заменит уже почти накопленный байт $AA (без одного бита) байтом $80. И уровень 4 не найдёт последний байт эпилога и забракует адресное поле.</p><p>Соответственно, я изменил логику таким образом: если накапливаемый байт не равен 0 (ноль означает, что байт уже ушел с уровня 3 на уровень 4 и мы ждём очередную единичку - т.е. байт как бы был прочитан ЦП из контроллера). то при получении бесконечного нуля на входе то, что накоплено в буфере байта, сдвигается максимально влево { while ((obuf &amp; 0x80) == 0) obuf &lt;&lt;= 1; } и отдаётся из уровня 3 на уровень 4, а уже потом в поток добавляется маркер $80 и тоже отдаётся на уровень 4. Он не нужен уровню 4, но он попадёт в промежуточный .eim-файл и, при разборе инцедентов, его можно будет увидеть в редакторе RawEdit.</p><p>PS Так как мост2 использует практически оригинальную схему эпловского дешифратора потока, он этой проблемме не подвержен.<br />Я проверил - он эти диски снял без проблем.</p><p>==</p><p>Интересность тут в том, что на своих дисках я не сталкивался с этой ошибкой.<br />Возможно потому, что мост2, которым записано большинство оставшихся у меня дисков, пишет трек за раз, не выключая запись, а те старые диски, которые были записаны ещё в конце 80-х, тоже писались копировщиком ИКП, который, возможно, тоже умеет писать трек за раз.<br />А может быть дело в дисководах. Может быть даже в экземпляре дисководов. Выключить запись, вероятно, можно по разному или в чуть разный момент. Если контроллер успевает выставить третий байт на запись - хотя бы один бит - ошибка бы уже не проявлялась.<br />Это может зависеть и от драйвера.</p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Sun, 19 Jan 2025 11:19:45 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7798#p7798</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7730#p7730</link>
			<description><![CDATA[<p>Я пока в себя прийти не могу. Как приду - отпишусь подробней. Но пока в шоке. </p><p>Очень странное чувство - вот вроде сегодня вычитывал диски, но при этом комп старый (у которого есть LPT) с антресоли не доставал, столик раскладной не подтаскивал, контроллер в мост2 не вставлял, MS-DOS не загружал.... и вообще почти ничего не делал. </p><p>Это еще что. Вставляю диск, а мост, хитрец, сам определяет от какой системы диск, и начинает читать, да еще и потом перечитывать сбойные места, если такие попались. </p><p>Хожу по квартире, занимаюсь своими делами, а мост3 сам всё делает, а диски менять его научить можно? :). И дружески так подмигивает мне лампочками. Надо сказать, что мы сразу поладили. </p><p>Жаль, что нельзя определять присутствие дисководов подключенных к мосту и в случае с правым (MFM) его тип (кол-во сторон, плотность), а как это РС делает?</p>]]></description>
			<author><![CDATA[null@example.com (garnizon)]]></author>
			<pubDate>Fri, 23 Aug 2024 19:05:06 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7730#p7730</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7685#p7685</link>
			<description><![CDATA[<p>Мне удалось решить баг с битой переменной only140 и это оказалось тоже довольно любопытной историей.</p><p>Суть её в том, что firefox имеет странное поведение, не совпадающее с Chrome, например.<br />Когда FF восстанавливает вкладки при открытии браузера (если выставлен соответствующий режим),<br />он восстанавливает также значения полей в формах. Но похоже, что при этом, в интерпретаторе javascript,<br />автоматически синтезируется событие onchange.</p><p>Получается странная коллизия: мы ещё не запросили (или не получили ответ, например, если Мост3 сейчас выключен) конфиг-файл с шаблонами и номерами имён образов, но уже вынуждены их сохранить (onchange автоматически вызывает процедуру сохранения).<br />Ну хорошо, допустим, FF всё вытащил из своих хранилищ и прощелкал по всем полям onchange и всё ушло на сохранение в Мост3. Но вот что интересно: текстовые поля ввода восстанавливаются и синтезируют соответствующие поля конфига в памяти JS правильно, но не поле checkbox. На него события нет. Получается, что на сохранение в устройство уходит файл без only140, который как раз является checkbox.</p><p>Тут немного обсуждают эту штуку:<br /><a href="https://stackoverflow.com/questions/30707306/firefox-firing-onchange-events-after-tab-restore">https://stackoverflow.com/questions/307 … ab-restore</a><br />и даже вроде как признали это багом, пока не исправили.</p><p>А тут оказывается, что вопрос про сохранение значений checkbox пока открытый:<br /><a href="https://wiki.mozilla.org/Session_Restore#Data_to_be_saved">https://wiki.mozilla.org/Session_Restor … o_be_saved</a></p><p>---</p><p>А когда-то мне довелось (в другом проекте) довольно долго бороться с тем, что некоторые браузеры пытаются даже синтезировать некоторые поля форм там, где этого совсем не требуется. Однажды у меня браузер решил, что очередное поле ввода является логином пользователя. И упорно делал автозаполнение. Попытки запретить autocomplete аттрибутом поля ни к чему не привели, также как и попытки явно задать значение поля пустым.<br />пришлось поставить таймер на одну секунду и через секунду после загрузки страницы, когда браузер уже выполнил автозаполнение, очищать поле.</p><p>===</p><p>На этом разработка Мост3 завершена.<br />В первое сообщение темы добавил все ссылки на видео и user manual.</p><p>Дальнейшее покажет опытная эксплуатация.</p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Sun, 30 Jun 2024 01:12:40 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7685#p7685</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7674#p7674</link>
			<description><![CDATA[<p>Восьмая часть, завершение первого сезона:<br /><a href="https://youtu.be/3E0ZFm4zCFs">https://youtu.be/3E0ZFm4zCFs</a></p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Mon, 27 May 2024 17:23:06 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7674#p7674</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7673#p7673</link>
			<description><![CDATA[<p>Супер! 👍</p>]]></description>
			<author><![CDATA[null@example.com (avivanov76)]]></author>
			<pubDate>Fri, 24 May 2024 20:31:00 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7673#p7673</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7672#p7672</link>
			<description><![CDATA[<p>Кажись, я понял почему программа &quot;Скорость&quot; иногда сообщала о 399 мс на оборот.</p><p>Время измеряется подсистемой GPIO, которая в ядре линуха. Она сообщает о времени (некоем внутриядерном) изменения уровня на пине. Т.е. что-то вроде очереди событий, каждое из которых содержит таймштамп и описание события (RISING/FALLING). На первый взгляд было бы логично, если бы эти события выстраивались в какой нибудь FIFO и оттуда выгребались программой.</p><p>Но, похоже, авторы этой подсистемы решили, что FIFO - это скучно. И не ясно - какого размера его делать ? Или вообще динамически выделять память (если программа сейчас ничего не читает - на сколько может распухнуть такой стек буквально за секунды, если частота воздействия на лапку будет, например, 100 КГц&nbsp; ?)<br />Поэтому они сделали по простому: сообщают только время последнего события каждого типа.<br />Т.е. не успел выгрести из очереди очередной импульс - получишь тот, который успеешь. Т.е. последний. Сколько пропустил - неизвестно.</p><p>Проблема в том, что в зависимости от того, какая страница включена на OLED, отрисовка идёт либо в память ui_server либо отправляется ещё и на физический OLED. Ну и, возможно, какой-то ещё вывод на интерфейсы требует некоторого времени. Плюс опрос клавиатуры на плате (которая не умеет работать по прерываниям). В сумме это занимает где-то те же 200 мс. Т.е. иногда импульсы просто терялись.</p><p>Решение: сперва запрашиваем новые события без ожидания. Если ОС сразу вернула что-то, то ставим флаг enable_io = 0 и выполняем всю математику и учёт, но всевозможные printf предваряем конструкцией &quot;if (enable_io)&quot;.</p><p>Если же ОС ничего не вернула по запросу, тогда уже повторяем запрос, но с разрешенным ожиданием события в 250 мс. И сразу ставим enable_io = 1.</p><p>Т.е. любые события учитываются (число оборотов, средняя скорость и т.д.), но если явно не успеваем между !INDEX, то не отображаем очередной шаг пользователю.</p><p>И оно заработало нормально :)<br /></p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName('div')[1],b=this.getElementsByTagName('span')[0];if(a.style.display!=''){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)['backgroundColor'];if(d=='transparent'||d=='rgba(0, 0, 0, 0)')d=e;c=c.parentNode;}a.style.display='';a.style.backgroundColor=d;b.innerHTML='&#9650;';}else{a.style.display='none';b.innerHTML='&#9660;';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>Spoiler</div><div style="padding: 6px; margin: 0; display: none;"><p>The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;10 turn(s), instant time 199.55 ms, average time 199.5376 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;11 turn(s), instant time 199.54 ms, average time 199.5378 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;13 turn(s), instant time 199.54 ms, average time 199.5379 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;15 turn(s), instant time 199.54 ms, average time 199.5385 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;17 turn(s), instant time 199.56 ms, average time 199.5400 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;19 turn(s), instant time 199.54 ms, average time 199.5393 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;21 turn(s), instant time 199.55 ms, average time 199.5394 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;22 turn(s), instant time 199.54 ms, average time 199.5394 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;24 turn(s), instant time 199.55 ms, average time 199.5396 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;25 turn(s), instant time 199.54 ms, average time 199.5396 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;27 turn(s), instant time 199.54 ms, average time 199.5397 ms<br />The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;29 turn(s), instant time 199.54 ms, average time 199.5400 ms<br /></p></div></div>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Fri, 24 May 2024 16:50:23 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7672#p7672</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7671#p7671</link>
			<description><![CDATA[<p>Ну да, верно: если нужно как-то обработать исключения уровня &quot;доступ к памяти запрещён&quot; или что-то такое, то нужен свой обработчик сигналов.</p><p>В моём случае я использовал sigprocmask(). Это маскировка сигналов, причем пришедший сигнал не отбрасывается, он запоминается как необработанный. Как только маскировка снимается, сигнал будет обработан (в моём случае - обработчиком по умолчанию).</p><p>Т.е. полностью f1() у меня выглядит так:</p><div class="codebox"><pre><code>void ui_client(char * msg_in, char * msg_out, int msg_out_max) {
  int r;

/* Так как вход в этому процедуру закрывается семафором, не следует допускать возможность её прерывания.
   Иначе семафор не будет открыт, а повторый вызов этой же функции из процедур группы atexit() приведёт чему нибудь плохому */
  sigset_t curset, oldset;
  sigemptyset(&amp;curset);
  sigaddset(&amp;curset, SIGINT);   // Ctrl-C, # 2
  sigaddset(&amp;curset, SIGTERM);  // kill, # 15
  r = sigprocmask(SIG_BLOCK, &amp;curset, &amp;oldset);
  fatal(r, &quot;UI client: sigprocmask set&quot;);
    
  r = pthread_mutex_lock(&amp;io_mutex);
  fatal(r, &quot;UI client: lock semaphore&quot;);

  ui_client_int(msg_in, msg_out, msg_out_max);
  
  r = pthread_mutex_unlock(&amp;io_mutex);
  fatal(r, &quot;UI client: unlock semaphore&quot;);

  r = sigprocmask(SIG_SETMASK, &amp;oldset, NULL);
  fatal(r, &quot;UI client: sigprocmask restore&quot;);
}</code></pre></div><p>-----</p><p>Когда оставлял на выходные мост в доступе, предупреждал, что диск в 840ке долго обрабатывается.<br />Я думал дело&nbsp; в тормозящей флешке, но проанализировал снятые образы и нашел причину:<br />позиционер флопика иногда пропускал шаг головки (причём только от края к центру, как правило где-то в районе нулевого трека). Странно, он это любит делать при недостатке питания, но сейчас он был воткнут напрямую в БП, даже минуя плату моста.</p><p>Дальше было так: всё это мост пытается декодировать, но получает от декодера ошибки. Соответственно, логика управления декодерами пытается перебирать параметры, настройки. Трек же не прочитался нормально, а что именно ненормально - её не беспокоит (обычно 1-2 сектора, залетевшие с чужого трека, особенно на 140-ке, особенно при программном &quot;покачивании&quot; головки - явление вполне привычное).</p><p>Но где-то в районе 30 цилиндра, по программе, есть обязательная рекалибровка. После неё голова встаёт на своё место и дальше все треки декодируются быстро. А поскольку после пробега от 0 к 160-му цилиндру идёт ещё пробег обратно, то младшие цилиндры читаются снова на обратном проходе. Логика декодеров видит, что они прочитались успешно и, следовательно, обзорного прохода достаточно.</p><p>В результате программа резюмирует &quot;всё успешно прочиталось, ошибок нет, дочитывание не требуется&quot;.<br />А почему она тогда так тормозила и не узнаешь, пока не копнёшь поглубже.</p><br /><p>Тут как раз тот случай, когда проще перечитать, чем тратить процессорное время на попытки выковырять то, чего нету. Но и &quot;не шагающий&quot; дисковод (кстати - трёхдюймовка) - тоже явление редкое... Надо ли тут что-то менять в коде ?</p><p>Встроить какой-то быстрый обзор после чтения каждого трека и в случае, если больше половины адресных полей &quot;не оттуда&quot; - перечитывать трек ?..... Пока не буду. Посмотрим позднее, по итогам эксплуатации.</p><p>---</p><p>Доделал последний кусок в web-интерфейсе: удаление снятых образов. Заодно там же сделал возможность скачать образ или группу сразу в виде zip-архива. Не быстро (проца не хватает, чтобы быстро), но где-то 1 МБайт/секунду качается (компрессия налету, самая лёгкая, примерно 3:1). По FTP можно без компрессии можно забирать где-то 10 МБайт/секунду.</p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Fri, 24 May 2024 06:10:09 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7671#p7671</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7670#p7670</link>
			<description><![CDATA[<div class="quotebox"><cite>Voldemar0 пишет:</cite><blockquote><p>Речь об этом ?</p></blockquote></div><p>Да, об этом. Тут проблема в том, что сбои в f2 могут быть устранимые, типа деления на ноль, нехватки памяти, отсутствия файла и т.д. А могут быть неустранимые, например, переполнение стека, обращение к недопустимому адресу памяти или получение внешнего сигнала.<br />И если устранимых сбоев можно избежать, обложив весь код проверками, то со вторыми ничего поделать нельзя. Во всяком случае в C.<br />Кроме того, еще же есть библиотечные вызовы. Свою функцию можно как-то защитить проверками, а когда код чужой, то не факт, что его разработчик сделал все по фен-шую и вызов не уронит всю программу.</p><p>Мне кажется, стоит попробовать sigaction <a href="https://stackoverflow.com/questions/33848558/catching-sigterm-in-c">https://stackoverflow.com/questions/338 … gterm-in-c</a></p>]]></description>
			<author><![CDATA[null@example.com (avivanov76)]]></author>
			<pubDate>Thu, 23 May 2024 17:42:26 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7670#p7670</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7669#p7669</link>
			<description><![CDATA[<p>Чистый C.<br />Я не знаю внутренности LibC, но, судя по поведению прог, сигнал прерывает выполнение любого кода в любом месте и, даже если я напишу какую-то обёртку, она не будет выполнена дефолтным обработчиком.</p><p>Возможно, в C++ ставится свой обработчик сигнала в RTL, который может позволить выполнится finally, а уже после этого завершает программу. Т.е. сперва он прерывает текущий код, потом, используя какой-то стек текущих finally, передаёт управление на них и потом уже завершает прогу.</p><p>&gt; Я просто не представляю, как обработку управления ресурсами делать без try finally.</p><div class="codebox"><pre><code>f2() {
  action1();
  if action2() &lt; 0 then return; // сбой на каком-то этапе
  action3();
  // успешно всё выполнили
}

f1() {
  allocate(что нибудь);
 f2();
  free(что нибудь)
}</code></pre></div><p>Речь об этом ?</p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Thu, 23 May 2024 02:21:53 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7669#p7669</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7668#p7668</link>
			<description><![CDATA[<div class="quotebox"><cite>Voldemar0 пишет:</cite><blockquote><p>И вот тут возникала коллизия:<br />если сигнал прерывает процедуру обмена с ui-server остаётся заблокирован семафор обращения к ui-серверу.</p></blockquote></div><p>Хм. А код на чистом C написан? Я просто не представляю, как обработку управления ресурсами делать без try finally.<br />Когда есть блок finally, то он в любом случае выполняется и в нем можно разблокировать все ранее заблокированное.<br />Но для этого нужен C++.</p><p>Вроде есть какие-то костыли для эмуляции try catch <a href="https://stackoverflow.com/questions/10586003/try-catch-statements-in-c">https://stackoverflow.com/questions/105 … ments-in-c</a>, но сам я этого не пробовал.</p>]]></description>
			<author><![CDATA[null@example.com (avivanov76)]]></author>
			<pubDate>Wed, 22 May 2024 22:28:24 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7668#p7668</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7667#p7667</link>
			<description><![CDATA[<p><em>+ ! рекурсивный вызов ui_client ! понятно почему, не ясно как избежать</em></p><p>Тоже любопытный программный случай.</p><p>Обычно программу можно остановить какой нибудь комбинацией клавиш или пунктом в меню. Ну или нажать крестик и закрыть окно. Это - мягкая остановка, программа просто получает сообщение/событие о том, что нужно закончить работу.</p><p>Но если прога зависла, мы идём в Диспетчер Программ и там нажимаем &quot;завершить&quot;. В этом случае сперва программа также получает мягкое предупраждение, но затем ОС может закрыть её и принудительно.</p><p>В *NIX этот механизм я знаю немного лучше. Там есть понятие &quot;Сигнал&quot; - это некое асинхронное сообщение, которое одна программа или ядро ОС может передать другой программе. Но отличие &quot;сигнала&quot; от прочих механизмов в том, что ещё до запуска программы операционка (или LibC ?) уже предоставляет программе обработчики сигналов. Причем некоторые сигналы программа не может игнорировать совсем, а на другие может повесить свой обработчик. Больше половины обработчиков по умолчанию завершают работу программы.</p><p>Это немного напоминает агатовский УПР-СБР: по умолчанию его проигнорировать сложно, но можно попробовать сменить заранее вектор, тогда шанс есть.</p><p>Есть довольно любопытные сигналы, например SIGSTOP - полностью приостанавливает программу. Совершенно не спрашивая её согласия :) Потом можно вновь разрешить работать, послав сигнал SIGCONT. Мне в винде этого, порой, не хватает (Какой-то процесс выжрал все рессуры, но закрывать его не хочется, а нужно вот сейчас сделать другую работу. Что делать?).<br />А ещё SIGKILL - классный сигнал безусловного мгновенного закрытия программы. Программа не может его игнорировать.</p><p>Так вот в Мосту3 есть два способа завершить программу:</p><p>1) Нажать на плате большую кнопку старт-стопа или в web-интерфейсе нажать &#039;Q&#039;. Это мягкое завершение - программа получает сообщение о том, что пользователь просит её завершится и завершает работу, когда&nbsp; &nbsp;ей будет удобно и как ей будет удобно.</p><p>2) Нажать в web-интерфейсе кнопку запуска проги. Если прога уже запущена, ей будет отправлен сигнал завершения SIGTERM. Это сигнал мягкий - прога может его игнорировать. Но, по умолчанию, обработчик этого сигнала завершает программу. Этот вариант нужен, например, на случай зависания программы.</p><p>Поскольку прога может обработать этот сигнал, она не закрывается сразу. Прерывается текущая исполняемая процедура (сигналы - механизм асинхронный!), после чего, кроме прочего, вызывается группа процедур atexit. В эти процедуры складываются всевозможные действия, которые программист считает нужным выполнить независимо от причин, по которым программа завершает работу. В случае моста3 там собраны всевозможные сообщения (на web-интерфейс, на дисплейчик платы, на кнопки с подсветкой) о завершении программы.</p><p>И вот тут возникала коллизия:<br />если сигнал прерывает процедуру обмена с ui-server остаётся заблокирован семафор обращения к ui-серверу. Он должен разблокироваться при завершении процедуры, но её прервали и код завершения не выполнен. А atexit нужно обратится к ui-server&#039;у, чтобы сообщить о завершении проги. И он лезет в ту же процедуру, попадает на закрытый семафор и .. всё зависает. Открыть семафор некому.<br />В процессе поиска истоков проблемы я поменял тип семафора, чтобы в такой ситации он не блокировал всё, а сообщал об ошибке. Но саму проблему это не решает: сообщение о завершении работы пользователю не отправлено.</p><p>Отказываться от семафора нежелательно: он разводит во времени обращения к ui-серверу из разных тредов проги. Не то, чтобы у меня использовалось много тредов, но где-то они могут быть и на всякий случай семафор я использую.</p><p>Пришлось засесть за статьи по менеджменту сигналов. Важно не просто запретить прерывание обращения к ui-server, но и не запретить его во всех остальных случаях. Прога же может попасть в какой-то бесконечный цикл в декодерах, где-то ещё, она не будет проверять при этом мягкие просьбы из п1. И важно, чтобы хотя бы по п2 её можно было завершить.</p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName('div')[1],b=this.getElementsByTagName('span')[0];if(a.style.display!=''){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)['backgroundColor'];if(d=='transparent'||d=='rgba(0, 0, 0, 0)')d=e;c=c.parentNode;}a.style.display='';a.style.backgroundColor=d;b.innerHTML='&#9650;';}else{a.style.display='none';b.innerHTML='&#9660;';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>Spoiler</div><div style="padding: 6px; margin: 0; display: none;"><p>Для тех, кто запутался в семафорах и процедурах :)<br />Пришла в голову простая аналогия:<br />Есть ж/д депо. В него может заехать только один паровоз. У всех паровозов есть уникальные номера. На въезде в депо стоит чувак в форме и если депо свободно, он открывает семафор. Если депо занято - закрывает семафор. Но чувак в форме сообразительный и если вдруг депо занято паровозом N 5, а на въезде стоит ещё один паровоз N 5, чувак бежит к телефону и сообщает об ошибке в системе.<br />Либо просто держит семафор закрытым. Это зависит от должностной инструкции.</p><p>Почему он может увидеть тот же самый паровоз, который хочет заехать, но ещё не выезжал ? Потому что у депо есть второй выезд и в особых исключительных случаях паровоз может выехать через него. Исключительный случай - тот самый асинхронный SIGNAL.<br /></p></div></div>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Wed, 22 May 2024 14:21:04 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7667#p7667</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7666#p7666</link>
			<description><![CDATA[<p>О паре заметок в ToDo:</p><p><em>&quot;? глюки у дисплейчика ?&quot;</em></p><p>Мелкий дисплейчик на плате начал подглючивать: картинка иногда ломалась. Сперва лениво кивал на глюки самого дисплейчика, но сейчас на тестах две платы и когда глюки стали стабильными, причём на обеих платах, пришлось занести это в ToDo.</p><p>Сперва поискал вопрос об аппаратном сбросе, нашел его на плате дисплейчика, но он не выведен на гребёнку. Просто на плате есть RC-цепочка сброса и всё. А программного сброса в даташите не нашлось (ssd1306).<br />Но раньше же глюков не было ?</p><p>Поиски начал со здорового электролита, напаянного прямо на ноги дисплейчика. Не помогло.<br />Потом осцилографом порылся по шине i2c - там вполне прямоугольники, да и частота шины не высокая - 92 КГц примерно.<br />Потом был логический анализатор шины. Тут бага и нашлась. Но какая!</p><p>Контроллер дисплейчика умеет принимать растр либо команды по шине.<br />Чтобы отличать одно от другого имеется префиксный байт. Четыре варианта: команда в один байт, растр в один байт, команды до условия I2C-STOP, растр до этого же условия.</p><p>В драйвере (самописном) есть три процедуры: отправить растр (несколько байт), отправить команду (1 байт), отправить команду (массив). Команды бывают как из одного байта так и из нескольких.</p><p>Многие команды имеют размер 1 байт. Я их все отправлял процедурой отправки однобайтной команды.<br />Но когда был реализован основной функционал, я добавил команду управления яркостью дисплейчика. Она содержит два байта. И по привычке я их отправлял как две однобайтовых команды (copy-paste ж). И - что интересно - контроллер дисплея это вполне переваривал, несмотря на то, что в таком случае два байта (опкод + уровень яркости) приходили в отдельных I2C-транзакциях.</p><p>Яркость можно менять резко (просто задать любую нужную) или плавно крутить цикл от минимальной к максимальной или назад. Программно крутить, конечно. Я так и сделал. А поскольку это занимает около секунды, вынес эту операцию в отдельный тред - чтобы не задерживать исполнение других команд.<br />Пока всё было хорошо.</p><p>Но, когда дело шло к завершению разработки, я внёс запуск шелла в автозагрузку. И получилось следующее: рисуется заставка, экранчик плавно зажигается до максимума и затем плавно снижает яркость до минимума (но картинку ещё видно). Ещё до того, как игры с яркостью завершились, уже запускается шелл и начинает рисовать меню.<br />И вот тут команды управления курсором (от шелла) начинают смешиваться с командами управления яркостью (из скриптов ЛОГО): ЯРКОСТЬ.ОПКОД ... КУРСОР.КООРДИНАТА&nbsp; ... ЯРКОСТЬ.ЗНАЧЕНИЕ.</p><p>Естесно, контроллер дисплея сходил с ума - координата становилась яркостью, яркость - недокументированным опкодом.</p><p>Решение же простейшее: отправлять команду яркости как одну двухбайтовую команду. Тогда она не может быть разорвана на шине.</p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Wed, 22 May 2024 13:53:10 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7666#p7666</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7665#p7665</link>
			<description><![CDATA[<div class="quotebox"><cite>Voldemar0 пишет:</cite><blockquote><p>to (у которого куча значений направления, включая и &quot;для&quot;)</p></blockquote></div><p>To еще используется для обозначения результата действия или процесса. У for такого значения нет.</p><div class="quotebox"><cite>Voldemar0 пишет:</cite><blockquote><p>Я имел ввиду: &quot;Это успешное завершение.&quot;</p></blockquote></div><p>Я вот думаю - это все какие-то расплывчатые формулировки: &quot;успешное&quot;, &quot;хорошее&quot;. Может, указать конкретный результат, типа &quot;образ создан/не создан&quot;?</p><div class="quotebox"><cite>Voldemar0 пишет:</cite><blockquote><p>It&#039;s final. But I couldn&#039;t detect a main format :((</p></blockquote></div><p>Немного выбивается из общего стиля. В остальных фразах местоимений нет, а тут вдруг &quot;Я&quot;.<br />Предлагаю так: </p><p>The disk image has not been created. The main format could not be detected.</p><p>И остальные фразы (passive voice тут обязателен, потому что не &quot;формат определил&quot;, а &quot;формат был определен&quot;):</p><p>The disk image has been created. The main format was detected and read without errors.<br />The disk image has been created. The main format was detected, but %d error(s) were encountered while reading.</p>]]></description>
			<author><![CDATA[null@example.com (avivanov76)]]></author>
			<pubDate>Sat, 11 May 2024 21:08:37 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7665#p7665</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7664#p7664</link>
			<description><![CDATA[<p>Английский поправлю, спасибо!<br />Для меня использование for вместо by или to - не редкость. Я не совсем понимаю разницу между for (для ?) и to (у которого куча значений направления, включая и &quot;для&quot;)...</p><p>It is successfull final.<br />Я имел ввиду: &quot;Это успешное завершение.&quot;</p><p>В программе есть ещё два варианта:<br />It&#039;s final. But I couldn&#039;t detect a main format :((<br />It is successfull final. The main format detected and read without errors.<br />It is good final. The main format detected, but %d error(s) encountered while reading.</p><p>--</p><p>По прогам:</p><p>&gt; Config variable only140 not found !</p><p>Это мне пару раз встречалось - ошибка странная, я пока не понял, в каких случаях она возникает.<br />В файле конфига нет одной переменной (или почему-то её не находит процедура поиска).<br />Надо чинить.</p><p>Workaround: щёлкнуть галочку &quot;Использовать такой же шаблон для образов 840кб дисков:&quot;<br />Само значение галочки не важно, но при её изменении этот параметр пересоздаётся.</p><br /><p>&gt; Снятие образа 140К ведет себя так же.<br />Прога одна и та же (снятие 840 и 140), переменная тоже одна. Т.е. это та же ошибка.</p><p>&gt; &quot;примерная скорость&quot;</p><p>Это такая величина, которая измеряется при запуске привода 840 (для 140ки она - константа) и при смене уровня Density. Измеряется по сигналам индекса, но используется не для программы &quot;скорость&quot;, а для сохранения в eim3-файл, чтобы потом декодеры могли понимать, какую поправку на скорость чтения делать. Важно, что эта величина может быть только либо 300 либо 360.<br />Примерно так: if RealSpeed &gt; 330 then speed = 360 else speed = 300.</p><p>А дальше уже играет эта проблема:</p><p>&gt; The head is on the track&nbsp; &nbsp;0,&nbsp; &nbsp;36 turn(s), instant time &lt;b&gt;398.94&lt;/b&gt; ms, average time 210.5529 ms</p><p>И вот откуда эта штука возникает - я пока не понял. Выглядит так, как будто пропущен один index.<br />Вроде бы, это бывает не с любыми дисководами. Это надо будет проверять и искать причину.<br />Сейчас подключен привод 3.25&#039;&#039;, но бОльшую часть отладки я вел на разных 5.25&#039;&#039;. Вроде бы там не было этой проблемы.</p><p>&gt; Ошибка подключения к статусу 840кб</p><p>Это не совсем ошибка, тут причина вот в чём:<br />журналы и статусы - это просто файлы на RAM FS.<br />Т.е. после перезагрузки ОС они теряются. Пока не запустишь какую нибудь программу, журналов нет.<br />После запуска любой из прог журнал появляется.<br />Каждый новый запуск прог журнал переписываются.</p><br /><p>{<br />...можно создавать пустые журналы при запуске ОС...<br />Или с какой нибудь фразой, вроде &quot;Информации пока нет&quot;....<br />}</p>]]></description>
			<author><![CDATA[null@example.com (Voldemar0)]]></author>
			<pubDate>Sat, 11 May 2024 14:50:45 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7664#p7664</guid>
		</item>
		<item>
			<title><![CDATA[Re: Мост # 3 - специализированный компьютер для чтения дискет]]></title>
			<link>https://forum.agatcomp.ru//viewtopic.php?pid=7663#p7663</link>
			<description><![CDATA[<p>Пара мелких замечаний по английскому:<br /></p><div class="codebox"><pre><code>0.042 ms for move the head to the zero track</code></pre></div><p>Тут нужен предлог to: 0.042 ms <strong>to</strong> move</p><div class="codebox"><pre><code>It is successfull final.</code></pre></div><p>Тут пропущен артикль, плюс в слове successful только одна буква l. Но главное - фраза какая-то странная. Не понятно, а что тут по-русски должно было быть? Final - это последняя серия в сериале, последняя игра в турнире. А тут финал чего?<br />Если имелось в виду &quot;Обработка завершена успешно&quot;, то можно было написать &quot;Processing completed successfully&quot;.</p>]]></description>
			<author><![CDATA[null@example.com (avivanov76)]]></author>
			<pubDate>Fri, 10 May 2024 20:24:47 +0000</pubDate>
			<guid>https://forum.agatcomp.ru//viewtopic.php?pid=7663#p7663</guid>
		</item>
	</channel>
</rss>
