mirror of
				https://github.com/RaySollium99/picodrive.git
				synced 2025-10-27 21:48:50 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1017 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1017 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // EmuScan routines for Pico, also simple text and shape drawing routines.
 | |
| 
 | |
| // (c) Copyright 2006, notaz
 | |
| // All Rights Reserved
 | |
| 
 | |
| #include "vid.h"
 | |
| #include "ClientServer.h"
 | |
| #include "SimpleServer.h"
 | |
| #include "pico\picoInt.h"
 | |
| #include "blit.h"
 | |
| #include "debug.h"
 | |
| 
 | |
| // global stuff
 | |
| extern TPicoConfig currentConfig;
 | |
| extern TPicoKeyConfigEntry *keyConfig;
 | |
| extern TPicoAreaConfigEntry areaConfig[];
 | |
| extern const char * actionNames[];
 | |
| 
 | |
| // main framebuffer
 | |
| static void *screenbuff = 0; // pointer to real device video memory
 | |
| //static
 | |
| unsigned short *framebuff = 0;  // temporary buffer in sega native BGR format
 | |
| const  int framebuffsize  = (8+320)*(8+208+8)*2; // actual framebuffer size (in bytes+to support new rendering mode)
 | |
| static int framebuff_offs = 0; // where to start copying (in pixels)
 | |
| static int framebuff_len  = 0; // how much of the framebuffer actually needs to be copied (in pixels)
 | |
| static int fc_lines, fc_inc; // lines, inc for "0 fit2" mode
 | |
| 
 | |
| // drawer function pointers
 | |
| void (*drawTextFps)(const char *text) = 0;
 | |
| void (*drawTextNotice)(const char *text) = 0;
 | |
| 
 | |
| // blitter
 | |
| void (*vidBlit)(int full) = 0;
 | |
| void (*vidBlitKeyCfg)(int full) = 0;
 | |
| 
 | |
| // stuff for rendermode2
 | |
| static unsigned short cram_high[0x40];
 | |
| static unsigned short dt_dmask=0x0333;
 | |
| unsigned short color_redM2 = 0x022F;
 | |
| 
 | |
| // colors
 | |
| const unsigned short color_red     = 0x022F;
 | |
| const unsigned short color_red_dim = 0x0004;
 | |
| const unsigned short color_green   = 0x01F1;
 | |
| const unsigned short color_blue    = 0x0F11;
 | |
| const unsigned short color_grey    = 0x0222;
 | |
| 
 | |
| // other
 | |
| int txtheight_fit = 138;
 | |
| 
 | |
| // bitmasks
 | |
| static const unsigned long mask_numbers[] = {
 | |
| 	0x12244800, // 47 2F /
 | |
| 	0x69999600, // 48 30 0
 | |
| 	0x26222200, // 49 31 1
 | |
| 	0x69168F00, // 50 32 2
 | |
| 	0x69219600, // 51 33 3
 | |
| 	0x266AF200, // 52 34 4
 | |
| 	0xF8E11E00, // 53 35 5
 | |
| 	0x68E99600, // 54 36 6
 | |
| 	0x71222200, // 55 37 7
 | |
| 	0x69699600, // 56 38 8
 | |
| 	0x69719600, // 57 39 9
 | |
| 	0x04004000, // 58 3A :
 | |
| 	0x04004400, // 59 3B ;
 | |
| 	0x01242100, // 60 3C <
 | |
| 	0x00707000, // 61 3D =
 | |
| 	0x04212400, // 62 3E >
 | |
| 	0x69240400, // 63 3F ?
 | |
| 	0x00000000, // 64 40 @ [used instead of space for now]
 | |
| 	0x22579900, // 65 41 A
 | |
| 	0xE9E99E00, // 66 42 B
 | |
| 	0x69889600, // 67 43 C
 | |
| 	0xE9999E00, // 68 44 D
 | |
| 	0xF8E88F00, // 69 45 E
 | |
| 	0xF8E88800, // 70 46 F
 | |
| 	0x698B9700, // 71 47 G
 | |
| 	0x99F99900, // 72 48 H
 | |
| 	0x44444400, // 73 49 I
 | |
| 	0x11119600, // 74 4A J
 | |
| 	0x9ACCA900, // 75 4B K
 | |
| 	0x88888F00, // 76 4C L
 | |
| 	0x9F999900, // 77 4D M
 | |
| 	0x9DDBB900, // 78 4E N
 | |
| 	0x69999600, // 79 4F O
 | |
| 	0xE99E8800, // 80 50 P
 | |
| 	0x6999A500, // 81 51 Q
 | |
| 	0xE99E9900, // 82 52 R
 | |
| 	0x69429600, // 83 53 S
 | |
| 	0x72222200, // 84 54 T
 | |
| 	0x99999600, // 85 55 U
 | |
| 	0x55552200, // 86 56 V
 | |
| 	0x9999F900, // 87 57 W
 | |
| 	0x55225500, // 88 58 X
 | |
| 	0x55222200, // 89 59 Y
 | |
| 	0xF1248F00, // 90 5A Z
 | |
| };
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // Cram functions
 | |
| 
 | |
| inline int EmuCramNull(int cram)
 | |
| {
 | |
|   User::Panic(_L("Cram called!!"), 0);
 | |
|   return cram;
 | |
| }
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // PicoScan functions in "center" mode
 | |
| 
 | |
| int EmuScanCenter0(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   //unsigned short *vidmem=framebuff;
 | |
| 
 | |
|   //unsigned short *sp, *sto;
 | |
|   //sp=sdata+56; sto=sdata+264; vidmem += num*208;
 | |
| 
 | |
|   //do { *vidmem++ = *sp++; } while(sp < sto);
 | |
|   memcpy(framebuff + num*208, sdata+56, 208*2); // memcpy gives ~1 fps (~2 with optimized memcpy)
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int EmuScanCenter90(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   // ignore top and bottom lines
 | |
|   if(num < 8)   return 7-num;     // skip first 8 lines
 | |
|   if(num > 215) return 223+8-num; // this should not happen, just in case
 | |
| 
 | |
|   num -= 8;
 | |
|   if(!num) {
 | |
| 	if(Pico.video.reg[12]&1) { // copy less in 32-col mode
 | |
| 	  framebuff_offs= 0;
 | |
|       framebuff_len = 208*320;
 | |
| 	} else {
 | |
| 	  framebuff_offs= 208*32;
 | |
|       framebuff_len = 208*256;
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   unsigned short *vidmem=framebuff;
 | |
|   vidmem += 207-num; // adjust x
 | |
| 
 | |
|   // do less copy in 32-column mode
 | |
|   unsigned short *sp, *sto;
 | |
|   int pixels;
 | |
|   if(!(Pico.video.reg[12]&1))
 | |
|        { sp=sdata+32; sto=sdata+288; pixels = 288; vidmem += 32*208; }
 | |
|   else { sp=sdata;    sto=sdata+320; pixels = 320; }
 | |
| 
 | |
|   do { *vidmem = *sp++; vidmem+=208; } while(sp < sto);
 | |
| 
 | |
|   if(num == 207) return 16; // skip bottom of this frame and top of next
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int EmuScanCenter180(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   unsigned short *vidmem=framebuff;
 | |
| 
 | |
|   unsigned short *sp, *sto;
 | |
|   sp=sdata+56; sto=sdata+264; vidmem += (224-num)*208;
 | |
| 
 | |
|   do { *(--vidmem) = *sp++; } while(sp < sto); // reversed
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int EmuScanCenter270(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   // ignore top and bottom lines
 | |
|   if(num < 8)   return 7-num;     // skip first 8 lines
 | |
|   if(num > 215) return 223-num+8;
 | |
| 
 | |
|   num -= 8;
 | |
|   if(num > 207) return 0;
 | |
|   if(!num) {
 | |
| 	if(Pico.video.reg[12]&1) {
 | |
| 	  framebuff_offs= 0;
 | |
|       framebuff_len = 208*320;
 | |
| 	} else {
 | |
| 	  framebuff_offs= 208*32;
 | |
|       framebuff_len = 208*256;
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   unsigned short *vidmem=framebuff+320*208;
 | |
|   vidmem -= 208-num; // adjust x
 | |
| 
 | |
|   // do less copy in 32-column mode
 | |
|   unsigned short *sp, *sto;
 | |
|   if(!(Pico.video.reg[12]&1))
 | |
|        { sp=sdata+32; sto=sdata+288; vidmem -= 32*208; }
 | |
|   else { sp=sdata;    sto=sdata+320; }
 | |
| 
 | |
|   do { *vidmem = *sp++; vidmem-=208; } while(sp < sto);
 | |
| 
 | |
|   if(num == 207) return 16; // skip bottom of this frame and top of next
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // PicoScan functions in "fit" mode
 | |
| 
 | |
| static int EmuScanFit0(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   // 0.65, 145 lines in normal mode; 0.8125, 182 lines in 32-column mode
 | |
| 
 | |
|   // draw this line? (ARM4s don't support division, so do some tricks here)
 | |
|   static int u = 0, num2 = 0;
 | |
|   if(!num) {
 | |
|     u = num2 = 0;
 | |
| 	if(currentConfig.iScreenMode == TPicoConfig::PMFit) {
 | |
|       if(Pico.video.reg[12]&1) { // 32 col mode? This can change on any frame
 | |
|         fc_inc = 6500;
 | |
| 	    txtheight_fit = 138;
 | |
|         framebuff_len = 208*145;
 | |
| 	    memset(framebuff+208*145, 0, 208*37*2);
 | |
|       } else {
 | |
|         fc_inc = 8125;
 | |
| 	    txtheight_fit = 175;
 | |
|         framebuff_len = 208*182;
 | |
|       }
 | |
| 	}
 | |
|   }
 | |
|   u += fc_inc;
 | |
|   if(u < 10000) return 0;
 | |
|   u -= 10000;
 | |
| 
 | |
|   unsigned short *vidmem=framebuff;
 | |
| 
 | |
|   int slen;
 | |
|   unsigned short *sp;
 | |
|   if(!(Pico.video.reg[12]&1))
 | |
|        { sp=sdata+32; slen=256; }
 | |
|   else { sp=sdata;    slen=320; }
 | |
| 
 | |
|   vidmem += num2*208;
 | |
| /*
 | |
|   int i=0;
 | |
|   while(sp < sto) {
 | |
| 	i += inc;
 | |
|     if(i >= 10000) {
 | |
| 	  *vidmem++ = *sp;
 | |
| 	  i -= 10000;
 | |
| 	}
 | |
| 	sp++;
 | |
|   }
 | |
| */
 | |
|   PicuShrink(vidmem, 208, sp, slen);
 | |
| 
 | |
|   num2++;
 | |
| 
 | |
|   // calculate how many lines pico engine should skip,
 | |
|   // making sure this func will be called on scanline 0
 | |
|   int skip = 0;
 | |
|   while(u+fc_inc < 10000 && num+skip != 223) { u+=fc_inc; skip++; }
 | |
| 
 | |
|   return skip;
 | |
| }
 | |
| 
 | |
| int EmuScanFit90(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   // 0.9285
 | |
| 
 | |
|   // draw this line?
 | |
|   static int u = 0, num2 = 0;
 | |
|   if(!num) {
 | |
|     u = num2 = 0;
 | |
| 	if(Pico.video.reg[12]&1) {
 | |
| 	  framebuff_offs= 0;
 | |
|       framebuff_len = 208*320;
 | |
| 	} else {
 | |
| 	  framebuff_offs= 208*32;
 | |
|       framebuff_len = 208*256;
 | |
| 	}
 | |
|   }
 | |
|   u += 9285;
 | |
|   if(u < 10000) return 0;
 | |
|   u -= 10000;
 | |
| 
 | |
|   unsigned short *vidmem=framebuff;
 | |
|   vidmem += 207-num2; // adjust x
 | |
| 
 | |
|   // do less copy in 32-column mode
 | |
|   unsigned short *sp, *sto;
 | |
|   if(!(Pico.video.reg[12]&1))
 | |
|        { sp=sdata+32; sto=sdata+288; vidmem += 32*208; }
 | |
|   else { sp=sdata;    sto=sdata+320; }
 | |
| 
 | |
|   do { *vidmem = *sp++; vidmem+=208; } while(sp < sto);
 | |
| 
 | |
|   num2++;
 | |
| 
 | |
|   // skip next line?
 | |
|   if(u+9285 < 10000 && num != 223) { u+=9285; return 1; }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int EmuScanFit180(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   // 0.65, 145 lines in normal mode; 0.8125, 182 lines in 32-column mode
 | |
| 
 | |
|   // draw this line? (ARM4s don't support division)
 | |
|   static int u = 0, num2 = 0;
 | |
|   if(!num) {
 | |
|     u = num2 = 0;
 | |
| 	if(currentConfig.iScreenMode == TPicoConfig::PMFit) {
 | |
|       if(Pico.video.reg[12]&1) { // 32 col mode? This can change on any frame
 | |
| 	    fc_lines = 145;
 | |
|         fc_inc = 6500;
 | |
| 	    txtheight_fit = 138;
 | |
|         framebuff_len = 208*145;
 | |
| 	    memset(framebuff+208*145, 0, 208*37*2);
 | |
|       } else {
 | |
|         fc_lines = 182;
 | |
|         fc_inc = 8125;
 | |
| 	    txtheight_fit = 175;
 | |
|         framebuff_len = 208*182;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   u += fc_inc;
 | |
|   if(u < 10000) return 0;
 | |
|   u -= 10000;
 | |
| 
 | |
|   unsigned short *vidmem=framebuff;
 | |
| 
 | |
|   int slen;
 | |
|   unsigned short *sp;
 | |
|   if(!(Pico.video.reg[12]&1))
 | |
|        { sp=sdata+32; slen=256; }
 | |
|   else { sp=sdata;    slen=320; }
 | |
| 
 | |
|   vidmem += (fc_lines-num2)*208;
 | |
| 
 | |
|   PicuShrinkReverse(vidmem, 208, sp, slen);
 | |
| 
 | |
|   num2++;
 | |
| 
 | |
|   // skip some lines?
 | |
|   int skip = 0;
 | |
|   while(u+fc_inc < 10000 && num+skip != 223) { u+=fc_inc; skip++; }
 | |
| 
 | |
|   return skip;
 | |
| }
 | |
| 
 | |
| int EmuScanFit270(unsigned int num, unsigned short *sdata)
 | |
| {
 | |
|   // 0.9285
 | |
| 
 | |
|   // draw this line?
 | |
|   static int u = 0, num2 = 0;
 | |
|   if(!num) {
 | |
|     u = num2 = 0;
 | |
| 	if(Pico.video.reg[12]&1) {
 | |
| 	  framebuff_offs= 0;
 | |
|       framebuff_len = 208*320;
 | |
| 	} else {
 | |
| 	  framebuff_offs= 208*32;
 | |
|       framebuff_len = 208*256;
 | |
| 	}
 | |
|   }
 | |
|   u += 9285;
 | |
|   if(u < 10000) return 0;
 | |
|   u -= 10000;
 | |
| 
 | |
|   unsigned short *vidmem=framebuff+320*208;
 | |
|   vidmem -= 208-num2; // adjust x
 | |
| 
 | |
|   // do less copy in 32-column mode
 | |
|   unsigned short *sp, *sto;
 | |
|   if(!(Pico.video.reg[12]&1))
 | |
|        { sp=sdata+32; sto=sdata+288; vidmem -= 32*208; }
 | |
|   else { sp=sdata;    sto=sdata+320; }
 | |
| 
 | |
|   do { *vidmem = *sp++; vidmem-=208; } while(sp < sto);
 | |
| 
 | |
|   num2++;
 | |
| 
 | |
|   // skip next line?
 | |
|   if(u+9285 < 10000 && num != 223) { u+=9285; return 1; }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // text drawers
 | |
| // warning: text must be at least 1px away from screen borders
 | |
| 
 | |
| void drawTextM2(int x, int y, const char *text, long color)
 | |
| {
 | |
| 	unsigned short *vidmem=framebuff;
 | |
| 	int charmask, i, cx = x, cy;
 | |
| 	unsigned short *l, *le;
 | |
| 
 | |
| 	// darken the background (left border)
 | |
| 	for(l=vidmem+(cx-1)+(y-1)*328, le=vidmem+(cx-1)+(y+7)*328; l < le; l+=328)
 | |
| 		*l = (*l >> 2) & dt_dmask;
 | |
| 
 | |
| 	for(const char *p=text; *p; p++) {
 | |
| 		cy = y;
 | |
| 		charmask = *(mask_numbers + (*p - 0x2F));
 | |
| 
 | |
| 		for(l = vidmem+cx+(y-1)*328, le = vidmem+cx+(y+7)*328; l < le; l+=328-4) {
 | |
| 			*l = (*l >> 2) & dt_dmask; l++; *l = (*l >> 2) & dt_dmask; l++;
 | |
| 			*l = (*l >> 2) & dt_dmask; l++; *l = (*l >> 2) & dt_dmask; l++;
 | |
| 			*l = (*l >> 2) & dt_dmask;
 | |
| 		}
 | |
| 
 | |
| 		for(i=0; i < 24; i++) {
 | |
| 			// draw dot. Is this fast?
 | |
| 			if(charmask&0x80000000) *( vidmem + (cx+(i&3)) + (cy+(i>>2))*328 ) = color;
 | |
| 			charmask <<= 1;
 | |
| 		}
 | |
| 		cx += 5;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void drawText0(int x, int y, const char *text, long color)
 | |
| {
 | |
| 	unsigned short *vidmem=framebuff;
 | |
| 	int charmask, i, cx = x, cy;
 | |
| 	unsigned short *l, *le, dmask=0x0333;
 | |
| 
 | |
| 	// darken the background (left border)
 | |
| 	for(l=vidmem+(cx-1)+(y-1)*208, le=vidmem+(cx-1)+(y+7)*208; l < le; l+=208)
 | |
| 		*l = (*l >> 2) & dmask;
 | |
| 
 | |
| 	for(const char *p=text; *p; p++) {
 | |
| 		cy = y;
 | |
| 		charmask = *(mask_numbers + (*p - 0x2F));
 | |
| 
 | |
| 		for(l = vidmem+cx+(y-1)*208, le = vidmem+cx+(y+7)*208; l < le; l+=208-4) {
 | |
| 			*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
 | |
| 			*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
 | |
| 			*l = (*l >> 2) & dmask;
 | |
| 		}
 | |
| 
 | |
| 		for(i=0; i < 24; i++) {
 | |
| 			// draw dot. Is this fast?
 | |
| 			if(charmask&0x80000000) *( vidmem + (cx+(i&3)) + (cy+(i>>2))*208 ) = color;
 | |
| 			charmask <<= 1;
 | |
| 		}
 | |
| 		cx += 5;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void drawText90(int x, int y, const char *text, long color)
 | |
| {
 | |
| 	unsigned short *vidmem=framebuff;
 | |
| 	unsigned short *l, *le, dmask=0x0333;
 | |
| 	int charmask, i, cx, cy = y;
 | |
| 
 | |
| 	for(l=vidmem+(x+1)+(cy-1)*208, le=vidmem+(x-7)+(cy-1)*208; l > le; l--)
 | |
| 		*l = (*l >> 2) & dmask;
 | |
| 
 | |
| 	for(const char *p=text; *p; p++) {
 | |
| 		cx = x;
 | |
| 		charmask = *(mask_numbers + (*p - 0x2F));
 | |
| 
 | |
| 		for(l = vidmem+(x+1)+(cy)*208, le = vidmem+(x+1)+(cy+5)*208; l < le; l+=208+7) {
 | |
| 			*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
 | |
| 			*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
 | |
| 			*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
 | |
| 			*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask;
 | |
| 		}
 | |
| 
 | |
| 		for(i=0; i < 24; i++) {
 | |
| 			if(charmask&0x80000000) *( vidmem + (cy+(i&3))*208 + (cx-(i>>2)) ) = color;
 | |
| 			charmask <<= 1;
 | |
| 		}
 | |
| 		cy += 5;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void drawText180(int x, int y, const char *text, long color)
 | |
| {
 | |
| 	unsigned short *vidmem=framebuff;
 | |
| 	int charmask, i, cx = x, cy;
 | |
| 	unsigned short *l, *le, dmask=0x0333;
 | |
| 
 | |
| 	for(l=vidmem+(cx+1)+(y+1)*208, le=vidmem+(cx+1)+(y-7)*208; l > le; l-=208)
 | |
| 		*l = (*l >> 2) & dmask;
 | |
| 
 | |
| 	for(const char *p=text; *p; p++) {
 | |
| 		cy = y;
 | |
| 		charmask = *(mask_numbers + (*p - 0x2F));
 | |
| 
 | |
| 		for(l = vidmem+cx+(y+1)*208, le = vidmem+cx+(y-8)*208; l > le; l-=208-4) {
 | |
| 			*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
 | |
| 			*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
 | |
| 			*l = (*l >> 2) & dmask;
 | |
| 		}
 | |
| 
 | |
| 		for(i=0; i < 24; i++) {
 | |
| 			if(charmask&0x80000000) *( vidmem + (cx-(i&3)) + (cy-(i>>2))*208 ) = color;
 | |
| 			charmask <<= 1;
 | |
| 		}
 | |
| 		cx -= 5;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void drawText270(int x, int y, const char *text, long color)
 | |
| {
 | |
| 	unsigned short *vidmem=framebuff;
 | |
| 	int charmask, i, cx, cy = y;
 | |
| 	unsigned short *l, *le, dmask=0x0333;
 | |
| 
 | |
| 	for(l=vidmem+(x-1)+(cy+1)*208, le=vidmem+(x+7)+(cy+1)*208; l < le; l++)
 | |
| 		*l = (*l >> 2) & dmask;
 | |
| 
 | |
| 	for(const char *p=text; *p; p++) {
 | |
| 		cx = x;
 | |
| 		charmask = *(mask_numbers + (*p - 0x2F));
 | |
| 
 | |
| 		for(l = vidmem+(x-1)+(cy)*208, le = vidmem+(x-1)+(cy-5)*208; l > le; l-=208+7) {
 | |
| 			*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
 | |
| 			*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
 | |
| 			*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
 | |
| 			*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask;
 | |
| 		}
 | |
| 
 | |
| 		for(i=0; i < 24; i++) {
 | |
| 			if(charmask&0x80000000) *( vidmem + (cy-(i&3))*208 + (cx+(i>>2)) ) = color;
 | |
| 			charmask <<= 1;
 | |
| 		}
 | |
| 		cy -= 5;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void drawTextFpsM2(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawTextM2((Pico.video.reg[12]&1) ? 256 : 224, 200, text, color_redM2);
 | |
| }
 | |
| 
 | |
| void drawTextFps0(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText0(176, 216, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextFpsFit0(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText0(176, txtheight_fit, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextFps90(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText90(10, 256, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextFps180(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText180(32, 8, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextFps270(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText270(200, 64, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextNoticeM2(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawTextM2(20, 200, text, color_redM2);
 | |
| }
 | |
| 
 | |
| void drawTextNotice0(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText0(2, 216, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextNoticeFit0(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText0(2, txtheight_fit, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextNotice90(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText90(10, 34, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextNotice180(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText180(206, 8, text, color_red);
 | |
| }
 | |
| 
 | |
| void drawTextNotice270(const char *text)
 | |
| {
 | |
| 	if(!text) return;
 | |
| 	drawText270(200, 286, text, color_red);
 | |
| }
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // misc drawers
 | |
| 
 | |
| // draws rect with width - 1 and height - 1
 | |
| void drawRect(const TRect &rc, unsigned short color)
 | |
| {
 | |
| 	if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) {
 | |
| 		int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1;
 | |
| 		int stepY = rc.iTl.iY < rc.iBr.iY ? 1 : -1;
 | |
| 		
 | |
| 		for(int x = rc.iTl.iX;; x += stepX) {
 | |
| 			*(framebuff + rc.iTl.iY*208 + x) = *(framebuff + (rc.iBr.iY - stepY)*208 + x) = color;
 | |
| 			if(x == rc.iBr.iX - stepX) break;
 | |
| 		}
 | |
| 		
 | |
| 		for(int y = rc.iTl.iY;; y += stepY) {
 | |
| 			*(framebuff + y*208 + rc.iTl.iX) = *(framebuff + y*208 + rc.iBr.iX - stepX) = color;
 | |
| 			if(y == rc.iBr.iY - stepY) break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // draws fullsize filled rect
 | |
| void drawRectFilled(const TRect rc, unsigned short color)
 | |
| {
 | |
| 	if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) {
 | |
| 		int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1;
 | |
| 		int stepY = rc.iTl.iY < rc.iBr.iY ? 1 : -1;
 | |
| 		
 | |
| 		for(int y = rc.iTl.iY;; y += stepY) {
 | |
| 			for(int x = rc.iTl.iX;; x += stepX) {
 | |
| 				*(framebuff + y*208 + x) = *(framebuff + y*208 + x) = color;
 | |
| 				if(x == rc.iBr.iX) break;
 | |
| 			}
 | |
| 			if(y == rc.iBr.iY) break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // direction: -1 left, 1 right
 | |
| void drawArrow0(TPoint p, int direction, unsigned short color)
 | |
| {
 | |
| 	int width = 11;
 | |
| 	int x = p.iX;
 | |
| 	int y = p.iY;
 | |
| 
 | |
| 	for(; width > 0; x+=direction, y++, width -=2)
 | |
| 		for(int i=0; i < width; i++)
 | |
| 			*(framebuff + x + y*208 + i*208) = color;
 | |
| }
 | |
| 
 | |
| void drawArrow90(TPoint p, int direction, unsigned short color)
 | |
| {
 | |
| 	int width = 11;
 | |
| 	int x = p.iX - width;
 | |
| 	int y = p.iY;
 | |
| 
 | |
| 	for(; width > 0; x++, y+=direction, width -=2)
 | |
| 		for(int i=0; i < width; i++)
 | |
| 			*(framebuff + x + y*208 + i) = color;
 | |
| }
 | |
| 
 | |
| 
 | |
| // copies temporary framebuff to real device framebuffer
 | |
| void vidBlitRGB444(int full)
 | |
| {
 | |
| 	unsigned short *ps;
 | |
| 	unsigned short *pd;
 | |
| 	int pixels;
 | |
| 	if(full) {
 | |
| 		ps = framebuff;
 | |
| 		pd = (unsigned short *) screenbuff;
 | |
| 		pixels = 208*320;
 | |
| 	} else {
 | |
| 		ps = framebuff + framebuff_offs;
 | |
| 		pd = (unsigned short *) screenbuff + framebuff_offs;
 | |
| 		pixels = framebuff_len;
 | |
| 	}
 | |
| 
 | |
| 	vidConvCpyRGB444(pd, ps, pixels);
 | |
| 	//for(unsigned short *ps_end = ps + pixels; ps < ps_end; ps++)
 | |
| 		// Convert 0000bbb0 ggg0rrr0
 | |
| 		// to      0000rrr0 ggg0bbb0
 | |
| 	//	*pd++ = ((*ps&0x000F)<<8) | (*ps&0x00F0) | ((*ps&0x0F00)>>8);
 | |
| }
 | |
| 
 | |
| void vidBlitRGB565(int full)
 | |
| {
 | |
| 	unsigned short *ps;
 | |
| 	unsigned short *pd;
 | |
| 	int pixels;
 | |
| 	if(full) {
 | |
| 		ps = framebuff;
 | |
| 		pd = (unsigned short *) screenbuff;
 | |
| 		pixels = 208*320;
 | |
| 	} else {
 | |
| 		ps = framebuff  + framebuff_offs;
 | |
| 		pd = (unsigned short *) screenbuff + framebuff_offs;
 | |
| 		pixels = framebuff_len;
 | |
| 	}
 | |
| 
 | |
| 	vidConvCpyRGB565(pd, ps, pixels);
 | |
| 	//for(; ps < ps_end; ps++)
 | |
| 		// Convert 0000bbb0 ggg0rrr0
 | |
| 		// to      rrr00ggg 000bbb00
 | |
| 	//	*pd++ = ((*ps&0x000F)<<12) | ((*ps&0x00F0)<<3) | ((*ps&0x0F00)>>7);
 | |
| }
 | |
| 
 | |
| void vidBlitRGB32(int full)
 | |
| {
 | |
| 	unsigned short *ps;
 | |
| 	unsigned long  *pd;
 | |
| 	int pixels;
 | |
| 	if(full) {
 | |
| 		ps = framebuff;
 | |
| 		pd = (unsigned long *) screenbuff;
 | |
| 		pixels = 208*320;
 | |
| 	} else {
 | |
| 		ps = framebuff  + framebuff_offs;
 | |
| 		pd = (unsigned long *) screenbuff + framebuff_offs;
 | |
| 		pixels = framebuff_len;
 | |
| 	}
 | |
| 
 | |
| 	vidConvCpyRGB32(pd, ps, pixels);
 | |
| 	//for(; ps < ps_end; ps++)
 | |
| 		// Convert          0000bbb0 ggg0rrr0
 | |
| 		// to  ..0 rrr00000 ggg00000 bbb00000
 | |
| 	//	*pd++ = ((*ps&0x000F)<<20) | ((*ps&0x00F0)<<8) | ((*ps&0x0F00)>>4);
 | |
| }
 | |
| 
 | |
| // -------- rendermode 2 ---------
 | |
| 
 | |
| void vidBlit16M2(int full)
 | |
| {
 | |
| 	unsigned short *ps = framebuff+328*8;
 | |
| 	unsigned short *pd = (unsigned short *) screenbuff;
 | |
| 
 | |
| 	if(currentConfig.iScreenRotation == TPicoConfig::PRot90) {
 | |
| 		if(Pico.video.reg[12]&1)
 | |
| 			vidConvCpyM2_16_90(pd, ps, 320/8);
 | |
| 		else {
 | |
| 			if(full) memset(pd, 0, 208*32*2);
 | |
| 			pd += 208*32;
 | |
| 			vidConvCpyM2_16_90(pd, ps, 256/8);
 | |
| 			if(full) memset(pd + 208*256, 0, 208*32*2);
 | |
| 		}
 | |
| 	} else if(currentConfig.iScreenRotation == TPicoConfig::PRot270) {
 | |
| 		if(Pico.video.reg[12]&1)
 | |
| 			vidConvCpyM2_16_270(pd, ps, 320/8);
 | |
| 		else {
 | |
| 			if(full) memset(pd, 0, 208*32*2);
 | |
| 			pd += 208*32;
 | |
| 			ps -= 64;     // the blitter starts copying from the right border, so we need to adjust
 | |
| 			vidConvCpyM2_16_270(pd, ps, 256/8);
 | |
| 			if(full) memset(pd + 208*256, 0, 208*32*2);
 | |
| 		}
 | |
| 	}
 | |
| /*
 | |
|     for(int x=0; x < 320; x++)
 | |
| 		for(int y=207; y>=0; y--) {
 | |
| 			*pd++ = *(ps+8+x+y*328);
 | |
| 		}
 | |
| */
 | |
| }
 | |
| 
 | |
| void vidBlitRGB32M2(int full)
 | |
| {
 | |
| 	unsigned short *ps = framebuff+328*8;
 | |
| 	unsigned long  *pd = (unsigned long *) screenbuff;
 | |
| 
 | |
| 	if(currentConfig.iScreenRotation == TPicoConfig::PRot90) {
 | |
| 		if(Pico.video.reg[12]&1)
 | |
| 			vidConvCpyM2_RGB32_90(pd, ps, 320/8);
 | |
| 		else {
 | |
| 			if(full) memset(pd, 0, 208*32*4);
 | |
| 			pd += 208*32;
 | |
| 			vidConvCpyM2_RGB32_90(pd, ps, 256/8);
 | |
| 			if(full) memset(pd + 208*256, 0, 208*32*4);
 | |
| 		}
 | |
| 	} else if(currentConfig.iScreenRotation == TPicoConfig::PRot270) {
 | |
| 		if(Pico.video.reg[12]&1)
 | |
| 			vidConvCpyM2_RGB32_270(pd, ps, 320/8);
 | |
| 		else {
 | |
| 			if(full) memset(pd, 0, 208*32*4);
 | |
| 			pd += 208*32;
 | |
| 			ps -= 64;     // the blitter starts copying from the right border, so we need to adjust
 | |
| 			vidConvCpyM2_RGB32_270(pd, ps, 256/8);
 | |
| 			if(full) memset(pd + 208*256, 0, 208*32*4);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void PrepareCramRGB444M2()
 | |
| {
 | |
| 	vidConvCpyRGB444(cram_high, Pico.cram, 0x40);
 | |
| }
 | |
| 
 | |
| void PrepareCramRGB565M2()
 | |
| {
 | |
| 	vidConvCpyRGB565(cram_high, Pico.cram, 0x40);
 | |
| }
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // main functions
 | |
| 
 | |
| int vidInit(int displayMode, void *vidmem, int p800, int reinit)
 | |
| {
 | |
| 	if(!reinit) {
 | |
| 		// prepare framebuffer
 | |
| 		screenbuff = (unsigned short *) vidmem;
 | |
| 		framebuff =  (unsigned short *) malloc(framebuffsize);
 | |
| 
 | |
| 		if(!screenbuff) return KErrNotSupported;
 | |
| 		if(!framebuff)  return KErrNoMemory;
 | |
| 
 | |
| 		// Cram function: go and hack Pico so it never gets called
 | |
| 		PicoCram = EmuCramNull;
 | |
| 	}
 | |
| 
 | |
| 	// select suitable blitter
 | |
| 	switch(displayMode) {
 | |
| 		case EColor4K:  vidBlit = vidBlitKeyCfg = vidBlitRGB444; break;
 | |
| 		case EColor64K: vidBlit = vidBlitKeyCfg = vidBlitRGB565; break;
 | |
| 		case EColor16M: vidBlit = vidBlitKeyCfg = vidBlitRGB32;  break;
 | |
| 		default: return KErrNotSupported;
 | |
| 	}
 | |
| 
 | |
| 	memset(framebuff, 0, framebuffsize);
 | |
| 
 | |
| 	// rendermode 2?
 | |
| 	if(PicoOpt&0x10) {
 | |
| 		switch(displayMode) {
 | |
| 			case EColor4K:
 | |
| 				vidBlit = vidBlit16M2;
 | |
| 				PicoPrepareCram = PrepareCramRGB444M2;
 | |
| 				PicoCramHigh = cram_high;
 | |
| 				color_redM2 = 0x0F22;
 | |
| 				dt_dmask = 0x0333;
 | |
| 				break;
 | |
| 			case EColor64K:
 | |
| 				vidBlit = vidBlit16M2;
 | |
| 				PicoPrepareCram = PrepareCramRGB565M2;
 | |
| 				PicoCramHigh = cram_high;
 | |
| 				color_redM2 = 0xF882;
 | |
| 				dt_dmask = 0x39e7;
 | |
| 				break;
 | |
| 			case EColor16M:
 | |
| 				vidBlit = vidBlitRGB32M2;
 | |
| 				break;
 | |
| 		}
 | |
| 		drawTextFps = drawTextFpsM2;
 | |
| 		drawTextNotice = drawTextNoticeM2;
 | |
| 		vidBlit(1);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	framebuff_offs = 0;
 | |
| 	framebuff_len  = 208*320;
 | |
| 	vidBlit(1);
 | |
| 
 | |
| 	// setup all orientation related stuff
 | |
| 	if(currentConfig.iScreenRotation == TPicoConfig::PRot0) {
 | |
| 		if(currentConfig.iScreenMode == TPicoConfig::PMCenter) {
 | |
| 			PicoScan = EmuScanCenter0;
 | |
| 			framebuff_len = 208*224;
 | |
| 			drawTextFps = drawTextFps0;
 | |
| 			drawTextNotice = drawTextNotice0;
 | |
| 		} else {
 | |
| 			if(currentConfig.iScreenMode == TPicoConfig::PMFit2) {
 | |
| 				if(p800) {
 | |
| 					fc_inc = 6518; // 0.651786 (144+2)
 | |
| 					txtheight_fit = 139;
 | |
| 					framebuff_len = 208*146;
 | |
| 				} else {
 | |
| 					fc_inc = 9286; // 0.92857
 | |
| 					txtheight_fit = 201;
 | |
| 					framebuff_len = 208*208;
 | |
| 				}
 | |
| 			}
 | |
| 			PicoScan = EmuScanFit0;
 | |
| 			drawTextFps = drawTextFpsFit0;
 | |
| 			drawTextNotice = drawTextNoticeFit0;
 | |
| 		}
 | |
| 	} else if(currentConfig.iScreenRotation == TPicoConfig::PRot90) {
 | |
| 		if(currentConfig.iScreenMode == TPicoConfig::PMFit)
 | |
| 			 PicoScan = EmuScanFit90;
 | |
| 		else PicoScan = EmuScanCenter90;
 | |
| 		drawTextFps = drawTextFps90;
 | |
| 		drawTextNotice = drawTextNotice90;
 | |
| 	} else if(currentConfig.iScreenRotation == TPicoConfig::PRot180) {
 | |
| 		if(currentConfig.iScreenMode == TPicoConfig::PMCenter) {
 | |
| 			PicoScan = EmuScanCenter180;
 | |
| 			framebuff_len = 208*224;
 | |
| 		} else {
 | |
| 			if(currentConfig.iScreenMode == TPicoConfig::PMFit2) {
 | |
| 				if(p800) {
 | |
| 					fc_inc = 6518; // 0.651786
 | |
| 					fc_lines = 146;
 | |
| 					framebuff_len = 208*146;
 | |
| 				} else {
 | |
| 					fc_inc = 9286; // 0.92857
 | |
| 					fc_lines = 208;
 | |
| 					framebuff_len = 208*208;
 | |
| 				}
 | |
| 			}
 | |
| 			PicoScan = EmuScanFit180;
 | |
| 		}
 | |
| 		drawTextFps = drawTextFps180;
 | |
| 		drawTextNotice = drawTextNotice180;
 | |
| 	} else if(currentConfig.iScreenRotation == TPicoConfig::PRot270) {
 | |
| 		if(currentConfig.iScreenMode == TPicoConfig::PMFit)
 | |
| 			 PicoScan = EmuScanFit270;
 | |
| 		else PicoScan = EmuScanCenter270;
 | |
| 		drawTextFps = drawTextFps270;
 | |
| 		drawTextNotice = drawTextNotice270;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void vidFree()
 | |
| {
 | |
| 	free(framebuff);
 | |
| 	framebuff = 0;
 | |
| }
 | |
| 
 | |
| void vidDrawFrame(char *noticeStr, char *fpsStr, int num)
 | |
| {
 | |
| 	PicoFrame();
 | |
| 	if(currentConfig.iFlags & 2)
 | |
| 		drawTextFps(fpsStr);
 | |
| 	drawTextNotice(noticeStr);
 | |
| 
 | |
| 	vidBlit(!num); // copy full frame once a second
 | |
| }
 | |
| 
 | |
| void vidKeyConfigFrame(const TUint whichAction, TInt flipClosed)
 | |
| {
 | |
| 	int i;
 | |
| 	char buttonNames[128];
 | |
| 	buttonNames[0] = 0;
 | |
| 	memset(framebuff, 0, framebuffsize);
 | |
| 
 | |
| 	unsigned long currentActCode = 1 << whichAction;
 | |
| 
 | |
| 	if(flipClosed) {
 | |
| 		drawRectFilled(TRect(56, 2, 152, 16), color_grey); // 96x14
 | |
| 		drawArrow0(TPoint(64, 3), -1, color_green);
 | |
| 		drawArrow0(TPoint(144, 3), 1, color_green);
 | |
| 		drawText0(64, 20, "USE@JOG@TO@SELECT", color_red);
 | |
| 
 | |
| 		drawText0(68, 6, actionNames[whichAction], color_red);
 | |
| 	} else {
 | |
| 		// draw all "buttons" in reverse order
 | |
| 		const TPicoAreaConfigEntry *e = areaConfig + 1; i = 0;
 | |
| 		while(e->rect != TRect(0,0,0,0)) { e++; i++; }
 | |
| 		for(e--, i--; e->rect != TRect(0,0,0,0); e--, i--)
 | |
| 			drawRect(e->rect, (currentConfig.iAreaBinds[i] & currentActCode) ? color_red : color_red_dim);
 | |
| 	
 | |
| 		// draw config controls
 | |
| 		drawRectFilled(TRect(190, 112, 204, 208), color_grey);
 | |
| 		drawArrow90(TPoint(203, 120), -1, color_green);
 | |
| 		drawArrow90(TPoint(203, 200),  1, color_green);
 | |
| 
 | |
| 		drawText90(200, 124, actionNames[whichAction], color_red);
 | |
| 	}
 | |
| 
 | |
| 	// draw active button names if there are any
 | |
| 	i = 0;
 | |
| 	for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++, i++)
 | |
| 		if(currentConfig.iKeyBinds[i] & currentActCode) {
 | |
| 			if(buttonNames[0]) strcat(buttonNames, ";@");
 | |
| 			strcat(buttonNames, e->name);
 | |
| 		}
 | |
| 	if(buttonNames[0]) {
 | |
| 		if(flipClosed) {
 | |
| 			buttonNames[41] = 0; // only 61 chars fit
 | |
| 			drawText0(2, 138, buttonNames, color_blue);
 | |
| 		} else {
 | |
| 			buttonNames[61] = 0;
 | |
| 			drawText90(12, 10, buttonNames, color_blue);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	vidBlitKeyCfg(1);
 | |
| }
 | |
| 
 | |
| void vidDrawFCconfigDone()
 | |
| {
 | |
| 	drawText0(64, 20, "USE@JOG@TO@SELECT", 0); // blank prev text
 | |
| 	drawText0(54, 30, "OPEN@FLIP@TO@CONTINUE", color_red);
 | |
| 	vidBlitKeyCfg(1);
 | |
| }
 | |
| 
 | |
| void vidDrawNotice(const char *txt)
 | |
| {
 | |
| 	if(framebuff) {
 | |
| 		drawTextNotice(txt);
 | |
| 		vidBlit(1);
 | |
| 	}
 | |
| }
 | 
