Лисья Нора

Оглавление


§ Тайминги

Сегодня я хочу просто рассказать о том, как можно довольно легко и без проблем сделать вывод экрана спектрума на VGA монитор. Да, я как бы, говорю что легко, и без проблем, но это лишь потому, что я уже много раз делал этот видеовывод, да и сам он по себе простой.
Самая большая проблема заключается в другом. Тайминги, которые совершенно не совместимы между VGA 25 Мгц и телевизионным сигналом, который генерировал спектрум. Возьмем за основу самое стандартное разрешение VGA 640 x 480, и его тайминги.
| HORIZ VERT | ZX HORIZ VERT
Задний порожек | 48 33 | 48 48
Видимая область | 640 480 | 256 192
Передний порожек | 16 10 | 48 56
Синхронизация | 96 2 | 96 16
ИТОГО | 800 525 | 448 312
Итого, 800 пиксель-клоков на одну строку и 525 строк, 60 кадров в секунду. Красиво и быстро. А теперь, очень интересный момент. Надо сделать 50 кадров. Вот этот момент полон печали и тоски. Мало того, что надо сделать 50 кадров в секунду, так еще надо другие тайминги. Частота видеопроцессора у спектрума 7 Мгц.
В таблице у спектрума немного не так все работает. На самом деле, там 48 и 48 пиксельклока отведены для рисования бордера, а не порожеков, которые у VGA равны черному цвету, так что можно считать что общая область рисования картинки равна 352 пикселей и 96 это синхронизация. Высота картинки тоже составляет 296 пикселей. Это очень сильно много, так что вместить ничего не получится на VGA, чтобы было нормально, потому часть бордера придется скрыть. Например, слева и справа будет по 32 пикселя, а не по 48. А сверху и снизу, кстати говоря, 48 пикселей получится сделать (снизу все равно будет не 56).
Так что как бы я ни старался, уложить точно экран у меня не выйдет. Но ладно, я тогда буду просто пытаться делать максимально приблизительно и на 60 Гц вместо 50 Гц, что даст увеличение на 6/5 скорости работы процессора (+20%) чтобы четко и точно успевать бегать за лучом.

§ Видеоадаптер

Это общий код к модулю пиксельному процессору ZX Spectrum. Используется адрес от $4000 до $5AFF включительно.
/* verilator lint_off WIDTH */
/* verilator lint_off CASEINCOMPLETE */
 
module zxppu
(
input CLOCK,
output reg [3:0] R,
output reg [3:0] G,
output reg [3:0] B,
output HS,
output VS,
 
// Считывание данных
output reg [15:0] A,
input [ 7:0] D,
input [ 2:0] BORDER
);
 
// -----------------------------------------------------------------------------
// Тайминги для горизонтальной и вертикальной развертки
// Visible Front Sync Back Whole
parameter hzv = 640, hzf = 16, hzs = 96, hzb = 48, hzw = 800,
vtv = 400, vtf = 12, vts = 2, vtb = 35, vtw = 449;
// -----------------------------------------------------------------------------
assign HS = X < (hzb + hzv + hzf); // NEG
assign VS = Y >= (vtb + vtv + vtf); // POS
// -----------------------------------------------------------------------------
// Позиция луча в кадре и максимальные позиции (x,y)
reg [ 9:0] X = 0; wire xmax = (X == hzw - 1);
reg [ 9:0] Y = 0; wire ymax = (Y == vtw - 1);
wire [ 9:0] x = (X - hzb);
wire [ 9:0] y = (Y - vtb);
wire [ 7:0] xm = ((X - hzb - 64) >> 1) + 8;
wire [ 7:0] ym = ((Y - vtb - 8) >> 1);
// -----------------------------------------------------------------------------
reg [ 7:0] temp;
reg [ 7:0] attr;
reg [ 7:0] mask;
// -----------------------------------------------------------------------------
wire cbit = mask[ 3'h7 ^ x[3:1] ];
wire paper = (x >= 64 && x < 64 + 512 && y >= 8 && y < 8 + 384);
wire [ 3:0] K = paper ? ({attr[6], cbit ^ (attr[7] & flash) ? attr[2:0] : attr[5:3]}) : BORDER;
// -----------------------------------------------------------------------------
 
always @(posedge CLOCK) begin
 
// Черный цвет по краям
{R, G, B} <= 12'h000;
 
// Кадровая развертка
X <= xmax ? 0 : X + 1;
Y <= xmax ? (ymax ? 0 : Y + 1) : Y;
 
// Вывод окна видеоадаптера
if (X >= hzb && X < hzb + hzv && Y >= vtb && Y < vtb + vtv)
begin
 
// Цвет или бордер
{R, G, B} <=
{
{2{K[1]}}, {2{K[1] & K[3]}}, // RED
{2{K[2]}}, {2{K[2] & K[3]}}, // GREEN
{2{K[0]}}, {2{K[0] & K[3]}} // BLUE
};
 
case (x[3:0])
// Запрос CHAR $4000-$5700 (6144)
4'h0: begin A <= {ym[7:6], ym[2:0], ym[5:3], xm[7:3]} | 16'h4000; end
// Запрос ATTR $5800-$5AFF (768)
4'hE: begin A <= {ym[7:3], xm[7:3]} | 16'h5800; temp <= D; end
// Следующие 8 бит цвета готовы
4'hF: begin mask <= temp; attr <= D; end
endcase
 
end
 
end
 
// ---------------------------------------------------------------------
 
reg flash;
reg [23:0] timer;
 
always @(posedge CLOCK) begin
 
if (timer == 12500000) begin /* полсекунды */
timer <= 1'b0;
flash <= flash ^ 1'b1; // мигать каждые 0.5 секунд
end else begin
timer <= timer + 1'b1;
end
 
end
 
endmodule