Кстати, я тут уже говорил о принципах формирования видео у PPU. И уже провел все необходимые опыты (пока только NTSC, но скоро доберусь и до PAL). Сетап:

Verilog кодера NTSC по правилам PPU (используется 9 уровней на 8битном ЦАП, и его можно упростить):
Код:
module PAL_NTSC_Gen(
    input       NTSC_Clk,     // 21.4772 MHz
    input       PAL_Clk,      // 53.2034 MHz
    output      [7:0]DAC,
    output      [3:0]COUT,
    output      [12:1]PHOUT
);
// For test purpose
assign COUT[3:0] = HCOUNT[7:4];
assign PHOUT[12:1] = PH[12:1];

// Регистры
reg [2:0]PH_PSC;        // Phase prescaler
reg [12:1]PH;           // Color subcarrier phases
reg [3:0]PIX_PSC;       // Pixelclock prescaler
reg PIX_CLK;            // Pixelclock strobe
reg [9:0]HCOUNT;        // Horizontal counter
reg [9:0]VCOUNT;        // Vertical counter
reg HSYNC;              // Horizontal sync
reg BURST;              // Color burst
reg HSCREEN;            // Active video in scanline
reg VSYNC;              // Vertical sync
reg VSCREEN;            // Active video in frame

// used levels
wire [7:0]L0;
wire [7:0]L1;
wire [7:0]L2;
wire [7:0]L3;
wire [7:0]L4;
wire [7:0]L5;
wire [7:0]L6;
wire [7:0]L7;
wire [7:0]L8;
wire [7:0]L9;
// NTSC
assign L0 = 8'h00;
assign L1 = 8'h10;
assign L2 = 8'h18;
assign L3 = 8'h20;
assign L4 = 8'h32;
assign L5 = 8'h36;
assign L6 = 8'h3C;
assign L7 = 8'h4E;
assign L8 = 8'h55;
assign L9 = 8'h6E;

// Luma
wire [7:0]LUMAL;
wire [7:0]LUMAH;
assign LUMAL[7:0] = (~VCOUNT[6]) ?
                        (~VCOUNT[5]) ? L2[7:0] : L3[7:0]
                    :
                        (~VCOUNT[5]) ? L5[7:0] : L8[7:0];
assign LUMAH[7:0] = (~VCOUNT[6]) ?
                        (~VCOUNT[5]) ? L6[7:0] : L7[7:0]
                    : L9[7:0];

// Chroma
wire [7:0]CHROMA;
assign CHROMA[7:0] =    (~HCOUNT[7]) ?
                            (~HCOUNT[6]) ?
                                (~HCOUNT[5]) ?
                                    (~HCOUNT[4]) ?
                                        LUMAH[7:0]
                                        :
                                        (PH[1]) ? LUMAH[7:0] : LUMAL[7:0]
                                :
                                    (~HCOUNT[4]) ?
                                        (PH[2]) ? LUMAH[7:0] : LUMAL[7:0]
                                        :
                                        (PH[3]) ? LUMAH[7:0] : LUMAL[7:0]
                            :
                                (~HCOUNT[5]) ?
                                    (~HCOUNT[4]) ?
                                        (PH[4]) ? LUMAH[7:0] : LUMAL[7:0]
                                        :
                                        (PH[5]) ? LUMAH[7:0] : LUMAL[7:0]
                                :
                                    (~HCOUNT[4]) ?
                                        (PH[6]) ? LUMAH[7:0] : LUMAL[7:0]
                                        :
                                        (PH[7]) ? LUMAH[7:0] : LUMAL[7:0]
                        :
                            (~HCOUNT[6]) ?
                                (~HCOUNT[5]) ?
                                    (~HCOUNT[4]) ?
                                        (PH[8]) ? LUMAH[7:0] : LUMAL[7:0]
                                        :
                                        (PH[9]) ? LUMAH[7:0] : LUMAL[7:0]
                                :
                                    (~HCOUNT[4]) ?
                                        (PH[10]) ? LUMAH[7:0] : LUMAL[7:0]
                                        :
                                        (PH[11]) ? LUMAH[7:0] : LUMAL[7:0]
                            :
                                (~HCOUNT[5]) ?
                                    (~HCOUNT[4]) ?
                                        (PH[12]) ? LUMAH[7:0] : LUMAL[7:0]
                                        :
                                        LUMAL[7:0]
                                :
                                    (~HCOUNT[4]) ?
                                        L3[7:0]
                                        :
                                        L3[7:0];
// Video output
assign DAC[7:0] = (VSYNC | HSYNC) ?
                     // This is correct composite sync
                     (VSYNC & HSYNC) ? L3[7:0] : L0[7:0]
                  :  (BURST) ?
                        // This is color burst
                        (PH[8]) ? L4[7:0] : L1[7:0]
                     :    (VSCREEN & HSCREEN) ?
                              // this is picture
                              CHROMA[7:0]
                          // Otherwise it is blank
                          : L3[7:0];

// Because I don't have 42,9544 MHz generator and CPLD don't allow sync always block on both edge we update only even phases at negedge.
always @(negedge NTSC_Clk) begin
    {PH[11],PH[9],PH[7],PH[5],PH[3],PH[1]} <= {PH[12],PH[11],PH[9],PH[7],PH[5],PH[3]};
end
// Video generator
always @(posedge NTSC_Clk) begin
    // Color subcarrier phase prescaler
    if (PH_PSC[1] & !PH_PSC[0])
    begin
        PH_PSC[2:0] <= 3'h0; PH[12] <= !PH[12];
    end else PH_PSC[2:0] <= PH_PSC[2:0] + 3'h1;
    {PH[10],PH[8],PH[6],PH[4],PH[2]} <= {PH[12],PH[10],PH[8],PH[6],PH[4]};
   
    // Pixelclock prescaler
    PIX_PSC[3:0] <= PIX_PSC[3:0] + 4'h1;
    PIX_CLK <= !PIX_PSC[1] & !PIX_PSC[0];

    // HV COUNTERS: 341 pixels and 262 scanlines
    if (PIX_CLK)
    begin
        if (HCOUNT[9:0] == 9'd340)
        begin
            HCOUNT[9:0] <= 9'd000;
            if (VCOUNT[9:0] == 9'd261) VCOUNT[9:0] <= 9'd000; else VCOUNT[9:0] <= VCOUNT[9:0] + 9'd001;
        end else HCOUNT[9:0] <= HCOUNT[9:0] + 9'd001;
    end

    // Active video in scanline
    if (HCOUNT[9:0] == 9'd000) HSCREEN <= 1'b1;
    if (HCOUNT[9:0] == 9'd255) HSCREEN <= 1'b0;
    // Horizontal sync
    if (HCOUNT[9:0] == 9'd280) HSYNC <= 1'b1;
    if (HCOUNT[9:0] == 9'd305) HSYNC <= 1'b0;
    // Color burst
    if (HCOUNT[9:0] == 9'd309) BURST <= 1'b1;
    if (HCOUNT[9:0] == 9'd325) BURST <= 1'b0;
    // Vertical sync
    if (VCOUNT[9:0] == 9'd244) VSYNC <= 1'b1;
    if (VCOUNT[9:0] == 9'd248) VSYNC <= 1'b0;
    // Active video in frame
    if (VCOUNT[9:0] == 9'd000) VSCREEN <= 1'b1;
    if (VCOUNT[9:0] == 9'd240) VSCREEN <= 1'b0;
end
// end
endmodule
Этот код на частоте 21,47МГц формирует вот такую картинку:

При этом, из главной частоты формируются 12 фаз поднесущей (6 прямых и 6 инверсных, мастерклок ровно в 6 раз больше поднесущей):


И напоследок референс с оригинального NTSC PPU 2C02:

Как видно, цвета полностью соответствуют. Теперь надо сделать то же самое, но для PAL.

PS Спектрумостроителям на CPLD/FPGA на заметку.