From feb5b15e9bf9292e3d4d82ea1e01ab3557fb1240 Mon Sep 17 00:00:00 2001 From: Jens Arnold Date: Fri, 4 Jan 2008 23:42:38 +0000 Subject: All-new greyscale library, replacing the old one. Features: (1) Drawing/updating is faster than the old grayscale lib at full depth. (2) Always 129 shades instead of 2..33 shades. (3) No graininess caused by frequent updates (mpegplayer, doom, ...). (4) Needs less memory than the old grayscale lib at full depth. * The tradeoff is slightly higher CPU load in the ISR (frames are calculated 'live') and an extra function in the core. * Ported all plugins which used the graylib to use the new one. * Some slight optimisations for archos and H1x0 LCD update. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15998 a1c6a512-1295-4272-9138-f99709370657 --- firmware/export/lcd.h | 11 +- firmware/target/arm/ipod/lcd-gray.c | 117 ++++++++++ firmware/target/coldfire/iaudio/m5/lcd-as-m5.S | 93 ++++++-- firmware/target/coldfire/iaudio/m5/lcd-m5.c | 15 ++ firmware/target/coldfire/iriver/h100/lcd-as-h100.S | 120 +++++++--- firmware/target/coldfire/iriver/h100/lcd-h100.c | 15 ++ firmware/target/sh/archos/lcd-archos-bitmap.c | 19 +- firmware/target/sh/archos/lcd-as-archos-bitmap.S | 259 ++++++++++++++++----- 8 files changed, 545 insertions(+), 104 deletions(-) (limited to 'firmware') diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index de03222441..60d9efaf92 100644 --- a/firmware/export/lcd.h +++ b/firmware/export/lcd.h @@ -95,15 +95,24 @@ extern void lcd_puts_scroll(int x, int y, const unsigned char* string); extern void lcd_puts_scroll_style(int x, int y, const unsigned char* string, int style); +#ifdef HAVE_LCD_BITMAP + #if defined(HAVE_LCD_COLOR) #define LCD_YUV_DITHER 0x1 extern void lcd_yuv_set_options(unsigned options); extern void lcd_yuv_blit(unsigned char * const src[3], int src_x, int src_y, int stride, int x, int y, int width, int height); +#else +struct grey_data { + unsigned char phase; /* SH1 uses it signed (doesn't matter for high level) */ + unsigned char value; /* 0..128 are allowed */ +} __attribute__((packed)); +extern void lcd_grey_data(const struct grey_data *data, int count); /* private */ +extern void lcd_grey_phase_blit(const struct grey_data *data, int bx, int by, + int bwidth, int bheight, int stride); #endif -#ifdef HAVE_LCD_BITMAP /* performance function */ extern void lcd_blit(const fb_data* data, int x, int by, int width, int bheight, int stride); diff --git a/firmware/target/arm/ipod/lcd-gray.c b/firmware/target/arm/ipod/lcd-gray.c index 65fa2a779e..c7f4074c0b 100644 --- a/firmware/target/arm/ipod/lcd-gray.c +++ b/firmware/target/arm/ipod/lcd-gray.c @@ -299,6 +299,123 @@ void lcd_blit(const unsigned char* data, int bx, int y, int bwidth, } } +/* Performance function that works with an external buffer + note that bx and bwidth are in 8-pixel units! */ +void lcd_grey_phase_blit(const struct grey_data *data, int bx, int y, + int bwidth, int height, int stride) +{ + const struct grey_data *addr; + int width; + + while (height--) { + lcd_cmd_and_data(R_RAM_ADDR_SET, (y++ << 5) + addr_offset - bx); + lcd_prepare_cmd(R_RAM_DATA); + + addr = data; + width = bwidth; + asm volatile ( + "10: \n" + "ldmia %[addr]!, {r0-r3} \n" /* r0 = v1p1v0p0 ... */ +#ifdef IPOD_MINI2G + "mov r5, #0x7600 \n" +#else + "mov r5, #0 \n" +#endif + + "and r4, r0, %[mask] \n" /* r4 = --p1--p0 */ + "and r0, %[mask], r0, lsr #8 \n" /* r0 = --v1--v0 */ + + "tst r4, #0x80 \n" + "orreq r5, r5, #0xc0 \n" + "tst r4, #0x800000 \n" + "orreq r5, r5, #0x30 \n" + "bic r4, r4, %[clbt] \n" + + "add r4, r0, r4 \n" /* p0 += v0; p1 += v1; */ + "strb r4, [%[addr], #-16] \n" + "mov r4, r4, lsr #16 \n" + "strb r4, [%[addr], #-14] \n" + + "and r4, r1, %[mask] \n" + "and r1, %[mask], r1, lsr #8 \n" + + "tst r4, #0x80 \n" + "orreq r5, r5, #0x0c \n" + "tst r4, #0x800000 \n" + "orreq r5, r5, #0x03 \n" + "bic r4, r4, %[clbt] \n" + + "add r4, r1, r4 \n" + "strb r4, [%[addr], #-12] \n" + "mov r4, r4, lsr #16 \n" + "strb r4, [%[addr], #-10] \n" + +#ifdef IPOD_MINI2G + "mov r5, r5, lsl #8 \n" +#else + "1: \n" + "ldr r4, [%[lcdb]] \n" + "tst r4, #0x8000 \n" + "bne 1b \n" + + "str r5, [%[lcdb], #0x10] \n" + "mov r5, #0 \n" +#endif + + "and r4, r2, %[mask] \n" + "and r2, %[mask], r2, lsr #8 \n" + + "tst r4, #0x80 \n" + "orreq r5, r5, #0xc0 \n" + "tst r4, #0x800000 \n" + "orreq r5, r5, #0x30 \n" + "bic r4, r4, %[clbt] \n" + + "add r4, r2, r4 \n" + "strb r4, [%[addr], #-8] \n" + "mov r4, r4, lsr #16 \n" + "strb r4, [%[addr], #-6] \n" + + "and r4, r3, %[mask] \n" + "and r3, %[mask], r3, lsr #8 \n" + + "tst r4, #0x80 \n" + "orreq r5, r5, #0x0c \n" + "tst r4, #0x800000 \n" + "orreq r5, r5, #0x03 \n" + "bic r4, r4, %[clbt] \n" + + "add r4, r3, r4 \n" + "strb r4, [%[addr], #-4] \n" + "mov r4, r4, lsr #16 \n" + "strb r4, [%[addr], #-2] \n" + + "1: \n" + "ldr r4, [%[lcdb]] \n" + "tst r4, #0x8000 \n" + "bne 1b \n" +#ifdef IPOD_MINI2G + "str r5, [%[lcdb], #0x08] \n" +#else + "str r5, [%[lcdb], #0x10] \n" +#endif + + "subs %[wdth], %[wdth], #1 \n" + "bne 10b \n" + : /* outputs */ + [addr]"+r"(addr), + [wdth]"+r"(width) + : /* inputs */ + [mask]"r"(0x00ff00ff), + [clbt]"r"(0x00800080), + [lcdb]"r"(LCD1_BASE) + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", "r5" + ); + data += stride; + } +} + void lcd_update_rect(int x, int y, int width, int height) { int xmax, ymax; diff --git a/firmware/target/coldfire/iaudio/m5/lcd-as-m5.S b/firmware/target/coldfire/iaudio/m5/lcd-as-m5.S index 4a88dc92b0..7e89815ec8 100644 --- a/firmware/target/coldfire/iaudio/m5/lcd-as-m5.S +++ b/firmware/target/coldfire/iaudio/m5/lcd-as-m5.S @@ -41,18 +41,18 @@ lcd_write_command: lcd_write_command_ex: lea.l 0xf0008000, %a0 - move.l (4, %sp), %d0 /* Command */ - move.w %d0, (%a0)+ /* Write to LCD, set A0 = 1 */ + move.l (4, %sp), %d0 /* Command */ + move.w %d0, (%a0)+ /* Write to LCD, set A0 = 1 */ - move.l (8, %sp), %d0 /* Data */ - cmp.l #-1, %d0 /* -1? */ + move.l (8, %sp), %d0 /* Data */ + cmp.l #-1, %d0 /* -1? */ beq.b .last - move.w %d0, (%a0) /* Write to LCD */ + move.w %d0, (%a0) /* Write to LCD */ - move.l (12, %sp), %d0 /* Data */ - cmp.l #-1, %d0 /* -1? */ + move.l (12, %sp), %d0 /* Data */ + cmp.l #-1, %d0 /* -1? */ beq.b .last - move.w %d0, (%a0) /* Write to LCD */ + move.w %d0, (%a0) /* Write to LCD */ .last: rts @@ -65,19 +65,80 @@ lcd_write_command_ex: .type lcd_write_data,@function lcd_write_data: - move.l (4,%sp), %a0 /* Data pointer */ - move.l (8,%sp), %d0 /* Length */ - + movem.l (4, %sp), %a0-%a1 /* Data pointer */ + move.l %a1, %d0 /* Length */ lea 0xf0008002, %a1 + .loop: /* When running in IRAM, this loop takes 10 cycles plus the LCD write. The 10 cycles are necessary to follow the LCD timing specs at 140MHz */ - nop /* 3(0/0) */ - move.b (%a0)+, %d1 /* 3(1/0) */ - move.w %d1, (%a1) /* 1(0/1) */ - subq.l #1, %d0 /* 1(0/0) */ - bne .loop /* 2(0/0) */ + nop /* 3(0/0) */ + move.b (%a0)+, %d1 /* 3(1/0) */ + move.w %d1, (%a1) /* 1(0/1) */ + subq.l #1, %d0 /* 1(0/0) */ + bne .loop /* 2(0/0) */ rts .wd_end: .size lcd_write_data,.wd_end-lcd_write_data + + + .align 2 + .global lcd_grey_data + .type lcd_grey_data,@function + +lcd_grey_data: + lea.l (-4*4, %sp), %sp + movem.l %d2-%d5, (%sp) + movem.l (4*4+4, %sp), %a0-%a1 /* Data pointer */ + move.l %a1, %d0 /* Length */ + lea 0xf0008002, %a1 /* LCD data port */ + move.l #0xff00ff00, %d2 /* mask for splitting value/phase pairs */ + +.greyloop: + movem.l (%a0), %d4-%d5 /* fetch 4 pixel phase/value pairs at once */ + /* %d4 = p0v0p1v1, %d5 = p2v2p3v3 */ + move.l %d2, %d3 /* copy mask */ + and.l %d4, %d3 /* %d3 = p0--p1-- */ + eor.l %d3, %d4 /* %d4 = --v0--v1 */ + lsr.l #8, %d3 /* %d3 = --p0--p1 */ + + bclr.l #23, %d3 /* Z = !(p0 & 0x80); p0 &= ~0x80; */ + seq.b %d1 /* %d1 = ........................00000000 */ + lsl.l #2, %d1 /* %d1 = ......................00000000.. */ + bclr.l #7, %d3 /* Z = !(p1 & 0x80); p1 &= ~0x80; */ + seq.b %d1 /* %d1 = ......................0011111111 */ + lsl.l #2, %d1 /* %d1 = ....................0011111111.. */ + + add.l %d4, %d3 /* p0 += v0; p1 += v1; */ + move.b %d3, (2, %a0) /* store p1 */ + swap %d3 + move.b %d3, (%a0) /* store p0 */ + + move.l %d2, %d3 /* copy mask */ + and.l %d5, %d3 /* %d3 = p2--p3-- */ + eor.l %d3, %d5 /* %d5 = --v2--v3 */ + lsr.l #8, %d3 /* %d3 = --p2--p3 */ + + bclr.l #23, %d3 /* Z = !(p2 & 0x80); p2 &= ~0x80; */ + seq.b %d1 /* %d1 = ....................001122222222 */ + lsl.l #2, %d1 /* %d1 = ..................001122222222.. */ + bclr.l #7, %d3 /* Z = !(p3 & 0x80); p3 &= ~0x80; */ + seq.b %d1 /* %d1 = ..................00112233333333 */ + lsr.l #6, %d1 /* %d1 = ........................00112233 */ + + add.l %d5, %d3 /* p2 += v2; p3 += v3; */ + move.b %d3, (6, %a0) /* store p3 */ + swap %d3 + move.b %d3, (4, %a0) /* store p2 */ + + move.w %d1, (%a1) /* write pixel block */ + addq.l #8, %a0 /* advance address pointer */ + subq.l #1, %d0 /* any blocks left? */ + bne.b .greyloop + + movem.l (%sp), %d2-%d5 + lea.l (4*4, %sp), %sp + rts +.gd_end: + .size lcd_grey_data,.gd_end-lcd_grey_data diff --git a/firmware/target/coldfire/iaudio/m5/lcd-m5.c b/firmware/target/coldfire/iaudio/m5/lcd-m5.c index 2af46b3145..4f963795c7 100644 --- a/firmware/target/coldfire/iaudio/m5/lcd-m5.c +++ b/firmware/target/coldfire/iaudio/m5/lcd-m5.c @@ -171,6 +171,21 @@ void lcd_blit(const unsigned char* data, int x, int by, int width, } } +/* Performance function that works with an external buffer + note that by and bheight are in 4-pixel units! */ +void lcd_grey_phase_blit(const struct grey_data *data, int x, int by, + int width, int bheight, int stride) +{ + stride <<= 2; /* 4 pixels per block */ + while (bheight--) + { + lcd_write_command_ex(LCD_CNTL_PAGE, by++, -1); + lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1); + lcd_write_command(LCD_CNTL_DATA_WRITE); + lcd_grey_data(data, width); + data += stride; + } +} /* Update the display. This must be called after all other LCD functions that change the display. */ diff --git a/firmware/target/coldfire/iriver/h100/lcd-as-h100.S b/firmware/target/coldfire/iriver/h100/lcd-as-h100.S index c7509871fc..df410fa379 100644 --- a/firmware/target/coldfire/iriver/h100/lcd-as-h100.S +++ b/firmware/target/coldfire/iriver/h100/lcd-as-h100.S @@ -28,11 +28,10 @@ .type lcd_write_command,@function lcd_write_command: - move.l (4,%sp),%d0 - lea MBAR2,%a1 - move.l #~8,%d1 - and.l %d1,(0xb4,%a1) - move.w %d0,0xf0000000 + move.l #~8, %d1 + and.l %d1, (MBAR2+0xb4) + move.l (4, %sp), %d0 + move.w %d0, 0xf0000000 rts .wc_end: .size lcd_write_command,.wc_end-lcd_write_command @@ -43,26 +42,27 @@ lcd_write_command: .type lcd_write_command_ex,@function lcd_write_command_ex: - lea MBAR2,%a1 + lea.l 0xf0000000, %a0 + lea.l MBAR2+0xb4, %a1 - move.l (4,%sp),%d0 /* Command */ + move.l #~8, %d1 /* Set A0 = 0 */ + and.l %d1, (%a1) - move.l #~8,%d1 /* Set A0 = 0 */ - and.l %d1,(0xb4,%a1) - move.w %d0,0xf0000000 /* Write to LCD */ + move.l (4, %sp), %d0 /* Command */ + move.w %d0, (%a0) /* Write to LCD */ - not.l %d1 /* Set A0 = 1 */ - or.l %d1,(0xb4,%a1) + not.l %d1 /* Set A0 = 1 */ + or.l %d1, (%a1) - move.l (8,%sp),%d0 /* Data */ - cmp.l #0xffffffff,%d0 /* -1? */ + move.l (8, %sp), %d0 /* Data */ + cmp.l #-1, %d0 /* -1? */ beq.b .last - move.w %d0,0xf0000000 /* Write to LCD */ + move.w %d0, (%a0) /* Write to LCD */ - move.l (12,%sp),%d0 /* Data */ - cmp.l #0xffffffff,%d0 /* -1? */ + move.l (12, %sp), %d0 /* Data */ + cmp.l #-1, %d0 /* -1? */ beq.b .last - move.w %d0,0xf0000000 /* Write to LCD */ + move.w %d0, (%a0) /* Write to LCD */ .last: rts @@ -75,22 +75,84 @@ lcd_write_command_ex: .type lcd_write_data,@function lcd_write_data: - move.l (4,%sp),%a0 /* Data pointer */ - move.l (8,%sp),%d0 /* Length */ - lea MBAR2,%a1 - moveq #8,%d1 - or.l %d1,(0xb4,%a1) + movem.l (4, %sp), %a0-%a1 /* Data pointer */ + move.l %a1, %d0 /* Length */ + moveq #8, %d1 + or.l %d1, (MBAR2+0xb4) + lea.l 0xf0000000, %a1 - lea 0xf0000000,%a1 .loop: /* When running in IRAM, this loop takes 10 cycles plus the LCD write. The 10 cycles are necessary to follow the LCD timing specs at 140MHz */ - nop /* 3(0/0) */ - move.b (%a0)+,%d1 /* 3(1/0) */ - move.w %d1,(%a1) /* 1(0/1) */ - subq.l #1,%d0 /* 1(0/0) */ - bne .loop /* 2(0/0) */ + nop /* 3(0/0) */ + move.b (%a0)+, %d1 /* 3(1/0) */ + move.w %d1, (%a1) /* 1(0/1) */ + subq.l #1, %d0 /* 1(0/0) */ + bne .loop /* 2(0/0) */ rts .wd_end: .size lcd_write_data,.wd_end-lcd_write_data + + + .align 2 + .global lcd_grey_data + .type lcd_grey_data,@function + +lcd_grey_data: + lea.l (-4*4, %sp), %sp + movem.l %d2-%d5, (%sp) + movem.l (4*4+4, %sp), %a0-%a1 /* Data pointer */ + move.l %a1, %d0 /* Length */ + moveq #8, %d1 + or.l %d1, (MBAR2+0xb4) /* A0 = 1 (data) */ + lea 0xf0000000, %a1 /* LCD data port */ + move.l #0xff00ff00, %d2 /* mask for splitting value/phase pairs */ + +.greyloop: + movem.l (%a0), %d4-%d5 /* fetch 4 pixel phase/value pairs at once */ + /* %d4 = p0v0p1v1, %d5 = p2v2p3v3 */ + move.l %d2, %d3 /* copy mask */ + and.l %d4, %d3 /* %d3 = p0--p1-- */ + eor.l %d3, %d4 /* %d4 = --v0--v1 */ + lsr.l #8, %d3 /* %d3 = --p0--p1 */ + + bclr.l #23, %d3 /* Z = !(p0 & 0x80); p0 &= ~0x80; */ + seq.b %d1 /* %d1 = ........................00000000 */ + lsl.l #2, %d1 /* %d1 = ......................00000000.. */ + bclr.l #7, %d3 /* Z = !(p1 & 0x80); p1 &= ~0x80; */ + seq.b %d1 /* %d1 = ......................0011111111 */ + lsl.l #2, %d1 /* %d1 = ....................0011111111.. */ + + add.l %d4, %d3 /* p0 += v0; p1 += v1; */ + move.b %d3, (2, %a0) /* store p1 */ + swap %d3 + move.b %d3, (%a0) /* store p0 */ + + move.l %d2, %d3 /* copy mask */ + and.l %d5, %d3 /* %d3 = p2--p3-- */ + eor.l %d3, %d5 /* %d5 = --v2--v3 */ + lsr.l #8, %d3 /* %d3 = --p2--p3 */ + + bclr.l #23, %d3 /* Z = !(p2 & 0x80); p2 &= ~0x80; */ + seq.b %d1 /* %d1 = ....................001122222222 */ + lsl.l #2, %d1 /* %d1 = ..................001122222222.. */ + bclr.l #7, %d3 /* Z = !(p3 & 0x80); p3 &= ~0x80; */ + seq.b %d1 /* %d1 = ..................00112233333333 */ + lsr.l #6, %d1 /* %d1 = ........................00112233 */ + + add.l %d5, %d3 /* p2 += v2; p3 += v3; */ + move.b %d3, (6, %a0) /* store p3 */ + swap %d3 + move.b %d3, (4, %a0) /* store p2 */ + + move.w %d1, (%a1) /* write pixel block */ + addq.l #8, %a0 /* advance address pointer */ + subq.l #1, %d0 /* any blocks left? */ + bne.b .greyloop + + movem.l (%sp), %d2-%d5 + lea.l (4*4, %sp), %sp + rts +.gd_end: + .size lcd_grey_data,.gd_end-lcd_grey_data diff --git a/firmware/target/coldfire/iriver/h100/lcd-h100.c b/firmware/target/coldfire/iriver/h100/lcd-h100.c index a721273384..c17de952c3 100644 --- a/firmware/target/coldfire/iriver/h100/lcd-h100.c +++ b/firmware/target/coldfire/iriver/h100/lcd-h100.c @@ -180,6 +180,21 @@ void lcd_blit(const unsigned char* data, int x, int by, int width, } } +/* Performance function that works with an external buffer + note that by and bheight are in 4-pixel units! */ +void lcd_grey_phase_blit(const struct grey_data *data, int x, int by, + int width, int bheight, int stride) +{ + stride <<= 2; /* 4 pixels per block */ + while (bheight--) + { + lcd_write_command_ex(LCD_CNTL_PAGE, by++, -1); + lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1); + lcd_write_command(LCD_CNTL_DATA_WRITE); + lcd_grey_data(data, width); + data += stride; + } +} /* Update the display. This must be called after all other LCD functions that change the display. */ diff --git a/firmware/target/sh/archos/lcd-archos-bitmap.c b/firmware/target/sh/archos/lcd-archos-bitmap.c index 28600a9769..17c4d76092 100644 --- a/firmware/target/sh/archos/lcd-archos-bitmap.c +++ b/firmware/target/sh/archos/lcd-archos-bitmap.c @@ -155,10 +155,26 @@ void lcd_blit(const unsigned char* data, int x, int by, int width, } } +/* Performance function that works with an external buffer + note that by and bheight are in 8-pixel units! */ +void lcd_grey_phase_blit(const struct grey_data *data, int x, int by, + int width, int bheight, int stride) +{ + stride <<= 3; /* 8 pixels per block */ + while (bheight--) + { + lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); + lcd_write_command (LCD_CNTL_HIGHCOL | (((x+xoffset)>>4) & 0xf)); + lcd_write_command (LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf)); + + lcd_grey_data(data, width); + data += stride; + } +} + /* Update the display. This must be called after all other LCD functions that change the display. */ -void lcd_update(void) ICODE_ATTR; void lcd_update(void) { int y; @@ -175,7 +191,6 @@ void lcd_update(void) } /* Update a fraction of the display. */ -void lcd_update_rect(int, int, int, int) ICODE_ATTR; void lcd_update_rect(int x, int y, int width, int height) { int ymax; diff --git a/firmware/target/sh/archos/lcd-as-archos-bitmap.S b/firmware/target/sh/archos/lcd-as-archos-bitmap.S index bef231c3c7..a84ce50686 100644 --- a/firmware/target/sh/archos/lcd-as-archos-bitmap.S +++ b/firmware/target/sh/archos/lcd-as-archos-bitmap.S @@ -81,26 +81,25 @@ */ _lcd_write_command: - mov.l .lcdr,r3 /* put lcd data port address in r3 */ - mov r4,r1 /* copy data byte to r1 */ - mov #1,r5 /* set byte count to 1 (!) */ + mov.l .lcdr, r3 /* put lcd data port address in r3 */ + mov r4, r1 /* copy data byte to r1 */ /* This code will fail if an interrupt changes the contents of PBDRL. * If so, we must disable the interrupt here. */ - mov.b @r3,r0 /* r0 = PBDRL */ - or #(LCD_SD),r0 /* r0 |= LCD_SD */ - and #(~(LCD_CS|LCD_DS|LCD_SC)),r0 /* r0 &= ~(LCD_CS|LCD_DS|LCD_SC) */ + mov.b @r3, r0 /* r0 = PBDRL */ + mov r4, r5 /* (fake) end address = current address */ + or #(LCD_SD), r0 /* r0 |= LCD_SD */ + and #(~(LCD_CS|LCD_DS|LCD_SC)), r0 /* r0 &= ~(LCD_CS|LCD_DS|LCD_SC) */ bra .single_transfer /* jump into the transfer loop */ - neg r0,r2 /* r2 = 0 - r0 */ + neg r0, r2 /* r2 = 0 - r0 */ .align 2 .global _lcd_write_data .type _lcd_write_data,@function - /* A high performance function to write data to the display, * one or multiple bytes. * @@ -117,8 +116,8 @@ _lcd_write_command: */ _lcd_write_data: - mov.l .lcdr,r3 /* put lcd data port address in r3 */ - nop /* align here */ + mov.l .lcdr, r3 /* put lcd data port address in r3 */ + add r4, r5 /* end address */ /* This code will fail if an interrupt changes the contents of PBDRL. * If so, we must disable the interrupt here. If disabling interrupts @@ -127,85 +126,233 @@ _lcd_write_data: * disable/precalculate/transfer/enable for each iteration. However, * this would significantly decrease performance. */ - mov.b @r3,r0 /* r0 = PBDRL */ - or #(LCD_DS|LCD_SD),r0 /* r0 |= LCD_DS|LCD_SD */ - and #(~(LCD_CS|LCD_SC)),r0 /* r0 &= ~(LCD_CS|LCD_SC) */ - neg r0,r2 /* r2 = 0 - r0 */ + mov.b @r3, r0 /* r0 = PBDRL */ + or #(LCD_DS|LCD_SD), r0 /* r0 |= LCD_DS|LCD_SD */ + and #(~(LCD_CS|LCD_SC)), r0 /* r0 &= ~(LCD_CS|LCD_SC) */ + neg r0, r2 /* r2 = 0 - r0 */ /* loop exploits that SD is on bit 0 for recorders and Ondios */ .align 2 .multi_transfer: - mov.b @r4+,r1 /* load data byte from memory */ + mov.b @r4+, r1 /* load data byte from memory */ nop .single_transfer: shll16 r1 /* shift data to most significant byte */ shll8 r1 - not r1,r1 /* and invert for use with negc */ + not r1, r1 /* and invert for use with negc */ shll r1 /* shift the MSB into carry */ - negc r2,r0 /* carry to SD, SC low */ + negc r2, r0 /* carry to SD, SC low */ shll r1 /* next shift here for alignment */ - mov.b r0,@r3 /* set data to port */ - or #(LCD_SC),r0 /* rise SC (independent of SD level) */ - mov.b r0,@r3 /* set to port */ + mov.b r0, @r3 /* set data to port */ + or #(LCD_SC), r0 /* rise SC (independent of SD level) */ + mov.b r0, @r3 /* set to port */ - negc r2,r0 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + negc r2, r0 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 shll r1 - negc r2,r0 + negc r2, r0 shll r1 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 - negc r2,r0 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + negc r2, r0 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 shll r1 - negc r2,r0 + negc r2, r0 shll r1 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 - negc r2,r0 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + negc r2, r0 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 shll r1 - negc r2,r0 + negc r2, r0 shll r1 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 + + negc r2, r0 + mov.b r0, @r3 + or #(LCD_SC), r0 + mov.b r0, @r3 + + cmp/hi r4, r5 /* some blocks left? */ + bt .multi_transfer + + or #(LCD_CS|LCD_DS|LCD_SD|LCD_SC), r0 /* restore port */ + rts + mov.b r0, @r3 + + /* This is the place to reenable the interrupts, if we have disabled + * them. See above. */ + + + .align 2 + .global _lcd_grey_data + .type _lcd_grey_data,@function + +/* A high performance function to write grey phase data to the display, + * one or multiple pixels. + * + * Arguments: + * r4 - data address, (phase,value)-pairs + * r5 - pixel block count + * + * Register usage: + * r0 - current pixel value + * r1 - scratch + * r2 - precalculated port value (CS and SC low, DS and SD high), + * negated (neg)! + * r3 - lcd port address + * r5 - end address + * r6/r7 - current/next pixel phase + * r8 - current block address (for writing back phase) + * r9 - 0x80 (for phase modification) + */ + +_lcd_grey_data: + mov.l r8, @-r15 /* save r8 */ + shll2 r5 /* v */ + mov.l r9, @-r15 /* save r9 */ + shll2 r5 /* r5 *= 16; (8 pixel per block * 2 bytes/pixel) */ + mov.l .lcdr, r3 /* put lcd data port address in r3 */ + add r4, r5 /* end address */ - negc r2,r0 - mov.b r0,@r3 - or #(LCD_SC),r0 - mov.b r0,@r3 + /* This code will fail if an interrupt changes the contents of PBDRL. + * If so, we must disable the interrupt here. If disabling interrupts + * for a long time is undesirable, the loop has to be rewritten to + * disable/precalculate/transfer/enable for each iteration. However, + * this would significantly decrease performance. */ - add #-1,r5 /* decrease byte count */ - tst r5,r5 /* r5 == 0 ? */ - bf .multi_transfer /* no: next iteration */ + mov.b @r3, r0 /* r0 = PBDRL */ + mov r4, r8 /* copy start address */ + mov.b @r4+, r6 /* fetch first pixel phase */ + or #(LCD_DS|LCD_SD), r0 /* r0 |= LCD_DS|LCD_SD */ + and #(~(LCD_CS|LCD_SC)), r0 /* r0 &= ~(LCD_CS|LCD_SC) */ + neg r0, r2 /* r2 = 0 - r0 */ + mov #0x80, r9 /* for phase modification - "or #imm,xx" only allows r0 */ + + /* loop exploits that SD is on bit 0 for recorders and Ondios */ - or #(LCD_CS|LCD_DS|LCD_SD|LCD_SC),r0 /* restore port */ +.greyloop: + cmp/pz r6 /* phase non-negative? */ + mov.b @r4+, r0 /* fetch pixel value */ + negc r2, r1 /* T -> SD, SC low */ + mov.b r1, @r3 /* set port */ + or r9, r6 /* r6 -= (r6 >= 0) ? 128 : 0; */ + mov.b @r4+, r7 /* fetch next pixel phase */ + add #(LCD_SC), r1 /* rise SC */ + mov.b r1, @r3 /* set port */ + add r6, r0 /* calculate new phase */ + mov.b r0, @r8 /* store phase */ + + cmp/pz r7 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r7 + mov.b @r4+, r6 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r7, r0 + mov.b r0, @(2,r8) + + cmp/pz r6 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r6 + mov.b @r4+, r7 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r6, r0 + mov.b r0, @(4,r8) + + cmp/pz r7 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r7 + mov.b @r4+, r6 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r7, r0 + mov.b r0, @(6,r8) + + cmp/pz r6 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r6 + mov.b @r4+, r7 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r6, r0 + mov.b r0, @(8,r8) + + cmp/pz r7 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r7 + mov.b @r4+, r6 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r7, r0 + mov.b r0, @(10,r8) + + cmp/pz r6 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r6 + mov.b @r4+, r7 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r6, r0 + mov.b r0, @(12,r8) + + cmp/pz r7 + mov.b @r4+, r0 + negc r2, r1 + mov.b r1, @r3 + or r9, r7 + mov.b @r4+, r6 + add #(LCD_SC), r1 + mov.b r1, @r3 + add r7, r0 + mov.b r0, @(14,r8) + + add #16, r8 /* advance current block address */ + cmp/hi r4, r5 /* some blocks left? */ + bt .greyloop + + mov.l @r15+, r9 /* restore r9 */ + mov #(LCD_CS|LCD_DS|LCD_SD|LCD_SC), r0 + mov.l @r15+, r8 /* restore r8 */ + or r0, r1 /* restore port */ rts - mov.b r0,@r3 + mov.b r1, @r3 /* This is the place to reenable the interrupts, if we have disabled * them. See above. */ + .align 2 .lcdr: .long LCDR - -.end: - .size _lcd_write_command,.end-_lcd_write_command -- cgit v1.2.3