Передовица » Hardware » ДЗУ » Исследование КНГМД 140 » Программы секвенсора

Программы секвенсора

Язык программирования секвенсора

Синтаксис был предложен Jim Sather, я лишь слегка его дополнил. Всего имеется шесть команд:

  • NOP - нет операции;
  • CLR - очистка аккумулятора;
  • SL0 - сдвиг аккумулятора влево, самый младший бит становится равным 0;
  • SL1 - то же, но младший бит - 1;
  • SR - сдвиг аккумулятора вправо, самый старший бит считывается с линии "защита записи";
  • LD - параллельная загрузка аккумулятора с шины данных ЦПУ.

Команды кодируются четырьмя битами, любая комбинация бит представляет собой некоторую команду, но часть комбинаций имеют идентичное действие, поэтому отдельных команд всего шесть.

За мнемоникой команды следует знак "->" и шестнадцатеричная цифра - адрес следующей команды: указатель команд секвенсора не является счётчиком и очередное его значение берётся из кода текущей команды.

Указатель команд имеет четыре бита, всего ПЗУ содержит 256 команд. Поэтому ещё четыре бита - это:

  1. Защёлка "C/D" - в листингах обозначена как "CD".
  2. Защёлка "Write" - она условно разделяет процедуры.
  3. Инвертированные данные чтения.
  4. Старший бит аккумулятора.

Таким образом, адрес очередной команды формируется как указатель команд (т.е. значение, считанное из текущей команды) и четыре бита данных - при их изменении меняется и поток исполнения: программа "прыгает" между своими ветками - нечто вроде условного перехода.

Процедуры секвенсора

К описанному выше добавим ещё один синтаксический элемент: "XX>XX". Здесь XX - шестнадцатеричные числа - это адрес и хранимое по нему значение в реальном ПЗУ Агата, соответствующее данной команде. Для Эпл-листингов не приводятся.

В тексте рассматривается три версии контроллера, однако только процедуры чтения данных различаются. Остальные - идентичны. С них и начнём.

Процедура чтения флажка защиты записи

Все ветки этой процедуры заполнены единственной командой "SR -> 0". Таким образом, она очень быстро заполняет все биты аккумулятора флажком защиты записи, причем первоначально он появляется именно в старшем разряде. Отсюда и рекомендации мануала - следить за D7, хотя, если дать секвенсору всего несколько тактов, он заполнит вполне корректным значением весь байт. Одновременно происходит сброс указателя команд на 0.

Процедуры захвата байта с шины ЦПУ и сдвига байта в линию записи

Процедура состоит из двух половин, которые выбираются в зависимости от защелки "C/D", причем каждая из них разделена на две части старшим битом аккумулятора.

Первая половина - CD = 0 - сдвиг аккумулятора на каждый 8-й такт. Её части отличаются только адресом перехода на шаге 7 и 15: в зависимости от D7 происходит или не происходит изменение старшего бита указателя адреса. Его значение, в итоге, и уходит на дисковод как данные записи.

Вторая половина - CD = 1 - загрузка аккумулятора с шины ЦПУ. Её части имеют такое же отличие, но это не имеет особого значения, т.к. драйвер, сразу после записи данных в аккумулятор, переключает CD в состояние 0 - ещё до достижения адресов 7 или 15.

      -------------- CD=0 --------------- | -------------- CD=1 --------------- |
ADR   ------D7=0------ | ------D7=1------ | ------D7=0------ | ------D7=1------ |
0 -   08>88 : NOP -> 1 | 0A>88 : NOP -> 1 | 0C>88 : NOP -> 1 | 0E>88 : NOP -> 1 |
1 -   88>48 : NOP -> 2 | 8A>48 : NOP -> 2 | 8C>48 : NOP -> 2 | 8E>48 : NOP -> 2 |
2 -   09>C9 : SL0 -> 3 | 0B>C9 : SL0 -> 3 | 0D>CB : LD  -> 3 | 0F>CB : LD  -> 3 |
3 -   89>28 : NOP -> 4 | 8B>28 : NOP -> 4 | 8D>28 : NOP -> 4 | 8F>28 : NOP -> 4 |
4 -   48>A8 : NOP -> 5 | 4A>A8 : NOP -> 5 | 4C>A8 : NOP -> 5 | 4E>A8 : NOP -> 5 |
5 -   C8>68 : NOP -> 6 | CA>68 : NOP -> 6 | CC>68 : NOP -> 6 | CE>68 : NOP -> 6 |
6 -   49>E8 : NOP -> 7 | 4B>E8 : NOP -> 7 | 4D>E8 : NOP -> 7 | 4F>E8 : NOP -> 7 |
7 -   C9>08 : NOP -> 0 | CB>18 : NOP -> 8 | CD>08 : NOP -> 0 | CF>18 : NOP -> 8 |
8 -   28>98 : NOP -> 9 | 2A>98 : NOP -> 9 | 2C>98 : NOP -> 9 | 2E>98 : NOP -> 9 |
9 -   A8>58 : NOP -> A | AA>58 : NOP -> A | AC>58 : NOP -> A | AE>58 : NOP -> A |
A -   29>D9 : SL0 -> B | 2B>D9 : SL0 -> B | 2D>DB : LD  -> B | 2F>DB : LD  -> B |
B -   A9>38 : NOP -> C | AB>38 : NOP -> C | AD>38 : NOP -> C | AF>38 : NOP -> C |
C -   68>B8 : NOP -> D | 6A>B8 : NOP -> D | 6C>B8 : NOP -> D | 6E>B8 : NOP -> D |
D -   E8>78 : NOP -> E | EA>78 : NOP -> E | EC>78 : NOP -> E | EE>78 : NOP -> E |
E -   69>F8 : NOP -> F | 6B>F8 : NOP -> F | 6D>F8 : NOP -> F | 6F>F8 : NOP -> F |
F -   E9>18 : NOP -> 8 | EB>08 : NOP -> 0 | ED>18 : NOP -> 8 | EF>08 : NOP -> 0 |

Обе процедуры не зависят от значения данных чтения. Логично, не правда ли?

Процедура чтения данных (версия "Apple Disk 3.2")

Эта процедура зависит от D7 и данных чтения. Она разделена на две большие части: D7 = 0 - этот блок исполняется по мере считывания бит с пятого по нулевой и D7 = 1 - старший бит дополз до своего места и секвенсор даёт паузу, чтобы ЦПУ успел прочитать аккумулятор. Состояние линии чтения при этом продолжает анализироваться, но пока оно влияет только на указатель команд. Секвенсор сбросит аккумулятор только после того, как обнаружит "1" на линии чтения (т.е. read = 0: не забываем об инверсии входных данных) и отсчитает время для ещё одного бита. Получив его (или недождавшись) секвенсор сбрасывает аккумулятор, что сразу приводит к переходу в первую часть кода (D7 = 0), где выполняется SL1 и затем SL0 или SL1, в зависимости от значения считанного во второй половине значения. После чего продолжает ожидание остальных шести бит.

      -------------- D7=0 --------------- | -------------- D7=1 --------------- |
ADR   -----read=0----- | -----read=1----- | -----read=0----- | -----read=1----- |
0 -           NOP -> D |         NOP -> 1 |         NOP -> 1 |         NOP -> 0 |
1 -           NOP -> D |         NOP -> 2 |         NOP -> 2 |         NOP -> 2 |
2 -           NOP -> D |         NOP -> 3 |         NOP -> 3 |         NOP -> 3 |
3 -           NOP -> D |         NOP -> 4 |         NOP -> D |         NOP -> 4 |
4 -           NOP -> D |         NOP -> 5 |         NOP -> D |         NOP -> 5 |
5 -           NOP -> D |         NOP -> 6 |         NOP -> D |         NOP -> 6 |
6 -           NOP -> D |         NOP -> 7 |         NOP -> D |         NOP -> 7 |
7 -           NOP -> D |         NOP -> 8 |         NOP -> D |         NOP -> 8 |
8 -           NOP -> D |         NOP -> 9 |         NOP -> D |         NOP -> 9 |
9 -           NOP -> D |         SL0 -> 0 |         NOP -> D |         NOP -> A |
a -           SL1 -> C |         SL1 -> B |         NOP -> D |         NOP -> B |
b -           SL0 -> D |         SL0 -> 3 |         NOP -> D |         NOP -> C |
c -           SL0 -> D |         SL0 -> D |         NOP -> D |         CLR -> A |
d -           SL1 -> 1 |         SL1 -> 0 |         NOP -> E |         NOP -> E |
e -           SL1 -> F |         SL1 -> F |         NOP -> F |         NOP -> F |
f -           SL1 -> D |         SL1 -> 4 |         CLR -> E |         CLR -> E |

Важная особенность версии 3.2 - ожидание нулевого бита продолжается 10 тактов секвенсора. Эта версия расчитана на то, что подряд может идти только один нулевой бит. Т.е. считывание комбинации 1 0 0 будет выполнятся 30 тактов, в то время как три бита должны занимать лишь 24 такта. Набегание столь серьёзной ошибки приводит к тому, что реально такая процедура успешно читает, в лучшем случае, один сектор из шестнадцати - если на диске есть комбинации из двух нулей подряд. Эта версия использовалась с DOS до 3.2 включительно и предполагала 13 секторов по 256 декодированных (т.е. после всех расшифровок контроллером и драйвером) байт на дорожке. Последовательность из двух нулей не допускалась.

Процедура чтения данных (версия "Агат", оба варианта контроллера)

      -------------- D7=0 --------------- | -------------- D7=1 --------------- |
ADR   -----read=0----- | -----read=1----- | -----read=0----- | -----read=1----- |
0 -   00>B8 : NOP -> D | 10>88 : NOP -> 1 | 02>88 : NOP -> 1 | 12>08 : NOP -> 0 |
1 -   80>B8 : NOP -> D | 90>48 : NOP -> 2 | 82>C8 : NOP -> 3 | 92>C8 : NOP -> 3 |
2 -   01>B8 : NOP -> D | 11>C8 : NOP -> 3 | 03>88 : NOP -> 1 | 13>48 : NOP -> 2 |
3 -   81>B8 : NOP -> D | 91>28 : NOP -> 4 | 83>B8 : NOP -> D | 93>28 : NOP -> 4 |
4 -   40>B8 : NOP -> D | 50>A8 : NOP -> 5 | 42>B8 : NOP -> D | 52>A8 : NOP -> 5 |
5 -   C0>B8 : NOP -> D | D0>68 : NOP -> 6 | C2>B8 : NOP -> D | D2>68 : NOP -> 6 |
6 -   41>B8 : NOP -> D | 51>E8 : NOP -> 7 | 43>B8 : NOP -> D | 53>E8 : NOP -> 7 |
7 -   C1>B8 : NOP -> D | D1>18 : NOP -> 8 | C3>B8 : NOP -> D | D3>18 : NOP -> 8 |
8 -   20>B8 : NOP -> D | 30>98 : NOP -> 9 | 22>B8 : NOP -> D | 32>98 : NOP -> 9 |
9 -   A0>B8 : NOP -> D | B0>49 : SL0 -> 2 | A2>B8 : NOP -> D | B2>58 : NOP -> A |
A -   21>3D : SL1 -> C | 31>DD : SL1 -> B | 23>B8 : NOP -> D | 33>D8 : NOP -> B |
B -   A1>B9 : SL0 -> D | B1>C9 : SL0 -> 3 | A3>B8 : NOP -> D | B3>38 : NOP -> C |
C -   60>B9 : SL0 -> D | 70>B9 : SL0 -> D | 62>B8 : NOP -> D | 72>50 : CLR -> A |
D -   E0>8D : SL1 -> 1 | F0>0D : SL1 -> 0 | E2>78 : NOP -> E | F2>78 : NOP -> E |
E -   61>FD : SL1 -> F | 71>FD : SL1 -> F | 63>F8 : NOP -> F | 73>F8 : NOP -> F |
F -   E1>BD : SL1 -> D | F1>2D : SL1 -> 4 | E3>70 : CLR -> E | F3>70 : CLR -> E |

Вот тут-то самое интересное: Агат использовал 16 секторов по 256 байт на дорожке и ДОС3.3 (я уже по русски её называю, поскольку она была адаптирована для Агата). Но прошивка секвенсора - почти точь-в-точь - как 3.2 Эпла. Отличия: второй столбец, адрес 9 - здесь возврат после SL0 происходит на адрес 2, а не 0 - т.е. после очередного нуля секвенсор ждет следующий ноль только 8 тактов, что несколько уменьшает ошибку. Некоторые отличия видны также в колонках 3 и 4.

Процедура чтения данных (версия "Apple Disk 3.3")

А дальше мы видим оригинальную прошивку Эпла для версии DOS 3.3 и соответствующего ей контроллера. Есть что-то общее с Агатом ? :) Буквы, вроде, знакомые... Эта прошивка также ждёт повторный ноль только 8 тактов, но в ней есть много других отличий. Анализировать их лень, хотя и интересно: эта прошивка даёт более стабильное чтение, чем агатовская. Иногда разница весьма существенна.

Чтобы хоть как-то объяснить сей феномен, я представил себе такую ситуацию: допустим, очередная единичка пришла не вблизи "своего" момента (тайм-слот - 8 тактов), а раньше - например, проскочила комбинация 1010 (это явная помеха, но что с ней делать?). Как поступят контроллеры (порядок колонок/адресов):

  • Агат: первая/2..8 -> вторая/D -> первая/0 -> вторая/D - и так до бесконечности - т.е. комбинация 1010 даст в аккумуляторе 0101.
  • Эппл 3.3: первая/2..8 -> вторая/D -> первая/0 -> вторая/1 и только теперь готовность принять следующий бит - т.е. в результате в аккумулятор попадёт только одна единичка.

Возможно, это не самая важная причина, но, полагаю, направление поиска найдено верно.

      -------------- D7=0 --------------- | -------------- D7=1 --------------- |
ADR   -----read=0----- | -----read=1----- | -----read=0----- | -----read=1----- |
0 -           NOP -> 1 |         NOP -> 1 |         NOP -> 1 |         NOP -> 1 |
1 -           SL1 -> 2 |         SL1 -> 2 |         NOP -> 3 |         NOP -> 3 |
2 -           NOP -> D |         NOP -> 3 |         NOP -> 0 |         NOP -> 2 |
3 -           NOP -> D |         NOP -> 4 |         NOP -> 4 |         NOP -> 4 |
4 -           NOP -> D |         NOP -> 5 |         NOP -> D |         NOP -> 5 |
5 -           NOP -> D |         NOP -> 6 |         NOP -> D |         NOP -> 6 |
6 -           NOP -> D |         NOP -> 7 |         NOP -> D |         NOP -> 7 |
7 -           NOP -> D |         NOP -> 8 |         NOP -> D |         NOP -> 8 |
8 -           NOP -> D |         NOP -> 9 |         NOP -> D |         NOP -> 9 |
9 -           NOP -> D |         SL0 -> 2 |         NOP -> D |         NOP -> A |
a -           SL1 -> C |         SL1 -> B |         NOP -> D |         NOP -> B |
b -           SL0 -> D |         SL0 -> 5 |         NOP -> D |         NOP -> C |
c -           SL0 -> D |         SL0 -> D |         NOP -> D |         CLR -> A |
d -           NOP -> D |         NOP -> 0 |         NOP -> E |         NOP -> E |
e -           SL1 -> F |         SL1 -> F |         NOP -> F |         NOP -> F |
f -           SL1 -> D |         SL1 -> 4 |         CLR -> E |         CLR -> E |

Использование материалов проекта agatcomp без получения предварительного письменного разрешения agatcomp запрещено.


Почта для обратной связи: mail@agatcomp.ru


Живое общение по теме Агата: Telegram группа Agatcomp.


Накопленные знания и проекты: тематический ФОРУМ.


© 2004-2024 agatcomp.su / agatcomp.ru

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *