1 #ifndef DEBUG_SCREEN_H
2 #define DEBUG_SCREEN_H
3 
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdarg.h>
7 #include <inttypes.h>
8 
9 typedef struct PsvDebugScreenFont {
10 	unsigned char* glyphs, width, height, first, last, size_w, size_h;
11 } PsvDebugScreenFont;
12 
13 #include "debugScreenFont.c"
14 
15 #define SCREEN_WIDTH    (960)
16 #define SCREEN_HEIGHT   (544)
17 #define SCREEN_FB_WIDTH (960)
18 #define SCREEN_FB_SIZE  (2 * 1024 * 1024) //Must be 256KB aligned
19 #ifndef SCREEN_TAB_SIZE /* this allow easy overriding */
20 #define SCREEN_TAB_SIZE (8)
21 #endif
22 #define SCREEN_TAB_W    ((F.size_w) * SCREEN_TAB_SIZE)
23 #define F psvDebugScreenFont
24 
25 #define FROM_GREY(c     ) ((((c)*9)    <<16)  |  (((c)*9)       <<8)  | ((c)*9))
26 #define FROM_3BIT(c,dark) (((!!((c)&4))<<23)  | ((!!((c)&2))<<15)     | ((!!((c)&1))<<7) | (dark ? 0 : 0x7F7F7F))
27 #define FROM_6BIT(c     ) ((((c)%6)*(51<<16)) | ((((c)/6)%6)*(51<<8)) | ((((c)/36)%6)*51))
28 #define FROM_FULL(r,g,b ) ((r<<16) | (g<<8) | (b))
29 #define CLEARSCRN(H,toH,W,toW) for(int h = H; h < toH; h++)for(int w = W; w < toW; w++)((uint32_t*)base)[h*SCREEN_FB_WIDTH + w] = colorBg;
30 
31 static int mutex, coordX, savedX, coordY, savedY;
32 static uint32_t defaultFg = 0xFFFFFFFF, colorFg = 0xFFFFFFFF;
33 static uint32_t defaultBg = 0xFF000000, colorBg = 0xFF000000;
34 
35 
36 #ifdef __vita__
37 #include <psp2/display.h>
38 #include <psp2/kernel/sysmem.h>
39 #include <psp2/kernel/threadmgr.h>
40 static void* base; // pointer to frame buffer
41 #else
42 #define sceKernelLockMutex(m,v,x) m=v
43 #define sceKernelUnlockMutex(m,v) m=v
44 static char base[SCREEN_FB_WIDTH * SCREEN_HEIGHT * 4];
45 #endif
46 
psvDebugScreenEscape(const unsigned char * str)47 static size_t psvDebugScreenEscape(const unsigned char *str) {
48 	for(unsigned i = 0, argc = 0, arg[32] = {0}; argc < (sizeof(arg)/sizeof(*arg)) && str[i]!='\0'; i++)
49 		switch(str[i]) {
50 		case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
51 			arg[argc]=(arg[argc]*10) + (str[i] - '0');continue;
52 		case ';': argc++;continue;
53 		case 's': savedX = coordX; savedY = coordY; return i;
54 		case 'u': coordX = savedX; coordY = savedY; return i;
55 		case 'A': coordY -= arg[0]    * (F.size_h); return i;
56 		case 'B': coordY += arg[0]    * (F.size_h); return i;
57 		case 'C': coordX += arg[0]    * (F.size_w); return i;
58 		case 'D': coordX -= arg[0]    * (F.size_w); return i;
59 		case 'E': coordY += arg[0]    * (F.size_h); coordX = 0; return i;
60 		case 'F': coordY -= arg[0]    * (F.size_h); coordX = 0; return i;
61 		case 'G': coordX = (arg[0]-1) * (F.size_w); return i;
62 		case 'H':
63 		case 'f': coordY = (arg[0]-1) * (F.size_h);
64 		          coordX = (arg[1]-1) * (F.size_w); return i;
65 		case 'J': //clear part of (J=screen, K=Line) so J code reuse part of K
66 		case 'K': if(arg[0]==0)CLEARSCRN(coordY, coordY + F.size_h, coordX, SCREEN_WIDTH);//from curosr to end
67 		          if(arg[0]==1)CLEARSCRN(coordY, coordY + F.size_h, 0, coordX);//from begining to cursor
68 		          if(arg[0]==2)CLEARSCRN(coordY, coordY + F.size_h, 0, SCREEN_WIDTH);//whole line
69 		          if(str[i]=='K')return i;
70 		          if(arg[0]==0)CLEARSCRN(coordY, SCREEN_HEIGHT, 0, SCREEN_WIDTH);
71 		          if(arg[0]==1)CLEARSCRN(0, coordY, 0, SCREEN_WIDTH);
72 		          if(arg[0]==2)CLEARSCRN(0, SCREEN_HEIGHT, 0, SCREEN_WIDTH);
73 		          return i;
74 		case 'm':// Color
75 			if(!arg[0]) {arg[0] = 39;arg[1] = 49;argc = 1;}//no/0 args == reset BG + FG
76 			for(unsigned c = 0; c <= argc; c++) {
77 				uint32_t unit = arg[c] % 10, mode = arg[c] / 10, *color = mode&1 ? &colorFg : &colorBg;
78 				if (arg[c]==1)colorFg|=0x808080;
79 				if (arg[c]==2)colorFg&=0x7F7F7F;
80 				if (mode!=3 && mode!=4 && mode!=9 && mode!=10)continue;//skip unsported modes
81 				if (unit == 9){ // reset FG or BG
82 					*color = mode&1 ? defaultFg : defaultBg;
83 				} else if ((unit==8) && (arg[c+1]==5)) { // 8bit : [0-15][16-231][232-256] color map
84 					c+=2;*color = arg[c]<=15?FROM_3BIT(arg[c],mode<9):arg[c]>=232?FROM_GREY(arg[c]-232):FROM_6BIT(arg[c]-16);
85 				} else if ((unit==8) && (arg[c+1]==2)) { // 24b color space
86 					*color = FROM_FULL(arg[c+4], arg[c+3], arg[c+2]);c+=4;
87 				} else *color = FROM_3BIT(unit,mode<9); // standard 8+8 colors
88 			}
89 			return i;
90 		}
91 	return 0;
92 }
psvDebugScreenInit()93 int psvDebugScreenInit() {
94 #ifdef NO_psvDebugScreenInit
95 	return 0;/* avoid linking non-initializer (prx) with sceDisplay/sceMemory */
96 #else
97 	mutex = sceKernelCreateMutex("log_mutex", 0, 0, NULL);
98 	SceUID displayblock = sceKernelAllocMemBlock("display", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, SCREEN_FB_SIZE, NULL);
99 	sceKernelGetMemBlockBase(displayblock, (void**)&base);
100 	SceDisplayFrameBuf frame = { sizeof(frame), base, SCREEN_FB_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
101 	return sceDisplaySetFrameBuf(&frame, SCE_DISPLAY_SETBUF_NEXTFRAME);
102 #endif
103 }
104 
psvDebugScreenPuts(const char * _text)105 int psvDebugScreenPuts(const char * _text) {
106 	const unsigned char*text = (const unsigned char*)_text;
107 	int bytes_per_glyph = (F.width * F.height) / 8;
108 	sceKernelLockMutex(mutex, 1, NULL);
109 	int c;
110 	for (c = 0; text[c] ; c++) {
111 		unsigned char t = text[c];
112 		if (t == '\t') {
113 			coordX += SCREEN_TAB_W - coordX % SCREEN_TAB_W;
114 			continue;
115 		}
116 		if (coordX + F.width > SCREEN_WIDTH) {
117 			coordY += F.size_h;
118 			coordX = 0;
119 		}
120 		if (coordY + F.height > SCREEN_HEIGHT) {
121 			coordX = coordY = 0;
122 		}
123 		if (t == '\n') {
124 			coordX = 0;
125 			coordY += F.size_h;
126 			continue;
127 		} else if (t == '\r') {
128 			coordX = 0;
129 			continue;
130 		} else if ((t == '\e') && (text[c+1] == '[')) {
131 			c += psvDebugScreenEscape(text + c + 2) + 2;
132 			if(coordX < 0)coordX = 0;// CSI position are 1-based,
133 			if(coordY < 0)coordY = 0;// prevent 0-based coordiate from producing a negativ X/Y
134 			continue;
135 		}else if ((t > F.last) || (t < F.first))
136 			continue; // skip non printable glyph
137 		uint32_t *vram = ((uint32_t*)base) + coordX + coordY * SCREEN_FB_WIDTH;
138 		uint8_t *font = &F.glyphs[ (t - F.first) * bytes_per_glyph];
139 		for (int row = 0, mask = 1 << 7; row < F.height; row++, vram += SCREEN_FB_WIDTH) {
140 			for (uint32_t *pixel = vram, col = 0; col < F.width ; col++, mask>>=1) {
141 				if (!mask) {font++; mask = 1 << 7;}// no more mask : we exausted this byte
142 				*pixel++ = (*font&mask)?colorFg:colorBg;
143 			}
144 			for (uint32_t *pixel = vram + F.width, col = F.width; col < F.size_w ; col++)
145 				*pixel++ = colorBg;// right margin
146 		}
147 		for (int row = F.height; row < F.size_h; row++, vram += SCREEN_FB_WIDTH)
148 			for (uint32_t *pixel = vram, col = 0; col < F.size_w ; col++)
149 				*pixel++ = colorBg;// bottom margin
150 		coordX += F.size_w;
151 	}
152 	sceKernelUnlockMutex(mutex, 1);
153 	return c;
154 }
155 
156 __attribute__((__format__ (__printf__, 1, 2)))
psvDebugScreenPrintf(const char * format,...)157 int psvDebugScreenPrintf(const char *format, ...) {
158 	char buf[4096];
159 
160 	va_list opt;
161 	va_start(opt, format);
162 	int ret = vsnprintf(buf, sizeof(buf), format, opt);
163 	psvDebugScreenPuts(buf);
164 	va_end(opt);
165 
166 	return ret;
167 }
168 
psvDebugScreenSetFgColor(uint32_t rgb)169 void psvDebugScreenSetFgColor(uint32_t rgb){
170 	psvDebugScreenPrintf("\e[38;2;%lu;%lu;%lum", (rgb>>16)&0xFF, (rgb>>8)&0xFF, rgb&0xFF);
171 }
psvDebugScreenSetBgColor(uint32_t rgb)172 void psvDebugScreenSetBgColor(uint32_t rgb){
173 	psvDebugScreenPrintf("\e[48;2;%lu;%lu;%lum", (rgb>>16)&0xFF, (rgb>>8)&0xFF, rgb&0xFF);
174 }
175 #undef F
176 #endif
177