summaryrefslogtreecommitdiff
path: root/apps/plugins/lib/gray.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lib/gray.c')
-rw-r--r--apps/plugins/lib/gray.c1878
1 files changed, 0 insertions, 1878 deletions
diff --git a/apps/plugins/lib/gray.c b/apps/plugins/lib/gray.c
deleted file mode 100644
index e68e5eef7b..0000000000
--- a/apps/plugins/lib/gray.c
+++ /dev/null
@@ -1,1878 +0,0 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Grayscale framework
11*
12* This is a generic framework to use grayscale display within Rockbox
13* plugins. It obviously does not work for the player.
14*
15* Copyright (C) 2004 Jens Arnold
16*
17* All files in this archive are subject to the GNU General Public License.
18* See the file COPYING in the source tree root for full license agreement.
19*
20* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21* KIND, either express or implied.
22*
23****************************************************************************/
24
25#ifndef SIMULATOR /* not for simulator by now */
26#include "plugin.h"
27
28#ifdef HAVE_LCD_BITMAP /* and also not for the Player */
29#include "gray.h"
30
31/**** internal core functions and definitions ****/
32
33/* You do not want to touch these if you don't know exactly what you're
34 * doing. */
35
36#define GRAY_RUNNING 0x0001 /* grayscale overlay is running */
37#define GRAY_DEFERRED_UPDATE 0x0002 /* lcd_update() requested */
38
39/* unsigned 16 bit multiplication (a single instruction on the SH) */
40#define MULU16(a, b) ((unsigned long) \
41 (((unsigned short) (a)) * ((unsigned short) (b))))
42
43/* The grayscale buffer management structure */
44typedef struct
45{
46 int x;
47 int by; /* 8-pixel units */
48 int width;
49 int height;
50 int bheight; /* 8-pixel units */
51 int plane_size;
52 int depth; /* number_of_bitplanes = (number_of_grayscales - 1) */
53 int cur_plane; /* for the timer isr */
54 unsigned long randmask; /* mask for random value in _writepixel() */
55 unsigned long flags; /* various flags, see #defines */
56 unsigned long *bitpattern; /* pointer to start of pattern table */
57 unsigned char *data; /* pointer to start of bitplane data */
58 unsigned long fg_pattern; /* current foreground pattern */
59 unsigned long bg_pattern; /* current background pattern */
60 int drawmode; /* current draw mode */
61 struct font *curfont; /* current selected font */
62} tGraybuf;
63
64/** globals **/
65
66static struct plugin_api *rb = NULL; /* global api struct pointer */
67static tGraybuf *graybuf = NULL; /* pointer to grayscale buffer */
68static short random_buffer;
69
70/** prototypes **/
71
72static void _timer_isr(void);
73
74static void _writepixel(int x, int y, unsigned long pattern);
75static void _invertpixel(int x, int y, unsigned long pattern);
76
77static void _writearray(unsigned char *address, unsigned char *src, int stride,
78 unsigned mask);
79
80static void _writeblock(unsigned char *address, unsigned mask, unsigned bits);
81static void _invertblock(unsigned char *address, unsigned mask, unsigned bits);
82static void _writeblockfg(unsigned char *address, unsigned mask, unsigned bits);
83static void _writeblockbg(unsigned char *address, unsigned mask, unsigned bits);
84
85/** function pointer arrays **/
86
87static void (*_pixelfuncs[4])(int x, int y, unsigned long pattern) = {
88 _invertpixel, _writepixel, _writepixel, _writepixel
89};
90
91static void (*_blockfuncs[4])(unsigned char *address, unsigned mask,
92 unsigned bits) = {
93 _invertblock, _writeblockfg, _writeblockbg, _writeblock
94};
95
96/** implementation **/
97
98/* timer interrupt handler: display next bitplane */
99static void _timer_isr(void)
100{
101 rb->lcd_blit(graybuf->data + MULU16(graybuf->plane_size, graybuf->cur_plane),
102 graybuf->x, graybuf->by, graybuf->width, graybuf->bheight,
103 graybuf->width);
104
105 if (++graybuf->cur_plane >= graybuf->depth)
106 graybuf->cur_plane = 0;
107
108 if (graybuf->flags & GRAY_DEFERRED_UPDATE) /* lcd_update() requested? */
109 {
110 int x1 = MAX(graybuf->x, 0);
111 int x2 = MIN(graybuf->x + graybuf->width, LCD_WIDTH);
112 int y1 = MAX(graybuf->by << 3, 0);
113 int y2 = MIN((graybuf->by << 3) + graybuf->height, LCD_HEIGHT);
114
115 if (y1 > 0) /* refresh part above overlay, full width */
116 rb->lcd_update_rect(0, 0, LCD_WIDTH, y1);
117
118 if (y2 < LCD_HEIGHT) /* refresh part below overlay, full width */
119 rb->lcd_update_rect(0, y2, LCD_WIDTH, LCD_HEIGHT - y2);
120
121 if (x1 > 0) /* refresh part to the left of overlay */
122 rb->lcd_update_rect(0, y1, x1, y2 - y1);
123
124 if (x2 < LCD_WIDTH) /* refresh part to the right of overlay */
125 rb->lcd_update_rect(x2, y1, LCD_WIDTH - x2, y2 - y1);
126
127 graybuf->flags &= ~GRAY_DEFERRED_UPDATE; /* clear request */
128 }
129}
130
131/* Set a pixel to a specific bit pattern (low level routine) */
132static void _writepixel(int x, int y, unsigned long pattern)
133{
134 register unsigned mask, random;
135 register unsigned char *address;
136
137 /* Some (pseudo-)random function must be used here to shift the bit
138 * pattern randomly, otherwise you would get flicker and/or moire.
139 * Since rand() is relatively slow, I've implemented a simple, but very
140 * fast pseudo-random generator based on linear congruency in assembler.
141 * It delivers max. 16 pseudo-random bits in each iteration. */
142
143 /* simple but fast pseudo-random generator */
144 asm (
145 "mov #75,r1 \n"
146 "mulu %1,r1 \n" /* multiply by 75 */
147 "sts macl,%1 \n" /* get result */
148 "add #74,%1 \n" /* add another 74 */
149 /* Since the lower bits are not very random: */
150 "swap.b %1,%0 \n" /* get bits 8..15 (need max. 5) */
151 "and %2,%0 \n" /* mask out unneeded bits */
152 : /* outputs */
153 /* %0 */ "=&r"(random),
154 /* %1, in & out */ "+r"(random_buffer)
155 : /* inputs */
156 /* %2 */ "r"(graybuf->randmask)
157 : /* clobbers */
158 "r1", "macl"
159 );
160
161 /* precalculate mask and byte address in first bitplane */
162 asm (
163 "mov %3,%0 \n" /* take y as base for address offset */
164 "shlr2 %0 \n" /* shift right by 3 (= divide by 8) */
165 "shlr %0 \n"
166 "mulu %0,%2 \n" /* multiply with width */
167 "and #7,%3 \n" /* get lower 3 bits of y */
168 "sts macl,%0 \n" /* get mulu result */
169 "add %4,%0 \n" /* add base + x to get final address */
170
171 "mov %3,%1 \n" /* move lower 3 bits of y out of r0 */
172 "mova .wp_masktable,%3\n" /* get address of mask table in r0 */
173 "bra .wp_predone \n" /* skip the table */
174 "mov.b @(%3,%1),%1 \n" /* get entry from mask table */
175
176 ".align 2 \n"
177 ".wp_masktable: \n" /* mask table */
178 ".byte 0x01 \n"
179 ".byte 0x02 \n"
180 ".byte 0x04 \n"
181 ".byte 0x08 \n"
182 ".byte 0x10 \n"
183 ".byte 0x20 \n"
184 ".byte 0x40 \n"
185 ".byte 0x80 \n"
186
187 ".wp_predone: \n"
188 : /* outputs */
189 /* %0 */ "=&r"(address),
190 /* %1 */ "=&r"(mask)
191 : /* inputs */
192 /* %2 */ "r"(graybuf->width),
193 /* %3 = r0 */ "z"(y),
194 /* %4 */ "r"(graybuf->data + x)
195 : /* clobbers */
196 "macl"
197 );
198
199 /* the hard part: set bits in all bitplanes according to pattern */
200 asm volatile (
201 "cmp/hs %1,%5 \n" /* random >= depth ? */
202 "bf .wp_ntrim \n"
203 "sub %1,%5 \n" /* yes: random -= depth */
204 /* it's sufficient to do this once, since the mask guarantees
205 * random < 2 * depth */
206 ".wp_ntrim: \n"
207
208 /* calculate some addresses */
209 "mulu %4,%1 \n" /* end address offset */
210 "not %3,r1 \n" /* get inverse mask (for "and") */
211 "sts macl,%1 \n" /* result of mulu */
212 "mulu %4,%5 \n" /* address offset of <random>'th plane */
213 "add %2,%1 \n" /* end offset -> end address */
214 "sts macl,%5 \n" /* result of mulu */
215 "add %2,%5 \n" /* address of <random>'th plane */
216 "bra .wp_start1 \n"
217 "mov %5,r2 \n" /* copy address */
218
219 /* first loop: set bits from <random>'th bitplane to last */
220 ".wp_loop1: \n"
221 "mov.b @r2,r3 \n" /* get data byte */
222 "shlr %0 \n" /* shift bit mask, sets t bit */
223 "and r1,r3 \n" /* reset bit (-> "white") */
224 "bf .wp_white1 \n" /* t=0? -> "white" bit */
225 "or %3,r3 \n" /* set bit ("black" bit) */
226 ".wp_white1: \n"
227 "mov.b r3,@r2 \n" /* store data byte */
228 "add %4,r2 \n" /* advance address to next bitplane */
229 ".wp_start1: \n"
230 "cmp/hi r2,%1 \n" /* address < end address ? */
231 "bt .wp_loop1 \n"
232
233 "bra .wp_start2 \n"
234 "nop \n"
235
236 /* second loop: set bits from first to <random-1>'th bitplane
237 * Bit setting works the other way round here to equalize average
238 * execution times for bright and dark pixels */
239 ".wp_loop2: \n"
240 "mov.b @%2,r3 \n" /* get data byte */
241 "shlr %0 \n" /* shift bit mask, sets t bit */
242 "or %3,r3 \n" /* set bit (-> "black") */
243 "bt .wp_black2 \n" /* t=1? -> "black" bit */
244 "and r1,r3 \n" /* reset bit ("white" bit) */
245 ".wp_black2: \n"
246 "mov.b r3,@%2 \n" /* store data byte */
247 "add %4,%2 \n" /* advance address to next bitplane */
248 ".wp_start2: \n"
249 "cmp/hi %2,%5 \n" /* address < <random>'th address ? */
250 "bt .wp_loop2 \n"
251 : /* outputs */
252 : /* inputs */
253 /* %0 */ "r"(pattern),
254 /* %1 */ "r"(graybuf->depth),
255 /* %2 */ "r"(address),
256 /* %3 */ "r"(mask),
257 /* %4 */ "r"(graybuf->plane_size),
258 /* %5 */ "r"(random)
259 : /* clobbers */
260 "r1", "r2", "r3", "macl"
261 );
262}
263
264/* invert all bits for one pixel (low level routine) */
265static void _invertpixel(int x, int y, unsigned long pattern)
266{
267 register unsigned mask;
268 register unsigned char *address;
269
270 (void) pattern; /* not used for invert */
271
272 /* precalculate mask and byte address in first bitplane */
273 asm (
274 "mov %3,%0 \n" /* take y as base for address offset */
275 "shlr2 %0 \n" /* shift right by 3 (= divide by 8) */
276 "shlr %0 \n"
277 "mulu %0,%2 \n" /* multiply with width */
278 "and #7,%3 \n" /* get lower 3 bits of y */
279 "sts macl,%0 \n" /* get mulu result */
280 "add %4,%0 \n" /* add base + x to get final address */
281
282 "mov %3,%1 \n" /* move lower 3 bits of y out of r0 */
283 "mova .ip_masktable,%3\n" /* get address of mask table in r0 */
284 "bra .ip_predone \n" /* skip the table */
285 "mov.b @(%3,%1),%1 \n" /* get entry from mask table */
286
287 ".align 2 \n"
288 ".ip_masktable: \n" /* mask table */
289 ".byte 0x01 \n"
290 ".byte 0x02 \n"
291 ".byte 0x04 \n"
292 ".byte 0x08 \n"
293 ".byte 0x10 \n"
294 ".byte 0x20 \n"
295 ".byte 0x40 \n"
296 ".byte 0x80 \n"
297
298 ".ip_predone: \n"
299 : /* outputs */
300 /* %0 */ "=&r"(address),
301 /* %1 */ "=&r"(mask)
302 : /* inputs */
303 /* %2 */ "r"(graybuf->width),
304 /* %3 = r0 */ "z"(y),
305 /* %4 */ "r"(graybuf->data + x)
306 : /* clobbers */
307 "macl"
308 );
309
310 /* invert bits in all bitplanes */
311 asm volatile (
312 "mov #0,r1 \n" /* current_plane = 0 */
313
314 ".ip_loop: \n"
315 "mov.b @%1,r2 \n" /* get data byte */
316 "add #1,r1 \n" /* current_plane++; */
317 "xor %2,r2 \n" /* invert bits */
318 "mov.b r2,@%1 \n" /* store data byte */
319 "add %3,%1 \n" /* advance address to next bitplane */
320 "cmp/hi r1,%0 \n" /* current_plane < depth ? */
321 "bt .ip_loop \n"
322 : /* outputs */
323 : /* inputs */
324 /* %0 */ "r"(graybuf->depth),
325 /* %1 */ "r"(address),
326 /* %2 */ "r"(mask),
327 /* %3 */ "r"(graybuf->plane_size)
328 : /* clobbers */
329 "r1", "r2"
330 );
331}
332
333/* Write an 8-pixel block, defined by their brightnesses in a graymap.
334 * Address is the byte in the first bitplane, src is the graymap start address,
335 * stride is the increment for the graymap to get to the next pixel, mask
336 * determines which pixels of the destination block are changed. For "0" bits,
337 * the src address is not incremented! */
338static void _writearray(unsigned char *address, unsigned char *src, int stride,
339 unsigned mask)
340{
341 unsigned long pat_stack[8];
342 register unsigned char *end_addr;
343 register unsigned long *pat_ptr = &pat_stack[8];
344
345 /* precalculate the bit patterns with random shifts (same RNG as
346 * _writepixel, see there for an explanation) for all 8 pixels and put them
347 * on an extra "stack" */
348 asm (
349 "mov #8,r3 \n" /* loop count in r3: 8 pixels */
350 "mov %7,r2 \n" /* copy mask */
351
352 ".wa_loop: \n" /** load pattern for pixel **/
353 "mov #0,r0 \n" /* pattern for skipped pixel must be 0 */
354 "shlr r2 \n" /* shift out lsb of mask */
355 "bf .wa_skip \n" /* skip this pixel */
356
357 "mov.b @%2,r0 \n" /* load src byte */
358 "extu.b r0,r0 \n" /* extend unsigned */
359 "mulu %4,r0 \n" /* macl = byte * depth; */
360 "add %3,%2 \n" /* src += stride; */
361 "sts macl,r4 \n" /* r4 = macl; */
362 "add r4,r0 \n" /* byte += r4; */
363 "shlr8 r0 \n" /* byte >>= 8; */
364 "shll2 r0 \n"
365 "mov.l @(r0,%5),r4 \n" /* r4 = bitpattern[byte]; */
366
367 "mov #75,r0 \n"
368 "mulu r0,%0 \n" /* multiply by 75 */
369 "sts macl,%0 \n"
370 "add #74,%0 \n" /* add another 74 */
371 /* Since the lower bits are not very random: */
372 "swap.b %0,r1 \n" /* get bits 8..15 (need max. 5) */
373 "and %6,r1 \n" /* mask out unneeded bits */
374
375 "cmp/hs %4,r1 \n" /* random >= depth ? */
376 "bf .wa_ntrim \n"
377 "sub %4,r1 \n" /* yes: random -= depth; */
378 ".wa_ntrim: \n"
379
380 "mov.l .ashlsi3,r0 \n" /** rotate pattern **/
381 "jsr @r0 \n" /* r4 -> r0, shift left by r5 */
382 "mov r1,r5 \n"
383
384 "mov %4,r5 \n"
385 "sub r1,r5 \n" /* r5 = depth - r1 */
386 "mov.l .lshrsi3,r1 \n"
387 "jsr @r1 \n" /* r4 -> r0, shift right by r5 */
388 "mov r0,r1 \n" /* store previous result in r1 */
389
390 "or r1,r0 \n" /* rotated_pattern = r0 | r1 */
391
392 ".wa_skip: \n"
393 "mov.l r0,@-%1 \n" /* push on pattern stack */
394
395 "add #-1,r3 \n" /* decrease loop count */
396 "cmp/pl r3 \n" /* loop count > 0? */
397 "bt .wa_loop \n" /* yes: loop */
398 : /* outputs */
399 /* %0, in & out */ "+r"(random_buffer),
400 /* %1, in & out */ "+r"(pat_ptr)
401 : /* inputs */
402 /* %2 */ "r"(src),
403 /* %3 */ "r"(stride),
404 /* %4 */ "r"(graybuf->depth),
405 /* %5 */ "r"(graybuf->bitpattern),
406 /* %6 */ "r"(graybuf->randmask),
407 /* %7 */ "r"(mask)
408 : /* clobbers */
409 "r0", "r1", "r2", "r3", "r4", "r5", "macl"
410 );
411
412 end_addr = address + MULU16(graybuf->depth, graybuf->plane_size);
413
414 /* set the bits for all 8 pixels in all bytes according to the
415 * precalculated patterns on the pattern stack */
416 asm volatile (
417 "mov.l @%3+,r1 \n" /* pop all 8 patterns */
418 "mov.l @%3+,r2 \n"
419 "mov.l @%3+,r3 \n"
420 "mov.l @%3+,r8 \n"
421 "mov.l @%3+,r9 \n"
422 "mov.l @%3+,r10 \n"
423 "mov.l @%3+,r11 \n"
424 "mov.l @%3+,r12 \n"
425
426 "not %4,%4 \n" /* "set" mask -> "keep" mask */
427 "extu.b %4,%4 \n" /* mask out high bits */
428 "tst %4,%4 \n" /* nothing to keep? */
429 "bt .wa_sloop \n" /* yes: jump to short loop */
430
431 ".wa_floop: \n" /** full loop (there are bits to keep)**/
432 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
433 "rotcl r0 \n" /* rotate t bit into r0 */
434 "shlr r2 \n"
435 "rotcl r0 \n"
436 "shlr r3 \n"
437 "rotcl r0 \n"
438 "shlr r8 \n"
439 "rotcl r0 \n"
440 "shlr r9 \n"
441 "rotcl r0 \n"
442 "shlr r10 \n"
443 "rotcl r0 \n"
444 "shlr r11 \n"
445 "rotcl r0 \n"
446 "shlr r12 \n"
447 "mov.b @%0,%3 \n" /* read old value */
448 "rotcl r0 \n"
449 "and %4,%3 \n" /* mask out unneeded bits */
450 "or r0,%3 \n" /* set new bits */
451 "mov.b %3,@%0 \n" /* store value to bitplane */
452 "add %2,%0 \n" /* advance to next bitplane */
453 "cmp/hi %0,%1 \n" /* last bitplane done? */
454 "bt .wa_floop \n" /* no: loop */
455
456 "bra .wa_end \n"
457 "nop \n"
458
459 ".wa_sloop: \n" /** short loop (nothing to keep) **/
460 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
461 "rotcl r0 \n" /* rotate t bit into r0 */
462 "shlr r2 \n"
463 "rotcl r0 \n"
464 "shlr r3 \n"
465 "rotcl r0 \n"
466 "shlr r8 \n"
467 "rotcl r0 \n"
468 "shlr r9 \n"
469 "rotcl r0 \n"
470 "shlr r10 \n"
471 "rotcl r0 \n"
472 "shlr r11 \n"
473 "rotcl r0 \n"
474 "shlr r12 \n"
475 "rotcl r0 \n"
476 "mov.b r0,@%0 \n" /* store byte to bitplane */
477 "add %2,%0 \n" /* advance to next bitplane */
478 "cmp/hi %0,%1 \n" /* last bitplane done? */
479 "bt .wa_sloop \n" /* no: loop */
480
481 ".wa_end: \n"
482 : /* outputs */
483 : /* inputs */
484 /* %0 */ "r"(address),
485 /* %1 */ "r"(end_addr),
486 /* %2 */ "r"(graybuf->plane_size),
487 /* %3 */ "r"(pat_ptr),
488 /* %4 */ "r"(mask)
489 : /* clobbers */
490 "r0", "r1", "r2", "r3", "r8", "r9", "r10", "r11", "r12"
491 );
492}
493
494/* Write an 8-pixel block, defined by foreground and background pattern.
495 * Address is the byte in the first bitplane, mask determines which pixels to
496 * set, and bits determines if the pixel is foreground or background */
497static void _writeblock(unsigned char *address, unsigned mask, unsigned bits)
498{
499 unsigned long pat_stack[8];
500 register unsigned char *end_addr;
501 register unsigned long *pat_ptr = &pat_stack[8];
502
503 /* precalculate the bit patterns with random shifts (same RNG as _writepixel,
504 * see there for an explanation) for all 8 pixels and put them on an
505 * extra stack */
506 asm (
507 "mov #8,r3 \n" /* loop count in r3: 8 pixels */
508 "mov %6,r2 \n" /* copy mask */
509
510 ".wb_loop: \n" /** load pattern for pixel **/
511 "shlr r2 \n" /* shift out lsb of mask */
512 "bf .wb_skip \n" /* skip this pixel */
513
514 "mov %2,r4 \n" /* load foreground pattern */
515 "shlr %7 \n" /* shift out lsb of bits */
516 "bt .wb_fg \n" /* foreground? -> skip next insn */
517 "mov %3,r4 \n" /* load background pattern */
518 ".wb_fg: \n"
519
520 "mov #75,r0 \n"
521 "mulu r0,%0 \n" /* multiply by 75 */
522 "sts macl,%0 \n"
523 "add #74,%0 \n" /* add another 74 */
524 /* Since the lower bits are not very random: */
525 "swap.b %0,r1 \n" /* get bits 8..15 (need max. 5) */
526 "and %5,r1 \n" /* mask out unneeded bits */
527
528 "cmp/hs %4,r1 \n" /* random >= depth ? */
529 "bf .wb_ntrim \n"
530 "sub %4,r1 \n" /* yes: random -= depth; */
531 ".wb_ntrim: \n"
532
533 "mov.l .ashlsi3,r0 \n" /** rotate pattern **/
534 "jsr @r0 \n" /* r4 -> r0, shift left by r5 */
535 "mov r1,r5 \n"
536
537 "mov %4,r5 \n"
538 "sub r1,r5 \n" /* r5 = depth - r1 */
539 "mov.l .lshrsi3,r1 \n"
540 "jsr @r1 \n" /* r4 -> r0, shift right by r5 */
541 "mov r0,r1 \n" /* store previous result in r1 */
542
543 "bra .wb_store \n"
544 "or r1,r0 \n" /* rotated_pattern = r0 | r1 */
545
546 ".wb_skip: \n"
547 "shlr %7 \n" /* shift out lsb of bits to keep in sync */
548 "mov #0,r0 \n" /* pattern for skipped pixel must be 0 */
549
550 ".wb_store: \n"
551 "mov.l r0,@-%1 \n" /* push on pattern stack */
552
553 "add #-1,r3 \n" /* decrease loop count */
554 "cmp/pl r3 \n" /* loop count > 0? */
555 "bt .wb_loop \n" /* yes: loop */
556 : /* outputs */
557 /* %0, in & out */ "+r"(random_buffer),
558 /* %1, in & out */ "+r"(pat_ptr)
559 : /* inputs */
560 /* %2 */ "r"(graybuf->fg_pattern),
561 /* %3 */ "r"(graybuf->bg_pattern),
562 /* %4 */ "r"(graybuf->depth),
563 /* %5 */ "r"(graybuf->randmask),
564 /* %6 */ "r"(mask),
565 /* %7 */ "r"(bits)
566 : /* clobbers */
567 "r0", "r1", "r2", "r3", "r4", "r5", "macl"
568 );
569
570 end_addr = address + MULU16(graybuf->depth, graybuf->plane_size);
571
572 /* set the bits for all 8 pixels in all bytes according to the
573 * precalculated patterns on the pattern stack */
574 asm volatile (
575 "mov.l @%3+,r1 \n" /* pop all 8 patterns */
576 "mov.l @%3+,r2 \n"
577 "mov.l @%3+,r3 \n"
578 "mov.l @%3+,r8 \n"
579 "mov.l @%3+,r9 \n"
580 "mov.l @%3+,r10 \n"
581 "mov.l @%3+,r11 \n"
582 "mov.l @%3+,r12 \n"
583
584 "not %4,%4 \n" /* "set" mask -> "keep" mask */
585 "extu.b %4,%4 \n" /* mask out high bits */
586 "tst %4,%4 \n" /* nothing to keep? */
587 "bt .wb_sloop \n" /* yes: jump to short loop */
588
589 ".wb_floop: \n" /** full loop (there are bits to keep)**/
590 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
591 "rotcl r0 \n" /* rotate t bit into r0 */
592 "shlr r2 \n"
593 "rotcl r0 \n"
594 "shlr r3 \n"
595 "rotcl r0 \n"
596 "shlr r8 \n"
597 "rotcl r0 \n"
598 "shlr r9 \n"
599 "rotcl r0 \n"
600 "shlr r10 \n"
601 "rotcl r0 \n"
602 "shlr r11 \n"
603 "rotcl r0 \n"
604 "shlr r12 \n"
605 "mov.b @%0,%3 \n" /* read old value */
606 "rotcl r0 \n"
607 "and %4,%3 \n" /* mask out unneeded bits */
608 "or r0,%3 \n" /* set new bits */
609 "mov.b %3,@%0 \n" /* store value to bitplane */
610 "add %2,%0 \n" /* advance to next bitplane */
611 "cmp/hi %0,%1 \n" /* last bitplane done? */
612 "bt .wb_floop \n" /* no: loop */
613
614 "bra .wb_end \n"
615 "nop \n"
616
617 ".wb_sloop: \n" /** short loop (nothing to keep) **/
618 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
619 "rotcl r0 \n" /* rotate t bit into r0 */
620 "shlr r2 \n"
621 "rotcl r0 \n"
622 "shlr r3 \n"
623 "rotcl r0 \n"
624 "shlr r8 \n"
625 "rotcl r0 \n"
626 "shlr r9 \n"
627 "rotcl r0 \n"
628 "shlr r10 \n"
629 "rotcl r0 \n"
630 "shlr r11 \n"
631 "rotcl r0 \n"
632 "shlr r12 \n"
633 "rotcl r0 \n"
634 "mov.b r0,@%0 \n" /* store byte to bitplane */
635 "add %2,%0 \n" /* advance to next bitplane */
636 "cmp/hi %0,%1 \n" /* last bitplane done? */
637 "bt .wb_sloop \n" /* no: loop */
638
639 ".wb_end: \n"
640 : /* outputs */
641 : /* inputs */
642 /* %0 */ "r"(address),
643 /* %1 */ "r"(end_addr),
644 /* %2 */ "r"(graybuf->plane_size),
645 /* %3 */ "r"(pat_ptr),
646 /* %4 */ "r"(mask)
647 : /* clobbers */
648 "r0", "r1", "r2", "r3", "r8", "r9", "r10", "r11", "r12"
649 );
650}
651
652/* References to C library routines used in _writearray and _writeblock */
653asm (
654 ".align 2 \n"
655".ashlsi3: \n" /* C library routine: */
656 ".long ___ashlsi3 \n" /* shift r4 left by r5, return in r0 */
657".lshrsi3: \n" /* C library routine: */
658 ".long ___lshrsi3 \n" /* shift r4 right by r5, return in r0 */
659 /* both routines preserve r4, destroy r5 and take ~16 cycles */
660);
661
662/* Invert pixels within an 8-pixel block.
663 * Address is the byte in the first bitplane, mask and bits determine which
664 * pixels to invert ('And'ed together, for matching the parameters with
665 * _writeblock) */
666static void _invertblock(unsigned char *address, unsigned mask, unsigned bits)
667{
668 bits &= mask;
669 if (!bits)
670 return;
671
672 asm volatile (
673 "mov #0,r1 \n" /* current_plane = 0 */
674
675 ".im_loop: \n"
676 "mov.b @%1,r2 \n" /* get data byte */
677 "add #1,r1 \n" /* current_plane++; */
678 "xor %2,r2 \n" /* invert bits */
679 "mov.b r2,@%1 \n" /* store data byte */
680 "add %3,%1 \n" /* advance address to next bitplane */
681 "cmp/hi r1,%0 \n" /* current_plane < depth ? */
682 "bt .im_loop \n"
683 : /* outputs */
684 : /* inputs */
685 /* %0 */ "r"(graybuf->depth),
686 /* %1 */ "r"(address),
687 /* %2 */ "r"(bits),
688 /* %3 */ "r"(graybuf->plane_size)
689 : /* clobbers */
690 "r1", "r2", "macl"
691 );
692}
693
694/* Call _writeblock with the mask modified to draw foreground pixels only */
695static void _writeblockfg(unsigned char *address, unsigned mask, unsigned bits)
696{
697 mask &= bits;
698 if (mask)
699 _writeblock(address, mask, bits);
700}
701
702/* Call _writeblock with the mask modified to draw background pixels only */
703static void _writeblockbg(unsigned char *address, unsigned mask, unsigned bits)
704{
705 mask &= ~bits;
706 if (mask)
707 _writeblock(address, mask, bits);
708}
709
710/**** public functions ****/
711
712/* Initialize the framework
713 *
714 * Every framework needs such a function, and it has to be called as
715 * the very first one */
716void gray_init(struct plugin_api* newrb)
717{
718 rb = newrb;
719}
720
721/** General functions **/
722
723/* Prepare the grayscale display buffer
724 *
725 * arguments:
726 * gbuf = pointer to the memory area to use (e.g. plugin buffer)
727 * gbuf_size = max usable size of the buffer
728 * width = width in pixels (1..112)
729 * bheight = height in 8-pixel units (1..8)
730 * depth = desired number of shades - 1 (1..32)
731 *
732 * result:
733 * = depth if there was enough memory
734 * < depth if there wasn't enough memory. The number of displayable
735 * shades is smaller than desired, but it still works
736 * = 0 if there wasn't even enough memory for 1 bitplane (black & white)
737 *
738 * You can request any depth from 1 to 32, not just powers of 2. The routine
739 * performs "graceful degradation" if the memory is not sufficient for the
740 * desired depth. As long as there is at least enough memory for 1 bitplane,
741 * it creates as many bitplanes as fit into memory, although 1 bitplane will
742 * only deliver black & white display.
743 *
744 * If you need info about the memory taken by the grayscale buffer, supply an
745 * int* as the last parameter. This int will then contain the number of bytes
746 * used. The total memory needed can be calculated as follows:
747 * total_mem =
748 * sizeof(tGraybuf) (= 64 bytes currently)
749 * + sizeof(long) (= 4 bytes)
750 * + (width * bheight + sizeof(long)) * depth
751 * + 0..3 (longword alignment of grayscale display buffer)
752 */
753int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
754 int bheight, int depth, int *buf_taken)
755{
756 int possible_depth, plane_size;
757 int i, j, align;
758
759 if ((unsigned) width > LCD_WIDTH
760 || (unsigned) bheight > (LCD_HEIGHT/8)
761 || depth < 1)
762 return 0;
763
764 /* the buffer has to be long aligned */
765 align = 3 - (((unsigned long)gbuf + 3) & 3);
766 gbuf += align;
767 gbuf_size -= align;
768
769 plane_size = MULU16(width, bheight);
770 possible_depth = (gbuf_size - sizeof(tGraybuf) - sizeof(long))
771 / (plane_size + sizeof(long));
772
773 if (possible_depth < 1)
774 return 0;
775
776 depth = MIN(depth, 32);
777 depth = MIN(depth, possible_depth);
778
779 graybuf = (tGraybuf *) gbuf; /* global pointer to buffer structure */
780
781 graybuf->x = 0;
782 graybuf->by = 0;
783 graybuf->width = width;
784 graybuf->height = bheight << 3;
785 graybuf->bheight = bheight;
786 graybuf->plane_size = plane_size;
787 graybuf->depth = depth;
788 graybuf->cur_plane = 0;
789 graybuf->flags = 0;
790 graybuf->bitpattern = (unsigned long *) (gbuf + sizeof(tGraybuf));
791 graybuf->data = (unsigned char *) (graybuf->bitpattern + depth + 1);
792
793 i = depth - 1;
794 j = 8;
795 while (i != 0)
796 {
797 i >>= 1;
798 j--;
799 }
800 graybuf->randmask = 0xFFu >> j;
801
802 /* initial state is all white */
803 rb->memset(graybuf->data, 0, MULU16(depth, plane_size));
804
805 /* Precalculate the bit patterns for all possible pixel values */
806 for (i = 0; i <= depth; i++)
807 {
808 unsigned long pattern = 0;
809 int value = 0;
810
811 for (j = 0; j < depth; j++)
812 {
813 pattern <<= 1;
814 value += i;
815
816 if (value >= depth)
817 value -= depth; /* "white" bit */
818 else
819 pattern |= 1; /* "black" bit */
820 }
821 /* now the lower <depth> bits contain the pattern */
822
823 graybuf->bitpattern[i] = pattern;
824 }
825
826 graybuf->fg_pattern = graybuf->bitpattern[0]; /* black */
827 graybuf->bg_pattern = graybuf->bitpattern[depth]; /* white */
828 graybuf->drawmode = GRAY_DRAW_SOLID;
829 graybuf->curfont = FONT_SYSFIXED;
830
831 if (buf_taken) /* caller requested info about space taken */
832 {
833 *buf_taken = sizeof(tGraybuf) + sizeof(long)
834 + MULU16(plane_size + sizeof(long), depth) + align;
835 }
836
837 return depth;
838}
839
840/* Release the grayscale display buffer
841 *
842 * Switches the grayscale overlay off at first if it is still running,
843 * then sets the pointer to NULL.
844 * DO CALL either this function or at least gray_show_display(false)
845 * before you exit, otherwise nasty things may happen.
846 */
847void gray_release_buffer(void)
848{
849 gray_show_display(false);
850 graybuf = NULL;
851}
852
853/* Set position of the top left corner of the grayscale overlay
854 *
855 * x = left margin in pixels
856 * by = top margin in 8-pixel units
857 *
858 * You may set this in a way that the overlay spills across the right or
859 * bottom display border. In this case it will simply be clipped by the
860 * LCD controller. You can even set negative values, this will clip at the
861 * left or top border. I did not test it, but the limits may be +127 / -128
862 *
863 * If you use this while the grayscale overlay is running, the now-freed area
864 * will be restored.
865 */
866void gray_position_display(int x, int by)
867{
868 graybuf->x = x;
869 graybuf->by = by;
870
871 if (graybuf->flags & GRAY_RUNNING)
872 graybuf->flags |= GRAY_DEFERRED_UPDATE;
873}
874
875/* Switch the grayscale overlay on or off
876 *
877 * enable = true: the grayscale overlay is switched on if initialized
878 * = false: the grayscale overlay is switched off and the regular lcd
879 * content is restored
880 *
881 * DO NOT call lcd_update() or any other api function that directly accesses
882 * the lcd while the grayscale overlay is running! If you need to do
883 * lcd_update() to update something outside the grayscale overlay area, use
884 * gray_deferred_update() instead.
885 *
886 * Other functions to avoid are:
887 * lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(),
888 * lcd_set_invert_display(), lcd_set_flip(), lcd_roll()
889 *
890 * The grayscale display consumes ~50 % CPU power (for a full screen overlay,
891 * less if the overlay is smaller) when switched on. You can switch the overlay
892 * on and off as many times as you want.
893 */
894void gray_show_display(bool enable)
895{
896 if (enable)
897 {
898 graybuf->flags |= GRAY_RUNNING;
899 rb->plugin_register_timer(FREQ / 67, 1, _timer_isr);
900 }
901 else
902 {
903 rb->plugin_unregister_timer();
904 graybuf->flags &= ~GRAY_RUNNING;
905 rb->lcd_update(); /* restore whatever there was before */
906 }
907}
908
909/* Set the draw mode for subsequent drawing operations
910 *
911 * drawmode =
912 * GRAY_DRAW_INVERSE: Foreground pixels are inverted, background pixels are
913 * left untouched
914 * GRAY_DRAW_FG: Only foreground pixels are drawn
915 * GRAY_DRAW_BG: Only background pixels are drawn
916 * GRAY_DRAW_SOLID: Foreground and background pixels are drawn
917 */
918void gray_set_drawmode(int drawmode)
919{
920 if ((unsigned) drawmode <= GRAY_DRAW_SOLID)
921 graybuf->drawmode = drawmode;
922}
923
924/* Set the foreground shade for subsequent drawing operations
925 *
926 * brightness = 0 (black) .. 255 (white)
927 */
928void gray_set_foreground(int brightness)
929{
930 if ((unsigned) brightness <= 255)
931 graybuf->fg_pattern = graybuf->bitpattern[MULU16(brightness,
932 graybuf->depth + 1) >> 8];
933}
934
935/* Set the background shade for subsequent drawing operations
936 *
937 * brightness = 0 (black) .. 255 (white)
938 */
939void gray_set_background(int brightness)
940{
941 if ((unsigned) brightness <= 255)
942 graybuf->bg_pattern = graybuf->bitpattern[MULU16(brightness,
943 graybuf->depth + 1) >> 8];
944}
945
946/* Set draw mode, foreground and background shades at once
947 *
948 * If you hand it -1 (or in fact any other out-of-bounds value) for a
949 * parameter, that particular setting won't be changed
950 */
951void gray_set_drawinfo(int drawmode, int fg_brightness, int bg_brightness)
952{
953 gray_set_drawmode(drawmode);
954 gray_set_foreground(fg_brightness);
955 gray_set_background(bg_brightness);
956}
957
958/** Functions affecting the whole dsplay **/
959
960/* Clear the grayscale display (sets all pixels to white) */
961void gray_clear_display(void)
962{
963 rb->memset(graybuf->data, 0, MULU16(graybuf->depth, graybuf->plane_size));
964}
965
966/* Set the grayscale display to all black */
967void gray_black_display(void)
968{
969 rb->memset(graybuf->data, 0xFF, MULU16(graybuf->depth, graybuf->plane_size));
970}
971
972/* Do an lcd_update() to show changes done by rb->lcd_xxx() functions (in areas
973 * of the screen not covered by the grayscale overlay).
974 *
975 * If the grayscale overlay is running, the update will be done in the next
976 * call of the interrupt routine, otherwise it will be performed right away.
977 * See also comment for the gray_show_display() function.
978 */
979void gray_deferred_update(void)
980{
981 if (graybuf->flags & GRAY_RUNNING)
982 graybuf->flags |= GRAY_DEFERRED_UPDATE;
983 else
984 rb->lcd_update();
985}
986
987/** Scrolling functions **/
988
989/* Scroll the whole grayscale buffer left by <count> pixels
990 *
991 * black_border determines if the pixels scrolled in at the right are black
992 * or white
993 *
994 * Scrolling left/right by an even pixel count is almost twice as fast as
995 * scrolling by an odd pixel count.
996 */
997void gray_scroll_left(int count, bool black_border)
998{
999 int by, d;
1000 unsigned filler;
1001 unsigned char *ptr;
1002
1003 if ((unsigned) count >= (unsigned) graybuf->width)
1004 return;
1005
1006 filler = black_border ? 0xFF : 0;
1007
1008 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
1009 for (by = 0; by < graybuf->bheight; by++)
1010 {
1011 ptr = graybuf->data + MULU16(graybuf->width, by);
1012 for (d = 0; d < graybuf->depth; d++)
1013 {
1014 asm volatile (
1015 "mov %0,r1 \n" /* check if both source... */
1016 "or %2,r1 \n" /* ...and offset are even */
1017 "shlr r1 \n" /* -> lsb = 0 */
1018 "bf .sl_start2 \n" /* -> copy word-wise */
1019
1020 "add #-1,%2 \n" /* copy byte-wise */
1021 ".sl_loop1: \n"
1022 "mov.b @%0+,r1 \n"
1023 "mov.b r1,@(%2,%0) \n"
1024 "cmp/hi %0,%1 \n"
1025 "bt .sl_loop1 \n"
1026
1027 "bra .sl_end \n"
1028 "nop \n"
1029
1030 ".sl_start2: \n" /* copy word-wise */
1031 "add #-2,%2 \n"
1032 ".sl_loop2: \n"
1033 "mov.w @%0+,r1 \n"
1034 "mov.w r1,@(%2,%0) \n"
1035 "cmp/hi %0,%1 \n"
1036 "bt .sl_loop2 \n"
1037
1038 ".sl_end: \n"
1039 : /* outputs */
1040 : /* inputs */
1041 /* %0 */ "r"(ptr + count),
1042 /* %1 */ "r"(ptr + graybuf->width),
1043 /* %2 */ "z"(-count)
1044 : /* clobbers */
1045 "r1"
1046 );
1047
1048 rb->memset(ptr + graybuf->width - count, filler, count);
1049 ptr += graybuf->plane_size;
1050 }
1051 }
1052}
1053
1054/* Scroll the whole grayscale buffer right by <count> pixels
1055 *
1056 * black_border determines if the pixels scrolled in at the left are black
1057 * or white
1058 *
1059 * Scrolling left/right by an even pixel count is almost twice as fast as
1060 * scrolling by an odd pixel count.
1061 */
1062void gray_scroll_right(int count, bool black_border)
1063{
1064 int by, d;
1065 unsigned filler;
1066 unsigned char *ptr;
1067
1068 if ((unsigned) count >= (unsigned) graybuf->width)
1069 return;
1070
1071 filler = black_border ? 0xFF : 0;
1072
1073 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
1074 for (by = 0; by < graybuf->bheight; by++)
1075 {
1076 ptr = graybuf->data + MULU16(graybuf->width, by);
1077 for (d = 0; d < graybuf->depth; d++)
1078 {
1079 asm volatile (
1080 "mov %0,r1 \n" /* check if both source... */
1081 "or %2,r1 \n" /* ...and offset are even */
1082 "shlr r1 \n" /* -> lsb = 0 */
1083 "bf .sr_start2 \n" /* -> copy word-wise */
1084
1085 "add #-1,%2 \n" /* copy byte-wise */
1086 ".sr_loop1: \n"
1087 "mov.b @(%2,%0),r1 \n"
1088 "mov.b r1,@-%0 \n"
1089 "cmp/hi %1,%0 \n"
1090 "bt .sr_loop1 \n"
1091
1092 "bra .sr_end \n"
1093 "nop \n"
1094
1095 ".sr_start2: \n" /* copy word-wise */
1096 "add #-2,%2 \n"
1097 ".sr_loop2: \n"
1098 "mov.w @(%2,%0),r1 \n"
1099 "mov.w r1,@-%0 \n"
1100 "cmp/hi %1,%0 \n"
1101 "bt .sr_loop2 \n"
1102
1103 ".sr_end: \n"
1104 : /* outputs */
1105 : /* inputs */
1106 /* %0 */ "r"(ptr + graybuf->width),
1107 /* %1 */ "r"(ptr + count),
1108 /* %2 */ "z"(-count)
1109 : /* clobbers */
1110 "r1"
1111 );
1112
1113 rb->memset(ptr, filler, count);
1114 ptr += graybuf->plane_size;
1115 }
1116 }
1117}
1118
1119/* Scroll the whole grayscale buffer up by 8 pixels
1120 *
1121 * black_border determines if the pixels scrolled in at the bottom are black
1122 * or white
1123 *
1124 * Scrolling up/down by 8 pixels is very fast.
1125 */
1126void gray_scroll_up8(bool black_border)
1127{
1128 int by, d;
1129 unsigned filler;
1130 unsigned char *ptr;
1131
1132 filler = black_border ? 0xFF : 0;
1133
1134 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
1135 for (by = 1; by < graybuf->bheight; by++)
1136 {
1137 ptr = graybuf->data + MULU16(graybuf->width, by);
1138 for (d = 0; d < graybuf->depth; d++)
1139 {
1140 rb->memcpy(ptr - graybuf->width, ptr, graybuf->width);
1141 ptr += graybuf->plane_size;
1142 }
1143 }
1144 /* fill last row */
1145 ptr = graybuf->data + graybuf->plane_size - graybuf->width;
1146 for (d = 0; d < graybuf->depth; d++)
1147 {
1148 rb->memset(ptr, filler, graybuf->width);
1149 ptr += graybuf->plane_size;
1150 }
1151}
1152
1153/* Scroll the whole grayscale buffer down by 8 pixels
1154 *
1155 * black_border determines if the pixels scrolled in at the top are black
1156 * or white
1157 *
1158 * Scrolling up/down by 8 pixels is very fast.
1159 */
1160void gray_scroll_down8(bool black_border)
1161{
1162 int by, d;
1163 unsigned filler;
1164 unsigned char *ptr;
1165
1166 filler = black_border ? 0xFF : 0;
1167
1168 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
1169 for (by = graybuf->bheight - 1; by > 0; by--)
1170 {
1171 ptr = graybuf->data + MULU16(graybuf->width, by);
1172 for (d = 0; d < graybuf->depth; d++)
1173 {
1174 rb->memcpy(ptr, ptr - graybuf->width, graybuf->width);
1175 ptr += graybuf->plane_size;
1176 }
1177 }
1178 /* fill first row */
1179 ptr = graybuf->data;
1180 for (d = 0; d < graybuf->depth; d++)
1181 {
1182 rb->memset(ptr, filler, graybuf->width);
1183 ptr += graybuf->plane_size;
1184 }
1185}
1186
1187/* Scroll the whole grayscale buffer up by <count> pixels (<= 7)
1188 *
1189 * black_border determines if the pixels scrolled in at the bottom are black
1190 * or white
1191 *
1192 * Scrolling up/down pixel-wise is significantly slower than scrolling
1193 * left/right or scrolling up/down byte-wise because it involves bit
1194 * shifting. That's why it is asm optimized.
1195 */
1196void gray_scroll_up(int count, bool black_border)
1197{
1198 unsigned filler;
1199
1200 if ((unsigned) count > 7)
1201 return;
1202
1203 filler = black_border ? 0xFFu : 0;
1204
1205 /* scroll column by column to minimize flicker */
1206 asm volatile (
1207 "mov #0,r6 \n" /* x = 0 */
1208 "mova .su_shifttbl,r0 \n" /* calculate jump destination for */
1209 "mov.b @(r0,%6),%6 \n" /* shift amount from table */
1210 "bra .su_cloop \n" /* skip table */
1211 "add r0,%6 \n"
1212
1213 ".align 2 \n"
1214 ".su_shifttbl: \n" /* shift jump offset table */
1215 ".byte .su_shift0 - .su_shifttbl \n"
1216 ".byte .su_shift1 - .su_shifttbl \n"
1217 ".byte .su_shift2 - .su_shifttbl \n"
1218 ".byte .su_shift3 - .su_shifttbl \n"
1219 ".byte .su_shift4 - .su_shifttbl \n"
1220 ".byte .su_shift5 - .su_shifttbl \n"
1221 ".byte .su_shift6 - .su_shifttbl \n"
1222 ".byte .su_shift7 - .su_shifttbl \n"
1223
1224 ".su_cloop: \n" /* repeat for every column */
1225 "mov %1,r2 \n" /* get start address */
1226 "mov #0,r3 \n" /* current_plane = 0 */
1227
1228 ".su_oloop: \n" /* repeat for every bitplane */
1229 "mov r2,r4 \n" /* get start address */
1230 "mov #0,r5 \n" /* current_row = 0 */
1231 "mov %5,r1 \n" /* get filler bits */
1232
1233 ".su_iloop: \n" /* repeat for all rows */
1234 "sub %2,r4 \n" /* address -= width */
1235 "mov.b @r4,r0 \n" /* get data byte */
1236 "shll8 r1 \n" /* old data to 2nd byte */
1237 "extu.b r0,r0 \n" /* extend unsigned */
1238 "or r1,r0 \n" /* combine old data */
1239 "jmp @%6 \n" /* jump into shift "path" */
1240 "extu.b r0,r1 \n" /* store data for next round */
1241
1242 ".su_shift6: \n" /* shift right by 0..7 bits */
1243 "shlr2 r0 \n"
1244 ".su_shift4: \n"
1245 "shlr2 r0 \n"
1246 ".su_shift2: \n"
1247 "bra .su_shift0 \n"
1248 "shlr2 r0 \n"
1249 ".su_shift7: \n"
1250 "shlr2 r0 \n"
1251 ".su_shift5: \n"
1252 "shlr2 r0 \n"
1253 ".su_shift3: \n"
1254 "shlr2 r0 \n"
1255 ".su_shift1: \n"
1256 "shlr r0 \n"
1257 ".su_shift0: \n"
1258
1259 "mov.b r0,@r4 \n" /* store data */
1260 "add #1,r5 \n" /* current_row++ */
1261 "cmp/hi r5,%3 \n" /* current_row < bheight ? */
1262 "bt .su_iloop \n"
1263
1264 "add %4,r2 \n" /* start_address += plane_size */
1265 "add #1,r3 \n" /* current_plane++ */
1266 "cmp/hi r3,%0 \n" /* current_plane < depth ? */
1267 "bt .su_oloop \n"
1268
1269 "add #1,%1 \n" /* start_address++ */
1270 "add #1,r6 \n" /* x++ */
1271 "cmp/hi r6,%2 \n" /* x < width ? */
1272 "bt .su_cloop \n"
1273 : /* outputs */
1274 : /* inputs */
1275 /* %0 */ "r"(graybuf->depth),
1276 /* %1 */ "r"(graybuf->data + graybuf->plane_size),
1277 /* %2 */ "r"(graybuf->width),
1278 /* %3 */ "r"(graybuf->bheight),
1279 /* %4 */ "r"(graybuf->plane_size),
1280 /* %5 */ "r"(filler),
1281 /* %6 */ "r"(count)
1282 : /* clobbers */
1283 "r0", "r1", "r2", "r3", "r4", "r5", "r6"
1284 );
1285}
1286
1287/* Scroll the whole grayscale buffer down by <count> pixels (<= 7)
1288 *
1289 * black_border determines if the pixels scrolled in at the top are black
1290 * or white
1291 *
1292 * Scrolling up/down pixel-wise is significantly slower than scrolling
1293 * left/right or scrolling up/down byte-wise because it involves bit
1294 * shifting. That's why it is asm optimized.
1295 */
1296void gray_scroll_down(int count, bool black_border)
1297{
1298 unsigned filler;
1299
1300 if ((unsigned) count > 7)
1301 return;
1302
1303 filler = black_border ? (0xFFu << count) : 0;
1304
1305 /* scroll column by column to minimize flicker */
1306 asm volatile (
1307 "mov #0,r6 \n" /* x = 0 */
1308 "mova .sd_shifttbl,r0 \n" /* calculate jump destination for */
1309 "mov.b @(r0,%6),%6 \n" /* shift amount from table */
1310 "bra .sd_cloop \n" /* skip table */
1311 "add r0,%6 \n"
1312
1313 ".align 2 \n"
1314 ".sd_shifttbl: \n" /* shift jump offset table */
1315 ".byte .sd_shift0 - .sd_shifttbl \n"
1316 ".byte .sd_shift1 - .sd_shifttbl \n"
1317 ".byte .sd_shift2 - .sd_shifttbl \n"
1318 ".byte .sd_shift3 - .sd_shifttbl \n"
1319 ".byte .sd_shift4 - .sd_shifttbl \n"
1320 ".byte .sd_shift5 - .sd_shifttbl \n"
1321 ".byte .sd_shift6 - .sd_shifttbl \n"
1322 ".byte .sd_shift7 - .sd_shifttbl \n"
1323
1324 ".sd_cloop: \n" /* repeat for every column */
1325 "mov %1,r2 \n" /* get start address */
1326 "mov #0,r3 \n" /* current_plane = 0 */
1327
1328 ".sd_oloop: \n" /* repeat for every bitplane */
1329 "mov r2,r4 \n" /* get start address */
1330 "mov #0,r5 \n" /* current_row = 0 */
1331 "mov %5,r1 \n" /* get filler bits */
1332
1333 ".sd_iloop: \n" /* repeat for all rows */
1334 "shlr8 r1 \n" /* shift right to get residue */
1335 "mov.b @r4,r0 \n" /* get data byte */
1336 "jmp @%6 \n" /* jump into shift "path" */
1337 "extu.b r0,r0 \n" /* extend unsigned */
1338
1339 ".sd_shift6: \n" /* shift left by 0..7 bits */
1340 "shll2 r0 \n"
1341 ".sd_shift4: \n"
1342 "shll2 r0 \n"
1343 ".sd_shift2: \n"
1344 "bra .sd_shift0 \n"
1345 "shll2 r0 \n"
1346 ".sd_shift7: \n"
1347 "shll2 r0 \n"
1348 ".sd_shift5: \n"
1349 "shll2 r0 \n"
1350 ".sd_shift3: \n"
1351 "shll2 r0 \n"
1352 ".sd_shift1: \n"
1353 "shll r0 \n"
1354 ".sd_shift0: \n"
1355
1356 "or r0,r1 \n" /* combine with last residue */
1357 "mov.b r1,@r4 \n" /* store data */
1358 "add %2,r4 \n" /* address += width */
1359 "add #1,r5 \n" /* current_row++ */
1360 "cmp/hi r5,%3 \n" /* current_row < bheight ? */
1361 "bt .sd_iloop \n"
1362
1363 "add %4,r2 \n" /* start_address += plane_size */
1364 "add #1,r3 \n" /* current_plane++ */
1365 "cmp/hi r3,%0 \n" /* current_plane < depth ? */
1366 "bt .sd_oloop \n"
1367
1368 "add #1,%1 \n" /* start_address++ */
1369 "add #1,r6 \n" /* x++ */
1370 "cmp/hi r6,%2 \n" /* x < width ? */
1371 "bt .sd_cloop \n"
1372 : /* outputs */
1373 : /* inputs */
1374 /* %0 */ "r"(graybuf->depth),
1375 /* %1 */ "r"(graybuf->data),
1376 /* %2 */ "r"(graybuf->width),
1377 /* %3 */ "r"(graybuf->bheight),
1378 /* %4 */ "r"(graybuf->plane_size),
1379 /* %5 */ "r"(filler),
1380 /* %6 */ "r"(count)
1381 : /* clobbers */
1382 "r0", "r1", "r2", "r3", "r4", "r5", "r6"
1383 );
1384}
1385
1386/** Pixel and line functions **/
1387
1388/* Set a pixel with the current drawinfo
1389 *
1390 * If the drawmode is GRAY_DRAW_INVERSE, the pixel is inverted
1391 * GRAY_DRAW_FG and GRAY_DRAW_SOLID draw the pixel in the foreground shade
1392 * GRAY_DRAW_BG draws the pixel in the background shade
1393 */
1394void gray_drawpixel(int x, int y)
1395{
1396 unsigned long pattern;
1397
1398 if ((unsigned) x >= (unsigned) graybuf->width
1399 || (unsigned) y >= (unsigned) graybuf->height)
1400 return;
1401
1402 pattern = (graybuf->drawmode == GRAY_DRAW_BG) ?
1403 graybuf->bg_pattern : graybuf->fg_pattern;
1404
1405 _pixelfuncs[graybuf->drawmode](x, y, pattern);
1406}
1407
1408/* Draw a line from (x1, y1) to (x2, y2) with the current drawinfo,
1409 * See gray_drawpixel() for details
1410 */
1411void gray_drawline(int x1, int y1, int x2, int y2)
1412{
1413 int numpixels;
1414 int i;
1415 int deltax, deltay;
1416 int d, dinc1, dinc2;
1417 int x, xinc1, xinc2;
1418 int y, yinc1, yinc2;
1419 unsigned long pattern;
1420 void (*pixelfunc)(int x, int y, unsigned long pattern);
1421
1422 if ((unsigned) x1 >= (unsigned) graybuf->width
1423 || (unsigned) y1 >= (unsigned) graybuf->height
1424 || (unsigned) x2 >= (unsigned) graybuf->width
1425 || (unsigned) y2 >= (unsigned) graybuf->height)
1426 return;
1427
1428 deltax = abs(x2 - x1);
1429 deltay = abs(y2 - y1);
1430 xinc2 = 1;
1431 yinc2 = 1;
1432
1433 if (deltax >= deltay)
1434 {
1435 numpixels = deltax;
1436 d = 2 * deltay - deltax;
1437 dinc1 = deltay * 2;
1438 dinc2 = (deltay - deltax) * 2;
1439 xinc1 = 1;
1440 yinc1 = 0;
1441 }
1442 else
1443 {
1444 numpixels = deltay;
1445 d = 2 * deltax - deltay;
1446 dinc1 = deltax * 2;
1447 dinc2 = (deltax - deltay) * 2;
1448 xinc1 = 0;
1449 yinc1 = 1;
1450 }
1451 numpixels++; /* include endpoints */
1452
1453 if (x1 > x2)
1454 {
1455 xinc1 = -xinc1;
1456 xinc2 = -xinc2;
1457 }
1458
1459 if (y1 > y2)
1460 {
1461 yinc1 = -yinc1;
1462 yinc2 = -yinc2;
1463 }
1464
1465 x = x1;
1466 y = y1;
1467
1468 pixelfunc = _pixelfuncs[graybuf->drawmode];
1469 pattern = (graybuf->drawmode == GRAY_DRAW_BG) ?
1470 graybuf->bg_pattern : graybuf->fg_pattern;
1471
1472 for (i = 0; i < numpixels; i++)
1473 {
1474 pixelfunc(x, y, pattern);
1475
1476 if (d < 0)
1477 {
1478 d += dinc1;
1479 x += xinc1;
1480 y += yinc1;
1481 }
1482 else
1483 {
1484 d += dinc2;
1485 x += xinc2;
1486 y += yinc2;
1487 }
1488 }
1489}
1490
1491/* Draw a horizontal line from (x1, y) to (x2, y) with the current drawinfo,
1492 * See gray_drawpixel() for details
1493 */
1494void gray_horline(int x1, int x2, int y)
1495{
1496 int x;
1497 unsigned long pattern;
1498 void (*pixelfunc)(int x, int y, unsigned long pattern);
1499
1500 if ((unsigned) x1 >= (unsigned) graybuf->width
1501 || (unsigned) x2 >= (unsigned) graybuf->width
1502 || (unsigned) y >= (unsigned) graybuf->height)
1503 return;
1504
1505 if (x1 > x2)
1506 {
1507 x = x1;
1508 x1 = x2;
1509 x2 = x;
1510 }
1511 pixelfunc = _pixelfuncs[graybuf->drawmode];
1512 pattern = (graybuf->drawmode == GRAY_DRAW_BG) ?
1513 graybuf->bg_pattern : graybuf->fg_pattern;
1514
1515 for (x = x1; x <= x2; x++)
1516 pixelfunc(x, y, pattern);
1517
1518}
1519
1520/* Draw a vertical line from (x, y1) to (x, y2) with the current drawinfo,
1521 * See gray_drawpixel() for details
1522 *
1523 * This one uses the block drawing optimization, so it is rather fast.
1524 */
1525void gray_verline(int x, int y1, int y2)
1526{
1527 int shift, y, ny;
1528 unsigned bits, mask_top, mask_bottom;
1529 unsigned char *dst;
1530 void (*blockfunc)(unsigned char *address, unsigned mask, unsigned bits);
1531
1532 if ((unsigned) x >= (unsigned) graybuf->width
1533 || (unsigned) y1 >= (unsigned) graybuf->height
1534 || (unsigned) y2 >= (unsigned) graybuf->height)
1535 return;
1536
1537 if (y1 > y2)
1538 {
1539 y = y1;
1540 y1 = y2;
1541 y2 = y;
1542 }
1543 y = y1;
1544 ny = y2 - y1 + 1;
1545
1546 dst = graybuf->data + x + MULU16(graybuf->width, y >> 3);
1547 shift = y & 7;
1548 ny += shift;
1549
1550 mask_top = 0xFFu << (y & 7);
1551 mask_bottom = ~(0xFEu << ((ny - 1) & 7));
1552 if (ny <= 8)
1553 mask_bottom &= mask_top;
1554
1555 blockfunc = _blockfuncs[graybuf->drawmode];
1556 bits = (graybuf->drawmode == GRAY_DRAW_BG) ? 0u : 0xFFu;
1557
1558 if (ny > 8)
1559 {
1560 blockfunc(dst, mask_top, bits);
1561 dst += graybuf->width;
1562
1563 for (y = 8; y < ny - 8; y += 8)
1564 {
1565 blockfunc(dst, 0xFFu, bits);
1566 dst += graybuf->width;
1567 }
1568 }
1569
1570 blockfunc(dst, mask_bottom, bits);
1571}
1572
1573/** Rectangle functions **/
1574
1575/* Draw a (hollow) rectangle with the current drawinfo,
1576 * See gray_drawpixel() for details
1577 */
1578void gray_drawrect(int x, int y, int nx, int ny)
1579{
1580 int x2, y2;
1581
1582 if ((unsigned) x >= (unsigned) graybuf->width
1583 || (unsigned) y >= (unsigned) graybuf->height)
1584 return;
1585
1586 if ((unsigned) (y + ny) >= (unsigned) graybuf->height) /* clip bottom */
1587 ny = graybuf->height - y;
1588
1589 if ((unsigned) (x + nx) >= (unsigned) graybuf->width) /* clip right */
1590 nx = graybuf->width - x;
1591
1592 x2 = x + nx - 1;
1593 y2 = y + ny - 1;
1594
1595 gray_horline(x, x2, y);
1596 gray_horline(x, x2, y2);
1597 gray_verline(x, y, y2);
1598 gray_verline(x2, y, y2);
1599}
1600
1601/* Draw a filled rectangle with the current drawinfo,
1602 * See gray_drawpixel() for details
1603 *
1604 * This one uses the block drawing optimization, so it is rather fast.
1605 */
1606void gray_fillrect(int x, int y, int nx, int ny)
1607{
1608 int shift;
1609 unsigned bits, mask_top, mask_bottom;
1610 unsigned char *dst, *dst_row;
1611 void (*blockfunc)(unsigned char *address, unsigned mask, unsigned bits);
1612
1613 if ((unsigned) x >= (unsigned) graybuf->width
1614 || (unsigned) y >= (unsigned) graybuf->height)
1615 return;
1616
1617 if ((unsigned) (y + ny) >= (unsigned) graybuf->height) /* clip bottom */
1618 ny = graybuf->height - y;
1619
1620 if ((unsigned) (x + nx) >= (unsigned) graybuf->width) /* clip right */
1621 nx = graybuf->width - x;
1622
1623 dst = graybuf->data + x + MULU16(graybuf->width, y >> 3);
1624 shift = y & 7;
1625 ny += shift;
1626
1627 mask_top = 0xFFu << (y & 7);
1628 mask_bottom = ~(0xFEu << ((ny - 1) & 7));
1629 if (ny <= 8)
1630 mask_bottom &= mask_top;
1631
1632 blockfunc = _blockfuncs[graybuf->drawmode];
1633 bits = (graybuf->drawmode == GRAY_DRAW_BG) ? 0u : 0xFFu;
1634
1635 if (ny > 8)
1636 {
1637 dst_row = dst;
1638 for (x = 0; x < nx; x++)
1639 blockfunc(dst_row++, mask_top, bits);
1640
1641 dst += graybuf->width;
1642
1643 for (y = 8; y < ny - 8; y += 8)
1644 {
1645 dst_row = dst;
1646 for (x = 0; x < nx; x++)
1647 blockfunc(dst_row++, 0xFFu, bits);
1648
1649 dst += graybuf->width;
1650 }
1651 }
1652
1653 for (x = 0; x < nx; x++)
1654 blockfunc(dst++, mask_bottom, bits);
1655}
1656
1657/** Bitmap functions **/
1658
1659/* Copy a grayscale bitmap into the display
1660 *
1661 * A grayscale bitmap contains one byte for every pixel that defines the
1662 * brightness of the pixel (0..255). Bytes are read in row-major order.
1663 * The <stride> parameter is useful if you want to show only a part of a
1664 * bitmap. It should always be set to the "row length" of the bitmap, so
1665 * for displaying the whole bitmap, nx == stride.
1666 *
1667 * This is the only drawing function NOT using the drawinfo.
1668 */
1669void gray_drawgraymap(unsigned char *src, int x, int y, int nx, int ny,
1670 int stride)
1671{
1672 int shift;
1673 unsigned mask_top, mask_bottom;
1674 unsigned char *src_row, *dst, *dst_row;
1675
1676 if ((unsigned) x >= (unsigned) graybuf->width
1677 || (unsigned) y >= (unsigned) graybuf->height)
1678 return;
1679
1680 if ((unsigned) (y + ny) >= (unsigned) graybuf->height) /* clip bottom */
1681 ny = graybuf->height - y;
1682
1683 if ((unsigned) (x + nx) >= (unsigned) graybuf->width) /* clip right */
1684 nx = graybuf->width - x;
1685
1686 dst = graybuf->data + x + MULU16(graybuf->width, y >> 3);
1687 shift = y & 7;
1688 ny += shift;
1689
1690 mask_top = 0xFFu << (y & 7);
1691 mask_bottom = ~(0xFEu << ((ny - 1) & 7));
1692 if (ny <= 8)
1693 mask_bottom &= mask_top;
1694
1695 if (ny > 8)
1696 {
1697 src_row = src;
1698 dst_row = dst;
1699
1700 for (x = 0; x < nx; x++)
1701 _writearray(dst_row++, src_row++, stride, mask_top);
1702
1703 src += MULU16(stride, 8 - shift);
1704 dst += graybuf->width;
1705
1706 for (y = 8; y < ny - 8; y += 8)
1707 {
1708 src_row = src;
1709 dst_row = dst;
1710
1711 for (x = 0; x < nx; x++)
1712 _writearray(dst_row++, src_row++, stride, 0xFFu);
1713
1714 src += stride << 3;
1715 dst += graybuf->width;
1716 }
1717 }
1718
1719 for (x = 0; x < nx; x++)
1720 _writearray(dst++, src++, stride, mask_bottom);
1721}
1722
1723/* Display a bitmap with the current drawinfo
1724 *
1725 * The drawmode is used as described for gray_set_drawmode()
1726 *
1727 * This (now) uses the same bitmap format as the core b&w graphics routines,
1728 * so you can use bmp2rb to generate bitmaps for use with this function as
1729 * well.
1730 *
1731 * A bitmap contains one bit for every pixel that defines if that pixel is
1732 * foreground (1) or background (0). Bits within a byte are arranged
1733 * vertically, LSB at top.
1734 * The bytes are stored in row-major order, with byte 0 being top left,
1735 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
1736 * 0..7, the second row defines pixel row 8..15 etc.
1737 *
1738 * The <stride> parameter is useful if you want to show only a part of a
1739 * bitmap. It should always be set to the "row length" of the bitmap.
1740 */
1741void gray_drawbitmap(unsigned char *src, int x, int y, int nx, int ny,
1742 int stride)
1743{
1744 int shift;
1745 unsigned bits, mask_top, mask_bottom;
1746 unsigned char *src_col, *dst, *dst_col;
1747 void (*blockfunc)(unsigned char *address, unsigned mask, unsigned bits);
1748
1749 if ((unsigned) x >= (unsigned) graybuf->width
1750 || (unsigned) y >= (unsigned) graybuf->height)
1751 return;
1752
1753 if ((unsigned) (y + ny) >= (unsigned) graybuf->height) /* clip bottom */
1754 ny = graybuf->height - y;
1755
1756 if ((unsigned) (x + nx) >= (unsigned) graybuf->width) /* clip right */
1757 nx = graybuf->width - x;
1758
1759 dst = graybuf->data + x + MULU16(graybuf->width, y >> 3);
1760 shift = y & 7;
1761 ny += shift;
1762
1763 mask_top = 0xFFu << (y & 7);
1764 mask_bottom = ~(0xFEu << ((ny - 1) & 7));
1765 if (ny <= 8)
1766 mask_bottom &= mask_top;
1767
1768 blockfunc = _blockfuncs[graybuf->drawmode];
1769
1770 for(x = 0; x < nx; x++)
1771 {
1772 src_col = src++;
1773 dst_col = dst++;
1774 bits = 0;
1775 y = 0;
1776
1777 if (ny > 8)
1778 {
1779 bits = *src_col << shift;
1780 src_col += stride;
1781
1782 blockfunc(dst_col, mask_top, bits);
1783 dst_col += graybuf->width;
1784 bits >>= 8;
1785
1786 for (y = 8; y < ny - 8; y += 8)
1787 {
1788 bits |= *src_col << shift;
1789 src_col += stride;
1790
1791 blockfunc(dst_col, 0xFFu, bits);
1792 dst_col += graybuf->width;
1793 bits >>= 8;
1794 }
1795 }
1796 if (y + shift < ny)
1797 bits |= *src_col << shift;
1798
1799 blockfunc(dst_col, mask_bottom, bits);
1800 }
1801}
1802
1803/** Font support **/
1804
1805/* Set font for the font routines
1806 *
1807 * newfont can be FONT_SYSFIXED or FONT_UI the same way as with the Rockbox
1808 * core routines
1809 */
1810void gray_setfont(int newfont)
1811{
1812 graybuf->curfont = rb->font_get(newfont);
1813}
1814
1815/* Calculate width and height of the given text in pixels when rendered with
1816 * the currently selected font.
1817 *
1818 * This works exactly the same way as the core lcd_getstringsize(), only that
1819 * it uses the selected font for grayscale.
1820 */
1821int gray_getstringsize(unsigned char *str, int *w, int *h)
1822{
1823 int ch;
1824 int width = 0;
1825 struct font *pf = graybuf->curfont;
1826
1827 while ((ch = *str++))
1828 {
1829 /* check input range */
1830 if (ch < pf->firstchar || ch >= pf->firstchar + pf->size)
1831 ch = pf->defaultchar;
1832 ch -= pf->firstchar;
1833
1834 /* get proportional width */
1835 width += pf->width ? pf->width[ch] : pf->maxwidth;
1836 }
1837 if (w)
1838 *w = width;
1839 if (h)
1840 *h = pf->height;
1841 return width;
1842}
1843
1844/* Display text starting at (x, y) with the current font and drawinfo
1845 *
1846 * The drawmode is used as described for gray_set_drawmode()
1847 */
1848void gray_putsxy(int x, int y, unsigned char *str)
1849{
1850 int ch, width;
1851 bitmap_t *bits;
1852 struct font *pf = graybuf->curfont;
1853
1854 if ((unsigned) x >= (unsigned) graybuf->width
1855 || (unsigned) y >= (unsigned) graybuf->height)
1856 return;
1857
1858 while ((ch = *str++) != '\0' && x < graybuf->width)
1859 {
1860 /* check input range */
1861 if (ch < pf->firstchar || ch >= pf->firstchar + pf->size)
1862 ch = pf->defaultchar;
1863 ch -= pf->firstchar;
1864
1865 /* get proportional width and glyph bits */
1866 width = pf->width ? pf->width[ch] : pf->maxwidth;
1867 bits = pf->bits + (pf->offset ? pf->offset[ch]
1868 : MULU16(pf->height, ch));
1869
1870 gray_drawbitmap((unsigned char*) bits, x, y, width, pf->height,
1871 width);
1872 x += width;
1873 }
1874}
1875
1876#endif // #ifdef HAVE_LCD_BITMAP
1877#endif // #ifndef SIMULATOR
1878