Да, есть схема сравнения, и она действительно сравнивает все 16 бит. Но она как бы особняком стоит, и ее потом опишу, когда руки дойдут.
Глюк классный, не знал о нем)
Вид для печати
Да здесь тоже это видно. Особенность исполнения команды JSR. Из моего описания:
Алгоритм: tmp := dst; –(SP) := R; R := PC; PC := tmp
tmp := dst; –(SP) := PC; PC := tmp
Описание: Содержимое указанного регистра пересылается в стековую память и заполняется содержимым счётчика команд, который указывает на ячейку, следующую за командой JSR. Новое содержимое счётчика команд определяется адресом операнда. Так как регистр R указывает на слово, следующее за инструкцией JSR, то это даёт возможность расположить после инструкции параметры подпрограммы, которые должны быть извлечены с применением автоинкрементной или косвенно-автоинкрементной адресации через регистр R. Использование регистровой адресации вызывает прерывание программы по условию «запрещённый код» через вектор 48, так как переход на регистры невозможен. При исполнении команды сначала вычисляется адрес dst, и если при вычислении адреса происходит зависание, то исполнение команды прекращается и соответственно занос регистра в стек и присвоение ему значения счётчика команд не выполняются. Если вычисление адреса операнда завершилось успешно, а зависание произошло во время занесения в стек содержимого регистра, то не смотря на зависание, процессор копирует в регистр значение счётчика команд, а в счётчик команд адрес операнда, и только после этого прерывается по зависанию. При использовании в качестве регистра счётчика команд алгоритм исполнения инструкции состоит в занесении в стек старого значения счётчика команд, а новое значение счётчика команд равно адресу операнда.
Признаки: не изменяются.
- - - Добавлено - - -
Описан с программой теста - здесь.
Вообще, наличие кэша, расширенной арифметики и всего с этим связанного, усложнили процессор раза в 2. Если не в 2, то полтора точно.
- - - Добавлено - - -
Ну так если это прерывание, то оно и может наступить лишь после PLI_REQ.
Хотя, повторюсь, я блок таймаута еще не разбирал.
- - - Добавлено - - -
А, ну все же понятно. Цикл записи идет, а микропрограммный автомат его не ждет, идет дальше, пока не упрется в занятость блока ввода-вывода.
А так как таймаут зависания достаточно долгий, то за это время успевает выполниться вся микропрограмма до конца.
Это окончание цикла чтения автомат будет ждать. А конец цикла записи он не ждет.
Та же самая особенность, вероятно, и в обработчике прерывания, когда стек уменьшается на 4.
Это спасибо пусть @Vslav говорит, который взялся за ВМ3, где много чего впихнули)
Вы навели меня на интересную мысль.
Я все думал, зачем блок таймаута так жестко себя ведет, и подает сигнал ABORT по которому микропрограммный автомат сбрасывается, чего бы он не делал.
А сейчас подумал, ведь застрять из-за таймаута можно только в четко определенных местах. Если был цикл записи, то перед следующим циклом шины. Если цикл чтения, то опять же, место ожидания конца чтения определено. А так как таймаут большой по сравнению даже с самой большой микропрограммой, то к моменту его наступления, автомат будет стоять в строго определенных позициях.
Другой вопрос с внешним сбросом. Там циклы оборвутся в самых непредсказуемых местах.
Вот-вот. Если это чтение с шины, то его результата надо ждать в любом случае. А если запись, то засунул в BRD что надо, дал задание и щёлкай дальше. ГЫ. В чём-то напоминает SMARTDRV.
- - - Добавлено - - -
Тут есть ещё тонкость с чтением предвыборки. Например у нас в ячейке 0157776 команда BR куда-то назад. После её чтения и начала декодирования, будет читаться следующая инструкция по адресу 0160000. Но в данном случае TRAP4 не произойдёт. Т.е. при чтении предвыборки TRAP4 отложенный получается.
Там еще интереснее. По ошибкам шины на предвыборке ABORT не генерируется, вместо этого подается фантомный REPLY. А исключение будет генерироваться только при попытке использовать результаты неудавшейся предвыборки. Например, если есть команда mov (R0)+, @#50, и слово адрес приемника находится вне памяти, то будет выполнено:
- предвыборка слова 50, с фантомным тайм-аутом
- чтение по адресу регистра R0
- инкремент регистра R0
- исключение
У меня на гитхабе специальные тесты под предвыборку понаписаны, сложная она.
Интересная особенность у блока программного сброса по команде RESET (000005).
Таймингами генерации INIT в этом случае заведует блок таймаута. Вот такая унивесальность.
Когда активируется программный INIT, на выход процессора выдается сигнал INIT и блок таймаута начинает считать, а микропрограммный автомат приостанавлвиается.
Когда блок таймаута досчитал до 53 (сигнал TOUT), с выхода процессора снимается INIT, но микропрограмма все еще стоит на месте.
Видимо, в этом интервале внешнем устройствам дается время переинициализироваться после INIT.
Далее блок таймаута достчитывает до 512 (спад сигнала TINIT), и работа процессора продолжается.
Кстати, сигнал PLI_NRDY - это вовсе не сигнал неготовности ПЛМ прерываний, как можно подумать из названия. Он как раз и отвечает за альтернативный режим работы таймера, и приостановку микропрограммы на это время. Но переименовывать его я конечно же не стану)
Из такого алгоритма работы INIT, использующего таймер, возникает определенный вопрос. А что если при выборке команды RESET, была предвыборка следующей команды с несуществующего адреса, и начался отсчет таймаута. А тут бац, и INIT решила воспользоваться таймером. Накладочка получается)
- - - Добавлено - - -
Далее можно порассуждать. Вот у нас завис DIN из-за того, что шина не отвечает. При этом включился режим INIT.
Приоритет у INIT выше, поэтому событие TEVENT (таймаут шины) по достижении таймером значения 53 - не произойдет.
В общем, программный INIT отработает полностью, затем таймер таймаута пойдет считать по кругу, и уже на втором круге возьмет свое.
Таким образом, если случился таймаут по предвыборке следующей команды после RESET, то DIN зависнет на 565 тактов.
А учитывая, что внешние устройства при этом сбросятся, еще не известно, как они среагирует на этот активный DIN.
Как-то ту не совсем понятно с MTPS. В начальном описании её нет, но в алгоритме исполнения микрокода она упоминается.
Далее интереснее:
А по адресу 0x18 обработка JMP/JSR:
Я конечно понимаю, что здесь много общего с MTPS Rn, но на этом как-то не акцентируется внимание.
Забыл в шапке ее вписать. Конечно, она есть.
- - - Добавлено - - -
Тут видится несостыковка потому что по адресу 0x18 находится И обработчик JMP/JSR, и обработчик окончания MTPS.
Попробую это обозначить специально.
- - - Добавлено - - -
Исправил описание, добавил MTPS.
Написал и промоделировал такой тестик:
- памяти только 16К, 37776 последний доступный адрес RAM, обращение на 40000 уже вызывает тайм-аут шины
- по адресу 37776 записал код 000005 (RESET)
- перешел на 37776
Вот тайм-аут обращения оно отрабатывало очень долго, по ходу как для окончания INIT. Ну как бы проблема несущественная, все равно RESET должен закончится до исключения, нo SYNC/DIN держаться активными все это время. И по окончанию RESET оно еще раз чтение 40000 запрашивает, потом нормальный тайм-аут, и уже потом переход по вектору 4.
Скрытый текст
Согласен, проблема несущественная. Вряд ли кто так программировать будет. А повторный запрос инструкции есть, разработчики предусмотрели это. По алгоритму команды RESET, который описал Titus, потом идёт запрос некэшированной инструкции:
Код://======================================================================
//
// Команда RESET (000005)
//
//======================================================================
0x05: NO ALU PI_STB INIT RI=010 // Управление: инициировать INIT
//---------------------------------------------------------------------- (Общий шаг для многих команд)
0x25: ACC=ACC PLI_REQ // Запросить проверку запросов на прерывание
GOTO 0x21 // Перейти на команду выборки следующей некэшированной инструкции
Продолжаем.
Пока что простые операции.
MOV(B), BIS(B), BIC(B), ADD, SUB, CMP(B), BIT(B), XOR:
Код://======================================================================
//
// Операции с двумя операндами
// и регистровой адресацией Rs,Rd
//
// MOV(B), BIS(B), BIC(B), ADD, SUB, CMP(B), BIT(B), XOR
//
//======================================================================
0x3B: if BIC(B) Rd(.B)=Rd&~Rs VZN
if BIS(B) Rd(.B)=Rd|Rs VZN
if ADD Rd=Rd+Rs CVZN
if SUB Rd=Rd+~Rs+1 CVZN
if CMP(B) ACC(.B)=~Rd+Rs+1 CVZN
if BIT(B) ACC(.B)=Rd&Rs VZN
if XOR Rd=Rd^Rs VZN
if MOV Rd=Rs VZN
if MOVB Rd=SIGNEXT(Rs) VZN
endif
PLI_REQ // Запросить проверку запросов на прерывание
if (Rd=R7) GOTO 0x21 // Перейти на команду выборки следующей некэшированной инструкции
else GOTO 0x01 // Перейти на команду выборки следующей инструкции
Это еще что.
Вот как работает EALU - это песня.
Фактически работает тот же микропрограммный автомат вместе с обычным АЛУ, в то время, как блок расширенного АЛУ беспощадно ими управляет, подбрасывая лишние данные, зацикливая, добавляя регистры в цепочку сдвига, организуя цикл, приостанавливая микропрограмму на время. Как дирижер вбежавший в слаженный ансамбль, и заставляющий играть его по-своему.
Вот и понят один из столпов расширенной арифметики - умножение.
Достаточно сложно было собирать его логику по всей схеме. Все очень запутано и специфично. Пришлось даже попросить уважаемого @Ynicky прогнать умножение на модели, чтобы сравнить, не накосячил ли я в общих чертах.
После чего я написал на Си модель того, что описано ниже, и прогнал на всех комбинациях. Все сошлось в точности.
Интересная особенность EALU в том, что когда счетчик числа итераций CTR загружается новым значением, микропрограммный автомат останавливается, и АЛУ каждый шаг выполняет одну и ту же заданную микропрограммой математическо-логическую операцию, но на которую в широком диапазоне может влиять автомат EALU. После окончания счета, микропрограмма продолжается далее.
MUL:
- - - Добавлено - - -Код://======================================================================
//
// Команда MUL Rs,Rd
//
//======================================================================
0x3B: ACC=Rs // (в данной команде поля Rs и Rd кодируются не стандартно)
//----------------------------------------------------------------------
0x18: EA1=0
//----------------------------------------------------------------------
0x27: EA2=ACC // EA2 - Rs
//----------------------------------------------------------------------
0x35: CTR=0x10 // Счетчик на 16 итераций
//----------------------------------------------------------------------
0x1A: // Этот шаг АЛУ повторяет 16 раз
if (EA2[0]=1) EA1=EA1+Rd
else EA1=EA1
ЕА1.ЕА2=EA1.EA2>>1 // Арифметический сдвиг вправо с сохранением знака
EA1[15]=EA1[15]^c15^c14 // Если при сложении был перенос из 15-го бита, или
// перенос из 14-го бита (арифметическое переполнение),
// то знак меняется на противоположный
CTR=CTR-1
if (CTR>0) GOTO 0x1A
//----------------------------------------------------------------------
0x2A: // CTR=0
if (ACC[15]=1) EA1=EA1-Rd // Если ACC<0, то коррекция результата
else EA1=EA1
CVZN // Флаги устанавливаются согласно алгоритму MUL:
// N=EA1[15]
// if (((EA1=0x0000) and (EA2[15]=0)) or
// ((EA1=0xFFFF) and (EA2[15]=1))) C=0
// else C=1
// if (EA1.EA2=0) Z=1
// V=0
//----------------------------------------------------------------------
0x0E: Rd=EA1 // Старшая часть результата
//----------------------------------------------------------------------
0x0C: Rd|1=EA2 // Младшая часть результата
// Дальнейшая проверка рудиментарная, и наследуется от других команд
if (Rs=R7) GOTO 0x21 // Перейти на команду выборки следующей некэшированной инструкции
else GOTO 0x01 // Перейти на команду выборки следующей инструкции
Следует обратить внимание на то, что в конце проверяется Rs=R7, и исходя из этого делается перекеширование.
Хотя в случае умножения, следует проверять Rd|1=R7, что, как видно, не делается.
А значит в этом случае имеем глюк. Хотя, в процессоре есть блок, отвечающий за запись в R7, возможно, он спасет в этом случае. Пока что с ним не разбирался.
А может всё-таки записываем в Rd, а не в Rs. Ведь Rs - source, источник, а Rd - destination, приёмник. А так нужна проверка на Rd|1=R7. Надо попробовать воспроизвести глюк на реальной УКНЦ, нужно только правильные множители подобрать, в смысле значения регистров R6 и R7.
Что то мне кажется, что проверять надо и Rd=R7 и Rd|1=R7 ибо MUL #10, SP и MUL #10, R7