Если повезёт, со следующей недели всё-таки оформлю удалёнку, но всяко каникулы закончены, пора возвращаться к нашим баранам.

Начинаю тестировать улучшенный транслятор QuatCore, под "ускоренное" ядро, в котором в кои-то веки появился конвейер. Всё, что касается условных и безусловных переходов, обслуживается "аппаратно" - зажигаются флаги SrcDiscard и DestDiscard, не дающие выполниться командам, которые попали в конвейер "по инерции". Также есть аппаратная блокировка между запуском АЛУ и чтением результатов из АЛУ - мы дожидаемся окончания работы и только потом заносим значение Acc или C (оно во время умножения претерпевает циклические сдвиги и только под конец возвращается на место).
С остальными проблемами я пока что решил разбираться "программно", введением NOP или перестановкой операторов там, где это можно сделать. Уже давно придумал, как транслятор будет обнаруживать проблемные места - введением "списка ресурсов" для каждой команды. Если две команды, выполняющиеся "бок о бок" (одна: SrcAddr, получает новое значение на шину данных, вторая: DestAddr, запихивает ранее полученное значение куда надо), занимают один ресурс - выдаём Warning и втыкаем NOP. Т.е будет гарантировано, что программа всё-таки заработает как положено, а потом программист в лице меня наконец-то решит разобраться с надоевшими warning'ами и либо сам вставит эти NOPы, чтобы больше не ругался, либо найдёт, как избежать "конфликтов".
Но сейчас свежим взглядом посмотрел на это безобразие - и обнаружил очередную тонкость. Команды на чтение,
i (прочитать значение регистра i),
[X+2i+1] (прочитать значение из памяти по заданному адресу),
[X+i]
и т.д ДОЛЖНЫ ЗАНИМАТЬ РЕСУРС i.
Команды на запись,
i (записать новое значение в регистр),
i++,
iLOOP (если i>0, вычесть единичку и прыгнуть в начало цикла)
также ДОЛЖНЫ ЗАНИМАТЬ РЕСУРС i.
А вот команды на запись
[X+2i+1] (записать в память по заданному адресу),
[X+i]
и т.д. - НЕ ДОЛЖНЫ!
По крайней мере, при текущей системе команд...
Так получается, потому что в данный момент всё сделано "правильно" - ни одна команда на чтение не может изменить значение i. Поэтому если команда на запись тоже не изменяет значение i, а лишь использует его для обращение к нужной памяти, то мы заведомо знаем, что значение не поменяется (у нас ВСЕГДА одна команда на чтение и одна на запись), и никакого конфликта нет!
И такой выход из ситуации, когда из команд на запись в память, [X+2i+1], [X+i] и т.д. мы просто убираем данный ресурс, будет действовать, пока не появится хоть одной команды на чтение, которая всё-таки меняет i. Пока таких нет, и в ближайшее время не предвидится.
Посмотрим, а как обстоят дела с ресурсом SP (Stack Pointer). У нас есть команды на чтение:
SP (прочитать само значение регистра),
[SP] (прочитать память на вершине стека, т.е 1-ю локальную переменную),
[SP+1] (прочитать 2-ю локальную переменную),
[SP++],
[--SP]
и ровно такие же на запись.
Тут уже веселее. Поскольку одновременно читать из памяти и записывать в неё всё равно нельзя (для предотвращения этого мы используем отдельный ресурс, MemBus), то либо в DestAddr, либо в SrcAddr должен находиться SP, только в этом случае могут быть ситуации, не запрещённые через MemBus, но недопустимые с точки зрения SP.
1. Если в какой-то момент времени попалось 2 команды бок о бок:
SP [SP]
это криминал - у нас сначала должна завершиться запись в SP, а только потом начаться чтение.
И что бы не стояло на чтение - [SP], [SP+1], [SP++], [--SP] или даже SP (записали и тут же прочитали "записанное") - разницы нет.
2. Читаем регистр SP, записываем в память, без инкрементов/декрементов
[SP] SP
никаких проблем - запись в память пущай идёт, чтение регистра этому не мешает. Можно поставить и [SP+1], и [SP+i] - всё это подходит.
3. Читаем SP, записываем в память с инкрементом:
[SP++] SP
уже не годится - предполагается, нам нужно позволить довести операцию до конца, а только потом читать значение регистра
Напомню, здесь всегда приоритет отдаётся первой команде в строке, команде на запись, поскольку она ПРЕДШЕСТВУЕТ команде на чтение, это команда с предыдущей строки! Поэтому, чтобы поддерживать видимость, что команды выполняются одна за другой, и в начале выполнения следующей команды все уже на своих местах, нужно позволить ей завершить запись.
Если просто все команды, работающие с SP, заявят об использовании ресурса SP, транслятор станет чуть более параноидальным, чем надо - он запретит все варианты, тогда как пункт 2 должен был бы оставить.
К счастью, здесь тоже ВЫХОД ОЧЕНЬ ПРОСТОЙ: убираем использование ресурса SP из команд НА ЗАПИСЬ [SP], [SP+1], [SP+i] и так далее!
И здесь нам тоже повезло: за счёт наличия запрета на одновременный доступ к памяти и на запись, и на чтение (ресурс MemBus), мы знаем, что при использовании [SP], [SP+1], [SP+i] и т.д. НА ЗАПИСЬ, автоматически будет запрещена любая операция в квадратных скобках (доступ к памяти) НА ЧТЕНИЕ. Ну, не запрещена, а попросят соблюдать дистанцию, вставив посерединке NOP. Поэтому единственная операция без квадратных скобок, которая обращается к SP - это собственно "SP", а она содержимое регистра SP не меняет.
Похоже, что реализовать полную логику Multiple-Read-Exclusive-Write здесь не придётся, достаточно будет для каждой команды записать множество (set) ресурсов, и находить пересечение этих множеств для Dest и Src. Если пересечение дало пустое множество - всё хорошо, иначе warning.
Сейчас попробуем сделать...