Там схема в LogiSim нарисована, это вообще жесть, представляю сколько оно рисовалось
Там схема в LogiSim нарисована, это вообще жесть, представляю сколько оно рисовалось
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Именно. Я помню в детстве у меня был вот такой ЧБ телевизор "Кварц":
Он максимально тупой и когда его кинескоп был новым показывал просто отменную по четкости картинку. И действительно, те Денди, что у меня были, а так же Спектрум показывал на нем без черезстрочности. Т.е., как сейчас модно говорить: 240р. Это когда оба поля одинаковые. Чтобы поля отличались, нужно чтобы количество строк в каждом полуполе отличалось. У нас есть сейчас только NTSC версия и там есть вот такой узел:
Я не знаю, зачем сделано именно так, но если запретить перекидываться триггеру по V[8] то NTSC тоже перестает crawl! Вот чему равен LINE5:
Основной: 1 0101 0100, корректирующий: 1 0101 0011, т.е. разница буквально на 1 пиксель.
- - - Добавлено - - -
Опять же, там стоит AND, и сократиться на 1 пиксель только строка пререндера, на которую указывает сигнал RESCL... Причем, в каждом втором кадре. Но эффект есть.
- - - Добавлено - - -
Vslav, это рандомная логика ядра 6502.
Последний раз редактировалось HardWareMan; 20.10.2017 в 17:33.
Vslav, это что, они вон первозыч туда пихают... Но это уже оффтопик, хоть и близко к реверсу.
Сегодня поговорим о первом блоке PPU: выбор регистра для чтения или записи. Почему он? Он единственный который в качестве входа использует внешние пины (со стороны процессора) и формирует внутренние сигналы. Вот так он выглядит в оригинальном схемном вводе:
Здесь: N_DBE это строб обращения, R_W это направление обращения (лог.0 - запись в PPU), A[2..0] - жгут адресных линий. Сюда же следует добавить вот эту защелку, которая сохраняет записываемые данные для внутренних нужд:
Собственно, здесь все просто. Адреса поданы на дешифратор, который преобразует 3х битный код в 8 сигналов. Эти 8 сигналов смешиваются с сигналами управления (N_DBE и R_W), от чего получается набор сигналов Rx и Wx. Первый означает чтение из регистра а второй запись в него. Не все регистры полноценны: некоторые доступны только на чтение а некоторые только на запись. Узел вверху на мультиплексорах это обычная перекидушка на эквивалентах защелок (помните, мультиплексор на 4х транзисторах?). Он устанавливается в конкретное состояние при чтении из регистра #2 - по сигналу R2, а перекидывается при обращении к регистрам #5 и #6, по сигналам R5_x и R6_x. Это двойные регистры, первый (#5) регистр прокрутки а второй (#6) регистр адреса VRAM для обращения процессора. Особенность PPU в том, что для рендера и для обращения процессором к VRAM используется один и тот же счетчик, так что обращаться к VRAM следует только во время кадрового гашения либо при отключенном рендере, иначе результат не предсказуем.
После чистки схема этого узла получается следующей:
В принципе, она практически не отличается от исходной. Однако она будет немного отличаться в Верилоге. Я объясню. Дело в том, что входные сигналы являются полностью асинхронными к внутренним сигналам и поэтому их надо синхронизировать. Как говорят в книжках: перевести в локальный (рабочий) тактовый домен. Так как сигналы полностью асинхроны к частоте ядра, то привязывать их можно только к главному мастерклоку - самой быстрой доступной частоте. Это гарантирует, что ядро не пропустит входящий запрос. В Верилоге это будет выглядеть так:
Для начала мы наберем наш PPU в виде отдельных блоков-модулей, а потом объеденим их под одной крышей, чтобы не плодить сущностей. Я думаю тут комментировать не надо, это действительно самый простой из модулей.Код:// Управление регистрами PPU со стороны CPU module PPU_REG_SEL( // Входные цепи input Clk, // Тактовая последовательность input [2:0]Adr, // Адрес регистра input RnW, // Направление обращения R/W input nSTB, // Строб обращения к PPU input [7:0]DIn, // Входные данные // Выходные цепи output reg W0, // Запись в регистр #0 output reg W1, // Запись в регистр #1 output reg R2, // Чтение из регистра #2 output reg W3, // Запись в регистр #3 output reg R4, // Чтение из регистра #4 output reg W4, // Запись в регистр #4 output reg W5_1, // Запись в регистр #5/1 output reg W5_2, // Запись в регистр #5/2 output reg W6_1, // Запись в регистр #6/1 output reg W6_2, // Запись в регистр #6/2 output reg R7, // Чтение из регистра #7 output reg W7, // Запись в регистр #7 output reg [7:0]DOut // Выходные данные ); // Внутренние регистры reg [2:0]AdrR; reg RnWR; reg nSTBR; reg Sel; reg FlipR; wire Flip; assign Flip = W5_1 | W5_2 | W6_1 | W6_2; // Логика работы проста always @(posedge Clk) begin // Синхронизируем сигналы AdrR[2:0] <= Adr[2:0]; RnWR <= RnW; nSTBR <= nSTB; // Декодируем W0 <= ~AdrR[2] & ~AdrR[1] & ~AdrR[0] & ~RnWR & ~nSTBR; W1 <= ~AdrR[2] & ~AdrR[1] & AdrR[0] & ~RnWR & ~nSTBR; R2 <= ~AdrR[2] & AdrR[1] & ~AdrR[0] & RnWR & ~nSTBR; W3 <= ~AdrR[2] & AdrR[1] & AdrR[0] & ~RnWR & ~nSTBR; R4 <= AdrR[2] & ~AdrR[1] & ~AdrR[0] & RnWR & ~nSTBR; W4 <= AdrR[2] & ~AdrR[1] & ~AdrR[0] & ~RnWR & ~nSTBR; W5_1 <= AdrR[2] & ~AdrR[1] & AdrR[0] & ~RnWR & ~nSTBR & ~Sel; W5_2 <= AdrR[2] & ~AdrR[1] & AdrR[0] & ~RnWR & ~nSTBR & Sel; W6_1 <= AdrR[2] & AdrR[1] & ~AdrR[0] & ~RnWR & ~nSTBR & ~Sel; W6_2 <= AdrR[2] & AdrR[1] & ~AdrR[0] & ~RnWR & ~nSTBR & Sel; R7 <= AdrR[2] & AdrR[1] & AdrR[0] & RnWR & ~nSTBR; W7 <= AdrR[2] & AdrR[1] & AdrR[0] & ~RnWR & ~nSTBR; // Выбор регистра из пары if (R2) Sel <= 1'b0; else if (FlipR & ~Flip) Sel <= ~Sel; FlipR <= Flip; // Запоминаем данные if (~nSTB & ~RnW) DOut[7:0] <= DIn[7:0]; end // Конец модуля endmodule
Последний раз редактировалось HardWareMan; 20.10.2017 в 19:20.
Рендер начинает брать данные с другого места, а в моменты перезагрузок счетчиков восстанавливать некоторые адреса, мешая собственно самому CPU.
Titus, ессно, мы же повторим внутреннюю логическую структуру.
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)