From acc9d19a32b0bd3c750855ba8b9d18a71226525b Mon Sep 17 00:00:00 2001 From: aurlex Date: Tue, 20 Aug 2024 13:03:39 +0100 Subject: [PATCH] fb driver --- repbuild/src/main.rs | 4 +- sysdata/libraries/stn/src/lib.hb | 3 +- sysdata/libraries/stn/src/math.hb | 7 ++ sysdata/programs/fb_driver/UNTESTED_FUNCTIONS | 7 ++ sysdata/programs/fb_driver/meta.toml | 2 +- sysdata/programs/fb_driver/src/color.hb | 27 +++++ sysdata/programs/fb_driver/src/draw.hb | 113 ++++++++++++++++++ .../programs/fb_driver/src/examples/amogus.hb | 26 ++++ .../programs/fb_driver/src/examples/colors.hb | 27 +++++ .../fb_driver/src/examples/front_buffer.hb | 19 +++ .../programs/fb_driver/src/examples/lines.hb | 25 ++++ .../programs/fb_driver/src/examples/square.hb | 32 +++++ .../programs/fb_driver/src/examples/strobe.hb | 20 ++++ sysdata/programs/fb_driver/src/lib.hb | 100 ++++++++++++++++ sysdata/programs/fb_driver/src/main.hb | 14 +-- 15 files changed, 411 insertions(+), 15 deletions(-) create mode 100644 sysdata/libraries/stn/src/math.hb create mode 100644 sysdata/programs/fb_driver/UNTESTED_FUNCTIONS create mode 100644 sysdata/programs/fb_driver/src/color.hb create mode 100644 sysdata/programs/fb_driver/src/draw.hb create mode 100644 sysdata/programs/fb_driver/src/examples/amogus.hb create mode 100644 sysdata/programs/fb_driver/src/examples/colors.hb create mode 100644 sysdata/programs/fb_driver/src/examples/front_buffer.hb create mode 100644 sysdata/programs/fb_driver/src/examples/lines.hb create mode 100644 sysdata/programs/fb_driver/src/examples/square.hb create mode 100644 sysdata/programs/fb_driver/src/examples/strobe.hb create mode 100644 sysdata/programs/fb_driver/src/lib.hb diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index c308774..af09c6d 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -336,8 +336,8 @@ fn run(release: bool, target: Target) -> Result<(), Error> { "-drive", "file=target/disk.img,format=raw", "-m", "4G", "-smp", "cores=4", - // "-enable-kvm", - "-cpu", "Broadwell-v4", + "-enable-kvm", + "-cpu", "host", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04" ]); } diff --git a/sysdata/libraries/stn/src/lib.hb b/sysdata/libraries/stn/src/lib.hb index e8b9ef3..ff45c3b 100644 --- a/sysdata/libraries/stn/src/lib.hb +++ b/sysdata/libraries/stn/src/lib.hb @@ -1,4 +1,5 @@ string := @use("rel:string.hb") log := @use("rel:log.hb") memory := @use("rel:memory.hb") -buffer := @use("rel:buffer.hb") \ No newline at end of file +buffer := @use("rel:buffer.hb") +math := @use("rel:math.hb") \ No newline at end of file diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb new file mode 100644 index 0000000..2fd1d02 --- /dev/null +++ b/sysdata/libraries/stn/src/math.hb @@ -0,0 +1,7 @@ +abs := fn(x: int): int { + mask := x >> 31 + return (x ^ mask) - mask +} +min := fn(a: int, b: int): int { + return b + (a - b & a - b >> 31) +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/UNTESTED_FUNCTIONS b/sysdata/programs/fb_driver/UNTESTED_FUNCTIONS new file mode 100644 index 0000000..7c7d0f5 --- /dev/null +++ b/sysdata/programs/fb_driver/UNTESTED_FUNCTIONS @@ -0,0 +1,7 @@ +color.blend + +lib.composite +lib.screen2rect +lib.rect2screen + +draw.tri_line \ No newline at end of file diff --git a/sysdata/programs/fb_driver/meta.toml b/sysdata/programs/fb_driver/meta.toml index 9863f08..c2659bc 100644 --- a/sysdata/programs/fb_driver/meta.toml +++ b/sysdata/programs/fb_driver/meta.toml @@ -1,6 +1,6 @@ [package] name = "fb_driver" -authors = ["able"] +authors = ["able", "aurlex"] [dependants.libraries] diff --git a/sysdata/programs/fb_driver/src/color.hb b/sysdata/programs/fb_driver/src/color.hb new file mode 100644 index 0000000..51bbf14 --- /dev/null +++ b/sysdata/programs/fb_driver/src/color.hb @@ -0,0 +1,27 @@ +ColorBGRA := struct {b: u8, g: u8, r: u8, a: u8} + +/* ALL the colo(u)rs you will ever need. + they dont work though, cause hblang bug (reg id leaked, again) */ +WHITE := ColorBGRA.{b: 255, g: 255, r: 255, a: 255} +BLACK := ColorBGRA.{b: 0, g: 0, r: 0, a: 255} +GRAY := ColorBGRA.{b: 127, g: 127, r: 127, a: 255} +RED := ColorBGRA.{b: 0, g: 0, r: 205, a: 255} +GREEN := ColorBGRA.{b: 0, g: 205, r: 0, a: 255} +YELLOW := ColorBGRA.{b: 0, g: 205, r: 205, a: 255} +BLUE := ColorBGRA.{b: 205, g: 0, r: 0, a: 255} +MAGENTA := ColorBGRA.{b: 205, g: 0, r: 205, a: 255} +CYAN := ColorBGRA.{b: 205, g: 205, r: 0, a: 255} +LIGHTGRAY := ColorBGRA.{b: 229, g: 229, r: 229, a: 255} +LIGHTRED := ColorBGRA.{b: 0, g: 0, r: 255, a: 255} +LIGHTGREEN := ColorBGRA.{b: 0, g: 255, r: 0, a: 255} +LIGHTYELLOW := ColorBGRA.{b: 0, g: 255, r: 255, a: 255} +LIGHTBLUE := ColorBGRA.{b: 255, g: 0, r: 0, a: 255} +LIGHTMAGENTA := ColorBGRA.{b: 255, g: 0, r: 255, a: 255} +LIGHTCYAN := ColorBGRA.{b: 255, g: 255, r: 0, a: 255} + +// i have no clue if this works. please don't me ask how it works. -aurlex +blend := fn(fg: ColorBGRA, bg: ColorBGRA): ColorBGRA { + s := fg + bg + m := s - ((fg ^ bg) & 16843008) & 16843008 + return (m >> 8 | 16777216 * (s < fg)) * 255 | s - m +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/draw.hb b/sysdata/programs/fb_driver/src/draw.hb new file mode 100644 index 0000000..9faf24a --- /dev/null +++ b/sysdata/programs/fb_driver/src/draw.hb @@ -0,0 +1,113 @@ +.{draw_pixel, screenidx, Transform, Point, Rect, Buffer, FB_WIDTH} := @use("rel:lib.hb") +ColorBGRA := @use("rel:color.hb").ColorBGRA +math := @use("../../../libraries/stn/src/lib.hb").math + +/* draws a filled rectangle to the screen + will be optimised later */ +rect_fill := fn(buffer: Buffer, pos: Point, tr: Transform, color: ColorBGRA): void { + n := 0 + loop if n == tr.height * tr.width break else { + *(buffer.write + screenidx(.(n % tr.width + pos.x, n / tr.width + pos.y))) = color + n += 1 + } + return +} +/* draws a wireframe rectangle to the screen + will also be optimised later */ +rect_line := fn(buffer: Buffer, pos: Point, tr: Transform, color: ColorBGRA, thickness: int): void { + t := 0 + y := 0 + x := 0 + loop if t == thickness break else { + y = pos.y + x = pos.x + loop if y == pos.y + tr.height break else { + *(buffer.write + pos.x + t + FB_WIDTH * y) = color; + *(buffer.write + pos.x + tr.width - t + FB_WIDTH * y) = color + y += 1 + } + loop if x == pos.x + tr.width break else { + *(buffer.write + x + (pos.y + t) * FB_WIDTH) = color; + *(buffer.write + x + (pos.y + tr.height - t) * FB_WIDTH) = color + x += 1 + } + t += 1 + } + return +} + +// do not use, use line() instead +line_low := fn(buffer: Buffer, p0: Point, p1: Point, color: ColorBGRA): void { + dx := p1.x - p0.x + dy := p1.y - p0.y + yi := 1 + if dy < 0 { + yi = 0 - 1 + dy = 0 - dy + } + D := 2 * dy - dx + y := p0.y + x := p0.x + loop if x == p1.x break else { + *(buffer.write + x + y * FB_WIDTH) = color + if D > 0 { + y += yi + D += 2 * (dy - dx) + } else { + D += 2 * dy + } + x += 1 + } + return +} +// do not use, use line() instead +line_high := fn(buffer: Buffer, p0: Point, p1: Point, color: ColorBGRA): void { + dx := p1.x - p0.x + dy := p1.y - p0.y + xi := 1 + if dy < 0 { + xi = 0 - 1 + dx = 0 - dx + } + D := 2 * dx - dy + x := p0.x + y := p0.y + loop if y == p1.y break else { + *(buffer.write + x + y * FB_WIDTH) = color + if D > 0 { + x += xi + D += 2 * (dx - dy) + } else { + D += 2 * dx + } + y += 1 + } + return +} + +/* implementation of Bresenham's line algorithm + TODO: thickness, might need better math library */ +line := fn(buffer: Buffer, p0: Point, p1: Point, color: ColorBGRA, thickness: int): void { + if math.abs(p1.y - p0.y) < math.abs(p1.x - p0.x) { + if p0.x > p1.x { + line_low(buffer, p1, p0, color) + } else { + line_low(buffer, p0, p1, color) + } + } else { + if p0.y > p1.y { + line_high(buffer, p1, p0, color) + } else { + line_high(buffer, p0, p1, color) + } + } + return +} + +// theoretically draws a wireframe polygon to the screen. untested. +tri_line := fn(buffer: Buffer, p0: Point, p1: Point, p2: Point, color: ColorBGRA, thickness: int): void { + line(buffer, p0, p1, color, thickness) + line(buffer, p1, p2, color, thickness) + line(buffer, p2, p0, color, thickness) + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/examples/amogus.hb b/sysdata/programs/fb_driver/src/examples/amogus.hb new file mode 100644 index 0000000..b045f47 --- /dev/null +++ b/sysdata/programs/fb_driver/src/examples/amogus.hb @@ -0,0 +1,26 @@ +.{rect_line} := @use("../draw.hb"); +.{present, create_buffer, clear} := @use("../lib.hb") + +/* expected result: + the impostor travels left and loops around the screen */ + +example := fn(): void { + // Creates a back buffer, which we write to, to avoid screen flickering + buffer := create_buffer() + x := 0 + loop { + // draw all our shapes to the back buffer + rect_line(buffer, .(200 - x, 80), .(430, 380), .(0, 0, 255, 0), 1) + rect_line(buffer, .(630 - x, 120), .(120, 300), .(0, 0, 255, 0), 1) + rect_line(buffer, .(200 - x, 460), .(160, 270), .(0, 0, 255, 0), 1) + rect_line(buffer, .(470 - x, 460), .(160, 270), .(0, 0, 255, 0), 1) + rect_line(buffer, .(140 - x, 140), .(340, 250), .(255, 255, 0, 0), 1) + /* push the back buffer to the front buffer + this displays our image to the screen */ + present(buffer) + // erase our old image + clear(buffer, .(0, 0, 0, 0)) + x += 1 + } + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/examples/colors.hb b/sysdata/programs/fb_driver/src/examples/colors.hb new file mode 100644 index 0000000..a20f5ff --- /dev/null +++ b/sysdata/programs/fb_driver/src/examples/colors.hb @@ -0,0 +1,27 @@ +.{clear, create_buffer, present} := @use("../lib.hb"); +.{ColorBGRA} := @use("../color.hb") + +/* expected result: + the screen fades from green to cyan + then wraps back to green + note that this may happen too fast for you to notice... */ + +example := fn(): void { + // creates a back buffer, which we write to, to avoid screen flickering + buffer := create_buffer() + color := ColorBGRA.(0, 255, 0, 255) + /* have to explicitly say 0 is a u8, or we do something crazy to the colors. + looks like a compiler bug */ + n := @as(i8, @as(u8, 0)) - 1 + loop { + clear(buffer, color) + present(buffer) + /* because for some reason just comparing doesnt work. + also looks like a compiler bug */ + if (color.b & 255) == 255 | (color.b & 255) == 0 { + n = 0 - n + } + color.b += n + } + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/examples/front_buffer.hb b/sysdata/programs/fb_driver/src/examples/front_buffer.hb new file mode 100644 index 0000000..8c1bf5a --- /dev/null +++ b/sysdata/programs/fb_driver/src/examples/front_buffer.hb @@ -0,0 +1,19 @@ +.{front_buffer_ptr, front_buffer_copy, get_front_buffer, Buffer} := @use("../lib.hb"); + +example := fn(): void { + // you can get the raw frontbuffer pointer using + raw_buffer := front_buffer_ptr + // this buffer is the one that you write individual pixels to + + // you can gete the copy frontbuffer pointer using + copy_buffer := copy_buffer_ptr + /* this buffer is used for massive writing + operations by taking advantage of + static copying */ + + // you can construct a buffer like so + buffer := Buffer.{write: raw_buffer, copy: copy_buffer} + // this is the operation that get_front_buffer does + same_buffer := get_front_buffer() + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/examples/lines.hb b/sysdata/programs/fb_driver/src/examples/lines.hb new file mode 100644 index 0000000..11adde7 --- /dev/null +++ b/sysdata/programs/fb_driver/src/examples/lines.hb @@ -0,0 +1,25 @@ +.{line} := @use("../draw.hb"); +.{clear, create_buffer, present, FB_WIDTH, FB_HEIGHT, Point} := @use("../lib.hb") + +/* expected result: + a 3d-looking blue set of lines + created on a blue background */ + +example := fn(): void { + // creates a back buffer, which we write to, to avoid screen flickering + buffer := create_buffer() + // fill the screen in blue + clear(buffer, .(100, 50, 0, 255)) + p0 := Point.(0, 0 - 1) + p1 := Point.(0, FB_HEIGHT - 1) + loop if p0.y >= FB_HEIGHT break else { + // draw a line between p0 and p1 + line(buffer, p0, p1, .(255, 180, 100, 255), 3) + p0.y += FB_HEIGHT >> 6 + p1.x += FB_WIDTH >> 6 + } + /* push the back buffer to the front buffer + this displays our image to the screen */ + present(buffer) + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/examples/square.hb b/sysdata/programs/fb_driver/src/examples/square.hb new file mode 100644 index 0000000..84b3994 --- /dev/null +++ b/sysdata/programs/fb_driver/src/examples/square.hb @@ -0,0 +1,32 @@ +.{rect_line} := @use("../draw.hb"); +.{clear, create_buffer, present, Point, FB_HEIGHT, FB_WIDTH} := @use("../lib.hb") + +/* expected result: + the white outline of a square bounces around the screen */ + +example := fn(): void { + // creates a back buffer, which we write to, to avoid screen flickering + buffer := create_buffer() + vel := Point.{x: 1, y: 1} + pos := Point.{x: 100, y: 100} + loop { + // draw the square at our current position + rect_line(buffer, pos, .(100, 100), .(255, 255, 255, 255), 3) + /* push the back buffer to the front buffer + this displays our image to the screen */ + present(buffer) + // erase our old image + clear(buffer, .(0, 0, 0, 0)) + + // bounce the square if it touches the screen edges + if pos.x == 0 | pos.x == FB_WIDTH - 100 { + vel.x = 0 - vel.x + } + if pos.y == 0 | pos.y == FB_HEIGHT - 100 { + vel.y = 0 - vel.y + } + + pos += vel + } + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/examples/strobe.hb b/sysdata/programs/fb_driver/src/examples/strobe.hb new file mode 100644 index 0000000..e46653c --- /dev/null +++ b/sysdata/programs/fb_driver/src/examples/strobe.hb @@ -0,0 +1,20 @@ +.{clear, create_buffer, present} := @use("../lib.hb") + +/* expected result: (EPILEPSY WARNING) + the screen rapidly flashes red then black */ + +example := fn(): void { + // creates a back buffer, which we write to, to avoid screen flickering + buffer := create_buffer() + loop { + // screen go red + clear(buffer, .(0, 0, 255, 0)) + // show the red + present(buffer) + // screen go black + clear(buffer, .(0, 255, 255, 0)) + // show the black + present(buffer) + } + return +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/lib.hb b/sysdata/programs/fb_driver/src/lib.hb new file mode 100644 index 0000000..ddfc3e4 --- /dev/null +++ b/sysdata/programs/fb_driver/src/lib.hb @@ -0,0 +1,100 @@ +.{memory, log, math} := @use("../../../libraries/stn/src/lib.hb"); +.{ColorBGRA, blend} := @use("rel:color.hb") + +FB_WIDTH := 1024 +FB_HEIGHT := 768 +FB_PIXELS := FB_WIDTH * FB_HEIGHT +FB_BYTES := FB_PIXELS << 2 +// actual enforced max copy size is (1 << 16) - 1, but this was faster +MAX_COPY_SIZE := 6144 +// see stn.math.min, cant use here due to compiler bug (reg id leaked) +COPY_PIXELS := MAX_COPY_SIZE + (FB_BYTES - MAX_COPY_SIZE & FB_BYTES - MAX_COPY_SIZE >> 31) >> 2 +PARTITIONS := FB_PIXELS / COPY_PIXELS +TOTAL_PAGES := 1 + FB_BYTES >> 12 + +Buffer := struct {write: ^ColorBGRA, copy: ^[ColorBGRA; COPY_PIXELS]} +Point := struct {x: int, y: int} +Transform := struct {width: int, height: int} +Rect := struct {p1: Point, p2: Point} + +front_buffer_ptr := @as(^ColorBGRA, @bitcast(18446603339442421760)) +front_buffer_copy := @as(^[ColorBGRA; COPY_PIXELS], @bitcast(front_buffer_ptr)) + +get_front_buffer := fn(): Buffer { + // trying to return front_buffer_ptr or front_buffer_copy causes reg id leak + return Buffer.{write: @as(^ColorBGRA, @bitcast(18446603339442421760)), copy: @as(^[ColorBGRA; COPY_PIXELS], @bitcast(18446603339442421760))} +} +/* this is separate to create_raw_buffer because returning a Buffer from + create_raw_buffer causes reg id leak */ +create_buffer := fn(): Buffer { + ptr := create_raw_buffer() + ptr_copy := @as(^[ColorBGRA; COPY_PIXELS], @bitcast(ptr)) + // same here, bitcasting inside the struct literal causes reg id leak + buffer := Buffer.{write: ptr, copy: ptr_copy} + return buffer +} +create_raw_buffer := fn(): ^ColorBGRA { + // helps to trace allocation bugs + log.info("Creating buffer. This will allocate.\0") + if TOTAL_PAGES <= 255 { + return @bitcast(memory.request_page(TOTAL_PAGES)) + } + ptr := memory.request_page(255) + remaining := TOTAL_PAGES - 255 + loop if remaining <= 0 break else { + if remaining < 255 { + memory.request_page(remaining) + } else { + memory.request_page(255) + } + remaining -= 255 + } + return @bitcast(ptr) +} +// sets the buffer to the color. very fast. +clear := fn(buffer: Buffer, color: ColorBGRA): void { + n := 0 + // write the first pixel chunk + loop if n >= COPY_PIXELS break else { + *(buffer.write + n) = color + n += 1 + } + n = 1 + // copy that pixel chunk through the buffer, taking advantage of memory copying + loop if n >= PARTITIONS break else { + *(buffer.copy + n) = *buffer.copy + n += 1 + } + return +} +// only required to be called when using a back buffer. if using single-buffered rendering, do not call this. +present := fn(buffer: Buffer): void { + offset := 0 + // copy chunks of the read buffer to the front buffer + loop if offset >= PARTITIONS break else { + *(front_buffer_copy + offset) = *(buffer.copy + offset) + offset += 1 + } + return +} +// composites the contents of buffer1 into buffer2, accounting for alpha transparency +// i dont know if it works. i have not tested it. it probably doesnt work +composite := fn(buffer1: Buffer, buffer2: Buffer): void { + n := 0 + loop if n == FB_PIXELS break else { + bg := *(buffer2.write + n); + *(buffer2.write + n) = blend(*(buffer1.write + n), bg) + n += 1 + } + return +} +// really need to be able to inline this please - aurlex +screenidx := fn(pos: Point): int { + return pos.x + FB_WIDTH * pos.y +} +point2rect := fn(pos: Point, tr: Transform): Rect { + return .(pos, .(pos.x + tr.x, pos.y + tr.y)) +} +rect2point := fn(rect: Rect): struct {point: Point, transform: Transform} { + return .(.(0, 0), .(0, 0)) +} \ No newline at end of file diff --git a/sysdata/programs/fb_driver/src/main.hb b/sysdata/programs/fb_driver/src/main.hb index a6483dc..0068d1f 100644 --- a/sysdata/programs/fb_driver/src/main.hb +++ b/sysdata/programs/fb_driver/src/main.hb @@ -1,15 +1,7 @@ -frame_buffer := @as(^u8, @bitcast(18446603339442421760)) +// change "lines.hb" to another example to see it onscreen +example := @use("examples/lines.hb").example main := fn(): int { - color := 17 - loop { - len := 786432 * 4 + 1 - loop if len == 0 break else { - fb := frame_buffer + len; - *fb = color - len -= 1 - color += len - } - } + example() return 0 } \ No newline at end of file