[Hardware Hacking General] Rick_1995:
( This isn't exactly hardware hacking, but there's no software hacking subforum for bare metal software )

I'm working on framebuffer driver for a 32 bit arm (cortex-a8) soc, the pixels on the board are 16 bit (rgb565), so raw pixel read/writes are much slower than they can be (halfword load/store), I've tried combining pixels at 32 bit boundary into one 32 bit data type but it is still a bit slow.

Another problem is that the first pixel is always unaligned at 32 bit boundary, so first and last pixels are half word writes :(

I was wondering if anyone could optimize this any further ?

It prints only 32 < ASCII chars > 127 (space is a no-op with just a cursor increment)

void fb_putc(char c) {
if ((c > 32) && (c < 127)) {
// If we do this, we have 15 kb less to scroll if done after printing the line.
if(fb.cursor.y >= (SCREEN_HEIGHT - FONT_HEIGHT)) {
fb.cursor.y -= FONT_HEIGHT; // Move cursor 1 line up
memcpy(fb.base, fb.base + FB_LINE_SZ, FB_SCROLL_SZ); // Move all but last line to first line
memset(fb.base + FB_SCROLL_SZ, fb.bg_color, FB_LINE_SZ); // Clear last line
}
c = ((c - 32) << 4);
uint8_t fbitmap;
uint16_t fbcol = fb.fg_color; // pen color
uint16_t tbcol = fb.tg_color; // pen back color
uint16_t *fbpx_16 = (uint16_t *) fb.base + (((fb.cursor.y << 9) - (fb.cursor.y << 5)) + (fb.cursor.x + 1));
uint32_t fbpx_32_tmp = 0;
for (int iy = 0; iy < FONT_HEIGHT; iy++)
{
// Hardcoded unwinded loop for fonts with width 8
fbitmap = fontdata[c + iy];
if (fbitmap & 0x80) *fbpx_16++ = fbcol; else *fbpx_16++ = tgcol;

fbpx_32_tmp ^= fbpx_32_tmp;
if (fbitmap & 0x40) fbpx_32_tmp |= (fbcol << 16); else fbpx_32_tmp |= (tgcol << 16);
if (fbitmap & 0x20) fbpx_32_tmp |= fbcol; else fbpx_32_tmp |= tgcol;
*(uint32_t *) fbpx_16++ = fbpx_32_tmp;

fbpx_32_tmp ^= fbpx_32_tmp;
if (fbitmap & 0x10) fbpx_32_tmp |= (fbcol << 16); else fbpx_32_tmp |= (tgcol << 16);
if (fbitmap & 0x08) fbpx_32_tmp |= fbcol; else fbpx_32_tmp |= tgcol;
*(uint32_t *) fbpx_16++ = fbpx_32_tmp;

fbpx_32_tmp ^= fbpx_32_tmp;
if (fbitmap & 0x04) fbpx_32_tmp |= (fbcol << 16); else fbpx_32_tmp |= (tgcol << 16);
if (fbitmap & 0x02) fbpx_32_tmp |= fbcol; else fbpx_32_tmp |= tgcol;
*(uint32_t *) fbpx_16++ = fbpx_32_tmp;

if (fbitmap & 0x01) *fbpx_16 = fbcol; else *fbpx_16 = tgcol;
fbpx_16 += SCREEN_STRIDE + 1; // Move to next line's first pixel we'll be manipulating
}
fb.cursor.x += FONT_WIDTH;
}
... (code for \n, \t, ' ' etc..)
}

What i thought of:

1) Write pixel data to seperate aligned memory and then use memcpy to transfer it to framebuffer (memcpy is neon + pld, 32 bytes per loop), dst is still unaligned so this should actually be worse.

2) Write pixel data to seperate grid then DMA it to the framebuffer

3) use neon for writing pixels ? (this could actually be slow because it will block arm pipeline while comparing font bitmap for checking wether to write pen color or background color.

Another attempt (but this is very much same as above except for less junk branches put in by the compiler, gcc)

@ r0 *bitmap, r1 *base, r2 BG, r3 FG, r4, r5, r6 corrupted
@ extern void fbS(void *bitmap, void *fbmem, uint16_t tg, uint16_t fg);
.arm
.text

.align 2
.global fbS
.type fbS, #function
fbS:
stmdb sp!, {r4, r5, r6}
eor r6, r6, r6
10:
add r6, r6, #1

ldrb r4, [r0], #1

tst r4, #0x80
strneh r2, [r1], #2
streqh r3, [r1], #2

eor r5, r5, r5
tst r4, #0x40
orrne r5, r5, r2, lsl #16
orreq r5, r5, r3, lsl #16
tst r4, #0x20
orrne r5, r5, r2
orreq r5, r5, r3
str r5, [r1], #4

eor r5, r5, r5
tst r4, #0x40
orrne r5, r5, r2, lsl #16
orreq r5, r5, r3, lsl #16
tst r4, #0x20
orrne r5, r5, r2
orreq r5, r5, r3
str r5, [r1], #4

eor r5, r5, r5
tst r4, #0x40
orrne r5, r5, r2, lsl #16
orreq r5, r5, r3, lsl #16
tst r4, #0x20
orrne r5, r5, r2
orreq r5, r5, r3
str r5, [r1], #4

eor r5, r5, r5
tst r4, #0x01
strneh r2, [r1], #2
streqh r3, [r1], #2

add r5, r5, #944

tst r6, #16
blo 10b

ldmdb sp!, {r4, r5, r6}
mov pc, lr

And the psuedo code:

r6 = 0
10:
r6++
r4 = (u8) *r0++

if (r4 & (1 << 7))
*(u16 *) r1++ = r2
else
*(u16 *) r1++ = r3

r5 = 0
if (r4 & (1 << 6))
r5 |= r2 << 16
else
r5 |= r3 << 16
if (r4 & (1 << 5))
r5 |= r2
else
r5 |= r3
*(u32 *) r1++ = r5

r5 = 0
if (r4 & (1 << 4))
r5 |= r2 << 16
else
r5 |= r3 << 16
if (r4 & (1 << 3))
r5 |= r2
else
r5 |= r3
*(u32 *) r1++ = r5

r5 = 0
if (r4 & (1 << 2))
r5 |= r2 << 16
else
r5 |= r3 << 16
if (r4 & (1 << 1))
r5 |= r2
else
r5 |= r3
*(u32 *) r1++ = r5

if (r4 & (1 << 0))
*(u16 *) r1++ = r2
else
*(u16 *) r1++ = r3

r1 += 944 @ (SCREEN_WIDTH - FONT_WIDTH) << 1

if (r6 < 16) goto 10

return