/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Additional LCD routines not present in the rockbox core * Scrolling functions * * Copyright (C) 2005 Jens Arnold * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #ifdef HAVE_LCD_BITMAP #include "xlcd.h" #if (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) static const unsigned short patterns[4] = {0xFFFF, 0xFF00, 0x00FF, 0x0000}; #endif #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING) && (LCD_DEPTH < 8) /* Scroll left */ void xlcd_scroll_left(int count) { int bitcount, oldmode; int blockcount, blocklen; if ((unsigned) count >= LCD_WIDTH) return; #if LCD_DEPTH == 2 blockcount = count >> 2; blocklen = LCD_FBWIDTH - blockcount; bitcount = 2 * (count & 3); #endif if (blockcount) { unsigned char *data = rb->lcd_framebuffer; unsigned char *data_end = data + LCD_FBWIDTH*LCD_HEIGHT; do { rb->memmove(data, data + blockcount, blocklen); data += LCD_FBWIDTH; } while (data < data_end); } if (bitcount) { int bx, y; unsigned char *addr = rb->lcd_framebuffer + blocklen; #if LCD_DEPTH == 2 unsigned fill = 0x55 * (~rb->lcd_get_background() & 3); #endif for (y = 0; y < LCD_HEIGHT; y++) { unsigned char *row_addr = addr; unsigned data = fill; for (bx = 0; bx < blocklen; bx++) { --row_addr; data = (data >> 8) | (*row_addr << bitcount); *row_addr = data; } addr += LCD_FBWIDTH; } } oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(LCD_WIDTH - count, 0, count, LCD_HEIGHT); rb->lcd_set_drawmode(oldmode); } /* Scroll right */ void xlcd_scroll_right(int count) { int bitcount, oldmode; int blockcount, blocklen; if ((unsigned) count >= LCD_WIDTH) return; #if LCD_DEPTH == 2 blockcount = count >> 2; blocklen = LCD_FBWIDTH - blockcount; bitcount = 2 * (count & 3); #endif if (blockcount) { unsigned char *data = rb->lcd_framebuffer; unsigned char *data_end = data + LCD_FBWIDTH*LCD_HEIGHT; do { rb->memmove(data + blockcount, data, blocklen); data += LCD_FBWIDTH; } while (data < data_end); } if (bitcount) { int bx, y; unsigned char *addr = rb->lcd_framebuffer + blockcount; #if LCD_DEPTH == 2 unsigned fill = 0x55 * (~rb->lcd_get_background() & 3); #endif for (y = 0; y < LCD_HEIGHT; y++) { unsigned char *row_addr = addr; unsigned data = fill; for (bx = 0; bx < blocklen; bx++) { data = (data << 8) | *row_addr; *row_addr = data >> bitcount; row_addr++; } addr += LCD_FBWIDTH; } } oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(0, 0, count, LCD_HEIGHT); rb->lcd_set_drawmode(oldmode); } #else /* LCD_PIXELFORMAT vertical packed or >= 8bit / pixel */ /* Scroll left */ void xlcd_scroll_left(int count) { fb_data *data, *data_end; int length, oldmode; if ((unsigned)count >= LCD_WIDTH) return; data = rb->lcd_framebuffer; data_end = data + LCD_WIDTH*LCD_FBHEIGHT; length = LCD_WIDTH - count; do { rb->memmove(data, data + count, length * sizeof(fb_data)); data += LCD_WIDTH; } while (data < data_end); oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(length, 0, count, LCD_HEIGHT); rb->lcd_set_drawmode(oldmode); } /* Scroll right */ void xlcd_scroll_right(int count) { fb_data *data, *data_end; int length, oldmode; if ((unsigned)count >= LCD_WIDTH) return; data = rb->lcd_framebuffer; data_end = data + LCD_WIDTH*LCD_FBHEIGHT; length = LCD_WIDTH - count; do { rb->memmove(data + count, data, length * sizeof(fb_data)); data += LCD_WIDTH; } while (data < data_end); oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(0, 0, count, LCD_HEIGHT); rb->lcd_set_drawmode(oldmode); } #endif /* LCD_PIXELFORMAT, LCD_DEPTH */ #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING) || (LCD_DEPTH >= 8) /* Scroll up */ void xlcd_scroll_up(int count) { int length, oldmode; if ((unsigned)count >= LCD_HEIGHT) return; length = LCD_HEIGHT - count; rb->memmove(rb->lcd_framebuffer, rb->lcd_framebuffer + count * LCD_FBWIDTH, length * LCD_FBWIDTH * sizeof(fb_data)); oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(0, length, LCD_WIDTH, count); rb->lcd_set_drawmode(oldmode); } /* Scroll down */ void xlcd_scroll_down(int count) { int length, oldmode; if ((unsigned)count >= LCD_HEIGHT) return; length = LCD_HEIGHT - count; rb->memmove(rb->lcd_framebuffer + count * LCD_FBWIDTH, rb->lcd_framebuffer, length * LCD_FBWIDTH * sizeof(fb_data)); oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(0, 0, LCD_WIDTH, count); rb->lcd_set_drawmode(oldmode); } #else /* LCD_PIXELFORMAT == VERTICAL_PACKING, LCD_PIXELFORMAT == VERTICAL_INTERLEAVED */ /* Scroll up */ void xlcd_scroll_up(int count) { int bitcount, oldmode; int blockcount, blocklen; if ((unsigned) count >= LCD_HEIGHT) return; #if (LCD_DEPTH == 1) \ || (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) blockcount = count >> 3; bitcount = count & 7; #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING) blockcount = count >> 2; bitcount = 2 * (count & 3); #endif blocklen = LCD_FBHEIGHT - blockcount; if (blockcount) { rb->memmove(rb->lcd_framebuffer, rb->lcd_framebuffer + blockcount * LCD_FBWIDTH, blocklen * LCD_FBWIDTH * sizeof(fb_data)); } if (bitcount) { #if LCD_PIXELFORMAT == VERTICAL_PACKING #if (CONFIG_CPU == SH7034) && (LCD_DEPTH == 1) asm ( "mov #0,r4 \n" /* x = 0 */ "mova .su_shifttbl,r0 \n" /* calculate jump destination for */ "mov.b @(r0,%[cnt]),%[cnt] \n" /* shift amount from table */ "bra .su_cloop \n" /* skip table */ "add r0,%[cnt] \n" ".align 2 \n" ".su_shifttbl: \n" /* shift jump offset table */ ".byte .su_shift0 - .su_shifttbl \n" ".byte .su_shift1 - .su_shifttbl \n" ".byte .su_shift2 - .su_shifttbl \n" ".byte .su_shift3 - .su_shifttbl \n" ".byte .su_shift4 - .su_shifttbl \n" ".byte .su_shift5 - .su_shifttbl \n" ".byte .su_shift6 - .su_shifttbl \n" ".byte .su_shift7 - .su_shifttbl \n" ".su_cloop: \n" /* repeat for every column */ "mov %[addr],r2 \n" /* get start address */ "mov #0,r3 \n" /* current_row = 0 */ "mov #0,r1 \n" /* fill with zero */ ".su_iloop: \n" /* repeat for all rows */ "sub %[wide],r2 \n" /* address -= width */ "mov.b @r2,r0 \n" /* get data byte */ "shll8 r1 \n" /* old data to 2nd byte */ "extu.b r0,r0 \n" /* extend unsigned */ "or r1,r0 \n" /* combine old data */ "jmp @%[cnt] \n" /* jump into shift "path" */ "extu.b r0,r1 \n" /* store data for next round */ ".su_shift6: \n" /* shift right by 0..7 bits */ "shll2 r0 \n" "bra .su_shift0 \n" "shlr8 r0 \n" ".su_shift4: \n" "shlr2 r0 \n" ".su_shift2: \n" "bra .su_shift0 \n" "shlr2 r0 \n" ".su_shift7: \n" "shlr2 r0 \n" ".su_shift5: \n" "shlr2 r0 \n" ".su_shift3: \n" "shlr2 r0 \n" ".su_shift1: \n" "shlr r0 \n" ".su_shift0: \n" "mov.b r0,@r2 \n" /* store data */ "add #1,r3 \n" /* current_row++ */ "cmp/hi r3,%[rows] \n" /* current_row < bheight - shift ? */ "bt .su_iloop \n" "add #1,%[addr] \n" /* start_address++ */ "add #1,r4 \n" /* x++ */ "cmp/hi r4,%[wide] \n" /* x < width ? */ "bt .su_cloop \n" : /* outputs */ : /* inputs */ [addr]"r"(rb->lcd_framebuffer + blocklen * LCD_FBWIDTH), [wide]"r"(LCD_FBWIDTH), [rows]"r"(blocklen), [cnt] "r"(bitcount) : /* clobbers */ "r0", "r1", "r2", "r3", "r4" ); #elif defined(CPU_COLDFIRE) && (LCD_DEPTH == 2) asm ( "move.l %[wide],%%d3\n" /* columns = width */ ".su_cloop: \n" /* repeat for every column */ "move.l %[addr],%%a1\n" /* get start address */ "move.l %[rows],%%d2\n" /* rows = row_count */ "move.l %[bkg],%%d1 \n" /* fill with background */ ".su_iloop: \n" /* repeat for all rows */ "sub.l %[wide],%%a1\n" /* address -= width */ "clr.l %%d0 \n" "move.b (%%a1),%%d0 \n" /* get data byte */ "lsl.l #8,%%d1 \n" /* old data to 2nd byte */ "or.l %%d1,%%d0 \n" /* combine old data */ "clr.l %%d1 \n" "move.b %%d0,%%d1 \n" /* keep data for next round */ "lsr.l %[cnt],%%d0 \n" /* shift right */ "move.b %%d0,(%%a1) \n" /* store data */ "subq.l #1,%%d2 \n" /* rows-- */ "bne.b .su_iloop \n" "addq.l #1,%[addr] \n" /* start_address++ */ "subq.l #1,%%d3 \n" /* columns-- */ "bne.b .su_cloop \n" : /* outputs */ : /* inputs */ [wide]"r"(LCD_FBWIDTH), [rows]"r"(blocklen), [addr]"a"(rb->lcd_framebuffer + blocklen * LCD_FBWIDTH), [cnt] "d"(bitcount), [bkg] "d"(0x55 * (~rb->lcd_get_background() & 3)) : /* clobbers */ "a1", "d0", "d1", "d2", "d3" ); #else /* C version */ int x, by; unsigned char *addr = rb->lcd_framebuffer + blocklen * LCD_FBWIDTH; #if LCD_DEPTH == 2 unsigned fill = 0x55 * (~rb->lcd_get_background() & 3); #else const unsigned fill = 0; #endif for (x = 0; x < LCD_WIDTH; x++) { unsigned char *col_addr = addr++; unsigned data = fill; for (by = 0; by < blocklen; by++) { col_addr -= LCD_FBWIDTH; data = (data << 8) | *col_addr; *col_addr = data >> bitcount; } } #endif /* CPU, LCD_DEPTH */ #elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED #if LCD_DEPTH == 2 int x, by; fb_data *addr = rb->lcd_framebuffer + blocklen * LCD_FBWIDTH; unsigned fill, mask; fill = patterns[rb->lcd_get_background() & 3] << 8; mask = (0xFFu >> bitcount) << bitcount; mask |= mask << 8; for (x = 0; x < LCD_WIDTH; x++) { fb_data *col_addr = addr++; unsigned olddata = fill; unsigned data; for (by = 0; by < blocklen; by++) { col_addr -= LCD_FBWIDTH; data = *col_addr; *col_addr = (olddata ^ ((data ^ olddata) & mask)) >> bitcount; olddata = data << 8; } } #endif /* LCD_DEPTH == 2 */ #endif /* LCD_PIXELFORMAT */ } oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(0, LCD_HEIGHT - count, LCD_WIDTH, count); rb->lcd_set_drawmode(oldmode); } /* Scroll up */ void xlcd_scroll_down(int count) { int bitcount, oldmode; int blockcount, blocklen; if ((unsigned) count >= LCD_HEIGHT) return; #if (LCD_DEPTH == 1) \ || (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) blockcount = count >> 3; bitcount = count & 7; #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING) blockcount = count >> 2; bitcount = 2 * (count & 3); #endif blocklen = LCD_FBHEIGHT - blockcount; if (blockcount) { rb->memmove(rb->lcd_framebuffer + blockcount * LCD_FBWIDTH, rb->lcd_framebuffer, blocklen * LCD_FBWIDTH * sizeof(fb_data)); } if (bitcount) { #if LCD_PIXELFORMAT == VERTICAL_PACKING #if (CONFIG_CPU == SH7034) && (LCD_DEPTH == 1) asm ( "mov #0,r4 \n" /* x = 0 */ "mova .sd_shifttbl,r0 \n" /* calculate jump destination for */ "mov.b @(r0,%[cnt]),%[cnt] \n" /* shift amount from table */ "bra .sd_cloop \n" /* skip table */ "add r0,%[cnt] \n" ".align 2 \n" ".sd_shifttbl: \n" /* shift jump offset table */ ".byte .sd_shift0 - .sd_shifttbl \n" ".byte .sd_shift1 - .sd_shifttbl \n" ".byte .sd_shift2 - .sd_shifttbl \n" ".byte .sd_shift3 - .sd_shifttbl \n" ".byte .sd_shift4 - .sd_shifttbl \n" ".byte .sd_shift5 - .sd_shifttbl \n" ".byte .sd_shift6 - .sd_shifttbl \n" ".byte .sd_shift7 - .sd_shifttbl \n" ".sd_cloop: \n" /* repeat for every column */ "mov %[addr],r2 \n" /* get start address */ "mov #0,r3 \n" /* current_row = 0 */ "mov #0,r1 \n" /* fill with zero */ ".sd_iloop: \n" /* repeat for all rows */ "shlr8 r1 \n" /* shift right to get residue */ "mov.b @r2,r0 \n" /* get data byte */ "jmp @%[cnt] \n" /* jump into shift "path" */ "extu.b r0,r0 \n" /* extend unsigned */ ".sd_shift6: \n" /* shift left by 0..7 bits */ "shll8 r0 \n" "bra .sd_shift0 \n" "shlr2 r0 \n" ".sd_shift4: \n" "shll2 r0 \n" ".sd_shift2: \n" "bra .sd_shift0 \n" "shll2 r0 \n" ".sd_shift7: \n" "shll2 r0 \n" ".sd_shift5: \n" "shll2 r0 \n" ".sd_shift3: \n" "shll2 r0 \n" ".sd_shift1: \n" "shll r0 \n" ".sd_shift0: \n" "or r0,r1 \n" /* combine with last residue */ "mov.b r1,@r2 \n" /* store data */ "add %[wide],r2 \n" /* address += width */ "add #1,r3 \n" /* current_row++ */ "cmp/hi r3,%[rows] \n" /* current_row < bheight - shift ? */ "bt .sd_iloop \n" "add #1,%[addr] \n" /* start_address++ */ "add #1,r4 \n" /* x++ */ "cmp/hi r4,%[wide] \n" /* x < width ? */ "bt .sd_cloop \n" : /* outputs */ : /* inputs */ [addr]"r"(rb->lcd_framebuffer + blockcount * LCD_FBWIDTH), [wide]"r"(LCD_WIDTH), [rows]"r"(blocklen), [cnt] "r"(bitcount) : /* clobbers */ "r0", "r1", "r2", "r3", "r4" ); #elif defined(CPU_COLDFIRE) && (LCD_DEPTH == 2) asm ( "move.l %[wide],%%d3\n" /* columns = width */ ".sd_cloop: \n" /* repeat for every column */ "move.l %[addr],%%a1\n" /* get start address */ "move.l %[rows],%%d2\n" /* rows = row_count */ "move.l %[bkg],%%d1 \n" /* fill with background */ ".sd_iloop: \n" /* repeat for all rows */ "lsr.l #8,%%d1 \n" /* shift right to get residue */ "clr.l %%d0 \n" "move.b (%%a1),%%d0 \n" /* get data byte */ "lsl.l %[cnt],%%d0 \n" "or.l %%d0,%%d1 \n" /* combine with last residue */ "move.b %%d1,(%%a1) \n" /* store data */ "add.l %[wide],%%a1\n" /* address += width */ "subq.l #1,%%d2 \n" /* rows-- */ "bne.b .sd_iloop \n" "lea.l (1,%[addr]),%[addr] \n" /* start_address++ */ "subq.l #1,%%d3 \n" /* columns-- */ "bne.b .sd_cloop \n" : /* outputs */ : /* inputs */ [wide]"r"(LCD_WIDTH), [rows]"r"(blocklen), [addr]"a"(rb->lcd_framebuffer + blockcount * LCD_FBWIDTH), [cnt] "d"(bitcount), [bkg] "d"((0x55 * (~rb->lcd_get_background() & 3)) << bitcount) : /* clobbers */ "a1", "d0", "d1", "d2", "d3" ); #else /* C version */ int x, by; unsigned char *addr = rb->lcd_framebuffer + blockcount * LCD_FBWIDTH; #if LCD_DEPTH == 2 unsigned fill = (0x55 * (~rb->lcd_get_background() & 3)) << bitcount; #else const unsigned fill = 0; #endif for (x = 0; x < LCD_WIDTH; x++) { unsigned char *col_addr = addr++; unsigned data = fill; for (by = 0; by < blocklen; by++) { data = (data >> 8) | (*col_addr << bitcount); *col_addr = data; col_addr += LCD_FBWIDTH; } } #endif /* CPU, LCD_DEPTH */ #elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED #if LCD_DEPTH == 2 int x, by; fb_data *addr = rb->lcd_framebuffer + blockcount * LCD_FBWIDTH; unsigned fill, mask; fill = patterns[rb->lcd_get_background() & 3] >> (8 - bitcount); mask = (0xFFu >> bitcount) << bitcount; mask |= mask << 8; for (x = 0; x < LCD_WIDTH; x++) { fb_data *col_addr = addr++; unsigned olddata = fill; unsigned data; for (by = 0; by < blocklen; by++) { data = *col_addr << bitcount; *col_addr = olddata ^ ((data ^ olddata) & mask); olddata = data >> 8; col_addr += LCD_FBWIDTH; } } #endif /* LCD_DEPTH == 2 */ #endif /* LCD_PIXELFORMAT */ } oldmode = rb->lcd_get_drawmode(); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(0, 0, LCD_WIDTH, count); rb->lcd_set_drawmode(oldmode); } #endif /* LCD_PIXELFORMAT, LCD_DEPTH */ #endif /* HAVE_LCD_BITMAP */