PPU A13 to detect scan line

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
PPU A13 to detect scan line
by on (#158246)
Is there a way via PPU A13 sign to detect scan line.
Re: PPU A13 to detect scan line
by on (#158248)
It is possible, but obnoxious. PPU A13 alternates 43 times per scanline, so dividing by 43 lets you count scanlines ... except on the first scanline when the pixel is missing, in which case it's 42.

If you can use both PPU A13 and PPU /RD, see INL's post (summary: at the end of the scanline, there's a dummy bonus name+attribute table fetch, followed by a real name+attribute table fetch. Once you see the third (or fourth) PPU/RD in a row while PPU A13 is still high, you know that's where rendering has to be)
Re: PPU A13 to detect scan line
by on (#158266)
lidnariq wrote:
It is possible, but obnoxious. PPU A13 alternates 43 times per scanline, so dividing by 43 lets you count scanlines ... except on the first scanline when the pixel is missing, in which case it's 42.

If you can use both PPU A13 and PPU /RD, see INL's post (summary: at the end of the scanline, there's a dummy bonus name+attribute table fetch, followed by a real name+attribute table fetch. Once you see the third (or fourth) PPU/RD in a row while PPU A13 is still high, you know that's where rendering has to be)


Yes , I can use both PPU A13 and PPU /RD,but how to detect the start of the scanline.
Re: PPU A13 to detect scan line
by on (#158271)
PPU A13 and PPU /RD can together detect the first nametable fetch on the scanline, which should happen ... well, fairly early in the scanline. I don't know exactly when, I've never been clear on what exactly is the difference between "2C02 hpos" and "pixel N on screen".

Or are you asking how to convert what I said to something you can use? That's be something like
Code:
 always @(negedge PPUnRD) begin
   if (PPUA13 == 1) begin
      if (Count < 3'd4) Count <= Count + 1;
      if (Count == 3'd4) begin
         Count <= 3'd5;
         IRQ <= 1;
      end
   end
   else Count <= 3'd0;
 end

except IRQ needs to be handled differently because I'm simply failing to merge clock domains here (in this case, PPUnRD and CPUM2)
Re: PPU A13 to detect scan line
by on (#158423)
lidnariq wrote:
PPU A13 and PPU /RD can together detect the first nametable fetch on the scanline, which should happen ... well, fairly early in the scanline. I don't know exactly when, I've never been clear on what exactly is the difference between "2C02 hpos" and "pixel N on screen".

Or are you asking how to convert what I said to something you can use? That's be something like
Code:
 always @(negedge PPUnRD) begin
   if (PPUA13 == 1) begin
      if (Count < 3'd4) Count <= Count + 1;
      if (Count == 3'd4) begin
         Count <= 3'd5;
         IRQ <= 1;
      end
   end
   else Count <= 3'd0;
 end

except IRQ needs to be handled differently because I'm simply failing to merge clock domains here (in this case, PPUnRD and CPUM2)


Image
Re: PPU A13 to detect scan line
by on (#158425)
I have no idea what you're trying to indicate with those two lines from Saleae. I see vblank (when both are high) and activity the rest of the time.
Re: PPU A13 to detect scan line
by on (#158426)
lidnariq wrote:
I have no idea what you're trying to indicate with those two lines from Saleae. I see vblank (when both are high) and activity the rest of the time.


infact, this code is OK.but not 43 times per scanline.I don't know why.
Code:
 always @(negedge PPUnRD) begin
   if (PPUA13 == 1) begin
      if (Count < 3'd4) Count <= Count + 1;
      if (Count == 3'd4) begin
         Count <= 3'd5;
         IRQ <= 1;
      end
   end
   else Count <= 3'd0;
 end
Re: PPU A13 to detect scan line
by on (#158449)
There are two different things:

1- PPU A13 alternates 43 times per scanline. (42 on the scanline with the 2C02's missing dot)

2- PPU A13 stays high for four successive reads right around the end/beginning of each scanline.

You can see both on http://wiki.nesdev.com/w/index.php/File:Ntsc_timing.png

1- shows up because there are 43 NT/AT fetches, 42 tile data fetches, and 1 idle pixel on each scanline

2- shows up because columns 337, 339, 1, and 3 are all NT/AT fetches.
Re: PPU A13 to detect scan line
by on (#158490)
lidnariq wrote:
There are two different things:

1- PPU A13 alternates 43 times per scanline. (42 on the scanline with the 2C02's missing dot)

2- PPU A13 stays high for four successive reads right around the end/beginning of each scanline.

You can see both on http://wiki.nesdev.com/w/index.php/File:Ntsc_timing.png

1- shows up because there are 43 NT/AT fetches, 42 tile data fetches, and 1 idle pixel on each scanline

2- shows up because columns 337, 339, 1, and 3 are all NT/AT fetches.


I found this MMC3 verilog sourcecode:
Code:
// This mapper also handles mapper 119 and 47.
module MMC3(input clk, input ce, input reset,
            input [31:0] flags,
            input [15:0] prg_ain, output [21:0] prg_aout,
            input prg_read, prg_write,                   // Read / write signals
            input [7:0] prg_din,
            output prg_allow,                            // Enable access to memory for the specified operation.
            input [13:0] chr_ain, output [21:0] chr_aout,
            output chr_allow,                            // Allow write
            output vram_a10,                             // Value for A10 address line
            output vram_ce,                              // True if the address should be routed to the internal 2kB VRAM.
            output reg irq);
  reg [2:0] bank_select;             // Register to write to next
  reg prg_rom_bank_mode;             // Mode for PRG banking
  reg chr_a12_invert;                // Mode for CHR banking
  reg mirroring;                     // 0 = vertical, 1 = horizontal
  reg irq_enable, irq_reload;        // IRQ enabled, and IRQ reload requested
  reg [7:0] irq_latch, counter;      // IRQ latch value and current counter
  reg ram_enable, ram_protect;       // RAM protection bits
  reg [6:0] chr_bank_0, chr_bank_1;  // Selected CHR banks
  reg [7:0] chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5;
  reg [5:0] prg_bank_0, prg_bank_1;  // Selected PRG banks
  wire prg_is_ram;
   
  // The alternative behavior has slightly different IRQ counter semantics.
  wire mmc3_alt_behavior = 0;
 
  // TQROM maps 8kB CHR RAM
  wire TQROM = (flags[7:0] == 119);
  wire TxSROM = (flags[7:0] == 118); // Connects CHR A17 to CIRAM A10
 
  // Mapper 47 is a multicart that has 128k for each game. It has no RAM.
  wire mapper47 = (flags[7:0] == 47);
  reg mapper47_multicart;
   
  wire [7:0] new_counter = (counter == 0 || irq_reload) ? irq_latch : counter - 1;
  reg [3:0] a12_ctr;
   
  always @(posedge clk)
  if (reset) //初始化
  begin
    irq <= 0;
    bank_select <= 0;
    prg_rom_bank_mode <= 0;
    chr_a12_invert <= 0;
    mirroring <= 0;
    {irq_enable, irq_reload} <= 0;
    {irq_latch, counter} <= 0;
    {ram_enable, ram_protect} <= 0;
    {chr_bank_0, chr_bank_1} <= 0;
    {chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5} <= 0;
    {prg_bank_0, prg_bank_1} <= 0;
    a12_ctr <= 0;
  end
  else if (ce)
  begin
    if (prg_write && prg_ain[15])//W 8000-FFFF
   begin
      case({prg_ain[14], prg_ain[13], prg_ain[0]})
      3'b00_0: {chr_a12_invert, prg_rom_bank_mode, bank_select} <= {prg_din[7], prg_din[6], prg_din[2:0]}; // Bank select ($8000-$9FFE, even)
      3'b00_1:
     begin // Bank data ($8001-$9FFF, odd)
        case (bank_select)
        0: chr_bank_0 <= prg_din[7:1];  // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
        1: chr_bank_1 <= prg_din[7:1];  // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
        2: chr_bank_2 <= prg_din;       // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
        3: chr_bank_3 <= prg_din;       // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
        4: chr_bank_4 <= prg_din;       // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
        5: chr_bank_5 <= prg_din;       // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
        6: prg_bank_0 <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
        7: prg_bank_1 <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $A000-$BFFF
        endcase
      end
      3'b01_0: mirroring <= prg_din[0];                   // Mirroring ($A000-$BFFE, even)
      3'b01_1: {ram_enable, ram_protect} <= prg_din[7:6]; // PRG RAM protect ($A001-$BFFF, odd)
      3'b10_0: irq_latch <= prg_din;                      // IRQ latch ($C000-$DFFE, even)
      3'b10_1: irq_reload <= 1;                           // IRQ reload ($C001-$DFFF, odd)
      3'b11_0: begin irq_enable <= 0; irq <= 0; end       // IRQ disable ($E000-$FFFE, even)
      3'b11_1: irq_enable <= 1;                           // IRQ enable ($E001-$FFFF, odd)
      endcase
    end
   
    // For Mapper 47
    // $6000-7FFF:  [.... ...B]  Block select
    if (prg_write && prg_is_ram)
      mapper47_multicart <= prg_din[0];
       
    // Trigger IRQ counter on rising edge of chr_ain[12]
    // All MMC3A's and non-Sharp MMC3B's will generate only a single IRQ when $C000 is $00.
    // This is because this version of the MMC3 generates IRQs when the scanline counter is decremented to 0.
    // In addition, writing to $C001 with $C000 still at $00 will result in another single IRQ being generated.
    // In the community, this is known as the "alternate" or "old" behavior.
    // All MMC3C's and Sharp MMC3B's will generate an IRQ on each scanline while $C000 is $00.
    // This is because this version of the MMC3 generates IRQs when the scanline counter is equal to 0.
    // In the community, this is known as the "normal" or "new" behavior.
    if (chr_ain[12] && a12_ctr == 0)
   begin
      counter <= new_counter;
      if ( (!mmc3_alt_behavior  || counter != 0 || irq_reload) && new_counter == 0 && irq_enable)
     begin
      irq <= 1;
      end
      irq_reload <= 0;     
    end
    a12_ctr <= chr_ain[12] ? 4'b1111 : (a12_ctr != 0) ? a12_ctr - 4'b0001 : a12_ctr;
  end

  // The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
  reg [5:0] prgsel;
  always @* begin
    casez({prg_ain[14:13], prg_rom_bank_mode})
    3'b00_0: prgsel = prg_bank_0;  // $8000 mode 0
    3'b00_1: prgsel = 6'b111110;   // $8000 fixed to second last bank
    3'b01_?: prgsel = prg_bank_1;  // $A000 mode 0,1
    3'b10_0: prgsel = 6'b111110;   // $C000 fixed to second last bank
    3'b10_1: prgsel = prg_bank_0;  // $C000 mode 1
    3'b11_?: prgsel = 6'b111111;   // $E000 fixed to last bank
    endcase
    // mapper47 is limited to 128k PRG, the top bits are controlled by mapper47_multicart instead.
    if (mapper47) prgsel[5:4] = {1'b0, mapper47_multicart};
  end

  // The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
  reg [7:0] chrsel;
  always @* begin
    casez({chr_ain[12] ^ chr_a12_invert, chr_ain[11], chr_ain[10]})
    3'b00?: chrsel = {chr_bank_0, chr_ain[10]};
    3'b01?: chrsel = {chr_bank_1, chr_ain[10]};
    3'b100: chrsel = chr_bank_2;
    3'b101: chrsel = chr_bank_3;
    3'b110: chrsel = chr_bank_4;
    3'b111: chrsel = chr_bank_5;
    endcase
    // mapper47 is limited to 128k CHR, the top bit is controlled by mapper47_multicart instead.
    if (mapper47) chrsel[7] = mapper47_multicart;
  end

  wire [21:0] prg_aout_tmp = {3'b00_0,  prgsel, prg_ain[12:0]};

  assign {chr_allow, chr_aout} =
      (TQROM && chrsel[6]) ? {1'b1,      9'b11_1111_111, chrsel[2:0], chr_ain[9:0]} :   // TQROM 8kb CHR-RAM
                             {flags[15], 4'b10_00, chrsel, chr_ain[9:0]};               // Standard MMC3

  assign prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000 && ram_enable && !(ram_protect && prg_write);
  assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram && !mapper47;
  wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
  assign prg_aout = prg_is_ram  && !mapper47 ? prg_ram : prg_aout_tmp;
  assign vram_a10 = (TxSROM == 0) ? (mirroring ? chr_ain[11] : chr_ain[10]) :
                                    chrsel[7];
  assign vram_ce = chr_ain[13];
endmodule
Re: PPU A13 to detect scan line
by on (#158492)
MMC3 detects IRQs in an entirely different manner. It uses PPU A12 and M2:
* On a rising edge of PPU A12, assert IRQCLOCK
* While PPU A12 is low, if M2 rises twice, deassert IRQCLOCK

The verilog you found is slightly different.
Re: PPU A13 to detect scan line
by on (#158493)
lidnariq wrote:
MMC3 detects IRQs in an entirely different manner. It uses PPU A12 and M2:
* On a rising edge of PPU A12, assert IRQCLOCK
* While PPU A12 is low, if M2 rises twice, deassert IRQCLOCK

The verilog you found is slightly different.


It means While PPU A12 is low, if M2 rises twice, I can count scanline?
I need a way to detect scanline, if can use PPU A12 and M2, is also OK.
Re: PPU A13 to detect scan line
by on (#158494)
Yeah, something approximately like
Code:
always @(posedge M2) begin
  if (PPUA12 == 0 && M2counter < 3) M2counter <= M2counter + 1;
  if (PPUA12 == 1) M2counter <= 0;
end

assign IRQclock = ~((M2counter < 2 & nIRQclock);
assign nIRQclock = ~((~PPUA12 & IRQclock);

I've probably made a stupid braino in that, though.
Re: PPU A13 to detect scan line
by on (#158495)
lidnariq wrote:
Yeah, something approximately like
Code:
always @(posedge M2) begin
  if (PPUA12 == 0 && M2counter < 3) M2counter <= M2counter + 1;
  if (PPUA12 == 1) M2counter <= 0;
end

assign IRQclock = ~((M2counter < 2 & nIRQclock);
assign nIRQclock = ~((~PPUA12 & IRQclock);

I've probably made a stupid braino in that, though.


It's OK, Thank you , I will try to test on my dev cart. and try to log out my original cart.