Browse Source

vdp renderer, improvements for 8bit fast

improved 240 lines support, add setting buffer width, structural improvements
kub 3 years ago
parent
commit
f55fb31463
6 changed files with 152 additions and 103 deletions
  1. 1 1
      Makefile
  2. 1 1
      pico/32x/32x.c
  3. 3 2
      pico/draw.c
  4. 142 97
      pico/draw2.c
  5. 2 1
      pico/pico.h
  6. 3 1
      pico/pico_int.h

+ 1 - 1
Makefile

@@ -167,7 +167,7 @@ PLATFORM_MP3 = 1
 PLATFORM_ZLIB = 1
 endif
 ifeq "$(PLATFORM)" "psp"
-CFLAGS += -DUSE_BGR565 -DDRAW2_OVERRIDE_LINE_WIDTH=512 -G8 # -DLPRINTF_STDIO -DFW15
+CFLAGS += -DUSE_BGR565 -G8 # -DLPRINTF_STDIO -DFW15
 LDFLAGS := $(filter-out -lpsp%, $(LDFLAGS)) # collides with PSP image generation
 LDLIBS += -lpspnet_inet -lpspgu -lpspge -lpsppower -lpspaudio -lpspdisplay
 LDLIBS += -lpspaudiocodec -lpsprtc -lpspctrl -lpspsdk -lpspuser -lpspkernel

+ 1 - 1
pico/32x/32x.c

@@ -224,7 +224,7 @@ static void p32x_start_blank(void)
     pprof_start(draw);
 
     offs = 8; lines = 224;
-    if ((Pico.video.reg[1] & 8) && !(PicoIn.opt & POPT_ALT_RENDERER)) {
+    if (Pico.video.reg[1] & 8) {
       offs = 0;
       lines = 240;
     }

+ 3 - 2
pico/draw.c

@@ -1840,6 +1840,7 @@ PICO_INTERNAL void PicoFrameStart(void)
   if (!(Pico.video.reg[12] & 1))
     Pico.est.rendstatus |= PDRAW_32_COLS;
   if (Pico.video.reg[1] & 8) {
+    Pico.est.rendstatus |= PDRAW_30_ROWS;
     offs = 0;
     lines = 240;
   }
@@ -1984,7 +1985,7 @@ void PicoDrawSetOutFormat(pdso_t which, int use_32x_line_mode)
 {
   PicoDrawSetInternalBuf(NULL, 0);
   PicoDrawSetOutBufMD(NULL, 0);
-  PicoDraw2SetOutBuf(NULL);
+  PicoDraw2SetOutBuf(NULL, 0);
   switch (which)
   {
     case PDF_8BIT:
@@ -2016,7 +2017,7 @@ void PicoDrawSetOutBufMD(void *dest, int increment)
     // kludge for no-copy mode, using ALT_RENDERER layout
     PicoDrawSetInternalBuf(dest, increment);
   } else if (FinalizeLine == NULL) {
-    PicoDraw2SetOutBuf(dest);
+    PicoDraw2SetOutBuf(dest, increment);
   } else if (dest != NULL) {
     DrawLineDestBase = dest;
     DrawLineDestIncrement = increment;

+ 142 - 97
pico/draw2.c

@@ -23,7 +23,7 @@
 #define LINE_WIDTH 328
 #endif
 
-static unsigned char PicoDraw2FB_[(8+320) * (8+240+8) + 8];
+static unsigned char PicoDraw2FB_[LINE_WIDTH * (8+240+8) + 8];
 
 static u32 HighCache2A[2*41*(TILE_ROWS+1)+1+1]; // caches for high layers
 static u32 HighCache2B[2*41*(TILE_ROWS+1)+1+1];
@@ -43,16 +43,17 @@ void DrawSpriteFull(unsigned int *sprite, struct PicoEState *est);
 #else
 
 
-static int TileXnormYnorm(unsigned char *pd,int addr,unsigned char pal, struct PicoVideo *pvid)
+static int TileXnormYnorm(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)
 {
 	unsigned int pack=0; unsigned int t=0, blank = 1;
+	unsigned short *vram = est->PicoMem_vram;
 	int i, inc=2;
 
 #if INTERLACE
-	if ((pvid->reg[12]&6) == 6) inc = 4;
+	if (est->rendstatus & PDRAW_INTERLACE) inc = 4;
 #endif
-	for(i=8; i; i--, addr+=inc, pd += LINE_WIDTH) {
-		pack=*(u32 *)(PicoMem.vram+addr); // Get 8 pixels
+	for(i=8; i; i--, addr+=inc, pd += est->Draw2Width) {
+		pack=*(u32 *)(vram+addr); // Get 8 pixels
 		if(!pack) continue;
 
 		t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);
@@ -69,16 +70,17 @@ static int TileXnormYnorm(unsigned char *pd,int addr,unsigned char pal, struct P
 	return blank; // Tile blank?
 }
 
-static int TileXflipYnorm(unsigned char *pd,int addr,unsigned char pal, struct PicoVideo *pvid)
+static int TileXflipYnorm(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)
 {
 	unsigned int pack=0; unsigned int t=0, blank = 1;
+	unsigned short *vram = est->PicoMem_vram;
 	int i, inc=2;
 
 #if INTERLACE
-	if ((pvid->reg[12]&6) == 6) inc = 4;
+	if (est->rendstatus & PDRAW_INTERLACE) inc = 4;
 #endif
-	for(i=8; i; i--, addr+=inc, pd += LINE_WIDTH) {
-		pack=*(u32 *)(PicoMem.vram+addr); // Get 8 pixels
+	for(i=8; i; i--, addr+=inc, pd += est->Draw2Width) {
+		pack=*(u32 *)(vram+addr); // Get 8 pixels
 		if(!pack) continue;
 
 		t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);
@@ -94,17 +96,18 @@ static int TileXflipYnorm(unsigned char *pd,int addr,unsigned char pal, struct P
 	return blank; // Tile blank?
 }
 
-static int TileXnormYflip(unsigned char *pd,int addr,unsigned char pal, struct PicoVideo *pvid)
+static int TileXnormYflip(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)
 {
 	unsigned int pack=0; unsigned int t=0, blank = 1;
+	unsigned short *vram = est->PicoMem_vram;
 	int i, inc=2;
 
 #if INTERLACE
-	if ((pvid->reg[12]&6) == 6) inc = 4, addr += 16;
+	if (est->rendstatus & PDRAW_INTERLACE) inc = 4, addr += 16;
 #endif
 	addr+=14;
-	for(i=8; i; i--, addr-=inc, pd += LINE_WIDTH) {
-		pack=*(u32 *)(PicoMem.vram+addr); // Get 8 pixels
+	for(i=8; i; i--, addr-=inc, pd += est->Draw2Width) {
+		pack=*(u32 *)(vram+addr); // Get 8 pixels
 		if(!pack) continue;
 
 		t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);
@@ -121,17 +124,18 @@ static int TileXnormYflip(unsigned char *pd,int addr,unsigned char pal, struct P
 	return blank; // Tile blank?
 }
 
-static int TileXflipYflip(unsigned char *pd,int addr,unsigned char pal, struct PicoVideo *pvid)
+static int TileXflipYflip(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)
 {
 	unsigned int pack=0; unsigned int t=0, blank = 1;
+	unsigned short *vram = est->PicoMem_vram;
 	int i, inc=2;
 
 #if INTERLACE
-	if ((pvid->reg[12]&6) == 6) inc = 4, addr += 16;
+	if (est->rendstatus & PDRAW_INTERLACE) inc = 4, addr += 16;
 #endif
 	addr+=14;
-	for(i=8; i; i--, addr-=inc, pd += LINE_WIDTH) {
-		pack=*(u32 *)(PicoMem.vram+addr); // Get 8 pixels
+	for(i=8; i; i--, addr-=inc, pd += est->Draw2Width) {
+		pack=*(u32 *)(vram+addr); // Get 8 pixels
 		if(!pack) continue;
 
 		t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);
@@ -151,9 +155,10 @@ static int TileXflipYflip(unsigned char *pd,int addr,unsigned char pal, struct P
 // start: (tile_start<<16)|row_start, end: [same]
 static void DrawWindowFull(int start, int end, int prio, struct PicoEState *est)
 {
-	struct PicoVideo *pvid=&Pico.video;
+	struct PicoVideo *pvid=&est->Pico->video;
 	int nametab, nametab_step, trow, tilex, blank=-1, code;
 	unsigned char *scrpos = est->Draw2FB;
+	int scrstart = est->Draw2Start;
 	int tile_start, tile_end; // in cells
 
 	// parse ranges
@@ -172,17 +177,17 @@ static void DrawWindowFull(int start, int end, int prio, struct PicoEState *est)
 	{
 		nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode
 		nametab_step = 1<<5;
-		if (!(PicoIn.opt&POPT_DIS_32C_BORDER))
+		if (est->rendstatus & PDRAW_BORDER_32)
 			scrpos += 32;
 	}
-	nametab += nametab_step*start;
+	nametab += nametab_step*(start-scrstart);
 
 	// check priority
 	code=PicoMem.vram[nametab+tile_start];
 	if ((code>>15) != prio) return; // hack: just assume that whole window uses same priority
 
-	scrpos+=8*LINE_WIDTH+8;
-	scrpos+=8*LINE_WIDTH*(start-START_ROW);
+	scrpos+=8*est->Draw2Width+8;
+	scrpos+=8*est->Draw2Width*(start-scrstart);
 
 	// do a window until we reach planestart row
 	for(trow = start; trow < end; trow++, nametab+=nametab_step) { // current tile row
@@ -196,21 +201,26 @@ static void DrawWindowFull(int start, int end, int prio, struct PicoEState *est)
 			if (code==blank) continue;
 
 			// Get tile address/2:
-			addr=(code&0x7ff)<<4;
+#if INTERLACE
+			if (est->rendstatus & PDRAW_INTERLACE)
+				addr=(code&0x3ff)<<5;
+			else
+#endif
+				addr=(code&0x7ff)<<4;
 
 //			pal=PicoCramHigh+((code>>9)&0x30);
 			pal=(unsigned char)((code>>9)&0x30);
 
 			switch((code>>11)&3) {
-				case 0: zero=TileXnormYnorm(scrpos+(tilex<<3),addr,pal,pvid); break;
-				case 1: zero=TileXflipYnorm(scrpos+(tilex<<3),addr,pal,pvid); break;
-				case 2: zero=TileXnormYflip(scrpos+(tilex<<3),addr,pal,pvid); break;
-				case 3: zero=TileXflipYflip(scrpos+(tilex<<3),addr,pal,pvid); break;
+				case 0: zero=TileXnormYnorm(scrpos+(tilex<<3),addr,pal,est); break;
+				case 1: zero=TileXflipYnorm(scrpos+(tilex<<3),addr,pal,est); break;
+				case 2: zero=TileXnormYflip(scrpos+(tilex<<3),addr,pal,est); break;
+				case 3: zero=TileXflipYflip(scrpos+(tilex<<3),addr,pal,est); break;
 			}
 			if(zero) blank=code; // We know this tile is blank now
 		}
 
-		scrpos += LINE_WIDTH*8;
+		scrpos += est->Draw2Width*8;
 	}
 }
 
@@ -218,11 +228,12 @@ static void DrawWindowFull(int start, int end, int prio, struct PicoEState *est)
 static void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,
 			  struct PicoEState *est)
 {
-	struct PicoVideo *pvid=&Pico.video;
+	struct PicoVideo *pvid=&est->Pico->video;
 	static char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps
 	int width, height, ymask, htab;
 	int nametab, hscroll=0, vscroll, cells;
 	unsigned char *scrpos;
+	int scrstart = est->Draw2Start;
 	int blank=-1, xmask, nametab_row, trow;
 
 	// parse ranges
@@ -257,9 +268,9 @@ static void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,
 	else          nametab=(pvid->reg[4]&0x07)<<12; // B
 
 	scrpos = est->Draw2FB;
-	if (!(pvid->reg[12]&1) && !(PicoIn.opt&POPT_DIS_32C_BORDER))
+	if (est->rendstatus & PDRAW_BORDER_32)
 		scrpos += 32;
-	scrpos+=8*LINE_WIDTH*(planestart-START_ROW);
+	scrpos+=8*est->Draw2Width*(planestart-scrstart);
 
 	if((pvid->reg[11]&4)||(PicoMem.vsram[plane]&7))
 		planeend++; // we (may) have vertically clipped tiles due to vscroll, so we need 1 more row
@@ -273,7 +284,8 @@ static void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,
 			vscroll=PicoMem.vsram[plane+0x20];//&0x1ff;
 #endif
 #if INTERLACE
-		if ((pvid->reg[12]&6) == 6) vscroll >>= 1;
+		if (est->rendstatus & PDRAW_INTERLACE)
+			vscroll >>= 1;
 #endif
 		nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]); // pointer to nametable entries for this row
 
@@ -303,7 +315,8 @@ static void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,
 			if ((pvid->reg[11]&4) && !(vsidx&1)) { // 2-cell mode
 				vscroll=PicoMem.vsram[vsidx+plane];//&0x1ff;
 #if INTERLACE
-				if ((pvid->reg[12]&6) == 6) vscroll >>= 1;
+				if (est->rendstatus & PDRAW_INTERLACE)
+					vscroll >>= 1;
 #endif
 				nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]); // pointer to nametable entries for this row
 			}
@@ -321,26 +334,26 @@ static void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,
 
 			// Get tile address/2:
 #if INTERLACE
-		if ((pvid->reg[12]&6) == 6)
-			addr=(code&0x3ff)<<5;
-		else
+			if (est->rendstatus & PDRAW_INTERLACE)
+				addr=(code&0x3ff)<<5;
+			else
 #endif
-			addr=(code&0x7ff)<<4;
+				addr=(code&0x7ff)<<4;
 
 //			pal=PicoCramHigh+((code>>9)&0x30);
 			pal=(unsigned char)((code>>9)&0x30);
 
-			scroff=(8-(vscroll&7))*LINE_WIDTH;
+			scroff=(8-(vscroll&7))*est->Draw2Width;
 			switch((code>>11)&3) {
-				case 0: zero=TileXnormYnorm(scrpos+scroff+dx,addr,pal,pvid); break;
-				case 1: zero=TileXflipYnorm(scrpos+scroff+dx,addr,pal,pvid); break;
-				case 2: zero=TileXnormYflip(scrpos+scroff+dx,addr,pal,pvid); break;
-				case 3: zero=TileXflipYflip(scrpos+scroff+dx,addr,pal,pvid); break;
+				case 0: zero=TileXnormYnorm(scrpos+scroff+dx,addr,pal,est); break;
+				case 1: zero=TileXflipYnorm(scrpos+scroff+dx,addr,pal,est); break;
+				case 2: zero=TileXnormYflip(scrpos+scroff+dx,addr,pal,est); break;
+				case 3: zero=TileXflipYflip(scrpos+scroff+dx,addr,pal,est); break;
 			}
 			if(zero) blank=code; // We know this tile is blank now
 		}
 
-		scrpos += LINE_WIDTH*8;
+		scrpos += est->Draw2Width*8;
 	}
 
 	*hcache = 0; // terminate cache
@@ -356,35 +369,36 @@ static void DrawTilesFromCacheF(u32 *hc, struct PicoEState *est)
 	unsigned char pal;
 	short blank=-1; // The tile we know is blank
 	unsigned char *scrpos = est->Draw2FB, *pd = 0;
+	int scrstart = est->Draw2Start;
 
-	if (!(Pico.video.reg[12]&1) && !(PicoIn.opt&POPT_DIS_32C_BORDER))
+	if (est->rendstatus & PDRAW_BORDER_32)
 		scrpos += 32;
 
 	while((code=*hc++)) {
-		vscroll=(*hc++)*LINE_WIDTH - START_ROW*LINE_WIDTH*8;
+		vscroll=(*hc++ - START_ROW*8)*est->Draw2Width;
 		if((short)code == blank) continue;
 
 		// y pos
 		if(((unsigned)code>>27) != prevy) {
 			prevy = (unsigned)code>>27;
-			pd = scrpos + prevy*LINE_WIDTH*8;
+			pd = scrpos + (prevy-scrstart)*est->Draw2Width*8;
 		}
 
 		// Get tile address/2:
 #if INTERLACE
-		if ((Pico.video.reg[12]&6) == 6)
-		addr=(code&0x3ff)<<5;
+		if (est->rendstatus & PDRAW_INTERLACE)
+			addr=(code&0x3ff)<<5;
 		else
 #endif
-		addr=(code&0x7ff)<<4;
+			addr=(code&0x7ff)<<4;
 //		pal=PicoCramHigh+((code>>9)&0x30);
 		pal=(unsigned char)((code>>9)&0x30);
 
 		switch((code>>11)&3) {
-			case 0: zero=TileXnormYnorm(pd+vscroll+((code>>16)&0x1ff),addr,pal,&Pico.video); break;
-			case 1: zero=TileXflipYnorm(pd+vscroll+((code>>16)&0x1ff),addr,pal,&Pico.video); break;
-			case 2: zero=TileXnormYflip(pd+vscroll+((code>>16)&0x1ff),addr,pal,&Pico.video); break;
-			case 3: zero=TileXflipYflip(pd+vscroll+((code>>16)&0x1ff),addr,pal,&Pico.video); break;
+			case 0: zero=TileXnormYnorm(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;
+			case 1: zero=TileXflipYnorm(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;
+			case 2: zero=TileXnormYflip(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;
+			case 3: zero=TileXflipYflip(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;
 		}
 
 		if(zero) blank=(short)code;
@@ -400,16 +414,17 @@ static void DrawSpriteFull(unsigned int *sprite, struct PicoEState *est)
 	unsigned char pal;
 	int tile,code,tdeltax,tdeltay;
 	unsigned char *scrpos;
+	int scrstart = est->Draw2Start;
 	int sx, sy;
 
 	sy=sprite[0];
 	height=sy>>24;
 #if INTERLACE
-	if ((Pico.video.reg[12]&6) == 6)
-	sy = ((sy>>1)&0x1ff)-0x78;
+	if (est->rendstatus & PDRAW_INTERLACE)
+		sy = ((sy>>1)&0x1ff)-0x78;
 	else
 #endif
-	sy=(sy&0x1ff)-0x78; // Y
+		sy=(sy&0x1ff)-0x78; // Y
 	width=(height>>2)&3; height&=3;
 	width++; height++; // Width and height in tiles
 
@@ -427,19 +442,18 @@ static void DrawSpriteFull(unsigned int *sprite, struct PicoEState *est)
 	pal=(unsigned char)((code>>9)&0x30);
 
 	// goto first vertically visible tile
-	while(sy <= START_ROW*8) { sy+=8; tile+=tdeltay; height--; }
+	while(sy <= scrstart*8) { sy+=8; tile+=tdeltay; height--; }
 
 	scrpos = est->Draw2FB;
-	if (!(Pico.video.reg[12]&1) && !(PicoIn.opt&POPT_DIS_32C_BORDER))
+	if (est->rendstatus&PDRAW_BORDER_32)
 		scrpos += 32;
-	scrpos+=(sy-START_ROW*8)*LINE_WIDTH;
+	scrpos+=(sy-scrstart*8)*est->Draw2Width;
 
 	for (; height > 0; height--, sy+=8, tile+=tdeltay)
 	{
-		int w = width, x=sx, t=tile;
-		int s=4;
+		int w = width, x=sx, t=tile, s;
 
-		if(sy >= END_ROW*8+8) return; // offscreen
+		if((sy-scrstart*8) >= END_ROW*8+8) return; // offscreen
 
 		for (; w; w--,x+=8,t+=tdeltax)
 		{
@@ -448,30 +462,36 @@ static void DrawSpriteFull(unsigned int *sprite, struct PicoEState *est)
 
 			t&=0x7fff; // Clip tile address
 #if INTERLACE
-	if ((Pico.video.reg[12]&6) == 6) s=5;
+			if (est->rendstatus & PDRAW_INTERLACE)
+				s=5;
+			else
 #endif
+				s=4;
 			switch((code>>11)&3) {
-				case 0: TileXnormYnorm(scrpos+x,t<<s,pal,&Pico.video); break;
-				case 1: TileXflipYnorm(scrpos+x,t<<s,pal,&Pico.video); break;
-				case 2: TileXnormYflip(scrpos+x,t<<s,pal,&Pico.video); break;
-				case 3: TileXflipYflip(scrpos+x,t<<s,pal,&Pico.video); break;
+				case 0: TileXnormYnorm(scrpos+x,t<<s,pal,est); break;
+				case 1: TileXflipYnorm(scrpos+x,t<<s,pal,est); break;
+				case 2: TileXnormYflip(scrpos+x,t<<s,pal,est); break;
+				case 3: TileXflipYflip(scrpos+x,t<<s,pal,est); break;
 			}
 		}
 
-		scrpos+=8*LINE_WIDTH;
+		scrpos+=8*est->Draw2Width;
 	}
 }
 #endif
 
 
-static void DrawAllSpritesFull(int prio, int maxwidth)
+static void DrawAllSpritesFull(int prio, int maxwidth, struct PicoEState *est)
 {
-	struct PicoVideo *pvid=&Pico.video;
+	struct PicoVideo *pvid=&est->Pico->video;
 	int table=0,maskrange=0;
 	int i,u,link=0;
 	unsigned int *sprites[80]; // Sprites
 	int y_min=START_ROW*8, y_max=END_ROW*8; // for a simple sprite masking
-	int max_sprites = Pico.video.reg[12]&1 ? 80 : 64;
+	int max_sprites = pvid->reg[12]&1 ? 80 : 64;
+
+	if (est->rendstatus & PDRAW_30_ROWS)
+		y_min += 8, y_max += 8;
 
 	table=pvid->reg[5]&0x7f;
 	if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode
@@ -489,11 +509,11 @@ static void DrawAllSpritesFull(int prio, int maxwidth)
 
 		// check if it is not hidden vertically
 #if INTERLACE
-		if ((pvid->reg[12]&6) == 6)
-		sy = ((code>>1)&0x1ff)-0x80;
+		if (est->rendstatus & PDRAW_INTERLACE)
+			sy = ((code>>1)&0x1ff)-0x80;
 		else
 #endif
-		sy = (code&0x1ff)-0x80;
+			sy = (code&0x1ff)-0x80;
 		height = (((code>>24)&3)+1)<<3;
 		if(sy+height <= y_min || sy > y_max) goto nextsprite;
 
@@ -534,7 +554,7 @@ static void DrawAllSpritesFull(int prio, int maxwidth)
 	// Go through sprites backwards:
 	for (i--; i >= 0; i--)
 	{
-		DrawSpriteFull(sprites[i], &Pico.est);
+		DrawSpriteFull(sprites[i], est);
 	}
 }
 
@@ -548,24 +568,34 @@ static void BackFillFull(void *dst, int reg7)
 	back|=back<<8;
 	back|=back<<16;
 
-	memset32(dst, back, LINE_WIDTH*(8+(END_ROW-START_ROW)*8)/4);
+	memset32(dst, back, Pico.est.Draw2Width*(8+(END_ROW-START_ROW)*8)/4);
 }
 #endif
 
 static void DrawDisplayFull(void)
 {
 	struct PicoEState *est = &Pico.est;
-	struct PicoVideo *pvid=&Pico.video;
+	struct PicoVideo *pvid=&est->Pico->video;
 	int win, edge=0, hvwin=0; // LSb->MSb: hwin&plane, vwin&plane, full
-	int planestart=START_ROW, planeend=END_ROW; // plane A start/end when window shares display with plane A (in tile rows or columns)
-	int winstart=START_ROW, winend=END_ROW;     // same for window
+	int scrstart=START_ROW, scrend = END_ROW; // our render area
+	int planestart, planeend; // plane A start/end when window shares display with plane A (in tile rows or columns)
+	int winstart, winend;     // same for window
 	int maxw, maxcolc; // max width and col cells
 
-	if(pvid->reg[12]&1) {
-		maxw = 328; maxcolc = 40;
-	} else {
+	if(est->rendstatus & PDRAW_32_COLS) {
 		maxw = 264; maxcolc = 32;
+	} else {
+		maxw = 328; maxcolc = 40;
+	}
+	if(est->rendstatus & PDRAW_30_ROWS) {
+		// In 240 line mode, the top and bottom 8 lines are omitted
+		// since this renderer always renderers 224 lines
+		scrstart ++, scrend ++;
 	}
+	est->Draw2Start = scrstart;
+
+	planestart = scrstart, planeend = scrend;
+	winstart = scrstart, winend = scrend;
 
 	// 32C border for centering? (for asm)
 	est->rendstatus &= ~PDRAW_BORDER_32;
@@ -582,12 +612,12 @@ static void DrawDisplayFull(void)
 			hvwin=4;
 		} else if(win < 0x80) {
 			// window on the top
-			     if(edge <= START_ROW) hvwin=0; // window not visible in our drawing region
-			else if(edge >= END_ROW)   hvwin=4;
+			     if(edge <= scrstart) hvwin=0; // window not visible in our drawing region
+			else if(edge >= scrend)   hvwin=4;
 			else planestart = winend = edge;
 		} else if(win > 0x80) {
 			// window at the bottom
-			if(edge >= END_ROW) hvwin=0;
+			if(edge >= scrend) hvwin=0;
 			else planeend = winstart = edge;
 		}
 	}
@@ -622,19 +652,19 @@ static void DrawDisplayFull(void)
 
 	HighCache2A[0] = HighCache2B[0] = 0;
 	if (!(pvid->debug_p & PVD_KILL_B))
-		DrawLayerFull(1, HighCache2B, START_ROW, (maxcolc<<16)|END_ROW, est);
+		DrawLayerFull(1, HighCache2B, scrstart, (maxcolc<<16)|scrend, est);
 	if (!(pvid->debug_p & PVD_KILL_A)) switch (hvwin)
 	{
 		case 4:
 		// fullscreen window
-		DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 0, est);
+		DrawWindowFull(scrstart, (maxcolc<<16)|scrend, 0, est);
 		break;
 
 		case 3:
 		// we have plane A and both v and h windows
 		DrawLayerFull(0, HighCache2A, planestart, planeend, est);
 		DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 0, est); // h
-		DrawWindowFull((winstart&~0xff)|START_ROW, (winend&~0xff)|END_ROW, 0, est);    // v
+		DrawWindowFull((winstart&~0xff)|scrstart, (winend&~0xff)|scrend, 0, est);    // v
 		break;
 
 		case 2:
@@ -646,11 +676,11 @@ static void DrawDisplayFull(void)
 
 		default:
 		// fullscreen plane A
-		DrawLayerFull(0, HighCache2A, START_ROW, (maxcolc<<16)|END_ROW, est);
+		DrawLayerFull(0, HighCache2A, scrstart, (maxcolc<<16)|scrend, est);
 		break;
 	}
 	if (!(pvid->debug_p & PVD_KILL_S_LO))
-		DrawAllSpritesFull(0, maxw);
+		DrawAllSpritesFull(0, maxw, est);
 
 	if (HighCache2B[0]) DrawTilesFromCacheF(HighCache2B, est);
 	if (HighCache2A[0]) DrawTilesFromCacheF(HighCache2A, est);
@@ -658,13 +688,13 @@ static void DrawDisplayFull(void)
 	{
 		case 4:
 		// fullscreen window
-		DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 1, est);
+		DrawWindowFull(scrstart, (maxcolc<<16)|scrend, 1, est);
 		break;
 
 		case 3:
 		// we have plane A and both v and h windows
 		DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 1, est); // h
-		DrawWindowFull((winstart&~0xff)|START_ROW, (winend&~0xff)|END_ROW, 1, est);    // v
+		DrawWindowFull((winstart&~0xff)|scrstart, (winend&~0xff)|scrend, 1, est);    // v
 		break;
 
 		case 2:
@@ -674,7 +704,7 @@ static void DrawDisplayFull(void)
 		break;
 	}
 	if (!(pvid->debug_p & PVD_KILL_S_HI))
-		DrawAllSpritesFull(1, maxw);
+		DrawAllSpritesFull(1, maxw, est);
 }
 
 
@@ -690,18 +720,33 @@ PICO_INTERNAL void PicoFrameFull()
 	if (Pico.video.reg[1] & 0x40)
 		DrawDisplayFull();
 
+	// clear top and bottom 8 lines in 240 mode, since draw2 only does 224
+	if (Pico.est.rendstatus & PDRAW_30_ROWS) {
+		unsigned char *pd = Pico.est.Draw2FB;
+		int i;
+
+		for (i = 8; i > 0; i--, pd += Pico.est.Draw2Width)
+			memset32((int *)pd, 0xe0e0e0e0, 328/4);
+		pd += Pico.est.Draw2Width*(END_ROW-START_ROW)*8;
+		for (i = 8; i > 0; i--, pd += Pico.est.Draw2Width)
+			memset32((int *)pd, 0xe0e0e0e0, 328/4);
+	}
+
 	pprof_end(draw);
 }
 
-void PicoDraw2SetOutBuf(void *dest)
+void PicoDraw2SetOutBuf(void *dest, int incr)
 {
-	if (dest)
+	if (dest) {
 		Pico.est.Draw2FB = dest;
-	else
+		Pico.est.Draw2Width = incr;
+	} else {
 		Pico.est.Draw2FB = PicoDraw2FB_;
+		Pico.est.Draw2Width = LINE_WIDTH;
+	}
 }
 
 void PicoDraw2Init(void)
 {
-	PicoDraw2SetOutBuf(NULL);
+	PicoDraw2SetOutBuf(NULL, 0);
 }

+ 2 - 1
pico/pico.h

@@ -208,9 +208,10 @@ void PicoDoHighPal555(int sh, int line, struct PicoEState *est);
 #define PDRAW_SONIC_MODE    (1<<5) // mid-frame palette changes for 8bit renderer
 #define PDRAW_PLANE_HI_PRIO (1<<6) // have layer with all hi prio tiles (mk3)
 #define PDRAW_SHHI_DONE     (1<<7) // layer sh/hi already processed
-#define PDRAW_32_COLS       (1<<8) // 32 column mode
+#define PDRAW_32_COLS       (1<<8) // 32 columns mode
 #define PDRAW_BORDER_32     (1<<9) // center H32 in buffer (32 px border)
 #define PDRAW_SKIP_FRAME   (1<<10) // frame is skipped
+#define PDRAW_30_ROWS      (1<<11) // 30 rows mode (240 lines)
 extern int rendstatus_old;
 extern int rendlines;
 

+ 3 - 1
pico/pico_int.h

@@ -359,6 +359,8 @@ struct PicoEState
   void *PicoMem_cram;
   unsigned int  *PicoOpt;
   unsigned char *Draw2FB;
+  int Draw2Width;
+  int Draw2Start;
   unsigned short HighPal[0x100];
   unsigned short SonicPal[0x100];
   int SonicPalCount;
@@ -673,7 +675,7 @@ extern int DrawLineDestIncrement;
 extern u32 VdpSATCache[128];
 
 // draw2.c
-void PicoDraw2SetOutBuf(void *dest);
+void PicoDraw2SetOutBuf(void *dest, int incr);
 void PicoDraw2Init(void);
 PICO_INTERNAL void PicoFrameFull();