From 96c2bd5cd550fb224bd9c1f6bbd3aee90f17775f Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 13 Oct 2024 23:38:43 +0100 Subject: [PATCH 1/5] bitmap image support & better mem opts restructure render lib add peony's render api additions and example add image example fix some dubious bugs i had made in unused code in mem_serve.rs remove tetris stub (now in peony's fork) update hblang --- Cargo.lock | 6 +- kernel/src/holeybytes/ecah.rs | 8 +- .../holeybytes/kernel_services/mem_serve.rs | 65 ++++-- kernel/src/holeybytes/mem.rs | 1 - repbuild/src/main.rs | 7 +- sysdata/libraries/dt_api/src/lib.hb | 2 +- sysdata/libraries/render/src/image.hb | 51 +++++ sysdata/libraries/render/src/lib.hb | 50 ++--- sysdata/libraries/render/src/software.hb | 185 ++++++++---------- sysdata/libraries/render/src/svga.hb | 36 ++-- sysdata/libraries/stn/src/memory.hb | 31 +++ .../render_example/src/examples/image.hb | 31 +++ .../render_example/src/examples/mini.bmp | Bin 0 -> 102454 bytes .../src/examples/tactical_screen.hb | 83 ++++++++ sysdata/programs/render_example/src/main.hb | 2 +- sysdata/programs/tetris/meta.toml | 11 -- sysdata/programs/tetris/src/main.hb | 15 -- 17 files changed, 373 insertions(+), 211 deletions(-) create mode 100644 sysdata/libraries/render/src/image.hb create mode 100644 sysdata/programs/render_example/src/examples/image.hb create mode 100644 sysdata/programs/render_example/src/examples/mini.bmp create mode 100644 sysdata/programs/render_example/src/examples/tactical_screen.hb delete mode 100644 sysdata/programs/tetris/meta.toml delete mode 100644 sysdata/programs/tetris/src/main.hb diff --git a/Cargo.lock b/Cargo.lock index bfd0b3e..25cd7bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,12 +350,12 @@ checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6d7e726066109e8b08f049bbc4684bba2a2eb88a" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6d7e726066109e8b08f049bbc4684bba2a2eb88a" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e" dependencies = [ "hashbrown 0.15.0", "hbbytecode", @@ -367,7 +367,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6d7e726066109e8b08f049bbc4684bba2a2eb88a" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e" dependencies = [ "hbbytecode", ] diff --git a/kernel/src/holeybytes/ecah.rs b/kernel/src/holeybytes/ecah.rs index 8d67692..0d077d3 100644 --- a/kernel/src/holeybytes/ecah.rs +++ b/kernel/src/holeybytes/ecah.rs @@ -97,7 +97,8 @@ pub fn handler(vm: &mut Vm) { match msg_type { 0 => unsafe { let size = msg_vec[1]; - let addr = u16::from_le_bytes(msg_vec[2..4].try_into().unwrap()); + let addr = + u16::from_le_bytes(msg_vec[2..4].try_into().unwrap_unchecked()); let value = match size { 0 => x86_in::(addr) as u64, 1 => x86_in::(addr) as u64, @@ -109,7 +110,8 @@ pub fn handler(vm: &mut Vm) { }, 1 => unsafe { let size = msg_vec[1]; - let addr = u16::from_le_bytes(msg_vec[2..4].try_into().unwrap()); + let addr = + u16::from_le_bytes(msg_vec[2..4].try_into().unwrap_unchecked()); // info!("Setting address {}", addr); match size { @@ -173,7 +175,7 @@ pub fn handler(vm: &mut Vm) { Ok(msg) => msg, Err(_) => return, }; - if msg.len() > max_length.try_into().unwrap() { + if msg.len() > unsafe { max_length.try_into().unwrap_unchecked() } { info!("{}", max_length); error!("Message is too long to map in."); } else { diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index 935ca9b..e99a1aa 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -32,40 +32,37 @@ pub fn memory_msg_handler( let msg_vec = block_read(mem_addr, length); let msg_type = msg_vec[0]; match msg_type { - 0 => { + 0 => unsafe { let page_count = msg_vec[1]; let mptr_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap(); let mptr: u64 = u64::from_le_bytes(mptr_raw); log::debug!("Allocating {} pages @ {:x}", page_count, mptr); - let ptr = unsafe { - alloc(Layout::from_size_align_unchecked( - page_count as usize * 4096, - 4096, - )) - }; + let ptr = alloc(Layout::from_size_align_unchecked( + page_count as usize * 4096, + 4096, + )); vm.registers[1] = hbvm::value::Value(ptr as u64); log::debug!("Kernel ptr: {:x}", ptr as u64); - } + }, - 1 => { + 1 => unsafe { let page_count = msg_vec[1]; let mptr_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap(); let mptr: u64 = u64::from_le_bytes(mptr_raw); log::debug!("Deallocating {} pages @ {:x}", page_count, mptr); - unsafe { - dealloc( - mptr as *mut u8, - Layout::from_size_align_unchecked(page_count as usize * 4096, 4096), - ) - } - } + + dealloc( + mptr as *mut u8, + Layout::from_size_align_unchecked(page_count as usize * 4096, 4096), + ) + }, 2 => { use MemoryQuotaType::*; - let quota_type = match msg_vec[0] { + let quota_type = match msg_vec[1] { 0 => NoQuota, 1 => SoftQuota, 2 => HardQuota, @@ -82,10 +79,42 @@ pub fn memory_msg_handler( ) } 3 => { - let page_count = msg_vec[0]; + let page_count = msg_vec[1]; log::debug!(" {} pages", page_count); } + // memcpy + 4 => unsafe { + let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; + let src = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as *const u8; + let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8; + src.copy_to(dest, count); + }, + // memset + 5 => unsafe { + let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; + let size = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as usize; + let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8; + let src = u64::from_le_bytes(msg_vec[25..33].try_into().unwrap_unchecked()) as *mut u8; + let total_size = count * size; + + if total_size > 32 { + core::ptr::copy(src, dest, size); + let pattern = core::slice::from_raw_parts(dest, size); + let mut offset = size; + + while offset < total_size { + let remaining = total_size - offset; + let copy_size = remaining.min(offset); + core::ptr::copy_nonoverlapping(pattern.as_ptr(), dest.add(offset), copy_size); + offset += copy_size; + } + } else { + for i in 0..total_size { + *dest.add(i) = *src.add(i % size); + } + } + }, _ => { log::debug!("Unknown memory service message type: {}", msg_type); } diff --git a/kernel/src/holeybytes/mem.rs b/kernel/src/holeybytes/mem.rs index 79a3614..94d6583 100644 --- a/kernel/src/holeybytes/mem.rs +++ b/kernel/src/holeybytes/mem.rs @@ -37,7 +37,6 @@ impl hbvm::mem::Memory for Memory { target: *mut u8, count: usize, ) -> Result<(), hbvm::mem::LoadError> { - // if addr.get() % 4096 == 0 {} core::ptr::copy_nonoverlapping(addr.get() as *const u8, target, count); Ok(()) } diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 4420c29..9963774 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -395,12 +395,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { "-parallel", "none", "-monitor", "none", "-machine", accel, - "-cpu", - if accel != "accel=tcg" { - "host" - } else { - "Broadwell-v4" - }, + "-cpu", "max", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", ]); } diff --git a/sysdata/libraries/dt_api/src/lib.hb b/sysdata/libraries/dt_api/src/lib.hb index 8f2dffe..9159c83 100644 --- a/sysdata/libraries/dt_api/src/lib.hb +++ b/sysdata/libraries/dt_api/src/lib.hb @@ -1,6 +1,6 @@ .{string} := @use("../../stn/src/lib.hb") -dt_get := fn(query: ^u8): int { +dt_get := fn($Expr: type, query: ^u8): Expr { length := string.length(query) return @eca(3, 5, query, length) } \ No newline at end of file diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb new file mode 100644 index 0000000..ee43be2 --- /dev/null +++ b/sysdata/libraries/render/src/image.hb @@ -0,0 +1,51 @@ +.{Color} := @use("./lib.hb"); +.{memory, log} := @use("../../stn/src/lib.hb") + +Image := struct { + buf: ^Color, + width: i32, + height: i32, +} + +BitmapFileHeader := packed struct { + img_type: u16, + size: u32, + reserved_1: u16, + reserved_2: u16, + offset: u32, +} + +BitmapInfoHeader := packed struct { + size: u32, + width: i32, + height: i32, + planes: u16, + bits: u16, + compression: u32, + image_size: u32, + x_resolution: i32, + y_resolution: i32, + n_colours: u32, + important_colours: u32, +} + +BitmapColorHeader := packed struct { + red_mask: u32, + green_mask: u32, + blue_mask: u32, + alpha_mask: u32, + color_space_type: u32, + unused: u32, +} + +from_bmp := fn(bmp: ^u8): Image { + file_header := @as(^BitmapFileHeader, @bitcast(bmp)) + if file_header.img_type != 0x4D42 { + log.error("failed to load bmp image: not a bmp image, idiot\0") + return @as(Image, idk) + } + info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) + bmp += file_header.offset + + return .(@bitcast(bmp), @bitcast(info_header.width), @bitcast(info_header.height)) +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/lib.hb b/sysdata/libraries/render/src/lib.hb index 5c0baed..556387c 100644 --- a/sysdata/libraries/render/src/lib.hb +++ b/sysdata/libraries/render/src/lib.hb @@ -1,5 +1,6 @@ svga := @use("svga.hb") software := @use("software.hb") +image := @use("image.hb") // default mode mode := software @@ -8,23 +9,24 @@ init := mode.init doublebuffer := mode.doublebuffer // Colours -Color := mode.Color -white := mode.white -black := mode.black -gray := mode.gray -red := mode.red -green := mode.green -yellow := mode.yellow -blue := mode.blue -magenta := mode.magenta -cyan := mode.cyan -light_gray := mode.light_gray -light_red := mode.light_red -light_green := mode.light_green -light_yellow := mode.light_yellow -light_blue := mode.light_blue -light_magenta := mode.light_magenta -light_cyan := mode.light_cyan +Color := packed struct {b: u8, g: u8, r: u8, a: u8} +Image := image.Image +white := Color.(255, 255, 255, 255) +black := Color.(0, 0, 0, 255) +gray := Color.(127, 127, 127, 255) +red := Color.(0, 0, 205, 255) +green := Color.(0, 205, 0, 255) +yellow := Color.(0, 205, 205, 255) +blue := Color.(205, 0, 0, 255) +magenta := Color.(205, 0, 205, 255) +cyan := Color.(205, 205, 0, 255) +light_gray := Color.(229, 229, 229, 255) +light_red := Color.(0, 0, 255, 255) +light_green := Color.(0, 255, 0, 255) +light_yellow := Color.(0, 255, 255, 255) +light_blue := Color.(255, 0, 0, 255) +light_magenta := Color.(255, 0, 255, 255) +light_cyan := Color.(255, 255, 0, 255) // Drawing put_pixel := mode.put_pixel @@ -32,7 +34,11 @@ put_rect := mode.put_rect put_filled_rect := mode.put_filled_rect put_line := mode.put_line clear := mode.clear -put_img := mode.put_img +put_image := mode.put_image +// thanks peony for these three! +put_trirect := mode.put_trirect +put_vline := mode.put_vline +put_hline := mode.put_hline // Display width := mode.width @@ -41,10 +47,4 @@ dimensions := mode.dimensions set_height := mode.set_height set_width := mode.set_width set_dimensions := mode.set_dimensions -sync := mode.sync - -// Trash Image -Image := struct { - start: ^Color, - end: ^Color, -} \ No newline at end of file +sync := mode.sync \ No newline at end of file diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index a89057b..01107df 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -1,62 +1,32 @@ .{math, memory} := @use("../../stn/src/lib.hb"); .{dt_get} := @use("../../dt_api/src/lib.hb"); -.{Image} := @use("lib.hb"); +.{Color, Image} := @use("lib.hb"); .{Vec2} := math -Color := struct {b: u8, g: u8, r: u8, a: u8} -white := Color.(255, 255, 255, 255) -black := Color.(0, 0, 0, 255) -gray := Color.(127, 127, 127, 255) -red := Color.(0, 0, 205, 255) -green := Color.(0, 205, 0, 255) -yellow := Color.(0, 205, 205, 255) -blue := Color.(205, 0, 0, 255) -magenta := Color.(205, 0, 205, 255) -cyan := Color.(205, 205, 0, 255) -light_gray := Color.(229, 229, 229, 255) -light_red := Color.(0, 0, 255, 255) -light_green := Color.(0, 255, 0, 255) -light_yellow := Color.(0, 255, 255, 255) -light_blue := Color.(255, 0, 0, 255) -light_magenta := Color.(255, 0, 255, 255) -light_cyan := Color.(255, 255, 0, 255) - -// might not work for some resolutions, but needs to be comptime because... -copy_pixels := 0xC000 >> 2 - ctx := @as(Context, idk) -// some of these are redudant holdovers from fb_driver -// will keep them for future work if necessary Context := struct { fb: ^Color, bb: ^Color, buf: ^Color, width: int, height: int, - partitions: int, pixels: int, - bb_pages: int, double_buffer: bool, } init := fn(): void { - width := dt_get("framebuffer/fb0/width\0") - height := dt_get("framebuffer/fb0/height\0") + width := dt_get(int, "framebuffer/fb0/width\0") + height := dt_get(int, "framebuffer/fb0/height\0") pixels := width * height - bytes := pixels << 2 - partitions := pixels / copy_pixels - pages := 1 + bytes >> 12 - back_buffer := create_back_buffer(pages) + back_buffer := memory.alloc(Color, pixels * @bitcast(@sizeof(Color))) ctx = Context.{ - fb: dt_get("framebuffer/fb0/ptr\0"), + fb: dt_get(^Color, "framebuffer/fb0/ptr\0"), bb: back_buffer, buf: back_buffer, width, height, - partitions, pixels, - bb_pages: pages, double_buffer: true, } return @@ -72,60 +42,12 @@ doublebuffer := fn(enable: bool): void { return } -create_back_buffer := fn(pages: int): ^Color { - if pages <= 0xFF { - return @bitcast(@inline(memory.request_page, pages)) - } - ptr := @inline(memory.request_page, 255) - remaining := pages - 0xFF - loop if remaining <= 0 break else { - if remaining < 0xFF { - memory.request_page(remaining) - } else { - memory.request_page(0xFF) - } - remaining -= 0xFF - } - return @bitcast(ptr) -} - clear := fn(color: Color): void { - cursor := ctx.buf - boundary := cursor + 512 - loop if cursor == boundary break else { - *cursor = color - cursor += 1 - } - boundary += 512 * 7 - loop if cursor == boundary break else { - *@as(^[Color; 512], @bitcast(cursor)) = *@as(^[Color; 512], @bitcast(ctx.buf)) - cursor += 512 - } - boundary += copy_pixels - 4096 - loop if cursor == boundary break else { - *@as(^[Color; 4096], @bitcast(cursor)) = *@as(^[Color; 4096], @bitcast(ctx.buf)) - cursor += 4096 - } - boundary += (ctx.partitions - 1) * copy_pixels - loop if cursor == boundary break else { - *@as(^[Color; copy_pixels], @bitcast(cursor)) = *@as(^[Color; copy_pixels], @bitcast(ctx.buf)) - cursor += @sizeof([u8; copy_pixels]) - } - return + return @inline(memory.set, Color, &color, ctx.buf, @bitcast(ctx.pixels)) } sync := fn(): void { - if ctx.double_buffer { - bb := ctx.buf - fb := ctx.fb - boundary := bb + ctx.pixels - loop if bb == boundary break else { - *@as(^[Color; copy_pixels], @bitcast(fb)) = *@as(^[Color; copy_pixels], @bitcast(bb)) - bb += copy_pixels - fb += copy_pixels - } - } - return + return @inline(memory.copy, Color, ctx.buf, ctx.fb, @bitcast(ctx.pixels)) } width := fn(): int { @@ -146,35 +68,25 @@ put_pixel := fn(pos: Vec2(int), color: Color): void { } put_filled_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - x := pos.x y := pos.y - end := pos + tr - loop if x == end.x break else { - loop if y == end.y break else { - *(ctx.buf + @inline(screenidx, x, y)) = color - y += 1 - } - x += 1 - y = pos.y + end_y := y + tr.y + loop if y == end_y break else { + @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, y), @bitcast(tr.x)) + y += 1 } return } put_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - x := pos.x y := pos.y - end := pos + tr - loop if y == end.y break else { - *(ctx.buf + @inline(screenidx, x, y)) = color; - *(ctx.buf + @inline(screenidx, x + tr.x, y)) = color + end_y := y + tr.y + loop if y == end_y break else { + *(ctx.buf + @inline(screenidx, pos.x, y)) = color; + *(ctx.buf + @inline(screenidx, pos.x + tr.x, y)) = color y += 1 } - y = pos.y - loop if x == end.x break else { - *(ctx.buf + @inline(screenidx, x, y)) = color; - *(ctx.buf + @inline(screenidx, x, y + tr.y)) = color - x += 1 - } + @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, y), @bitcast(tr.x)) + @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, y - tr.y), @bitcast(tr.x)) return } @@ -259,6 +171,67 @@ set_dimensions := fn(new: Vec2(int)): void { return } -put_img := fn(img: Image, pos: Vec2(int)): void { +put_image := fn(image: Image, pos: Vec2(int)): void { + y := 0 + loop if y == image.height break else { + @inline(memory.copy, Color, image.buf + y * image.width, ctx.buf + @inline(screenidx, pos.x, pos.y + image.height - y), @intcast(image.width)) + y += 1 + } + return +} + +// peony-made +put_trirect := fn(pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void { + step := Vec2(int).(1, 1) + if size.x < 0 { + step.x = -1 + } + if size.y < 0 { + step.y = size.y / size.x + } + + start_y := pos.y + target := pos + size + + loop if pos.x == target.x break else { + put_vline(pos.x, pos.y, target.y, color0) + @inline(put_vline, pos.x, pos.y, start_y, color1) + pos += step + } + + return +} + +// peony-made +put_vline := fn(x: int, y0: int, y1: int, color: Color): void { + if y1 < y0 { + tmp := y0 + y0 = y1 + y1 = tmp + } + y := y0 + + loop if y == y1 break else { + *(ctx.buf + @inline(screenidx, x, y)) = color + y += 1 + } + + return +} + +// peony-made +put_hline := fn(y: int, x0: int, x1: int, color: Color): void { + if x1 < x0 { + tmp := x0 + x0 = x1 + x1 = tmp + } + x := x0 + + loop if x == x1 break else { + *(ctx.buf + @inline(screenidx, x, y)) = color + x += 1 + } + return } \ No newline at end of file diff --git a/sysdata/libraries/render/src/svga.hb b/sysdata/libraries/render/src/svga.hb index 0790198..033d762 100644 --- a/sysdata/libraries/render/src/svga.hb +++ b/sysdata/libraries/render/src/svga.hb @@ -1,23 +1,5 @@ -.{Vec2, Image} := @use("lib.hb") -// .{pci, memory, string, log} := @use("../../stn/src/lib.hb"); - -Color := struct {b: u8, g: u8, r: u8, a: u8} -white := Color.(255, 255, 255, 255) -black := Color.(0, 0, 0, 255) -gray := Color.(127, 127, 127, 255) -red := Color.(0, 0, 205, 255) -green := Color.(0, 205, 0, 255) -yellow := Color.(0, 205, 205, 255) -blue := Color.(205, 0, 0, 255) -magenta := Color.(205, 0, 205, 255) -cyan := Color.(205, 205, 0, 255) -light_gray := Color.(229, 229, 229, 255) -light_red := Color.(0, 0, 255, 255) -light_green := Color.(0, 255, 0, 255) -light_yellow := Color.(0, 255, 255, 255) -light_blue := Color.(255, 0, 0, 255) -light_magenta := Color.(255, 0, 255, 255) -light_cyan := Color.(255, 255, 0, 255) +.{Vec2} := @use("../../stn/src/lib.hb").math; +.{Image, Color} := @use("lib.hb") clear := fn(color: Color): void { return @@ -79,6 +61,18 @@ init := fn(): void { return } -put_img := fn(img: Image, pos: Vec2(int)): void { +put_image := fn(img: Image, pos: Vec2(int)): void { + return +} + +put_trirect := fn(pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void { + return +} + +put_vline := fn(x: int, y0: int, y1: int, color: Color): void { + return +} + +put_hline := fn(y: int, x0: int, x1: int, color: Color): void { return } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index 4cbdd6d..21a0387 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -1,3 +1,24 @@ +PAGE_SIZE := 4096 +MAX_ALLOC := 0xFF + +alloc := fn($Expr: type, bytes: int): ^Expr { + pages := (1 + bytes) / PAGE_SIZE + if pages <= MAX_ALLOC { + return @bitcast(@inline(request_page, pages)) + } + ptr := @inline(request_page, 0xFF) + remaining := pages - MAX_ALLOC + loop if remaining <= 0 break else { + if remaining < MAX_ALLOC { + request_page(remaining) + } else { + request_page(MAX_ALLOC) + } + remaining -= MAX_ALLOC + } + return @bitcast(ptr) +} + request_page := fn(page_count: u8): ^u8 { msg := "\{00}\{01}xxxxxxxx\0" msg_page_count := msg + 1; @@ -36,4 +57,14 @@ outl := fn(addr: u16, value: u32): void { inl := fn(addr: u16): u32 { return @eca(3, 3, &InlMsg.(0, 2, addr), @sizeof(InlMsg)) +} + +CopyMsg := packed struct {a: u8, count: uint, src: uint, dest: ^u8} +copy := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void { + return @eca(3, 2, &CopyMsg.(4, count * @sizeof(Expr), @bitcast(src), @bitcast(dest)), @sizeof(CopyMsg)) +} + +SetMsg := packed struct {a: u8, count: uint, size: uint, dest: ^u8, src: ^u8} +set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void { + return @eca(3, 2, &SetMsg.(5, count, @sizeof(Expr), @bitcast(dest), @bitcast(src)), @sizeof(SetMsg)) } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/image.hb b/sysdata/programs/render_example/src/examples/image.hb new file mode 100644 index 0000000..6546d74 --- /dev/null +++ b/sysdata/programs/render_example/src/examples/image.hb @@ -0,0 +1,31 @@ +.{Vec2} := @use("../../../../libraries/stn/src/lib.hb").math; +render := @use("../../../../libraries/render/src/lib.hb") + +/* expected result: + a cute image bounces around the screen */ + +mini_bmp := @embed("./mini.bmp") + +example := fn(): void { + render.init() + mini := render.image.from_bmp(@bitcast(&mini_bmp)) + vel := Vec2(int).(1, 1) + pos := Vec2(int).(100, 100) + width := render.width() + height := render.height() + loop { + render.put_image(mini, pos) + render.sync() + render.clear(render.black) + + if pos.x == 0 | pos.x == width - mini.width { + vel.x = -vel.x + } + if pos.y == 0 | pos.y == height - mini.height { + vel.y = -vel.y + } + + pos += vel + } + return +} \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/mini.bmp b/sysdata/programs/render_example/src/examples/mini.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7e0b4ceec3f9c4bdab3a64aa01c75826149183ac GIT binary patch literal 102454 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 X^XDOEuT^AZWLDtJYJIDO@b~`!1F?>E literal 0 HcmV?d00001 diff --git a/sysdata/programs/render_example/src/examples/tactical_screen.hb b/sysdata/programs/render_example/src/examples/tactical_screen.hb new file mode 100644 index 0000000..4f227de --- /dev/null +++ b/sysdata/programs/render_example/src/examples/tactical_screen.hb @@ -0,0 +1,83 @@ +render := @use("../../../../libraries/render/src/lib.hb"); +.{math, random} := @use("../../../../libraries/stn/src/lib.hb") +Vec2 := math.Vec2 + +/* expected result: + a grid of green lines scrolling from the left top corner to the right bottom one + with a "target" randomly apperaing in one of them and a "seeker" "catching" it*/ + +example := fn(): void { + render.init() + + width := render.width() + height := render.height() + cell_size := 0 + range := Vec2(int).(0, 0) + if width > height { + cell_size = width / 40 + range = .(39, height / cell_size - 1) + } else { + cell_size = height / 40 + range = .(width / cell_size - 1, 39) + } + width -= 1 + height -= 1 + + scroll := 0 + target := Vec2(int).(random.range(int, 0, range.x), random.range(int, 0, range.y)) + + halfcell := cell_size / 2 + octcell := cell_size / 8 + sevenoctcell := cell_size - octcell + + seeker := Vec2(int).(random.range(int, 0, range.x), random.range(int, 0, range.y)) + + loop { + render.clear(render.black) + + target_pixel_coord := target * .(cell_size, cell_size) + .(scroll, scroll) + render.put_trirect(target_pixel_coord, .(cell_size, cell_size), render.red, render.light_red) + + render.put_hline(target_pixel_coord.y + halfcell, target_pixel_coord.x - octcell, target_pixel_coord.x - sevenoctcell, render.light_red) + render.put_hline(target_pixel_coord.y + halfcell, target_pixel_coord.x + cell_size + octcell, target_pixel_coord.x + cell_size + sevenoctcell, render.light_red) + render.put_vline(target_pixel_coord.x + halfcell, target_pixel_coord.y - octcell, target_pixel_coord.y - sevenoctcell, render.light_red) + render.put_vline(target_pixel_coord.x + halfcell, target_pixel_coord.y + cell_size + octcell, target_pixel_coord.y + cell_size + sevenoctcell, render.light_red) + + x := scroll + loop if x > width break else { + render.put_vline(x, 0, height, .(0, 127, 0, 127)) + x += cell_size + } + + y := scroll + loop if y > height break else { + render.put_hline(y, 0, width, .(0, 127, 0, 127)) + y += cell_size + } + + render.put_hline(seeker.y * cell_size + halfcell + scroll, 0, width, render.light_green) + render.put_vline(seeker.x * cell_size + halfcell + scroll, 0, height, render.light_green) + + render.sync() + + if seeker.x < target.x { + seeker.x += 1 + } else if seeker.x > target.x { + seeker.x -= 1 + } else if seeker.y < target.y { + seeker.y += 1 + } else if seeker.y > target.y { + seeker.y -= 1 + } else { + target = .(random.range(int, 0, range.x), random.range(int, 0, range.y)) + } + + scroll += 1 + if scroll > cell_size { + scroll = 0 + target += .(1, 1) + seeker += .(1, 1) + } + } + return +} \ No newline at end of file diff --git a/sysdata/programs/render_example/src/main.hb b/sysdata/programs/render_example/src/main.hb index 35b2706..1ce2d4e 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1,4 +1,4 @@ -.{example} := @use("./examples/amogus.hb") +.{example} := @use("./examples/image.hb") main := fn(): void { @inline(example) diff --git a/sysdata/programs/tetris/meta.toml b/sysdata/programs/tetris/meta.toml deleted file mode 100644 index d2a5f97..0000000 --- a/sysdata/programs/tetris/meta.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "tetris" -authors = ["peony"] - -[dependants.libraries] - -[dependants.binaries] -hblang.version = "1.0.0" - -[build] -command = "hblang src/main.hb" diff --git a/sysdata/programs/tetris/src/main.hb b/sysdata/programs/tetris/src/main.hb deleted file mode 100644 index e61324b..0000000 --- a/sysdata/programs/tetris/src/main.hb +++ /dev/null @@ -1,15 +0,0 @@ -.{memory, log, string, buffer} := @use("../../../libraries/stn/src/lib.hb") - -main := fn(): void { - storage := @as(u8, 0) - output_buffer := memory.request_page(1) - input_buffer := buffer.search("XKeyboard\0") - loop { - buffer.recv(input_buffer, &storage, 1) - if storage != 0 { - log.info(string.display_int(storage, output_buffer)) - storage = 0 - } - } - return -} \ No newline at end of file From 3af28f16660cd4e46daddd83b6c0d6fb40cd7e87 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 13 Oct 2024 23:41:17 +0100 Subject: [PATCH 2/5] use able image --- .../render_example/src/examples/able.bmp | Bin 0 -> 100534 bytes .../render_example/src/examples/image.hb | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 sysdata/programs/render_example/src/examples/able.bmp diff --git a/sysdata/programs/render_example/src/examples/able.bmp b/sysdata/programs/render_example/src/examples/able.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7de373dd62363316506fe61ed31f1084e3526180 GIT binary patch literal 100534 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 Date: Mon, 14 Oct 2024 01:31:23 +0100 Subject: [PATCH 3/5] optimisations --- .../holeybytes/kernel_services/mem_serve.rs | 117 ++++++++++++++++-- known_bugs.md | 2 + sysdata/libraries/render/src/software.hb | 52 +++++--- 3 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 known_bugs.md diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index e99a1aa..2c01f0d 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -24,6 +24,7 @@ fn alloc_page(vm: &mut Vm, _mem_addr: u64, _length: usize) -> Result<(), MemoryS Ok(()) } +#[inline(always)] pub fn memory_msg_handler( vm: &mut Vm, mem_addr: u64, @@ -82,32 +83,124 @@ pub fn memory_msg_handler( let page_count = msg_vec[1]; log::debug!(" {} pages", page_count); } - // memcpy + // trash but fast memcpy 4 => unsafe { let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; let src = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as *const u8; let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8; - src.copy_to(dest, count); + + let mut src_ptr = src; + let mut dest_ptr = dest; + let mut remaining = count; + + while (dest_ptr as usize) & 7 != 0 && remaining > 0 { + *dest_ptr = *src_ptr; + src_ptr = src_ptr.add(1); + dest_ptr = dest_ptr.add(1); + remaining -= 1; + } + + let mut src_ptr_64 = src_ptr as *const u64; + let mut dest_ptr_64 = dest_ptr as *mut u64; + while remaining >= 64 { + let (s1, s2, s3, s4, s5, s6, s7, s8) = ( + *src_ptr_64, + *src_ptr_64.add(1), + *src_ptr_64.add(2), + *src_ptr_64.add(3), + *src_ptr_64.add(4), + *src_ptr_64.add(5), + *src_ptr_64.add(6), + *src_ptr_64.add(7), + ); + *dest_ptr_64 = s1; + *dest_ptr_64.add(1) = s2; + *dest_ptr_64.add(2) = s3; + *dest_ptr_64.add(3) = s4; + *dest_ptr_64.add(4) = s5; + *dest_ptr_64.add(5) = s6; + *dest_ptr_64.add(6) = s7; + *dest_ptr_64.add(7) = s8; + src_ptr_64 = src_ptr_64.add(8); + dest_ptr_64 = dest_ptr_64.add(8); + remaining -= 64; + } + + while remaining >= 8 { + *dest_ptr_64 = *src_ptr_64; + src_ptr_64 = src_ptr_64.add(1); + dest_ptr_64 = dest_ptr_64.add(1); + remaining -= 8; + } + + src_ptr = src_ptr_64 as *const u8; + dest_ptr = dest_ptr_64 as *mut u8; + for _ in 0..remaining { + *dest_ptr = *src_ptr; + src_ptr = src_ptr.add(1); + dest_ptr = dest_ptr.add(1); + } }, - // memset + + // trash but fast memset 5 => unsafe { let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; let size = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as usize; let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8; - let src = u64::from_le_bytes(msg_vec[25..33].try_into().unwrap_unchecked()) as *mut u8; + let src = + u64::from_le_bytes(msg_vec[25..33].try_into().unwrap_unchecked()) as *const u8; let total_size = count * size; if total_size > 32 { - core::ptr::copy(src, dest, size); - let pattern = core::slice::from_raw_parts(dest, size); - let mut offset = size; + let mut pattern_512 = [0u8; 64]; + for i in 0..64 { + pattern_512[i] = *src.add(i % size); + } + let pattern_512_ptr = pattern_512.as_ptr() as *const u64; - while offset < total_size { - let remaining = total_size - offset; - let copy_size = remaining.min(offset); - core::ptr::copy_nonoverlapping(pattern.as_ptr(), dest.add(offset), copy_size); - offset += copy_size; + let mut dest_ptr = dest; + let mut remaining = total_size; + + while (dest_ptr as usize) & 7 != 0 && remaining > 0 { + *dest_ptr = *src; + dest_ptr = dest_ptr.add(1); + remaining -= 1; + } + + let mut dest_ptr_64 = dest_ptr as *mut u64; + while remaining >= 64 { + let (p1, p2, p3, p4, p5, p6, p7, p8) = ( + *pattern_512_ptr, + *pattern_512_ptr.add(1), + *pattern_512_ptr.add(2), + *pattern_512_ptr.add(3), + *pattern_512_ptr.add(4), + *pattern_512_ptr.add(5), + *pattern_512_ptr.add(6), + *pattern_512_ptr.add(7), + ); + *dest_ptr_64 = p1; + *dest_ptr_64.add(1) = p2; + *dest_ptr_64.add(2) = p3; + *dest_ptr_64.add(3) = p4; + *dest_ptr_64.add(4) = p5; + *dest_ptr_64.add(5) = p6; + *dest_ptr_64.add(6) = p7; + *dest_ptr_64.add(7) = p8; + dest_ptr_64 = dest_ptr_64.add(8); + remaining -= 64; + } + + while remaining >= 8 { + *dest_ptr_64 = *pattern_512_ptr; + dest_ptr_64 = dest_ptr_64.add(1); + remaining -= 8; + } + + dest_ptr = dest_ptr_64 as *mut u8; + for i in 0..remaining { + *dest_ptr.add(i) = *src.add(i % size); } } else { for i in 0..total_size { diff --git a/known_bugs.md b/known_bugs.md new file mode 100644 index 0000000..bdb8fca --- /dev/null +++ b/known_bugs.md @@ -0,0 +1,2 @@ +# i did not know where to put this +- memcpy / memset cause crash on debug builds due to ptr misalignment that is not present on release builds \ No newline at end of file diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 01107df..32c1c1d 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -68,25 +68,32 @@ put_pixel := fn(pos: Vec2(int), color: Color): void { } put_filled_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - y := pos.y - end_y := y + tr.y - loop if y == end_y break else { - @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, y), @bitcast(tr.x)) - y += 1 + start_idx := @inline(screenidx, pos.x, pos.y) + end_idx := @inline(screenidx, pos.x, pos.y + tr.y) + + loop if start_idx >= end_idx break else { + @inline(memory.set, Color, &color, ctx.buf + start_idx, @bitcast(tr.x)) + start_idx += ctx.width } + return } put_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - y := pos.y - end_y := y + tr.y - loop if y == end_y break else { - *(ctx.buf + @inline(screenidx, pos.x, y)) = color; - *(ctx.buf + @inline(screenidx, pos.x + tr.x, y)) = color - y += 1 + start_idx := @inline(screenidx, pos.x, pos.y) + end_idx := @inline(screenidx, pos.x, pos.y + tr.y) + right_start_idx := @inline(screenidx, pos.x + tr.x, pos.y) + + loop if start_idx > end_idx break else { + *(ctx.buf + start_idx) = color; + *(ctx.buf + right_start_idx) = color + start_idx += ctx.width + right_start_idx += ctx.width } - @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, y), @bitcast(tr.x)) - @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, y - tr.y), @bitcast(tr.x)) + + @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, pos.y), @bitcast(tr.x + 1)) + @inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, pos.y + tr.y), @bitcast(tr.x + 1)) + return } @@ -172,10 +179,21 @@ set_dimensions := fn(new: Vec2(int)): void { } put_image := fn(image: Image, pos: Vec2(int)): void { - y := 0 - loop if y == image.height break else { - @inline(memory.copy, Color, image.buf + y * image.width, ctx.buf + @inline(screenidx, pos.x, pos.y + image.height - y), @intcast(image.width)) - y += 1 + // y := 0 + // loop if y == image.height break else { + // @inline(memory.copy, Color, image.buf + y * image.width, ctx.buf + @inline(screenidx, pos.x, pos.y + image.height - y), @intcast(image.width)) + // y += 1 + // } + // return + + start_idx := @inline(screenidx, pos.x, pos.y) + end_idx := @inline(screenidx, pos.x, pos.y + image.height) + cursor := image.width * image.height + + loop if start_idx >= end_idx break else { + @inline(memory.copy, Color, image.buf + cursor, ctx.buf + start_idx, @intcast(image.width)) + start_idx += ctx.width + cursor -= image.width } return } From 6fa1c829fd110b4f16dc42c451b77c0a43a88605 Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 13 Oct 2024 22:34:33 -0500 Subject: [PATCH 4/5] Horizon work --- sysdata/libraries/horizon_api/src/lib.hb | 12 +++++++++--- sysdata/libraries/horizon_api/src/widgets.hb | 20 ++++++++++++++++++++ sysdata/libraries/ignim/src/application.hb | 2 +- sysdata/libraries/intouch/src/keycodes.hb | 2 +- sysdata/system_config.toml | 8 ++++---- 5 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 sysdata/libraries/horizon_api/src/widgets.hb diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index c3a53ff..cc374ca 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -3,12 +3,16 @@ stn := @use("../../stn/src/lib.hb"); input := @use("../../intouch/src/lib.hb") +widgets := @use("widgets.hb") + WindowID := struct { host_id: int, window_id: int, } -create_window := fn(channel: int): void { +VoidWindowID := WindowID.(0, 0) + +create_window := fn(channel: int): WindowID { // get the horizon buffer // request a new window and provide the callback buffer // wait to recieve a message @@ -16,10 +20,12 @@ create_window := fn(channel: int): void { windowing_system_buffer := buffer.search("XHorizon\0") if windowing_system_buffer == 0 { - return + return VoidWindowID } else { msg := "\{01}\0" msg_length := 2 - return @eca(3, windowing_system_buffer, msg, msg_length) + + @as(void, @eca(3, windowing_system_buffer, msg, msg_length)) + return WindowID.(1, 0) } } \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets.hb new file mode 100644 index 0000000..04574f2 --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets.hb @@ -0,0 +1,20 @@ +// Widget types + +// End types + +LayoutChildHorizontalFirst := 0 +LayoutChildVerticalFirst := 1 + +Size := struct { + min_width: int, + max_width: int, + min_height: int, + max_height: int, +} + +Widget := struct { + size: Size, + clickable: bool, + layout: u8, + a: bool, +} \ No newline at end of file diff --git a/sysdata/libraries/ignim/src/application.hb b/sysdata/libraries/ignim/src/application.hb index f3f22d8..4837740 100644 --- a/sysdata/libraries/ignim/src/application.hb +++ b/sysdata/libraries/ignim/src/application.hb @@ -7,7 +7,7 @@ ApplicationInfo := struct { pNext: ^int, application_name: ^u8, application_version: int, - engine_name: int, + engine_name: ^u8, engine_version: int, api_version: int, } diff --git a/sysdata/libraries/intouch/src/keycodes.hb b/sysdata/libraries/intouch/src/keycodes.hb index b5fbc3d..f0273bd 100644 --- a/sysdata/libraries/intouch/src/keycodes.hb +++ b/sysdata/libraries/intouch/src/keycodes.hb @@ -64,4 +64,4 @@ RightSuper := KeyCode.(312) Mode := KeyCode.(313) /* Multi-key compose key */ -Compose := KeyCode.(314) +Compose := KeyCode.(314) \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index e4bd86f..b05a036 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -35,11 +35,11 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver_test] # path = "boot:///serial_driver_test.hbf" -# [boot.limine.ableos.modules.horizon] -# path = "boot:///horizon.hbf" +[boot.limine.ableos.modules.horizon] +path = "boot:///horizon.hbf" -# [boot.limine.ableos.modules.horizon_testing_program] -# path = "boot:///horizon_testing_program.hbf" +[boot.limine.ableos.modules.horizon_testing_program] +path = "boot:///horizon_testing_program.hbf" # [boot.limine.ableos.modules.dt_buffer_test] # path = "boot:///dt_buffer_test.hbf" From 1eee33ce8b280e53a75fac56b83ba0bdd8c4ad08 Mon Sep 17 00:00:00 2001 From: koniifer Date: Mon, 14 Oct 2024 18:54:53 +0100 Subject: [PATCH 5/5] fix icky pointer misalignment move dt_api to stn for ease of use do away with now redundant strobe example (colour example is basically strobe now) --- .../holeybytes/kernel_services/mem_serve.rs | 185 +++++++----------- sysdata/libraries/dt_api/README.md | 1 - sysdata/libraries/dt_api/src/lib.hb | 6 - sysdata/libraries/render/src/software.hb | 9 +- sysdata/libraries/stn/src/dt.hb | 5 + sysdata/libraries/stn/src/lib.hb | 4 +- sysdata/libraries/stn/src/memory.hb | 4 +- sysdata/programs/dt_buffer_test/src/main.hb | 9 +- .../render_example/src/examples/strobe.hb | 15 -- sysdata/programs/svga_driver/src/device.hb | 4 +- sysdata/system_config.toml | 10 +- 11 files changed, 96 insertions(+), 156 deletions(-) delete mode 100644 sysdata/libraries/dt_api/README.md delete mode 100644 sysdata/libraries/dt_api/src/lib.hb create mode 100644 sysdata/libraries/stn/src/dt.hb delete mode 100644 sysdata/programs/render_example/src/examples/strobe.hb diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index 2c01f0d..f08a81f 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -24,6 +24,74 @@ fn alloc_page(vm: &mut Vm, _mem_addr: u64, _length: usize) -> Result<(), MemoryS Ok(()) } +#[inline(always)] +unsafe fn memcpy(mut dest: *mut u8, mut src: *const u8, mut count: usize) { + if count < 16 { + src.copy_to_nonoverlapping(dest, count); + return; + } + + // let dest_misalignment = dest as usize & 7; + // if dest_misalignment != 0 { + // let align_bytes = 8 - dest_misalignment; + // src.copy_to_nonoverlapping(dest, align_bytes); + // dest = dest.add(align_bytes); + // src = src.add(align_bytes); + // count -= align_bytes; + // } + + while count >= 8 { + if (src as usize) & 7 == 0 && (dest as usize) & 7 == 0 { + *(dest as *mut u64) = *(src as *const u64); + } else { + src.copy_to_nonoverlapping(dest, 8); + } + dest = dest.add(8); + src = src.add(8); + count -= 8; + } + + if count > 0 { + src.copy_to_nonoverlapping(dest, count); + } +} + +#[inline(always)] +unsafe fn memset(mut dest: *mut u8, src: *const u8, count: usize, size: usize) { + let mut remaining = count * size; + + if remaining < 16 { + src.copy_to_nonoverlapping(dest, remaining); + return; + } + + let mut buffer = [0u8; 64]; + let mut buffer_size = size; + src.copy_to_nonoverlapping(buffer.as_mut_ptr(), size); + + while buffer_size * 2 <= 64 { + buffer + .as_mut_ptr() + .copy_to_nonoverlapping(buffer.as_mut_ptr().add(buffer_size), buffer_size); + buffer_size *= 2; + } + + let buffer_ptr = buffer.as_ptr() as *const u64; + while remaining >= 8 { + if (dest as usize) & 7 == 0 { + *(dest as *mut u64) = *buffer_ptr; + } else { + buffer.as_ptr().copy_to_nonoverlapping(dest, 8); + } + dest = dest.add(8); + remaining -= 8; + } + + if remaining > 0 { + buffer.as_ptr().copy_to_nonoverlapping(dest, remaining); + } +} + #[inline(always)] pub fn memory_msg_handler( vm: &mut Vm, @@ -83,130 +151,21 @@ pub fn memory_msg_handler( let page_count = msg_vec[1]; log::debug!(" {} pages", page_count); } - // trash but fast memcpy 4 => unsafe { let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; let src = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as *const u8; let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8; - let mut src_ptr = src; - let mut dest_ptr = dest; - let mut remaining = count; - - while (dest_ptr as usize) & 7 != 0 && remaining > 0 { - *dest_ptr = *src_ptr; - src_ptr = src_ptr.add(1); - dest_ptr = dest_ptr.add(1); - remaining -= 1; - } - - let mut src_ptr_64 = src_ptr as *const u64; - let mut dest_ptr_64 = dest_ptr as *mut u64; - while remaining >= 64 { - let (s1, s2, s3, s4, s5, s6, s7, s8) = ( - *src_ptr_64, - *src_ptr_64.add(1), - *src_ptr_64.add(2), - *src_ptr_64.add(3), - *src_ptr_64.add(4), - *src_ptr_64.add(5), - *src_ptr_64.add(6), - *src_ptr_64.add(7), - ); - *dest_ptr_64 = s1; - *dest_ptr_64.add(1) = s2; - *dest_ptr_64.add(2) = s3; - *dest_ptr_64.add(3) = s4; - *dest_ptr_64.add(4) = s5; - *dest_ptr_64.add(5) = s6; - *dest_ptr_64.add(6) = s7; - *dest_ptr_64.add(7) = s8; - src_ptr_64 = src_ptr_64.add(8); - dest_ptr_64 = dest_ptr_64.add(8); - remaining -= 64; - } - - while remaining >= 8 { - *dest_ptr_64 = *src_ptr_64; - src_ptr_64 = src_ptr_64.add(1); - dest_ptr_64 = dest_ptr_64.add(1); - remaining -= 8; - } - - src_ptr = src_ptr_64 as *const u8; - dest_ptr = dest_ptr_64 as *mut u8; - for _ in 0..remaining { - *dest_ptr = *src_ptr; - src_ptr = src_ptr.add(1); - dest_ptr = dest_ptr.add(1); - } + memcpy(dest, src, count); }, - - // trash but fast memset 5 => unsafe { let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; let size = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as usize; - let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8; let src = - u64::from_le_bytes(msg_vec[25..33].try_into().unwrap_unchecked()) as *const u8; + u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *const u8; + let dest = u64::from_le_bytes(msg_vec[25..33].try_into().unwrap_unchecked()) as *mut u8; - let total_size = count * size; - - if total_size > 32 { - let mut pattern_512 = [0u8; 64]; - for i in 0..64 { - pattern_512[i] = *src.add(i % size); - } - let pattern_512_ptr = pattern_512.as_ptr() as *const u64; - - let mut dest_ptr = dest; - let mut remaining = total_size; - - while (dest_ptr as usize) & 7 != 0 && remaining > 0 { - *dest_ptr = *src; - dest_ptr = dest_ptr.add(1); - remaining -= 1; - } - - let mut dest_ptr_64 = dest_ptr as *mut u64; - while remaining >= 64 { - let (p1, p2, p3, p4, p5, p6, p7, p8) = ( - *pattern_512_ptr, - *pattern_512_ptr.add(1), - *pattern_512_ptr.add(2), - *pattern_512_ptr.add(3), - *pattern_512_ptr.add(4), - *pattern_512_ptr.add(5), - *pattern_512_ptr.add(6), - *pattern_512_ptr.add(7), - ); - *dest_ptr_64 = p1; - *dest_ptr_64.add(1) = p2; - *dest_ptr_64.add(2) = p3; - *dest_ptr_64.add(3) = p4; - *dest_ptr_64.add(4) = p5; - *dest_ptr_64.add(5) = p6; - *dest_ptr_64.add(6) = p7; - *dest_ptr_64.add(7) = p8; - dest_ptr_64 = dest_ptr_64.add(8); - remaining -= 64; - } - - while remaining >= 8 { - *dest_ptr_64 = *pattern_512_ptr; - dest_ptr_64 = dest_ptr_64.add(1); - remaining -= 8; - } - - dest_ptr = dest_ptr_64 as *mut u8; - for i in 0..remaining { - *dest_ptr.add(i) = *src.add(i % size); - } - } else { - for i in 0..total_size { - *dest.add(i) = *src.add(i % size); - } - } + memset(dest, src, count, size); }, _ => { log::debug!("Unknown memory service message type: {}", msg_type); diff --git a/sysdata/libraries/dt_api/README.md b/sysdata/libraries/dt_api/README.md deleted file mode 100644 index 61d4547..0000000 --- a/sysdata/libraries/dt_api/README.md +++ /dev/null @@ -1 +0,0 @@ -# dt_api \ No newline at end of file diff --git a/sysdata/libraries/dt_api/src/lib.hb b/sysdata/libraries/dt_api/src/lib.hb deleted file mode 100644 index 9159c83..0000000 --- a/sysdata/libraries/dt_api/src/lib.hb +++ /dev/null @@ -1,6 +0,0 @@ -.{string} := @use("../../stn/src/lib.hb") - -dt_get := fn($Expr: type, query: ^u8): Expr { - length := string.length(query) - return @eca(3, 5, query, length) -} \ No newline at end of file diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 32c1c1d..aa83562 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -1,5 +1,4 @@ -.{math, memory} := @use("../../stn/src/lib.hb"); -.{dt_get} := @use("../../dt_api/src/lib.hb"); +.{math, memory, dt} := @use("../../stn/src/lib.hb"); .{Color, Image} := @use("lib.hb"); .{Vec2} := math @@ -16,12 +15,12 @@ Context := struct { } init := fn(): void { - width := dt_get(int, "framebuffer/fb0/width\0") - height := dt_get(int, "framebuffer/fb0/height\0") + width := dt.get(int, "framebuffer/fb0/width\0") + height := dt.get(int, "framebuffer/fb0/height\0") pixels := width * height back_buffer := memory.alloc(Color, pixels * @bitcast(@sizeof(Color))) ctx = Context.{ - fb: dt_get(^Color, "framebuffer/fb0/ptr\0"), + fb: dt.get(^Color, "framebuffer/fb0/ptr\0"), bb: back_buffer, buf: back_buffer, width, diff --git a/sysdata/libraries/stn/src/dt.hb b/sysdata/libraries/stn/src/dt.hb new file mode 100644 index 0000000..160d537 --- /dev/null +++ b/sysdata/libraries/stn/src/dt.hb @@ -0,0 +1,5 @@ +.{string} := @use("../../stn/src/lib.hb") + +get := fn($Expr: type, query: ^u8): Expr { + return @eca(3, 5, query, @inline(string.length, query)) +} \ No newline at end of file diff --git a/sysdata/libraries/stn/src/lib.hb b/sysdata/libraries/stn/src/lib.hb index 09ff85d..6c67d45 100644 --- a/sysdata/libraries/stn/src/lib.hb +++ b/sysdata/libraries/stn/src/lib.hb @@ -1,9 +1,9 @@ acs := @use("acs.hb") - string := @use("string.hb") log := @use("log.hb") memory := @use("memory.hb") buffer := @use("buffer.hb") math := @use("math.hb") random := @use("random.hb") -file := @use("file_io.hb") \ No newline at end of file +file := @use("file_io.hb") +dt := @use("dt.hb") \ No newline at end of file diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index 21a0387..9167b32 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -64,7 +64,7 @@ copy := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void { return @eca(3, 2, &CopyMsg.(4, count * @sizeof(Expr), @bitcast(src), @bitcast(dest)), @sizeof(CopyMsg)) } -SetMsg := packed struct {a: u8, count: uint, size: uint, dest: ^u8, src: ^u8} +SetMsg := packed struct {a: u8, count: uint, size: uint, src: ^u8, dest: ^u8} set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void { - return @eca(3, 2, &SetMsg.(5, count, @sizeof(Expr), @bitcast(dest), @bitcast(src)), @sizeof(SetMsg)) + return @eca(3, 2, &SetMsg.(5, count, @sizeof(Expr), @bitcast(src), @bitcast(dest)), @sizeof(SetMsg)) } \ No newline at end of file diff --git a/sysdata/programs/dt_buffer_test/src/main.hb b/sysdata/programs/dt_buffer_test/src/main.hb index 4d4d14c..ce42ef0 100644 --- a/sysdata/programs/dt_buffer_test/src/main.hb +++ b/sysdata/programs/dt_buffer_test/src/main.hb @@ -1,14 +1,13 @@ -dt_api := @use("../../../libraries/dt_api/src/lib.hb"); -.{dt_get} := dt_api +.{dt} := @use("../../../libraries/stn/src/lib.hb") main := fn(): int { - dt_api.dt_get("framebuffer/fb0/width\0") - dt_api.dt_get("cpu/cpu0/architecture\0") + dt.get(int, "framebuffer/fb0/width\0") + dt.get(int, "cpu/cpu0/architecture\0") // Checking if the first detected serial port is memory mapped or port mapped // 0 -> memory mapped // 1 -> port mapped - dt_get("serial_ports/sp0/mapping\0") + dt.get(int, "serial_ports/sp0/mapping\0") return 0 } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/strobe.hb b/sysdata/programs/render_example/src/examples/strobe.hb deleted file mode 100644 index 4681cc5..0000000 --- a/sysdata/programs/render_example/src/examples/strobe.hb +++ /dev/null @@ -1,15 +0,0 @@ -render := @use("../../../../libraries/render/src/lib.hb") - -/* expected result: (EPILEPSY WARNING) - the screen rapidly flashes red then black */ - -example := fn(): void { - render.init() - loop { - render.clear(render.red) - render.sync() - render.clear(render.yellow) - render.sync() - } - return -} \ No newline at end of file diff --git a/sysdata/programs/svga_driver/src/device.hb b/sysdata/programs/svga_driver/src/device.hb index daf98e4..741d9e4 100644 --- a/sysdata/programs/svga_driver/src/device.hb +++ b/sysdata/programs/svga_driver/src/device.hb @@ -2,9 +2,7 @@ stn := @use("../../../libraries/stn/src/lib.hb"); .{string, memory, buffer, log} := stn pci := @use("../../../libraries/pci/src/lib.hb"); -.{PCIAddress, get_ids, config_read32} := pci; - -.{dt_get} := @use("../../../libraries/dt_api/src/lib.hb") +.{PCIAddress, get_ids, config_read32} := pci reg := @use("./reg.hb") diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index b05a036..1792d68 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -3,11 +3,12 @@ default_entry = 1 timeout = 0 verbose = false +# interface_resolution = "1920x1080x24" interface_resolution = "1024x768x24" -# interface_resolution = "640x480x32" +# interface_resolution = "640x480x24" # Terminal related settings -term_wallpaper = "boot:///background.bmp" -# term_wallpaper = "boot:///empty-background.bmp" +# term_wallpaper = "boot:///background.bmp" +term_wallpaper = "boot:///empty-background.bmp" term_background = "008080" [boot.limine.ableos] @@ -15,8 +16,9 @@ comment = "Default AbleOS boot entry." protocol = "limine" kernel_path = "boot:///kernel_${ARCH}" kernel_cmdline = "" -# resolution = "640x480x32" +# resolution = "1920x1080x24" resolution = "1024x768x24" +# resolution = "640x480x24" [boot.limine.ableos.modules]