summaryrefslogtreecommitdiff
path: root/apps/plugins/lib/gray_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lib/gray_core.c')
-rw-r--r--apps/plugins/lib/gray_core.c674
1 files changed, 555 insertions, 119 deletions
diff --git a/apps/plugins/lib/gray_core.c b/apps/plugins/lib/gray_core.c
index b582cd801a..18b9c3c821 100644
--- a/apps/plugins/lib/gray_core.c
+++ b/apps/plugins/lib/gray_core.c
@@ -7,13 +7,13 @@
7* \/ \/ \/ \/ \/ 7* \/ \/ \/ \/ \/
8* $Id$ 8* $Id$
9* 9*
10* Grayscale framework 10* Greyscale framework
11* Core functions 11* Core & miscellaneous functions
12* 12*
13* This is a generic framework to use grayscale display within Rockbox 13* This is a generic framework to use grayscale display within Rockbox
14* plugins. It obviously does not work for the player. 14* plugins. It obviously does not work for the player.
15* 15*
16* Copyright (C) 2004 Jens Arnold 16* Copyright (C) 2004-2005 Jens Arnold
17* 17*
18* All files in this archive are subject to the GNU General Public License. 18* All files in this archive are subject to the GNU General Public License.
19* See the file COPYING in the source tree root for full license agreement. 19* See the file COPYING in the source tree root for full license agreement.
@@ -31,28 +31,28 @@
31 31
32/* Global variables */ 32/* Global variables */
33struct plugin_api *_gray_rb = NULL; /* global api struct pointer */ 33struct plugin_api *_gray_rb = NULL; /* global api struct pointer */
34_tGraybuf *_graybuf = NULL; /* pointer to grayscale buffer */ 34struct _gray_info _gray_info; /* global info structure */
35short _gray_random_buffer; /* buffer for random number generator */ 35short _gray_random_buffer; /* buffer for random number generator */
36 36
37/* Prototypes */ 37/* Prototypes */
38static void _timer_isr(void); 38static void _timer_isr(void);
39 39
40/* timer interrupt handler: display next bitplane */ 40/* Timer interrupt handler: display next bitplane */
41static void _timer_isr(void) 41static void _timer_isr(void)
42{ 42{
43 _gray_rb->lcd_blit(_graybuf->data + MULU16(_graybuf->plane_size, 43 _gray_rb->lcd_blit(_gray_info.plane_data + MULU16(_gray_info.plane_size,
44 _graybuf->cur_plane), _graybuf->x, _graybuf->by, 44 _gray_info.cur_plane), _gray_info.x, _gray_info.by,
45 _graybuf->width, _graybuf->bheight, _graybuf->width); 45 _gray_info.width, _gray_info.bheight, _gray_info.width);
46 46
47 if (++_graybuf->cur_plane >= _graybuf->depth) 47 if (++_gray_info.cur_plane >= _gray_info.depth)
48 _graybuf->cur_plane = 0; 48 _gray_info.cur_plane = 0;
49 49
50 if (_graybuf->flags & _GRAY_DEFERRED_UPDATE) /* lcd_update() requested? */ 50 if (_gray_info.flags & _GRAY_DEFERRED_UPDATE) /* lcd_update() requested? */
51 { 51 {
52 int x1 = MAX(_graybuf->x, 0); 52 int x1 = MAX(_gray_info.x, 0);
53 int x2 = MIN(_graybuf->x + _graybuf->width, LCD_WIDTH); 53 int x2 = MIN(_gray_info.x + _gray_info.width, LCD_WIDTH);
54 int y1 = MAX(_graybuf->by << 3, 0); 54 int y1 = MAX(_gray_info.by << _PBLOCK_EXP, 0);
55 int y2 = MIN((_graybuf->by << 3) + _graybuf->height, LCD_HEIGHT); 55 int y2 = MIN((_gray_info.by << _PBLOCK_EXP) + _gray_info.height, LCD_HEIGHT);
56 56
57 if (y1 > 0) /* refresh part above overlay, full width */ 57 if (y1 > 0) /* refresh part above overlay, full width */
58 _gray_rb->lcd_update_rect(0, 0, LCD_WIDTH, y1); 58 _gray_rb->lcd_update_rect(0, 0, LCD_WIDTH, y1);
@@ -66,90 +66,108 @@ static void _timer_isr(void)
66 if (x2 < LCD_WIDTH) /* refresh part to the right of overlay */ 66 if (x2 < LCD_WIDTH) /* refresh part to the right of overlay */
67 _gray_rb->lcd_update_rect(x2, y1, LCD_WIDTH - x2, y2 - y1); 67 _gray_rb->lcd_update_rect(x2, y1, LCD_WIDTH - x2, y2 - y1);
68 68
69 _graybuf->flags &= ~_GRAY_DEFERRED_UPDATE; /* clear request */ 69 _gray_info.flags &= ~_GRAY_DEFERRED_UPDATE; /* clear request */
70 } 70 }
71} 71}
72 72
73/*--------------------------------------------------------------------------- 73/* Initialise the framework and prepare the greyscale display buffer
74 Initialize the framework
75 ----------------------------------------------------------------------------
76 Every framework needs such a function, and it has to be called as
77 the very first one */
78void gray_init(struct plugin_api* newrb)
79{
80 _gray_rb = newrb;
81}
82 74
83/*---------------------------------------------------------------------------
84 Prepare the grayscale display buffer
85 ----------------------------------------------------------------------------
86 arguments: 75 arguments:
76 newrb = pointer to plugin api
87 gbuf = pointer to the memory area to use (e.g. plugin buffer) 77 gbuf = pointer to the memory area to use (e.g. plugin buffer)
88 gbuf_size = max usable size of the buffer 78 gbuf_size = max usable size of the buffer
89 width = width in pixels (1..112) 79 buffered = use chunky pixel buffering with delta buffer?
90 bheight = height in 8-pixel units (1..8) 80 This allows to use all drawing functions, but needs more
91 depth = desired number of shades - 1 (1..32) 81 memory. Unbuffered operation provides only a subset of
82 drawing functions. (only gray_bitmap drawing and scrolling)
83 width = width in pixels (1..LCD_WIDTH)
84 bheight = height in LCD pixel-block units (8 pixels for b&w LCD,
85 4 pixels for 4-grey LCD) (1..LCD_HEIGHT/block_size)
86 depth = number of bitplanes to use (1..32 for b&w LCD, 1..16 for
87 4-grey LCD).
92 88
93 result: 89 result:
94 = depth if there was enough memory 90 = depth if there was enough memory
95 < depth if there wasn't enough memory. The number of displayable 91 < depth if there wasn't enough memory. The number of displayable
96 shades is smaller than desired, but it still works 92 shades is smaller than desired, but it still works
97 = 0 if there wasn't even enough memory for 1 bitplane (black & white) 93 = 0 if there wasn't even enough memory for 1 bitplane
98 94
99 You can request any depth from 1 to 32, not just powers of 2. The routine 95 You can request any depth in the allowed range, not just powers of 2. The
100 performs "graceful degradation" if the memory is not sufficient for the 96 routine performs "graceful degradation" if the memory is not sufficient for
101 desired depth. As long as there is at least enough memory for 1 bitplane, 97 the desired depth. As long as there is at least enough memory for 1 bitplane,
102 it creates as many bitplanes as fit into memory, although 1 bitplane will 98 it creates as many bitplanes as fit into memory, although 1 bitplane won't
103 only deliver black & white display. 99 deliver an enhancement over the native display.
104 100
105 If you need info about the memory taken by the grayscale buffer, supply an 101 The number of displayable shades is calculated as follows:
106 int* as the last parameter. This int will then contain the number of bytes 102 b&w LCD: shades = depth + 1
107 used. The total memory needed can be calculated as follows: 103 4-grey LCD: shades = 3 * depth + 1
108 total_mem = 104
109 sizeof(_tGraybuf) (= 64 bytes currently) 105 If you need info about the memory taken by the greyscale buffer, supply a
110 + sizeof(long) (= 4 bytes) 106 long* as the last parameter. This long will then contain the number of bytes
111 + (width * bheight + sizeof(long)) * depth 107 used. The total memory needed can be calculated as follows:
112 + 0..3 (longword alignment of grayscale display buffer) 108 total_mem =
113 */ 109 shades * sizeof(long) (bitpatterns)
114int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width, 110 + (width * bheight) * depth (bitplane data)
115 int bheight, int depth, int *buf_taken) 111 + buffered ? (chunky front- & backbuffer)
112 (width * bheight * 8(4) * 2) : 0
113 + 0..3 (longword alignment) */
114int gray_init(struct plugin_api* newrb, unsigned char *gbuf, long gbuf_size,
115 bool buffered, int width, int bheight, int depth, long *buf_taken)
116{ 116{
117 int possible_depth, plane_size; 117 int possible_depth, i, j;
118 int i, j, align; 118 long plane_size, buftaken;
119
120 _gray_rb = newrb;
119 121
120 if ((unsigned) width > LCD_WIDTH 122 if ((unsigned) width > LCD_WIDTH
121 || (unsigned) bheight > (LCD_HEIGHT/8) 123 || (unsigned) bheight > (LCD_HEIGHT/_PBLOCK)
122 || depth < 1) 124 || depth < 1)
123 return 0; 125 return 0;
124 126
125 /* the buffer has to be long aligned */ 127 /* the buffer has to be long aligned */
126 align = 3 - (((unsigned long)gbuf + 3) & 3); 128 buftaken = (-(long)gbuf) & 3;
127 gbuf += align; 129 gbuf += buftaken;
128 gbuf_size -= align; 130
131 /* chunky front- & backbuffer */
132 if (buffered)
133 {
134 plane_size = MULU16(width, bheight << _PBLOCK_EXP);
135 buftaken += 2 * plane_size;
136 if (buftaken > gbuf_size)
137 return 0;
138
139 _gray_info.cur_buffer = gbuf;
140 gbuf += plane_size;
141 /* set backbuffer to 0xFF to guarantee the initial full update */
142 _gray_rb->memset(gbuf, 0xFF, plane_size);
143 _gray_info.back_buffer = gbuf;
144 gbuf += plane_size;
145 }
129 146
130 plane_size = MULU16(width, bheight); 147 plane_size = MULU16(width, bheight);
131 possible_depth = (gbuf_size - sizeof(_tGraybuf) - sizeof(long)) 148 possible_depth = (gbuf_size - buftaken - sizeof(long))
132 / (plane_size + sizeof(long)); 149 / (plane_size + _LEVEL_FAC * sizeof(long));
133 150
134 if (possible_depth < 1) 151 if (possible_depth < 1)
135 return 0; 152 return 0;
136 153
137 depth = MIN(depth, 32); 154 depth = MIN(depth, _MAX_DEPTH);
138 depth = MIN(depth, possible_depth); 155 depth = MIN(depth, possible_depth);
139 156
140 _graybuf = (_tGraybuf *) gbuf; /* global pointer to buffer structure */ 157 _gray_info.x = 0;
141 158 _gray_info.by = 0;
142 _graybuf->x = 0; 159 _gray_info.width = width;
143 _graybuf->by = 0; 160 _gray_info.height = bheight << _PBLOCK_EXP;
144 _graybuf->width = width; 161 _gray_info.bheight = bheight;
145 _graybuf->height = bheight << 3; 162 _gray_info.plane_size = plane_size;
146 _graybuf->bheight = bheight; 163 _gray_info.depth = depth;
147 _graybuf->plane_size = plane_size; 164 _gray_info.cur_plane = 0;
148 _graybuf->depth = depth; 165 _gray_info.flags = 0;
149 _graybuf->cur_plane = 0; 166 _gray_info.plane_data = gbuf;
150 _graybuf->flags = 0; 167 gbuf += depth * plane_size;
151 _graybuf->bitpattern = (unsigned long *) (gbuf + sizeof(_tGraybuf)); 168 _gray_info.bitpattern = (unsigned long *)gbuf;
152 _graybuf->data = (unsigned char *) (_graybuf->bitpattern + depth + 1); 169 buftaken += depth * plane_size
170 + (_LEVEL_FAC * depth + 1) * sizeof(long);
153 171
154 i = depth - 1; 172 i = depth - 1;
155 j = 8; 173 j = 8;
@@ -158,12 +176,10 @@ int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
158 i >>= 1; 176 i >>= 1;
159 j--; 177 j--;
160 } 178 }
161 _graybuf->randmask = 0xFFu >> j; 179 _gray_info.randmask = 0xFFu >> j;
162 180
163 /* initial state is all white */
164 _gray_rb->memset(_graybuf->data, 0, MULU16(depth, plane_size));
165
166 /* Precalculate the bit patterns for all possible pixel values */ 181 /* Precalculate the bit patterns for all possible pixel values */
182#if LCD_DEPTH == 1
167 for (i = 0; i <= depth; i++) 183 for (i = 0; i <= depth; i++)
168 { 184 {
169 unsigned long pattern = 0; 185 unsigned long pattern = 0;
@@ -181,72 +197,492 @@ int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
181 } 197 }
182 /* now the lower <depth> bits contain the pattern */ 198 /* now the lower <depth> bits contain the pattern */
183 199
184 _graybuf->bitpattern[i] = pattern; 200 _gray_info.bitpattern[i] = pattern;
185 } 201 }
186 202#elif LCD_DEPTH == 2
187 _graybuf->fg_pattern = _graybuf->bitpattern[0]; /* black */ 203 for (i = 0; i < depth; i++)
188 _graybuf->bg_pattern = _graybuf->bitpattern[depth]; /* white */
189 _graybuf->drawmode = GRAY_DRAW_SOLID;
190 _graybuf->curfont = FONT_SYSFIXED;
191
192 if (buf_taken) /* caller requested info about space taken */
193 { 204 {
194 *buf_taken = sizeof(_tGraybuf) + sizeof(long) 205 unsigned long pattern = 0;
195 + MULU16(plane_size + sizeof(long), depth) + align; 206 int value = 0;
207
208 for (j = 0; j < depth; j++)
209 {
210 pattern <<= 2;
211 value += i;
212
213 if (value >= depth)
214 value -= depth;
215 else
216 pattern |= 3;
217 }
218
219 _gray_info.bitpattern[i] = pattern | (~pattern & 0xaaaaaaaa);
220 _gray_info.bitpattern[i+depth] = (pattern & 0xaaaaaaaa)
221 | (~pattern & 0x55555555);
222 _gray_info.bitpattern[i+2*depth] = pattern & 0x55555555;
196 } 223 }
224 _gray_info.bitpattern[3*depth] = 0;
225#endif /* LCD_DEPTH */
226
227 _gray_info.fg_brightness = 0;
228 _gray_info.bg_brightness = _LEVEL_FAC * depth;
229 _gray_info.drawmode = DRMODE_SOLID;
230 _gray_info.curfont = FONT_SYSFIXED;
231
232 if (buf_taken) /* caller requested info about space taken */
233 *buf_taken = buftaken;
197 234
198 return depth; 235 return depth;
199} 236}
200 237
201/*--------------------------------------------------------------------------- 238/* Release the greyscale display buffer and the library
202 Release the grayscale display buffer 239 DO CALL either this function or at least gray_show_display(false)
203 ---------------------------------------------------------------------------- 240 before you exit, otherwise nasty things may happen. */
204 Switches the grayscale overlay off at first if it is still running, 241void gray_release(void)
205 then sets the pointer to NULL.
206 DO CALL either this function or at least gray_show_display(false)
207 before you exit, otherwise nasty things may happen.
208 */
209void gray_release_buffer(void)
210{ 242{
211 gray_show_display(false); 243 gray_show(false);
212 _graybuf = NULL;
213} 244}
214 245
215/*--------------------------------------------------------------------------- 246/* Switch the greyscale overlay on or off
216 Switch the grayscale overlay on or off 247 DO NOT call lcd_update() or any other api function that directly accesses
217 ---------------------------------------------------------------------------- 248 the lcd while the greyscale overlay is running! If you need to do
218 enable = true: the grayscale overlay is switched on if initialized 249 lcd_update() to update something outside the greyscale overlay area, use
219 = false: the grayscale overlay is switched off and the regular lcd 250 gray_deferred_update() instead.
220 content is restored
221
222 DO NOT call lcd_update() or any other api function that directly accesses
223 the lcd while the grayscale overlay is running! If you need to do
224 lcd_update() to update something outside the grayscale overlay area, use
225 gray_deferred_update() instead.
226 251
227 Other functions to avoid are: 252 Other functions to avoid are:
228 lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(), 253 lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(),
229 lcd_set_invert_display(), lcd_set_flip(), lcd_roll() 254 lcd_set_invert_display(), lcd_set_flip(), lcd_roll() */
230 255void gray_show(bool enable)
231 The grayscale display consumes ~50 % CPU power (for a full screen overlay,
232 less if the overlay is smaller) when switched on. You can switch the overlay
233 on and off as many times as you want.
234 */
235void gray_show_display(bool enable)
236{ 256{
237 if (enable) 257 if (enable)
238 { 258 {
239 _graybuf->flags |= _GRAY_RUNNING; 259 _gray_info.flags |= _GRAY_RUNNING;
240 _gray_rb->plugin_register_timer(FREQ / 67, 1, _timer_isr); 260 _gray_rb->plugin_register_timer(FREQ / 67, 1, _timer_isr);
241 } 261 }
242 else 262 else
243 { 263 {
244 _gray_rb->plugin_unregister_timer(); 264 _gray_rb->plugin_unregister_timer();
245 _graybuf->flags &= ~_GRAY_RUNNING; 265 _gray_info.flags &= ~_GRAY_RUNNING;
246 _gray_rb->lcd_update(); /* restore whatever there was before */ 266 _gray_rb->lcd_update(); /* restore whatever there was before */
247 } 267 }
248} 268}
249 269
250#endif // #ifdef HAVE_LCD_BITMAP 270/* Update a rectangular area of the greyscale overlay */
251#endif // #ifndef SIMULATOR 271void gray_update_rect(int x, int y, int width, int height)
272{
273 int ymax;
274 long srcofs;
275 unsigned char *dst;
276
277 if (width <= 0)
278 return; /* nothing to do */
279
280 /* The Y coordinates have to work on whole pixel block rows */
281 ymax = (y + height - 1) >> _PBLOCK_EXP;
282 y >>= _PBLOCK_EXP;
283
284 if (x + width > _gray_info.width)
285 width = _gray_info.width - x;
286 if (ymax >= _gray_info.bheight)
287 ymax = _gray_info.bheight - 1;
288
289 srcofs = (y << _PBLOCK_EXP) + MULU16(_gray_info.height, x);
290 dst = _gray_info.plane_data + MULU16(_gray_info.width, y) + x;
291
292 /* Copy specified rectange bitmap to hardware */
293 for (; y <= ymax; y++)
294 {
295 long srcofs_row = srcofs;
296 unsigned char *dst_row = dst;
297 unsigned char *dst_end = dst_row + width;
298
299 do
300 {
301#if (CONFIG_CPU == SH7034) && (LCD_DEPTH == 1)
302 unsigned mask;
303 unsigned long change;
304 unsigned long pat_stack[8];
305 unsigned long *pat_ptr;
306 unsigned char *end_addr;
307
308 asm (
309 "mov.l @(%3,%1),r1 \n"
310 "mov.l @(%3,%2),r2 \n"
311 "xor r1,r2 \n"
312 "add #4,%3 \n"
313 "mov.l @(%3,%1),r1 \n"
314 "mov.l @(%3,%2),%0 \n"
315 "xor r1,%0 \n"
316 "or r2,%0 \n"
317 : /* outputs */
318 /* %0 */ "=r"(change)
319 : /* inputs */
320 /* %1 */ "r"(_gray_info.cur_buffer),
321 /* %2 */ "r"(_gray_info.back_buffer),
322 /* %3 */ "z"(srcofs_row)
323 : /* clobbers */
324 "r1", "r2"
325 );
326
327 if (change != 0)
328 {
329 pat_ptr = &pat_stack[8];
330
331 /* precalculate the bit patterns with random shifts
332 * for all 8 pixels and put them on an extra "stack" */
333 asm volatile (
334 "add %5,%3 \n"
335 "add %5,%4 \n"
336 "mov #8,r3 \n" /* loop count in r3: 8 pixels */
337
338 ".ur_pre_loop: \n"
339 "mov.b @%3+,r0 \n" /* read current buffer */
340 "mov.b @%4,r1 \n" /* read back buffer */
341 "mov #0,r2 \n" /* preset for skipped pixel */
342 "mov.b r0,@%4 \n" /* update back buffer */
343 "add #1,%4 \n"
344 "cmp/eq r0,r1 \n" /* no change? */
345 "bt .ur_skip \n" /* -> skip */
346
347 "shll2 r0 \n" /* pixel value -> pattern offset */
348 "mov.l @(r0,%7),r4 \n" /* r4 = bitpattern[byte]; */
349
350 "mov #75,r0 \n"
351 "mulu r0,%0 \n" /* multiply by 75 */
352 "sts macl,%0 \n"
353 "add #74,%0 \n" /* add another 74 */
354 /* Since the lower bits are not very random: */
355 "swap.b %0,r1 \n" /* get bits 8..15 (need max. 5) */
356 "and %8,r1 \n" /* mask out unneeded bits */
357
358 "cmp/hs %6,r1 \n" /* random >= depth ? */
359 "bf .ur_ntrim \n"
360 "sub %6,r1 \n" /* yes: random -= depth; */
361 ".ur_ntrim: \n"
362
363 "mov.l .ashlsi3,r0 \n" /** rotate pattern **/
364 "jsr @r0 \n" /* r4 -> r0, shift left by r5 */
365 "mov r1,r5 \n"
366
367 "mov %6,r5 \n"
368 "sub r1,r5 \n" /* r5 = depth - r1 */
369 "mov.l .lshrsi3,r1 \n"
370 "jsr @r1 \n" /* r4 -> r0, shift right by r5 */
371 "mov r0,r2 \n" /* store previous result in r2 */
372
373 "or r0,r2 \n" /* rotated_pattern = r2 | r0 */
374 "clrt \n" /* mask bit = 0 (replace) */
375
376 ".ur_skip: \n" /* T == 1 if skipped */
377 "rotcr %2 \n" /* get mask bit */
378 "mov.l r2,@-%1 \n" /* push on pattern stack */
379
380 "add #-1,r3 \n" /* decrease loop count */
381 "cmp/pl r3 \n" /* loop count > 0? */
382 "bt .ur_pre_loop\n" /* yes: loop */
383 "shlr8 %2 \n"
384 "shlr16 %2 \n"
385 : /* outputs */
386 /* %0, in & out */ "+r"(_gray_random_buffer),
387 /* %1, in & out */ "+r"(pat_ptr),
388 /* %2 */ "=&r"(mask)
389 : /* inputs */
390 /* %3 */ "r"(_gray_info.cur_buffer),
391 /* %4 */ "r"(_gray_info.back_buffer),
392 /* %5 */ "2"(srcofs_row),
393 /* %6 */ "r"(_gray_info.depth),
394 /* %7 */ "r"(_gray_info.bitpattern),
395 /* %8 */ "r"(_gray_info.randmask)
396 : /* clobbers */
397 "r0", "r1", "r2", "r3", "r4", "r5", "macl", "pr"
398 );
399
400 end_addr = dst_row + MULU16(_gray_info.depth, _gray_info.plane_size);
401
402 /* set the bits for all 8 pixels in all bytes according to the
403 * precalculated patterns on the pattern stack */
404 asm (
405 "mov.l @%3+,r1 \n" /* pop all 8 patterns */
406 "mov.l @%3+,r2 \n"
407 "mov.l @%3+,r3 \n"
408 "mov.l @%3+,r6 \n"
409 "mov.l @%3+,r7 \n"
410 "mov.l @%3+,r8 \n"
411 "mov.l @%3+,r9 \n"
412 "mov.l @%3+,r10 \n"
413
414 "tst %4,%4 \n" /* nothing to keep? */
415 "bt .ur_sloop \n" /* yes: jump to short loop */
416
417 ".ur_floop: \n" /** full loop (there are bits to keep)**/
418 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
419 "rotcl r0 \n" /* rotate t bit into r0 */
420 "shlr r2 \n"
421 "rotcl r0 \n"
422 "shlr r3 \n"
423 "rotcl r0 \n"
424 "shlr r6 \n"
425 "rotcl r0 \n"
426 "shlr r7 \n"
427 "rotcl r0 \n"
428 "shlr r8 \n"
429 "rotcl r0 \n"
430 "shlr r9 \n"
431 "rotcl r0 \n"
432 "shlr r10 \n"
433 "mov.b @%0,%3 \n" /* read old value */
434 "rotcl r0 \n"
435 "and %4,%3 \n" /* mask out unneeded bits */
436 "or r0,%3 \n" /* set new bits */
437 "mov.b %3,@%0 \n" /* store value to bitplane */
438 "add %2,%0 \n" /* advance to next bitplane */
439 "cmp/hi %0,%1 \n" /* last bitplane done? */
440 "bt .ur_floop \n" /* no: loop */
441
442 "bra .ur_end \n"
443 "nop \n"
444
445 ".ur_sloop: \n" /** short loop (nothing to keep) **/
446 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
447 "rotcl r0 \n" /* rotate t bit into r0 */
448 "shlr r2 \n"
449 "rotcl r0 \n"
450 "shlr r3 \n"
451 "rotcl r0 \n"
452 "shlr r6 \n"
453 "rotcl r0 \n"
454 "shlr r7 \n"
455 "rotcl r0 \n"
456 "shlr r8 \n"
457 "rotcl r0 \n"
458 "shlr r9 \n"
459 "rotcl r0 \n"
460 "shlr r10 \n"
461 "rotcl r0 \n"
462 "mov.b r0,@%0 \n" /* store byte to bitplane */
463 "add %2,%0 \n" /* advance to next bitplane */
464 "cmp/hi %0,%1 \n" /* last bitplane done? */
465 "bt .ur_sloop \n" /* no: loop */
466
467 ".ur_end: \n"
468 : /* outputs */
469 : /* inputs */
470 /* %0 */ "r"(dst_row),
471 /* %1 */ "r"(end_addr),
472 /* %2 */ "r"(_gray_info.plane_size),
473 /* %3 */ "r"(pat_ptr),
474 /* %4 */ "r"(mask)
475 : /* clobbers */
476 "r0", "r1", "r2", "r3", "r6", "r7", "r8", "r9", "r10"
477 );
478 }
479#endif
480 srcofs_row += _gray_info.height;
481 dst_row++;
482 }
483 while (dst_row < dst_end);
484
485 srcofs += _PBLOCK;
486 dst += _gray_info.width;
487 }
488}
489
490#if CONFIG_CPU == SH7034
491/* References to C library routines used in gray_update_rect() */
492asm (
493 ".align 2 \n"
494".ashlsi3: \n" /* C library routine: */
495 ".long ___ashlsi3 \n" /* shift r4 left by r5, return in r0 */
496".lshrsi3: \n" /* C library routine: */
497 ".long ___lshrsi3 \n" /* shift r4 right by r5, return in r0 */
498 /* both routines preserve r4, destroy r5 and take ~16 cycles */
499);
500#endif
501
502/* Update the whole greyscale overlay */
503void gray_update(void)
504{
505 gray_update_rect(0, 0, _gray_info.width, _gray_info.height);
506}
507
508/* Do an lcd_update() to show changes done by rb->lcd_xxx() functions
509 (in areas of the screen not covered by the greyscale overlay). */
510void gray_deferred_lcd_update(void)
511{
512 if (_gray_info.flags & _GRAY_RUNNING)
513 _gray_info.flags |= _GRAY_DEFERRED_UPDATE;
514 else
515 _gray_rb->lcd_update();
516}
517
518/*** Screenshot ***/
519
520#define BMP_NUMCOLORS (_MAX_DEPTH * _LEVEL_FAC + 1)
521#define BMP_BPP 8
522#define BMP_LINESIZE ((LCD_WIDTH + 3) & ~3)
523#define BMP_HEADERSIZE (54 + 4 * BMP_NUMCOLORS)
524#define BMP_DATASIZE (BMP_LINESIZE * LCD_HEIGHT)
525#define BMP_TOTALSIZE (BMP_HEADERSIZE + BMP_DATASIZE)
526
527#define LE16_CONST(x) (x)&0xff, ((x)>>8)&0xff
528#define LE32_CONST(x) (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff
529
530static const unsigned char bmpheader[] =
531{
532 0x42, 0x4d, /* 'BM' */
533 LE32_CONST(BMP_TOTALSIZE), /* Total file size */
534 0x00, 0x00, 0x00, 0x00, /* Reserved */
535 LE32_CONST(BMP_HEADERSIZE), /* Offset to start of pixel data */
536
537 0x28, 0x00, 0x00, 0x00, /* Size of (2nd) header */
538 LE32_CONST(LCD_WIDTH), /* Width in pixels */
539 LE32_CONST(LCD_HEIGHT), /* Height in pixels */
540 0x01, 0x00, /* Number of planes (always 1) */
541 LE16_CONST(BMP_BPP), /* Bits per pixel 1/4/8/16/24 */
542 0x00, 0x00, 0x00, 0x00, /* Compression mode, 0 = none */
543 LE32_CONST(BMP_DATASIZE), /* Size of bitmap data */
544 0xc4, 0x0e, 0x00, 0x00, /* Horizontal resolution (pixels/meter) */
545 0xc4, 0x0e, 0x00, 0x00, /* Vertical resolution (pixels/meter) */
546 LE32_CONST(BMP_NUMCOLORS), /* Number of used colours */
547 LE32_CONST(BMP_NUMCOLORS), /* Number of important colours */
548};
549
550#if LCD_DEPTH == 1
551#define BMP_RED 0x90
552#define BMP_GREEN 0xee
553#define BMP_BLUE 0x90
554#elif LCD_DEPTH == 2
555#define BMP_RED 0xad
556#define BMP_GREEN 0xd8
557#define BMP_BLUE 0xe6
558#endif
559
560static unsigned char linebuf[BMP_LINESIZE];
561
562/* Save the current display content (b&w and greyscale overlay) to an 8-bit
563 * BMP file in the root directory. */
564void gray_screendump(void)
565{
566 int fh, i, bright;
567 int x, y, by, mask;
568 int gx, gby;
569 unsigned char *lcdptr, *grayptr, *grayptr2;
570 char filename[MAX_PATH];
571
572#ifdef HAVE_RTC
573 struct tm *tm = _gray_rb->get_time();
574
575 _gray_rb->snprintf(filename, MAX_PATH,
576 "/graydump %04d-%02d-%02d %02d-%02d-%02d.bmp",
577 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
578 tm->tm_hour, tm->tm_min, tm->tm_sec);
579#else
580 {
581 DIR* dir;
582 int max_dump_file = 1; /* default to graydump_0001.bmp */
583 dir = _gray_rb->opendir("/");
584 if (dir) /* found */
585 {
586 /* Search for the highest screendump filename present,
587 increment behind that. So even with "holes"
588 (deleted files), the newest will always have the
589 highest number. */
590 while(true)
591 {
592 struct dirent* entry;
593 int curr_dump_file;
594 /* walk through the directory content */
595 entry = _gray_rb->readdir(dir);
596 if (!entry)
597 {
598 _gray_rb->closedir(dir);
599 break; /* end of dir */
600 }
601 if (_gray_rb->strncasecmp(entry->d_name, "graydump_", 9))
602 continue; /* no screendump file */
603 curr_dump_file = _gray_rb->atoi(&entry->d_name[9]);
604 if (curr_dump_file >= max_dump_file)
605 max_dump_file = curr_dump_file + 1;
606 }
607 }
608 _gray_rb->snprintf(filename, MAX_PATH, "/graydump_%04d.bmp",
609 max_dump_file);
610 }
611#endif
612
613 fh = _gray_rb->creat(filename, O_WRONLY);
614
615 if (fh < 0)
616 return;
617
618 _gray_rb->write(fh, bmpheader, sizeof(bmpheader)); /* write header */
619
620 /* build clut, always 33 entries */
621 linebuf[3] = 0;
622
623 for (i = 0; i < BMP_NUMCOLORS; i++)
624 {
625 bright = MIN(i, _LEVEL_FAC * _gray_info.depth);
626 linebuf[0] = MULU16(BMP_BLUE, bright) / (_LEVEL_FAC * _gray_info.depth);
627 linebuf[1] = MULU16(BMP_GREEN, bright) / (_LEVEL_FAC * _gray_info.depth);
628 linebuf[2] = MULU16(BMP_RED, bright) / (_LEVEL_FAC * _gray_info.depth);
629 _gray_rb->write(fh, linebuf, 4);
630 }
631
632 /* 8-bit BMP image goes bottom -> top */
633 for (y = LCD_HEIGHT - 1; y >= 0; y--)
634 {
635#if LCD_DEPTH == 1
636 _gray_rb->memset(linebuf, BMP_NUMCOLORS-1, LCD_WIDTH);
637
638 mask = 1 << (y & 7);
639 by = y >> 3;
640 lcdptr = _gray_rb->lcd_framebuffer + MULU16(LCD_WIDTH, by);
641 gby = by - _gray_info.by;
642
643 if ((_gray_info.flags & _GRAY_RUNNING)
644 && (unsigned) gby < (unsigned) _gray_info.bheight)
645 {
646 /* line contains greyscale (and maybe b&w) graphics */
647 grayptr = _gray_info.plane_data + MULU16(_gray_info.width, gby);
648
649 for (x = 0; x < LCD_WIDTH; x++)
650 {
651 if (*lcdptr++ & mask)
652 linebuf[x] = 0;
653
654 gx = x - _gray_info.x;
655
656 if ((unsigned)gx < (unsigned)_gray_info.width)
657 {
658 bright = 0;
659 grayptr2 = grayptr + gx;
660
661 for (i = 0; i < _gray_info.depth; i++)
662 {
663 if (!(*grayptr2 & mask))
664 bright++;
665 grayptr2 += _gray_info.plane_size;
666 }
667 linebuf[x] = bright;
668 }
669 }
670 }
671 else
672 {
673 /* line contains only b&w graphics */
674 for (x = 0; x < LCD_WIDTH; x++)
675 if (*lcdptr++ & mask)
676 linebuf[x] = 0;
677 }
678#endif
679
680 _gray_rb->write(fh, linebuf, sizeof(linebuf));
681 }
682
683 _gray_rb->close(fh);
684}
685
686#endif /* HAVE_LCD_BITMAP */
687#endif /* !SIMULATOR */
252 688