:v2_dizzy_facepalm: итого проверок остаётся столько же, сколько было
разворачивают циклы вообще-то ради уменьшения доли в нём служебных команд
а нужного точного числа повторений добиваются начальным переходом внутрь тела цикла
Вид для печати
Внутрь тела перейти сложнее, потому что надо получить адрес нужной итерации. Или можно проще?
Проверок осталось столько же, но сброс конвейера только один на N итераций, "jp loop".
Bolt, с аппаратными циклами сбрасывать конвейер вообще не требуется, поскольку видя совпадение PC с адресом последней инструкции цикла, мы уменьшим на 1 счётчик и если он не нулевой, просто загрузим в PC из другого регистра адрес его начала, а не PC+1. Этот более общий механизм, чем строковые инструкции из x86, поскольку позволяет организовывать циклы из нескольких инструкций без дополнительных накладных расходов.
Можно и так, конечно.
- - - Добавлено - - -
Вариант нулевой. Конвейер как бы есть, но прогоняем по нему инструкции по одной. Самый наипростейший вариант, но и самый медленный.
Вариант первый. Грузим в конвейер с одной стороны, забираем с другой, на каждом переходе ждём N тактов для заполнения. Вариант простой, но потеряется много тактов, особенно на коротких циклах.
Вариант второй. Делаем быструю djnz для коротких циклов, которая будет работать без дополнительных тактов. Вариант чуть сложнее в реализации.
Вариант третий. Делаем предсказание переходов. Плюс один блок памяти, сотня-другая ячеек в ПЛИС, но выигрываем в скорости на переходах. Вариант достаточно сложный, если заморочиться с алгоритмом предсказания. Но djnz так же выполняется, как и раньше, пусть даже за один-два такта.
Вариант четвёртый, с регистрами. Лишних тактов на djnz не тратится, но тут я вообще не понимаю как это реализовать. Нет, если тупо гнать данные, сравнивая PC и перепрыгивая на другой адрес, то всё просто. Но что делать с прерываниями внутри цикла? Какой адрес возврата сохранять в стек? Какое значение читать из регистра-счётчика (оно будет меньше, чем должно быть для текущей выполняемой инструкции)? Как перегружать поток при выходе из цикла?
на прерывание завести что-то параллельное, запретив вложенные прерывания? Конечно не полный параллелизм, а до завершения циклов. Или делать останов до завершения обработки прерывания. Если только нет недопустимых инструкций и прерывания при недопустимом обращении к памяти(порту).
andrews, планируются и недопустимые инструкции, и прерывания при недопустимых обращениях.
Три режима: ядро, пользователь, и эмуляция Z80. В режиме ядра можно всё, в режиме пользователя запрет на некоторые команды, в режиме эмуляции перехват обращения к портам для эмуляции железа. Программа в режиме эмуляции по идее вообще не должна обнаружить, что она не на железе выполняется.
Ещё может быть нераспределённая память. То есть приложению выделяется конечно же не 16 мегабайт, а сколько оно попросит (и сколько есть), в адресном пространстве будут "дырки". Можно игнорировать запись и читать 00h, а можно вызывать прерывание.
В самом простом для понимания варианте есть:
Инструкция запуска цикла адрес следующий инструкции записывает в LoopStat, а в LoopEnd и LoopCnt пишет переданные в параметрах значения. Когда PC становится равным LoopEnd мы уменьшаем счётчик и если еще не 0, то перезаписываем PC из LoopStart, иначе продолжаем выполнять код как обычно. Проблема здесь только в том, что мы либо теряем такт на перезапись PC, либо снижаем скорость выборки программы чтобы успело установиться новое значение PC при выдаче его на шину. Именно по-этому метку нужно перенести на последнюю инструкцию, тогда компаратор PC c LoopEnd скажет нужно ли заменить его на LoopStart (при условии что счётчик не 0).Код:DoLoop LoopEnd, LoopCnt
LoopStart: - реально не требуется так как команда запуска цикла может вычислить эту метку сама
...
LastInst
LoopEnd: - метка первой команды за циклом(для простоты понимания)
Реально действия последней команды ничем не отличаются от остальных, за исключением того, что к ним добавляется уменьшение счётчика и выбор что записывать в PC. То есть, если переход в прерывание выполняется перед последней командой цикла, в стек пойдёт её адрес. Если после, то сохраняется адрес первой команды за циклом, если счётчик как раз обнулился, иначе сохраняется LoopStart. Ни эти регистры на прерывание, ни прерывание на регистры никак не влияют, поскольку нужно вернуться из прерывания и дойти до LoopEnd, чтобы этот механизм снова сработал. Если последней командой цикла стоит вызов подпрограммы, мы должны сохранить в стек либо адрес после цикла, либо LoopStart. Из такого цикла можно делать условные переходы, главное потом вернуться обратно внутрь.
Если мы хотим запустить вложенный цикл, то нам требуется сохранить LoopStart, LoopEnd и LoopCnt, а после его окончания восстановить. Или можно поручить это команде которая запускает цикл, а восстановление будет производиться при обнулении счётчика. Хотя для ситуации когда цикл не вложенный, и перед запуском счётчик и так был 0 это будет лишней работой. Может, есть смысл сделать два вида команды запуска цикла с сохранением предыдущего и без, правда тогда еще появится аппаратный флаг, чтобы указать на необходимость восстановления в конце счётчиков внешнего цикла.
Соображение по поводу конвейера и обработки прерываний: сигнал запроса прерываний должен просто заблокировать выборку следующей инструкции, заморозить PC, запретить прерывания и подсунуть в регистр инструкций команду перехода к обработчику, достаточно обычного CALL и на этом его миссия может быть завершена. Тогда вызов прерываний пойдёт по конвейеру вместе с обычными командами.
Так...
Вот смотри. Есть у нас "конвейер". Допустим, на 5 тактов. Я понимаю как это сделать, и как сделать LoopCnt, но я не понимаю как выгребать из разных заковыристых ситуаций.
Выполняем длинный кусок без ветвлений.
Когда на выходе PC, на входе PC+4.
Если прилетело прерывание - что сохранить в стек?
Цикл из одной инструкции. Или из двух. Когда выполняем PC, на входе PC или PC-1. Что сохранять в стек?
В теле цикла используется счётчик. Выполняется инструкция на выходе конвейера, но счётчик-то уже на 4 уменьшился.
А тут ещё и прерывание. Что сохранять в стек? А что делать с тем, что уже выбрано? Что делать с счётчиком, который уже уменьшен?
Сохранять в стек адрес следующей инструкции, то есть которая на предпоследнем этапе? А если счётчик ушёл в ноль и выбрана ещё одна инструкция после цикла?
Ну сохранили адрес предпоследней, то есть инструкции в цикле, которая единственная, а счётчик уже обнулён. Куда возвращаться и что делать? Прокрутить цикл ещё 2^32 раз? Предусмотреть ещё один флаг?
Не, ну можно, например, доделать что уже попало в конвейер, потом уходить в прерывание. А в конвейере уже начался новый цикл.
И теперь это всё на HDL, плз.
Я, наверное, тупой, раз не понимаю как такие очевидные вещи делаются.
Регистрам Loopcnt - нет.
- - - Добавлено - - -
Тут другая проблема актуальна.
Выбираем 5 инструкций. Но 3 выбраны, и это джамп, а 2 уже уползло в запретную страницу, и они уже не понадобятся. Это как разруливать? Запретить программисту располагать код ближе, чем на 5 байт к концу страницы?
Bolt, по поводу разруливания заковыристых ситуаций основное соображение такое:
Прерывание нужно отправлять в конвейер как обычную инструкцию(запретив при этом другие), нет смысла ради него что-то бросать, поскольку быстрее в прерывание мы не попадём.
А вот при исключении конвейер нужно очистить, что сильно упрощается, если инструкции что-то записывают только на последней стадии. Всё, что меняется на предыдущих стадиях придётся тащить через конвейер, чтобы при исключении иметь возможность восстановить состояние.
А вот для доступа в запрещённые страницы придётся снабжать буферные регистры тегами, сигнализирующими о такой ситуации, чтобы исключение возникало, только если эти данные действительно потребуются.
Это всё соображения с точки зрения программирования. Не знаю как, видимо это случилось не сразу, но разработчики смогли заставить работать аппаратные циклы у монстра, выполняющего до 4 скалярных + 4 векторных инструкций за такт (векторные регистры по 64 байта, есть инструкции выполняющие по 32 умножения для double или 128 для float). Правда переставлять инструкции он не умеет и если хоть одна чего-то ждёт, тормозить будет весь конвейер, в общем программировать его тот еще ад.
Мне понравилась ветка Ynicky.
https://zx-pk.ru/threads/31254-yadro...=1#post1041654
https://zx-pk.ru/threads/31254-yadro...=1#post1041762
Где он предложил для замены z80 новую RISC-архитектуру. Корнечно, я не настаиваю на этой идее, что она самая правильная, но считаю её хорошей альтернативой. Может, не цепляться за комманды z80,а сделать RISC-архитектуру наиболее подходящую для эмуляции z80? Что-то подобное было реализовано исторически в амиге, где на смену м680000 пришёл поверписи. Можно сделать такую RISC-архитектуру, которая напоминала бы своими подходами z80 для удобства программистов переходящих с z80, но не отягощённую повтором специфики z80 точь в вточь. Главное условие - хорошая эмуляция z80 и удобство перехода на неё программистов привыкших к z80.
Для начало можно обкатать процессор предложенный Ynicky. Посмотреть, что можно усовершенствовать, а что убрать. Хотя я заметил, что процессор Ynicky не имеет каких-то подходов общих с Z80. Это создаст трудность перехода на него у программистов привыкших к z80.
Прочёл док на процессор Ynicky. Недостаток - раздельня память комманд и данных. А для компьютера надо общую память для комманд и данных. Поэтому для использования этого процесса необходимо подкорректировать архитектуру в сторону общей памяти комманд и данных.
Во, точно.
Вот именно, не сразу. А я только начал.
О какой архитектуре идёт речь?
С точки зрения программирования нафантазировать можно что угодно, а вот реализация этих фантазий может быть очень сложной.
Как получилось с eZ80 - а давайте по опкоду посчитаем длину инструкции. Простая вроде бы задача. Ага, щаззз! Кстати, можешь сам попробовать, там интересно ;)
- - - Добавлено - - -
Мне тоже понравилась, но не только сама идея, она прям напрашивается, а ещё то, что она была доведена до рабочего состояния.
Не надо удобства. Переходить на неё программистам не придётся.
Делаем RISC-архитектуру, которая способна работать как интерпретатор Z80. И пофиг на систему команд.
На эту архитектуру сверху наваливаем 32 бита. Как ни крути - получим нечто похожее на Z80. Пофиг на бинарную совместимость.
Пишем интерпретатор в командах этого RISC и запихиваем в ПЗУ.
Получили процессор с двумя похожими идеологически, но бинарно несовместимыми системами команд, внутри которого процессор, работающий по третьей системе.
Тут надо две картинки: "мы поставили процессор в процессор, чтобы ты мог программировать процессор, когда программируешь процессор" и "we need to go deeper..." :)
ничего не делаем, берём обычный арм, распределяем память на 8-битную спектрумовскую и 32-битную армовскую
каждому 8-битному опкоду в спековской памяти сопоставляем 32-битный опкод вызова процедуры в армовской
при перезаписи байта в спековской памяти (кроме одного спецзначения) изменяем и опкод процедуры
иии... всё, мы "расширили z80" на сколько нам угодно любых 32-битных команд :D
Lethargeek, а потом в 128-м переключается страница иии... всё, надо копировать 64 килобайта? :)
А ещё перезапись байта меняет не только один опкод, а до четырёх инструкций сразу. Не знаю где об это можно споткнуться, но сам факт...
она ценна сама по себе, потому что те ядра, которые бесплатно шли к Altera и Xilinx-ам как-то не пользовались особой популярностью для ретро-компов.
- - - Добавлено - - -
какой именно? 1-й, 4-й, 11-й. И разве разработчиков этих ядер волновала возможность реализации на них z80 и Spectrum-ов. Любое ненужное излишество это вентили, вентили, вентили. Вот люди в России не знают чем загрузить ранее приобретенную фабрику на 130 нм чипы и даже выбивают деньги на 65 нм под Эльбрусы. Так что если ASIC влезет в 130 нм то это низкая себестоимость. Потом я не согласен, что более сложная архитектура или архитектура "черного ящика" жизнеспособна. В любом случае это ограничит сферу ее применения. А так делать значит обрекать ее на жизнь недолгую и непопулярную. "Делай проще, дурашка" самый верный принцип! Можно и другие критерии для себя сформулировать и им следовать в разработке.
Пока она находится на этапе изготовления опытных образцов и в интернете про неё кроме названия нифига нет. Что будет дальше тоже сложно сказать, может будет выпущена небольшая партия, а может примут решение перед этим что-то еще доработать. По частоте и пропускной способности памяти она конечно отстаёт от интела, по теоретической вычислительной мощности примерно сопоставимо, но всё зависит от задачи и степени её оптимизации. Кроме умножения матриц, которое очень полезно для нейронных сетей, там есть некоторые фичи присутствующие у видеокарт, к примеру внутренняя память делится на 32 банка и можно выполнять по 32 обращения каждый такт(элементы по 1, 2 или 4 байта). Интел в последних версиях процессора может читать по 8 независимым адресам, правда если будут кеш промахи это будет долго, у внутренней памяти в этом смысле есть плюсы. Реально данные поступят через 8 тактов, но если это учесть, то к примеру для ветвления по 3х уровневому дереву, можно получить среднюю производительность 1 дерево/такт. При этом для каждого узла читается по два весовых коэффициента и два индекса в массиве, затем читаются элементы массива, перемножаются и сравниваются, после чего выбирается левое или правое поддерево, ну а в финале выбирается лист с цифрой, которые должны суммироваться чтобы выяснить в какой момент нужно прервать цикл.
Нужно соблюдать баланс между возможностями аппаратуры и удобством программирования, последнее очень важный фактор для успеха любой архитектуры. У монстра к примеру DMA для полной загрузки шины требует выравнивания пересылок на 16 байт, а потом оказывается что нужно сдвинуть строки во внутренней памяти, чтобы избежать конфликтов банков и это удваивает объём кода и учетверяет время для его отладки.
У меня был написан на ассемблере дизассемблер инструкций реального и защищённого режимов X86 включая MMX, но когда началось SSE2 с префиксами стало очень весело, и вот тут я это дело забросил. А для Z80 нам требуется пропустить все префиксы, те которые влияют на декодирование инструкции превратить в набор дополнительных бит, добавить опкод, после чего из таблицы мы должны получить длину. Можно решать эту задачу и параллельно, если число префиксов ограничено, то классифицируем все префиксы, при примеру 1, 2, 3 или 0 - "не префикс", собираем эти биты вместе, далее из таблицы вытаскиваем длину префиксов, и адрес таблицы длин для опкодов.
Не могу оценить её ценность, но скорее всего это в итоге будет специализированный микрокод. Для Z80 эмулятор с некоторыми допущениями сейчас занимает 2 килобайта.
- - - Добавлено - - -
Да в Z80 всё просто, я про eZ80. У него не только префиксы меняют длину, но и внутренний режим. А режим переключается переходами с префиксом, уходом в обработчик прерывания, а ещё режим может восстанавливаться из стека. Вот на чтении из стека я сломался :) Не знаю, может и можно это отловить.
а ядро сейчас в виде эмулятора или уже на fpga?
- - - Добавлено - - -
все операции с матрицами и векторами используются и в манипуляциях с игровыми объектами и в 2d и в 3d, но это может быть и не внутри ядра, если игровое поле большое все-равно придется лазать по памяти. Тут "тяжелые" изображения сильно все усугубляют. Поэтому для "легких" нужно свой видеопроцессор разрабатывать, хоть с физикой, хоть без.
Я не про то, не про удобство программирования.
Можно нафантазировать любую систему команд, которая сможет считать вдоль, поперёк, а ещё по диагонали и стоя в гамаке :) Потом так же тупо "в лоб" описать это на HDL, не думая что там получится в "железе". А получится там 5000 cell-ов и частота 30 МГц.
А можно, увидев такое непотребство, как-то переделать, что-то выкинуть, что-то добавить, и получить 1000 cell-ов и 100 МГц.
И дело не столько в характеристиках, сколько вот в этой... неупорядоченности, что ли. Да тот же Z80. Номер регистра берётся и из битов 0..2, и из битов 3..5, и от префиксов зависит, и исключения из нумерации, и младший бит отбрасывается, и чего там только нет. По флагам картина такая же. Кто сможет коротко описать как меняются флаги Z80, даже документированные? Это играйте, это не играйте, а здесь рыбу заворачивали. Давайте напишем case на 100 строк, синтезатор разберётся.
Поэтому я против аппаратных циклов. Они очень усложняют то, что уже есть. Дожать один такт, создав себе кучу проблем, и потом героически их решать? Нет, я сначала сделаю как могу, хоть 10 тактов на эту djnz, потом буду думать как ускорить.
- - - Добавлено - - -
В виде эмулятора, с некоторыми допущениями, кое-что написано именно как программа, не как эмулятор. Эмуляция эмуляции :) не знаю как объяснить.
В FPGA только прикидываю отдельные блоки, сколько займут места и на какой частоте смогут работать.
- - - Добавлено - - -
2 килобайта это тот самый микрокод, который для внутреннего ядра.
Допустим разрядность элемента массива равна разрядности слова банка, каждый банк имеет отдельную шину адреса и данных. Если мы поделим память на 4 банка и конкретный банк будут выбирать 2 младших бита адреса, то нет никаких проблем прочитать 4 подряд идущих элемента, начиная с любого банка. Если шаг между элементами будет кратен 2, к примеру в массиве лежат двумерные координаты, а обновить нам требуется только X, половина банков у нас будет простаивать, а другая трудиться в два раза дольше. Если в массиве лежат трёхмерные координаты, то обновить X можно без проблем задействуя все 4 банка, да и вообще проблем не будет со структурами из нечётного числа слов. А вот когда структура станет кратна 4м словам, то при обновлении X трудиться придётся одному банку, пока другие будут простаивать.
Решений здесь существует несколько:
1) Растащить поля структуры по отдельным массивам, в структуру поместить адреса этих массивов, чтобы по индексу можно было собрать нужную структуру из разных массивов.
2) Дополнить структуру до нечётного числа слов, что несколько затратно по памяти для мелких структур.
3) Самый интересный вариант - сделать больше банков, сохранив в целях совместимости количество шин.
Для последнего варианта можно поделить каждый банк на 5 более мелких(всего 20 банков), и добавить 5 ортогональных шин. Если младший бит шага между элементами нечётный, запросы идут по 4м шинам как обычно, а если чётный, значит их нужно перенаправить на ортогональные шины. Здесь правда возникает проблема, если шаг будет кратен и тому и другому, к примеру 10 или 20. В этом случае придётся вернуться ко 2 варианту, но затраты памяти здесь уже будут 10% или 5%, а не 50% как в случае структур из двух слов. Можно рассмотреть и схемы разбиения 8x9 или 16x17, но для них всех нужен быстрый алгоритм вычисления остатка при делении на числа вида 2^n+1, у кого какие соображения по этому поводу? Скорее всего это будет что-то типа признака делимости на 11 для десятичной системы, но насколько сложная схема получится для железа?
Есть еще вариант вычислить от адреса CRC, после чего делить его на номер банка и номер ячейки в нём, здесь конфликты будут возникать для любого шага, но в теории они они должны быть равномерны по всем банкам и очередь может помочь с ними справиться. Опять же вопрос насколько сложно это будет для железа?
PS: В общем мысль про CRC пришла к выводу, что имеея 2^n банков достаточно разбить адрес на группы по n бит после чего их все проксорить. Таким образом можно будет параллельно обращаться к элементам с любым шагом и в среднем нагрузка на банки будет равномерной. Есть правда минус в том, что для каждого банка нужна очередь запросов, и при конфликтах время получения всех данных будет удлиняться. Плюс метода в том, что при изменении количества банков нужно только скорректировать количество параллельно обрабатываемых элементов, а переделывать размещение данных не потребуется.
blackmirror, что-то я вообще не понял про что это.
Это про много ядер обновляющих поля структур в общем поле памяти из нескольких банков в случае если размер структур кратен суммарной ширине банков и конфликты которые при этом возникают. Правда рассуждать можно проще, если 2^n банков не позволяют эффективно разруливать конфликты при структурах кратных 2^s, значит банков нужно нечётное количество, к примеру 2^n+1. C crc связываться не стоит, там остаются размеры структур на которых всё будет плохо, самый действенный вариант это делать банков больше чем ядер, но очереди всё равно нужны.
Как часто нужны числа более 16 миллионов? Для математики? А для индексов с 24-битной адресации не очень, посему они вполне могут быть одноцикловыми (4 байта) - например, старший байт - команда, а 3 младших - константа.
Простите, а что в это время делает процессорное ядро? Ждёт освобождения PC, ведь это же ядра епархия?
И какой тогда смысл в аппаратных циклах?
Или всё-таки что-то делает?
Тогда причём здесь PC ?
В народе этот узел называется DMA и к процессорному ядру никакого отношения не имеет.
Вот когда понадобится - тогда и добавится. :)
Дык захват прерывания происходит в самом конце выполняемой в данный момент инструкции, посему, разумеется, PC+длина инструкции (или что там будет результатом её выполнения).
32-битная команда будет поболее 100 строк...
Сииильно поболе.. :)
Но это тоже не тупик.
Кейсов ведь может быть более одного и работать они могут вполне себе параллельно.
Для примера потеоретизируем для однобитных сигналов.
Упрощённо, каждый логический блок имеет 8 входов + входы расширения, на каждый вход/выход стоит свой ключ, + коммутация сигналов внутри блока - тоже ключи, пусть для простоты будет 2 последовательных ключа, итого 4. Пусть каждый имеет задержку в 0,5 наносекунды. Для того, чтоб построить мультиплексор на 100 входов, нам нужно задействовать 100/8=12,5 (13) последовательно включённых блока, получив в результате задержку в 13*4*0,5=26 условных наносекунд.
Либо поставить, например, 5 двадцативходовых мультиплексоров впараллель и один, объединяющий их на выходе. Итого на круг получим 20/8=2,5 (3) + 1 = 4 последовательных ячейки по 4 ключа = 4*4*0,5=8 условных наносекунд или трёх с четвертью кратное ускорение мультиплексора... Расплата - "лишний" мультиплексор на выходе.
Чисто теоретически. :)
Усложнение не всегда ведёт к замедлению.
Но это тоже не догма.
Самым медленным устройством у нас является память (или УВВ, если угодно, не суть), она задаёт быстродействие, а внутрях оно может выполнять один МЦ и за несколько тактов, только небольшими кусочками, опять же по конвейеру, для пользователя это прозрачно. А это уже экономия ячеек.
Аппаратные циклы начинают свою работу на этапе выборки инструкции, когда PC выбираемой команды совпадает с последней командой цикла. Они работают параллельно с выполнением команд и не ждут окончания их выполнения, а сразу говорят, что далее выбирать команды следует с начала. Хотя для них могут быть ограничения в виде невозможности иметь последней командой цикла переход или вызов подпрограммы с непредсказуемым адресом. Иными словами который читается из памяти/вычисляется/зависит от вычисляемого условия. Смысл их в том, что для организации цикла не нужно выполнять декремент и переход, не нужно разворачивать циклы - можно писать максимально коротко не увеличивая нагрузку на кеш команд. С их помощью можно организовать цикл любой нужной длины, а не только в 1 команду из числа тех, что поддерживают такой префикс.
omercury, выполняется это цикл столько, сколько в него засунули команд, а к dma он отношения не имеет.
А, понятно. То есть оптимизируем уже не ядро, и даже не два ядра, а МНОГО ядер :)
- - - Добавлено - - -
А кто сказал, что циклы 4-байтовые? :) Про сокращённые константы и так понятно, просто указал предельный случай.
- - - Добавлено - - -
Кажется, в арифметике ошибка, с логикой тоже не совсем согласен, но в целом мысль понятна.
Поэтому и пробую блоки на HDL, чтобы посмотреть до чего оптимизатор может их дооптимизировать.
- - - Добавлено - - -
omercury, говоря "аппаратные циклы" blackmirror имеет в виду такую штуку, которая добравшись до определённого адреса далее выбирает не PC+1, а некий другой адрес. Не какая-то djnz, а само, сразу. Да, похоже на DMA, но заталкивает байты он не в звуковую карту, а в блок выполнения команд. Но эта штука лично у меня вызывает много вопросов.
Дык об уде и речь, рыбу никто не обещал. :)
На оптимизатор надейся...
Из описания как раз и понял, что "аппаратные циклы" как раз пропускают циклы программные, "пододвигая" в конвейере инструкции и распараллеливая процессы аппаратными примочками. Единственная клубничка на тортик - без средств разрешения конфликтов...
Пришла тут мысль, что регистровый файл может быть вообще из одного только счётчика команд. Все команды при этом состоят из 4х слов: операция, первый операнд, второй операнд, результат. Все операнды и результат представлены своими адресами, то есть фактически мы получаем доступ к переменной по её адресу. Память для кода и данных общая, то есть если нужно, правим и код программы. Правда требуется выделить особый адрес обозначающий обращения к счётчику команд, чтобы можно было делать переходы.
Идея вроде бы бредовая, но когда команды в конвейере ждут чтения своих операндов выглядят они примерно так же.
Вопросы.
Первый.
По-моему я его уже задавал, но ответ не помню.
Почему rlc и rlc, имея в названии букву "c", прокручивают 8 бит, а rl и rr, не имеющие этой буквы, прокручивают 9 бит вместе с флагом переноса? Где логика?
Второй.
Флаги у Z80 конечно интересно устанавливаются, но в них есть скрытая система. Но вот cpl (инверсия аккумулятора) из этой системы выбивается, после неё перенос всегда должен быть установлен. Если кто в курсе, объясните почему она не меняет флаг переноса. В этом есть смысл? Или тоже "не баг, а фича"?
Третий.
Что если упростить эту систему флагов? Если, например, всегда будут меняться все флаги, смогут ли работать старые программы?
потому что c в данном случае значит cyclic
почему это должен быть всегда установлен? тогда уж сброшен, как после других побитовых операций
наверно, быстрое получение -1 из 0 для операций типа reg+carry-1 или хитрых сдвигов каких-нибудь
ведь и установка уже есть, сбрасывающих перенос команд хватает и так
всегда - это когда? ld тоже? :) но даже без неё работать точно не смогут, в частности, неполным влиянием inc/dec на флаги часто пользуются
Ух, ё... Да, вот теперь логика есть.
Нет. Это не битовая операция, потому что установлен флаг N, а его устанавливает только вычитание. Это "0x00-A-carry", carry=1. И при этом она выбивается из системы, как будто у неё отдельная блокировка на изменение этого флага.
Нет, ld без флагов :) И про inc/dec тоже знаю.
В общем, не могу расставить инструкции для нового процессора. То ли там какой-то облом есть, который я не вижу, то ли мой перфекционизм мешает :)
- - - Добавлено - - -
Пример облома, который можно не заметить.
Вот тебе фигурки (типа тетриса), надо заполнить ими прямоугольник. На третий день раскладывания возникает мысль: а тупо по площади они туда умещаются или нет? :)
0xFF - A
При каком значении A появится перенос h? Никогда.
0x00 - A - carry
При каком значении A появится перенос h? Всегда.
еще раз: ну при чём тут carry? когда результат от его значения не зависит
- - - Добавлено - - -
и у тебя там ДВА вычитания, так результатом первого или второго должен быть H?
- - - Добавлено - - -
если первого, то при a=1 не появится, если второго, то появится только для a=0