2021-01-12 18:22:11 +00:00
|
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
|
|
|
|
#include "fb.h"
|
2021-05-01 09:31:58 +00:00
|
|
|
#include "assert.h"
|
2021-04-15 13:47:33 +00:00
|
|
|
#include "iodev.h"
|
2021-05-07 03:18:07 +00:00
|
|
|
#include "malloc.h"
|
2021-10-15 14:40:09 +00:00
|
|
|
#include "memory.h"
|
2021-02-12 19:09:49 +00:00
|
|
|
#include "string.h"
|
2021-04-15 13:47:33 +00:00
|
|
|
#include "types.h"
|
2021-01-12 18:22:11 +00:00
|
|
|
#include "utils.h"
|
|
|
|
#include "xnuboot.h"
|
|
|
|
|
2021-02-12 19:09:49 +00:00
|
|
|
#define FB_DEPTH_FLAG_RETINA 0x10000
|
|
|
|
#define FB_DEPTH_MASK 0xff
|
2021-01-12 18:22:11 +00:00
|
|
|
|
2021-02-12 19:09:49 +00:00
|
|
|
fb_t fb;
|
2021-01-12 18:22:11 +00:00
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
struct image {
|
2021-02-12 19:09:49 +00:00
|
|
|
u32 *ptr;
|
|
|
|
u32 width;
|
|
|
|
u32 height;
|
2021-05-07 03:18:07 +00:00
|
|
|
};
|
2021-02-12 19:09:49 +00:00
|
|
|
|
2021-03-14 22:50:01 +00:00
|
|
|
static struct {
|
|
|
|
struct {
|
|
|
|
u8 *ptr;
|
|
|
|
u32 width;
|
|
|
|
u32 height;
|
|
|
|
} font;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
u32 row;
|
|
|
|
u32 col;
|
|
|
|
|
|
|
|
u32 max_row;
|
|
|
|
u32 max_col;
|
|
|
|
} cursor;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
u32 rows;
|
|
|
|
u32 cols;
|
|
|
|
} margin;
|
|
|
|
|
|
|
|
int initialized;
|
2021-04-15 13:53:13 +00:00
|
|
|
} console = {.initialized = 0};
|
2021-03-14 22:50:01 +00:00
|
|
|
|
2021-02-12 19:09:49 +00:00
|
|
|
extern u8 _binary_build_bootlogo_128_bin_start[];
|
|
|
|
extern u8 _binary_build_bootlogo_256_bin_start[];
|
2021-01-12 18:22:11 +00:00
|
|
|
|
2021-02-12 21:27:39 +00:00
|
|
|
extern u8 _binary_build_font_bin_start[];
|
|
|
|
extern u8 _binary_build_font_retina_bin_start[];
|
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
const struct image logo_128 = {
|
|
|
|
.ptr = (void *)_binary_build_bootlogo_128_bin_start,
|
|
|
|
.width = 128,
|
|
|
|
.height = 128,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct image logo_256 = {
|
|
|
|
.ptr = (void *)_binary_build_bootlogo_256_bin_start,
|
|
|
|
.width = 256,
|
|
|
|
.height = 256,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct image *logo;
|
|
|
|
struct image orig_logo;
|
|
|
|
|
2021-11-02 06:24:26 +00:00
|
|
|
void fb_update(void)
|
|
|
|
{
|
2021-11-17 10:23:12 +00:00
|
|
|
memcpy128(fb.hwptr, fb.ptr, fb.size);
|
2021-11-02 06:24:26 +00:00
|
|
|
}
|
|
|
|
|
2021-03-14 22:50:01 +00:00
|
|
|
static void fb_clear_font_row(u32 row)
|
|
|
|
{
|
|
|
|
const u32 row_size = (console.margin.cols + console.cursor.max_col) * console.font.width * 4;
|
|
|
|
const u32 ystart = (console.margin.rows + row) * console.font.height * fb.stride;
|
|
|
|
|
|
|
|
for (u32 y = 0; y < console.font.height; ++y)
|
2021-11-02 06:24:26 +00:00
|
|
|
memset32(fb.ptr + ystart + y * fb.stride, 0, row_size);
|
2021-03-14 22:50:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_move_font_row(u32 dst, u32 src)
|
|
|
|
{
|
|
|
|
const u32 row_size = (console.margin.cols + console.cursor.max_col) * console.font.width * 4;
|
|
|
|
u32 ysrc = (console.margin.rows + src) * console.font.height;
|
|
|
|
u32 ydst = (console.margin.rows + dst) * console.font.height;
|
|
|
|
|
|
|
|
ysrc *= fb.stride;
|
|
|
|
ydst *= fb.stride;
|
|
|
|
|
|
|
|
for (u32 y = 0; y < console.font.height; ++y)
|
2021-11-02 06:24:26 +00:00
|
|
|
memcpy32(fb.ptr + ydst + y * fb.stride, fb.ptr + ysrc + y * fb.stride, row_size);
|
2021-03-14 22:50:01 +00:00
|
|
|
|
|
|
|
fb_clear_font_row(src);
|
|
|
|
}
|
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
static inline u32 rgb2pixel_30(rgb_t c)
|
2021-01-12 18:22:11 +00:00
|
|
|
{
|
2021-05-07 03:18:07 +00:00
|
|
|
return (c.b << 2) | (c.g << 12) | (c.r << 22);
|
|
|
|
}
|
2021-03-14 22:50:01 +00:00
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
static inline rgb_t pixel2rgb_30(u32 c)
|
|
|
|
{
|
|
|
|
return (rgb_t){(c >> 22) & 0xff, (c >> 12) & 0xff, c >> 2};
|
|
|
|
}
|
2021-03-14 22:50:01 +00:00
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
static inline void fb_set_pixel(u32 x, u32 y, rgb_t c)
|
|
|
|
{
|
|
|
|
fb.ptr[x + y * fb.stride] = rgb2pixel_30(c);
|
2021-01-12 18:22:11 +00:00
|
|
|
}
|
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
static inline rgb_t fb_get_pixel(u32 x, u32 y)
|
2021-01-12 18:22:11 +00:00
|
|
|
{
|
2021-05-07 03:18:07 +00:00
|
|
|
return pixel2rgb_30(fb.ptr[x + y * fb.stride]);
|
2021-01-12 18:22:11 +00:00
|
|
|
}
|
|
|
|
|
2021-02-12 19:09:49 +00:00
|
|
|
void fb_blit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride)
|
2021-01-12 18:22:11 +00:00
|
|
|
{
|
2021-02-12 19:09:49 +00:00
|
|
|
u8 *p = data;
|
2021-01-12 18:22:11 +00:00
|
|
|
|
2021-02-12 19:09:49 +00:00
|
|
|
for (u32 i = 0; i < h; i++) {
|
|
|
|
for (u32 j = 0; j < w; j++) {
|
|
|
|
rgb_t color = {.r = p[(j + i * stride) * 4],
|
|
|
|
.g = p[(j + i * stride) * 4 + 1],
|
|
|
|
.b = p[(j + i * stride) * 4 + 2]};
|
|
|
|
fb_set_pixel(x + j, y + i, color);
|
|
|
|
}
|
|
|
|
}
|
2021-11-02 06:24:26 +00:00
|
|
|
fb_update();
|
2021-01-12 18:22:11 +00:00
|
|
|
}
|
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
void fb_unblit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride)
|
|
|
|
{
|
|
|
|
u8 *p = data;
|
|
|
|
|
|
|
|
for (u32 i = 0; i < h; i++) {
|
|
|
|
for (u32 j = 0; j < w; j++) {
|
|
|
|
rgb_t color = fb_get_pixel(x + j, y + i);
|
|
|
|
p[(j + i * stride) * 4] = color.r;
|
|
|
|
p[(j + i * stride) * 4 + 1] = color.g;
|
|
|
|
p[(j + i * stride) * 4 + 2] = color.b;
|
|
|
|
p[(j + i * stride) * 4 + 3] = 0xff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 19:09:49 +00:00
|
|
|
void fb_fill(u32 x, u32 y, u32 w, u32 h, rgb_t color)
|
2021-01-12 18:22:11 +00:00
|
|
|
{
|
2021-05-07 03:18:07 +00:00
|
|
|
u32 c = rgb2pixel_30(color);
|
2021-02-12 19:09:49 +00:00
|
|
|
for (u32 i = 0; i < h; i++)
|
2021-05-07 03:18:07 +00:00
|
|
|
memset32(&fb.ptr[x + (y + i) * fb.stride], c, w * 4);
|
2021-11-02 06:24:26 +00:00
|
|
|
fb_update();
|
2021-05-07 03:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fb_clear(rgb_t color)
|
|
|
|
{
|
|
|
|
u32 c = rgb2pixel_30(color);
|
|
|
|
memset32(fb.ptr, c, fb.stride * fb.height * 4);
|
2021-11-02 06:24:26 +00:00
|
|
|
fb_update();
|
2021-05-07 03:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fb_blit_image(u32 x, u32 y, const struct image *img)
|
|
|
|
{
|
|
|
|
fb_blit(x, y, img->width, img->height, img->ptr, img->width);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_unblit_image(u32 x, u32 y, struct image *img)
|
|
|
|
{
|
|
|
|
fb_unblit(x, y, img->width, img->height, img->ptr, img->width);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_blit_logo(const struct image *logo)
|
|
|
|
{
|
|
|
|
fb_blit_image((fb.width - logo->width) / 2, (fb.height - logo->height) / 2, logo);
|
2021-01-12 18:22:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fb_display_logo(void)
|
|
|
|
{
|
|
|
|
printf("fb: display logo\n");
|
2021-05-07 03:18:07 +00:00
|
|
|
fb_blit_logo(logo);
|
2021-01-12 18:22:11 +00:00
|
|
|
}
|
2021-03-14 22:50:01 +00:00
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
void fb_restore_logo(void)
|
|
|
|
{
|
|
|
|
if (!orig_logo.ptr)
|
|
|
|
return;
|
|
|
|
fb_blit_logo(&orig_logo);
|
|
|
|
}
|
|
|
|
|
2021-05-07 18:44:16 +00:00
|
|
|
void fb_improve_logo(void)
|
|
|
|
{
|
|
|
|
const u8 magic[] = "BY;iX2gK0b89P9P*Qa";
|
|
|
|
u8 *p = (void *)orig_logo.ptr;
|
|
|
|
|
|
|
|
if (!p || p[orig_logo.width * (orig_logo.height + 1) * 2] <= 250)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (u32 i = 0; i < orig_logo.height; i++) {
|
|
|
|
const u8 *c = &magic[min((max(i * 128 / orig_logo.height, 41) - 41) / 11, 5) * 3];
|
|
|
|
for (u32 j = 0; j < (orig_logo.width * 4); j++, p++)
|
|
|
|
*p = (*p * (c[(j - (j >> 2)) % 3] - 42)) / 63;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
static inline rgb_t font_get_pixel(u8 c, u32 x, u32 y)
|
2021-03-14 22:50:01 +00:00
|
|
|
{
|
|
|
|
c -= 0x20;
|
|
|
|
u8 v =
|
|
|
|
console.font.ptr[c * console.font.width * console.font.height + y * console.font.width + x];
|
|
|
|
|
|
|
|
rgb_t col = {.r = v, .g = v, .b = v};
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_putbyte(u8 c)
|
|
|
|
{
|
|
|
|
u32 x = (console.margin.cols + console.cursor.col) * console.font.width;
|
|
|
|
u32 y = (console.margin.rows + console.cursor.row) * console.font.height;
|
|
|
|
|
|
|
|
for (u32 i = 0; i < console.font.height; i++)
|
|
|
|
for (u32 j = 0; j < console.font.width; j++)
|
|
|
|
fb_set_pixel(x + j, y + i, font_get_pixel(c, j, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fb_putchar(u8 c)
|
|
|
|
{
|
|
|
|
if (c == '\r') {
|
|
|
|
console.cursor.col = 0;
|
|
|
|
} else if (c == '\n') {
|
|
|
|
console.cursor.row++;
|
|
|
|
console.cursor.col = 0;
|
|
|
|
} else if (c >= 0x20 && c < 0x7f) {
|
|
|
|
fb_putbyte(c);
|
|
|
|
console.cursor.col++;
|
|
|
|
} else {
|
|
|
|
fb_putbyte('?');
|
|
|
|
console.cursor.col++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (console.cursor.col == console.cursor.max_col) {
|
|
|
|
console.cursor.row++;
|
|
|
|
console.cursor.col = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (console.cursor.row == console.cursor.max_row)
|
|
|
|
fb_console_scroll(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_console_scroll(u32 n)
|
|
|
|
{
|
|
|
|
u32 row = 0;
|
|
|
|
n = min(n, console.cursor.row);
|
|
|
|
for (; row < console.cursor.max_row - n; ++row)
|
|
|
|
fb_move_font_row(row, row + n);
|
|
|
|
for (; row < console.cursor.max_row; ++row)
|
|
|
|
fb_clear_font_row(row);
|
|
|
|
console.cursor.row -= n;
|
|
|
|
}
|
|
|
|
|
2021-04-15 13:44:55 +00:00
|
|
|
void fb_console_reserve_lines(u32 n)
|
|
|
|
{
|
|
|
|
if ((console.cursor.max_row - console.cursor.row) <= n)
|
|
|
|
fb_console_scroll(1 + n - (console.cursor.max_row - console.cursor.row));
|
2021-11-02 06:24:26 +00:00
|
|
|
fb_update();
|
2021-04-15 13:44:55 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 13:47:33 +00:00
|
|
|
ssize_t fb_console_write(const char *bfr, size_t len)
|
2021-03-14 22:50:01 +00:00
|
|
|
{
|
2021-04-15 13:47:33 +00:00
|
|
|
ssize_t wrote = 0;
|
|
|
|
|
2021-04-15 13:36:12 +00:00
|
|
|
if (!console.initialized)
|
2021-04-15 13:47:33 +00:00
|
|
|
return 0;
|
2021-03-14 22:50:01 +00:00
|
|
|
|
2021-04-15 13:47:33 +00:00
|
|
|
while (len--) {
|
2021-03-14 22:50:01 +00:00
|
|
|
fb_putchar(*bfr++);
|
2021-04-15 13:47:33 +00:00
|
|
|
wrote++;
|
|
|
|
}
|
|
|
|
|
2021-11-02 06:24:26 +00:00
|
|
|
fb_update();
|
|
|
|
|
2021-04-15 13:47:33 +00:00
|
|
|
return wrote;
|
2021-03-14 22:50:01 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 13:47:33 +00:00
|
|
|
static bool fb_console_iodev_can_write(void *opaque)
|
|
|
|
{
|
|
|
|
UNUSED(opaque);
|
|
|
|
return console.initialized;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t fb_console_iodev_write(void *opaque, const void *buf, size_t len)
|
|
|
|
{
|
|
|
|
UNUSED(opaque);
|
|
|
|
return fb_console_write(buf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct iodev_ops iodev_fb_ops = {
|
|
|
|
.can_write = fb_console_iodev_can_write,
|
|
|
|
.write = fb_console_iodev_write,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct iodev iodev_fb = {
|
|
|
|
.ops = &iodev_fb_ops,
|
|
|
|
.usage = USAGE_CONSOLE,
|
2021-11-03 18:53:39 +00:00
|
|
|
.lock = SPINLOCK_INIT,
|
2021-04-15 13:47:33 +00:00
|
|
|
};
|
2021-05-07 03:18:07 +00:00
|
|
|
|
2021-08-31 05:10:03 +00:00
|
|
|
static void fb_clear_console(void)
|
|
|
|
{
|
|
|
|
for (u32 row = 0; row < console.cursor.max_row; ++row)
|
|
|
|
fb_clear_font_row(row);
|
2021-11-02 06:24:26 +00:00
|
|
|
|
|
|
|
console.cursor.col = 0;
|
|
|
|
console.cursor.row = 0;
|
|
|
|
fb_update();
|
2021-08-31 05:10:03 +00:00
|
|
|
}
|
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
void fb_init(void)
|
|
|
|
{
|
2021-11-02 06:24:26 +00:00
|
|
|
fb.hwptr = (void *)cur_boot_args.video.base;
|
2021-05-07 03:18:07 +00:00
|
|
|
fb.stride = cur_boot_args.video.stride / 4;
|
|
|
|
fb.width = cur_boot_args.video.width;
|
|
|
|
fb.height = cur_boot_args.video.height;
|
|
|
|
fb.depth = cur_boot_args.video.depth & FB_DEPTH_MASK;
|
2021-11-02 06:24:26 +00:00
|
|
|
fb.size = cur_boot_args.video.stride * cur_boot_args.video.height;
|
2021-05-07 03:18:07 +00:00
|
|
|
printf("fb init: %dx%d (%d) [s=%d] @%p\n", fb.width, fb.height, fb.depth, fb.stride, fb.ptr);
|
|
|
|
|
2021-11-02 06:24:26 +00:00
|
|
|
mmu_add_mapping(cur_boot_args.video.base, cur_boot_args.video.base, ALIGN_UP(fb.size, 0x4000),
|
|
|
|
MAIR_IDX_FRAMEBUFFER, PERM_RW);
|
|
|
|
|
|
|
|
fb.ptr = malloc(fb.size);
|
|
|
|
memcpy(fb.ptr, fb.hwptr, fb.size);
|
2021-10-15 14:40:09 +00:00
|
|
|
|
2021-05-07 03:18:07 +00:00
|
|
|
if (cur_boot_args.video.depth & FB_DEPTH_FLAG_RETINA) {
|
|
|
|
logo = &logo_256;
|
|
|
|
console.font.ptr = _binary_build_font_retina_bin_start;
|
|
|
|
console.font.width = 16;
|
|
|
|
console.font.height = 32;
|
|
|
|
} else {
|
|
|
|
logo = &logo_128;
|
|
|
|
console.font.ptr = _binary_build_font_bin_start;
|
|
|
|
console.font.width = 8;
|
|
|
|
console.font.height = 16;
|
|
|
|
}
|
|
|
|
|
2021-08-31 05:10:03 +00:00
|
|
|
if (!orig_logo.ptr) {
|
|
|
|
orig_logo = *logo;
|
|
|
|
orig_logo.ptr = malloc(orig_logo.width * orig_logo.height * 4);
|
|
|
|
fb_unblit_image((fb.width - orig_logo.width) / 2, (fb.height - orig_logo.height) / 2,
|
|
|
|
&orig_logo);
|
|
|
|
}
|
2021-05-07 03:18:07 +00:00
|
|
|
|
|
|
|
console.margin.rows = 2;
|
|
|
|
console.margin.cols = 4;
|
|
|
|
console.cursor.col = 0;
|
|
|
|
console.cursor.row = 0;
|
|
|
|
|
|
|
|
console.cursor.max_row = (fb.height / console.font.height) - 2 * console.margin.rows;
|
|
|
|
console.cursor.max_col =
|
|
|
|
((fb.width - logo->width) / 2) / console.font.width - 2 * console.margin.cols;
|
|
|
|
|
|
|
|
console.initialized = 1;
|
|
|
|
|
2021-08-31 05:10:03 +00:00
|
|
|
fb_clear_console();
|
2021-05-07 03:18:07 +00:00
|
|
|
|
|
|
|
printf("fb console: max rows %d, max cols %d\n", console.cursor.max_row,
|
|
|
|
console.cursor.max_col);
|
|
|
|
}
|
|
|
|
|
2021-08-31 05:10:03 +00:00
|
|
|
void fb_shutdown(bool restore_logo)
|
2021-05-07 03:18:07 +00:00
|
|
|
{
|
|
|
|
if (!console.initialized)
|
|
|
|
return;
|
|
|
|
|
|
|
|
console.initialized = 0;
|
2021-08-31 05:10:03 +00:00
|
|
|
fb_clear_console();
|
|
|
|
if (restore_logo) {
|
|
|
|
fb_restore_logo();
|
|
|
|
free(orig_logo.ptr);
|
|
|
|
orig_logo.ptr = NULL;
|
|
|
|
}
|
2021-11-02 06:24:26 +00:00
|
|
|
free(fb.ptr);
|
2021-05-07 03:18:07 +00:00
|
|
|
}
|