От EmuStudio и UKNCBTL?
Вид для печати
Узнать бы ещё - есть этот глюк в 1806ВМ2 или нет.
Решил проверить глюк с командами EIS с использованием регистра R7, если команда располагается в последней ячейке памяти. В этой ситуации по предвыборке должна прочитаться ячейка с адресом 0160000, соответственно чтения не будет. А вот будет ли в этом случае TRAP4?
Сама программа:
В итоге TRAP4 не произошло, но останов был по адресу 020000, а не 020002. В ячейки 017776 и 020000 занёс команду INC R0. В итоге значение регистра R0 увеличилось на два. При умножении 0160000 на 0177777 получается 020000, это значение заносится в R7. Но из-за того, что по предвыборке не удалось прочесть ячейку 0160000, значение R7 уменьшается на два и происходит переход на адрес 017776, вместо 020000.Код:157770 005000 CLR R0
157772 012703 MOV #-1,R3
157774 177777
157776 070703 MUL R3,R7
Из этого следует, что если в ячейке 0157776 будет команда типа MOV R0,R1, то при её исполнении по предвыборке будет попытка чтения ячейки с адресом 0160000. Попытка будет неудачная, тогда получается, что следующая некэшированная инструкция также будет читаться с адреса 0160000. Таким образом получается, что ячейка с адресом 0160000 будет читаться дважды.
Итак, гость нашей студии - монстр таёжного рока - команда деления.
Самая массивная из всех команд процессора ВМ2. Она задействует и ресурсы микрокоманд, приостанавливая и запуская несколько раз конвейер заново. И стандартное ALU, и, конечно же, расширенное ALU. Те, ко ее кодировали, конечно, монстры. Я сам разбирался с ней 3 дня и 3 ночи, а еще строил модель на Си для проверки итогового алгоритма, а также просил @Ynicky сравнить с моделью.
DIV Rs,Rd:
Заметки к процедуре деления:Код://======================================================================
//
// Команда DIV Rs,Rd
//
//======================================================================
0x3B: ACC=Rs // ACC - делитель
// (в данной команде поля Rs и Rd кодируются не стандартно)
//----------------------------------------------------------------------
0x1A: EA1=Rd // EA1 - делимое, старшая часть
//----------------------------------------------------------------------
0x2A: EA2=Rd|1 // EA2 - делимое, младшая часть (EA1.EA2 - делимое)
//----------------------------------------------------------------------
0x27: CTR=0x0014 // Счетчик на 20 итераций
//---------------------------------------------------------------------- CTR=20
0x35: ACC=ACC // Проверка делителя на ноль
if (ACC=0) ZERO_DIV=1 // Если ACC=0, то ZERO_DIV=1 (деление на ноль)
else ZERO_DIV=0 //
TLZ=EA1[15] // TLZ=EA1[15] (знак делимого)
CTR=CTR-1
//---------------------------------------------------------------------- CTR=19
0x09: // Первый цикл деления
if (TLZ^ACC[15]=1) // Если у делимого и делителя разные знаки
EA1=EA1+ACC // EA1=EA1+ACC
else EA1=EA1-ACC // иначе EA1=EA1-ACC
EA1.EA2=(EA1.EA2<<1)+EA1[15] // Циклический сдвиг влево
EA2[0]=~EA2[0]^ACC[15] // Инвертируем бит займа и накладываем по XOR знак делителя
FCARRY=EA2[0] // Запомнить первый займ
CTR=CTR-1
//---------------------------------------------------------------------- CTR=18..4 (15 итераций)
0x09: loop:
// Основной цикл деления
if (EA2[0]=0) // Если не было займа, то
EA1=EA1+ACC // EA1=EA1+ACC
else EA1=EA1-ACC // иначе EA1=EA1-ACC
EA1.EA2=(EA1.EA2<<1)+EA1[15] // Циклический сдвиг влево
EA2[0]=~EA2[0]^ACC[15] // Инвертируем бит займа и накладываем по XOR знак делителя
CTR=CTR-1
if (CTR>3) GOTO loop
//---------------------------------------------------------------------- CTR=3
0x31: // Завершающий цикл деления
if (EA2[0]=0) // Если не было займа, то
EA1=EA1+ACC // EA1=EA1+ACC
else EA1=EA1-ACC // иначе EA1=EA1-ACC
ЕА2=(EA2<<1)+EA1[15] // Сдвиг результата влево
EA2[0]=~EA1[0]^ACC[15] // Инвертируем бит займа и накладываем по XOR знак делителя
CTR=CTR-1
//---------------------------------------------------------------------- CTR=2
0x31: // Корректировка остатка
if (EA1=0) // Если EA1=0 (остаток равен 0),
EA1=EA1 // то ничего не делаем
else if (EA2[0]=0) // Иначе если не было займа, то
EA1=EA1+ACC // EA1=EA1+ACC
else EA1=EA1-ACC // иначе EA1=EA1-ACC
CTR=CTR-1
//---------------------------------------------------------------------- CTR=1
0x31: // Корректировка остатка
if (EA1=0) // Если EA1=0 (остаток равен 0),
EA1=EA1 // то ничего не делаем
DIV_Z=1
else
DIV_Z=0 // Иначе
if (TLZ^EA1[15]=0) // Если остаток и делимое одинакового знака, то
EA1=EA1 // ничего не делаем,
else if (ACC[15]^EA1[15]=0) // иначе если остаток и делитель одинакового знака, то
EA1=EA1-ACC // EA1=EA1-ACC
else EA1=EA1+ACC // иначе EA1=EA1+ACC
endif
CTR=CTR-1
//---------------------------------------------------------------------- CTR=0
0x19: // Корректировка результата в зависимости от знаков
if (((TLZ=0) & (ACC[15]=1)) or // Если знак делимого плюс, и знак делителя минус, или
((TLZ=1) & (ACC[15]=0) & (DIV_Z=0)) or // если знак делимого минус, знак делителя плюс, и DIV_Z=0, или
((TLZ=1) & (ACC[15]=1) & (DIV_Z=1))) // если знак делимого минус, знак делителя минус, и DIV_Z=1, то
EA2=EA2+1 NZVC // EA2=EA2+1 с установкой флагов
else EA2=EA2^0 NZVC // Иначе EA2=EA2 с установкой флагов
// Алгоритм установки флагов
C=ZERO_DIV // Флаг C отображает деление на ноль
if ((ZERO_DIV=1) or // Флаг V отображает деление на ноль и арифметическое переполнение
(FCARRY^ACC[15]^TLZ=1) or
(FCARRY^EA2[15]^CARRY))
V=1
else V=0
if (EA2=0) Z=1 // Флаг Z отображает равенство результата нулю
else Z=0
N=EA2[15] // Флаг N отображает знак результата
//---------------------------------------------------------------------- Проверка арифметического переполнения и деления на ноль
0x1E: ACC=PSW&0x02 GET_STATE Z=0 // Если PSW[V]=1 (Z=0), то BRA=1, иначе BRA=0
//----------------------------------------------------------------------
0x2D: NO ALU PI_STB VEC=0x03 RI=100 // Управление: константа вектора 0x0C, используется как константа для АЛУ
if (BRA=1) GOTO 0x2C // Если BRA=1 (V=1) (арифметическое переполнение)
else GOTO 0x0E // Иначе BRA=0 (V=0) (нет переполнения)
//---------------------------------------------------------------------- Нет переполнения и деления на ноль
0x0E: Rd|1=EA1 // EA1 - остаток
//----------------------------------------------------------------------
0x0C: Rd=EA2 // EA2 - частное
if (Rs=R7) GOTO 0x21 // Перейти на команду выборки следующей некэшированной инструкции
else GOTO 0x01 // Перейти на команду выборки следующей инструкции
//---------------------------------------------------------------------- Арифметическое переполнение или деление на ноль
0x2C: PSW=PSW&~0x0C // Очистить флаги N и Z
//----------------------------------------------------------------------
0x28: CPSW=PSW // Копировать PSW в CPSW, потому что при работе с констатной, CPSW в данном режиме не обновляется
//----------------------------------------------------------------------
0x08: ACC=ACC PLI_REQ // Запросить проверку запросов на прерывание
if X(Rs) or @X(Rs) or (Rs=R7) // Если использовали адресацию по R7, то
GOTO 0x21 // Перейти на команду выборки следующей некэшированной инструкции
else // Иначе
GOTO 0x01 // Перейти на команду выборки следующей инструкции
1. На проверку флага V в PLM проверки условий (PLM_BRA), видимо, уже не хватало места, и проверку флага V сделали через проверку флага Z (PSW & 0x02). Что на 4 такта замедляет деление, но ввиду и так достаточно длинного его выполнения, особой роли не играет.
2. В конце деления запись результата в R7 проверяется стандартным для обычных двухоперандных инструкций методом. Однако, ввиду того, что Rd и Rs у команд EALU поменяны местами, эта проверка является некорректной.
3. Не смотря на то, что я всячески стараюсь не менять названия сигналов, данные @Vslav'ом, тут все же пришлось заменить /WAIT_DIV на DIV_Z, ибо никак не соответствует.
4. Каждый из столь емких шагов EALU делается за 4 такта, за исключением проверки флагов, которая всегда делается за 8 тактов.
5. В блоке констант не нашлось констатны 0xF3, поэтому для этого пришлось воспользоваться хитрым образом констатной из блока векторов прерываний 0x0C (BPT), инвертируя ее.
6. Из-за того, что в случае деления на ноль или переполнения, используется модификация PSW (сброс флагов N и Z), а так же нестандартное копирование PSW в CPSW, блокировка CPSW при запрещенных прерываниях в HALT-режиме не сработает.
В пультовом отладчике. Таймер желательно выключить, запускать с запретом прерываний, т.е. RS=340. Программка проверки занимает несколько слов, так быстрее проверить так.
- - - Добавлено - - -
Ох! Очень интересно! Получается, что при арифметическом переполнении результат PSW обязательно копируется в CPSW, даже несмотря на HALT с запрещёнными прерываниями. Надо обязательно проверить.
Добавил пукнт 6 в заметки. Теперь можно проверить деление на ноль в HALT-режиме с запрещенными прерываниями, и убедиться, что деление портит CPSW.
- - - Добавлено - - -
Да, именно это я и хотел предложить)
А все потому, что приляпывали деление, когда вся основная структура процессора уже была сформирована.
Нет проверки V? Не беда, проверим через Z.
Нет блока обнуления N и Z. Тоже ерунда, занулим через константу. Нет констатны? Возьмем из блока прерываний. CPSW не обновится... тогда принудительно обновим CPSW. А... тогда в HALT-режиме с запрещенными прерываниями испортится CPSW. Да, плевать, кто заметит)
Ну что ж, диагноз товарища Саахова полностью подтвердился.
Сама программа, запускается с RS=600 (HALT с запрещёнными прерываниями):
При значении R3=1 происходит переполнение, а при R3=0 - деление на ноль. Если R3=0177777, то операция завершается нормально. При переполнении и делении на ноль CPSW изменяется, при нормальном завершении остаётся неизменным.Код:1000 012700 MOV #777,R0
1002 000777
1004 000034 WCPS
1006 012703 MOV #1,R3
1010 000001
1012 005000 CLR R0
1014 071303 DIV R3,R3
1016 000024 RCPS
1020 106701 MFPS R1
1022 000000 HALT
- - - Добавлено - - -
А на каком тесте?
- - - Добавлено - - -
А вот с этим я не согласен. Используется четыре флага - NZVC.
N = 0x08, 010
Z = 0x04, 004
V = 0x02, 002
C = 0x01, 001
Так что проверяют именно флаг переполнения V, и флаг Z тут вообще не при чём.
Да нет, не в том дело.
Чтобы проверить флаг V, делается ACC=PSW&0x02.
Затем уже по результату этой операции устанавливается или не устанавливается флаг Z. На регистр PSW это никак не влияет, потому что результат в PSW обратно не копируется.
А потом уже по этому флагу Z принимается решение, туда идем, или сюда.
Просто в матрице PLM_BRA нет комбинаций, позволяющих проверить отдельно флаг V. Но есть комбинации, позволяющие проверить флаг Z. Поэтому и едет поезд в Киев через Магадан.
Правильно. Что тут противоречит моему описанию? )
Еще раз на пальцах.
В регистре PSW имеем установленный бит V. T.e. 0x0002.
Делаем ACC = PSW & 0x0002.
Если в результате ACC = 0 (бит V не был установлен в регистре PSW), то устанавливается флаг Z.
Если в результате ACC = 2 (бит V был установлен в регистре PSW), то флаг Z обнуляется.
Флаг V и не должен устанавливаться. Устанавливается флаг Z по которому определяется, был ли установлен флаг V.
Видимо, потому что сначала закодировали матрицу PLM_BRA, а потом оказалось, что расширенной арифметике этого не хватает.
Для команды SOB была написана проверка флага Z. Ей и воспользовались.
Напомню:
Код:NA4 = 1, NA5 = 1: // Для команды выборки инструкции 0x01
0.000.x00.1xx BR
0.000.xx1.1xx Z=1 BEQ, BLE
x.000.x01.1xx Z=1 BEQ, BLOS
1.000.x11.0xx C=0 BCC
1.000.xx1.1xx C=1 BCS, BLOS
1.000.x00.0xx N=0 BPL
1.000.x00.1xx N=1 BMI
1.000.x10.0xx V=0 BVC
1.000.x10.1xx V=1 BVS
0.000.x10.0xx V=1,N=1 BGE
0.000.x10.0xx V=0,N=0 BGE
0.000.x1x.1xx V=1,N=0 BLT, BLE
0.000.x1x.1xx V=0,N=1 BLT, BLE
NA4 = 1: // Для команды выборки инструкции 0x01 и других(?)
// Проверка NA5=1 упразднена, т.к. пересечение с командой SOB тоже проверяет Z=0
0.000.x01.0xx Z=0, BNE
1.000.x01.0xx C=0,Z=0 BHI
0.000.x11.0xx V=1,Z=0,N=1 BGT
0.000.x1x.0xx V=0,Z=0,N=0 BGT, BGE
NA4 = 0, NA5 = 1: // RTI, RTT
BIT_13 & BIT_14 & BIT_15 = 1
NA4 = 1, NA5 = 0: // Для команды SOB, DIV
Z=0
NA4 = 0, NA5 = 0: // Для команды CODE030, FIS?
N=0
В этом.
Тут надо ещё смотреть значение R6 до выполнения и после выполнения, желательно настроить вектор 4, чтобы узнать, был ли TRAP4.
- - - Добавлено - - -
Начинать желательно с этого.
Нуу.... Где то у меня был 1806вм2 на переходнике.. и вроде была плата с процом в панельке... Чуть позже гляну и отпишусь
- - - Добавлено - - -
Облом.. Единственная плата с процом на панельке - не рабочая...
1801вм3 проверьте
небось тоже глюкалово
Алкоголики ВМ3 не делали
Код:***** ДОСТУПНОЕ ОЗУ - 3,7 M*
@ 001000
@1000/000000 5000
00001002/000000 12703
00001004/000000 2
00001006/000000 70703
00001010/000000 5200
@1000G
@ 002022
@$0/000000
кстати 1801вм2 с ромбом никто не видел? фото нет ни у кого?
скорее всего он не прошел приемку...
или его не заказывали трезвенники???