From 96c2bd5cd550fb224bd9c1f6bbd3aee90f17775f Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 13 Oct 2024 23:38:43 +0100 Subject: [PATCH 01/66] 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 bfd0b3e1..25cd7bfc 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 8d676927..0d077d36 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 935ca9ba..e99a1aa4 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 79a36149..94d65833 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 4420c29e..9963774e 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 8f2dffe3..9159c839 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 00000000..ee43be28 --- /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 5c0baed8..556387c6 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 a89057b7..01107dff 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 0790198e..033d7626 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 4cbdd6d8..21a0387f 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 00000000..6546d747 --- /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 00000000..4f227de2 --- /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 35b2706e..1ce2d4e6 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 d2a5f974..00000000 --- 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 e61324b3..00000000 --- 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 02/66] 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 03/66] 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 e99a1aa4..2c01f0d3 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 00000000..bdb8fca0 --- /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 01107dff..32c1c1df 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 04/66] 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 c3a53ff6..cc374caf 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 00000000..04574f28 --- /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 f3f22d83..4837740d 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 b5fbc3dc..f0273bd5 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 e4bd86f7..b05a036e 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 05/66] 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 2c01f0d3..f08a81f6 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 61d4547f..00000000 --- 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 9159c839..00000000 --- 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 32c1c1df..aa83562f 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 00000000..160d5376 --- /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 09ff85dd..6c67d451 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 21a0387f..9167b327 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 4d4d14c4..ce42ef02 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 4681cc5e..00000000 --- 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 daf98e46..741d9e4a 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 b05a036e..1792d684 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] From 2a082d82837e7cb083d99a067f348ea5d1a10b44 Mon Sep 17 00:00:00 2001 From: koniifer Date: Tue, 15 Oct 2024 01:24:29 +0100 Subject: [PATCH 06/66] rudimentary render surfaces --- Cargo.lock | 10 +- sysdata/libraries/render/src/image.hb | 37 ++- sysdata/libraries/render/src/lib.hb | 45 +++- sysdata/libraries/render/src/software.hb | 236 +++++++++--------- sysdata/libraries/render/src/svga.hb | 78 ------ sysdata/libraries/stn/src/memory.hb | 2 +- .../render_example/src/examples/able.bmp | Bin 100534 -> 102454 bytes .../render_example/src/examples/amogus.hb | 16 +- .../render_example/src/examples/colors.hb | 6 +- .../render_example/src/examples/image.hb | 26 +- .../render_example/src/examples/lines.hb | 20 +- .../render_example/src/examples/random.hb | 7 +- .../render_example/src/examples/square.hb | 14 +- .../render_example/src/examples/surface.hb | 39 +++ .../render_example/src/examples/svga.hb | 8 - .../src/examples/tactical_screen.hb | 28 +-- sysdata/system_config.toml | 8 +- 17 files changed, 293 insertions(+), 287 deletions(-) delete mode 100644 sysdata/libraries/render/src/svga.hb create mode 100644 sysdata/programs/render_example/src/examples/surface.hb delete mode 100644 sysdata/programs/render_example/src/examples/svga.hb diff --git a/Cargo.lock b/Cargo.lock index 25cd7bfc..bc371f91 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#c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#ea736d88244ce1d85999d7ce6387a63c655b7000" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#ea736d88244ce1d85999d7ce6387a63c655b7000" 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#c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#ea736d88244ce1d85999d7ce6387a63c655b7000" dependencies = [ "hbbytecode", ] @@ -985,9 +985,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb index ee43be28..1f55cbd6 100644 --- a/sysdata/libraries/render/src/image.hb +++ b/sysdata/libraries/render/src/image.hb @@ -1,11 +1,5 @@ -.{Color} := @use("./lib.hb"); -.{memory, log} := @use("../../stn/src/lib.hb") - -Image := struct { - buf: ^Color, - width: i32, - height: i32, -} +.{Color, Surface} := @use("./lib.hb"); +.{log} := @use("../../stn/src/lib.hb") BitmapFileHeader := packed struct { img_type: u16, @@ -38,14 +32,35 @@ BitmapColorHeader := packed struct { unused: u32, } -from_bmp := fn(bmp: ^u8): Image { +surface_from_bmp := fn(bmp: ^u8): Surface { 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) + return @as(Surface, idk) } info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) bmp += file_header.offset - return .(@bitcast(bmp), @bitcast(info_header.width), @bitcast(info_header.height)) + a := 0 + px := info_header.width * info_header.height + ptr := @as(^Color, @bitcast(bmp)) + tmp := @as(Color, idk) + row := 0 + + loop if row == info_header.height / 2 break else { + col := 0 + loop if col == info_header.width break else { + top_index := row * info_header.width + col + bottom_index := (info_header.height - 1 - row) * info_header.width + col + + tmp = *(ptr + top_index); + *(ptr + top_index) = *(ptr + bottom_index); + *(ptr + bottom_index) = tmp + + col += 1 + } + row += 1 + } + + return .(@bitcast(bmp), @intcast(info_header.width), @intcast(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 556387c6..3bb56c3c 100644 --- a/sysdata/libraries/render/src/lib.hb +++ b/sysdata/libraries/render/src/lib.hb @@ -1,4 +1,4 @@ -svga := @use("svga.hb") +.{memory} := @use("../../stn/src/lib.hb") software := @use("software.hb") image := @use("image.hb") @@ -8,9 +8,36 @@ mode := software init := mode.init doublebuffer := mode.doublebuffer +Surface := struct { + buf: ^Color, + width: int, + height: int, +} + +new_surface := fn(width: int, height: int): Surface { + return .( + @inline(memory.alloc, Color, width * height * @bitcast(@sizeof(Color))), + width, + height, + ) +} + +surface_from_ptr := fn(ptr: ^Color, width: int, height: int): Surface { + return .( + ptr, + width, + height, + ) +} + +clone_surface := fn(surface: Surface): Surface { + new := new_surface(surface.width, surface.height) + @inline(memory.copy, Color, surface.buf, new.buf, @intcast(surface.width * surface.height)) + return new +} + // Colours 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) @@ -34,17 +61,17 @@ put_rect := mode.put_rect put_filled_rect := mode.put_filled_rect put_line := mode.put_line clear := mode.clear -put_image := mode.put_image +put_surface := mode.put_surface // thanks peony for these three! put_trirect := mode.put_trirect put_vline := mode.put_vline put_hline := mode.put_hline // Display -width := mode.width -height := mode.height -dimensions := mode.dimensions -set_height := mode.set_height -set_width := mode.set_width -set_dimensions := mode.set_dimensions +// width := mode.width +// height := mode.height +// dimensions := mode.dimensions +// set_height := mode.set_height +// set_width := mode.set_width +// set_dimensions := mode.set_dimensions 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 aa83562f..3dcbd09f 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -1,102 +1,109 @@ .{math, memory, dt} := @use("../../stn/src/lib.hb"); -.{Color, Image} := @use("lib.hb"); +.{Color, Surface, new_surface} := @use("lib.hb"); .{Vec2} := math -ctx := @as(Context, idk) +framebuffer := @as(^Color, idk) -Context := struct { - fb: ^Color, - bb: ^Color, - buf: ^Color, - width: int, - height: int, - pixels: int, - double_buffer: bool, -} +// init := fn(): void { +// 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"), +// bb: back_buffer, +// buf: back_buffer, +// width, +// height, +// pixels, +// double_buffer: true, +// } +// return +// } -init := fn(): void { +init := fn(double_buffer: bool): Surface { + framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\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"), - bb: back_buffer, - buf: back_buffer, - width, - height, - pixels, - double_buffer: true, - } - return -} - -doublebuffer := fn(enable: bool): void { - if enable { - ctx.buf = ctx.bb + if double_buffer { + return new_surface(width, height) } else { - ctx.buf = ctx.fb + return .(framebuffer, width, height) } - ctx.double_buffer = enable +} + +clear := fn(surface: Surface, color: Color): void { + return @inline(memory.set, Color, &color, surface.buf, @bitcast(surface.width * surface.height)) +} + +sync := fn(surface: Surface): void { + return @inline(memory.copy, Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height)) +} + +screenidx := fn(surface: Surface, x: int, y: int): int { + return x + surface.width * y +} + +put_pixel := fn(surface: Surface, pos: Vec2(int), color: Color): void { + *(surface.buf + @inline(screenidx, surface, pos.x, pos.y)) = color return } -clear := fn(color: Color): void { - return @inline(memory.set, Color, &color, ctx.buf, @bitcast(ctx.pixels)) -} +// put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { +// start_idx := @inline(screenidx, surface, pos.x, pos.y) +// end_idx := @inline(screenidx, surface, pos.x, pos.y + tr.y) -sync := fn(): void { - return @inline(memory.copy, Color, ctx.buf, ctx.fb, @bitcast(ctx.pixels)) -} +// loop if start_idx >= end_idx break else { +// @inline(memory.set, Color, &color, surface.buf + start_idx, @bitcast(tr.x)) +// start_idx += surface.width +// } -width := fn(): int { - return ctx.width -} +// return +// } -height := fn(): int { - return ctx.height -} +put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { + top_start_idx := @inline(screenidx, surface, pos.x, pos.y) + bottom_start_idx := @inline(screenidx, surface, pos.x, pos.y + tr.y - 1) + rows_to_fill := tr.y / 2 + top_cursor := 0 + bottom_cursor := (tr.y - 1) * surface.width + i := 0 + loop if i == rows_to_fill break else { + @inline(memory.set, Color, &color, surface.buf + top_start_idx, @bitcast(tr.x)) + @inline(memory.set, Color, &color, surface.buf + bottom_start_idx, @bitcast(tr.x)) -screenidx := fn(x: int, y: int): int { - return x + ctx.width * y -} + top_start_idx += surface.width + bottom_start_idx -= surface.width + i += 1 + } -put_pixel := fn(pos: Vec2(int), color: Color): void { - *(ctx.buf + @inline(screenidx, pos.x, pos.y)) = color - return -} - -put_filled_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - 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 + if tr.y % 2 != 0 { + middle_idx := @inline(screenidx, surface, pos.x, pos.y + rows_to_fill) + @inline(memory.set, Color, &color, surface.buf + middle_idx, @bitcast(tr.x)) } return } -put_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - 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) +put_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { + start_idx := @inline(screenidx, surface, pos.x, pos.y) + end_idx := @inline(screenidx, surface, pos.x, pos.y + tr.y) + right_start_idx := @inline(screenidx, surface, 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 + *(surface.buf + start_idx) = color; + *(surface.buf + right_start_idx) = color + start_idx += surface.width + right_start_idx += surface.width } - @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)) + @inline(memory.set, Color, &color, surface.buf + @inline(screenidx, surface, pos.x, pos.y), @bitcast(tr.x + 1)) + @inline(memory.set, Color, &color, surface.buf + @inline(screenidx, surface, pos.x, pos.y + tr.y), @bitcast(tr.x + 1)) return } -put_line_low := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { +put_line_low := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { dx := p1.x - p0.x dy := p1.y - p0.y yi := 1 @@ -108,7 +115,7 @@ put_line_low := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { y := p0.y x := p0.x loop if x == p1.x break else { - *(ctx.buf + @inline(screenidx, x, y)) = color + *(surface.buf + @inline(screenidx, surface, x, y)) = color if D > 0 { y += yi D += 2 * (dy - dx) @@ -120,7 +127,7 @@ put_line_low := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { return } -put_line_high := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { +put_line_high := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { dx := p1.x - p0.x dy := p1.y - p0.y xi := 1 @@ -132,7 +139,7 @@ put_line_high := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { x := p0.x y := p0.y loop if y == p1.y break else { - *(ctx.buf + @inline(screenidx, x, y)) = color + *(surface.buf + @inline(screenidx, surface, x, y)) = color if D > 0 { x += xi D += 2 * (dx - dy) @@ -144,61 +151,64 @@ put_line_high := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { return } -put_line := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { +put_line := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { if math.abs(int, p1.y - p0.y) < math.abs(int, p1.x - p0.x) { if p0.x > p1.x { - @inline(put_line_low, p1, p0, color) + @inline(put_line_low, surface, p1, p0, color) } else { - @inline(put_line_low, p0, p1, color) + @inline(put_line_low, surface, p0, p1, color) } } else { if p0.y > p1.y { - @inline(put_line_high, p1, p0, color) + @inline(put_line_high, surface, p1, p0, color) } else { - @inline(put_line_high, p0, p1, color) + @inline(put_line_high, surface, p0, p1, color) } } return } -set_height := fn(new: int): void { - return -} +// put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int)): void { +// start_idx := @inline(screenidx, surface, pos.x, pos.y) +// end_idx := @inline(screenidx, surface, pos.x, pos.y + top.height) +// cursor := top.width * top.height -set_width := fn(new: int): void { - return -} +// loop if start_idx >= end_idx break else { +// @inline(memory.copy, Color, top.buf + cursor, surface.buf + start_idx, @intcast(top.width)) +// start_idx += surface.width +// cursor -= top.width +// } +// return +// } -dimensions := fn(): Vec2(int) { - return .(ctx.width, ctx.height) -} +put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int)): void { + top_start_idx := @inline(screenidx, surface, pos.x, pos.y) + bottom_start_idx := @inline(screenidx, surface, pos.x, pos.y + top.height - 1) + rows_to_copy := top.height / 2 + top_cursor := 0 + bottom_cursor := top.width * (top.height - 1) + i := 0 + loop if i == rows_to_copy break else { + @inline(memory.copy, Color, top.buf + top_cursor, surface.buf + top_start_idx, @intcast(top.width)) + @inline(memory.copy, Color, top.buf + bottom_cursor, surface.buf + bottom_start_idx, @intcast(top.width)) -set_dimensions := fn(new: Vec2(int)): void { - return -} - -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 - - 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 + top_start_idx += surface.width + bottom_start_idx -= surface.width + top_cursor += top.width + bottom_cursor -= top.width + i += 1 } + + if top.height % 2 != 0 { + middle_idx := @inline(screenidx, surface, pos.x, pos.y + rows_to_copy) + @inline(memory.copy, Color, top.buf + top_cursor, surface.buf + middle_idx, @intcast(top.width)) + } + return } // peony-made -put_trirect := fn(pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void { +put_trirect := fn(surface: Surface, pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void { step := Vec2(int).(1, 1) if size.x < 0 { step.x = -1 @@ -211,8 +221,8 @@ put_trirect := fn(pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color) 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) + put_vline(surface, pos.x, pos.y, target.y, color0) + @inline(put_vline, surface, pos.x, pos.y, start_y, color1) pos += step } @@ -220,7 +230,7 @@ put_trirect := fn(pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color) } // peony-made -put_vline := fn(x: int, y0: int, y1: int, color: Color): void { +put_vline := fn(surface: Surface, x: int, y0: int, y1: int, color: Color): void { if y1 < y0 { tmp := y0 y0 = y1 @@ -229,7 +239,7 @@ put_vline := fn(x: int, y0: int, y1: int, color: Color): void { y := y0 loop if y == y1 break else { - *(ctx.buf + @inline(screenidx, x, y)) = color + *(surface.buf + @inline(screenidx, surface, x, y)) = color y += 1 } @@ -237,7 +247,7 @@ put_vline := fn(x: int, y0: int, y1: int, color: Color): void { } // peony-made -put_hline := fn(y: int, x0: int, x1: int, color: Color): void { +put_hline := fn(surface: Surface, y: int, x0: int, x1: int, color: Color): void { if x1 < x0 { tmp := x0 x0 = x1 @@ -246,7 +256,7 @@ put_hline := fn(y: int, x0: int, x1: int, color: Color): void { x := x0 loop if x == x1 break else { - *(ctx.buf + @inline(screenidx, x, y)) = color + *(surface.buf + @inline(screenidx, surface, x, y)) = color x += 1 } diff --git a/sysdata/libraries/render/src/svga.hb b/sysdata/libraries/render/src/svga.hb deleted file mode 100644 index 033d7626..00000000 --- a/sysdata/libraries/render/src/svga.hb +++ /dev/null @@ -1,78 +0,0 @@ -.{Vec2} := @use("../../stn/src/lib.hb").math; -.{Image, Color} := @use("lib.hb") - -clear := fn(color: Color): void { - return -} - -width := fn(): int { - return 0 -} - -height := fn(): int { - return 0 -} - -dimensions := fn(): Vec2(int) { - return .(0, 0) -} - -put_pixel := fn(position: Vec2(int), color: Color): void { - return -} - -put_filled_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - return -} - -put_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void { - return -} - -put_line_low := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { - return -} -// do not use, use line() instead -put_line_high := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { - return -} - -put_line := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void { - return -} - -set_height := fn(new: int): void { - return -} - -set_width := fn(new: int): void { - return -} - -set_dimensions := fn(new: Vec2(int)): void { - return -} - -sync := fn(): void { - return -} - -init := fn(): void { - return -} - -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 9167b327..9b89cbea 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -2,7 +2,7 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF alloc := fn($Expr: type, bytes: int): ^Expr { - pages := (1 + bytes) / PAGE_SIZE + pages := 1 + bytes / PAGE_SIZE if pages <= MAX_ALLOC { return @bitcast(@inline(request_page, pages)) } diff --git a/sysdata/programs/render_example/src/examples/able.bmp b/sysdata/programs/render_example/src/examples/able.bmp index 7de373dd62363316506fe61ed31f1084e3526180..059086b3f0c1c4a974acf8fb72b2cc073567c430 100644 GIT binary patch literal 102454 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 literal 100534 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 height break else { - render.put_line(p0, p1, .(255, 180, 100, 255)) - render.put_line(.(width, height) - p0, .(width, height) - p1, .(255, 180, 100, 255)) - p0.y += height >> 6 - p1.x += width >> 6 + p1 := Vec2(int).(0, screen.height) + loop if p0.y >= screen.height break else { + render.put_line(screen, p0, p1, .(255, 180, 100, 255)) + render.put_line(screen, .(screen.width, screen.height) - p0, .(screen.width, screen.height) - p1, .(255, 180, 100, 255)) + p0.y += screen.height >> 6 + p1.x += screen.width >> 6 } - render.sync() + render.sync(screen) return } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/random.hb b/sysdata/programs/render_example/src/examples/random.hb index 7c68c5e1..fed2790d 100644 --- a/sysdata/programs/render_example/src/examples/random.hb +++ b/sysdata/programs/render_example/src/examples/random.hb @@ -2,16 +2,15 @@ render := @use("../../../../libraries/render/src/lib.hb") example := fn(): void { - render.init() - render.doublebuffer(false) - render.clear(render.black) + screen := render.init(false) + render.clear(screen, render.black) loop { x := random.range(int, 0, 1024) y := random.range(int, 0, 768) r := random.range(int, 0, 255) g := random.range(int, 0, 75) b := random.range(int, 0, 155) - render.put_pixel(.(x, y), .(b, g, r, 255)) + render.put_pixel(screen, .(x, y), .(b, g, r, 255)) } return } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/square.hb b/sysdata/programs/render_example/src/examples/square.hb index 46ee8109..09f71e09 100644 --- a/sysdata/programs/render_example/src/examples/square.hb +++ b/sysdata/programs/render_example/src/examples/square.hb @@ -6,22 +6,20 @@ render := @use("../../../../libraries/render/src/lib.hb") a square that changes colour bounces around the screen */ example := fn(): void { - render.init() + screen := render.init(true) vel := Vec2(int).(1, 1) pos := Vec2(int).(100, 100) - width := render.width() - height := render.height() color := @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) loop { - render.put_filled_rect(pos, .(100, 100), color) - render.sync() - render.clear(render.black) + render.put_filled_rect(screen, pos, .(100, 100), color) + render.sync(screen) + render.clear(screen, render.black) - if pos.x == 0 | pos.x == width - 100 { + if pos.x == 0 | pos.x == screen.width - 100 { vel.x = -vel.x color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) } - if pos.y == 0 | pos.y == height - 100 { + if pos.y == 0 | pos.y == screen.height - 100 { vel.y = -vel.y color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) } diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb new file mode 100644 index 00000000..95fbd514 --- /dev/null +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -0,0 +1,39 @@ +.{Vec2} := @use("../../../../libraries/stn/src/lib.hb").math +render := @use("../../../../libraries/render/src/lib.hb") + +/* expected result: + the lines example bounces around the screen */ + +example := fn(): void { + screen := render.init(true) + + image := render.new_surface(341, 256) + + p0 := Vec2(int).(0, 0) + p1 := Vec2(int).(0, image.height) + render.clear(image, .(100, 50, 0, 255)) + loop if p0.y >= image.height break else { + render.put_line(image, p0, p1, .(255, 180, 100, 255)) + render.put_line(image, .(image.width, image.height) - p0, .(image.width, image.height) - p1, .(255, 180, 100, 255)) + p0.y += image.height >> 6 + p1.x += image.width >> 6 + } + + vel := Vec2(int).(1, 1) + pos := Vec2(int).(100, 100) + loop { + render.put_surface(screen, image, pos) + render.sync(screen) + render.clear(screen, render.black) + + if pos.x == 0 | pos.x == screen.width - image.width { + vel.x = -vel.x + } + if pos.y == 0 | pos.y == screen.height - image.height { + vel.y = -vel.y + } + + pos += vel + } + return +} \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/svga.hb b/sysdata/programs/render_example/src/examples/svga.hb deleted file mode 100644 index 101aa6fb..00000000 --- a/sysdata/programs/render_example/src/examples/svga.hb +++ /dev/null @@ -1,8 +0,0 @@ -render := @use("../../../../libraries/render/src/lib.hb") - -render.mode = render.svga - -example := fn(): void { - render.init() - return -} \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/tactical_screen.hb b/sysdata/programs/render_example/src/examples/tactical_screen.hb index 4f227de2..5b2e909e 100644 --- a/sysdata/programs/render_example/src/examples/tactical_screen.hb +++ b/sysdata/programs/render_example/src/examples/tactical_screen.hb @@ -7,10 +7,10 @@ Vec2 := math.Vec2 with a "target" randomly apperaing in one of them and a "seeker" "catching" it*/ example := fn(): void { - render.init() + screen := render.init(true) - width := render.width() - height := render.height() + width := screen.width + height := screen.height cell_size := 0 range := Vec2(int).(0, 0) if width > height { @@ -33,32 +33,32 @@ example := fn(): void { seeker := Vec2(int).(random.range(int, 0, range.x), random.range(int, 0, range.y)) loop { - render.clear(render.black) + render.clear(screen, 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_trirect(screen, 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) + render.put_hline(screen, target_pixel_coord.y + halfcell, target_pixel_coord.x - octcell, target_pixel_coord.x - sevenoctcell, render.light_red) + render.put_hline(screen, 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(screen, target_pixel_coord.x + halfcell, target_pixel_coord.y - octcell, target_pixel_coord.y - sevenoctcell, render.light_red) + render.put_vline(screen, 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)) + render.put_vline(screen, 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)) + render.put_hline(screen, 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.put_hline(screen, seeker.y * cell_size + halfcell + scroll, 0, width, render.light_green) + render.put_vline(screen, seeker.x * cell_size + halfcell + scroll, 0, height, render.light_green) - render.sync() + render.sync(screen) if seeker.x < target.x { seeker.x += 1 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 1792d684..e5deef95 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,11 +37,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 fc06820332ca3856c0ca645b3dfb396a15b50469 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 15 Oct 2024 03:44:12 -0500 Subject: [PATCH 07/66] fix visibility --- kernel/src/arch/aarch64/logging.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/arch/aarch64/logging.rs b/kernel/src/arch/aarch64/logging.rs index 231cf397..bdf55f58 100644 --- a/kernel/src/arch/aarch64/logging.rs +++ b/kernel/src/arch/aarch64/logging.rs @@ -3,7 +3,7 @@ pub static SERIAL_CONSOLE: Mutex = Mutex::new(SerialConsole { uart: 0x09000000 as *mut u8, }); -struct SerialConsole { +pub struct SerialConsole { uart: *mut u8, } From 0ebb1f200e222d143bc5015a1c218cff723f1999 Mon Sep 17 00:00:00 2001 From: koniifer Date: Tue, 15 Oct 2024 21:11:06 +0100 Subject: [PATCH 08/66] make surface example more interesting, optimise memory filling --- sysdata/libraries/render/src/software.hb | 90 +++++-------------- .../render_example/src/examples/surface.hb | 38 ++++---- .../src/examples/tactical_screen.hb | 2 +- sysdata/programs/render_example/src/main.hb | 2 +- 4 files changed, 47 insertions(+), 85 deletions(-) diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 3dcbd09f..ae8905b6 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -4,23 +4,6 @@ framebuffer := @as(^Color, idk) -// init := fn(): void { -// 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"), -// bb: back_buffer, -// buf: back_buffer, -// width, -// height, -// pixels, -// double_buffer: true, -// } -// return -// } - init := fn(double_buffer: bool): Surface { framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0") width := dt.get(int, "framebuffer/fb0/width\0") @@ -49,37 +32,22 @@ put_pixel := fn(surface: Surface, pos: Vec2(int), color: Color): void { return } -// put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { -// start_idx := @inline(screenidx, surface, pos.x, pos.y) -// end_idx := @inline(screenidx, surface, pos.x, pos.y + tr.y) - -// loop if start_idx >= end_idx break else { -// @inline(memory.set, Color, &color, surface.buf + start_idx, @bitcast(tr.x)) -// start_idx += surface.width -// } - -// return -// } - put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { - top_start_idx := @inline(screenidx, surface, pos.x, pos.y) - bottom_start_idx := @inline(screenidx, surface, pos.x, pos.y + tr.y - 1) - rows_to_fill := tr.y / 2 - top_cursor := 0 - bottom_cursor := (tr.y - 1) * surface.width - i := 0 - loop if i == rows_to_fill break else { - @inline(memory.set, Color, &color, surface.buf + top_start_idx, @bitcast(tr.x)) - @inline(memory.set, Color, &color, surface.buf + bottom_start_idx, @bitcast(tr.x)) + top_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y) + bottom_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y + tr.y - 1) + rows_to_fill := tr.y + + loop if rows_to_fill <= 1 break else { + @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) + @inline(memory.set, Color, &color, bottom_start_idx, @bitcast(tr.x)) top_start_idx += surface.width bottom_start_idx -= surface.width - i += 1 + rows_to_fill -= 2 } - if tr.y % 2 != 0 { - middle_idx := @inline(screenidx, surface, pos.x, pos.y + rows_to_fill) - @inline(memory.set, Color, &color, surface.buf + middle_idx, @bitcast(tr.x)) + if rows_to_fill == 1 { + @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) } return @@ -168,40 +136,26 @@ put_line := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): vo return } -// put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int)): void { -// start_idx := @inline(screenidx, surface, pos.x, pos.y) -// end_idx := @inline(screenidx, surface, pos.x, pos.y + top.height) -// cursor := top.width * top.height - -// loop if start_idx >= end_idx break else { -// @inline(memory.copy, Color, top.buf + cursor, surface.buf + start_idx, @intcast(top.width)) -// start_idx += surface.width -// cursor -= top.width -// } -// return -// } - put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int)): void { - top_start_idx := @inline(screenidx, surface, pos.x, pos.y) - bottom_start_idx := @inline(screenidx, surface, pos.x, pos.y + top.height - 1) - rows_to_copy := top.height / 2 - top_cursor := 0 - bottom_cursor := top.width * (top.height - 1) - i := 0 - loop if i == rows_to_copy break else { - @inline(memory.copy, Color, top.buf + top_cursor, surface.buf + top_start_idx, @intcast(top.width)) - @inline(memory.copy, Color, top.buf + bottom_cursor, surface.buf + bottom_start_idx, @intcast(top.width)) + top_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y) + bottom_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y + top.height - 1) + rows_to_copy := top.height + top_cursor := top.buf + bottom_cursor := top.buf + top.width * (top.height - 1) + + loop if rows_to_copy <= 1 break else { + @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width)) + @inline(memory.copy, Color, bottom_cursor, bottom_start_idx, @bitcast(top.width)) top_start_idx += surface.width bottom_start_idx -= surface.width top_cursor += top.width bottom_cursor -= top.width - i += 1 + rows_to_copy -= 2 } - if top.height % 2 != 0 { - middle_idx := @inline(screenidx, surface, pos.x, pos.y + rows_to_copy) - @inline(memory.copy, Color, top.buf + top_cursor, surface.buf + middle_idx, @intcast(top.width)) + if rows_to_copy == 1 { + @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width)) } return diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb index 95fbd514..7eec6d43 100644 --- a/sysdata/programs/render_example/src/examples/surface.hb +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -1,30 +1,37 @@ -.{Vec2} := @use("../../../../libraries/stn/src/lib.hb").math +.{Vec2} := @use("../../../../libraries/stn/src/lib.hb").math; +.{random} := @use("../../../../libraries/stn/src/lib.hb") render := @use("../../../../libraries/render/src/lib.hb") /* expected result: - the lines example bounces around the screen */ + the square example bounces around the screen */ example := fn(): void { screen := render.init(true) image := render.new_surface(341, 256) - - p0 := Vec2(int).(0, 0) - p1 := Vec2(int).(0, image.height) - render.clear(image, .(100, 50, 0, 255)) - loop if p0.y >= image.height break else { - render.put_line(image, p0, p1, .(255, 180, 100, 255)) - render.put_line(image, .(image.width, image.height) - p0, .(image.width, image.height) - p1, .(255, 180, 100, 255)) - p0.y += image.height >> 6 - p1.x += image.width >> 6 - } - - vel := Vec2(int).(1, 1) + vel := Vec2(int).(-1, -1) pos := Vec2(int).(100, 100) + vel_inner := Vec2(int).(1, 1) + pos_inner := Vec2(int).(10, 10) + color := @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) loop { + render.clear(image, render.black) + render.clear(screen, render.black) + + render.put_filled_rect(image, pos_inner, .(100, 100), color) + render.put_rect(image, .(0, 0), .(image.width - 1, image.height - 1), render.white) + render.put_surface(screen, image, pos) render.sync(screen) - render.clear(screen, render.black) + + if pos_inner.x == 0 | pos_inner.x == image.width - 100 { + vel_inner.x = -vel_inner.x + color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + } + if pos_inner.y == 0 | pos_inner.y == image.height - 100 { + vel_inner.y = -vel_inner.y + color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + } if pos.x == 0 | pos.x == screen.width - image.width { vel.x = -vel.x @@ -34,6 +41,7 @@ example := fn(): void { } pos += vel + pos_inner += vel_inner } return } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/tactical_screen.hb b/sysdata/programs/render_example/src/examples/tactical_screen.hb index 5b2e909e..bbbaed2b 100644 --- a/sysdata/programs/render_example/src/examples/tactical_screen.hb +++ b/sysdata/programs/render_example/src/examples/tactical_screen.hb @@ -4,7 +4,7 @@ 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*/ + with a "target" randomly apperaing in one of them and a "seeker" "catching" it */ example := fn(): void { screen := render.init(true) diff --git a/sysdata/programs/render_example/src/main.hb b/sysdata/programs/render_example/src/main.hb index 1ce2d4e6..a3981b65 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1,4 +1,4 @@ -.{example} := @use("./examples/image.hb") +.{example} := @use("./examples/surface.hb") main := fn(): void { @inline(example) From 34cbd9a5e6c945373dc9c9599e16ddfbe9bd2c8a Mon Sep 17 00:00:00 2001 From: koniifer Date: Tue, 15 Oct 2024 21:43:23 +0100 Subject: [PATCH 09/66] fiddling --- sysdata/libraries/stn/src/random.hb | 3 ++- .../src/examples/{ => assets}/able.bmp | Bin .../src/examples/{ => assets}/mini.bmp | Bin .../render_example/src/examples/image.hb | 4 ++-- .../render_example/src/examples/square.hb | 15 ++++++++------- .../render_example/src/examples/surface.hb | 17 +++++++++-------- sysdata/programs/render_example/src/main.hb | 5 +---- 7 files changed, 22 insertions(+), 22 deletions(-) rename sysdata/programs/render_example/src/examples/{ => assets}/able.bmp (100%) rename sysdata/programs/render_example/src/examples/{ => assets}/mini.bmp (100%) diff --git a/sysdata/libraries/stn/src/random.hb b/sysdata/libraries/stn/src/random.hb index 1e625602..9b4cdfc6 100644 --- a/sysdata/libraries/stn/src/random.hb +++ b/sysdata/libraries/stn/src/random.hb @@ -3,5 +3,6 @@ any := fn($Expr: type): Expr { } range := fn($Expr: type, min: Expr, max: Expr): Expr { - return @eca(3, 4) % (max - min + 1) + min + // wtf is this + return @intcast(@as(int, @eca(3, 4)) % @as(int, @intcast(max) - @intcast(min)) + 1 + @intcast(min)) } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/able.bmp b/sysdata/programs/render_example/src/examples/assets/able.bmp similarity index 100% rename from sysdata/programs/render_example/src/examples/able.bmp rename to sysdata/programs/render_example/src/examples/assets/able.bmp diff --git a/sysdata/programs/render_example/src/examples/mini.bmp b/sysdata/programs/render_example/src/examples/assets/mini.bmp similarity index 100% rename from sysdata/programs/render_example/src/examples/mini.bmp rename to sysdata/programs/render_example/src/examples/assets/mini.bmp diff --git a/sysdata/programs/render_example/src/examples/image.hb b/sysdata/programs/render_example/src/examples/image.hb index b1ac1dee..ce59daf4 100644 --- a/sysdata/programs/render_example/src/examples/image.hb +++ b/sysdata/programs/render_example/src/examples/image.hb @@ -4,8 +4,8 @@ render := @use("../../../../libraries/render/src/lib.hb") /* expected result: a cute image bounces around the screen */ -bmp_1 := @embed("./able.bmp") -bmp_2 := @embed("./mini.bmp") +bmp_1 := @embed("./assets/able.bmp") +bmp_2 := @embed("./assets/mini.bmp") example := fn(): void { screen := render.init(true) diff --git a/sysdata/programs/render_example/src/examples/square.hb b/sysdata/programs/render_example/src/examples/square.hb index 09f71e09..5f894956 100644 --- a/sysdata/programs/render_example/src/examples/square.hb +++ b/sysdata/programs/render_example/src/examples/square.hb @@ -8,20 +8,21 @@ render := @use("../../../../libraries/render/src/lib.hb") example := fn(): void { screen := render.init(true) vel := Vec2(int).(1, 1) - pos := Vec2(int).(100, 100) - color := @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + side := screen.width / 8 + pos := Vec2(int).((screen.width - side) / 2, (screen.height - side) / 2) + color := random.range(render.Color, render.black, render.white) loop { - render.put_filled_rect(screen, pos, .(100, 100), color) + render.put_filled_rect(screen, pos, .(side, side), color) render.sync(screen) render.clear(screen, render.black) - if pos.x == 0 | pos.x == screen.width - 100 { + if pos.x == 0 | pos.x == screen.width - side { vel.x = -vel.x - color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + color = random.range(render.Color, render.black, render.white) } - if pos.y == 0 | pos.y == screen.height - 100 { + if pos.y == 0 | pos.y == screen.height - side { vel.y = -vel.y - color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + color = random.range(render.Color, render.black, render.white) } pos += vel diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb index 7eec6d43..191c8ad8 100644 --- a/sysdata/programs/render_example/src/examples/surface.hb +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -8,29 +8,30 @@ render := @use("../../../../libraries/render/src/lib.hb") example := fn(): void { screen := render.init(true) - image := render.new_surface(341, 256) + image := render.new_surface(screen.width / 3, screen.height / 3) vel := Vec2(int).(-1, -1) pos := Vec2(int).(100, 100) + side := image.width / 8 vel_inner := Vec2(int).(1, 1) - pos_inner := Vec2(int).(10, 10) - color := @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + pos_inner := Vec2(int).((image.width - side) / 2, (image.height - side) / 2) + color := random.range(render.Color, render.black, render.white) loop { render.clear(image, render.black) render.clear(screen, render.black) - render.put_filled_rect(image, pos_inner, .(100, 100), color) + render.put_filled_rect(image, pos_inner, .(side, side), color) render.put_rect(image, .(0, 0), .(image.width - 1, image.height - 1), render.white) render.put_surface(screen, image, pos) render.sync(screen) - if pos_inner.x == 0 | pos_inner.x == image.width - 100 { + if pos_inner.x == 0 | pos_inner.x == image.width - side { vel_inner.x = -vel_inner.x - color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + color = random.range(render.Color, render.black, render.white) } - if pos_inner.y == 0 | pos_inner.y == image.height - 100 { + if pos_inner.y == 0 | pos_inner.y == image.height - side { vel_inner.y = -vel_inner.y - color = @as(render.Color, @intcast(random.range(int, 0, 0xFFFFFF))) + color = random.range(render.Color, render.black, render.white) } if pos.x == 0 | pos.x == screen.width - image.width { diff --git a/sysdata/programs/render_example/src/main.hb b/sysdata/programs/render_example/src/main.hb index a3981b65..289d8128 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1,6 +1,3 @@ .{example} := @use("./examples/surface.hb") -main := fn(): void { - @inline(example) - return -} \ No newline at end of file +main := example \ No newline at end of file From ed7c8f20cb94b0f411d79de6ace1056661399be7 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 15 Oct 2024 17:06:38 -0500 Subject: [PATCH 10/66] animal crossing new Horizon beginnings --- sysdata/programs/horizon/src/main.hb | 28 ++++++++++++++++++++++++++++ sysdata/system_config.toml | 12 ++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 3c7c6317..ada7eeda 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -3,9 +3,37 @@ stn := @use("../../../libraries/stn/src/lib.hb"); horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") +render := @use("../../../libraries/render/src/lib.hb"); +.{Vec2} := @use("../../../libraries/stn/src/lib.hb").math + +Window := struct { + // TODO: Replace this with widgets + // implicit_framebuffer: + width: int, + height: int, + x: int, + y: int, +} + main := fn(): int { a := buffer.create("XHorizon\0") + screen := render.init(true) + + image := render.new_surface(screen.width / 3, screen.height / 3) + + pos := Vec2(int).(100, 100) + loop { + // Clear the screen + render.clear(screen, render.black) + + // TODO: Get windows out of the hashmap + render.clear(image, render.white) + + // Apply the image to the screen + render.put_surface(screen, image, pos) + // Sync the screen + render.sync(screen) } return 0 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index e5deef95..835b3fda 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -31,17 +31,17 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# 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 84f86488c797eae4959b6a49e9d2b786ba4c6d72 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 15 Oct 2024 17:25:11 -0500 Subject: [PATCH 11/66] buggy code --- sysdata/programs/horizon/src/main.hb | 31 +++++++++++++------ .../horizon_testing_program/src/main.hb | 28 ++++++++--------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index ada7eeda..9fc340a4 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -1,14 +1,14 @@ stn := @use("../../../libraries/stn/src/lib.hb"); -.{string, memory, buffer} := stn +.{string, memory, buffer, random} := stn; +.{Vec2} := stn.math horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") -render := @use("../../../libraries/render/src/lib.hb"); -.{Vec2} := @use("../../../libraries/stn/src/lib.hb").math +render := @use("../../../libraries/render/src/lib.hb") Window := struct { // TODO: Replace this with widgets - // implicit_framebuffer: + implicit_framebuffer: render.Surface, width: int, height: int, x: int, @@ -17,21 +17,34 @@ Window := struct { main := fn(): int { a := buffer.create("XHorizon\0") + + // BUG: Backbuffering is disabled screen := render.init(true) - image := render.new_surface(screen.width / 3, screen.height / 3) + // Clear the screen to black. + render.clear(screen, render.black) + + window := render.new_surface(screen.width / 3, screen.height / 3) pos := Vec2(int).(100, 100) + color := random.range(render.Color, render.black, render.white) + loop { // Clear the screen render.clear(screen, render.black) - // TODO: Get windows out of the hashmap - render.clear(image, render.white) + // TODO: Read the window buffer here + + // TODO: Get windows out of a collection and iter through + // + { + render.clear(window, render.white) + + // Apply the image to the screen + render.put_surface(screen, window, pos) + } - // Apply the image to the screen - render.put_surface(screen, image, pos) // Sync the screen render.sync(screen) } diff --git a/sysdata/programs/horizon_testing_program/src/main.hb b/sysdata/programs/horizon_testing_program/src/main.hb index 01d57a3c..b8f93160 100644 --- a/sysdata/programs/horizon_testing_program/src/main.hb +++ b/sysdata/programs/horizon_testing_program/src/main.hb @@ -8,28 +8,28 @@ ignim := @use("../../../libraries/ignim/src/lib.hb"); .{errors} := ignim main := fn(): int { - windowing_system_buffer := buffer.create("XHorizon\0") + windowing_system_buffer := buffer.search("XHorizon\0") // TODO: get WindowID create_window(windowing_system_buffer) - program_name := "Horizon Testing Program\0" - program_version := ignim.version.make_version(0, 1, 0) - engine_name := "None\0" - engine_version := ignim.version.make_version(0, 0, 0) - api_version := ignim.version.make_api_version(0, 1, 0, 0) + // program_name := "Horizon Testing Program\0" + // program_version := ignim.version.make_version(0, 1, 0) + // engine_name := "None\0" + // engine_version := ignim.version.make_version(0, 0, 0) + // api_version := ignim.version.make_api_version(0, 1, 0, 0) - app_info := ignim.application.new_application_info(program_name, program_version, engine_name, engine_version, api_version) + // app_info := ignim.application.new_application_info(program_name, program_version, engine_name, engine_version, api_version) - create_info := ignim.instance.new_create_info(&app_info) + // create_info := ignim.instance.new_create_info(&app_info) - instance := ignim.instance.void_instance() + // instance := ignim.instance.void_instance() - // TODO: recursively follow this https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance - ret := ignim.instance.create_instance(&create_info, 0, &instance) - if ret == errors.IncompatibleDriver { - log.error("Driver Incompatible with Vulkan\0") - } + // // TODO: recursively follow this https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance + // ret := ignim.instance.create_instance(&create_info, 0, &instance) + // if ret == errors.IncompatibleDriver { + // log.error("Driver Incompatible with Vulkan\0") + // } return 0 } \ No newline at end of file From 086cc4aef08b13432e2061e5d8c19d28126c85ce Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 16 Oct 2024 00:03:23 +0100 Subject: [PATCH 12/66] patch minor logging bug --- .../holeybytes/kernel_services/logging_service.rs | 2 +- sysdata/libraries/stn/src/log.hb | 15 +++++++-------- sysdata/programs/tests/src/main.hb | 6 ++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/kernel/src/holeybytes/kernel_services/logging_service.rs b/kernel/src/holeybytes/kernel_services/logging_service.rs index a0b461f9..48a38b94 100644 --- a/kernel/src/holeybytes/kernel_services/logging_service.rs +++ b/kernel/src/holeybytes/kernel_services/logging_service.rs @@ -14,7 +14,7 @@ pub fn log_msg_handler(_vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), let file_name = "None"; let line_number = 0; - match core::str::from_utf8(&msg_vec[..msg_vec.len()]) { + match core::str::from_utf8(&msg_vec[..msg_vec.len() - 1]) { Ok(strr) => { use log::Level::*; let log_level = match log_level { diff --git a/sysdata/libraries/stn/src/log.hb b/sysdata/libraries/stn/src/log.hb index 14639df8..72c68ebd 100644 --- a/sysdata/libraries/stn/src/log.hb +++ b/sysdata/libraries/stn/src/log.hb @@ -1,15 +1,14 @@ string := @use("string.hb") -buffer := @use("buffer.hb") -log := fn(message: ^u8, level: u8): void { +log := fn($Level: u8, message: ^u8): void { message_length := @inline(string.length, message); - *(message + message_length) = level + *(message + message_length) = Level return @eca(3, 1, message, message_length + 1) } -error := fn(message: ^u8): void return log(message, 0) -warn := fn(message: ^u8): void return log(message, 1) -info := fn(message: ^u8): void return log(message, 2) -debug := fn(message: ^u8): void return log(message, 3) -trace := fn(message: ^u8): void return log(message, 4) \ No newline at end of file +error := fn(message: ^u8): void return @inline(log, 0, message) +warn := fn(message: ^u8): void return @inline(log, 1, message) +info := fn(message: ^u8): void return @inline(log, 2, message) +debug := fn(message: ^u8): void return @inline(log, 3, message) +trace := fn(message: ^u8): void return @inline(log, 4, message) \ No newline at end of file diff --git a/sysdata/programs/tests/src/main.hb b/sysdata/programs/tests/src/main.hb index be45842a..e89eafb5 100644 --- a/sysdata/programs/tests/src/main.hb +++ b/sysdata/programs/tests/src/main.hb @@ -9,5 +9,11 @@ service_search := fn(): void { main := fn(): int { //service_search() + buf := "\0\0\0\0" + x := 0 + loop if x == 255 break else { + log.info(string.display_int(x, buf)) + x += 1 + } return 0 } \ No newline at end of file From 2bc13cd7d886710590f606b2df012e88a4926696 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 15 Oct 2024 18:06:33 -0500 Subject: [PATCH 13/66] fix :thumbsup: --- sysdata/programs/horizon/src/main.hb | 2 +- .../horizon_testing_program/src/main.hb | 28 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 9fc340a4..9c02982d 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -39,7 +39,7 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through // { - render.clear(window, render.white) + render.clear(window, color) // Apply the image to the screen render.put_surface(screen, window, pos) diff --git a/sysdata/programs/horizon_testing_program/src/main.hb b/sysdata/programs/horizon_testing_program/src/main.hb index b8f93160..60650e11 100644 --- a/sysdata/programs/horizon_testing_program/src/main.hb +++ b/sysdata/programs/horizon_testing_program/src/main.hb @@ -8,28 +8,30 @@ ignim := @use("../../../libraries/ignim/src/lib.hb"); .{errors} := ignim main := fn(): int { - windowing_system_buffer := buffer.search("XHorizon\0") + x := 0 + loop if x > 1000000 break else x += 1 + windowing_system_buffer := buffer.search("XHorizon\0") // TODO: get WindowID create_window(windowing_system_buffer) - // program_name := "Horizon Testing Program\0" - // program_version := ignim.version.make_version(0, 1, 0) - // engine_name := "None\0" - // engine_version := ignim.version.make_version(0, 0, 0) - // api_version := ignim.version.make_api_version(0, 1, 0, 0) + program_name := "Horizon Testing Program\0" + program_version := ignim.version.make_version(0, 1, 0) + engine_name := "None\0" + engine_version := ignim.version.make_version(0, 0, 0) + api_version := ignim.version.make_api_version(0, 1, 0, 0) - // app_info := ignim.application.new_application_info(program_name, program_version, engine_name, engine_version, api_version) + app_info := ignim.application.new_application_info(program_name, program_version, engine_name, engine_version, api_version) - // create_info := ignim.instance.new_create_info(&app_info) + create_info := ignim.instance.new_create_info(&app_info) - // instance := ignim.instance.void_instance() + instance := ignim.instance.void_instance() // // TODO: recursively follow this https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance - // ret := ignim.instance.create_instance(&create_info, 0, &instance) - // if ret == errors.IncompatibleDriver { - // log.error("Driver Incompatible with Vulkan\0") - // } + ret := ignim.instance.create_instance(&create_info, 0, &instance) + if ret == errors.IncompatibleDriver { + // log.error("Driver Incompatible with Vulkan\0") + } return 0 } \ No newline at end of file From 34101d2e8c7c8ac40931ef952cff64c7591c4cd4 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 15 Oct 2024 20:30:24 -0500 Subject: [PATCH 14/66] Windowing changes --- sysdata/libraries/horizon_api/src/lib.hb | 30 +++++++++--- sysdata/programs/horizon/src/main.hb | 48 ++++++++++++++++--- .../horizon_testing_program/src/main.hb | 33 +++++++------ 3 files changed, 83 insertions(+), 28 deletions(-) diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index cc374caf..be58f081 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -1,5 +1,7 @@ stn := @use("../../stn/src/lib.hb"); -.{string, memory, buffer} := stn +.{string, memory, buffer, log} := stn + +render := @use("../../../libraries/render/src/lib.hb") input := @use("../../intouch/src/lib.hb") @@ -12,20 +14,34 @@ WindowID := struct { VoidWindowID := WindowID.(0, 0) -create_window := fn(channel: int): WindowID { +create_window := fn(channel: int): ^render.Surface { // get the horizon buffer // request a new window and provide the callback buffer // wait to recieve a message windowing_system_buffer := buffer.search("XHorizon\0") + mem_buf := memory.request_page(1) if windowing_system_buffer == 0 { - return VoidWindowID + return 0 } else { - msg := "\{01}\0" - msg_length := 2 + // msg := "\{01}\0" + // msg_length := 2 - @as(void, @eca(3, windowing_system_buffer, msg, msg_length)) - return WindowID.(1, 0) + // @as(void, @eca(3, windowing_system_buffer, msg, msg_length)) + + x := 0 + loop if x > 1000 break else x += 1 + + ret := buffer.recv(windowing_system_buffer, mem_buf, 4096) + if ret == 0 { + log.info("No messages\0") + } + + if *mem_buf == 0 { + log.info("No messages\0") + } + + return 0 } } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 9c02982d..e9ba1729 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -1,5 +1,5 @@ stn := @use("../../../libraries/stn/src/lib.hb"); -.{string, memory, buffer, random} := stn; +.{string, memory, buffer, random, log} := stn; .{Vec2} := stn.math horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") @@ -16,7 +16,7 @@ Window := struct { } main := fn(): int { - a := buffer.create("XHorizon\0") + win_buff := buffer.create("XHorizon\0") // BUG: Backbuffering is disabled screen := render.init(true) @@ -26,24 +26,60 @@ main := fn(): int { window := render.new_surface(screen.width / 3, screen.height / 3) - pos := Vec2(int).(100, 100) + x := 10 + mem_buf := memory.request_page(1) color := random.range(render.Color, render.black, render.white) + side := window.width / 8 + + vel_inner := Vec2(int).(1, 1) + pos_inner := Vec2(int).((window.width - side) / 2, (window.height - side) / 2) loop { // Clear the screen render.clear(screen, render.black) // TODO: Read the window buffer here + { + ret := buffer.recv(win_buff, mem_buf, 4096) + if ret == 0 { + log.info("No messages\0") + } + } + + if pos_inner.x == 0 | pos_inner.x == window.width - side { + vel_inner.x = -vel_inner.x + color = random.range(render.Color, render.black, render.white) + } + if pos_inner.y == 0 | pos_inner.y == window.height - side { + vel_inner.y = -vel_inner.y + color = random.range(render.Color, render.black, render.white) + } // TODO: Get windows out of a collection and iter through - // - { - render.clear(window, color) + window_count := 0 + loop { + render.clear(window, render.black) + + // Draw the decorators + { + render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) + // render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) + } + render.put_filled_rect(window, pos_inner, .(side, side), color) // Apply the image to the screen + pos := Vec2(int).(x, 100) + render.put_surface(screen, window, pos) + if window_count >= 1 { + x = 10 + break + } + window_count += 1 + x += 400 } + pos_inner += vel_inner // Sync the screen render.sync(screen) diff --git a/sysdata/programs/horizon_testing_program/src/main.hb b/sysdata/programs/horizon_testing_program/src/main.hb index 60650e11..3fe241bd 100644 --- a/sysdata/programs/horizon_testing_program/src/main.hb +++ b/sysdata/programs/horizon_testing_program/src/main.hb @@ -9,29 +9,32 @@ ignim := @use("../../../libraries/ignim/src/lib.hb"); main := fn(): int { x := 0 - loop if x > 1000000 break else x += 1 + // loop if x > 10000 break else x += 1 windowing_system_buffer := buffer.search("XHorizon\0") // TODO: get WindowID - create_window(windowing_system_buffer) + wid := create_window(windowing_system_buffer) + if false { + program_name := "Horizon Testing Program\0" + program_version := ignim.version.make_version(0, 1, 0) + engine_name := "None\0" + engine_version := ignim.version.make_version(0, 0, 0) + api_version := ignim.version.make_api_version(0, 1, 0, 0) - program_name := "Horizon Testing Program\0" - program_version := ignim.version.make_version(0, 1, 0) - engine_name := "None\0" - engine_version := ignim.version.make_version(0, 0, 0) - api_version := ignim.version.make_api_version(0, 1, 0, 0) + app_info := ignim.application.new_application_info(program_name, program_version, engine_name, engine_version, api_version) - app_info := ignim.application.new_application_info(program_name, program_version, engine_name, engine_version, api_version) + create_info := ignim.instance.new_create_info(&app_info) - create_info := ignim.instance.new_create_info(&app_info) + instance := ignim.instance.void_instance() - instance := ignim.instance.void_instance() - - // // TODO: recursively follow this https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance - ret := ignim.instance.create_instance(&create_info, 0, &instance) - if ret == errors.IncompatibleDriver { - // log.error("Driver Incompatible with Vulkan\0") + // // TODO: recursively follow this https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance + ret := ignim.instance.create_instance(&create_info, 0, &instance) + if ret == errors.IncompatibleDriver { + log.error("Driver Incompatible with Vulkan\0") + } } + // TODO: get window from the window system and draw to the surface + return 0 } \ No newline at end of file From 7caa47b9fbd43b2d4cf40dcc9b96d79cc386a0ba Mon Sep 17 00:00:00 2001 From: koniifer Date: Thu, 17 Oct 2024 15:31:42 +0100 Subject: [PATCH 15/66] make surface example cooler --- sysdata/libraries/render/src/lib.hb | 40 +++---------------- sysdata/libraries/render/src/software.hb | 39 ++++++++++++++++-- sysdata/libraries/stn/src/memory.hb | 9 ++++- .../render_example/src/examples/surface.hb | 7 ++-- sysdata/system_config.toml | 12 +++--- 5 files changed, 58 insertions(+), 49 deletions(-) diff --git a/sysdata/libraries/render/src/lib.hb b/sysdata/libraries/render/src/lib.hb index 3bb56c3c..9227f528 100644 --- a/sysdata/libraries/render/src/lib.hb +++ b/sysdata/libraries/render/src/lib.hb @@ -1,4 +1,3 @@ -.{memory} := @use("../../stn/src/lib.hb") software := @use("software.hb") image := @use("image.hb") @@ -7,34 +6,11 @@ mode := software init := mode.init doublebuffer := mode.doublebuffer - -Surface := struct { - buf: ^Color, - width: int, - height: int, -} - -new_surface := fn(width: int, height: int): Surface { - return .( - @inline(memory.alloc, Color, width * height * @bitcast(@sizeof(Color))), - width, - height, - ) -} - -surface_from_ptr := fn(ptr: ^Color, width: int, height: int): Surface { - return .( - ptr, - width, - height, - ) -} - -clone_surface := fn(surface: Surface): Surface { - new := new_surface(surface.width, surface.height) - @inline(memory.copy, Color, surface.buf, new.buf, @intcast(surface.width * surface.height)) - return new -} +Surface := mode.Surface +new_surface := mode.new_surface +surface_from_ptr := mode.surface_from_ptr +clone_surface := mode.clone_surface +free_surface := mode.free_surface // Colours Color := packed struct {b: u8, g: u8, r: u8, a: u8} @@ -68,10 +44,4 @@ put_vline := mode.put_vline put_hline := mode.put_hline // Display -// width := mode.width -// height := mode.height -// dimensions := mode.dimensions -// set_height := mode.set_height -// set_width := mode.set_width -// set_dimensions := mode.set_dimensions 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 ae8905b6..8cd0b744 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -1,14 +1,47 @@ .{math, memory, dt} := @use("../../stn/src/lib.hb"); -.{Color, Surface, new_surface} := @use("lib.hb"); +.{Color} := @use("lib.hb"); .{Vec2} := math +Surface := struct { + buf: ^Color, + width: int, + height: int, +} + +new_surface := fn(width: int, height: int): Surface { + return .( + @inline(memory.alloc, Color, width * height), + width, + height, + ) +} + +surface_from_ptr := fn(ptr: ^Color, width: int, height: int): Surface { + return .( + ptr, + width, + height, + ) +} + +clone_surface := fn(surface: ^Surface): Surface { + new := new_surface(surface.width, surface.height) + @inline(memory.copy, Color, surface.buf, new.buf, @intcast(surface.width * surface.height)) + return new +} + +free_surface := fn(surface: ^Surface): void { + // todo + return +} + framebuffer := @as(^Color, idk) -init := fn(double_buffer: bool): Surface { +init := fn(doublebuffer: bool): Surface { framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0") width := dt.get(int, "framebuffer/fb0/width\0") height := dt.get(int, "framebuffer/fb0/height\0") - if double_buffer { + if doublebuffer { return new_surface(width, height) } else { return .(framebuffer, width, height) diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index 9b89cbea..b51c5ad3 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -1,8 +1,8 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF -alloc := fn($Expr: type, bytes: int): ^Expr { - pages := 1 + bytes / PAGE_SIZE +alloc := fn($Expr: type, num: int): ^Expr { + pages := 1 + @bitcast(@sizeof(Expr)) * num / PAGE_SIZE if pages <= MAX_ALLOC { return @bitcast(@inline(request_page, pages)) } @@ -19,6 +19,11 @@ alloc := fn($Expr: type, bytes: int): ^Expr { return @bitcast(ptr) } +free := fn($Expr: type, ptr: ^Expr, num: int): void { + // todo + return +} + request_page := fn(page_count: u8): ^u8 { msg := "\{00}\{01}xxxxxxxx\0" msg_page_count := msg + 1; diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb index 191c8ad8..a3b75ac9 100644 --- a/sysdata/programs/render_example/src/examples/surface.hb +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -16,13 +16,14 @@ example := fn(): void { pos_inner := Vec2(int).((image.width - side) / 2, (image.height - side) / 2) color := random.range(render.Color, render.black, render.white) loop { - render.clear(image, render.black) render.clear(screen, render.black) - + // color += .(1, 1, 1, 1) render.put_filled_rect(image, pos_inner, .(side, side), color) - render.put_rect(image, .(0, 0), .(image.width - 1, image.height - 1), render.white) + render.put_rect(image, pos_inner, .(side, side), render.black) + render.put_rect(image, .(0, 0), .(image.width - 1, image.height - 1), color) render.put_surface(screen, image, pos) + render.put_rect(image, pos_inner, .(side, side), color) render.sync(screen) if pos_inner.x == 0 | pos_inner.x == image.width - side { diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 835b3fda..e5deef95 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -31,17 +31,17 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +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 a94332370a8ea572ef04b5d5b8c18b4905674393 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sat, 19 Oct 2024 15:54:19 +0100 Subject: [PATCH 16/66] various render changes, implement sine & cosine for stn.math --- Cargo.lock | 38 ++++++------ .../holeybytes/kernel_services/mem_serve.rs | 9 --- sysdata/libraries/render/TODO.md | 33 +++++++++++ sysdata/libraries/render/src/image.hb | 41 ++++++++++++- sysdata/libraries/render/src/lib.hb | 2 + sysdata/libraries/render/src/software.hb | 58 ++++++++++--------- sysdata/libraries/stn/src/math.hb | 39 +++++++++++++ sysdata/programs/horizon/src/main.hb | 2 +- .../render_example/src/examples/image.hb | 10 ++-- .../render_example/src/examples/surface.hb | 17 ++++-- 10 files changed, 182 insertions(+), 67 deletions(-) create mode 100644 sysdata/libraries/render/TODO.md diff --git a/Cargo.lock b/Cargo.lock index bc371f91..8ad2b1ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" [[package]] name = "autocfg" @@ -145,9 +145,9 @@ checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.30" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "shlex", ] @@ -350,12 +350,12 @@ checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#ea736d88244ce1d85999d7ce6387a63c655b7000" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#ea736d88244ce1d85999d7ce6387a63c655b7000" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" 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#ea736d88244ce1d85999d7ce6387a63c655b7000" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" dependencies = [ "hbbytecode", ] @@ -420,9 +420,9 @@ checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -567,9 +567,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "limine" @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -945,9 +945,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" dependencies = [ "once_cell", "ring", @@ -968,9 +968,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -1035,9 +1035,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "67d42a0bd4ac281beff598909bb56a86acaf979b84483e1c79c10dcaf98f8cf3" dependencies = [ "itoa", "memchr", diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index f08a81f6..bacf2d5d 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -31,15 +31,6 @@ unsafe fn memcpy(mut dest: *mut u8, mut src: *const u8, mut count: usize) { 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); diff --git a/sysdata/libraries/render/TODO.md b/sysdata/libraries/render/TODO.md new file mode 100644 index 00000000..746ca4ba --- /dev/null +++ b/sysdata/libraries/render/TODO.md @@ -0,0 +1,33 @@ +# Images +- General over image format +- Support formats: + - PNG +- Animation + +# API +- Font Loader + - VGA fonts +- Colour operations: + - Alpha Composite + - Invert +- Surface Operations: + - FlipV + - FlipH + - Resize + - Wrap the colour operations + - Tile + - Gradient overlay +- Draw operations: + - Curve raster algorithm + - VGA font fast blit + - VGA font render + - Polygon + +# Backend +- SVGA Driver +- Support whatever vulkan stuff able is cooking + +# Bits and bobs on the table +- Funny 3D Renderer +- stn.memory.swap & kernel message +- Make memory.{copy, set} smart \ No newline at end of file diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb index 1f55cbd6..fd683f51 100644 --- a/sysdata/libraries/render/src/image.hb +++ b/sysdata/libraries/render/src/image.hb @@ -1,5 +1,5 @@ -.{Color, Surface} := @use("./lib.hb"); -.{log} := @use("../../stn/src/lib.hb") +.{Color, Surface, new_surface} := @use("./lib.hb"); +.{log, memory} := @use("../../stn/src/lib.hb") BitmapFileHeader := packed struct { img_type: u16, @@ -63,4 +63,41 @@ surface_from_bmp := fn(bmp: ^u8): Surface { } return .(@bitcast(bmp), @intcast(info_header.width), @intcast(info_header.height)) +} + +new_surface_from_bmp := fn(bmp: ^u8): Surface { + 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(Surface, idk) + } + info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) + bmp += file_header.offset + + width := @as(int, @intcast(info_header.width)) + height := @as(int, @intcast(info_header.height)) + + surface := new_surface(width, height) + top_start_idx := surface.buf + bottom_start_idx := surface.buf + width * (height - 1) + rows_to_copy := height + top_cursor := @as(^Color, @bitcast(bmp)) + bottom_cursor := top_cursor + width * (height - 1) + + loop if rows_to_copy <= 1 break else { + @inline(memory.copy, Color, top_cursor, bottom_start_idx, @bitcast(width)) + @inline(memory.copy, Color, bottom_cursor, top_start_idx, @bitcast(width)) + + top_start_idx += surface.width + bottom_start_idx -= surface.width + top_cursor += width + bottom_cursor -= width + rows_to_copy -= 2 + } + + if rows_to_copy == 1 { + @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(width)) + } + + return surface } \ No newline at end of file diff --git a/sysdata/libraries/render/src/lib.hb b/sysdata/libraries/render/src/lib.hb index 9227f528..3132b3b8 100644 --- a/sysdata/libraries/render/src/lib.hb +++ b/sysdata/libraries/render/src/lib.hb @@ -11,6 +11,8 @@ new_surface := mode.new_surface surface_from_ptr := mode.surface_from_ptr clone_surface := mode.clone_surface free_surface := mode.free_surface +index := mode.index +indexptr := mode.indexptr // Colours Color := packed struct {b: u8, g: u8, r: u8, a: u8} diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 8cd0b744..038bbea7 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -31,7 +31,7 @@ clone_surface := fn(surface: ^Surface): Surface { } free_surface := fn(surface: ^Surface): void { - // todo + // todo: depends on stn.memory.free return } @@ -56,18 +56,22 @@ sync := fn(surface: Surface): void { return @inline(memory.copy, Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height)) } -screenidx := fn(surface: Surface, x: int, y: int): int { +index := fn(surface: Surface, x: int, y: int): int { return x + surface.width * y } +indexptr := fn(surface: Surface, x: int, y: int): ^Color { + return surface.buf + @inline(index, surface, x, y) +} + put_pixel := fn(surface: Surface, pos: Vec2(int), color: Color): void { - *(surface.buf + @inline(screenidx, surface, pos.x, pos.y)) = color + *@inline(indexptr, surface, pos.x, pos.y) = color return } put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { - top_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y) - bottom_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y + tr.y - 1) + top_start_idx := @inline(indexptr, surface, pos.x, pos.y) + bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1) rows_to_fill := tr.y loop if rows_to_fill <= 1 break else { @@ -87,19 +91,19 @@ put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Co } put_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { - start_idx := @inline(screenidx, surface, pos.x, pos.y) - end_idx := @inline(screenidx, surface, pos.x, pos.y + tr.y) - right_start_idx := @inline(screenidx, surface, pos.x + tr.x, pos.y) + start_idx := @inline(indexptr, surface, pos.x, pos.y) + end_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y) + right_start_idx := @inline(indexptr, surface, pos.x + tr.x, pos.y) loop if start_idx > end_idx break else { - *(surface.buf + start_idx) = color; - *(surface.buf + right_start_idx) = color + *start_idx = color; + *right_start_idx = color start_idx += surface.width right_start_idx += surface.width } - @inline(memory.set, Color, &color, surface.buf + @inline(screenidx, surface, pos.x, pos.y), @bitcast(tr.x + 1)) - @inline(memory.set, Color, &color, surface.buf + @inline(screenidx, surface, pos.x, pos.y + tr.y), @bitcast(tr.x + 1)) + @inline(memory.set, Color, &color, @inline(indexptr, surface, pos.x, pos.y), @bitcast(tr.x + 1)) + @inline(memory.set, Color, &color, @inline(indexptr, surface, pos.x, pos.y + tr.y), @bitcast(tr.x + 1)) return } @@ -116,7 +120,7 @@ put_line_low := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color) y := p0.y x := p0.x loop if x == p1.x break else { - *(surface.buf + @inline(screenidx, surface, x, y)) = color + *@inline(indexptr, surface, x, y) = color if D > 0 { y += yi D += 2 * (dy - dx) @@ -140,7 +144,7 @@ put_line_high := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color x := p0.x y := p0.y loop if y == p1.y break else { - *(surface.buf + @inline(screenidx, surface, x, y)) = color + *@inline(indexptr, surface, x, y) = color if D > 0 { x += xi D += 2 * (dx - dy) @@ -169,16 +173,21 @@ put_line := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): vo return } -put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int)): void { - top_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y) - bottom_start_idx := surface.buf + @inline(screenidx, surface, pos.x, pos.y + top.height - 1) +put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int), flip_v: bool): void { + top_start_idx := @inline(indexptr, surface, pos.x, pos.y) + bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + top.height - 1) rows_to_copy := top.height top_cursor := top.buf bottom_cursor := top.buf + top.width * (top.height - 1) loop if rows_to_copy <= 1 break else { - @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width)) - @inline(memory.copy, Color, bottom_cursor, bottom_start_idx, @bitcast(top.width)) + if flip_v { + @inline(memory.copy, Color, top_cursor, bottom_start_idx, @bitcast(top.width)) + @inline(memory.copy, Color, bottom_cursor, top_start_idx, @bitcast(top.width)) + } else { + @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width)) + @inline(memory.copy, Color, bottom_cursor, bottom_start_idx, @bitcast(top.width)) + } top_start_idx += surface.width bottom_start_idx -= surface.width @@ -208,7 +217,7 @@ put_trirect := fn(surface: Surface, pos: Vec2(int), size: Vec2(int), color0: Col target := pos + size loop if pos.x == target.x break else { - put_vline(surface, pos.x, pos.y, target.y, color0) + @inline(put_vline, surface, pos.x, pos.y, target.y, color0) @inline(put_vline, surface, pos.x, pos.y, start_y, color1) pos += step } @@ -226,7 +235,7 @@ put_vline := fn(surface: Surface, x: int, y0: int, y1: int, color: Color): void y := y0 loop if y == y1 break else { - *(surface.buf + @inline(screenidx, surface, x, y)) = color + *@inline(indexptr, surface, x, y) = color y += 1 } @@ -240,12 +249,7 @@ put_hline := fn(surface: Surface, y: int, x0: int, x1: int, color: Color): void x0 = x1 x1 = tmp } - x := x0 - - loop if x == x1 break else { - *(surface.buf + @inline(screenidx, surface, x, y)) = color - x += 1 - } + @inline(memory.set, Color, &color, @inline(indexptr, surface, x0, y), @bitcast(x1 - x0 - 1)) return } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index f4e0c847..563e9c3d 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -10,7 +10,46 @@ max := fn($Expr: type, a: Expr, b: Expr): Expr { c := a - b return a - (c & c >> @intcast(@sizeof(Expr) - 1)) } +signum := fn($Expr: type, x: Expr): int { + if x > @as(Expr, @intcast(0)) { + return 1 + } else if x < @as(Expr, @intcast(0)) { + return -1 + } else { + return 0 + } +} +signincl := fn($Expr: type, x: Expr): int { + if x > @as(Expr, @intcast(0)) { + return 1 + } + return -1 +} Vec2 := fn($Expr: type): type { return struct {x: Expr, y: Expr} +} + +SIN_TABLE := [int].(0, 174, 348, 523, 697, 871, 1045, 1218, 1391, 1564, 1736, 1908, 2079, 2249, 2419, 2588, 2756, 2923, 3090, 3255, 3420, 3583, 3746, 3907, 4067, 4226, 4384, 4540, 4695, 4848, 5000, 5150, 5299, 5446, 5591, 5735, 5877, 6018, 6156, 6293, 6427, 6560, 6691, 6819, 6946, 7071, 7193, 7313, 7431, 7547, 7660, 7771, 7880, 7986, 8090, 8191, 8290, 8386, 8480, 8571, 8660, 8746, 8829, 8910, 8987, 9063, 9135, 9205, 9271, 9335, 9396, 9455, 9510, 9563, 9612, 9659, 9702, 9743, 9781, 9816, 9848, 9877, 9902, 9925, 9945, 9961, 9975, 9986, 9993, 9998, 10000) + +sin_i := fn(theta_deg: int, amplitude: int): int { + theta := theta_deg % 360 + if theta < 0 { + theta += 360 + } + + quadrant := theta / 90 + theta = theta % 90 + + sign := 1 - ((quadrant & 2) >> 1) * 2 + complement := quadrant & 1 + + index := theta * (1 - complement) + (90 - theta) * complement + sin_value := SIN_TABLE[index] * sign + + return (sin_value * amplitude + 5000) / 10000 +} + +cos_i := fn(theta_deg: int, amplitude: int): int { + return @inline(sin_i, theta_deg + 90, amplitude) } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index e9ba1729..95e5cfba 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -71,7 +71,7 @@ main := fn(): int { // Apply the image to the screen pos := Vec2(int).(x, 100) - render.put_surface(screen, window, pos) + render.put_surface(screen, window, pos, false) if window_count >= 1 { x = 10 break diff --git a/sysdata/programs/render_example/src/examples/image.hb b/sysdata/programs/render_example/src/examples/image.hb index ce59daf4..b7e1ba0a 100644 --- a/sysdata/programs/render_example/src/examples/image.hb +++ b/sysdata/programs/render_example/src/examples/image.hb @@ -8,27 +8,27 @@ bmp_1 := @embed("./assets/able.bmp") bmp_2 := @embed("./assets/mini.bmp") example := fn(): void { - screen := render.init(true) images := [render.Surface; 2].( render.image.surface_from_bmp(@bitcast(&bmp_1)), render.image.surface_from_bmp(@bitcast(&bmp_2)), ) + screen := render.init(true) vel := Vec2(int).(1, 1) pos := Vec2(int).(100, 100) - n := -1 + n := 0 loop { image := images[n] - render.put_surface(screen, image, pos) + render.put_surface(screen, image, pos, false) render.sync(screen) render.clear(screen, render.black) if pos.x == 0 | pos.x == screen.width - image.width { vel.x = -vel.x - n = -1 - n + n = 1 - n } if pos.y == 0 | pos.y == screen.height - image.height { vel.y = -vel.y - n = -1 - n + n = 1 - n } pos += vel diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb index a3b75ac9..50353ebc 100644 --- a/sysdata/programs/render_example/src/examples/surface.hb +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -14,7 +14,10 @@ example := fn(): void { side := image.width / 8 vel_inner := Vec2(int).(1, 1) pos_inner := Vec2(int).((image.width - side) / 2, (image.height - side) / 2) - color := random.range(render.Color, render.black, render.white) + // workaround for compiler bug + color := render.Color.(0, 0, 0, 0) + color = random.range(render.Color, render.black, render.white) + target_color := color loop { render.clear(screen, render.black) // color += .(1, 1, 1, 1) @@ -22,17 +25,17 @@ example := fn(): void { render.put_rect(image, pos_inner, .(side, side), render.black) render.put_rect(image, .(0, 0), .(image.width - 1, image.height - 1), color) - render.put_surface(screen, image, pos) + render.put_surface(screen, image, pos, false) render.put_rect(image, pos_inner, .(side, side), color) render.sync(screen) if pos_inner.x == 0 | pos_inner.x == image.width - side { vel_inner.x = -vel_inner.x - color = random.range(render.Color, render.black, render.white) + target_color = random.range(render.Color, render.black, render.white) } if pos_inner.y == 0 | pos_inner.y == image.height - side { vel_inner.y = -vel_inner.y - color = random.range(render.Color, render.black, render.white) + target_color = random.range(render.Color, render.black, render.white) } if pos.x == 0 | pos.x == screen.width - image.width { @@ -42,6 +45,12 @@ example := fn(): void { vel.y = -vel.y } + color += .( + @intcast(color.b < target_color.b) - @intcast(color.b > target_color.b), + @intcast(color.g < target_color.g) - @intcast(color.g > target_color.g), + @intcast(color.r < target_color.r) - @intcast(color.r > target_color.r), + 0, + ) pos += vel pos_inner += vel_inner } From 41d9c0b82ad5202c480a95cc45f987e2fffa7936 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sat, 19 Oct 2024 19:45:41 +0100 Subject: [PATCH 17/66] miscellaneous changes, free performance tweaks, and fiddle with enabling avx finally --- Cargo.lock | 72 ++++----------- dev/src/main.rs | 1 + kernel/.cargo/config.toml | 6 -- kernel/Cargo.toml | 2 +- kernel/src/arch/x86_64/mod.rs | 92 ++++++++++--------- kernel/src/holeybytes/ecah.rs | 15 --- .../holeybytes/kernel_services/mem_serve.rs | 49 +++------- kernel/src/holeybytes/mod.rs | 2 +- ...4-v3-ableos.json => x86_64_v3-ableos.json} | 0 repbuild/Cargo.toml | 5 +- repbuild/src/main.rs | 18 +++- .../render_example/src/examples/surface.hb | 3 +- 12 files changed, 103 insertions(+), 162 deletions(-) rename kernel/targets/{x86_64-v3-ableos.json => x86_64_v3-ableos.json} (100%) diff --git a/Cargo.lock b/Cargo.lock index 8ad2b1ab..536d9609 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,18 +26,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "allocator-api2" version = "0.2.18" @@ -127,9 +115,6 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -dependencies = [ - "allocator-api2", -] [[package]] name = "byteorder" @@ -255,6 +240,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -331,43 +322,37 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - [[package]] name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#15e4762d4ac8993d12fe2dd54e2b2d842c8a034b" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#15e4762d4ac8993d12fe2dd54e2b2d842c8a034b" dependencies = [ - "hashbrown 0.15.0", + "hashbrown", "hbbytecode", "hbvm", "log", - "regalloc2", ] [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#15e4762d4ac8993d12fe2dd54e2b2d842c8a034b" dependencies = [ "hbbytecode", ] @@ -514,7 +499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown", ] [[package]] @@ -545,7 +530,7 @@ dependencies = [ "aarch64-cpu", "crossbeam-queue", "derive_more", - "hashbrown 0.14.5", + "hashbrown", "hbvm", "limine", "log", @@ -830,19 +815,6 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "regalloc2" -version = "0.10.2" -source = "git+https://github.com/jakubDoka/regalloc2?branch=reuse-allocations#21c43e3ee182824e92e2b25f1d3c03ed47f9c02b" -dependencies = [ - "allocator-api2", - "bumpalo", - "hashbrown 0.14.5", - "log", - "rustc-hash", - "smallvec", -] - [[package]] name = "regex-syntax" version = "0.8.5" @@ -1035,9 +1007,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.131" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d42a0bd4ac281beff598909bb56a86acaf979b84483e1c79c10dcaf98f8cf3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -1325,12 +1297,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "versioning" version = "0.1.3" diff --git a/dev/src/main.rs b/dev/src/main.rs index c7128221..aaa017b1 100644 --- a/dev/src/main.rs +++ b/dev/src/main.rs @@ -1,3 +1,4 @@ +#![allow(unused)] use std::io::Write; use idl::build_idl; diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index dc3c3df2..e0525903 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -4,9 +4,3 @@ build-std-features = ["compiler-builtins-mem"] [build] target = "./targets/x86_64-ableos.json" - -# [target.'cfg(target_arch = "x86_64")'] -# rustflags = [ -# "-C", -#"target-feature=+sse4.1,+avx,+aes,+fma,+popcnt,+bmi2,+avx2,+lzcnt,+xsave", -# ] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ff6604b0..3f7e4bcb 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -16,7 +16,7 @@ uart_16550 = { version = "0.3", features = ["nightly"] } xml.git = "https://git.ablecorp.us/ableos/ableos_userland" versioning.git = "https://git.ablecorp.us/ableos/ableos_userland" # able_graphics_library.git = "https://git.ablecorp.us/ableos/ableos_userland" -hashbrown = { version = "0.14", features = ["nightly"] } +hashbrown = { version = "0.15", features = ["nightly"] } limine = "0.1" [dependencies.crossbeam-queue] diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 413bde69..c45a7e2f 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -30,6 +30,7 @@ const INITIAL_KERNEL_HEAP_SIZE: *const () = _initial_kernel_heap_size as _; #[no_mangle] #[naked] +#[cfg(not(target_feature = "avx2"))] unsafe extern "C" fn _kernel_start() -> ! { // Initialise SSE, then jump to kernel entrypoint core::arch::asm!( @@ -49,58 +50,59 @@ unsafe extern "C" fn _kernel_start() -> ! { ) } -// #[no_mangle] -// #[naked] -// unsafe extern "C" fn _kernel_start() -> ! { -// core::arch::asm!( -// // Enable protected mode and configure control registers -// "mov rax, cr0", -// "and ax, 0xFFFB", // Clear CR0.EM (bit 2) for coprocessor emulation -// "or ax, 0x2", // Set CR0.MP (bit 1) for coprocessor monitoring -// "mov cr0, rax", +#[no_mangle] +#[naked] +#[cfg(target_feature = "avx2")] +unsafe extern "C" fn _kernel_start() -> ! { + core::arch::asm!( + // Enable protected mode and configure control registers + "mov rax, cr0", + "and ax, 0xFFFB", // Clear CR0.EM (bit 2) for coprocessor emulation + "or ax, 0x2", // Set CR0.MP (bit 1) for coprocessor monitoring + "mov cr0, rax", -// "mov rax, cr4", -// "or ax, (1 << 9) | (1 << 10)", // Set CR4.OSFXSR (bit 9) and CR4.OSXMMEXCPT (bit 10) -// "mov cr4, rax", + "mov rax, cr4", + "or ax, (1 << 9) | (1 << 10)", // Set CR4.OSFXSR (bit 9) and CR4.OSXMMEXCPT (bit 10) + "mov cr4, rax", -// // Enable OSXSAVE (required for AVX, AVX2, and XSAVE) -// "mov rax, cr4", -// "or eax, 1 << 18", // Set CR4.OSXSAVE (bit 18) -// "mov cr4, rax", + // Enable OSXSAVE (required for AVX, AVX2, and XSAVE) + "mov rax, cr4", + "or eax, 1 << 18", // Set CR4.OSXSAVE (bit 18) + "mov cr4, rax", -// // Enable AVX and AVX2 state saving -// "xor rcx, rcx", -// "xgetbv", -// "or eax, 7", // Enable SSE, AVX, and AVX2 state saving -// "xsetbv", + // Enable AVX and AVX2 state saving + "xor rcx, rcx", + "xgetbv", + "or eax, 7", // Enable SSE, AVX, and AVX2 state saving + "xsetbv", -// // Check for AVX and XSAVE support -// "mov eax, 1", -// "cpuid", -// "and ecx, 0x18000000", -// "cmp ecx, 0x18000000", -// "jne {1}", // Jump if AVX/OSXSAVE is not supported + // Check for AVX and XSAVE support + "mov eax, 1", + "cpuid", + "and ecx, 0x18000000", + "cmp ecx, 0x18000000", + "jne {1}", // Jump if AVX/OSXSAVE is not supported -// // Check for BMI2 and AVX2 support -// "mov eax, 7", -// "xor ecx, ecx", -// "cpuid", -// "and ebx, (1 << 8) | (1 << 5)", // Check BMI2 (bit 8) and AVX2 (bit 5) -// "cmp ebx, (1 << 8) | (1 << 5)", // Compare to ensure both are supported + // Check for BMI2 and AVX2 support + "mov eax, 7", + "xor ecx, ecx", + "cpuid", + "and ebx, (1 << 8) | (1 << 5)", // Check BMI2 (bit 8) and AVX2 (bit 5) + "cmp ebx, (1 << 8) | (1 << 5)", // Compare to ensure both are supported -// // Check for LZCNT and POPCNT support -// "mov eax, 1", -// "cpuid", -// "and ecx, (1 << 5) | (1 << 23)", // Check LZCNT (bit 5) and POPCNT (bit 23) -// "cmp ecx, (1 << 5) | (1 << 23)", // Compare to ensure both are supported + // Check for LZCNT and POPCNT support + "mov eax, 1", + "cpuid", + "and ecx, (1 << 5) | (1 << 23)", // Check LZCNT (bit 5) and POPCNT (bit 23) + "cmp ecx, (1 << 5) | (1 << 23)", // Compare to ensure both are supported -// // Jump to the kernel entry point -// "jmp {0}", -// sym start, -// sym oops, -// options(noreturn), -// ) -// } + // Jump to the kernel entry point + "jmp {0}", + sym start, + sym oops, + options(noreturn), + ) +} unsafe extern "C" fn oops() -> ! { panic!("your cpu is ancient >:(") diff --git a/kernel/src/holeybytes/ecah.rs b/kernel/src/holeybytes/ecah.rs index 0d077d36..5535113b 100644 --- a/kernel/src/holeybytes/ecah.rs +++ b/kernel/src/holeybytes/ecah.rs @@ -26,9 +26,6 @@ unsafe fn x86_in(address: u16) -> T { #[inline(always)] pub fn handler(vm: &mut Vm) { let ecall_number = vm.registers[2].cast::(); - // log::info!("eca called :pensive:"); - // debug!("Ecall number {:?}", ecall_number); - //info!("Register dump: {:?}", vm.registers); match ecall_number { 0 => { @@ -207,15 +204,3 @@ pub enum LogError { NoMessages, InvalidLogFormat, } - -// use {alloc::vec, log::Record}; -// fn memory_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> { -// let mut val = alloc::vec::Vec::new(); -// for _ in 0..4096 { -// val.push(0); -// } -// info!("Block address: {:?}", val.as_ptr()); -// vm.registers[1] = hbvm::value::Value(val.as_ptr() as u64); -// vm.registers[2] = hbvm::value::Value(4096); -// Ok(()) -// } diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index bacf2d5d..29c22820 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -17,38 +17,16 @@ pub enum MemoryQuotaType { } fn alloc_page(vm: &mut Vm, _mem_addr: u64, _length: usize) -> Result<(), MemoryServiceError> { - let ptr = unsafe { alloc(Layout::from_size_align_unchecked(4096, 4096)) }; + let ptr = unsafe { alloc(Layout::from_size_align_unchecked(4096, 8)) }; info!("Block address: {:?}", ptr); vm.registers[1] = hbvm::value::Value(ptr as u64); vm.registers[2] = hbvm::value::Value(4096); 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; - } - - 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) { + const BLOCK_SIZE: usize = 64; let mut remaining = count * size; if remaining < 16 { @@ -56,11 +34,11 @@ unsafe fn memset(mut dest: *mut u8, src: *const u8, count: usize, size: usize) { return; } - let mut buffer = [0u8; 64]; + let mut buffer = [0u8; BLOCK_SIZE]; let mut buffer_size = size; src.copy_to_nonoverlapping(buffer.as_mut_ptr(), size); - while buffer_size * 2 <= 64 { + while buffer_size * 2 <= BLOCK_SIZE { buffer .as_mut_ptr() .copy_to_nonoverlapping(buffer.as_mut_ptr().add(buffer_size), buffer_size); @@ -68,12 +46,15 @@ unsafe fn memset(mut dest: *mut u8, src: *const u8, count: usize, size: usize) { } let buffer_ptr = buffer.as_ptr() as *const u64; + + while (dest as usize) & 7 != 0 && remaining >= 8 { + buffer.as_ptr().copy_to_nonoverlapping(dest, 1); + dest = dest.add(1); + remaining -= 1; + } + 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 as *mut u64) = *buffer_ptr; dest = dest.add(8); remaining -= 8; } @@ -101,7 +82,7 @@ pub fn memory_msg_handler( let ptr = alloc(Layout::from_size_align_unchecked( page_count as usize * 4096, - 4096, + 8, )); vm.registers[1] = hbvm::value::Value(ptr as u64); @@ -117,7 +98,7 @@ pub fn memory_msg_handler( dealloc( mptr as *mut u8, - Layout::from_size_align_unchecked(page_count as usize * 4096, 4096), + Layout::from_size_align_unchecked(page_count as usize * 4096, 8), ) }, 2 => { @@ -147,7 +128,7 @@ pub fn memory_msg_handler( 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; - memcpy(dest, src, count); + src.copy_to_nonoverlapping(dest, count); }, 5 => unsafe { let count = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap_unchecked()) as usize; diff --git a/kernel/src/holeybytes/mod.rs b/kernel/src/holeybytes/mod.rs index 9b9a1d27..42e4812d 100644 --- a/kernel/src/holeybytes/mod.rs +++ b/kernel/src/holeybytes/mod.rs @@ -97,7 +97,7 @@ impl HandlePageFault for PageFaultHandler { #[inline(always)] const fn stack_layout() -> Layout { - unsafe { Layout::from_size_align_unchecked(STACK_SIZE, 4096) } + unsafe { Layout::from_size_align_unchecked(STACK_SIZE, 8) } } #[inline(always)] diff --git a/kernel/targets/x86_64-v3-ableos.json b/kernel/targets/x86_64_v3-ableos.json similarity index 100% rename from kernel/targets/x86_64-v3-ableos.json rename to kernel/targets/x86_64_v3-ableos.json diff --git a/repbuild/Cargo.toml b/repbuild/Cargo.toml index 4d7abaad..01e77555 100644 --- a/repbuild/Cargo.toml +++ b/repbuild/Cargo.toml @@ -20,8 +20,9 @@ derive_more = { version = "1", default-features = false, features = [ error-stack = "0.5" fatfs = "0.3" toml = "0.8" -# hbasm.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git" -hblang.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git" +hblang = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ + "std", +], default-features = false } log = "0.4" raw-cpuid = "11" diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 9963774e..f32f9935 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -38,6 +38,8 @@ fn main() -> Result<(), Error> { target = Target::Riscv64Virt; } else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" { target = Target::Aarch64; + } else if arg == "avx2" { + target = Target::X86_64Avx2; } else { return Err(report!(Error::InvalidSubCom)); } @@ -61,6 +63,8 @@ fn main() -> Result<(), Error> { target = Target::Aarch64; } else if arg == "--noaccel" { do_accel = false; + } else if arg == "avx2" { + target = Target::X86_64Avx2; } else { return Err(report!(Error::InvalidSubCom)); } @@ -304,6 +308,9 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { if target == Target::Aarch64 { com.args(["--target", "targets/aarch64-virt-ableos.json"]); } + if target == Target::X86_64Avx2 { + com.args(["--target", "targets/x86_64_v3-ableos.json"]); + } match com.status() { Ok(s) if s.code() != Some(0) => bail!(Error::Build), @@ -317,6 +324,10 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { path.push_str("_x86-64"); "target/x86_64-ableos" } + Target::X86_64Avx2 => { + path.push_str("_x86-64"); + "target/x86_64_v3-ableos" + } Target::Riscv64Virt => "target/riscv64-virt-ableos", Target::Aarch64 => { path.push_str("_aarch64"); @@ -341,7 +352,7 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { let target_str = match target { - Target::X86_64 => "qemu-system-x86_64", + Target::X86_64 | Target::X86_64Avx2 => "qemu-system-x86_64", Target::Riscv64Virt => "qemu-system-riscv64", Target::Aarch64 => "qemu-system-aarch64", }; @@ -384,7 +395,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { }; match target { - Target::X86_64 => { + Target::X86_64 | Target::X86_64Avx2 => { #[rustfmt::skip] com.args([ "-bios", &ovmf_path.change_context(Error::OvmfFetch)?, @@ -440,7 +451,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { fn fetch_ovmf(target: Target) -> Result { let (ovmf_url, ovmf_path) = match target { - Target::X86_64 => ( + Target::X86_64 | Target::X86_64Avx2 => ( "https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd", "target/RELEASEX64_OVMF.fd", ), @@ -489,6 +500,7 @@ impl Context for OvmfFetchError {} #[derive(Clone, Copy, PartialEq, Eq)] enum Target { X86_64, + X86_64Avx2, Riscv64Virt, Aarch64, } diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb index 50353ebc..003baec7 100644 --- a/sysdata/programs/render_example/src/examples/surface.hb +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -3,7 +3,7 @@ render := @use("../../../../libraries/render/src/lib.hb") /* expected result: - the square example bounces around the screen */ + bouncing gradient square inside coloured bouncing box inside black screen */ example := fn(): void { screen := render.init(true) @@ -20,7 +20,6 @@ example := fn(): void { target_color := color loop { render.clear(screen, render.black) - // color += .(1, 1, 1, 1) render.put_filled_rect(image, pos_inner, .(side, side), color) render.put_rect(image, pos_inner, .(side, side), render.black) render.put_rect(image, .(0, 0), .(image.width - 1, image.height - 1), color) From 2ba2dcb464f3af1cb0eeaae6d5c57d438b487e0f Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 20 Oct 2024 12:11:29 +0100 Subject: [PATCH 18/66] change logger message format so it doesn't mutate the string --- kernel/src/holeybytes/kernel_services/logging_service.rs | 8 ++++++-- sysdata/libraries/stn/src/log.hb | 7 +++---- sysdata/libraries/stn/src/string.hb | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/kernel/src/holeybytes/kernel_services/logging_service.rs b/kernel/src/holeybytes/kernel_services/logging_service.rs index 48a38b94..b1fbf4ae 100644 --- a/kernel/src/holeybytes/kernel_services/logging_service.rs +++ b/kernel/src/holeybytes/kernel_services/logging_service.rs @@ -9,12 +9,16 @@ use log::Record; pub fn log_msg_handler(_vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> { let msg_vec = block_read(mem_addr, length); - let log_level = msg_vec.last().unwrap(); + let log_level = msg_vec[0]; + let strptr = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap()); + let strlen = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap()) as usize; + + let str = block_read(strptr, strlen); let file_name = "None"; let line_number = 0; - match core::str::from_utf8(&msg_vec[..msg_vec.len() - 1]) { + match core::str::from_utf8(&str) { Ok(strr) => { use log::Level::*; let log_level = match log_level { diff --git a/sysdata/libraries/stn/src/log.hb b/sysdata/libraries/stn/src/log.hb index 72c68ebd..95d91c1e 100644 --- a/sysdata/libraries/stn/src/log.hb +++ b/sysdata/libraries/stn/src/log.hb @@ -1,10 +1,9 @@ string := @use("string.hb") -log := fn($Level: u8, message: ^u8): void { - message_length := @inline(string.length, message); - *(message + message_length) = Level +LogMsg := packed struct {level: u8, string: ^u8, strlen: uint} - return @eca(3, 1, message, message_length + 1) +log := fn($Level: u8, message: ^u8): void { + return @eca(3, 1, LogMsg.(Level, message, @inline(string.length, message)), @sizeof(LogMsg)) } error := fn(message: ^u8): void return @inline(log, 0, message) diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 042de025..8e800011 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -1,5 +1,5 @@ -length := fn(ptr: ^u8): int { - len := 0 +length := fn(ptr: ^u8): uint { + len := @as(uint, 0) loop if *(ptr + len) == 0 break else len += 1 return len } From bcfaf89ed04d72084919084c4e0c5741e1989acb Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 20 Oct 2024 12:36:34 +0100 Subject: [PATCH 19/66] fix buffer mutating message --- .../kernel_services/service_definition_service.rs | 10 ++++------ sysdata/libraries/stn/src/buffer.hb | 11 ++++------- sysdata/libraries/stn/src/string.hb | 4 +--- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/kernel/src/holeybytes/kernel_services/service_definition_service.rs b/kernel/src/holeybytes/kernel_services/service_definition_service.rs index 52f37ad6..7cf03ecc 100644 --- a/kernel/src/holeybytes/kernel_services/service_definition_service.rs +++ b/kernel/src/holeybytes/kernel_services/service_definition_service.rs @@ -22,21 +22,19 @@ pub enum ServiceError { pub fn sds_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), ServiceError> { let msg_vec = block_read(mem_addr, length); let sds_event_type: ServiceEventType = msg_vec[0].into(); - - // info!("Length {}", msg_vec.len()); + let strptr = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap()); + let strlen = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap()) as usize; + let string_vec = block_read(strptr, strlen); + let string = core::str::from_utf8(string_vec).expect("Our bytes should be valid utf8"); use ServiceEventType::*; match sds_event_type { CreateService => { - let string = - core::str::from_utf8(&msg_vec[1..]).expect("Our bytes should be valid utf8"); let ret = sds_create_service(string); vm.registers[1] = hbvm::value::Value(ret as u64); } DeleteService => todo!(), SearchServices => { - let string = - core::str::from_utf8(&msg_vec[1..]).expect("Our bytes should be valid utf8"); let ret = sds_search_service(string); vm.registers[1] = hbvm::value::Value(ret as u64); } diff --git a/sysdata/libraries/stn/src/buffer.hb b/sysdata/libraries/stn/src/buffer.hb index 554783b1..23d7e67e 100644 --- a/sysdata/libraries/stn/src/buffer.hb +++ b/sysdata/libraries/stn/src/buffer.hb @@ -8,15 +8,12 @@ write := fn(msg: ^u8, buffer_id: int, length: int): void { return @eca(3, buffer_id, msg, length) } +BufferMsg := packed struct {operation: u8, msg: ^u8, msg_len: uint} + create := fn(msg: ^u8): int { - msg_length := @inline(string.length, msg); - *msg = 0 - return @eca(3, 0, msg, msg_length) + return @eca(3, 0, BufferMsg.(0, msg, @inline(string.length, msg)), @sizeof(BufferMsg)) } search := fn(msg: ^u8): int { - msg_length := @inline(string.length, msg); - *msg = 3 - - return @eca(3, 0, msg, msg_length) + return @eca(3, 0, BufferMsg.(3, msg, @inline(string.length, msg)), @sizeof(BufferMsg)) } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 8e800011..679c180c 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -31,9 +31,7 @@ display_int := fn(num: int, p: ^u8): ^u8 { } reverse := fn(s: ^u8): void { - //reverse a string, don't remove digits - len := 0 - loop if *(s + len) == 0 break else len += 1 + len := @inline(length, s) i := 0 j := len - 1 temp := 0 From b35b430047f1c1a7a3bcc3b6eda3f92bd36a67c0 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 20 Oct 2024 13:31:44 +0100 Subject: [PATCH 20/66] display_int radices --- sysdata/libraries/stn/src/string.hb | 37 ++++++++++++++++++++++++----- sysdata/programs/tests/src/main.hb | 2 +- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 679c180c..c84ac557 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -4,32 +4,57 @@ length := fn(ptr: ^u8): uint { return len } -// WTFFF is wrong with display_int -display_int := fn(num: int, p: ^u8): ^u8 { +display_int := fn(num: int, p: ^u8, radix: int): ^u8 { ptr := p negative := num < 0 if negative { num = -num } + + if radix == 2 { + *ptr = 48 + ptr += 1; + *ptr = 98 + ptr += 1 + } else if radix == 16 { + *ptr = 48 + ptr += 1; + *ptr = 120 + ptr += 1 + } else if radix == 8 { + *ptr = 48 + ptr += 1; + *ptr = 111 + ptr += 1 + } + digits_start := ptr if num == 0 { *ptr = 48 ptr += 1 } else { loop if num == 0 break else { - *ptr = num % 10 + 48 + digit := num % radix + if digit < 10 { + *ptr = digit + 48 + } else { + *ptr = digit + 55 + } ptr += 1 - num /= 10 + num /= radix } } + if negative { *ptr = 45 ptr += 1 }; + *ptr = 0 - @inline(reverse, p) + + @inline(reverse, digits_start) + return p } - reverse := fn(s: ^u8): void { len := @inline(length, s) i := 0 diff --git a/sysdata/programs/tests/src/main.hb b/sysdata/programs/tests/src/main.hb index e89eafb5..b2eee5ee 100644 --- a/sysdata/programs/tests/src/main.hb +++ b/sysdata/programs/tests/src/main.hb @@ -12,7 +12,7 @@ main := fn(): int { buf := "\0\0\0\0" x := 0 loop if x == 255 break else { - log.info(string.display_int(x, buf)) + log.info(string.display_int(x, buf, 10)) x += 1 } return 0 From 6ff65eee417ad428b96606ad6569383edcf8423c Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 23 Oct 2024 21:22:28 +0100 Subject: [PATCH 21/66] kaboom --- kernel/src/arch/x86_64/mod.rs | 1 + .../holeybytes/kernel_services/mem_serve.rs | 6 +- sysdata/libraries/horizon_api/src/lib.hb | 3 +- sysdata/libraries/render/src/software.hb | 10 ++- sysdata/libraries/stn/src/buffer.hb | 8 +-- sysdata/libraries/stn/src/memory.hb | 66 +++++++++++-------- sysdata/programs/horizon/src/main.hb | 3 +- sysdata/programs/ps2_driver/src/main.hb | 5 +- sysdata/programs/serial_driver/src/main.hb | 2 +- .../programs/serial_driver_test/src/main.hb | 2 +- sysdata/system_config.toml | 9 +-- 11 files changed, 64 insertions(+), 51 deletions(-) diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index c45a7e2f..d0270ff9 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -251,6 +251,7 @@ unsafe extern "C" fn start() -> ! { /// Spin loop pub fn spin_loop() -> ! { loop { + core::hint::spin_loop(); x86_64::instructions::hlt() } } diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index 29c22820..743440e9 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -75,16 +75,14 @@ pub fn memory_msg_handler( match msg_type { 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 = alloc(Layout::from_size_align_unchecked( page_count as usize * 4096, 8, )); + log::debug!("Allocating {} pages @ {:x}", page_count, ptr as u64); + vm.registers[1] = hbvm::value::Value(ptr as u64); log::debug!("Kernel ptr: {:x}", ptr as u64); }, diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index be58f081..e8d24601 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -25,6 +25,7 @@ create_window := fn(channel: int): ^render.Surface { if windowing_system_buffer == 0 { return 0 } else { + // ! bad able, stop using string messages :ragey: // msg := "\{01}\0" // msg_length := 2 @@ -33,7 +34,7 @@ create_window := fn(channel: int): ^render.Surface { x := 0 loop if x > 1000 break else x += 1 - ret := buffer.recv(windowing_system_buffer, mem_buf, 4096) + ret := buffer.recv([u8; 4096], windowing_system_buffer, mem_buf) if ret == 0 { log.info("No messages\0") } diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 038bbea7..7f577d37 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -30,9 +30,9 @@ clone_surface := fn(surface: ^Surface): Surface { return new } -free_surface := fn(surface: ^Surface): void { - // todo: depends on stn.memory.free - return +// ! is broken, check memory.free function +free_surface := fn(surface: Surface): void { + return @inline(memory.free, Color, surface.buf, @intcast(surface.width * surface.height), false) } framebuffer := @as(^Color, idk) @@ -53,6 +53,10 @@ clear := fn(surface: Surface, color: Color): void { } sync := fn(surface: Surface): void { + // vague safety + if surface.buf == framebuffer { + return + } return @inline(memory.copy, Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height)) } diff --git a/sysdata/libraries/stn/src/buffer.hb b/sysdata/libraries/stn/src/buffer.hb index 23d7e67e..23041dc1 100644 --- a/sysdata/libraries/stn/src/buffer.hb +++ b/sysdata/libraries/stn/src/buffer.hb @@ -1,11 +1,11 @@ string := @use("string.hb") -recv := fn(buffer_id: int, memory_map_location: ^u8, length: int): ^u8 { - return @eca(4, buffer_id, memory_map_location, length) +recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): ^Expr { + return @eca(4, buffer_id, memory_map_location, @sizeof(Expr)) } -write := fn(msg: ^u8, buffer_id: int, length: int): void { - return @eca(3, buffer_id, msg, length) +write := fn($Expr: type, msg: ^Expr, buffer_id: int): void { + return @eca(3, buffer_id, msg, @sizeof(Expr)) } BufferMsg := packed struct {operation: u8, msg: ^u8, msg_len: uint} diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index b51c5ad3..6022d5ae 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -1,65 +1,79 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF +MAX_FREE := 0xFF + +calc_pages := fn($Expr: type, num: int): int { + return 1 + @bitcast(@sizeof(Expr)) * num / PAGE_SIZE +} alloc := fn($Expr: type, num: int): ^Expr { - pages := 1 + @bitcast(@sizeof(Expr)) * num / PAGE_SIZE + pages := @inline(calc_pages, Expr, num) if pages <= MAX_ALLOC { - return @bitcast(@inline(request_page, pages)) + return @bitcast(request_page(@intcast(pages))) } - ptr := @inline(request_page, 0xFF) + ptr := request_page(0xFF) remaining := pages - MAX_ALLOC loop if remaining <= 0 break else { if remaining < MAX_ALLOC { - request_page(remaining) + request_page(@intcast(remaining)) } else { - request_page(MAX_ALLOC) + request_page(@intcast(MAX_ALLOC)) } remaining -= MAX_ALLOC } return @bitcast(ptr) } -free := fn($Expr: type, ptr: ^Expr, num: int): void { - // todo +// ! is broked, somebody fix please :( +free := fn($Expr: type, ptr: ^Expr, num: int, nullify: bool): void { + if nullify { + null := @as(u8, 0) + set(u8, &null, @bitcast(ptr), @bitcast(num) * @bitcast(PAGE_SIZE)) + } + pages := @inline(calc_pages, Expr, num) + if pages <= MAX_FREE { + return release_page(@bitcast(ptr), @intcast(pages)) + } + page_ptr := @as(^[u8; PAGE_SIZE], @bitcast(ptr)) + 1 + remaining := pages - MAX_FREE + loop if remaining <= 0 break else { + if remaining < MAX_FREE { + release_page(@bitcast(page_ptr), @intcast(remaining)) + } else { + release_page(@bitcast(page_ptr), @intcast(MAX_FREE)) + } + remaining -= MAX_FREE + page_ptr += 1 + } return } -request_page := fn(page_count: u8): ^u8 { - msg := "\{00}\{01}xxxxxxxx\0" - msg_page_count := msg + 1; - *msg_page_count = page_count - return @eca(3, 2, msg, 12) +RqPageMsg := packed struct {a: u8, count: u8} +request_page := fn(count: u8): ^u8 { + return @eca(3, 2, &RqPageMsg.(0, count), @sizeof(RqPageMsg)) } -release_page := fn(ptr: ^u8, page_count: u8): void { - msg := "\{01}\{00}xxxxxxxx\0" - - msg_page_count := msg + 1; - *msg_page_count = page_count - - msg_ptr := @as(^^u8, @bitcast(msg + 2)); - *msg_ptr = ptr - - return @eca(3, 2, msg, 12) +RlPageMsg := packed struct {a: u8, count: u8, ptr: ^u8} +release_page := fn(ptr: ^u8, count: u8): void { + return @eca(3, 2, &RlPageMsg.(1, count, ptr), @sizeof(RlPageMsg)) } OutbMsg := packed struct {a: u8, b: u8, addr: u16, value: u8} -InbMsg := packed struct {a: u8, b: u8, addr: u16} -OutlMsg := packed struct {a: u8, b: u8, addr: u16, value: u32} -InlMsg := packed struct {a: u8, b: u8, addr: u16} - outb := fn(addr: u16, value: u8): void { return @eca(3, 3, &OutbMsg.(1, 0, addr, value), @sizeof(OutbMsg)) } +InbMsg := packed struct {a: u8, b: u8, addr: u16} inb := fn(addr: u16): u8 { return @eca(3, 3, &InbMsg.(0, 0, addr), @sizeof(InbMsg)) } +OutlMsg := packed struct {a: u8, b: u8, addr: u16, value: u32} outl := fn(addr: u16, value: u32): void { return @eca(3, 3, &OutlMsg.(1, 2, addr, value), @sizeof(OutlMsg)) } +InlMsg := packed struct {a: u8, b: u8, addr: u16} inl := fn(addr: u16): u32 { return @eca(3, 3, &InlMsg.(0, 2, addr), @sizeof(InlMsg)) } diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 95e5cfba..171606b8 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -18,7 +18,6 @@ Window := struct { main := fn(): int { win_buff := buffer.create("XHorizon\0") - // BUG: Backbuffering is disabled screen := render.init(true) // Clear the screen to black. @@ -41,7 +40,7 @@ main := fn(): int { // TODO: Read the window buffer here { - ret := buffer.recv(win_buff, mem_buf, 4096) + ret := buffer.recv([u8; 4096], win_buff, mem_buf) if ret == 0 { log.info("No messages\0") } diff --git a/sysdata/programs/ps2_driver/src/main.hb b/sysdata/programs/ps2_driver/src/main.hb index 26be5025..bdd72f56 100644 --- a/sysdata/programs/ps2_driver/src/main.hb +++ b/sysdata/programs/ps2_driver/src/main.hb @@ -1,4 +1,4 @@ -.{memory, log, string, buffer} := @use("../../../libraries/stn/src/lib.hb") +.{memory, log, buffer} := @use("../../../libraries/stn/src/lib.hb") send_byte := fn(byte: u8): u8 { memory.outb(96, byte) @@ -15,7 +15,6 @@ main := fn(): int { if send_byte(244) == 250 { log.info("Enabled scanning\0") } - ptr := memory.request_page(1) prev_input := 250 loop { input := memory.inb(96) @@ -23,7 +22,7 @@ main := fn(): int { continue } prev_input = input - buffer.write(&input, buf, 1) + buffer.write(u8, &input, buf) } return 0 } \ No newline at end of file diff --git a/sysdata/programs/serial_driver/src/main.hb b/sysdata/programs/serial_driver/src/main.hb index 9270463f..0173b67f 100644 --- a/sysdata/programs/serial_driver/src/main.hb +++ b/sysdata/programs/serial_driver/src/main.hb @@ -25,7 +25,7 @@ main := fn(): int { mem := memory.request_page(1) loop { - ptr := @eca(4, a, mem, 0x1000) + ptr := @as(^u8, @eca(4, a, mem, 0x1000)) if ptr == 0 { serial_println("No message\0") } diff --git a/sysdata/programs/serial_driver_test/src/main.hb b/sysdata/programs/serial_driver_test/src/main.hb index d0998bcf..d3011c33 100644 --- a/sysdata/programs/serial_driver_test/src/main.hb +++ b/sysdata/programs/serial_driver_test/src/main.hb @@ -6,7 +6,7 @@ log_info := fn(): void { } else { msg := "XABC\0" msg_length := @inline(string.length, msg) - @eca(3, a, msg, msg_length) + @as(void, @eca(3, a, msg, msg_length)) } return diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index e5deef95..f5562824 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -25,15 +25,15 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.tests] # path = "boot:///tests.hbf" -# [boot.limine.ableos.modules.serial_driver] -# path = "boot:///serial_driver.hbf" - # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" [boot.limine.ableos.modules.render_example] path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.serial_driver] +# path = "boot:///serial_driver.hbf" + # [boot.limine.ableos.modules.serial_driver_test] # path = "boot:///serial_driver_test.hbf" @@ -57,6 +57,3 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" - -# [boot.limine.ableos.modules.tetris] -# path = "boot:///tetris.hbf" From fac573837fdc5751bf044aeab9e55bc9103e0906 Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 16 Oct 2024 07:34:19 -0500 Subject: [PATCH 22/66] Work --- sysdata/programs/horizon/src/main.hb | 5 +++-- .../programs/render_example/src/examples/tactical_screen.hb | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 171606b8..7722ef4e 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -50,7 +50,7 @@ main := fn(): int { vel_inner.x = -vel_inner.x color = random.range(render.Color, render.black, render.white) } - if pos_inner.y == 0 | pos_inner.y == window.height - side { + if pos_inner.y == 20 | pos_inner.y == window.height - side { vel_inner.y = -vel_inner.y color = random.range(render.Color, render.black, render.white) } @@ -59,11 +59,12 @@ main := fn(): int { window_count := 0 loop { render.clear(window, render.black) + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) // Draw the decorators { render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) - // render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) + render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) } render.put_filled_rect(window, pos_inner, .(side, side), color) diff --git a/sysdata/programs/render_example/src/examples/tactical_screen.hb b/sysdata/programs/render_example/src/examples/tactical_screen.hb index bbbaed2b..3fc1f164 100644 --- a/sysdata/programs/render_example/src/examples/tactical_screen.hb +++ b/sysdata/programs/render_example/src/examples/tactical_screen.hb @@ -55,8 +55,8 @@ example := fn(): void { y += cell_size } - render.put_hline(screen, seeker.y * cell_size + halfcell + scroll, 0, width, render.light_green) - render.put_vline(screen, seeker.x * cell_size + halfcell + scroll, 0, height, render.light_green) + render.put_hline(screen, seeker.y * cell_size + halfcell + scroll, 0, width, render.blue) + render.put_vline(screen, seeker.x * cell_size + halfcell + scroll, 0, height, render.blue) render.sync(screen) From 5af563175577e18e264724ec0922a4b1b5c649b8 Mon Sep 17 00:00:00 2001 From: koniifer Date: Fri, 25 Oct 2024 16:37:38 +0100 Subject: [PATCH 23/66] minor changes --- Cargo.lock | 685 ++---------------- dev/src/main.rs | 5 +- kernel/src/holeybytes/ecah.rs | 10 +- .../holeybytes/kernel_services/mem_serve.rs | 19 +- kernel/src/lib.rs | 4 +- repbuild/Cargo.toml | 10 +- repbuild/src/dev.rs | 23 +- repbuild/src/main.rs | 45 +- sysdata/libraries/horizon_api/src/lib.hb | 4 +- sysdata/libraries/render/src/image.hb | 11 +- sysdata/libraries/render/src/software.hb | 58 +- sysdata/libraries/stn/src/buffer.hb | 8 + sysdata/libraries/stn/src/log.hb | 4 +- sysdata/libraries/stn/src/memory.hb | 46 +- sysdata/libraries/stn/src/random.hb | 5 +- sysdata/libraries/stn/src/string.hb | 19 +- sysdata/programs/dt_buffer_test/src/main.hb | 10 +- .../filesystem_fat32/src/attributes.hb | 12 +- .../src/bios_parameter_block.hb | 17 +- sysdata/programs/filesystem_fat32/src/file.hb | 2 +- sysdata/programs/filesystem_fat32/src/main.hb | 2 +- sysdata/programs/horizon/src/main.hb | 18 +- sysdata/programs/ps2_driver/src/main.hb | 2 +- .../render_example/src/examples/image.hb | 6 +- .../render_example/src/examples/lines.hb | 4 +- .../render_example/src/examples/random.hb | 10 +- .../render_example/src/examples/square.hb | 10 +- .../render_example/src/examples/surface.hb | 24 +- .../src/examples/tactical_screen.hb | 12 +- sysdata/programs/serial_driver/src/main.hb | 2 +- sysdata/programs/tests/src/main.hb | 6 +- sysdata/system_config.toml | 12 +- 32 files changed, 264 insertions(+), 841 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 536d9609..5b028ce4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,21 +11,6 @@ dependencies = [ "tock-registers", ] -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "allocator-api2" version = "0.2.18" @@ -49,9 +34,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "autocfg" @@ -59,21 +44,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", -] - [[package]] name = "base64" version = "0.22.1" @@ -115,6 +85,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "byteorder" @@ -122,12 +95,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" - [[package]] name = "cc" version = "1.1.31" @@ -255,56 +222,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -317,10 +234,10 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.31.1" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" @@ -336,129 +253,28 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#15e4762d4ac8993d12fe2dd54e2b2d842c8a034b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#faa8dd2e6fabe2e0e4a375e677171856da491c61" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#15e4762d4ac8993d12fe2dd54e2b2d842c8a034b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#faa8dd2e6fabe2e0e4a375e677171856da491c61" dependencies = [ - "hashbrown", + "hashbrown 0.15.0", "hbbytecode", "hbvm", "log", + "regalloc2", ] [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#15e4762d4ac8993d12fe2dd54e2b2d842c8a034b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#faa8dd2e6fabe2e0e4a375e677171856da491c61" dependencies = [ "hbbytecode", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "hyper" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - [[package]] name = "iana-time-zone" version = "0.1.61" @@ -499,21 +315,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - [[package]] name = "js-sys" version = "0.3.72" @@ -530,7 +334,7 @@ dependencies = [ "aarch64-cpu", "crossbeam-queue", "derive_more", - "hashbrown", + "hashbrown 0.15.0", "hbvm", "limine", "log", @@ -617,33 +421,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys 0.52.0", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -653,15 +430,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.20.2" @@ -680,84 +448,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] -[[package]] -name = "quinn" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" -dependencies = [ - "bytes", - "rand", - "ring", - "rustc-hash", - "rustls", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" -dependencies = [ - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - [[package]] name = "quote" version = "1.0.37" @@ -767,36 +466,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "raw-cpuid" version = "10.7.0" @@ -815,6 +484,19 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "regalloc2" +version = "0.10.2" +source = "git+https://github.com/jakubDoka/regalloc2?branch=reuse-allocations#21c43e3ee182824e92e2b25f1d3c03ed47f9c02b" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.14.5", + "log", + "rustc-hash", + "smallvec", +] + [[package]] name = "regex-syntax" version = "0.8.5" @@ -831,52 +513,9 @@ dependencies = [ "hblang", "log", "raw-cpuid 11.2.0", - "reqwest", "str-reader", "toml", -] - -[[package]] -name = "reqwest" -version = "0.12.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "windows-registry", + "ureq", ] [[package]] @@ -891,15 +530,9 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys 0.52.0", + "windows-sys", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - [[package]] name = "rustc-hash" version = "2.0.0" @@ -921,6 +554,7 @@ version = "0.23.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -929,15 +563,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.10.0" @@ -961,12 +586,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - [[package]] name = "sbi" version = "0.2.0" @@ -987,36 +606,24 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "serde_json" -version = "1.0.132" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - [[package]] name = "serde_spanned" version = "0.6.8" @@ -1026,18 +633,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "shlex" version = "1.3.0" @@ -1059,16 +654,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "spin" version = "0.9.8" @@ -1092,44 +677,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -dependencies = [ - "futures-core", -] - -[[package]] -name = "thiserror" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tinyvec" version = "1.8.0" @@ -1151,32 +707,6 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - [[package]] name = "toml" version = "0.8.19" @@ -1211,37 +741,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "uart_16550" version = "0.3.1" @@ -1286,6 +785,21 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +dependencies = [ + "base64", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots", +] + [[package]] name = "url" version = "2.5.2" @@ -1311,15 +825,6 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1352,18 +857,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.95" @@ -1393,16 +886,6 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" -[[package]] -name = "web-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webpki-roots" version = "0.26.6" @@ -1421,36 +904,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -1460,15 +913,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -1598,27 +1042,6 @@ dependencies = [ "serde", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zeroize" version = "1.8.1" diff --git a/dev/src/main.rs b/dev/src/main.rs index aaa017b1..488c63bd 100644 --- a/dev/src/main.rs +++ b/dev/src/main.rs @@ -1,4 +1,3 @@ -#![allow(unused)] use std::io::Write; use idl::build_idl; @@ -139,8 +138,8 @@ fn build(name: String) { } } -pub fn build_program(name: String) {} -pub fn build_library(name: String) {} +pub fn build_program(_name: String) {} +pub fn build_library(_name: String) {} fn help() { println!( diff --git a/kernel/src/holeybytes/ecah.rs b/kernel/src/holeybytes/ecah.rs index 5535113b..ab45e643 100644 --- a/kernel/src/holeybytes/ecah.rs +++ b/kernel/src/holeybytes/ecah.rs @@ -131,9 +131,13 @@ pub fn handler(vm: &mut Vm) { 3 => unimplemented!("TODO: implement whatever buffer 3 does for no x86_64"), // source of rng 4 => { - // limit to last 32 bits - vm.registers[1] = - hbvm::value::Value(crate::arch::hardware_random_u64() & 0xFFFFFFFF); + let block = block_read(mem_addr, length); + block.chunks_mut(8.min(length)).for_each(|chunk| { + chunk.clone_from_slice( + &crate::arch::hardware_random_u64().to_le_bytes()[..chunk.len()], + ); + }); + vm.registers[1] = hbvm::value::Value(mem_addr); } 5 => match dt_msg_handler(vm, mem_addr, length) { Ok(()) => {} diff --git a/kernel/src/holeybytes/kernel_services/mem_serve.rs b/kernel/src/holeybytes/kernel_services/mem_serve.rs index 743440e9..b7693907 100644 --- a/kernel/src/holeybytes/kernel_services/mem_serve.rs +++ b/kernel/src/holeybytes/kernel_services/mem_serve.rs @@ -38,7 +38,7 @@ unsafe fn memset(mut dest: *mut u8, src: *const u8, count: usize, size: usize) { let mut buffer_size = size; src.copy_to_nonoverlapping(buffer.as_mut_ptr(), size); - while buffer_size * 2 <= BLOCK_SIZE { + while core::intrinsics::likely(buffer_size * 2 <= BLOCK_SIZE) { buffer .as_mut_ptr() .copy_to_nonoverlapping(buffer.as_mut_ptr().add(buffer_size), buffer_size); @@ -53,7 +53,7 @@ unsafe fn memset(mut dest: *mut u8, src: *const u8, count: usize, size: usize) { remaining -= 1; } - while remaining >= 8 { + while core::intrinsics::likely(remaining >= 8) { *(dest as *mut u64) = *buffer_ptr; dest = dest.add(8); remaining -= 8; @@ -122,18 +122,17 @@ pub fn memory_msg_handler( log::debug!(" {} pages", page_count); } 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 count = u32::from_le_bytes(msg_vec[1..5].try_into().unwrap_unchecked()) as usize; + let src = u64::from_le_bytes(msg_vec[5..13].try_into().unwrap_unchecked()) as *const u8; + let dest = u64::from_le_bytes(msg_vec[13..21].try_into().unwrap_unchecked()) as *mut u8; src.copy_to_nonoverlapping(dest, count); }, 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 src = - 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 count = u32::from_le_bytes(msg_vec[1..5].try_into().unwrap_unchecked()) as usize; + let size = u32::from_le_bytes(msg_vec[5..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; memset(dest, src, count, size); }, diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 9a8060bc..a94f94b1 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -6,6 +6,7 @@ slice_split_once, exclusive_wrapper, new_uninit, + core_intrinsics, abi_x86_interrupt, alloc_error_handler, ptr_sub_ptr, @@ -14,8 +15,7 @@ pointer_is_aligned_to )] #![test_runner(crate::test_runner)] -#![cfg_attr(not(debug_assertions), allow(unused, deprecated))] -#![allow(dead_code)] +#![allow(dead_code, internal_features)] extern crate alloc; mod allocator; diff --git a/repbuild/Cargo.toml b/repbuild/Cargo.toml index 01e77555..ea418579 100644 --- a/repbuild/Cargo.toml +++ b/repbuild/Cargo.toml @@ -22,11 +22,13 @@ fatfs = "0.3" toml = "0.8" hblang = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ "std", + "opts", ], default-features = false } log = "0.4" raw-cpuid = "11" +ureq = { version = "2", default-features = false, features = ["tls"] } -[dependencies.reqwest] -version = "0.12" -default-features = false -features = ["rustls-tls", "blocking"] +# [dependencies.reqwest] +# version = "0.12" +# default-features = false +# features = ["rustls-tls", "blocking"] diff --git a/repbuild/src/dev.rs b/repbuild/src/dev.rs index 2e812288..7186d39c 100644 --- a/repbuild/src/dev.rs +++ b/repbuild/src/dev.rs @@ -63,7 +63,7 @@ impl Package { build_cmd, } } - pub fn build(&self) { + pub fn build(&self) -> std::io::Result<()> { if self.binaries.contains(&"hblang".to_string()) { let file = self.build_cmd.split_ascii_whitespace().last().unwrap(); @@ -71,15 +71,24 @@ impl Package { let mut bytes = Vec::new(); // compile here - let _ = hblang::run_compiler( + hblang::run_compiler( &path, Options { fmt: true, + optimize: true, ..Default::default() }, &mut bytes, - ); - let _ = hblang::run_compiler(&path, Default::default(), &mut bytes); + )?; + + hblang::run_compiler( + &path, + Options { + optimize: true, + ..Default::default() + }, + &mut bytes, + )?; match std::fs::create_dir("target/programs") { Ok(_) => (), @@ -88,15 +97,17 @@ impl Package { } std::fs::write(format!("target/programs/{}.hbf", self.name), &bytes).unwrap(); bytes.clear(); - let _ = hblang::run_compiler( + hblang::run_compiler( &path, Options { dump_asm: true, + optimize: true, ..Default::default() }, &mut bytes, - ); + )?; std::fs::write(format!("target/programs/{}.hba", self.name), &bytes).unwrap(); } + Ok(()) } } diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index f32f9935..19f2d381 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -1,5 +1,3 @@ -// #![allow(unused)] - mod dev; use { @@ -8,7 +6,6 @@ use { error_stack::{bail, report, Context, Report, Result, ResultExt}, fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek}, std::{ - // fmt::Display, fs::{self, File}, io::{self, Write}, path::Path, @@ -84,7 +81,7 @@ fn main() -> Result<(), Error> { " -r / --release: build in release mode\n", " -d / --debuginfo: build with debug info\n", " --noaccel: run without acceleration (e.g, no kvm)\n", - "[ rv64 / riscv64 / riscv64-virt / aarch64 / arm64 / aarch64-virt ]: sets target" + "[ rv64 / riscv64 / riscv64-virt / aarch64 / arm64 / aarch64-virt / avx2 ]: sets target" ),); Ok(()) } @@ -207,21 +204,25 @@ TERM_BACKDROP={} let modules = value.get_mut("modules").unwrap().as_table_mut().unwrap(); // let mut real_modules = modules.clone(); - modules.into_iter().for_each(|(_, value)| { - if value.is_table() { - let path = get_path_without_boot_prefix( - value.get("path").expect("You must have `path` as a value"), - ) - .unwrap() - .split(".") - .next() - .unwrap(); - let p = Package::load_from_file( - format!("sysdata/programs/{}/meta.toml", path).to_owned(), - ); - p.build(); - } - }); + modules + .into_iter() + .map(|(_, value)| -> Result<(), io::Error> { + if value.is_table() { + let path = get_path_without_boot_prefix( + value.get("path").expect("You must have `path` as a value"), + ) + .unwrap() + .split(".") + .next() + .unwrap(); + let p = Package::load_from_file( + format!("sysdata/programs/{}/meta.toml", path).to_owned(), + ); + p.build()?; + } + Ok(()) + }) + .for_each(drop); modules.into_iter().for_each(|(_key, value)| { if value.is_table() { let path = value.get("path").expect("You must have `path` as a value"); @@ -473,12 +474,12 @@ fn fetch_ovmf(target: Target) -> Result { Ok(_) => return Ok(ovmf_path.to_owned()), Err(e) => return Err(report!(e).change_context(OvmfFetchError::Io)), }; - let mut bytes = reqwest::blocking::get(ovmf_url) + let req = ureq::get(ovmf_url) + .call() .map_err(Report::from) .change_context(OvmfFetchError::Fetch)?; - bytes - .copy_to(&mut file) + std::io::copy(&mut req.into_reader(), &mut file) .map_err(Report::from) .change_context(OvmfFetchError::Io)?; diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index e8d24601..c2e2f3d4 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -23,7 +23,7 @@ create_window := fn(channel: int): ^render.Surface { mem_buf := memory.request_page(1) if windowing_system_buffer == 0 { - return 0 + return @as(^render.Surface, idk) } else { // ! bad able, stop using string messages :ragey: // msg := "\{01}\0" @@ -43,6 +43,6 @@ create_window := fn(channel: int): ^render.Surface { log.info("No messages\0") } - return 0 + return @as(^render.Surface, idk) } } \ No newline at end of file diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb index fd683f51..46f5d7bd 100644 --- a/sysdata/libraries/render/src/image.hb +++ b/sysdata/libraries/render/src/image.hb @@ -41,14 +41,13 @@ surface_from_bmp := fn(bmp: ^u8): Surface { info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) bmp += file_header.offset - a := 0 px := info_header.width * info_header.height ptr := @as(^Color, @bitcast(bmp)) tmp := @as(Color, idk) - row := 0 + row := @as(i32, 0) loop if row == info_header.height / 2 break else { - col := 0 + col := @as(i32, 0) loop if col == info_header.width break else { top_index := row * info_header.width + col bottom_index := (info_header.height - 1 - row) * info_header.width + col @@ -62,7 +61,7 @@ surface_from_bmp := fn(bmp: ^u8): Surface { row += 1 } - return .(@bitcast(bmp), @intcast(info_header.width), @intcast(info_header.height)) + return .(@bitcast(bmp), info_header.width, info_header.height) } new_surface_from_bmp := fn(bmp: ^u8): Surface { @@ -74,8 +73,8 @@ new_surface_from_bmp := fn(bmp: ^u8): Surface { info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) bmp += file_header.offset - width := @as(int, @intcast(info_header.width)) - height := @as(int, @intcast(info_header.height)) + width := @as(uint, @intcast(info_header.width)) + height := @as(uint, @intcast(info_header.height)) surface := new_surface(width, height) top_start_idx := surface.buf diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 7f577d37..ddf939ff 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -4,11 +4,11 @@ Surface := struct { buf: ^Color, - width: int, - height: int, + width: uint, + height: uint, } -new_surface := fn(width: int, height: int): Surface { +new_surface := fn(width: uint, height: uint): Surface { return .( @inline(memory.alloc, Color, width * height), width, @@ -16,7 +16,7 @@ new_surface := fn(width: int, height: int): Surface { ) } -surface_from_ptr := fn(ptr: ^Color, width: int, height: int): Surface { +surface_from_ptr := fn(ptr: ^Color, width: uint, height: uint): Surface { return .( ptr, width, @@ -39,8 +39,8 @@ framebuffer := @as(^Color, idk) init := fn(doublebuffer: bool): Surface { framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0") - width := dt.get(int, "framebuffer/fb0/width\0") - height := dt.get(int, "framebuffer/fb0/height\0") + width := dt.get(uint, "framebuffer/fb0/width\0") + height := dt.get(uint, "framebuffer/fb0/height\0") if doublebuffer { return new_surface(width, height) } else { @@ -49,7 +49,7 @@ init := fn(doublebuffer: bool): Surface { } clear := fn(surface: Surface, color: Color): void { - return @inline(memory.set, Color, &color, surface.buf, @bitcast(surface.width * surface.height)) + return @inline(memory.set, Color, &color, surface.buf, surface.width * surface.height) } sync := fn(surface: Surface): void { @@ -60,20 +60,20 @@ sync := fn(surface: Surface): void { return @inline(memory.copy, Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height)) } -index := fn(surface: Surface, x: int, y: int): int { +index := fn(surface: Surface, x: uint, y: uint): uint { return x + surface.width * y } -indexptr := fn(surface: Surface, x: int, y: int): ^Color { +indexptr := fn(surface: Surface, x: uint, y: uint): ^Color { return surface.buf + @inline(index, surface, x, y) } -put_pixel := fn(surface: Surface, pos: Vec2(int), color: Color): void { +put_pixel := fn(surface: Surface, pos: Vec2(uint), color: Color): void { *@inline(indexptr, surface, pos.x, pos.y) = color return } -put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { +put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void { top_start_idx := @inline(indexptr, surface, pos.x, pos.y) bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1) rows_to_fill := tr.y @@ -94,7 +94,7 @@ put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Co return } -put_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { +put_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void { start_idx := @inline(indexptr, surface, pos.x, pos.y) end_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y) right_start_idx := @inline(indexptr, surface, pos.x + tr.x, pos.y) @@ -112,15 +112,15 @@ put_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): v return } -put_line_low := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { - dx := p1.x - p0.x - dy := p1.y - p0.y +put_line_low := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void { + dx := @as(int, @bitcast(p1.x - p0.x)) + dy := @as(int, @bitcast(p1.y - p0.y)) yi := 1 if dy < 0 { yi = -1 dy = -dy } - D := 2 * dy - dx + D := @as(int, 2) * dy - dx y := p0.y x := p0.x loop if x == p1.x break else { @@ -136,15 +136,15 @@ put_line_low := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color) return } -put_line_high := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { - dx := p1.x - p0.x - dy := p1.y - p0.y +put_line_high := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void { + dx := @as(int, @bitcast(p1.x - p0.x)) + dy := @as(int, @bitcast(p1.y - p0.y)) xi := 1 if dy < 0 { xi = -1 dx = -dx } - D := 2 * dx - dy + D := @as(int, 2) * dx - dy x := p0.x y := p0.y loop if y == p1.y break else { @@ -160,8 +160,8 @@ put_line_high := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color return } -put_line := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { - if math.abs(int, p1.y - p0.y) < math.abs(int, p1.x - p0.x) { +put_line := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void { + if math.abs(uint, p1.y - p0.y) < math.abs(uint, p1.x - p0.x) { if p0.x > p1.x { @inline(put_line_low, surface, p1, p0, color) } else { @@ -177,7 +177,7 @@ put_line := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): vo return } -put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int), flip_v: bool): void { +put_surface := fn(surface: Surface, top: Surface, pos: Vec2(uint), flip_v: bool): void { top_start_idx := @inline(indexptr, surface, pos.x, pos.y) bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + top.height - 1) rows_to_copy := top.height @@ -208,29 +208,29 @@ put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int), flip_v: bool): } // peony-made -put_trirect := fn(surface: Surface, pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void { +put_trirect := fn(surface: Surface, pos: Vec2(uint), 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 + step.y /= @bitcast(size.x) } start_y := pos.y - target := pos + size + target := pos + @bitcast(size) loop if pos.x == target.x break else { @inline(put_vline, surface, pos.x, pos.y, target.y, color0) @inline(put_vline, surface, pos.x, pos.y, start_y, color1) - pos += step + pos += @bitcast(step) } return } // peony-made -put_vline := fn(surface: Surface, x: int, y0: int, y1: int, color: Color): void { +put_vline := fn(surface: Surface, x: uint, y0: uint, y1: uint, color: Color): void { if y1 < y0 { tmp := y0 y0 = y1 @@ -247,7 +247,7 @@ put_vline := fn(surface: Surface, x: int, y0: int, y1: int, color: Color): void } // peony-made -put_hline := fn(surface: Surface, y: int, x0: int, x1: int, color: Color): void { +put_hline := fn(surface: Surface, y: uint, x0: uint, x1: uint, color: Color): void { if x1 < x0 { tmp := x0 x0 = x1 diff --git a/sysdata/libraries/stn/src/buffer.hb b/sysdata/libraries/stn/src/buffer.hb index 23041dc1..d4aa4e7a 100644 --- a/sysdata/libraries/stn/src/buffer.hb +++ b/sysdata/libraries/stn/src/buffer.hb @@ -8,6 +8,14 @@ write := fn($Expr: type, msg: ^Expr, buffer_id: int): void { return @eca(3, buffer_id, msg, @sizeof(Expr)) } +recv_length := fn(buffer_id: int, memory_map_location: ^u8, length: int): void { + return @eca(4, buffer_id, memory_map_location, length) +} + +write_length := fn(msg: ^u8, buffer_id: int, length: int): void { + return @eca(3, buffer_id, msg, length) +} + BufferMsg := packed struct {operation: u8, msg: ^u8, msg_len: uint} create := fn(msg: ^u8): int { diff --git a/sysdata/libraries/stn/src/log.hb b/sysdata/libraries/stn/src/log.hb index 95d91c1e..bd7d6256 100644 --- a/sysdata/libraries/stn/src/log.hb +++ b/sysdata/libraries/stn/src/log.hb @@ -2,8 +2,8 @@ string := @use("string.hb") LogMsg := packed struct {level: u8, string: ^u8, strlen: uint} -log := fn($Level: u8, message: ^u8): void { - return @eca(3, 1, LogMsg.(Level, message, @inline(string.length, message)), @sizeof(LogMsg)) +log := fn(level: u8, message: ^u8): void { + return @eca(3, 1, LogMsg.(level, message, @inline(string.length, message)), @sizeof(LogMsg)) } error := fn(message: ^u8): void return @inline(log, 0, message) diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index 6022d5ae..540d3d78 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -2,49 +2,27 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF MAX_FREE := 0xFF -calc_pages := fn($Expr: type, num: int): int { - return 1 + @bitcast(@sizeof(Expr)) * num / PAGE_SIZE +calc_pages := fn($Expr: type, num: uint): uint { + return 1 + @sizeof(Expr) * num / PAGE_SIZE } -alloc := fn($Expr: type, num: int): ^Expr { +alloc := fn($Expr: type, num: uint): ^Expr { pages := @inline(calc_pages, Expr, num) if pages <= MAX_ALLOC { return @bitcast(request_page(@intcast(pages))) } ptr := request_page(0xFF) remaining := pages - MAX_ALLOC - loop if remaining <= 0 break else { - if remaining < MAX_ALLOC { - request_page(@intcast(remaining)) - } else { - request_page(@intcast(MAX_ALLOC)) - } + loop if remaining < MAX_ALLOC break else { + _ := request_page(@intcast(MAX_ALLOC)) remaining -= MAX_ALLOC } + _ := request_page(@intcast(remaining)) return @bitcast(ptr) } -// ! is broked, somebody fix please :( -free := fn($Expr: type, ptr: ^Expr, num: int, nullify: bool): void { - if nullify { - null := @as(u8, 0) - set(u8, &null, @bitcast(ptr), @bitcast(num) * @bitcast(PAGE_SIZE)) - } - pages := @inline(calc_pages, Expr, num) - if pages <= MAX_FREE { - return release_page(@bitcast(ptr), @intcast(pages)) - } - page_ptr := @as(^[u8; PAGE_SIZE], @bitcast(ptr)) + 1 - remaining := pages - MAX_FREE - loop if remaining <= 0 break else { - if remaining < MAX_FREE { - release_page(@bitcast(page_ptr), @intcast(remaining)) - } else { - release_page(@bitcast(page_ptr), @intcast(MAX_FREE)) - } - remaining -= MAX_FREE - page_ptr += 1 - } +// ! stub +free := fn($Expr: type, ptr: ^Expr, num: uint, nullify: bool): void { return } @@ -78,12 +56,12 @@ 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} +CopyMsg := packed struct {a: u8, count: u32, src: ^u8, 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)) + return @eca(3, 2, &CopyMsg.(4, @intcast(count * @sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(CopyMsg)) } -SetMsg := packed struct {a: u8, count: uint, size: uint, src: ^u8, dest: ^u8} +SetMsg := packed struct {a: u8, count: u32, size: u32, 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(src), @bitcast(dest)), @sizeof(SetMsg)) + return @eca(3, 2, &SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(SetMsg)) } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/random.hb b/sysdata/libraries/stn/src/random.hb index 9b4cdfc6..4fb7b3ff 100644 --- a/sysdata/libraries/stn/src/random.hb +++ b/sysdata/libraries/stn/src/random.hb @@ -1,8 +1,7 @@ any := fn($Expr: type): Expr { - return @intcast(@eca(3, 4)) + return *@eca(3, 4, &@as(Expr, idk), @sizeof(Expr)) } range := fn($Expr: type, min: Expr, max: Expr): Expr { - // wtf is this - return @intcast(@as(int, @eca(3, 4)) % @as(int, @intcast(max) - @intcast(min)) + 1 + @intcast(min)) + return @inline(any, Expr) % (max - min) + @intcast(1) + min } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index c84ac557..3307199b 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -1,10 +1,11 @@ length := fn(ptr: ^u8): uint { len := @as(uint, 0) + // loop if *(ptr + len) == 0 return len else len += 1 loop if *(ptr + len) == 0 break else len += 1 return len } -display_int := fn(num: int, p: ^u8, radix: int): ^u8 { +display_int := fn(num: int, p: ^u8, radix: uint): ^u8 { ptr := p negative := num < 0 if negative { @@ -33,14 +34,14 @@ display_int := fn(num: int, p: ^u8, radix: int): ^u8 { ptr += 1 } else { loop if num == 0 break else { - digit := num % radix + digit := num % @bitcast(radix) if digit < 10 { - *ptr = digit + 48 + *ptr = @intcast(digit) + 48 } else { - *ptr = digit + 55 + *ptr = @intcast(digit) + 55 } ptr += 1 - num /= radix + num /= @bitcast(radix) } } @@ -55,11 +56,11 @@ display_int := fn(num: int, p: ^u8, radix: int): ^u8 { return p } + reverse := fn(s: ^u8): void { - len := @inline(length, s) - i := 0 - j := len - 1 - temp := 0 + i := @as(uint, 0) + j := @inline(length, s) - 1 + temp := @as(u8, 0) loop if i >= j break else { temp = *(s + i); *(s + i) = *(s + j); diff --git a/sysdata/programs/dt_buffer_test/src/main.hb b/sysdata/programs/dt_buffer_test/src/main.hb index ce42ef02..7d37f6ad 100644 --- a/sysdata/programs/dt_buffer_test/src/main.hb +++ b/sysdata/programs/dt_buffer_test/src/main.hb @@ -1,13 +1,13 @@ .{dt} := @use("../../../libraries/stn/src/lib.hb") -main := fn(): int { - dt.get(int, "framebuffer/fb0/width\0") - dt.get(int, "cpu/cpu0/architecture\0") +main := fn(): void { + dt.get(void, "framebuffer/fb0/width\0") + dt.get(void, "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(int, "serial_ports/sp0/mapping\0") + dt.get(void, "serial_ports/sp0/mapping\0") - return 0 + return } \ No newline at end of file diff --git a/sysdata/programs/filesystem_fat32/src/attributes.hb b/sysdata/programs/filesystem_fat32/src/attributes.hb index 2c5c3ca0..cd9dac66 100644 --- a/sysdata/programs/filesystem_fat32/src/attributes.hb +++ b/sysdata/programs/filesystem_fat32/src/attributes.hb @@ -1,7 +1,7 @@ -READ_ONLY := 0x1 -HIDDEN := 0x2 -SYSTEM := 0x4 -VOLUME_ID := 0x8 -DIRECTORY := 0x10 -ARCHIVE := 0x20 +READ_ONLY := @as(u32, 0x1) +HIDDEN := @as(u32, 0x2) +SYSTEM := @as(u32, 0x4) +VOLUME_ID := @as(u32, 0x8) +DIRECTORY := @as(u32, 0x10) +ARCHIVE := @as(u32, 0x20) LFN := READ_ONLY | HIDDEN | SYSTEM | VOLUME_ID \ No newline at end of file diff --git a/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb b/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb index fa9d8ca1..8517c5bd 100644 --- a/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb +++ b/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb @@ -63,7 +63,7 @@ VolumeName := [u8; 11] SystemIdentifierString := [u8; 8] VALID_SYSTEM_IDENTIFIER_STRING := [u8].(46, 41, 54, 33, 32, 20, 20, 20) -BOOTABLE_PARTITION_SIGNATURE := 0xAA55 +BOOTABLE_PARTITION_SIGNATURE := @as(u32, 0xAA55) BootCode := [u8; 420] @@ -100,9 +100,10 @@ ebr_sanity_check := fn(ebr: ExtendedBootRecord): int { log.warn("EBR-Signature sanity check failed\0") } - if ebr.system_identifier_string != VALID_SYSTEM_IDENTIFIER_STRING { - log.warn("EBR-Signature-Identifier-String sanity check failed\0") - } + // ! comparison between [u8] is not supported in hblang + // if ebr.system_identifier_string != VALID_SYSTEM_IDENTIFIER_STRING { + // log.warn("EBR-Signature-Identifier-String sanity check failed\0") + // } return 0 } @@ -131,21 +132,21 @@ new_ebr := fn(): ExtendedBootRecord { ) } -VALID_LEAD_FS_INFO := 0x41615252 -VALID_TRAIL_FS_INFO := 0xAA550000 +VALID_LEAD_FS_INFO := @as(u32, 0x41615252) +VALID_TRAIL_FS_INFO := @as(u32, 0xAA550000) FSInfo := struct { // Must be 0x41615252 to indicate a valid FSInfo structure lead_signature: u32, lead_reserved: [u8; 480], - // If the value is 0xFFFFFFFF, then the free count is unknown and must be computed. However, this value might be incorrect and should at least be range checked (<= volume cluster count) + // If the value is 0xFFFFFFFF, then the free count is unknown and must be computed. However, this value might be incorrect and should at least be range checked (<= volume cluster count) last_known_free_cluster_count: u32, last_known_avalible_cluster: u32, trail_reserved: [u8; 12], trail_signature: u32, } -fs_info_sanity_check := fn(fs_info: FSInfo): int { +fs_info_sanity_check := fn(fs_info: FSInfo): uint { ret := 0 if fs_info.lead_signature != VALID_LEAD_FS_INFO { ret &= 1 diff --git a/sysdata/programs/filesystem_fat32/src/file.hb b/sysdata/programs/filesystem_fat32/src/file.hb index c7548455..175bff09 100644 --- a/sysdata/programs/filesystem_fat32/src/file.hb +++ b/sysdata/programs/filesystem_fat32/src/file.hb @@ -7,7 +7,7 @@ FileName := [u8; 11] FileEntry := struct { file_name: FileName, attributes: u8, - // We could use this byte for something but we likely will not + // We could use this byte for something but we likely will not nt_reserved: u8, hundredths_seconds_creation: u8, creation_time: datetime.time, diff --git a/sysdata/programs/filesystem_fat32/src/main.hb b/sysdata/programs/filesystem_fat32/src/main.hb index 6e88cbbc..e15a16cf 100644 --- a/sysdata/programs/filesystem_fat32/src/main.hb +++ b/sysdata/programs/filesystem_fat32/src/main.hb @@ -16,7 +16,7 @@ FAT12 := 1 FAT16 := 2 FAT32 := 3 -calculate_fat_type := fn(sector_size: int, total_clusters: int): int { +calculate_fat_type := fn(sector_size: uint, total_clusters: uint): uint { if sector_size == 0 { return ExFAT } else if total_clusters < 4085 { diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 7722ef4e..ed2c23fe 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -25,14 +25,14 @@ main := fn(): int { window := render.new_surface(screen.width / 3, screen.height / 3) - x := 10 + x := 0 mem_buf := memory.request_page(1) - color := random.range(render.Color, render.black, render.white) + color := random.any(render.Color) side := window.width / 8 vel_inner := Vec2(int).(1, 1) - pos_inner := Vec2(int).((window.width - side) / 2, (window.height - side) / 2) + pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) loop { // Clear the screen @@ -48,11 +48,11 @@ main := fn(): int { if pos_inner.x == 0 | pos_inner.x == window.width - side { vel_inner.x = -vel_inner.x - color = random.range(render.Color, render.black, render.white) + color = random.any(render.Color) } if pos_inner.y == 20 | pos_inner.y == window.height - side { vel_inner.y = -vel_inner.y - color = random.range(render.Color, render.black, render.white) + color = random.any(render.Color) } // TODO: Get windows out of a collection and iter through @@ -69,17 +69,17 @@ main := fn(): int { render.put_filled_rect(window, pos_inner, .(side, side), color) // Apply the image to the screen - pos := Vec2(int).(x, 100) + pos := Vec2(uint).(x, 100) render.put_surface(screen, window, pos, false) if window_count >= 1 { - x = 10 + x = 0 break } window_count += 1 - x += 400 + x += screen.width / 2 } - pos_inner += vel_inner + pos_inner += @bitcast(vel_inner) // Sync the screen render.sync(screen) diff --git a/sysdata/programs/ps2_driver/src/main.hb b/sysdata/programs/ps2_driver/src/main.hb index bdd72f56..7256181b 100644 --- a/sysdata/programs/ps2_driver/src/main.hb +++ b/sysdata/programs/ps2_driver/src/main.hb @@ -7,7 +7,7 @@ send_byte := fn(byte: u8): u8 { main := fn(): int { buf := buffer.create("XKeyboard\0") - send_byte(238) + _ := send_byte(238) log.info("PS/2 Driver Loaded\0") if send_byte(238) == 238 { log.info("PS/2 Keyboard Echoed\0") diff --git a/sysdata/programs/render_example/src/examples/image.hb b/sysdata/programs/render_example/src/examples/image.hb index b7e1ba0a..25d68404 100644 --- a/sysdata/programs/render_example/src/examples/image.hb +++ b/sysdata/programs/render_example/src/examples/image.hb @@ -8,13 +8,13 @@ bmp_1 := @embed("./assets/able.bmp") bmp_2 := @embed("./assets/mini.bmp") example := fn(): void { - images := [render.Surface; 2].( + images := [render.Surface].( render.image.surface_from_bmp(@bitcast(&bmp_1)), render.image.surface_from_bmp(@bitcast(&bmp_2)), ) screen := render.init(true) vel := Vec2(int).(1, 1) - pos := Vec2(int).(100, 100) + pos := Vec2(uint).(100, 100) n := 0 loop { image := images[n] @@ -31,7 +31,7 @@ example := fn(): void { n = 1 - n } - pos += vel + pos += @bitcast(vel) } return } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/lines.hb b/sysdata/programs/render_example/src/examples/lines.hb index 386883d8..346ca9e9 100644 --- a/sysdata/programs/render_example/src/examples/lines.hb +++ b/sysdata/programs/render_example/src/examples/lines.hb @@ -8,8 +8,8 @@ render := @use("../../../../libraries/render/src/lib.hb") example := fn(): void { screen := render.init(true) render.clear(screen, .(100, 50, 0, 255)) - p0 := Vec2(int).(0, 0) - p1 := Vec2(int).(0, screen.height) + p0 := Vec2(uint).(0, 0) + p1 := Vec2(uint).(0, screen.height) loop if p0.y >= screen.height break else { render.put_line(screen, p0, p1, .(255, 180, 100, 255)) render.put_line(screen, .(screen.width, screen.height) - p0, .(screen.width, screen.height) - p1, .(255, 180, 100, 255)) diff --git a/sysdata/programs/render_example/src/examples/random.hb b/sysdata/programs/render_example/src/examples/random.hb index fed2790d..6710c107 100644 --- a/sysdata/programs/render_example/src/examples/random.hb +++ b/sysdata/programs/render_example/src/examples/random.hb @@ -5,11 +5,11 @@ example := fn(): void { screen := render.init(false) render.clear(screen, render.black) loop { - x := random.range(int, 0, 1024) - y := random.range(int, 0, 768) - r := random.range(int, 0, 255) - g := random.range(int, 0, 75) - b := random.range(int, 0, 155) + x := random.range(uint, 0, screen.width) + y := random.range(uint, 0, screen.height) + r := random.range(u8, 0, 255) + g := random.range(u8, 0, 75) + b := random.range(u8, 0, 155) render.put_pixel(screen, .(x, y), .(b, g, r, 255)) } return diff --git a/sysdata/programs/render_example/src/examples/square.hb b/sysdata/programs/render_example/src/examples/square.hb index 5f894956..b66723d7 100644 --- a/sysdata/programs/render_example/src/examples/square.hb +++ b/sysdata/programs/render_example/src/examples/square.hb @@ -9,8 +9,8 @@ example := fn(): void { screen := render.init(true) vel := Vec2(int).(1, 1) side := screen.width / 8 - pos := Vec2(int).((screen.width - side) / 2, (screen.height - side) / 2) - color := random.range(render.Color, render.black, render.white) + pos := Vec2(uint).((screen.width - side) / 2, (screen.height - side) / 2) + color := random.any(render.Color) loop { render.put_filled_rect(screen, pos, .(side, side), color) render.sync(screen) @@ -18,14 +18,14 @@ example := fn(): void { if pos.x == 0 | pos.x == screen.width - side { vel.x = -vel.x - color = random.range(render.Color, render.black, render.white) + color = random.any(render.Color) } if pos.y == 0 | pos.y == screen.height - side { vel.y = -vel.y - color = random.range(render.Color, render.black, render.white) + color = random.any(render.Color) } - pos += vel + pos += @bitcast(vel) } return } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/surface.hb b/sysdata/programs/render_example/src/examples/surface.hb index 003baec7..5a3eefc7 100644 --- a/sysdata/programs/render_example/src/examples/surface.hb +++ b/sysdata/programs/render_example/src/examples/surface.hb @@ -10,14 +10,12 @@ example := fn(): void { image := render.new_surface(screen.width / 3, screen.height / 3) vel := Vec2(int).(-1, -1) - pos := Vec2(int).(100, 100) + pos := Vec2(uint).(100, 100) side := image.width / 8 vel_inner := Vec2(int).(1, 1) - pos_inner := Vec2(int).((image.width - side) / 2, (image.height - side) / 2) - // workaround for compiler bug - color := render.Color.(0, 0, 0, 0) - color = random.range(render.Color, render.black, render.white) - target_color := color + pos_inner := Vec2(uint).((image.width - side) / 2, (image.height - side) / 2) + color := random.any(render.Color) + target_color := random.any(render.Color) loop { render.clear(screen, render.black) render.put_filled_rect(image, pos_inner, .(side, side), color) @@ -30,11 +28,11 @@ example := fn(): void { if pos_inner.x == 0 | pos_inner.x == image.width - side { vel_inner.x = -vel_inner.x - target_color = random.range(render.Color, render.black, render.white) + target_color = random.any(render.Color) } if pos_inner.y == 0 | pos_inner.y == image.height - side { vel_inner.y = -vel_inner.y - target_color = random.range(render.Color, render.black, render.white) + target_color = random.any(render.Color) } if pos.x == 0 | pos.x == screen.width - image.width { @@ -45,13 +43,13 @@ example := fn(): void { } color += .( - @intcast(color.b < target_color.b) - @intcast(color.b > target_color.b), - @intcast(color.g < target_color.g) - @intcast(color.g > target_color.g), - @intcast(color.r < target_color.r) - @intcast(color.r > target_color.r), + @bitcast(color.b < target_color.b) - @bitcast(color.b > target_color.b), + @bitcast(color.g < target_color.g) - @bitcast(color.g > target_color.g), + @bitcast(color.r < target_color.r) - @bitcast(color.r > target_color.r), 0, ) - pos += vel - pos_inner += vel_inner + pos += @bitcast(vel) + pos_inner += @bitcast(vel_inner) } return } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/tactical_screen.hb b/sysdata/programs/render_example/src/examples/tactical_screen.hb index 3fc1f164..b15792a6 100644 --- a/sysdata/programs/render_example/src/examples/tactical_screen.hb +++ b/sysdata/programs/render_example/src/examples/tactical_screen.hb @@ -12,7 +12,7 @@ example := fn(): void { width := screen.width height := screen.height cell_size := 0 - range := Vec2(int).(0, 0) + range := Vec2(uint).(0, 0) if width > height { cell_size = width / 40 range = .(39, height / cell_size - 1) @@ -24,19 +24,19 @@ example := fn(): void { height -= 1 scroll := 0 - target := Vec2(int).(random.range(int, 0, range.x), random.range(int, 0, range.y)) + target := Vec2(uint).(random.range(uint, 0, range.x), random.range(uint, 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)) + seeker := Vec2(uint).(random.range(uint, 0, range.x), random.range(uint, 0, range.y)) loop { render.clear(screen, render.black) - target_pixel_coord := target * .(cell_size, cell_size) + .(scroll, scroll) - render.put_trirect(screen, target_pixel_coord, .(cell_size, cell_size), render.red, render.light_red) + target_pixel_coord := target * .(@bitcast(cell_size), @bitcast(cell_size)) + .(scroll, scroll) + render.put_trirect(screen, target_pixel_coord, .(@bitcast(cell_size), @bitcast(cell_size)), render.red, render.light_red) render.put_hline(screen, target_pixel_coord.y + halfcell, target_pixel_coord.x - octcell, target_pixel_coord.x - sevenoctcell, render.light_red) render.put_hline(screen, target_pixel_coord.y + halfcell, target_pixel_coord.x + cell_size + octcell, target_pixel_coord.x + cell_size + sevenoctcell, render.light_red) @@ -69,7 +69,7 @@ example := fn(): void { } else if seeker.y > target.y { seeker.y -= 1 } else { - target = .(random.range(int, 0, range.x), random.range(int, 0, range.y)) + target = .(random.range(uint, 0, range.x), random.range(uint, 0, range.y)) } scroll += 1 diff --git a/sysdata/programs/serial_driver/src/main.hb b/sysdata/programs/serial_driver/src/main.hb index 0173b67f..51343634 100644 --- a/sysdata/programs/serial_driver/src/main.hb +++ b/sysdata/programs/serial_driver/src/main.hb @@ -1,7 +1,7 @@ .{memory, buffer} := @use("../../../libraries/stn/src/lib.hb") serial_print := fn(ptr: ^u8): void { - letter := 0 + letter := @as(u8, 0) loop if *ptr == 0 break else { letter = *ptr memory.outb(0xF803, letter) diff --git a/sysdata/programs/tests/src/main.hb b/sysdata/programs/tests/src/main.hb index b2eee5ee..049e404d 100644 --- a/sysdata/programs/tests/src/main.hb +++ b/sysdata/programs/tests/src/main.hb @@ -10,9 +10,9 @@ service_search := fn(): void { main := fn(): int { //service_search() buf := "\0\0\0\0" - x := 0 - loop if x == 255 break else { - log.info(string.display_int(x, buf, 10)) + x := @as(int, 0) + loop if x > 255 break else { + log.info(string.display_int(x, buf, 2)) x += 1 } return 0 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index f5562824..a97af89b 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,11 +37,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 eaace7d9c1def6ce66ecf602176b26669e14f9a1 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sat, 26 Oct 2024 09:23:28 +0100 Subject: [PATCH 24/66] unoptimised text load & render --- sysdata/consolefonts/tamsyn/10x20b.psf | Bin 0 -> 10823 bytes sysdata/consolefonts/tamsyn/10x20r.psf | Bin 0 -> 10823 bytes sysdata/consolefonts/tamsyn/5x9b.psf | Bin 0 -> 2887 bytes sysdata/consolefonts/tamsyn/5x9r.psf | Bin 0 -> 2887 bytes sysdata/consolefonts/tamsyn/6x12b.psf | Bin 0 -> 3655 bytes sysdata/consolefonts/tamsyn/6x12r.psf | Bin 0 -> 3655 bytes sysdata/consolefonts/tamsyn/7x13b.psf | Bin 0 -> 3911 bytes sysdata/consolefonts/tamsyn/7x13r.psf | Bin 0 -> 3911 bytes sysdata/consolefonts/tamsyn/7x14b.psf | Bin 0 -> 4167 bytes sysdata/consolefonts/tamsyn/7x14r.psf | Bin 0 -> 4165 bytes sysdata/consolefonts/tamsyn/8x15b.psf | Bin 0 -> 4423 bytes sysdata/consolefonts/tamsyn/8x15r.psf | Bin 0 -> 4421 bytes sysdata/consolefonts/tamsyn/8x16b.psf | Bin 0 -> 4679 bytes sysdata/consolefonts/tamsyn/8x16r.psf | Bin 0 -> 4677 bytes sysdata/consolefonts/tamsyn/LICENSE | 10 +++ sysdata/libraries/render/TODO.md | 5 +- sysdata/libraries/render/src/image.hb | 4 +- sysdata/libraries/render/src/lib.hb | 2 + sysdata/libraries/render/src/software.hb | 63 ++++++++++++++- sysdata/libraries/render/src/text.hb | 75 ++++++++++++++++++ sysdata/libraries/stn/src/assets/sin_table | Bin 0 -> 728 bytes .../stn/src/assets/this-is-temporary | 0 sysdata/libraries/stn/src/math.hb | 28 ++++--- .../render_example/src/examples/text.hb | 23 ++++++ sysdata/programs/render_example/src/main.hb | 2 +- sysdata/system_config.toml | 12 +-- 26 files changed, 198 insertions(+), 26 deletions(-) create mode 100644 sysdata/consolefonts/tamsyn/10x20b.psf create mode 100644 sysdata/consolefonts/tamsyn/10x20r.psf create mode 100644 sysdata/consolefonts/tamsyn/5x9b.psf create mode 100644 sysdata/consolefonts/tamsyn/5x9r.psf create mode 100644 sysdata/consolefonts/tamsyn/6x12b.psf create mode 100644 sysdata/consolefonts/tamsyn/6x12r.psf create mode 100644 sysdata/consolefonts/tamsyn/7x13b.psf create mode 100644 sysdata/consolefonts/tamsyn/7x13r.psf create mode 100644 sysdata/consolefonts/tamsyn/7x14b.psf create mode 100644 sysdata/consolefonts/tamsyn/7x14r.psf create mode 100644 sysdata/consolefonts/tamsyn/8x15b.psf create mode 100644 sysdata/consolefonts/tamsyn/8x15r.psf create mode 100644 sysdata/consolefonts/tamsyn/8x16b.psf create mode 100644 sysdata/consolefonts/tamsyn/8x16r.psf create mode 100644 sysdata/consolefonts/tamsyn/LICENSE create mode 100644 sysdata/libraries/render/src/text.hb create mode 100644 sysdata/libraries/stn/src/assets/sin_table create mode 100644 sysdata/libraries/stn/src/assets/this-is-temporary create mode 100644 sysdata/programs/render_example/src/examples/text.hb diff --git a/sysdata/consolefonts/tamsyn/10x20b.psf b/sysdata/consolefonts/tamsyn/10x20b.psf new file mode 100644 index 0000000000000000000000000000000000000000..4a582d9edd10dd598469dd6bb3ffb479fadb5145 GIT binary patch literal 10823 zcmeHN+ml>H8Sm6qC6=|tzVNUqQVCIRqQb_N5MkK~ihu~=uBd=Bj&1~US%^Yn;!Xn! z%0;<^n{p>0_lt&$f-sq{%BN6yg~}T*`~keC>c{t;Z@%e1=k&~(G8^DwyQ@$4>HhWC z{ayMy-92;N>*w6!I2_4okreoG9A}4f`m(%7q2-!UY(*`@7oYc+t{o-~Px13bPPv&Z zPJO=9D2+bqit#`Rldd>W>M4}UVXR#oA^kqqF86hbF_5H}>N~%6NX~D5$4K!zto+jY zD9d_SKGJe7(@WRPw_FYtz2$DwO>o?iJty3$ZquO}Dxy2?rm{Hh#=Etl6kzHxWC>U*`!w{a7J9|7$S?AuE#X$VR0-8tkHsMR zZMyYtv)k^rWaa51Bzqs_ai-jcjGl6wo~h8&u~oWtm;Ro&viXg&oCU3xl^S@&EpcP+ z2)5FuYk9?{aIMTr>u#=s-ZIvBo#R}_tMH!YSLc_PUus;HpHLB6{pp4FS~kPQ3B4Kp zSf_E>+AWXUq%3+V+Lc&=YC=}PVjjDHFilyWLe{etCX`ISDOAlhG8+_>2ANwih)nkQ zJ$|46up9UJwrjI?YDFk>XA%s%#S(}s=DJ$2yva4Gu%c@RVha){+=>*8{rpUqqH1@B zYf>TDpHWOpv5c#y$nk8{PV2ljW#q{|^V3=J+K6VEB4~a}*RDL%>sMEpt;zoO-0)47 z%m%lW>*P)vlf$2zjYqliOq3X zda3mk(^I)LQ{>pfb+k3^FsIT^+i6*E<#i+j1U{{l_G{KE`D%`_4~Zul^steo-D2&e z^{e!h&g$+gVmekgKj&!U=#H4rF5$M-nsLS^;f^l(< z{aHHZwelsFmvH(2hj=%wyOR7)We{cEgVnlHjycs{!mq?&gQA^02QhtFBi1LiKGZHp z*j8h!DUWM3g`RjKrCG*}WqY%6M%WH7y_Yn4#c_2$!LDuYd48MLy_eg!XGdgvDgP%H zAYnRb3&HQ(xaI-%8&O^#`SHx|(|%XRZMkc5QR-5mieKxa`Y_!ExmmmAd3tj_o3M7v z)A|^(=3c&;clRbE$@>NMf#w0zb}AN-<{3 zk|lL;VJY)Gke?@l277#_kZfk<$f}@E0dp{w!^6{e_k;8RpCs2RXCGN6;AIq_)P0{ z*5R4=aJ~6e;Cl0`!1d-=iBqXhv+rKzSMi;qJXf(t&!3acUaIaLjon}`HmLPY~MU~48|2k|l5N{F(lZ@Oq$<3DCu9$~3tj(|rXOgMHnZ;D$9Ip)B zUk#q0`M+v<#?f{^+8%AU+T#88x*OV7U(7{@B5#SZj$;4kxK7rb1eLXUq(Sl%UG&4n z@*9MUMW1M#1})?;V4F2jjy02II`~tHyP1+>d3pw}Sni-S^8bJ|BX;1iM$8 z&f@~Vst?5!{a?JV8;lG54#tJ~4aNnzRN;g#(U=7NTG?AxRd}cm3?6WrEi(?mPE8`3 z_qWr*xFDCoxWMmVT(G|)PP|h7>!oXP6aQ1d>}3mkIIRsl>&~h8o0Q&n8a?2n`|p8- z&a3kLXy=2R?&5R@r=6VM$!dM}e3;*NaoWx4MNT(K7j&Kij>JlQ1z*KcI2y;`SR9A1 z;dp!<-@rF<0#3vPR^eNi#J6!0R^w!>!FO;9PQ?^X!!&AGi_`I4d=F>f`#2M4;cWZ> z=irAp7w2Ie&c_9~5EtQMT!KsSBm5Yb;V1Yheum3&1+K);QAY#6Koh^juP}r4_%*J= z)wl*5uo0WE8MC++*I^5OgWuwM+<>jvhTma3cHsAz%eWZ$=sb?j6X-lCymoe>^E5io zFyqgn^BiX{aC%7uk%_RgTS#@@lE>Rw@jZ|S-3Yn~bTjA=pg)3c0o@Av6X?&N+d#L2 z?f~5h`U~ihd_S={T=i$=n>GPpvORugPs6A33>{& z3-mPT8PGpK&w`!!3G4Z-U+e{TuWj(A%JQ MK>r233wjTq0xbmld;kCd literal 0 HcmV?d00001 diff --git a/sysdata/consolefonts/tamsyn/10x20r.psf b/sysdata/consolefonts/tamsyn/10x20r.psf new file mode 100644 index 0000000000000000000000000000000000000000..e9222b77d336baf4eeeedde32764cf5352cc7b62 GIT binary patch literal 10823 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 B)?5Gp literal 0 HcmV?d00001 diff --git a/sysdata/consolefonts/tamsyn/5x9r.psf b/sysdata/consolefonts/tamsyn/5x9r.psf new file mode 100644 index 0000000000000000000000000000000000000000..185806c5575d067809dc630076f27bc65c75dde7 GIT binary patch literal 2887 zcmeHI*_IVG6fGZo@U1=}&a+z*IDpHPc0s7Kzm`y^G>Ecz}UeD-uFwX;uhlI$esF5R(Yc|tQRhbUlm7(ZGL)%GIG z=GwnSo{5C>ZX&-x8d}{oYi(&`N`cyxwiN8sN*jx9-(pG`9niRSAn*TJ`VHpg}6{|Jvg4ho0(>W7Mhx1ic6uJ)Q8}Io|YR^9fc@aHi zd}O$uA}*Dnh-A<0^t)7S0_b2WG&?Wa98} zOgdM8)flCl-8-i)5>1RueF9jKaY5>8cxny5xt^r8d6E{AXIHyPN5GZ4g4JfASivu$`ZL7xDO8Pe72Lq{uJQZ;e$ zLHZDfyKw>N>_H)&G)>7>!>AZ{W*Y6IvkyXN-|CZav}>6r^hrqgzE8M86i8hIDq#%A z>vCv(7gWpIA${YD)&KUN6ySr?OJVY|%nc*-$x3f(I166Ym`POipnN}3)+4^y_9paH zGVP}q8Ug7+PP8^_O!T#6Y7XYz-)G$Ztp)6-{X%0h;s$Mz^L-7n@L-K?fh}7wu1s`g zNy`kfmf<^V%5W7!jxvrHKc=R%1MC@@OtYvyd~4d&ly-oxom9@>1pS6rnbg@htHVXt z+{7GVc{(b1YFc1R=1OJw7$WfcZ4ZS@)D5SBDL9;-IJ^ZM9+~h}mo=QxK z0n6X0Ztt}ZIVuR`I8zXaV6i?)5J>wl{pBn)704C3L56Wkm{=`u`JUe7;OX$4#yEV5=Y@^9D`$V z9FE5cI1wk|WSoLiF~Dg!9cSQ7oQ1P-4yJG}&cpe*02ksSTnvj#a49as<+uV@;wntz zYAnGuxE9ypdfb2vH{vGTj9YLkZo}=k19##s+>Lv1FYd$raCiU@;vqbYNAM^f!{c}Y zPvR*&jc4#Io6m_d1%zXcI=8 zx$!xSw(zr+WxEOyBpl6asL@WXb_KWxQb68;EQ7oYc@OeFWI5ym$O_1ZkdGiALsmjQ zfqV-24B{c5Lsmh)fXqO?gsg^q1z7|68uATfEo2?!TgZ2i?;#~*J!Au9BV-n`39=dT z17r@e1@a?gD`XpFJLD(G&yWi83*=YGZ;*M&4#@A2KOj3He?tC(?1Jou{0;dBvIqYH D?YdE9 literal 0 HcmV?d00001 diff --git a/sysdata/consolefonts/tamsyn/6x12b.psf b/sysdata/consolefonts/tamsyn/6x12b.psf new file mode 100644 index 0000000000000000000000000000000000000000..4ae9d6a4480198d161fc665bc51ba903c5675c51 GIT binary patch literal 3655 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 literal 0 HcmV?d00001 diff --git a/sysdata/consolefonts/tamsyn/7x14b.psf b/sysdata/consolefonts/tamsyn/7x14b.psf new file mode 100644 index 0000000000000000000000000000000000000000..a473750d7816a1fbf8dd3b58a859e506d5aa2b63 GIT binary patch literal 4167 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 literal 0 HcmV?d00001 diff --git a/sysdata/consolefonts/tamsyn/8x15r.psf b/sysdata/consolefonts/tamsyn/8x15r.psf new file mode 100644 index 0000000000000000000000000000000000000000..0054a2b4f3ed691ef2bcd72e08ad90c94e0ecdb5 GIT binary patch literal 4421 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 qd%4T-YdG@`(`|h62ilaSiQ*0soIQ7CI z%ZKWL!fcI5w}wIf*uUTZ`P2(Soz-f!^MWKP6qt5;l>BKs??RgBuzgN+{1(H}-TZl; zI1fE0J9|#Qea8M-JX&Au&nP(;Pptm}Uh;)7JROEB){jj#s~amsl}IX8Wb*}!#Rlit z&2$j7g<(FOE(O-vj(X3m;EHMW@rK?}tJOMY)VsUqYOJ(-`Z7UaOa?(l5yEWNBq5s( zEn_j)$R(U0`E7PfbdOw)iX1pgE$4EEm`-Q3$uO)`^h_!0?pZVtMI|d71<>IhMSK0Gncm(tBp)Xdrz50r81nP=V@q|JIv{CSO=W5B}Gfn)q9yHNA`$z z-T6-o7Tb80ZvS)mztIk`M&>*7&Ff@ikiFkce`9jx%zP`T2y_N84&5}vcZ=H?4_nw~@lcfvQB)nWnDJMJ*_{UtR}%8wQB-j9N?0F3mR{D`4}!AA%%w_a zoWH1ATexgtt?K+!&Zy?>buI6&*SpxPd^E7AhxX!lRDM-+>!D*)3M_w2&!qK?rqi6W zS7z+FOgcSm`Bg3IX0g#eqF>`;F87>>`B?yi@%kHP zN{rZF$@H?t_Kg2-{j`7d*R6j_S2*LNW+M{&rxTh=P45U*i}jUt0l0j)_$I`3osL94 zh!g9dO6dZK*PqxFz-W1sAI`D@xWyZq&UCWq&fnQ1Q~fyC3K^ zqkRw4zK3bg!`UF^-4`hDVaj`$_2yyvR4TN$uWD~E=)w{Bvye}yG39RiW zu(p@PTp|Cy;A184(dlmzw~W7X+*Om~^-Zbxy!!(6(;qf_Uoif~*!mY^ogc;S_*H(A znEOSaPw{;)l`iQ&Af<{?N_vhlfY_&HaI?UOK?|29}!p%?Q7u z1E)4`glf&hYCN@R3;PE4_3WG2Pw3gzdfZC9i~R`uLH0FD1kIg5h;|e(8AV)!YcT~= zF%8$@dfb2;F&#Id12ZraH)9rV!EDSyC+6Z-+=dcvM;UjZf-Xe36II-WZp=dsb@bqF z%ttTo!2&Eq9~NOT?nOWD!~Ix-2k;;s!cshpNAM_?;V}$gIUdIocoI+HX*`2x@f@DV z3wRMPVGu841zy3ccnz;(C5EsHZ{SS~<1I9F6z3e8+tA#O<_;BWa~GPs(cHtp_oBIv zj|1$7R3dZ=m7?Y`H5}KkCn2jL??Bc--i5pec^|SC@&V*S$VZTMkdGnjA)i1#g?t9t z0QnrU5%L9O6XZ+CX2@5NEs(Dv-$1^FY=wLW`5y8EWE*5VWCvs?WEW&NWDn#=$X>`k y$bQHH$U(><$WM@;A%`KqKz@b%1~~#b3i%!K2jm#!Psm@8= surface.width { + cursor.x = pos.x + cursor.y += font.height + font.line_gap + } + if cursor.y + font.height > surface.height break + + dest := @inline(indexptr, surface, cursor.x, cursor.y) + src := glyph_data + rows_remaining := font.height + + loop if rows_remaining == 0 break else { + byte := *glyph_data + pixel_dest := dest + mask := @as(u8, 0x80) + bits_remaining := font.width + + loop if bits_remaining == 0 break else { + if (byte & mask) != 0 { + *pixel_dest = color + } + pixel_dest += 1 + mask >>= 1 + if mask == 0 { + glyph_data += 1 + byte = *glyph_data + mask = 0x80 + } + bits_remaining -= 1 + } + + glyph_data += 1 + dest += surface.width + rows_remaining -= 1 + } + + cursor.x += font.width + font.char_gap + current_char += 1 + } + return } \ No newline at end of file diff --git a/sysdata/libraries/render/src/text.hb b/sysdata/libraries/render/src/text.hb new file mode 100644 index 00000000..46896106 --- /dev/null +++ b/sysdata/libraries/render/src/text.hb @@ -0,0 +1,75 @@ +.{log} := @use("../../stn/src/lib.hb") + +PSF1Header := packed struct { + magic: u16, + font_mode: u8, + character_size: u8, +} + +PSF2Header := packed struct { + magic: u32, + version: u32, + header_size: u32, + flags: u32, + num_glyph: u32, + bytes_per_glyph: u32, + height: u32, + width: u32, +} + +Font := struct { + data: ^u8, + width: uint, + height: uint, + num_glyphs: uint, + bytes_per_glyph: uint, + has_unicode_table: bool, + line_gap: uint, + char_gap: uint, +} + +font_from_psf1 := fn(psf: ^u8): Font { + header := @as(^PSF1Header, @bitcast(psf)) + if header.magic != 0x436 { + log.error("failed to load psf font: not a psf1 font, idiot\0") + return idk + } + + psf += @sizeof(PSF1Header) + + return .( + psf, + 8, + @intcast(header.character_size), + 256, + @intcast(header.character_size), + false, + 0, + 0, + ) +} + +font_from_psf2 := fn(psf: ^u8): Font { + header := @as(^PSF2Header, @bitcast(psf)) + if header.magic != 0x864AB572 { + log.error("failed to load psf font: not a psf2 font, idiot\0") + return idk + } + + psf += header.header_size + + return .( + psf, + @intcast(header.width), + @intcast(header.height), + @intcast(header.num_glyph), + @intcast(header.bytes_per_glyph), + (header.flags & 1) != 0, + 0, + 0, + ) +} + +get_glyph := fn(font: Font, index: uint): ^u8 { + return font.data + index * font.bytes_per_glyph +} \ No newline at end of file diff --git a/sysdata/libraries/stn/src/assets/sin_table b/sysdata/libraries/stn/src/assets/sin_table new file mode 100644 index 0000000000000000000000000000000000000000..0d1c9d8b1737b6f0e24ea094333419297756bd95 GIT binary patch literal 728 zcmXZUJxc;m7(ijO4>L{8vNBCg%b+ocen8M)DA1|Jp`oFmKw6v{S{hthirQ@`h^R>l zY-tLjIS7KF7HPEMoVz^tyzk`(0-Tms`?y4cc&D{~IyHc8-5I2_L)g*15S<^!XWb3^ zegq$NJ3=R;c&pV>x~CU0dM%D0+DOoaF?`UCB>k&L<8*EUuXQ~|ziDlf?rLL-j!t7! zGimx=KS6wPHKhzh^l$h7_ vO1ru`&wgKT^he7J>??Y#_xh>vGVfM&QxCMMEq&5={n9@TF7jVkW17%^E6g+* literal 0 HcmV?d00001 diff --git a/sysdata/libraries/stn/src/assets/this-is-temporary b/sysdata/libraries/stn/src/assets/this-is-temporary new file mode 100644 index 00000000..e69de29b diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index 563e9c3d..91089194 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -30,26 +30,30 @@ Vec2 := fn($Expr: type): type { return struct {x: Expr, y: Expr} } -SIN_TABLE := [int].(0, 174, 348, 523, 697, 871, 1045, 1218, 1391, 1564, 1736, 1908, 2079, 2249, 2419, 2588, 2756, 2923, 3090, 3255, 3420, 3583, 3746, 3907, 4067, 4226, 4384, 4540, 4695, 4848, 5000, 5150, 5299, 5446, 5591, 5735, 5877, 6018, 6156, 6293, 6427, 6560, 6691, 6819, 6946, 7071, 7193, 7313, 7431, 7547, 7660, 7771, 7880, 7986, 8090, 8191, 8290, 8386, 8480, 8571, 8660, 8746, 8829, 8910, 8987, 9063, 9135, 9205, 9271, 9335, 9396, 9455, 9510, 9563, 9612, 9659, 9702, 9743, 9781, 9816, 9848, 9877, 9902, 9925, 9945, 9961, 9975, 9986, 9993, 9998, 10000) +SIN_TABLE := @as([int; 91], @bitcast(@embed("./assets/sin_table"))) -sin_i := fn(theta_deg: int, amplitude: int): int { - theta := theta_deg % 360 +sin := fn(theta: int, amplitude: uint): int { if theta < 0 { - theta += 360 + theta += (-theta / 360 + 1) * 360 + } else if theta >= 360 { + theta -= theta / 360 * 360 } quadrant := theta / 90 - theta = theta % 90 + index := theta % 90 - sign := 1 - ((quadrant & 2) >> 1) * 2 - complement := quadrant & 1 + if @as(u8, @intcast(quadrant)) == @as(u8, 1) { + index = 90 - index + } - index := theta * (1 - complement) + (90 - theta) * complement - sin_value := SIN_TABLE[index] * sign + value := SIN_TABLE[@bitcast(index)] + if quadrant >= 2 { + value = -value + } - return (sin_value * amplitude + 5000) / 10000 + return (value * @bitcast(amplitude) + 5000) / 10000 } -cos_i := fn(theta_deg: int, amplitude: int): int { - return @inline(sin_i, theta_deg + 90, amplitude) +cos := fn(theta: int, amplitude: uint): int { + return @inline(sin, theta + 90, amplitude) } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb new file mode 100644 index 00000000..98789d64 --- /dev/null +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -0,0 +1,23 @@ +.{Vec2, sin, cos} := @use("../../../../libraries/stn/src/lib.hb").math +render := @use("../../../../libraries/render/src/lib.hb") + +/* expected result: + words */ + +psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") + +example := fn(): void { + screen := render.init(true) + font := render.text.font_from_psf2(@bitcast(&psf)) + t := 0 + str := "Hello, World! +This is a test +of multiline rendering\0" + loop { + render.clear(screen, render.black) + render.put_text(screen, font, .(t, t % screen.height), render.red, str) + render.sync(screen) + t += 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 289d8128..3edd356e 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1,3 +1,3 @@ -.{example} := @use("./examples/surface.hb") +.{example} := @use("./examples/text.hb") main := example \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index a97af89b..f5562824 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,11 +37,11 @@ resolution = "1024x768x24" # [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 3c3c156eb0e7f740c16f0d04daca32529de45e62 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 1 Nov 2024 13:09:12 -0500 Subject: [PATCH 25/66] misc changes + mouse driver work --- Cargo.lock | 6 +- repbuild/Cargo.toml | 2 +- repbuild/src/dev.rs | 7 +- repbuild/src/main.rs | 3 +- sysdata/libraries/render/src/software.hb | 2 +- sysdata/programs/horizon/src/main.hb | 7 ++ .../horizon_testing_program/src/main.hb | 2 + sysdata/programs/mouse_driver/README.md | 1 + sysdata/programs/mouse_driver/meta.toml | 11 +++ sysdata/programs/mouse_driver/src/main.hb | 79 +++++++++++++++++++ .../render_example/src/examples/text.hb | 7 +- sysdata/programs/render_example/src/main.hb | 4 +- sysdata/system_config.toml | 8 +- 13 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 sysdata/programs/mouse_driver/README.md create mode 100644 sysdata/programs/mouse_driver/meta.toml create mode 100644 sysdata/programs/mouse_driver/src/main.hb diff --git a/Cargo.lock b/Cargo.lock index 5b028ce4..b20adbfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,12 +253,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#faa8dd2e6fabe2e0e4a375e677171856da491c61" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#38a00cbaa09434324d209fc5f59480d2b6743fb3" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#faa8dd2e6fabe2e0e4a375e677171856da491c61" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#38a00cbaa09434324d209fc5f59480d2b6743fb3" dependencies = [ "hashbrown 0.15.0", "hbbytecode", @@ -270,7 +270,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#faa8dd2e6fabe2e0e4a375e677171856da491c61" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#38a00cbaa09434324d209fc5f59480d2b6743fb3" dependencies = [ "hbbytecode", ] diff --git a/repbuild/Cargo.toml b/repbuild/Cargo.toml index ea418579..a681d458 100644 --- a/repbuild/Cargo.toml +++ b/repbuild/Cargo.toml @@ -22,7 +22,7 @@ fatfs = "0.3" toml = "0.8" hblang = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ "std", - "opts", + # "opts", ], default-features = false } log = "0.4" raw-cpuid = "11" diff --git a/repbuild/src/dev.rs b/repbuild/src/dev.rs index 7186d39c..d2a66e34 100644 --- a/repbuild/src/dev.rs +++ b/repbuild/src/dev.rs @@ -47,7 +47,6 @@ impl Package { for (count, (name, table)) in bin_table.into_iter().enumerate() { // if count != 0 { - println!("{}", name); binaries.push(name.clone()); // } } @@ -75,7 +74,7 @@ impl Package { &path, Options { fmt: true, - optimize: true, + // optimize: true, ..Default::default() }, &mut bytes, @@ -84,7 +83,7 @@ impl Package { hblang::run_compiler( &path, Options { - optimize: true, + // optimize: true, ..Default::default() }, &mut bytes, @@ -101,7 +100,7 @@ impl Package { &path, Options { dump_asm: true, - optimize: true, + // optimize: true, ..Default::default() }, &mut bytes, diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 19f2d381..fadaa604 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -407,7 +407,8 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { "-parallel", "none", "-monitor", "none", "-machine", accel, - "-cpu", "max", + "-cpu", "max", "-serial", "stdio", + "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", ]); } diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 28f99e35..3fa79deb 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -36,7 +36,7 @@ free_surface := fn(surface: Surface): void { return @inline(memory.free, Color, surface.buf, @intcast(surface.width * surface.height), false) } -framebuffer := @as(^Color, idk) +framebuffer := @as(^Color, null) init := fn(doublebuffer: bool): Surface { framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0") diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index ed2c23fe..f8af80b4 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -15,6 +15,8 @@ Window := struct { y: int, } +psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") + main := fn(): int { win_buff := buffer.create("XHorizon\0") @@ -33,6 +35,8 @@ main := fn(): int { vel_inner := Vec2(int).(1, 1) pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) + str := "Window Title Bar\0" + font := render.text.font_from_psf2(@bitcast(&psf)) loop { // Clear the screen @@ -43,6 +47,8 @@ main := fn(): int { ret := buffer.recv([u8; 4096], win_buff, mem_buf) if ret == 0 { log.info("No messages\0") + } else { + log.info("Handle Messages\0") } } @@ -65,6 +71,7 @@ main := fn(): int { { render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) + render.put_text(window, font, .(window.width / 2, 1), render.white, str) } render.put_filled_rect(window, pos_inner, .(side, side), color) diff --git a/sysdata/programs/horizon_testing_program/src/main.hb b/sysdata/programs/horizon_testing_program/src/main.hb index 3fe241bd..d5b6362d 100644 --- a/sysdata/programs/horizon_testing_program/src/main.hb +++ b/sysdata/programs/horizon_testing_program/src/main.hb @@ -7,6 +7,8 @@ horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); ignim := @use("../../../libraries/ignim/src/lib.hb"); .{errors} := ignim +psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") + main := fn(): int { x := 0 // loop if x > 10000 break else x += 1 diff --git a/sysdata/programs/mouse_driver/README.md b/sysdata/programs/mouse_driver/README.md new file mode 100644 index 00000000..b951afac --- /dev/null +++ b/sysdata/programs/mouse_driver/README.md @@ -0,0 +1 @@ +# mouse_driver \ No newline at end of file diff --git a/sysdata/programs/mouse_driver/meta.toml b/sysdata/programs/mouse_driver/meta.toml new file mode 100644 index 00000000..21bdba85 --- /dev/null +++ b/sysdata/programs/mouse_driver/meta.toml @@ -0,0 +1,11 @@ +[package] +name = "mouse_driver" +authors = [""] + +[dependants.libraries] + +[dependants.binaries] +hblang.version = "1.0.0" + +[build] +command = "hblang src/main.hb" diff --git a/sysdata/programs/mouse_driver/src/main.hb b/sysdata/programs/mouse_driver/src/main.hb new file mode 100644 index 00000000..f1c24f31 --- /dev/null +++ b/sysdata/programs/mouse_driver/src/main.hb @@ -0,0 +1,79 @@ +.{memory, buffer, log, string} := @use("../../../libraries/stn/src/lib.hb") +render := @use("../../../libraries/render/src/lib.hb") + +ACK := 250 + +wait_for := fn(for: u8): void { + log.info("Start waiting\0") + loop { + if (memory.inb(0x64) & 2 >> for) == for { + log.info("End waiting\0") + return + } + } +} + +send_info := fn(info: u8): void { + wait_for(1) + memory.outb(0x64, info) +} + +send_command := fn(command: u8): void { + send_info(0xD4) + wait_for(1) + memory.outb(0x60, command) +} + +get_response := fn(): u8 { + wait_for(1) + return memory.inb(0x60) +} + +main := fn(): int { + // screen := render.init(true) + + format_page := memory.alloc(u8, 1024) + + wait_for(0) + memory.outb(0x64, 0xA8) + log.info("Aux mouse device enabled.\0") + + send_command(0xF6) + a := get_response() + + send_command(0xF4) + b := get_response() + + x := -0 + y := -0 + + loop { + // render.clear(screen, render.black) + + loop { + if (memory.inb(0x64) & 0x20) == 0x20 { + log.info("Yeah\0") + + break + } else { + } + } + + status := memory.inb(0x60) + log.info(string.display_int(status, format_page, 10)) + d_x := memory.inb(0x60) + log.info(string.display_int(d_x, format_page, 10)) + x = x + d_x + d_y := memory.inb(0x60) + y = y + d_y + log.info(string.display_int(d_y, format_page, 10)) + // render.put_rect(screen, .(x, y), .(x + 10, y + 10), render.white) + + log.info("XY\0") + + log.info(string.display_int(x, format_page, 10)) + log.info(string.display_int(y, format_page, 10)) + } + + return 0 +} \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 98789d64..11086678 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -10,14 +10,13 @@ example := fn(): void { screen := render.init(true) font := render.text.font_from_psf2(@bitcast(&psf)) t := 0 - str := "Hello, World! -This is a test -of multiline rendering\0" + str := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX\0" + loop { render.clear(screen, render.black) render.put_text(screen, font, .(t, t % screen.height), render.red, str) render.sync(screen) - t += 1 + // t += 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 3edd356e..77cb04c0 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1,3 +1 @@ -.{example} := @use("./examples/text.hb") - -main := example \ No newline at end of file +.{example: main} := @use("./examples/text.hb") \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index f5562824..09fe797d 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -57,3 +57,7 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" + + +[boot.limine.ableos.modules.mouse_driver] +path = "boot:///mouse_driver.hbf" From 80d363bc59320a410093783d9748b22955b72666 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 1 Nov 2024 15:11:33 -0500 Subject: [PATCH 26/66] panic func in stn --- sysdata/libraries/stn/src/panic.hb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sysdata/libraries/stn/src/panic.hb diff --git a/sysdata/libraries/stn/src/panic.hb b/sysdata/libraries/stn/src/panic.hb new file mode 100644 index 00000000..eddf15a8 --- /dev/null +++ b/sysdata/libraries/stn/src/panic.hb @@ -0,0 +1,8 @@ +panic := fn(message: ?^u8): void { + if message != null { + log.error(message) + } + // TODO: replace with die keyword when it gets pushed + loop { + } +} \ No newline at end of file From 69c95c35b5b4f3c5fb1e8631e6de53bf31cfc8ae Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 1 Nov 2024 17:37:47 -0500 Subject: [PATCH 27/66] fix serial argument --- repbuild/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index fadaa604..19f2d381 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -407,8 +407,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { "-parallel", "none", "-monitor", "none", "-machine", accel, - "-cpu", "max", "-serial", "stdio", - + "-cpu", "max", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", ]); } From 6894ef236044b92be6092c4dfbe0378922dd5604 Mon Sep 17 00:00:00 2001 From: Able Date: Sat, 2 Nov 2024 11:09:31 -0500 Subject: [PATCH 28/66] keycode nonsense --- sysdata/libraries/intouch/src/keycodes.hb | 162 ++++++++++++++-------- 1 file changed, 108 insertions(+), 54 deletions(-) diff --git a/sysdata/libraries/intouch/src/keycodes.hb b/sysdata/libraries/intouch/src/keycodes.hb index f0273bd5..cdab92f1 100644 --- a/sysdata/libraries/intouch/src/keycodes.hb +++ b/sysdata/libraries/intouch/src/keycodes.hb @@ -1,67 +1,121 @@ +/* +Originally I was modelling this after the following(1). I have since changed my mind. +I am now modelling it as I see fit. This is likely not the final version. + +1) https://www.libsdl.org/release/SDL-1.2.15/include/SDL_keysym.h +*/ + KeyCode := u32 -// https://www.libsdl.org/release/SDL-1.2.15/include/SDL_keysym.h -Backspace := KeyCode.(8) -Tab := KeyCode.(9) -Clear := KeyCode.(12) -Return := KeyCode.(13) -Pause := KeyCode.(19) -Escape := KeyCode.(27) -Space := KeyCode.(32) +// Typically this is not a keycode you will ever recieve. +None := KeyCode.(0) -A := KeyCode.(97) -/* -ETC -*/ -Z := KeyCode.(122) +Escape := KeyCode.(1) +/* Alphabet keycodes */ -Delete := KeyCode.(127) +A := KeyCode.(2) +B := KeyCode.(3) +C := KeyCode.(4) +D := KeyCode.(5) +E := KeyCode.(6) +F := KeyCode.(7) +G := KeyCode.(8) +H := KeyCode.(9) +I := KeyCode.(10) +J := KeyCode.(11) +K := KeyCode.(12) +L := KeyCode.(13) +M := KeyCode.(14) +N := KeyCode.(15) +O := KeyCode.(16) +P := KeyCode.(17) +Q := KeyCode.(18) +R := KeyCode.(19) +S := KeyCode.(20) +T := KeyCode.(21) +U := KeyCode.(22) +V := KeyCode.(23) +W := KeyCode.(24) +X := KeyCode.(25) +Y := KeyCode.(26) +Z := KeyCode.(27) -/* -ETC -*/ +/* Numeric keycodes*/ -KeypadNumber0 := KeyCode.(256) -KeypadNumber1 := KeyCode.(257) -KeypadNumber2 := KeyCode.(258) -KeypadNumber3 := KeyCode.(259) -KeypadNumber4 := KeyCode.(260) -KeypadNumber5 := KeyCode.(261) -KeypadNumber6 := KeyCode.(262) -KeypadNumber7 := KeyCode.(263) -KeypadNumber8 := KeyCode.(264) -KeypadNumber9 := KeyCode.(265) +Number0 := KeyCode.(28) +Number1 := KeyCode.(29) +Number2 := KeyCode.(30) +Number3 := KeyCode.(31) +Number4 := KeyCode.(32) +Number5 := KeyCode.(33) +Number6 := KeyCode.(34) +Number7 := KeyCode.(35) +Number8 := KeyCode.(36) +Number9 := KeyCode.(37) -KeypadPeriod := KeyCode.(266) -KeypadDivide := KeyCode.(267) -KeypadMultiply := KeyCode.(268) -KeypadMinus := KeyCode.(269) -KeypadPlus := KeyCode.(270) -KeypadEnter := KeyCode.(271) -KeypadEquals := KeyCode.(272) +KeypadNumber0 := KeyCode.(38) +KeypadNumber1 := KeyCode.(39) +KeypadNumber2 := KeyCode.(40) +KeypadNumber3 := KeyCode.(41) +KeypadNumber4 := KeyCode.(42) +KeypadNumber5 := KeyCode.(43) +KeypadNumber6 := KeyCode.(44) +KeypadNumber7 := KeyCode.(45) +KeypadNumber8 := KeyCode.(46) +KeypadNumber9 := KeyCode.(47) -NumLock := KeyCode.(300) -CapsLock := KeyCode.(301) -ScrollLock := KeyCode.(302) +KeypadPeriod := KeyCode.(48) +KeypadDivide := KeyCode.(49) +KeypadMultiply := KeyCode.(50) +KeypadMinus := KeyCode.(51) +KeypadPlus := KeyCode.(52) +KeypadEnter := KeyCode.(53) +KeypadEquals := KeyCode.(54) -RightShift := KeyCode.(303) -LeftShift := KeyCode.(304) - -RightControl := KeyCode.(305) -LeftControl := KeyCode.(306) -RightAlt := KeyCode.(307) -LeftAlt := KeyCode.(308) -RightMeta := KeyCode.(309) -LeftMeta := KeyCode.(310) - -/* Left "Windows" key */ -LeftSuper := KeyCode.(311) - -/* Right "Windows" key */ -RightSuper := KeyCode.(312) +Delete := KeyCode.(55) +/* Locking Keys */ +NumLock := KeyCode.(56) +CapsLock := KeyCode.(57) +ScrollLock := KeyCode.(58) /* "Alt Gr" key */ -Mode := KeyCode.(313) +Mode := KeyCode.(59) /* Multi-key compose key */ -Compose := KeyCode.(314) \ No newline at end of file +Compose := KeyCode.(60) + +LeftAlt := KeyCode.(61) +LeftControl := KeyCode.(62) +LeftMeta := KeyCode.(63) +LeftShift := KeyCode.(64) +/* Left "Windows" key */ +LeftSuper := KeyCode.(65) + +RightAlt := KeyCode.(66) +RightControl := KeyCode.(67) +RightMeta := KeyCode.(68) +RightShift := KeyCode.(69) +/* Right "Windows" key */ +RightSuper := KeyCode.(70) + +/* + This block of any triggers on any press of any of the keys. + Typically this is the event to care about. +*/ +AnyNumber0 := KeyCode.(71) +AnyNumber1 := KeyCode.(72) +AnyNumber2 := KeyCode.(73) +AnyNumber3 := KeyCode.(74) +AnyNumber4 := KeyCode.(75) +AnyNumber5 := KeyCode.(76) +AnyNumber6 := KeyCode.(77) +AnyNumber7 := KeyCode.(78) +AnyNumber8 := KeyCode.(79) +AnyNumber9 := KeyCode.(80) + +AnyAlt := KeyCode.(81) +AnyControl := KeyCode.(82) +AnyMeta := KeyCode.(83) +AnyShift := KeyCode.(84) +/* Any "Windows" key */ +AnySuper := KeyCode.(85) \ No newline at end of file From 9e83707a28f8c279e90bac9acff93c3ae15ee361 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 3 Nov 2024 22:31:53 +0000 Subject: [PATCH 29/66] misc changes --- Cargo.lock | 30 +++++++-------- sysdata/libraries/horizon_api/src/lib.hb | 2 +- sysdata/libraries/render/src/image.hb | 8 ++-- sysdata/libraries/render/src/software.hb | 20 ++++++---- sysdata/libraries/render/src/text.hb | 20 +++++----- sysdata/libraries/stn/src/buffer.hb | 2 +- sysdata/libraries/stn/src/lib.hb | 12 +++++- sysdata/libraries/stn/src/memory.hb | 8 +++- sysdata/libraries/stn/src/panic.hb | 8 ---- sysdata/libraries/stn/src/string.hb | 9 ++--- .../src/bios_parameter_block.hb | 7 +--- sysdata/programs/horizon/src/main.hb | 14 ++++--- sysdata/programs/ps2_driver/src/main.hb | 2 +- .../render_example/src/examples/image.hb | 5 ++- .../render_example/src/examples/text.hb | 37 +++++++++++++------ .../programs/serial_driver_test/src/main.hb | 4 +- sysdata/system_config.toml | 9 ++--- 17 files changed, 111 insertions(+), 86 deletions(-) delete mode 100644 sysdata/libraries/stn/src/panic.hb diff --git a/Cargo.lock b/Cargo.lock index b20adbfc..af66e89e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "autocfg" @@ -97,9 +97,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.31" +version = "1.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" dependencies = [ "shlex", ] @@ -253,12 +253,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#38a00cbaa09434324d209fc5f59480d2b6743fb3" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#38a00cbaa09434324d209fc5f59480d2b6743fb3" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" dependencies = [ "hashbrown 0.15.0", "hbbytecode", @@ -270,7 +270,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#38a00cbaa09434324d209fc5f59480d2b6743fb3" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" dependencies = [ "hbbytecode", ] @@ -550,9 +550,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "log", "once_cell", @@ -606,18 +606,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -677,9 +677,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index c2e2f3d4..1db3411e 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -35,7 +35,7 @@ create_window := fn(channel: int): ^render.Surface { loop if x > 1000 break else x += 1 ret := buffer.recv([u8; 4096], windowing_system_buffer, mem_buf) - if ret == 0 { + if ret == null { log.info("No messages\0") } diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb index a07d862b..249094ad 100644 --- a/sysdata/libraries/render/src/image.hb +++ b/sysdata/libraries/render/src/image.hb @@ -32,11 +32,11 @@ BitmapColorHeader := packed struct { unused: u32, } -surface_from_bmp := fn(bmp: ^u8): Surface { +surface_from_bmp := fn(bmp: ^u8): ?Surface { 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 idk + return null } info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) bmp += file_header.offset @@ -64,11 +64,11 @@ surface_from_bmp := fn(bmp: ^u8): Surface { return .(@bitcast(bmp), info_header.width, info_header.height) } -new_surface_from_bmp := fn(bmp: ^u8): Surface { +new_surface_from_bmp := fn(bmp: ^u8): ?Surface { 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 idk + return null } info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) bmp += file_header.offset diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 3fa79deb..588eee2e 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -3,6 +3,9 @@ .{get_glyph, Font} := text; .{Vec2} := math +// safety: don't use before init() or you will get a memory access violation +framebuffer := memory.dangling(^Color) + Surface := struct { buf: ^Color, width: uint, @@ -36,8 +39,6 @@ free_surface := fn(surface: Surface): void { return @inline(memory.free, Color, surface.buf, @intcast(surface.width * surface.height), false) } -framebuffer := @as(^Color, null) - init := fn(doublebuffer: bool): Surface { framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0") width := dt.get(uint, "framebuffer/fb0/width\0") @@ -80,8 +81,9 @@ put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: rows_to_fill := tr.y loop if rows_to_fill <= 1 break else { - @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) - @inline(memory.set, Color, &color, bottom_start_idx, @bitcast(tr.x)) + // inline is broked + memory.set(Color, &color, top_start_idx, @bitcast(tr.x)) + memory.set(Color, &color, bottom_start_idx, @bitcast(tr.x)) top_start_idx += surface.width bottom_start_idx -= surface.width @@ -272,10 +274,12 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: } glyph_data := @inline(get_glyph, font, @intcast(*current_char)) - if glyph_data == idk { - current_char += 1 - continue - } + // ! cool null check don't disable :dead: + // !! i disabled the cool null check because compiler + // if glyph_data == null { + // current_char += 1 + // continue + // } if cursor.x % surface.width + font.width >= surface.width { cursor.x = pos.x diff --git a/sysdata/libraries/render/src/text.hb b/sysdata/libraries/render/src/text.hb index 46896106..37cabf74 100644 --- a/sysdata/libraries/render/src/text.hb +++ b/sysdata/libraries/render/src/text.hb @@ -28,11 +28,11 @@ Font := struct { char_gap: uint, } -font_from_psf1 := fn(psf: ^u8): Font { +font_from_psf1 := fn(psf: ^u8): ?Font { header := @as(^PSF1Header, @bitcast(psf)) if header.magic != 0x436 { log.error("failed to load psf font: not a psf1 font, idiot\0") - return idk + return null } psf += @sizeof(PSF1Header) @@ -40,30 +40,30 @@ font_from_psf1 := fn(psf: ^u8): Font { return .( psf, 8, - @intcast(header.character_size), + header.character_size, 256, - @intcast(header.character_size), + header.character_size, false, 0, 0, ) } -font_from_psf2 := fn(psf: ^u8): Font { +font_from_psf2 := fn(psf: ^u8): ?Font { header := @as(^PSF2Header, @bitcast(psf)) if header.magic != 0x864AB572 { log.error("failed to load psf font: not a psf2 font, idiot\0") - return idk + return null } psf += header.header_size return .( psf, - @intcast(header.width), - @intcast(header.height), - @intcast(header.num_glyph), - @intcast(header.bytes_per_glyph), + header.width, + header.height, + header.num_glyph, + header.bytes_per_glyph, (header.flags & 1) != 0, 0, 0, diff --git a/sysdata/libraries/stn/src/buffer.hb b/sysdata/libraries/stn/src/buffer.hb index d4aa4e7a..f2fad6b5 100644 --- a/sysdata/libraries/stn/src/buffer.hb +++ b/sysdata/libraries/stn/src/buffer.hb @@ -1,6 +1,6 @@ string := @use("string.hb") -recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): ^Expr { +recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): ?^Expr { return @eca(4, buffer_id, memory_map_location, @sizeof(Expr)) } diff --git a/sysdata/libraries/stn/src/lib.hb b/sysdata/libraries/stn/src/lib.hb index 6c67d451..8c6fabc9 100644 --- a/sysdata/libraries/stn/src/lib.hb +++ b/sysdata/libraries/stn/src/lib.hb @@ -6,4 +6,14 @@ buffer := @use("buffer.hb") math := @use("math.hb") random := @use("random.hb") file := @use("file_io.hb") -dt := @use("dt.hb") \ No newline at end of file +dt := @use("dt.hb") + +panic := fn(message: ?^u8): never { + log.error("Error: Panic Called, Message:\0") + if message == null { + log.error("None\0") + } else { + log.error(message) + } + die +} \ No newline at end of file diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index 540d3d78..f133e63b 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -2,6 +2,10 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF MAX_FREE := 0xFF +dangling := fn($Expr: type): Expr { + return @bitcast(@sizeof(Expr)) +} + calc_pages := fn($Expr: type, num: uint): uint { return 1 + @sizeof(Expr) * num / PAGE_SIZE } @@ -14,10 +18,10 @@ alloc := fn($Expr: type, num: uint): ^Expr { ptr := request_page(0xFF) remaining := pages - MAX_ALLOC loop if remaining < MAX_ALLOC break else { - _ := request_page(@intcast(MAX_ALLOC)) + _ = request_page(@intcast(MAX_ALLOC)) remaining -= MAX_ALLOC } - _ := request_page(@intcast(remaining)) + _ = request_page(@intcast(remaining)) return @bitcast(ptr) } diff --git a/sysdata/libraries/stn/src/panic.hb b/sysdata/libraries/stn/src/panic.hb deleted file mode 100644 index eddf15a8..00000000 --- a/sysdata/libraries/stn/src/panic.hb +++ /dev/null @@ -1,8 +0,0 @@ -panic := fn(message: ?^u8): void { - if message != null { - log.error(message) - } - // TODO: replace with die keyword when it gets pushed - loop { - } -} \ No newline at end of file diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 3307199b..5551b5cb 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -1,8 +1,7 @@ + length := fn(ptr: ^u8): uint { - len := @as(uint, 0) - // loop if *(ptr + len) == 0 return len else len += 1 - loop if *(ptr + len) == 0 break else len += 1 - return len + len := 0 + loop if *(ptr + len) == 0 return len else len += 1 } display_int := fn(num: int, p: ^u8, radix: uint): ^u8 { @@ -58,7 +57,7 @@ display_int := fn(num: int, p: ^u8, radix: uint): ^u8 { } reverse := fn(s: ^u8): void { - i := @as(uint, 0) + i := 0 j := @inline(length, s) - 1 temp := @as(u8, 0) loop if i >= j break else { diff --git a/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb b/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb index 8517c5bd..d6774c1a 100644 --- a/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb +++ b/sysdata/programs/filesystem_fat32/src/bios_parameter_block.hb @@ -9,8 +9,7 @@ OemIdent := struct { } new_oem_ident := fn(major: int, minor: int): OemIdent { - ver := [u8].(0, 0, 0, 0, 0, 0, 0, 0) - return OemIdent.(ver, ver) + return .(.(0, 0, 0, 0, 0, 0, 0, 0), .(0, 0, 0, 0, 0, 0, 0, 0)) } BiosParameterBlock := struct { @@ -38,9 +37,7 @@ bpb_sanity_check := fn(bpb: BiosParameterBlock): int { } new_bpb := fn(): BiosParameterBlock { - oem := new_oem_ident(0, 0) - - return BiosParameterBlock.(VALID_JUMP_BYTES, oem, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + return .(VALID_JUMP_BYTES, new_oem_ident(0, 0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) } sector_count := fn(bpb: BiosParameterBlock): u32 { diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index f8af80b4..2ab0368c 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -36,7 +36,8 @@ main := fn(): int { vel_inner := Vec2(int).(1, 1) pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) str := "Window Title Bar\0" - font := render.text.font_from_psf2(@bitcast(&psf)) + // really we should null check but it is a bit broked + font := @unwrap(render.text.font_from_psf2(@bitcast(&psf))) loop { // Clear the screen @@ -45,11 +46,12 @@ main := fn(): int { // TODO: Read the window buffer here { ret := buffer.recv([u8; 4096], win_buff, mem_buf) - if ret == 0 { - log.info("No messages\0") - } else { - log.info("Handle Messages\0") - } + // for some reason this null check causes the compiler to spin forever + // if ret == null { + // log.info("No messages\0") + // } else { + // log.info("Handle Messages\0") + // } } if pos_inner.x == 0 | pos_inner.x == window.width - side { diff --git a/sysdata/programs/ps2_driver/src/main.hb b/sysdata/programs/ps2_driver/src/main.hb index 7256181b..150d3b06 100644 --- a/sysdata/programs/ps2_driver/src/main.hb +++ b/sysdata/programs/ps2_driver/src/main.hb @@ -7,7 +7,7 @@ send_byte := fn(byte: u8): u8 { main := fn(): int { buf := buffer.create("XKeyboard\0") - _ := send_byte(238) + _ = send_byte(238) log.info("PS/2 Driver Loaded\0") if send_byte(238) == 238 { log.info("PS/2 Keyboard Echoed\0") diff --git a/sysdata/programs/render_example/src/examples/image.hb b/sysdata/programs/render_example/src/examples/image.hb index 25d68404..49dafd06 100644 --- a/sysdata/programs/render_example/src/examples/image.hb +++ b/sysdata/programs/render_example/src/examples/image.hb @@ -8,9 +8,10 @@ bmp_1 := @embed("./assets/able.bmp") bmp_2 := @embed("./assets/mini.bmp") example := fn(): void { + // strictly we should be null checking here but i am lazy images := [render.Surface].( - render.image.surface_from_bmp(@bitcast(&bmp_1)), - render.image.surface_from_bmp(@bitcast(&bmp_2)), + @unwrap(render.image.surface_from_bmp(@bitcast(&bmp_1))), + @unwrap(render.image.surface_from_bmp(@bitcast(&bmp_2))), ) screen := render.init(true) vel := Vec2(int).(1, 1) diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 11086678..92c66f50 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -1,22 +1,37 @@ -.{Vec2, sin, cos} := @use("../../../../libraries/stn/src/lib.hb").math render := @use("../../../../libraries/render/src/lib.hb") /* expected result: - words */ + extended unicode test */ psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") example := fn(): void { - screen := render.init(true) + screen := render.init(false) font := render.text.font_from_psf2(@bitcast(&psf)) - t := 0 - str := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX\0" - loop { - render.clear(screen, render.black) - render.put_text(screen, font, .(t, t % screen.height), render.red, str) - render.sync(screen) - // t += 1 - } + str := "Extended unicode test: + +ABCDEFGHIJKLMNOPQRSTUVWXYZ +abcdefghijklmnopqrstuvwxyz +0123456789 +!\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ +ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞß +àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ +ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ +ĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ +„‟†‡•‣․‥…‧ +₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿ +∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟ +─│┌┐└┘├┤┬┴┼╋╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿ +▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟ +■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○ +←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻ + 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟 +✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟ +✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿\0" + + render.clear(screen, render.black) + render.put_text(screen, @unwrap(font), .(0, 0), render.white, str) + render.sync(screen) return } \ No newline at end of file diff --git a/sysdata/programs/serial_driver_test/src/main.hb b/sysdata/programs/serial_driver_test/src/main.hb index d3011c33..19ad6c15 100644 --- a/sysdata/programs/serial_driver_test/src/main.hb +++ b/sysdata/programs/serial_driver_test/src/main.hb @@ -5,7 +5,9 @@ log_info := fn(): void { if a == 0 { } else { msg := "XABC\0" - msg_length := @inline(string.length, msg) + // inline is broked + // msg_length := @inline(string.length, msg) + msg_length := 5 @as(void, @eca(3, a, msg, msg_length)) } diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 09fe797d..ada45086 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ resolution = "1024x768x24" # [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" @@ -58,6 +58,5 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" - -[boot.limine.ableos.modules.mouse_driver] -path = "boot:///mouse_driver.hbf" +# [boot.limine.ableos.modules.mouse_driver] +# path = "boot:///mouse_driver.hbf" From f4ad4b685692a5281b64de102a26dbd552ec8e6a Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 3 Nov 2024 16:48:36 -0600 Subject: [PATCH 30/66] mouse driver --- sysdata/programs/app_bar/README.md | 2 + .../{mouse_driver => app_bar}/meta.toml | 4 +- sysdata/programs/app_bar/src/main.hb | 35 +++++ sysdata/programs/mouse_driver/README.md | 1 - sysdata/programs/mouse_driver/src/main.hb | 79 ---------- sysdata/programs/ps2_mouse_driver/README.md | 2 + sysdata/programs/ps2_mouse_driver/meta.toml | 11 ++ sysdata/programs/ps2_mouse_driver/src/main.hb | 144 ++++++++++++++++++ sysdata/system_config.toml | 11 +- 9 files changed, 203 insertions(+), 86 deletions(-) create mode 100644 sysdata/programs/app_bar/README.md rename sysdata/programs/{mouse_driver => app_bar}/meta.toml (76%) create mode 100644 sysdata/programs/app_bar/src/main.hb delete mode 100644 sysdata/programs/mouse_driver/README.md delete mode 100644 sysdata/programs/mouse_driver/src/main.hb create mode 100644 sysdata/programs/ps2_mouse_driver/README.md create mode 100644 sysdata/programs/ps2_mouse_driver/meta.toml create mode 100644 sysdata/programs/ps2_mouse_driver/src/main.hb diff --git a/sysdata/programs/app_bar/README.md b/sysdata/programs/app_bar/README.md new file mode 100644 index 00000000..78d8cc61 --- /dev/null +++ b/sysdata/programs/app_bar/README.md @@ -0,0 +1,2 @@ +# app bar +The app bar is a mini-bar meant to launch applications. \ No newline at end of file diff --git a/sysdata/programs/mouse_driver/meta.toml b/sysdata/programs/app_bar/meta.toml similarity index 76% rename from sysdata/programs/mouse_driver/meta.toml rename to sysdata/programs/app_bar/meta.toml index 21bdba85..80e868e4 100644 --- a/sysdata/programs/mouse_driver/meta.toml +++ b/sysdata/programs/app_bar/meta.toml @@ -1,6 +1,6 @@ [package] -name = "mouse_driver" -authors = [""] +name = "app_bar" +authors = ["able"] [dependants.libraries] diff --git a/sysdata/programs/app_bar/src/main.hb b/sysdata/programs/app_bar/src/main.hb new file mode 100644 index 00000000..c855a4d3 --- /dev/null +++ b/sysdata/programs/app_bar/src/main.hb @@ -0,0 +1,35 @@ +stn := @use("../../../libraries/stn/src/lib.hb"); +.{string, memory, buffer, random, log} := stn; +.{Vec2} := stn.math + +horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") + +render := @use("../../../libraries/render/src/lib.hb") + +psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") + +main := fn(): int { + screen := render.init(true) + + // Clear the screen to black. + // render.clear(screen, render.black) + + x := 0 + + mem_buf := memory.request_page(1) + color := random.any(render.Color) + + str := "Window Title Bar\0" + font := render.text.font_from_psf2(@bitcast(&psf)) + + loop { + // Clear the screen + // render.clear(screen, render.black) + + render.put_rect(screen, .(0, 0), .(screen.width - 1, 26), render.white) + // Sync the screen + render.sync(screen) + } + + return 0 +} \ No newline at end of file diff --git a/sysdata/programs/mouse_driver/README.md b/sysdata/programs/mouse_driver/README.md deleted file mode 100644 index b951afac..00000000 --- a/sysdata/programs/mouse_driver/README.md +++ /dev/null @@ -1 +0,0 @@ -# mouse_driver \ No newline at end of file diff --git a/sysdata/programs/mouse_driver/src/main.hb b/sysdata/programs/mouse_driver/src/main.hb deleted file mode 100644 index f1c24f31..00000000 --- a/sysdata/programs/mouse_driver/src/main.hb +++ /dev/null @@ -1,79 +0,0 @@ -.{memory, buffer, log, string} := @use("../../../libraries/stn/src/lib.hb") -render := @use("../../../libraries/render/src/lib.hb") - -ACK := 250 - -wait_for := fn(for: u8): void { - log.info("Start waiting\0") - loop { - if (memory.inb(0x64) & 2 >> for) == for { - log.info("End waiting\0") - return - } - } -} - -send_info := fn(info: u8): void { - wait_for(1) - memory.outb(0x64, info) -} - -send_command := fn(command: u8): void { - send_info(0xD4) - wait_for(1) - memory.outb(0x60, command) -} - -get_response := fn(): u8 { - wait_for(1) - return memory.inb(0x60) -} - -main := fn(): int { - // screen := render.init(true) - - format_page := memory.alloc(u8, 1024) - - wait_for(0) - memory.outb(0x64, 0xA8) - log.info("Aux mouse device enabled.\0") - - send_command(0xF6) - a := get_response() - - send_command(0xF4) - b := get_response() - - x := -0 - y := -0 - - loop { - // render.clear(screen, render.black) - - loop { - if (memory.inb(0x64) & 0x20) == 0x20 { - log.info("Yeah\0") - - break - } else { - } - } - - status := memory.inb(0x60) - log.info(string.display_int(status, format_page, 10)) - d_x := memory.inb(0x60) - log.info(string.display_int(d_x, format_page, 10)) - x = x + d_x - d_y := memory.inb(0x60) - y = y + d_y - log.info(string.display_int(d_y, format_page, 10)) - // render.put_rect(screen, .(x, y), .(x + 10, y + 10), render.white) - - log.info("XY\0") - - log.info(string.display_int(x, format_page, 10)) - log.info(string.display_int(y, format_page, 10)) - } - - return 0 -} \ No newline at end of file diff --git a/sysdata/programs/ps2_mouse_driver/README.md b/sysdata/programs/ps2_mouse_driver/README.md new file mode 100644 index 00000000..4c17847b --- /dev/null +++ b/sysdata/programs/ps2_mouse_driver/README.md @@ -0,0 +1,2 @@ +# ps2_mouse_driver +A small PS/2 mouse driver. This driver pushes changes to the input service in ableOS. \ No newline at end of file diff --git a/sysdata/programs/ps2_mouse_driver/meta.toml b/sysdata/programs/ps2_mouse_driver/meta.toml new file mode 100644 index 00000000..e3bfc9a3 --- /dev/null +++ b/sysdata/programs/ps2_mouse_driver/meta.toml @@ -0,0 +1,11 @@ +[package] +name = "ps2_mouse_driver" +authors = ["able", "peony"] + +[dependants.libraries] + +[dependants.binaries] +hblang.version = "1.0.0" + +[build] +command = "hblang src/main.hb" diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb new file mode 100644 index 00000000..0b70519f --- /dev/null +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -0,0 +1,144 @@ +.{memory, buffer, log, string, math} := @use("../../../libraries/stn/src/lib.hb") +Vec2 := math.Vec2 + +i9 := packed struct {sign: bool, value: u8} +Button := struct {id: u8} +LeftButton := Button.(1) +RightButton := Button.(2) +MiddleButton := Button.(4) +Button4 := Button.(8) +Button5 := Button.(16) + +mouse_moved := fn(delta: Vec2(i9)): void { + log.info("Mouse movement.\0") +} +button_event := fn(button: Button, pressed: bool): void { + if pressed { + log.info("Mouse-button pressed.\0") + } else { + log.info("Mouse-button released.\0") + } +} + +send_byte := fn(target: u8, data: u8): void { + loop if (memory.inb(0x64) & 2) == 0 break + memory.outb(target, data) +} + +reset_mouse := fn(): void { + @inline(send_byte, 0x64, 0xD4) + @inline(send_byte, 0x60, 0xFF) + loop if memory.inb(0x60) == 0xAA { + log.info("Self check passed.\0") + return + } +} + +send_command_byte := fn(byte: u8): void { + @inline(send_byte, 0x64, 0xD4) + @inline(send_byte, 0x60, byte) + loop if memory.inb(0x60) == 0xFA { + log.info("ACK\0") + return + } +} + +set_defaults := fn(): void @inline(send_command_byte, 0xF6) +disable_streaming := fn(): void @inline(send_command_byte, 0xF5) +enable_streaming := fn(): void @inline(send_command_byte, 0xF4) + +set_remote_mode := fn(): void @inline(send_command_byte, 0xF0) +set_warp_mode := fn(): void @inline(send_command_byte, 0xEE) +reset_warp_mode := fn(): void @inline(send_command_byte, 0xEC) +set_stream_mode := fn(): void @inline(send_command_byte, 0xEA) + +set_non_linear_scaling := fn(): void @inline(send_command_byte, 0xE7) +set_linear_scaling := fn(): void @inline(send_command_byte, 0xE6) + +resend_packet := fn(): void @inline(send_command_byte, 0xFE) + +SampleRate := struct {value: u8} +sr10 := SampleRate.(10) +sr20 := SampleRate.(20) +sr40 := SampleRate.(40) +sr60 := SampleRate.(60) +sr80 := SampleRate.(80) +sr100 := SampleRate.(100) +sr200 := SampleRate.(200) + +set_sample_rate := fn(sample_rate: SampleRate): void { + @inline(send_command_byte, 0xE6) + @inline(send_command_byte, sample_rate.value) +} + +Resolution := struct {value: u8} +res_1count_per_mm := Resolution.(0) +res_2count_per_mm := Resolution.(1) +res_4count_per_mm := Resolution.(2) +res_8count_per_mm := Resolution.(3) + +set_resolution := fn(resolution: Resolution): void { + @inline(send_command_byte, 0xE6) + @inline(send_command_byte, resolution.value) +} + +button_states := @as(u8, 0) + +main := fn(): int { + format_page := memory.alloc(u8, 1024) + + send_byte(0x64, 0xA8) + log.info("Aux mouse device enabled.\0") + + reset_mouse() + set_defaults() + enable_streaming() + + x := @as(i16, 0) + y := @as(i16, 0) + + loop { + loop if (memory.inb(0x64) & 0x20) == 0x20 break + + status := memory.inb(0x60) + if status == 0xAA { + loop if memory.inb(0x60) == 0 break + log.info("Mouse plugged in!\0") + reset_mouse() + set_defaults() + enable_streaming() + continue + } + + changes := button_states ^ status & 7 + + if (changes & LeftButton.id) != 0 { + button_event(LeftButton, (status & LeftButton.id) != 0) + } + if (changes & RightButton.id) != 0 { + button_event(RightButton, (status & RightButton.id) != 0) + } + if (changes & MiddleButton.id) != 0 { + button_event(MiddleButton, (status & MiddleButton.id) != 0) + } + + button_states ^= changes + + log.info(string.display_int(status, format_page, 10)) + + dx := i9.(false, 0) + dy := i9.(false, 0) + + dx.value = memory.inb(0x60) + dx.sign = (status & 0x10) > 0 + + dy.value = memory.inb(0x60) ^ 0xFF + dy.sign = (status & 0x20) == 0 + + if dy.value != 0 & dx.value != 0 { + mouse_moved(.(dx, dy)) + } + } + + return 0 +} \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index ada45086..8f8b1791 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ resolution = "1024x768x24" # [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" @@ -58,5 +58,8 @@ path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -# [boot.limine.ableos.modules.mouse_driver] -# path = "boot:///mouse_driver.hbf" +[boot.limine.ableos.modules.ps2_mouse_driver] +path = "boot:///ps2_mouse_driver.hbf" + +# [boot.limine.ableos.modules.app_bar] +# path = "boot:///app_bar.hbf" From 941eed0ac92e9d553c7028344045049a7add1b5e Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 3 Nov 2024 18:34:48 -0600 Subject: [PATCH 31/66] patch --- sysdata/programs/app_bar/src/main.hb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysdata/programs/app_bar/src/main.hb b/sysdata/programs/app_bar/src/main.hb index c855a4d3..68f601d1 100644 --- a/sysdata/programs/app_bar/src/main.hb +++ b/sysdata/programs/app_bar/src/main.hb @@ -12,7 +12,7 @@ main := fn(): int { screen := render.init(true) // Clear the screen to black. - // render.clear(screen, render.black) + render.clear(screen, render.black) x := 0 @@ -24,7 +24,7 @@ main := fn(): int { loop { // Clear the screen - // render.clear(screen, render.black) + render.clear(screen, render.black) render.put_rect(screen, .(0, 0), .(screen.width - 1, 26), render.white) // Sync the screen From 16135ae536a513f2219a57c91de8a79b2b1906da Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 3 Nov 2024 18:37:39 -0600 Subject: [PATCH 32/66] mouse render --- sysdata/programs/ps2_mouse_driver/src/main.hb | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 0b70519f..98b360a2 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -1,6 +1,8 @@ .{memory, buffer, log, string, math} := @use("../../../libraries/stn/src/lib.hb") Vec2 := math.Vec2 +render := @use("../../../libraries/render/src/lib.hb") + i9 := packed struct {sign: bool, value: u8} Button := struct {id: u8} LeftButton := Button.(1) @@ -82,20 +84,31 @@ set_resolution := fn(resolution: Resolution): void { @inline(send_command_byte, resolution.value) } +set_up_mouse := fn(): void { + @inline(reset_mouse) + @inline(set_resolution, res_8count_per_mm) + @inline(enable_streaming) +} + button_states := @as(u8, 0) main := fn(): int { format_page := memory.alloc(u8, 1024) + screen := render.init(true) + + // Clear the screen to black. + render.clear(screen, render.black) + send_byte(0x64, 0xA8) log.info("Aux mouse device enabled.\0") - reset_mouse() - set_defaults() - enable_streaming() + set_up_mouse() - x := @as(i16, 0) - y := @as(i16, 0) + set_resolution(res_8count_per_mm) + + x := @as(u8, 0) + y := @as(u8, 0) loop { loop if (memory.inb(0x64) & 0x20) == 0x20 break @@ -104,9 +117,7 @@ main := fn(): int { if status == 0xAA { loop if memory.inb(0x60) == 0 break log.info("Mouse plugged in!\0") - reset_mouse() - set_defaults() - enable_streaming() + set_up_mouse() continue } @@ -132,12 +143,20 @@ main := fn(): int { dx.value = memory.inb(0x60) dx.sign = (status & 0x10) > 0 - dy.value = memory.inb(0x60) ^ 0xFF + dy.value = -memory.inb(0x60) dy.sign = (status & 0x20) == 0 if dy.value != 0 & dx.value != 0 { mouse_moved(.(dx, dy)) + x += dx.value + y += dy.value } + + render.clear(screen, render.black) + + render.put_rect(screen, .(x, y), .(10, 10), render.white) + // Sync the screen + render.sync(screen) } return 0 From b795215b62cf4e5212b4e5fb4dfd0b5ff8b2d2f5 Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 3 Nov 2024 19:38:40 -0600 Subject: [PATCH 33/66] enable horizon and ps2 driver --- sysdata/libraries/intouch/src/events.hb | 22 ++++++++ sysdata/libraries/intouch/src/lib.hb | 29 ++++------ sysdata/programs/horizon/src/main.hb | 54 ++++++++++++------- sysdata/programs/ps2_mouse_driver/src/main.hb | 15 +----- sysdata/system_config.toml | 4 +- 5 files changed, 69 insertions(+), 55 deletions(-) create mode 100644 sysdata/libraries/intouch/src/events.hb diff --git a/sysdata/libraries/intouch/src/events.hb b/sysdata/libraries/intouch/src/events.hb new file mode 100644 index 00000000..1d544238 --- /dev/null +++ b/sysdata/libraries/intouch/src/events.hb @@ -0,0 +1,22 @@ +keycodes := @use("keycodes.hb"); +.{KeyCode} := keycodes + +KeyEvent := struct { + // 0 if down + // 1 if up + up: u8, + // 0 if not just triggered + // 1 if just triggered + just_triggered: u8, + key: KeyCode, +} + +MouseEvent := struct { + x_change: u8, + y_change: u8, + left: u8, + middle: u8, + right: u8, +} + +GamepadEvent := struct {} \ No newline at end of file diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index 86175a6c..2cfde555 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -1,22 +1,13 @@ -keycodes := @use("keycodes.hb"); -.{KeyCode} := keycodes +stn := @use("../../stn/src/lib.hb"); +.{log, buffer} := stn +events := @use("events.hb"); +.{KeyEvent, MouseEvent} := events -MouseEvent := struct { - x_change: u8, - y_change: u8, - left: u8, - middle: u8, - right: u8, +recieve_key_event := fn(): ?KeyEvent { + log.info("hi\0") + return null } -KeyEvent := struct { - // 0 if down - // 1 if up - up: u8, - // 0 if not just triggered - // 1 if just triggered - just_triggered: u8, - key: KeyCode, -} - -GamepadEvent := struct {} \ No newline at end of file +recieve_mouse_event := fn(): ?MouseEvent { + return null +} \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 2ab0368c..be9f9248 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -5,6 +5,7 @@ stn := @use("../../../libraries/stn/src/lib.hb"); horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") render := @use("../../../libraries/render/src/lib.hb") +intouch := @use("../../../libraries/intouch/src/lib.hb") Window := struct { // TODO: Replace this with widgets @@ -39,6 +40,9 @@ main := fn(): int { // really we should null check but it is a bit broked font := @unwrap(render.text.font_from_psf2(@bitcast(&psf))) + mouse_x := 0 + mouse_y := 0 + loop { // Clear the screen render.clear(screen, render.black) @@ -54,6 +58,16 @@ main := fn(): int { // } } + { + // get input events from drivers via intouch + key_event := intouch.recieve_key_event() + mouse_event := intouch.recieve_mouse_event() + + // render mouse + render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) + // Send events to focused window + } + if pos_inner.x == 0 | pos_inner.x == window.width - side { vel_inner.x = -vel_inner.x color = random.any(render.Color) @@ -65,29 +79,29 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through window_count := 0 - loop { - render.clear(window, render.black) - render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + // loop { + // render.clear(window, render.black) - // Draw the decorators - { - render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) - render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) - render.put_text(window, font, .(window.width / 2, 1), render.white, str) - } - render.put_filled_rect(window, pos_inner, .(side, side), color) + // // Draw the decorators + // { + // render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) + // render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) + // render.put_text(window, font, .(window.width / 2, 1), render.white, str) + // } + // render.put_filled_rect(window, pos_inner, .(side, side), color) - // Apply the image to the screen - pos := Vec2(uint).(x, 100) + // // Apply the image to the screen + // pos := Vec2(uint).(x, 100) - render.put_surface(screen, window, pos, false) - if window_count >= 1 { - x = 0 - break - } - window_count += 1 - x += screen.width / 2 - } + // render.put_surface(screen, window, pos, false) + // if window_count >= 1 { + // x = 0 + // break + // } + // window_count += 1 + // x += screen.width / 2 + // } pos_inner += @bitcast(vel_inner) // Sync the screen diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 98b360a2..62920d1c 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -1,8 +1,6 @@ .{memory, buffer, log, string, math} := @use("../../../libraries/stn/src/lib.hb") Vec2 := math.Vec2 -render := @use("../../../libraries/render/src/lib.hb") - i9 := packed struct {sign: bool, value: u8} Button := struct {id: u8} LeftButton := Button.(1) @@ -95,10 +93,7 @@ button_states := @as(u8, 0) main := fn(): int { format_page := memory.alloc(u8, 1024) - screen := render.init(true) - - // Clear the screen to black. - render.clear(screen, render.black) + mouse_buffer := buffer.create("Mouse\0") send_byte(0x64, 0xA8) log.info("Aux mouse device enabled.\0") @@ -148,15 +143,7 @@ main := fn(): int { if dy.value != 0 & dx.value != 0 { mouse_moved(.(dx, dy)) - x += dx.value - y += dy.value } - - render.clear(screen, render.black) - - render.put_rect(screen, .(x, y), .(10, 10), render.white) - // Sync the screen - render.sync(screen) } return 0 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 8f8b1791..e4000d46 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ resolution = "1024x768x24" # [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" From 086948d47ac64062ef15dafb728d2ac3c3d016da Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 3 Nov 2024 19:53:09 -0600 Subject: [PATCH 34/66] rename --- sysdata/libraries/intouch/src/lib.hb | 4 +++- .../programs/{ps2_driver => ps2_keyboard_driver}/README.md | 0 .../programs/{ps2_driver => ps2_keyboard_driver}/meta.toml | 2 +- .../programs/{ps2_driver => ps2_keyboard_driver}/src/main.hb | 0 sysdata/system_config.toml | 4 ++-- 5 files changed, 6 insertions(+), 4 deletions(-) rename sysdata/programs/{ps2_driver => ps2_keyboard_driver}/README.md (100%) rename sysdata/programs/{ps2_driver => ps2_keyboard_driver}/meta.toml (83%) rename sysdata/programs/{ps2_driver => ps2_keyboard_driver}/src/main.hb (100%) diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index 2cfde555..e592a7b1 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -4,7 +4,9 @@ events := @use("events.hb"); .{KeyEvent, MouseEvent} := events recieve_key_event := fn(): ?KeyEvent { - log.info("hi\0") + buf := buffer.search("PS/2 Keyboard\0") + // Read out of the keyboard buffer here + return null } diff --git a/sysdata/programs/ps2_driver/README.md b/sysdata/programs/ps2_keyboard_driver/README.md similarity index 100% rename from sysdata/programs/ps2_driver/README.md rename to sysdata/programs/ps2_keyboard_driver/README.md diff --git a/sysdata/programs/ps2_driver/meta.toml b/sysdata/programs/ps2_keyboard_driver/meta.toml similarity index 83% rename from sysdata/programs/ps2_driver/meta.toml rename to sysdata/programs/ps2_keyboard_driver/meta.toml index 0a2d770c..804602bd 100644 --- a/sysdata/programs/ps2_driver/meta.toml +++ b/sysdata/programs/ps2_keyboard_driver/meta.toml @@ -1,5 +1,5 @@ [package] -name = "ps2_driver" +name = "ps2_keyboard_driver" authors = ["Talha Qamar"] [dependants.libraries] diff --git a/sysdata/programs/ps2_driver/src/main.hb b/sysdata/programs/ps2_keyboard_driver/src/main.hb similarity index 100% rename from sysdata/programs/ps2_driver/src/main.hb rename to sysdata/programs/ps2_keyboard_driver/src/main.hb diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index e4000d46..0491a37a 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -49,8 +49,8 @@ path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.svga_driver] # path = "boot:///svga_driver.hbf" -# [boot.limine.ableos.modules.ps2_driver] -# path = "boot:///ps2_driver.hbf" +[boot.limine.ableos.modules.ps2_keyboard_driver] +path = "boot:///ps2_keyboard_driver.hbf" # [boot.limine.ableos.modules.filesystem_fat32] # path = "boot:///filesystem_fat32.hbf" From 664334fd001faa1494f73d6924423b7c90425c63 Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 6 Nov 2024 01:47:22 +0000 Subject: [PATCH 35/66] unicode stuff for psf rendering make silly keyboard app --- Cargo.lock | 312 +++++++++++++++--- sysdata/libraries/render/src/software.hb | 96 +++++- sysdata/libraries/render/src/text.hb | 103 +++++- sysdata/libraries/stn/src/buffer.hb | 2 +- sysdata/libraries/stn/src/string.hb | 1 - .../render_example/src/examples/text.hb | 176 ++++++++-- sysdata/system_config.toml | 8 +- 7 files changed, 603 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af66e89e..680a1a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.34" +version = "1.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" dependencies = [ "shlex", ] @@ -173,6 +173,17 @@ dependencies = [ "logos", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -241,9 +252,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ "allocator-api2", "equivalent", @@ -253,14 +264,14 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" dependencies = [ - "hashbrown 0.15.0", + "hashbrown 0.15.1", "hbbytecode", "hbvm", "log", @@ -270,7 +281,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#e8f1d2af8ca7520bbb695dcaf1029a348f29592e" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" dependencies = [ "hbbytecode", ] @@ -299,13 +310,142 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -315,7 +455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] @@ -334,7 +474,7 @@ dependencies = [ "aarch64-cpu", "crossbeam-queue", "derive_more", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "hbvm", "limine", "log", @@ -366,6 +506,12 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02034f8f6b3e7bf050f310fbaf6db0018b8e54b75598d0a4c97172054752fede" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -663,6 +809,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "str-reader" version = "0.1.2" @@ -687,19 +839,25 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "tinyvec_macros", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] [[package]] name = "tock-registers" @@ -752,27 +910,12 @@ dependencies = [ "x86", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" @@ -802,15 +945,27 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "versioning" version = "0.1.3" @@ -986,6 +1141,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x2apic" version = "0.4.3" @@ -1042,8 +1209,75 @@ dependencies = [ "serde", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 588eee2e..8f158430 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -1,6 +1,6 @@ .{math, memory, dt} := @use("../../stn/src/lib.hb"); .{Color, text} := @use("lib.hb"); -.{get_glyph, Font} := text; +.{get_glyph, get_glyph_unicode, Font, UNC_TABLE_SIZE} := text; .{Vec2} := math // safety: don't use before init() or you will get a memory access violation @@ -266,29 +266,90 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: current_char := str loop if *current_char == 0 break else { - if *current_char == 10 { - cursor.x = pos.x - cursor.y += font.height + font.line_gap + glyph_data := memory.dangling(^u8) + + if font.unicode != null { + code_point := @as(uint, 0) + first_byte := *current_char + num_bytes := 1 + + if (first_byte & 0x80) == 0 { + code_point = first_byte + } else if (first_byte & 0xE0) == 0xC0 { + num_bytes = 2 + code_point = first_byte & 0x1F + } else if (first_byte & 0xF0) == 0xE0 { + num_bytes = 3 + code_point = first_byte & 0xF + } else if (first_byte & 0xF8) == 0xF0 { + // handle later + current_char += 1 + continue + } else { + current_char += 1 + continue + } + + valid_sequence := true + i := 1 + loop if i >= num_bytes break else { + current_char += 1 + // have to check twice due to odd compiler bug...? + if *current_char == 0 | (*current_char & 0xC0) != 0x80 { + valid_sequence = false + } + if valid_sequence == false { + break + } + code_point = code_point << 6 | *current_char & 0x3F + i += 1 + } + + if valid_sequence == false { + current_char += 1 + continue + } + + current_char += 1 + + if code_point == 10 { + cursor.x = pos.x + cursor.y += font.height + font.line_gap + continue + } + + if code_point < UNC_TABLE_SIZE { + glyph_index := *(font.unicode + code_point) + + if glyph_index == 0xFFFF { + continue + } + glyph_data = font.data + glyph_index * font.bytes_per_glyph + } else { + continue + } + } else { + if *current_char > font.num_glyphs { + continue + } + glyph_data = @inline(get_glyph, font, *current_char) + if *current_char == 10 { + cursor.x = pos.x + cursor.y += font.height + font.line_gap + current_char += 1 + continue + } current_char += 1 - continue } - glyph_data := @inline(get_glyph, font, @intcast(*current_char)) - // ! cool null check don't disable :dead: - // !! i disabled the cool null check because compiler - // if glyph_data == null { - // current_char += 1 - // continue - // } + if cursor.y + font.height > surface.height break if cursor.x % surface.width + font.width >= surface.width { cursor.x = pos.x cursor.y += font.height + font.line_gap } - if cursor.y + font.height > surface.height break dest := @inline(indexptr, surface, cursor.x, cursor.y) - src := glyph_data rows_remaining := font.height loop if rows_remaining == 0 break else { @@ -303,7 +364,7 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: } pixel_dest += 1 mask >>= 1 - if mask == 0 { + if mask == 0 & bits_remaining > 0 { glyph_data += 1 byte = *glyph_data mask = 0x80 @@ -311,13 +372,14 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: bits_remaining -= 1 } - glyph_data += 1 + if mask != 0x80 { + glyph_data += 1 + } dest += surface.width rows_remaining -= 1 } cursor.x += font.width + font.char_gap - current_char += 1 } return diff --git a/sysdata/libraries/render/src/text.hb b/sysdata/libraries/render/src/text.hb index 37cabf74..5a70a072 100644 --- a/sysdata/libraries/render/src/text.hb +++ b/sysdata/libraries/render/src/text.hb @@ -1,4 +1,4 @@ -.{log} := @use("../../stn/src/lib.hb") +.{log, memory} := @use("../../stn/src/lib.hb") PSF1Header := packed struct { magic: u16, @@ -23,9 +23,9 @@ Font := struct { height: uint, num_glyphs: uint, bytes_per_glyph: uint, - has_unicode_table: bool, line_gap: uint, char_gap: uint, + unicode: ?^u16, } font_from_psf1 := fn(psf: ^u8): ?Font { @@ -43,13 +43,13 @@ font_from_psf1 := fn(psf: ^u8): ?Font { header.character_size, 256, header.character_size, - false, 0, 0, + null, ) } -font_from_psf2 := fn(psf: ^u8): ?Font { +font_from_psf2 := fn(psf: ^u8, unicode: bool): ?Font { header := @as(^PSF2Header, @bitcast(psf)) if header.magic != 0x864AB572 { log.error("failed to load psf font: not a psf2 font, idiot\0") @@ -58,18 +58,105 @@ font_from_psf2 := fn(psf: ^u8): ?Font { psf += header.header_size - return .( + font := Font.( psf, header.width, header.height, header.num_glyph, header.bytes_per_glyph, - (header.flags & 1) != 0, 0, 0, + null, ) + if (header.flags & 1) != 0 & unicode { + init_unicode(&font) + } + return font } -get_glyph := fn(font: Font, index: uint): ^u8 { - return font.data + index * font.bytes_per_glyph +get_glyph := fn(font: Font, index: u8): ^u8 { + return font.data + @as(uint, index) * font.bytes_per_glyph +} + +UNC_TABLE_SIZE := 1 << 16 + +init_unicode := fn(font: ^Font): void { + font.unicode = memory.alloc(u16, UNC_TABLE_SIZE) + + @inline(memory.set, u16, &0xFFFF, font.unicode, UNC_TABLE_SIZE) + + table := font.data + font.num_glyphs * font.bytes_per_glyph + curr_glyph := @as(u16, 0) + + loop if curr_glyph >= font.num_glyphs break else { + loop { + byte := *table + table += 1 + + if byte == 0xFF break + if byte == 0xFE { + continue + } + + unicode := @as(uint, 0) + bytes_to_read := @as(uint, 1) + + if (byte & 0x80) == 0 { + unicode = byte + } else if (byte & 0xE0) == 0xC0 { + unicode = byte & 0x1F + bytes_to_read = 2 + } else if (byte & 0xF0) == 0xE0 { + unicode = byte & 0xF + bytes_to_read = 3 + } else if (byte & 0xF8) == 0xF0 { + unicode = byte & 0x7 + bytes_to_read = 4 + } else { + continue + } + + valid := true + loop if bytes_to_read <= 1 break else { + next_byte := *table + if (next_byte & 0xC0) != 0x80 { + valid = false + } + if valid == false { + break + } + unicode = unicode << 6 | next_byte & 0x3F + table += 1 + bytes_to_read -= 1 + } + + if valid == false continue + + if bytes_to_read == 4 { + if unicode < 0x10000 | unicode > 0x10FFFF continue + + if unicode <= 0xFFFF { + if unicode < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + unicode) = curr_glyph + } + } else { + unicode -= 0x10000 + high_surrogate := 0xD800 | unicode >> 10 & 0x3FF + low_surrogate := 0xDC00 | unicode & 0x3FF + + if high_surrogate < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + high_surrogate) = curr_glyph + } + if low_surrogate < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + low_surrogate) = curr_glyph + } + } + } else { + if unicode < UNC_TABLE_SIZE { + *(@unwrap(font.unicode) + unicode) = curr_glyph + } + } + } + curr_glyph += 1 + } } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/buffer.hb b/sysdata/libraries/stn/src/buffer.hb index f2fad6b5..680aec89 100644 --- a/sysdata/libraries/stn/src/buffer.hb +++ b/sysdata/libraries/stn/src/buffer.hb @@ -1,6 +1,6 @@ string := @use("string.hb") -recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): ?^Expr { +recv := fn($Expr: type, buffer_id: int, memory_map_location: ^u8): void { return @eca(4, buffer_id, memory_map_location, @sizeof(Expr)) } diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 5551b5cb..71a8bcb2 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -1,4 +1,3 @@ - length := fn(ptr: ^u8): uint { len := 0 loop if *(ptr + len) == 0 return len else len += 1 diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 92c66f50..f13770ff 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -1,37 +1,163 @@ +.{memory, log, string} := @use("../../../../libraries/stn/src/lib.hb") render := @use("../../../../libraries/render/src/lib.hb") /* expected result: - extended unicode test */ + trash notepad app, colours should change with r,g,b,w keys */ psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") +send_byte := fn(byte: u8): u8 { + memory.outb(96, byte) + return memory.inb(96) +} + example := fn(): void { - screen := render.init(false) - font := render.text.font_from_psf2(@bitcast(&psf)) + screen := render.init(true) + font := render.text.font_from_psf2(@bitcast(&psf), false) - str := "Extended unicode test: + msg := "ableboard (tm) (patent pending):\n\0" + msg_len := string.length(msg) -ABCDEFGHIJKLMNOPQRSTUVWXYZ -abcdefghijklmnopqrstuvwxyz -0123456789 -!\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ -ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞß -àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ -ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ -ĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ -„‟†‡•‣․‥…‧ -₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿ -∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟ -─│┌┐└┘├┤┬┴┼╋╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿ -▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟ -■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○ -←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻ - 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟 -✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟ -✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿\0" + buf := memory.alloc(u8, 4096) + + @inline(memory.copy, u8, msg, buf, msg_len) + cursor := buf + msg_len + + if font == null { + return + } + + _ = send_byte(238) + log.info("PS/2 Driver Loaded\0") + if send_byte(238) == 238 { + log.info("PS/2 Keyboard Echoed\0") + } + if send_byte(244) == 250 { + log.info("Enabled scanning\0") + } + + color := render.white + + prev_input := 250 + + loop { + input := memory.inb(96) + if input != prev_input { + prev_input = input + char := map_keys(input) + if char != 0 { + if char == 0xA { + *cursor = 32 + cursor += 1; + *cursor = 92 + cursor += 1 + } else if char == 0x72 { + color = render.red + } else if char == 0x67 { + color = render.green + } else if char == 0x62 { + color = render.blue + } else if char == 0x77 { + color = render.white + }; + *cursor = char + cursor += 1 + } + } + render.clear(screen, render.black) + render.put_text(screen, font, .(0, 0), color, buf) + render.sync(screen) + } - render.clear(screen, render.black) - render.put_text(screen, @unwrap(font), .(0, 0), render.white, str) - render.sync(screen) return +} + +map_keys := fn(inp: u8): u8 { + if inp == 0x29 { + return 0x7E + } else if inp == 0x2 { + return 0x31 + } else if inp == 0x3 { + return 0x32 + } else if inp == 0x4 { + return 0x33 + } else if inp == 0x5 { + return 0x34 + } else if inp == 0x6 { + return 0x35 + } else if inp == 0x7 { + return 0x36 + } else if inp == 0x8 { + return 0x37 + } else if inp == 0x9 { + return 0x38 + } else if inp == 0xA { + return 0x39 + } else if inp == 0xB { + return 0x30 + } else if inp == 0xC { + return 0x2D + } else if inp == 0xD { + return 0x3D + } else if inp == 0x1C { + return 0xA + } else if inp == 0x10 { + return 0x71 + } else if inp == 0x11 { + return 0x77 + } else if inp == 0x12 { + return 0x65 + } else if inp == 0x13 { + return 0x72 + } else if inp == 0x14 { + return 0x74 + } else if inp == 0x15 { + return 0x79 + } else if inp == 0x16 { + return 0x75 + } else if inp == 0x17 { + return 0x69 + } else if inp == 0x18 { + return 0x6F + } else if inp == 0x19 { + return 0x70 + } else if inp == 0x1E { + return 0x61 + } else if inp == 0x1F { + return 0x73 + } else if inp == 0x20 { + return 0x64 + } else if inp == 0x21 { + return 0x66 + } else if inp == 0x22 { + return 0x67 + } else if inp == 0x23 { + return 0x68 + } else if inp == 0x24 { + return 0x6A + } else if inp == 0x25 { + return 0x6B + } else if inp == 0x26 { + return 0x6C + } else if inp == 0x2C { + return 0x7A + } else if inp == 0x2D { + return 0x78 + } else if inp == 0x2E { + return 0x63 + } else if inp == 0x2F { + return 0x76 + } else if inp == 0x30 { + return 0x62 + } else if inp == 0x31 { + return 0x6E + } else if inp == 0x32 { + return 0x6D + } else if inp == 0x2B { + return 0x5C + } else if inp == 0x39 { + return 32 + } else { + return 0 + } } \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 0491a37a..9a9ccbb6 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -58,8 +58,8 @@ path = "boot:///ps2_keyboard_driver.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -[boot.limine.ableos.modules.ps2_mouse_driver] -path = "boot:///ps2_mouse_driver.hbf" +# [boot.limine.ableos.modules.ps2_mouse_driver] +# path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 404ea22c465e2a796daf5fd95888b73ec7685eb3 Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 6 Nov 2024 01:49:57 +0000 Subject: [PATCH 36/66] rebase --- sysdata/programs/horizon/src/main.hb | 2 +- sysdata/system_config.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index be9f9248..5c79dfc0 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -38,7 +38,7 @@ main := fn(): int { pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) str := "Window Title Bar\0" // really we should null check but it is a bit broked - font := @unwrap(render.text.font_from_psf2(@bitcast(&psf))) + font := @unwrap(render.text.font_from_psf2(@bitcast(&psf), false)) mouse_x := 0 mouse_y := 0 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 9a9ccbb6..50020fe0 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ 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" From aafe047b5108ae7c9d27d71b8ed591bcee525700 Mon Sep 17 00:00:00 2001 From: koniifer Date: Wed, 6 Nov 2024 01:50:28 +0000 Subject: [PATCH 37/66] forgor --- sysdata/system_config.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 50020fe0..e9e920c8 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -49,8 +49,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.svga_driver] # path = "boot:///svga_driver.hbf" -[boot.limine.ableos.modules.ps2_keyboard_driver] -path = "boot:///ps2_keyboard_driver.hbf" +# [boot.limine.ableos.modules.ps2_keyboard_driver] +# path = "boot:///ps2_keyboard_driver.hbf" # [boot.limine.ableos.modules.filesystem_fat32] # path = "boot:///filesystem_fat32.hbf" From 85e63eb51c6b5bf1c955104ffa97ad1298e47d9b Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 22:16:08 -0600 Subject: [PATCH 38/66] Add backspace --- sysdata/programs/render_example/src/examples/text.hb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index f13770ff..d0490345 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -39,7 +39,6 @@ example := fn(): void { color := render.white prev_input := 250 - loop { input := memory.inb(96) if input != prev_input { @@ -59,13 +58,20 @@ example := fn(): void { color = render.blue } else if char == 0x77 { color = render.white + } else if char == 0xE { + cursor -= 1; + *cursor = 32 + + continue }; *cursor = char cursor += 1 } } render.clear(screen, render.black) + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) render.put_text(screen, font, .(0, 0), color, buf) + render.sync(screen) } @@ -99,6 +105,8 @@ map_keys := fn(inp: u8): u8 { return 0x2D } else if inp == 0xD { return 0x3D + } else if inp == 0xE { + return 0xE } else if inp == 0x1C { return 0xA } else if inp == 0x10 { From 795f10986f2ae55ad9d0c109059ac9d393254ae6 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 22:58:52 -0600 Subject: [PATCH 39/66] Add partial support for sending mouse position updates --- sysdata/libraries/intouch/src/lib.hb | 31 +++++++++++++++++-- sysdata/programs/horizon/src/main.hb | 14 +++++++-- sysdata/programs/ps2_mouse_driver/src/main.hb | 11 +++++-- sysdata/system_config.toml | 12 +++---- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index e592a7b1..a8d3e36e 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -1,15 +1,40 @@ stn := @use("../../stn/src/lib.hb"); -.{log, buffer} := stn +.{log, buffer, memory} := stn +keycodes := @use("keycodes.hb") + events := @use("events.hb"); .{KeyEvent, MouseEvent} := events recieve_key_event := fn(): ?KeyEvent { - buf := buffer.search("PS/2 Keyboard\0") + mem_page := memory.request_page(1) + + buf_id := buffer.search("PS/2 Keyboard\0") + // Read out of the keyboard buffer here + buffer.recv(KeyEvent, buf_id, mem_page) + + key_event := KeyEvent.(0, 0, 2) + // return key_event return null } -recieve_mouse_event := fn(): ?MouseEvent { +recieve_mouse_event := fn(): ?^MouseEvent { + mem_page := memory.request_page(1) + + buf_id := buffer.search("PS/2 Mouse\0") + + // Read out of the Mouse buffer here + buffer.recv(MouseEvent, buf_id, mem_page) + if *mem_page == 0 { + // log.info("Haha\0") + } else { + log.info("Mouse event recieved in horizon.\0") + dx := *mem_page + dy := *mem_page + 1 + mevent := MouseEvent.(dx, dy, 0, 0, 0) + return &mevent + } + return null } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 5c79dfc0..e78a4b3f 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -19,6 +19,13 @@ Window := struct { psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") main := fn(): int { + I_LOOP := 10000 + loop { + if I_LOOP >= 0 { + break + } + I_LOOP += 1 + } win_buff := buffer.create("XHorizon\0") screen := render.init(true) @@ -60,9 +67,12 @@ main := fn(): int { { // get input events from drivers via intouch - key_event := intouch.recieve_key_event() + // key_event := intouch.recieve_key_event() mouse_event := intouch.recieve_mouse_event() - + if mouse_event != null { + mouse_x += mouse_event.x_change + mouse_y -= mouse_event.y_change + } // render mouse render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) // Send events to focused window diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 62920d1c..82c849ee 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -1,6 +1,9 @@ .{memory, buffer, log, string, math} := @use("../../../libraries/stn/src/lib.hb") Vec2 := math.Vec2 +intouch := @use("../../../libraries/intouch/src/lib.hb"); +.{MouseEvent} := intouch.events + i9 := packed struct {sign: bool, value: u8} Button := struct {id: u8} LeftButton := Button.(1) @@ -91,10 +94,9 @@ set_up_mouse := fn(): void { button_states := @as(u8, 0) main := fn(): int { + mouse_buffer := buffer.create("PS/2 Mouse\0") format_page := memory.alloc(u8, 1024) - mouse_buffer := buffer.create("Mouse\0") - send_byte(0x64, 0xA8) log.info("Aux mouse device enabled.\0") @@ -142,7 +144,10 @@ main := fn(): int { dy.sign = (status & 0x20) == 0 if dy.value != 0 & dx.value != 0 { - mouse_moved(.(dx, dy)) + event := MouseEvent.(dx.value, dy.value, 0, 0, 0) + buffer.write(MouseEvent, &event, mouse_buffer) + + // mouse_moved(.(dx, dy)) } } diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index e9e920c8..b1d8a651 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,8 +37,8 @@ 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" @@ -58,8 +58,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -# [boot.limine.ableos.modules.ps2_mouse_driver] -# path = "boot:///ps2_mouse_driver.hbf" +[boot.limine.ableos.modules.ps2_mouse_driver] +path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 879bbfa1739e976443965ea6f918f8d0d181bd30 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 23:01:06 -0600 Subject: [PATCH 40/66] rework logic --- sysdata/libraries/intouch/src/lib.hb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index a8d3e36e..d57177ad 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -26,10 +26,7 @@ recieve_mouse_event := fn(): ?^MouseEvent { // Read out of the Mouse buffer here buffer.recv(MouseEvent, buf_id, mem_page) - if *mem_page == 0 { - // log.info("Haha\0") - } else { - log.info("Mouse event recieved in horizon.\0") + if *mem_page != 0 { dx := *mem_page dy := *mem_page + 1 mevent := MouseEvent.(dx, dy, 0, 0, 0) From 6a319c55b0e3ed93c0a43a9b7cdc5b69dd4198e3 Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 23:01:15 -0600 Subject: [PATCH 41/66] Proper Y direction --- sysdata/programs/horizon/src/main.hb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index e78a4b3f..b85acbd9 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -71,7 +71,7 @@ main := fn(): int { mouse_event := intouch.recieve_mouse_event() if mouse_event != null { mouse_x += mouse_event.x_change - mouse_y -= mouse_event.y_change + mouse_y += mouse_event.y_change } // render mouse render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) From 12883ac9266892b4fae6d9078bec6aedf28f4a4d Mon Sep 17 00:00:00 2001 From: Able Date: Tue, 5 Nov 2024 23:04:29 -0600 Subject: [PATCH 42/66] Remove busy loop --- sysdata/programs/horizon/src/main.hb | 46 +--------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index b85acbd9..07562935 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -19,13 +19,6 @@ Window := struct { psf := @embed("../../../consolefonts/tamsyn/10x20r.psf") main := fn(): int { - I_LOOP := 10000 - loop { - if I_LOOP >= 0 { - break - } - I_LOOP += 1 - } win_buff := buffer.create("XHorizon\0") screen := render.init(true) @@ -35,15 +28,10 @@ main := fn(): int { window := render.new_surface(screen.width / 3, screen.height / 3) - x := 0 - mem_buf := memory.request_page(1) color := random.any(render.Color) side := window.width / 8 - vel_inner := Vec2(int).(1, 1) - pos_inner := Vec2(uint).((window.width - side) / 2, (window.height - side) / 2) - str := "Window Title Bar\0" // really we should null check but it is a bit broked font := @unwrap(render.text.font_from_psf2(@bitcast(&psf), false)) @@ -78,41 +66,9 @@ main := fn(): int { // Send events to focused window } - if pos_inner.x == 0 | pos_inner.x == window.width - side { - vel_inner.x = -vel_inner.x - color = random.any(render.Color) - } - if pos_inner.y == 20 | pos_inner.y == window.height - side { - vel_inner.y = -vel_inner.y - color = random.any(render.Color) - } - // TODO: Get windows out of a collection and iter through - window_count := 0 + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - // loop { - // render.clear(window, render.black) - - // // Draw the decorators - // { - // render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) - // render.put_rect(window, .(0, 0), .(window.width - 1, 20), render.white) - // render.put_text(window, font, .(window.width / 2, 1), render.white, str) - // } - // render.put_filled_rect(window, pos_inner, .(side, side), color) - - // // Apply the image to the screen - // pos := Vec2(uint).(x, 100) - - // render.put_surface(screen, window, pos, false) - // if window_count >= 1 { - // x = 0 - // break - // } - // window_count += 1 - // x += screen.width / 2 - // } - pos_inner += @bitcast(vel_inner) // Sync the screen render.sync(screen) From a49ac7bca03c00eb1b6a3da16b7300ff0c1a0bf5 Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 6 Nov 2024 09:36:17 -0600 Subject: [PATCH 43/66] Label Widget inside horizon --- sysdata/programs/horizon/src/main.hb | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 07562935..5535bcd3 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -4,9 +4,35 @@ stn := @use("../../../libraries/stn/src/lib.hb"); horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") -render := @use("../../../libraries/render/src/lib.hb") +render := @use("../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text intouch := @use("../../../libraries/intouch/src/lib.hb") +Label := struct {is_dirty: bool, surface: Surface, text: ^u8, text_length: uint} +set_label_text := fn(label: Label, text: ^u8, text_length: uint): void { + label.is_dirty = true + label.text = text + label.text_length = text_length +} + +render_label_to_surface := fn(surface: Surface, label: Label, font: Font): void { + if label.is_dirty { + render.clear(label.surface, render.blue) + render.put_text(label.surface, font, .(0, 0), render.red, "hi\0") + } + pos := Vec2(uint).(100, 100) + render.put_surface(surface, label.surface, pos, false) + render.sync(surface) +} + +new_label := fn(text: ^u8): Label { + text_surface := render.new_surface(100, 16) + text_length := string.length(text) + label := Label.(true, text_surface, text, text_length) + return label +} + Window := struct { // TODO: Replace this with widgets implicit_framebuffer: render.Surface, @@ -37,6 +63,7 @@ main := fn(): int { mouse_x := 0 mouse_y := 0 + text_label := new_label("Hi\0") loop { // Clear the screen @@ -70,6 +97,8 @@ main := fn(): int { render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + render_label_to_surface(screen, text_label, font) + // Sync the screen render.sync(screen) } From ab8522fbe1f893a7998c8d470a7bb8b0fd40c02c Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 6 Nov 2024 11:43:40 -0600 Subject: [PATCH 44/66] widget UI --- sysdata/libraries/horizon_api/src/lib.hb | 1 + sysdata/libraries/horizon_api/src/ui.hb | 50 ++++++++++++++++++++ sysdata/libraries/horizon_api/src/widgets.hb | 41 ++++++++++++++-- sysdata/programs/horizon/src/main.hb | 38 ++++----------- 4 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 sysdata/libraries/horizon_api/src/ui.hb diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index 1db3411e..72f58d10 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -6,6 +6,7 @@ render := @use("../../../libraries/render/src/lib.hb") input := @use("../../intouch/src/lib.hb") widgets := @use("widgets.hb") +ui := @use("ui.hb") WindowID := struct { host_id: int, diff --git a/sysdata/libraries/horizon_api/src/ui.hb b/sysdata/libraries/horizon_api/src/ui.hb new file mode 100644 index 00000000..93fae2d8 --- /dev/null +++ b/sysdata/libraries/horizon_api/src/ui.hb @@ -0,0 +1,50 @@ +stn := @use("../../../libraries/stn/src/lib.hb"); +.{string, log} := stn; +.{Vec2} := stn.math + +render := @use("../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text + +UI := struct { + raw: ^u8, + raw_length: uint, + is_dirty: bool, + surface: Surface, +} + +render_ui := fn(surface: Surface, ui: UI): void { + if ui.is_dirty { + render.clear(ui.surface, render.black) + ui.is_dirty = false + } + pos := Vec2(uint).(0, 0) + render.put_surface(surface, ui.surface, pos, false) +} + +sexpr_parser := fn(sexpr: ^u8): UI { + cursor := sexpr + paren_balance := 0 + loop { + if *cursor == 0 { + if paren_balance != 0 { + log.error("Unbalanced Parens\0") + } + break + } else if *cursor == 40 { + log.info("Open paren\0") + paren_balance += 1 + } else if *cursor == 41 { + log.info("Closed paren\0") + paren_balance -= 1 + } + + cursor += 1 + } + + length := string.length(sexpr) + + ui_surface := render.new_surface(100, 100) + + return UI.(sexpr, length, true, ui_surface) +} \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets.hb index 04574f28..9bf56f75 100644 --- a/sysdata/libraries/horizon_api/src/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets.hb @@ -1,6 +1,13 @@ // Widget types // End types +stn := @use("../../../libraries/stn/src/lib.hb"); +.{string, log} := stn; +.{Vec2} := stn.math + +render := @use("../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text LayoutChildHorizontalFirst := 0 LayoutChildVerticalFirst := 1 @@ -12,9 +19,33 @@ Size := struct { max_height: int, } -Widget := struct { - size: Size, - clickable: bool, - layout: u8, - a: bool, +Label := struct { + is_dirty: bool, + surface: Surface, + text: ^u8, + text_length: uint, +} + +set_label_text := fn(label: Label, text: ^u8): void { + text_length := string.length(text) + + label.is_dirty = true + label.text = text + label.text_length = text_length +} + +render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: Vec2(uint)): void { + if label.is_dirty { + render.clear(label.surface, render.black) + render.put_text(label.surface, font, .(0, 0), render.white, label.text) + } + render.put_surface(surface, label.surface, pos, false) + render.sync(surface) +} + +new_label := fn(text: ^u8): Label { + text_surface := render.new_surface(100, 20) + text_length := string.length(text) + label := Label.(true, text_surface, text, text_length) + return label } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 5535bcd3..c33882e6 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -2,37 +2,15 @@ stn := @use("../../../libraries/stn/src/lib.hb"); .{string, memory, buffer, random, log} := stn; .{Vec2} := stn.math -horizon_api := @use("../../../libraries/horizon_api/src/lib.hb") +horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); +.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets; +.{sexpr_parser, render_ui} := horizon_api.ui render := @use("../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text intouch := @use("../../../libraries/intouch/src/lib.hb") -Label := struct {is_dirty: bool, surface: Surface, text: ^u8, text_length: uint} -set_label_text := fn(label: Label, text: ^u8, text_length: uint): void { - label.is_dirty = true - label.text = text - label.text_length = text_length -} - -render_label_to_surface := fn(surface: Surface, label: Label, font: Font): void { - if label.is_dirty { - render.clear(label.surface, render.blue) - render.put_text(label.surface, font, .(0, 0), render.red, "hi\0") - } - pos := Vec2(uint).(100, 100) - render.put_surface(surface, label.surface, pos, false) - render.sync(surface) -} - -new_label := fn(text: ^u8): Label { - text_surface := render.new_surface(100, 16) - text_length := string.length(text) - label := Label.(true, text_surface, text, text_length) - return label -} - Window := struct { // TODO: Replace this with widgets implicit_framebuffer: render.Surface, @@ -64,6 +42,8 @@ main := fn(): int { mouse_x := 0 mouse_y := 0 text_label := new_label("Hi\0") + widgets := "()\0" + ui := sexpr_parser(widgets) loop { // Clear the screen @@ -87,6 +67,7 @@ main := fn(): int { if mouse_event != null { mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change + set_label_text(text_label, "Mouse Moved\0") } // render mouse render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white) @@ -96,9 +77,10 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - - render_label_to_surface(screen, text_label, font) - + { + pos := Vec2(uint).(1, screen.height - 21) + render_label_to_surface(screen, text_label, font, pos) + } // Sync the screen render.sync(screen) } From 6a8d92d2c277c26e5823a71dbb6d98cf0ec9db83 Mon Sep 17 00:00:00 2001 From: koniifer Date: Thu, 7 Nov 2024 01:55:08 +0000 Subject: [PATCH 45/66] mostly qoi image support refactor image loader for multiple filetypes port some broken math stuff make logger more readable --- kernel/src/logger.rs | 27 +++-- repbuild/src/dev.rs | 3 - sysdata/libraries/render/src/image.hb | 102 ------------------ sysdata/libraries/render/src/image/bmp.hb | 52 +++++++++ sysdata/libraries/render/src/image/lib.hb | 34 ++++++ sysdata/libraries/render/src/image/qoi.hb | 101 +++++++++++++++++ sysdata/libraries/render/src/lib.hb | 2 +- sysdata/libraries/stn/src/math.hb | 47 ++++---- sysdata/libraries/stn/src/string.hb | 14 +++ .../src/examples/assets/mini.bmp | Bin 102454 -> 102454 bytes .../src/examples/assets/mini.qoi | Bin 0 -> 62864 bytes .../render_example/src/examples/image.hb | 45 ++++---- sysdata/programs/render_example/src/main.hb | 2 +- 13 files changed, 268 insertions(+), 161 deletions(-) delete mode 100644 sysdata/libraries/render/src/image.hb create mode 100644 sysdata/libraries/render/src/image/bmp.hb create mode 100644 sysdata/libraries/render/src/image/lib.hb create mode 100644 sysdata/libraries/render/src/image/qoi.hb create mode 100644 sysdata/programs/render_example/src/examples/assets/mini.qoi diff --git a/kernel/src/logger.rs b/kernel/src/logger.rs index fd9cc4a5..d1bd6e1c 100644 --- a/kernel/src/logger.rs +++ b/kernel/src/logger.rs @@ -36,13 +36,26 @@ impl log::Log for Logger { Level::Debug => "25", Level::Trace => "103", }; - let module = record.module_path().unwrap_or_default(); - let line = record.line().unwrap_or_default(); - crate::arch::log(format_args!( - "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n", - record.args(), - )) - .expect("write to serial console"); + let module = record + .module_path() + .unwrap_or_default() + .rsplit_once(':') + .unwrap_or_default() + .1; + if module == "" { + crate::arch::log(format_args!( + "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m: {}\r\n", + record.args(), + )) + .expect("write to serial console"); + } else { + let line = record.line().unwrap_or_default(); + crate::arch::log(format_args!( + "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n", + record.args(), + )) + .expect("write to serial console"); + } } fn flush(&self) {} diff --git a/repbuild/src/dev.rs b/repbuild/src/dev.rs index d2a66e34..f93edd15 100644 --- a/repbuild/src/dev.rs +++ b/repbuild/src/dev.rs @@ -74,7 +74,6 @@ impl Package { &path, Options { fmt: true, - // optimize: true, ..Default::default() }, &mut bytes, @@ -83,7 +82,6 @@ impl Package { hblang::run_compiler( &path, Options { - // optimize: true, ..Default::default() }, &mut bytes, @@ -100,7 +98,6 @@ impl Package { &path, Options { dump_asm: true, - // optimize: true, ..Default::default() }, &mut bytes, diff --git a/sysdata/libraries/render/src/image.hb b/sysdata/libraries/render/src/image.hb deleted file mode 100644 index 249094ad..00000000 --- a/sysdata/libraries/render/src/image.hb +++ /dev/null @@ -1,102 +0,0 @@ -.{Color, Surface, new_surface} := @use("./lib.hb"); -.{log, memory} := @use("../../stn/src/lib.hb") - -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, -} - -surface_from_bmp := fn(bmp: ^u8): ?Surface { - 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 null - } - info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) - bmp += file_header.offset - - px := info_header.width * info_header.height - ptr := @as(^Color, @bitcast(bmp)) - tmp := @as(Color, idk) - row := @as(i32, 0) - - loop if row == info_header.height / 2 break else { - col := @as(i32, 0) - loop if col == info_header.width break else { - top_index := row * info_header.width + col - bottom_index := (info_header.height - 1 - row) * info_header.width + col - - tmp = *(ptr + top_index); - *(ptr + top_index) = *(ptr + bottom_index); - *(ptr + bottom_index) = tmp - - col += 1 - } - row += 1 - } - - return .(@bitcast(bmp), info_header.width, info_header.height) -} - -new_surface_from_bmp := fn(bmp: ^u8): ?Surface { - 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 null - } - info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) - bmp += file_header.offset - - width := @as(uint, @intcast(info_header.width)) - height := @as(uint, @intcast(info_header.height)) - - surface := new_surface(width, height) - top_start_idx := surface.buf - bottom_start_idx := surface.buf + width * (height - 1) - rows_to_copy := height - top_cursor := @as(^Color, @bitcast(bmp)) - bottom_cursor := top_cursor + width * (height - 1) - - loop if rows_to_copy <= 1 break else { - @inline(memory.copy, Color, top_cursor, bottom_start_idx, @bitcast(width)) - @inline(memory.copy, Color, bottom_cursor, top_start_idx, @bitcast(width)) - - top_start_idx += surface.width - bottom_start_idx -= surface.width - top_cursor += width - bottom_cursor -= width - rows_to_copy -= 2 - } - - if rows_to_copy == 1 { - @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(width)) - } - - return surface -} \ No newline at end of file diff --git a/sysdata/libraries/render/src/image/bmp.hb b/sysdata/libraries/render/src/image/bmp.hb new file mode 100644 index 00000000..690198b3 --- /dev/null +++ b/sysdata/libraries/render/src/image/bmp.hb @@ -0,0 +1,52 @@ +.{Color, Surface, new_surface, put_surface} := @use("../lib.hb"); +.{log, memory} := @use("../../../stn/src/lib.hb") + +BitmapFileHeader := packed struct { + magic: 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 := fn(bmp: ^u8): ?Surface { + file_header := @as(^BitmapFileHeader, @bitcast(bmp)) + info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader))) + bmp += file_header.offset + + if file_header.magic != 0x4D42 | info_header.width == 0 | info_header.height == 0 { + log.error("Invalid BMP image.\0") + return null + } + + width := @as(uint, info_header.width) + height := @as(uint, info_header.height) + lhs := Surface.(@bitcast(bmp), width, height) + rhs := new_surface(width, height) + put_surface(rhs, lhs, .(0, 0), true) + + return rhs +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/image/lib.hb b/sysdata/libraries/render/src/image/lib.hb new file mode 100644 index 00000000..5b8765ed --- /dev/null +++ b/sysdata/libraries/render/src/image/lib.hb @@ -0,0 +1,34 @@ +.{log} := @use("../../../stn/src/lib.hb"); +.{Surface} := @use("../lib.hb") +bmp := @use("bmp.hb") +qoi := @use("qoi.hb") + +BMP := 0x4D42 +QOI := 0x66696F71 +// stand-in for null until bugfix +DOES_NOT_EXIST := 1 << 32 + +get_format := fn(file: ^u8): uint { + if *@as(^u16, @bitcast(file)) == BMP { + return BMP + } else if *@as(^u32, @bitcast(file)) == QOI { + return QOI + } else { + return DOES_NOT_EXIST + } +} + +from := fn(file: ^u8): ?Surface { + format := get_format(file) + + if format == DOES_NOT_EXIST { + log.error("Could not detect image format.\0") + return null + } else if format == BMP { + return bmp.from(file) + } else if format == QOI { + return qoi.from(file) + } + + return null +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/image/qoi.hb b/sysdata/libraries/render/src/image/qoi.hb new file mode 100644 index 00000000..ca6e8c1c --- /dev/null +++ b/sysdata/libraries/render/src/image/qoi.hb @@ -0,0 +1,101 @@ +.{Color, Surface, new_surface} := @use("../lib.hb"); +.{log, memory} := @use("../../../stn/src/lib.hb") + +/* source: + https://github.com/phoboslab/qoi/blob/master/qoi.h */ + +QOI_SRGB := 0 +QOI_LINEAR := 1 +QOI_OP_INDEX := 0x0 +QOI_OP_DIFF := 0x40 +QOI_OP_LUMA := 0x80 +QOI_OP_RUN := 0xC0 +QOI_OP_RGB := 0xFE +QOI_OP_RGBA := 0xFF +QOI_MASK_2 := 0xC0 +QOI_COLOR_HASH := fn(c: Color): u8 { + return (c.r * 3 + c.g * 5 + c.b * 7 + c.a * 11) % 64 +} +QOI_MAGIC := 0x716F6966 +QOI_PIXELS_MAX := 400000000 + +QuiteOkayHeader := packed struct { + magic: u32, + width: u32, + height: u32, + channels: u8, + colorspace: u8, +} + +be_to_le := fn(big: u32): u32 { + return (big & 0xFF000000) >> 24 | (big & 0xFF0000) >> 8 | (big & 0xFF00) << 8 | (big & 0xFF) << 24 +} + +from := fn(qoi: ^u8): ?Surface { + header := @as(^QuiteOkayHeader, @bitcast(qoi)) + + qoi += @sizeof(QuiteOkayHeader) + + width := be_to_le(header.width) + height := be_to_le(header.height) + + if be_to_le(header.magic) != QOI_MAGIC | width == 0 | height == 0 | header.channels < 3 | header.channels > 4 { + log.error("Invalid QOI image.\0") + return null + } + + surface := new_surface(width, height) + index := @as([Color; 64], idk) + + run := 0 + px := Color.(0, 0, 0, 255) + px_pos := 0 + + total_pixels := width * height + + loop if px_pos >= total_pixels break else { + if run > 0 { + run -= 1 + } else { + b1 := *qoi + qoi += 1 + + if b1 == QOI_OP_RGB { + px.r = *qoi + px.g = *(qoi + 1) + px.b = *(qoi + 2) + qoi += 3 + } else if b1 == QOI_OP_RGBA { + px.r = *qoi + px.g = *(qoi + 1) + px.b = *(qoi + 2) + px.a = *(qoi + 3) + qoi += 4 + } else if (b1 & QOI_MASK_2) == QOI_OP_INDEX { + px = index[b1 & 0x3F] + } else if (b1 & QOI_MASK_2) == QOI_OP_DIFF { + px.r = px.r + (b1 >> 4 & 0x3) - 2 & 0xFF + px.g = px.g + (b1 >> 2 & 0x3) - 2 & 0xFF + px.b = px.b + (b1 & 0x3) - 2 & 0xFF + } else if (b1 & QOI_MASK_2) == QOI_OP_LUMA { + b2 := *qoi + vg := (b1 & 0x3F) - 32 + + px.r = px.r + vg - 8 + (b2 >> 4 & 0xF) & 0xFF + px.g = px.g + vg & 0xFF + px.b = px.b + vg - 8 + (b2 & 0xF) & 0xFF + qoi += 1 + } else if (b1 & QOI_MASK_2) == QOI_OP_RUN { + run = b1 & 0x3F + } + + index[@inline(QOI_COLOR_HASH, px)] = px + }; + + *(surface.buf + px_pos) = px + + px_pos += 1 + } + + return surface +} \ No newline at end of file diff --git a/sysdata/libraries/render/src/lib.hb b/sysdata/libraries/render/src/lib.hb index 87e86c34..465a0045 100644 --- a/sysdata/libraries/render/src/lib.hb +++ b/sysdata/libraries/render/src/lib.hb @@ -1,5 +1,5 @@ software := @use("software.hb") -image := @use("image.hb") +image := @use("image/lib.hb") text := @use("text.hb") // default mode diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index 91089194..9d0910de 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -30,30 +30,37 @@ Vec2 := fn($Expr: type): type { return struct {x: Expr, y: Expr} } -SIN_TABLE := @as([int; 91], @bitcast(@embed("./assets/sin_table"))) +/* source: + https://github.com/baker-Xie/FastMath/blob/master/include/fast_math.h */ -sin := fn(theta: int, amplitude: uint): int { - if theta < 0 { - theta += (-theta / 360 + 1) * 360 - } else if theta >= 360 { - theta -= theta / 360 * 360 - } +// ! heavily broken, possibly due to compiler bug... or skill issues - quadrant := theta / 90 - index := theta % 90 +PI := 3.14159265358979323846 - if @as(u8, @intcast(quadrant)) == @as(u8, 1) { - index = 90 - index - } +SIN_TABLE := [f32].(0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245) +TAN_TABLE := [f32].(0.0, 0.01227246237956628, 0.02454862210892544, 0.03683218099484564, 0.04912684976946725, 0.06143635258159376, 0.07376443152244928, 0.08611485119762791, 0.09849140335716425, 0.110897911595913, 0.1233382361367387, 0.1358162787093877, 0.1483359875383474, 0.1609013624534892, 0.1735164601378558, 0.1861853995275837, 0.198912367379658, 0.2117016240239833, 0.2245575093171293, 0.2374844488160702, 0.2504869601913055, 0.263569659899918, 0.2767372701404143, 0.2899946261126061, 0.3033466836073424, 0.3167985269526038, 0.330355377344334, 0.3440226015924263, 0.3578057213145241, 0.3717104226127435, 0.3857425662711212, 0.3999081985145372, 0.414213562373095, 0.4286651096994995, 0.4432695138908643, 0.4580336833706724, 0.4729647758913199, 0.4880702137228629, 0.5033576997992942, 0.5188352348999757, 0.5345111359507916, 0.5503940555372639, 0.566493002730344, 0.5828173653349761, 0.5993769336819237, 0.616181926094866, 0.6332430161775691, 0.6505713620801533, 0.6681786379192989, 0.6860770675448629, 0.7042794608650442, 0.7227992529642059, 0.7416505462720354, 0.7608481560702512, 0.7804076596539435, 0.8003454494993202, 0.8206787908286602, 0.8414258840072547, 0.8626059322567399, 0.8842392152253498, 0.906347169019147, 0.9289524733703675, 0.9520791467009252, 0.9757526499323765, 0.9999999999999999, 1.024849894150227, 1.05033284623986, 1.076481336415266, 1.103329975733476, 1.130915687498827, 1.159277907333434, 1.188458804282966, 1.218503525587976, 1.249460468133579, 1.281381580036554, 1.31432269635108, 1.34834391348672, 1.383510007652874, 1.419890903494092, 1.457562200087105, 1.496605762665489, 1.537110389861882, 1.579172567960209, 1.622897325693455, 1.668399205583507, 1.715803370795664, 1.765246870094191, 1.816880087892402, 1.870868411789389, 1.927394156630064, 1.986658792343365, 2.04888553303075, 2.11432235754864, 2.183245547884151, 2.255963851929159, 2.33282340310135, 2.414213562373095, 2.500573890994256, 2.592402517738071, 2.690266237279613, 2.794812772490477, 2.906785761665535, 3.027043204317773, 3.156580333940787, 3.296558208938321, 3.448339762033025, 3.613535681307428, 3.7940634000883, 3.992223783770083, 4.210802033502797, 4.453202224414411, 4.723629327882301, 5.027339492125846, 5.370990435003726, 5.763142005118804, 6.21498777108904, 6.741452405414988, 7.362887641324242, 8.107785803676903, 9.017302360424724, 10.15317038760884, 11.61239886143525, 13.55666924235242, 16.27700795993539, 20.35546762498714, 27.15017066569958, 40.73548387208334, 81.48324020654604, 1633123935319537.0, -81.48324020654685, -40.73548387208354, -27.15017066569967, -20.35546762498719, -16.27700795993542, -13.55666924235244, -11.61239886143527, -10.15317038760886, -9.017302360424734, -8.10778580367691, -7.362887641324249, -6.741452405414994, -6.214987771089044, -5.763142005118809, -5.37099043500373, -5.02733949212585, -4.723629327882303, -4.453202224414413, -4.2108020335028, -3.992223783770084, -3.794063400088302, -3.61353568130743, -3.448339762033026, -3.296558208938323, -3.156580333940789, -3.027043204317775, -2.906785761665536, -2.794812772490478, -2.690266237279614, -2.592402517738072, -2.500573890994257, -2.414213562373095, -2.332823403101351, -2.25596385192916, -2.183245547884153, -2.114322357548642, -2.048885533030752, -1.986658792343365, -1.927394156630064, -1.870868411789389, -1.816880087892402, -1.765246870094192, -1.715803370795664, -1.668399205583508, -1.622897325693455, -1.57917256796021, -1.537110389861883, -1.49660576266549, -1.457562200087105, -1.419890903494092, -1.383510007652874, -1.34834391348672, -1.31432269635108, -1.281381580036555, -1.249460468133579, -1.218503525587977, -1.188458804282967, -1.159277907333435, -1.130915687498827, -1.103329975733476, -1.076481336415266, -1.05033284623986, -1.024849894150228, -1.0, -0.9757526499323768, -0.9520791467009256, -0.9289524733703679, -0.9063471690191476, -0.8842392152253504, -0.8626059322567398, -0.8414258840072547, -0.8206787908286604, -0.8003454494993202, -0.7804076596539438, -0.7608481560702515, -0.7416505462720356, -0.7227992529642062, -0.7042794608650446, -0.6860770675448633, -0.6681786379192988, -0.6505713620801532, -0.6332430161775691, -0.6161819260948661, -0.5993769336819238, -0.5828173653349762, -0.5664930027303442, -0.5503940555372643, -0.5345111359507919, -0.5188352348999761, -0.5033576997992947, -0.4880702137228627, -0.4729647758913199, -0.4580336833706724, -0.4432695138908644, -0.4286651096994996, -0.4142135623730952, -0.3999081985145373, -0.3857425662711215, -0.3717104226127437, -0.3578057213145244, -0.3440226015924267, -0.3303553773443338, -0.3167985269526037, -0.3033466836073424, -0.2899946261126062, -0.2767372701404144, -0.2635696598999182, -0.2504869601913056, -0.2374844488160704, -0.2245575093171296, -0.2117016240239837, -0.1989123673796584, -0.1861853995275837, -0.1735164601378557, -0.1609013624534892, -0.1483359875383475, -0.1358162787093878, -0.1233382361367388, -0.1108979115959132, -0.09849140335716448, -0.08611485119762818, -0.0737644315224496, -0.06143635258159368, -0.04912684976946721, -0.03683218099484564, -0.02454862210892548, -0.01227246237956636) +TABLE_SIZE := @as(i32, 256) - value := SIN_TABLE[@bitcast(index)] - if quadrant >= 2 { - value = -value - } - - return (value * @bitcast(amplitude) + 5000) / 10000 +sin := fn(theta: f32): f32 { + si := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) + d := theta - @itf(si) * 2.0 * PI / @itf(TABLE_SIZE) + ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1 + si &= TABLE_SIZE - 1 + return SIN_TABLE[si] + (SIN_TABLE[ci] - 0.5 * SIN_TABLE[si] * d) * d } -cos := fn(theta: int, amplitude: uint): int { - return @inline(sin, theta + 90, amplitude) +cos := fn(theta: f32): f32 { + ci := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) + d := theta - @itf(ci) * 2.0 * PI / @itf(TABLE_SIZE) + si := ci + TABLE_SIZE / 4 & TABLE_SIZE - 1 + ci &= TABLE_SIZE - 1 + return SIN_TABLE[si] + (SIN_TABLE[ci] - 0.5 * SIN_TABLE[si] * d) * d +} + +tan := fn(theta: f32): f32 { + a := @fti(theta * @itf(TABLE_SIZE) / PI) + d := theta - @itf(a) * PI / @itf(TABLE_SIZE) + d = d + 1.0 / 3.0 * d * d * d + a &= TABLE_SIZE - 1 + return (TAN_TABLE[a] + d) / (1.0 - TAN_TABLE[a] * d) } \ No newline at end of file diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 71a8bcb2..41a5535c 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -67,4 +67,18 @@ reverse := fn(s: ^u8): void { j -= 1 } return +} + +equals := fn(lhs: ^u8, rhs: ^u8): bool { + if lhs == rhs { + return true + } + i := 0 + loop if *(lhs + i) != *(rhs + i) { + return false + } else if *lhs == 0 { + return true + } else { + i += 1 + } } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/assets/mini.bmp b/sysdata/programs/render_example/src/examples/assets/mini.bmp index 7e0b4ceec3f9c4bdab3a64aa01c75826149183ac..49cfcd268931f7b1747923f1764860954a1df65c 100644 GIT binary patch literal 102454 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ℜrxLK&_b>dD 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 literal 102454 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 X^XDOEuT^AZWLDtJYJIDO@b~`!1F?>E diff --git a/sysdata/programs/render_example/src/examples/assets/mini.qoi b/sysdata/programs/render_example/src/examples/assets/mini.qoi new file mode 100644 index 0000000000000000000000000000000000000000..3e42d02376480110ecb4a27b06ff55dfd98943a2 GIT binary patch literal 62864 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 Date: Wed, 6 Nov 2024 19:57:43 -0600 Subject: [PATCH 46/66] widgeting work --- sysdata/libraries/horizon_api/src/widgets.hb | 10 ++++++++-- sysdata/programs/horizon/src/main.hb | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets.hb index 9bf56f75..1fa04b54 100644 --- a/sysdata/libraries/horizon_api/src/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets.hb @@ -40,12 +40,18 @@ render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: V render.put_text(label.surface, font, .(0, 0), render.white, label.text) } render.put_surface(surface, label.surface, pos, false) - render.sync(surface) } new_label := fn(text: ^u8): Label { - text_surface := render.new_surface(100, 20) + text_surface := render.new_surface(1000, 20) text_length := string.length(text) label := Label.(true, text_surface, text, text_length) return label +} + +VerticalLayout := 0 +HorizontalLayout := 1 + +Container := struct { + layout: uint, } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index c33882e6..ed405bbd 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -77,6 +77,15 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + { + // Scroll bar :ThumbsUp: + render.put_rect(screen, .(100, 100), .(100, 10), render.white) + render.put_filled_rect(screen, .(110, 100), .(20, 10), render.white) + + render.put_rect(screen, .(90, 110), .(10, 100), render.white) + render.put_filled_rect(screen, .(90, 120), .(10, 20), render.white) + } + { pos := Vec2(uint).(1, screen.height - 21) render_label_to_surface(screen, text_label, font, pos) From f13c682171db2231bac645c612ab98dea4e8ef93 Mon Sep 17 00:00:00 2001 From: Able Date: Wed, 6 Nov 2024 20:11:36 -0600 Subject: [PATCH 47/66] refactor and widgeting magic --- sysdata/libraries/horizon_api/src/lib.hb | 2 +- .../src/{widgets.hb => widgets/label.hb} | 28 ++++--------------- .../horizon_api/src/widgets/widget_types.hb | 4 +++ .../horizon_api/src/widgets/widgets.hb | 28 +++++++++++++++++++ sysdata/programs/horizon/src/main.hb | 2 +- 5 files changed, 39 insertions(+), 25 deletions(-) rename sysdata/libraries/horizon_api/src/{widgets.hb => widgets/label.hb} (64%) create mode 100644 sysdata/libraries/horizon_api/src/widgets/widget_types.hb create mode 100644 sysdata/libraries/horizon_api/src/widgets/widgets.hb diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb index 72f58d10..112bbac3 100644 --- a/sysdata/libraries/horizon_api/src/lib.hb +++ b/sysdata/libraries/horizon_api/src/lib.hb @@ -5,7 +5,7 @@ render := @use("../../../libraries/render/src/lib.hb") input := @use("../../intouch/src/lib.hb") -widgets := @use("widgets.hb") +widgets := @use("widgets/widgets.hb") ui := @use("ui.hb") WindowID := struct { diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/label.hb similarity index 64% rename from sysdata/libraries/horizon_api/src/widgets.hb rename to sysdata/libraries/horizon_api/src/widgets/label.hb index 1fa04b54..3a44b397 100644 --- a/sysdata/libraries/horizon_api/src/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/label.hb @@ -1,25 +1,13 @@ -// Widget types - -// End types -stn := @use("../../../libraries/stn/src/lib.hb"); +stn := @use("../../../../libraries/stn/src/lib.hb"); .{string, log} := stn; .{Vec2} := stn.math -render := @use("../../../libraries/render/src/lib.hb"); +render := @use("../../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text -LayoutChildHorizontalFirst := 0 -LayoutChildVerticalFirst := 1 - -Size := struct { - min_width: int, - max_width: int, - min_height: int, - max_height: int, -} - Label := struct { + magic: uint, is_dirty: bool, surface: Surface, text: ^u8, @@ -45,13 +33,7 @@ render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: V new_label := fn(text: ^u8): Label { text_surface := render.new_surface(1000, 20) text_length := string.length(text) - label := Label.(true, text_surface, text, text_length) + widget_type := 3 + label := Label.(widget_type, true, text_surface, text, text_length) return label -} - -VerticalLayout := 0 -HorizontalLayout := 1 - -Container := struct { - layout: uint, } \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widget_types.hb b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb new file mode 100644 index 00000000..9755ec8b --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb @@ -0,0 +1,4 @@ +VerticalWidgetType := 1 +HorizontalWidgetType := 2 + +LabelWidgetType := 3 \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb new file mode 100644 index 00000000..c9aca72a --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -0,0 +1,28 @@ +// Widget types + +// End types +stn := @use("../../../../libraries/stn/src/lib.hb"); +.{string, log} := stn; +.{Vec2} := stn.math + +render := @use("../../../../libraries/render/src/lib.hb"); +.{Surface} := render; +.{Font} := render.text + +widget_types := @use("widget_types.hb") +label := @use("label.hb") +// .{Label, render_label_to_surface, set_label_text} := label + +Size := struct { + min_width: int, + max_width: int, + min_height: int, + max_height: int, +} + +Vertical := packed struct { + magic: uint, + // array of children, idk + // use a vec or linked list or whatever + children: ^^u8, +} \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index ed405bbd..cf9a000e 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -3,7 +3,7 @@ stn := @use("../../../libraries/stn/src/lib.hb"); .{Vec2} := stn.math horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); -.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets; +.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets.label; .{sexpr_parser, render_ui} := horizon_api.ui render := @use("../../../libraries/render/src/lib.hb"); From ba59233ce729d9712c24bc3ff02e14ad64262cb7 Mon Sep 17 00:00:00 2001 From: koniifer Date: Thu, 7 Nov 2024 02:22:58 +0000 Subject: [PATCH 48/66] untested string.contains(haystack, needle) --- sysdata/libraries/stn/src/string.hb | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sysdata/libraries/stn/src/string.hb b/sysdata/libraries/stn/src/string.hb index 41a5535c..efea8702 100644 --- a/sysdata/libraries/stn/src/string.hb +++ b/sysdata/libraries/stn/src/string.hb @@ -81,4 +81,41 @@ equals := fn(lhs: ^u8, rhs: ^u8): bool { } else { i += 1 } +} + +contains := fn(haystack: ^u8, needle: ^u8): bool { + haystack_len := @inline(length, haystack) + needle_len := @inline(length, needle) + + if needle_len == 0 { + return true + } + if haystack_len < needle_len { + return false + } + + max_start := haystack_len - needle_len + + pos := 0 + loop if pos > max_start break else { + is_match := true + offset := 0 + + loop if offset >= needle_len break else { + if *(haystack + pos + offset) != *(needle + offset) { + is_match = false + } + if is_match == false { + break + } + offset += 1 + } + + if is_match { + return true + } + pos += 1 + } + + return false } \ No newline at end of file From e8edee4ccc5d741ab7e34850265b5585ed72ce49 Mon Sep 17 00:00:00 2001 From: Able Date: Thu, 7 Nov 2024 09:57:39 -0600 Subject: [PATCH 49/66] broken :thumbsup: --- Cargo.lock | 6 +++--- .../libraries/horizon_api/src/widgets/label.hb | 3 +-- .../horizon_api/src/widgets/widgets.hb | 8 +++++++- sysdata/libraries/intouch/src/lib.hb | 5 +++-- sysdata/programs/horizon/src/main.hb | 18 +++++++++--------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 680a1a51..fb7bb7e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,12 +264,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -281,7 +281,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#87cb77a553d68d45c4ade7a71cae5a56edcf686b" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" dependencies = [ "hbbytecode", ] diff --git a/sysdata/libraries/horizon_api/src/widgets/label.hb b/sysdata/libraries/horizon_api/src/widgets/label.hb index 3a44b397..5a02f157 100644 --- a/sysdata/libraries/horizon_api/src/widgets/label.hb +++ b/sysdata/libraries/horizon_api/src/widgets/label.hb @@ -33,7 +33,6 @@ render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: V new_label := fn(text: ^u8): Label { text_surface := render.new_surface(1000, 20) text_length := string.length(text) - widget_type := 3 - label := Label.(widget_type, true, text_surface, text, text_length) + label := Label.(3, true, text_surface, text, text_length) return label } \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb index c9aca72a..2cda29ab 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -11,7 +11,6 @@ render := @use("../../../../libraries/render/src/lib.hb"); widget_types := @use("widget_types.hb") label := @use("label.hb") -// .{Label, render_label_to_surface, set_label_text} := label Size := struct { min_width: int, @@ -25,4 +24,11 @@ Vertical := packed struct { // array of children, idk // use a vec or linked list or whatever children: ^^u8, +} + +Horizontal := packed struct { + magic: uint, + // array of children, idk + // use a vec or linked list or whatever + children: ^^u8, } \ No newline at end of file diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index d57177ad..b6d751ca 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -19,7 +19,7 @@ recieve_key_event := fn(): ?KeyEvent { return null } -recieve_mouse_event := fn(): ?^MouseEvent { +recieve_mouse_event := fn(): ?MouseEvent { mem_page := memory.request_page(1) buf_id := buffer.search("PS/2 Mouse\0") @@ -30,8 +30,9 @@ recieve_mouse_event := fn(): ?^MouseEvent { dx := *mem_page dy := *mem_page + 1 mevent := MouseEvent.(dx, dy, 0, 0, 0) - return &mevent + return mevent } + // log.warn("No mouse events\0") return null } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index cf9a000e..0f593285 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -50,15 +50,15 @@ main := fn(): int { render.clear(screen, render.black) // TODO: Read the window buffer here - { - ret := buffer.recv([u8; 4096], win_buff, mem_buf) - // for some reason this null check causes the compiler to spin forever - // if ret == null { - // log.info("No messages\0") - // } else { - // log.info("Handle Messages\0") - // } - } + // { + // ret := buffer.recv([u8; 4096], win_buff, mem_buf) + // // for some reason this null check causes the compiler to spin forever + // // if ret == null { + // // log.info("No messages\0") + // // } else { + // // log.info("Handle Messages\0") + // // } + // } { // get input events from drivers via intouch From 7f01b0e0f8a7d3fba371ca7b12c99732475aab1c Mon Sep 17 00:00:00 2001 From: Able Date: Thu, 7 Nov 2024 09:57:50 -0600 Subject: [PATCH 50/66] :thumbsup: --- sysdata/programs/horizon/src/main.hb | 1 + sysdata/programs/test_abc/README.md | 1 + sysdata/programs/test_abc/meta.toml | 11 +++++++++++ sysdata/programs/test_abc/src/main.hb | 19 +++++++++++++++++++ sysdata/system_config.toml | 11 +++++++---- 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 sysdata/programs/test_abc/README.md create mode 100644 sysdata/programs/test_abc/meta.toml create mode 100644 sysdata/programs/test_abc/src/main.hb diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 0f593285..8afd4f6b 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -65,6 +65,7 @@ main := fn(): int { // key_event := intouch.recieve_key_event() mouse_event := intouch.recieve_mouse_event() if mouse_event != null { + log.info("Mouse event") mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change set_label_text(text_label, "Mouse Moved\0") diff --git a/sysdata/programs/test_abc/README.md b/sysdata/programs/test_abc/README.md new file mode 100644 index 00000000..f569e632 --- /dev/null +++ b/sysdata/programs/test_abc/README.md @@ -0,0 +1 @@ +# test_abc \ No newline at end of file diff --git a/sysdata/programs/test_abc/meta.toml b/sysdata/programs/test_abc/meta.toml new file mode 100644 index 00000000..45bb1221 --- /dev/null +++ b/sysdata/programs/test_abc/meta.toml @@ -0,0 +1,11 @@ +[package] +name = "test_abc" +authors = [""] + +[dependants.libraries] + +[dependants.binaries] +hblang.version = "1.0.0" + +[build] +command = "hblang src/main.hb" diff --git a/sysdata/programs/test_abc/src/main.hb b/sysdata/programs/test_abc/src/main.hb new file mode 100644 index 00000000..f00414c0 --- /dev/null +++ b/sysdata/programs/test_abc/src/main.hb @@ -0,0 +1,19 @@ +stn := @use("../../../libraries/stn/src/lib.hb"); +.{log} := stn + +Structure := struct {} + +returner_fn := fn(): ?Structure { + structure := Structure.() + return structure +} + +main := fn(): int { + ret := returner_fn() + if ret != null { + log.info("not null\0") + return 1 + } + + return 0 +} \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index b1d8a651..082f3611 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -37,8 +37,8 @@ resolution = "1024x768x24" # [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" @@ -58,8 +58,11 @@ path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -[boot.limine.ableos.modules.ps2_mouse_driver] -path = "boot:///ps2_mouse_driver.hbf" +# [boot.limine.ableos.modules.ps2_mouse_driver] +# path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" + +# [boot.limine.ableos.modules.test_abc] +# path = "boot:///test_abc.hbf" From adbf32d970c27adddd0c2c2005bd5939ae1accc0 Mon Sep 17 00:00:00 2001 From: koniifer Date: Fri, 8 Nov 2024 01:48:17 +0000 Subject: [PATCH 51/66] text editor work and misc changes --- Cargo.lock | 147 +---------- kernel/Cargo.toml | 4 - repbuild/Cargo.toml | 24 +- sysdata/libraries/stn/src/assets/sin_table | Bin 728 -> 0 bytes .../stn/src/assets/this-is-temporary | 0 sysdata/libraries/stn/src/math.hb | 2 - .../render_example/src/examples/text.hb | 232 ++++++++---------- sysdata/programs/render_example/src/main.hb | 2 +- sysdata/system_config.toml | 4 +- 9 files changed, 108 insertions(+), 307 deletions(-) delete mode 100644 sysdata/libraries/stn/src/assets/sin_table delete mode 100644 sysdata/libraries/stn/src/assets/this-is-temporary diff --git a/Cargo.lock b/Cargo.lock index fb7bb7e9..7172fee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,21 +17,6 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.92" @@ -110,26 +95,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -208,7 +173,6 @@ checksum = "05669f8e7e2d7badc545c513710f0eba09c2fbef683eb859fd79c46c355048e0" dependencies = [ "bitflags 1.3.2", "byteorder", - "chrono", "log", ] @@ -264,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -281,34 +245,11 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#513d2c71276441d1951281eef0f27d5f94d1f738" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" dependencies = [ "hbbytecode", ] -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "1.5.0" @@ -458,15 +399,6 @@ dependencies = [ "hashbrown 0.15.1", ] -[[package]] -name = "js-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kernel" version = "0.2.0" @@ -567,15 +499,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.20.2" @@ -986,61 +909,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - [[package]] name = "webpki-roots" version = "0.26.6" @@ -1050,15 +918,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.52.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 3f7e4bcb..9cbe8956 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -24,10 +24,6 @@ version = "0.3" default-features = false features = ["alloc", "nightly"] -# [dependencies.clparse] -# git = "https://git.ablecorp.us/ableos/ableos_userland" -# default-features = false - [dependencies.derive_more] version = "1" default-features = false diff --git a/repbuild/Cargo.toml b/repbuild/Cargo.toml index a681d458..2b7fc845 100644 --- a/repbuild/Cargo.toml +++ b/repbuild/Cargo.toml @@ -6,29 +6,15 @@ edition = "2021" [dependencies] str-reader = "0.1" derive_more = { version = "1", default-features = false, features = [ - "add", - "add_assign", - "constructor", "display", - "from", - "into", - "mul", - "mul_assign", - "not", - "sum", ] } error-stack = "0.5" -fatfs = "0.3" -toml = "0.8" -hblang = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ +fatfs = { version = "0.3", default-features = false, features = [ "std", - # "opts", -], default-features = false } + "alloc", +] } +toml = "0.8" +hblang.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git" log = "0.4" raw-cpuid = "11" ureq = { version = "2", default-features = false, features = ["tls"] } - -# [dependencies.reqwest] -# version = "0.12" -# default-features = false -# features = ["rustls-tls", "blocking"] diff --git a/sysdata/libraries/stn/src/assets/sin_table b/sysdata/libraries/stn/src/assets/sin_table deleted file mode 100644 index 0d1c9d8b1737b6f0e24ea094333419297756bd95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 728 zcmXZUJxc;m7(ijO4>L{8vNBCg%b+ocen8M)DA1|Jp`oFmKw6v{S{hthirQ@`h^R>l zY-tLjIS7KF7HPEMoVz^tyzk`(0-Tms`?y4cc&D{~IyHc8-5I2_L)g*15S<^!XWb3^ zegq$NJ3=R;c&pV>x~CU0dM%D0+DOoaF?`UCB>k&L<8*EUuXQ~|ziDlf?rLL-j!t7! zGimx=KS6wPHKhzh^l$h7_ vO1ru`&wgKT^he7J>??Y#_xh>vGVfM&QxCMMEq&5={n9@TF7jVkW17%^E6g+* diff --git a/sysdata/libraries/stn/src/assets/this-is-temporary b/sysdata/libraries/stn/src/assets/this-is-temporary deleted file mode 100644 index e69de29b..00000000 diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index 9d0910de..64ba6b82 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -33,8 +33,6 @@ Vec2 := fn($Expr: type): type { /* source: https://github.com/baker-Xie/FastMath/blob/master/include/fast_math.h */ -// ! heavily broken, possibly due to compiler bug... or skill issues - PI := 3.14159265358979323846 SIN_TABLE := [f32].(0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245) diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index d0490345..67b9d9d0 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -1,19 +1,42 @@ .{memory, log, string} := @use("../../../../libraries/stn/src/lib.hb") render := @use("../../../../libraries/render/src/lib.hb") -/* expected result: - trash notepad app, colours should change with r,g,b,w keys */ +/* expected result: almost-not-trash notepad app + very jank + ----------------- + features: + - basic keys + - holding support with DAS + - visible cursor + - shift key support +*/ psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") -send_byte := fn(byte: u8): u8 { - memory.outb(96, byte) - return memory.inb(96) +handle_char := fn(char: u8, cursor: ^u8, buf: ^u8): ^u8 { + if char == 0 { + return cursor + } + if char != 0x8 { + *cursor = char + return cursor + 1 + } else if char == 0xA { + *cursor = 32 + cursor += 1; + *cursor = 92 + return cursor + 1 + } else if cursor > buf { + cursor -= 1; + *cursor = 32 + return cursor + } + return cursor } example := fn(): void { screen := render.init(true) - font := render.text.font_from_psf2(@bitcast(&psf), false) + window := render.new_surface(480, 340) + font := render.text.font_from_psf2(@bitcast(&psf), true) msg := "ableboard (tm) (patent pending):\n\0" msg_len := string.length(msg) @@ -25,147 +48,86 @@ example := fn(): void { if font == null { return - } + }; - _ = send_byte(238) - log.info("PS/2 Driver Loaded\0") - if send_byte(238) == 238 { - log.info("PS/2 Keyboard Echoed\0") - } - if send_byte(244) == 250 { - log.info("Enabled scanning\0") - } + *cursor = 95 + draw(screen, window, font, buf); + *cursor = 0 - color := render.white + memory.outb(96, 238) + memory.outb(96, 238) + memory.outb(96, 244) + + prev_input := @as(u8, 0xFF) + current_key := @as(u8, 0) + holding_timer := @as(u32, 0) + is_shift_pressed := @as(bool, false) + + initial_delay := @as(u32, 50) + repeat_delay := @as(u32, 7) - prev_input := 250 loop { input := memory.inb(96) - if input != prev_input { - prev_input = input - char := map_keys(input) - if char != 0 { - if char == 0xA { - *cursor = 32 - cursor += 1; - *cursor = 92 - cursor += 1 - } else if char == 0x72 { - color = render.red - } else if char == 0x67 { - color = render.green - } else if char == 0x62 { - color = render.blue - } else if char == 0x77 { - color = render.white - } else if char == 0xE { - cursor -= 1; - *cursor = 32 + is_release := (input & 0x80) != 0 + key_code := input & 0x7F - continue - }; - *cursor = char - cursor += 1 + if input != prev_input { + if is_release { + if key_code == current_key { + current_key = 0 + holding_timer = 0 + } else if input == 0xAA | input == 0xB6 { + is_shift_pressed = false + } + } else { + if input == 0x2A | input == 0x36 { + is_shift_pressed = true + } else { + current_key = input + holding_timer = 1 + cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + } + } + prev_input = input + } + + if current_key != 0 & holding_timer > 0 { + holding_timer += 1 + + if holding_timer >= initial_delay { + cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + holding_timer = initial_delay - repeat_delay + } + }; + *cursor = 95 + draw(screen, window, font, buf); + *cursor = 0 + + if holding_timer > 0 & current_key != 0 { + if (memory.inb(96) & 0x80) != 0 { + current_key = 0 + holding_timer = 0 } } - render.clear(screen, render.black) - render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - render.put_text(screen, font, .(0, 0), color, buf) - - render.sync(screen) } return } -map_keys := fn(inp: u8): u8 { - if inp == 0x29 { - return 0x7E - } else if inp == 0x2 { - return 0x31 - } else if inp == 0x3 { - return 0x32 - } else if inp == 0x4 { - return 0x33 - } else if inp == 0x5 { - return 0x34 - } else if inp == 0x6 { - return 0x35 - } else if inp == 0x7 { - return 0x36 - } else if inp == 0x8 { - return 0x37 - } else if inp == 0x9 { - return 0x38 - } else if inp == 0xA { - return 0x39 - } else if inp == 0xB { - return 0x30 - } else if inp == 0xC { - return 0x2D - } else if inp == 0xD { - return 0x3D - } else if inp == 0xE { - return 0xE - } else if inp == 0x1C { - return 0xA - } else if inp == 0x10 { - return 0x71 - } else if inp == 0x11 { - return 0x77 - } else if inp == 0x12 { - return 0x65 - } else if inp == 0x13 { - return 0x72 - } else if inp == 0x14 { - return 0x74 - } else if inp == 0x15 { - return 0x79 - } else if inp == 0x16 { - return 0x75 - } else if inp == 0x17 { - return 0x69 - } else if inp == 0x18 { - return 0x6F - } else if inp == 0x19 { - return 0x70 - } else if inp == 0x1E { - return 0x61 - } else if inp == 0x1F { - return 0x73 - } else if inp == 0x20 { - return 0x64 - } else if inp == 0x21 { - return 0x66 - } else if inp == 0x22 { - return 0x67 - } else if inp == 0x23 { - return 0x68 - } else if inp == 0x24 { - return 0x6A - } else if inp == 0x25 { - return 0x6B - } else if inp == 0x26 { - return 0x6C - } else if inp == 0x2C { - return 0x7A - } else if inp == 0x2D { - return 0x78 - } else if inp == 0x2E { - return 0x63 - } else if inp == 0x2F { - return 0x76 - } else if inp == 0x30 { - return 0x62 - } else if inp == 0x31 { - return 0x6E - } else if inp == 0x32 { - return 0x6D - } else if inp == 0x2B { - return 0x5C - } else if inp == 0x39 { - return 32 - } else { - return 0 +draw := fn(screen: render.Surface, window: render.Surface, font: render.text.Font, buf: ^u8): void { + render.clear(screen, render.black) + render.clear(window, render.black) + render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) + render.put_text(window, font, .(7, 7), render.gray, buf) + render.put_surface(screen, window, .(100, 100), false) + render.sync(screen) +} + +ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +map_keys := fn(scancode: u8, shift: bool): u8 { + if shift { + return ps2_table[scancode + 0x40] } + return ps2_table[scancode] } \ 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 06644f71..77cb04c0 100644 --- a/sysdata/programs/render_example/src/main.hb +++ b/sysdata/programs/render_example/src/main.hb @@ -1 +1 @@ -.{example: main} := @use("./examples/image.hb") \ No newline at end of file +.{example: main} := @use("./examples/text.hb") \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 082f3611..34b3a7ba 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" From 444fcdb0c4d5468f1d65536daa0b478147760cc1 Mon Sep 17 00:00:00 2001 From: koniifer Date: Fri, 8 Nov 2024 13:47:24 +0000 Subject: [PATCH 52/66] more text editor work --- Cargo.lock | 18 +-- sysdata/libraries/render/src/software.hb | 4 +- sysdata/libraries/stn/src/memory.hb | 4 +- .../render_example/src/examples/text.hb | 124 ++++++++++-------- 4 files changed, 84 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7172fee1..bdd8f050 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anyhow" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "autocfg" @@ -82,9 +82,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.1.36" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "shlex", ] @@ -228,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#0374848b283f29547625c76e7629d5d61bac3109" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" dependencies = [ "hbbytecode", ] @@ -428,9 +428,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "limine" diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb index 8f158430..7b9c6c12 100644 --- a/sysdata/libraries/render/src/software.hb +++ b/sysdata/libraries/render/src/software.hb @@ -4,7 +4,7 @@ .{Vec2} := math // safety: don't use before init() or you will get a memory access violation -framebuffer := memory.dangling(^Color) +framebuffer := memory.dangling(Color) Surface := struct { buf: ^Color, @@ -266,7 +266,7 @@ put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: current_char := str loop if *current_char == 0 break else { - glyph_data := memory.dangling(^u8) + glyph_data := memory.dangling(u8) if font.unicode != null { code_point := @as(uint, 0) diff --git a/sysdata/libraries/stn/src/memory.hb b/sysdata/libraries/stn/src/memory.hb index f133e63b..f0b951ab 100644 --- a/sysdata/libraries/stn/src/memory.hb +++ b/sysdata/libraries/stn/src/memory.hb @@ -2,8 +2,8 @@ PAGE_SIZE := 4096 MAX_ALLOC := 0xFF MAX_FREE := 0xFF -dangling := fn($Expr: type): Expr { - return @bitcast(@sizeof(Expr)) +dangling := fn($Expr: type): ^Expr { + return @bitcast(@alignof(Expr)) } calc_pages := fn($Expr: type, num: uint): uint { diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 67b9d9d0..71b4d50d 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -13,46 +13,34 @@ render := @use("../../../../libraries/render/src/lib.hb") psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") -handle_char := fn(char: u8, cursor: ^u8, buf: ^u8): ^u8 { - if char == 0 { - return cursor - } - if char != 0x8 { - *cursor = char - return cursor + 1 - } else if char == 0xA { - *cursor = 32 - cursor += 1; - *cursor = 92 - return cursor + 1 - } else if cursor > buf { - cursor -= 1; - *cursor = 32 - return cursor - } - return cursor -} +is_shift_pressed := false +is_ctrl_pressed := false +$initial_delay := 50 +$repeat_delay := 7 example := fn(): void { screen := render.init(true) window := render.new_surface(480, 340) - font := render.text.font_from_psf2(@bitcast(&psf), true) + font := render.text.font_from_psf2(@bitcast(&psf), false) - msg := "ableboard (tm) (patent pending):\n\0" + if font == null { + return + } + + msg := "sticky note:\n\0" msg_len := string.length(msg) buf := memory.alloc(u8, 4096) - @inline(memory.copy, u8, msg, buf, msg_len) - cursor := buf + msg_len + bottom := buf + msg_len - if font == null { - return - }; + @inline(memory.copy, u8, msg, buf, msg_len) + cursor := buf + msg_len; *cursor = 95 - draw(screen, window, font, buf); - *cursor = 0 + draw_window(window, font, buf) + draw_screen(screen, window); + *cursor = 32 memory.outb(96, 238) memory.outb(96, 238) @@ -60,32 +48,29 @@ example := fn(): void { prev_input := @as(u8, 0xFF) current_key := @as(u8, 0) - holding_timer := @as(u32, 0) - is_shift_pressed := @as(bool, false) - - initial_delay := @as(u32, 50) - repeat_delay := @as(u32, 7) + holding_timer := 0 loop { input := memory.inb(96) - is_release := (input & 0x80) != 0 - key_code := input & 0x7F - if input != prev_input { - if is_release { - if key_code == current_key { + if (input & 0x80) != 0 { + if (input & 0x7F) == current_key { current_key = 0 holding_timer = 0 } else if input == 0xAA | input == 0xB6 { is_shift_pressed = false + } else if input == 0x9D { + is_ctrl_pressed = false } } else { if input == 0x2A | input == 0x36 { is_shift_pressed = true + } else if input == 0x1D { + is_ctrl_pressed = true } else { current_key = input holding_timer = 1 - cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + cursor = handle_char(map_keys(current_key), cursor, bottom) } } prev_input = input @@ -95,13 +80,14 @@ example := fn(): void { holding_timer += 1 if holding_timer >= initial_delay { - cursor = handle_char(map_keys(current_key, is_shift_pressed), cursor, buf) + cursor = handle_char(map_keys(current_key), cursor, bottom) holding_timer = initial_delay - repeat_delay } }; *cursor = 95 - draw(screen, window, font, buf); - *cursor = 0 + draw_window(window, font, buf) + draw_screen(screen, window); + *cursor = 32 if holding_timer > 0 & current_key != 0 { if (memory.inb(96) & 0x80) != 0 { @@ -114,20 +100,52 @@ example := fn(): void { return } -draw := fn(screen: render.Surface, window: render.Surface, font: render.text.Font, buf: ^u8): void { - render.clear(screen, render.black) - render.clear(window, render.black) - render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.white) - render.put_text(window, font, .(7, 7), render.gray, buf) - render.put_surface(screen, window, .(100, 100), false) - render.sync(screen) +handle_char := fn(char: u8, cursor: ^u8, bottom: ^u8): ^u8 { + if char == 0 { + return cursor + } + if is_ctrl_pressed & char == 48 { + cursor = bottom + } else if char != 0x8 { + *cursor = char + return cursor + 1 + } else if char == 0xA { + *cursor = 32 + cursor += 1; + *cursor = 92 + return cursor + 1 + } else if cursor > bottom { + cursor -= 1; + *cursor = 32 + return cursor + } + return cursor } -ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -map_keys := fn(scancode: u8, shift: bool): u8 { - if shift { +map_keys := fn(scancode: u8): u8 { + if is_shift_pressed { return ps2_table[scancode + 0x40] } return ps2_table[scancode] +} + +ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) +$padding := 7 + +draw_window := fn(window: render.Surface, font: render.text.Font, buf: ^u8): void { + render.clear(window, render.light_yellow) + line := font.height + font.line_gap + padding - 1 + render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.black) + loop if line >= window.height break else { + render.put_hline(window, line, padding, window.width - padding, render.yellow) + line += font.height + font.line_gap + } + + render.put_text(window, font, .(padding, padding), render.black, buf) +} + +draw_screen := fn(screen: render.Surface, window: render.Surface): void { + render.clear(screen, render.light_blue) + render.put_surface(screen, window, .(100, 100), false) + render.sync(screen) } \ No newline at end of file From 2676bd7b62f1f43c67aeb450769ff1d9e2e5aaff Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Fri, 8 Nov 2024 15:04:10 +0100 Subject: [PATCH 53/66] fixing error messages --- repbuild/src/dev.rs | 31 ++++++++++--------- repbuild/src/main.rs | 21 ++++++++++++- .../render_example/src/examples/text.hb | 2 +- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/repbuild/src/dev.rs b/repbuild/src/dev.rs index f93edd15..91c46447 100644 --- a/repbuild/src/dev.rs +++ b/repbuild/src/dev.rs @@ -62,12 +62,11 @@ impl Package { build_cmd, } } - pub fn build(&self) -> std::io::Result<()> { + pub fn build(&self, out: &mut Vec) -> std::io::Result<()> { if self.binaries.contains(&"hblang".to_string()) { let file = self.build_cmd.split_ascii_whitespace().last().unwrap(); let path = format!("sysdata/programs/{}/{}", self.name, file); - let mut bytes = Vec::new(); // compile here hblang::run_compiler( @@ -76,15 +75,7 @@ impl Package { fmt: true, ..Default::default() }, - &mut bytes, - )?; - - hblang::run_compiler( - &path, - Options { - ..Default::default() - }, - &mut bytes, + out, )?; match std::fs::create_dir("target/programs") { @@ -92,17 +83,27 @@ impl Package { Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => (), Err(e) => panic!("{}", e), } - std::fs::write(format!("target/programs/{}.hbf", self.name), &bytes).unwrap(); - bytes.clear(); + + hblang::run_compiler( + &path, + Options { + ..Default::default() + }, + out, + )?; + std::fs::write(format!("target/programs/{}.hbf", self.name), &out)?; + out.clear(); + hblang::run_compiler( &path, Options { dump_asm: true, ..Default::default() }, - &mut bytes, + out, )?; - std::fs::write(format!("target/programs/{}.hba", self.name), &bytes).unwrap(); + std::fs::write(format!("target/programs/{}.hba", self.name), &out)?; + out.clear(); } Ok(()) } diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 19f2d381..898d1f74 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -1,6 +1,7 @@ mod dev; use { + core::fmt::Write as _, derive_more::Display, dev::Package, error_stack::{bail, report, Context, Report, Result, ResultExt}, @@ -204,6 +205,9 @@ TERM_BACKDROP={} let modules = value.get_mut("modules").unwrap().as_table_mut().unwrap(); // let mut real_modules = modules.clone(); + let mut errors = String::new(); + let mut out = Vec::new(); + modules .into_iter() .map(|(_, value)| -> Result<(), io::Error> { @@ -218,11 +222,26 @@ TERM_BACKDROP={} let p = Package::load_from_file( format!("sysdata/programs/{}/meta.toml", path).to_owned(), ); - p.build()?; + match p.build(&mut out) { + Ok(()) => {} + Err(_) => { + writeln!(errors, "========= while compiling {} =========", path) + .unwrap(); + errors.push_str(core::str::from_utf8(&out).expect("no")); + out.clear(); + } + } } Ok(()) }) .for_each(drop); + + if !errors.is_empty() { + writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!"); + std::eprint!("{errors}"); + continue; + } + modules.into_iter().for_each(|(_key, value)| { if value.is_table() { let path = value.get("path").expect("You must have `path` as a value"); diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 71b4d50d..2f2c2993 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -148,4 +148,4 @@ draw_screen := fn(screen: render.Surface, window: render.Surface): void { render.clear(screen, render.light_blue) render.put_surface(screen, window, .(100, 100), false) render.sync(screen) -} \ No newline at end of file +} From d2b5f0951125aee1ff063eb07cad6781023c3bb9 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 8 Nov 2024 10:16:24 -0600 Subject: [PATCH 54/66] changes + lui examples --- repbuild/src/main.rs | 2 +- .../libraries/horizon_api/examples/label.lui | 1 + .../horizon_api/examples/vertical.lui | 3 ++ sysdata/libraries/horizon_api/src/ui.hb | 7 +-- .../horizon_api/src/widgets/widgets.hb | 1 + sysdata/libraries/intouch/src/lib.hb | 5 +- sysdata/programs/horizon/src/main.hb | 46 +++++++++++++------ sysdata/programs/ps2_mouse_driver/src/main.hb | 12 +++-- sysdata/system_config.toml | 12 ++--- 9 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 sysdata/libraries/horizon_api/examples/label.lui create mode 100644 sysdata/libraries/horizon_api/examples/vertical.lui diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 898d1f74..05522e20 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -237,7 +237,7 @@ TERM_BACKDROP={} .for_each(drop); if !errors.is_empty() { - writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!"); + let _ = writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!"); std::eprint!("{errors}"); continue; } diff --git a/sysdata/libraries/horizon_api/examples/label.lui b/sysdata/libraries/horizon_api/examples/label.lui new file mode 100644 index 00000000..6db625c9 --- /dev/null +++ b/sysdata/libraries/horizon_api/examples/label.lui @@ -0,0 +1 @@ +(label "hello") diff --git a/sysdata/libraries/horizon_api/examples/vertical.lui b/sysdata/libraries/horizon_api/examples/vertical.lui new file mode 100644 index 00000000..ee6a4013 --- /dev/null +++ b/sysdata/libraries/horizon_api/examples/vertical.lui @@ -0,0 +1,3 @@ +(vertical + (label "hello") + (label "hello" color:red)) \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/ui.hb b/sysdata/libraries/horizon_api/src/ui.hb index 93fae2d8..e6512a1d 100644 --- a/sysdata/libraries/horizon_api/src/ui.hb +++ b/sysdata/libraries/horizon_api/src/ui.hb @@ -6,11 +6,8 @@ render := @use("../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text -UI := struct { - raw: ^u8, - raw_length: uint, - is_dirty: bool, - surface: Surface, +UI := struct {raw: ^u8, raw_length: uint, is_dirty: bool, surface: Surface, // Each child has their WidgetType as their first byte +// children: ^^u8, } render_ui := fn(surface: Surface, ui: UI): void { diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb index 2cda29ab..360b9943 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -23,6 +23,7 @@ Vertical := packed struct { magic: uint, // array of children, idk // use a vec or linked list or whatever + children: ^^u8, } diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index b6d751ca..ebfde1bf 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -27,12 +27,13 @@ recieve_mouse_event := fn(): ?MouseEvent { // Read out of the Mouse buffer here buffer.recv(MouseEvent, buf_id, mem_page) if *mem_page != 0 { + log.info("Mouse events\0") dx := *mem_page dy := *mem_page + 1 mevent := MouseEvent.(dx, dy, 0, 0, 0) return mevent } - // log.warn("No mouse events\0") - return null + // log.error("No mouse events\0") + return MouseEvent.(0, 0, 0, 0, 0) } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 8afd4f6b..33d3185b 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -9,6 +9,7 @@ horizon_api := @use("../../../libraries/horizon_api/src/lib.hb"); render := @use("../../../libraries/render/src/lib.hb"); .{Surface} := render; .{Font} := render.text + intouch := @use("../../../libraries/intouch/src/lib.hb") Window := struct { @@ -42,30 +43,45 @@ main := fn(): int { mouse_x := 0 mouse_y := 0 text_label := new_label("Hi\0") - widgets := "()\0" - ui := sexpr_parser(widgets) + // widgets := "()\0" + // ui := sexpr_parser(widgets) + + mouse_event := intouch.recieve_mouse_event() + + if mouse_event == null { + log.warn("null\0") + } else { + log.warn("not null\0") + } loop { // Clear the screen render.clear(screen, render.black) // TODO: Read the window buffer here - // { - // ret := buffer.recv([u8; 4096], win_buff, mem_buf) - // // for some reason this null check causes the compiler to spin forever - // // if ret == null { - // // log.info("No messages\0") - // // } else { - // // log.info("Handle Messages\0") - // // } - // } - { - // get input events from drivers via intouch - // key_event := intouch.recieve_key_event() + ret := buffer.recv([u8; 4096], win_buff, mem_buf) + // for some reason this null check causes the compiler to spin forever + // if ret == null { + // log.info("No messages\0") + // } else { + // log.info("Handle Messages\0") + // } + } + + // get input events from drivers via intouch + // key_event := intouch.recieve_key_event(); + // log.info("before mouse event check\0"); + { + // Note: MLokis, this inline halts the compiler forever + // mouse_event := @inline(intouch.recieve_mouse_event) + // Note: MLokis, this function returns null unless the mouse is moving mouse_event := intouch.recieve_mouse_event() + // + if mouse_event != null { - log.info("Mouse event") + log.warn("Mouse event recieved\0") + mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change set_label_text(text_label, "Mouse Moved\0") diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 82c849ee..1f66adad 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -13,13 +13,13 @@ Button4 := Button.(8) Button5 := Button.(16) mouse_moved := fn(delta: Vec2(i9)): void { - log.info("Mouse movement.\0") + // log.info("Mouse movement.\0") } button_event := fn(button: Button, pressed: bool): void { if pressed { - log.info("Mouse-button pressed.\0") + // log.info("Mouse-button pressed.\0") } else { - log.info("Mouse-button released.\0") + // log.info("Mouse-button released.\0") } } @@ -132,7 +132,7 @@ main := fn(): int { button_states ^= changes - log.info(string.display_int(status, format_page, 10)) + // log.info(string.display_int(status, format_page, 10)) dx := i9.(false, 0) dy := i9.(false, 0) @@ -144,7 +144,9 @@ main := fn(): int { dy.sign = (status & 0x20) == 0 if dy.value != 0 & dx.value != 0 { - event := MouseEvent.(dx.value, dy.value, 0, 0, 0) + y_change := dy.value + x_change := dx.value + event := MouseEvent.(x_change, y_change, 0, 0, 0) buffer.write(MouseEvent, &event, mouse_buffer) // mouse_moved(.(dx, dy)) diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 34b3a7ba..dfe4619d 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,8 +37,8 @@ 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" @@ -58,8 +58,8 @@ path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -# [boot.limine.ableos.modules.ps2_mouse_driver] -# path = "boot:///ps2_mouse_driver.hbf" +[boot.limine.ableos.modules.ps2_mouse_driver] +path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 818bcb458f2d4073d6b5aca1a59fc8394c62ebb3 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 8 Nov 2024 10:29:06 -0600 Subject: [PATCH 55/66] widgets --- sysdata/libraries/horizon_api/examples/horizontal.lui | 4 ++++ sysdata/libraries/horizon_api/src/widgets/image.hb | 5 +++++ sysdata/libraries/horizon_api/src/widgets/widget_types.hb | 5 ++++- sysdata/libraries/horizon_api/src/widgets/widgets.hb | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 sysdata/libraries/horizon_api/examples/horizontal.lui create mode 100644 sysdata/libraries/horizon_api/src/widgets/image.hb diff --git a/sysdata/libraries/horizon_api/examples/horizontal.lui b/sysdata/libraries/horizon_api/examples/horizontal.lui new file mode 100644 index 00000000..89547809 --- /dev/null +++ b/sysdata/libraries/horizon_api/examples/horizontal.lui @@ -0,0 +1,4 @@ +(horizontal + spacing : 10 + (label "hi") + (label "goodbye")) \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/image.hb b/sysdata/libraries/horizon_api/src/widgets/image.hb new file mode 100644 index 00000000..faa0d995 --- /dev/null +++ b/sysdata/libraries/horizon_api/src/widgets/image.hb @@ -0,0 +1,5 @@ +Image := struct { + magic: uint, + is_dirty: bool, + surface: Surface, +} \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widget_types.hb b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb index 9755ec8b..97ddcb80 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widget_types.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widget_types.hb @@ -1,4 +1,7 @@ +NoWidget := 0 + VerticalWidgetType := 1 HorizontalWidgetType := 2 -LabelWidgetType := 3 \ No newline at end of file +LabelWidgetType := 3 +ImageWidgetType := 4 \ No newline at end of file diff --git a/sysdata/libraries/horizon_api/src/widgets/widgets.hb b/sysdata/libraries/horizon_api/src/widgets/widgets.hb index 360b9943..cb15cbbb 100644 --- a/sysdata/libraries/horizon_api/src/widgets/widgets.hb +++ b/sysdata/libraries/horizon_api/src/widgets/widgets.hb @@ -11,6 +11,7 @@ render := @use("../../../../libraries/render/src/lib.hb"); widget_types := @use("widget_types.hb") label := @use("label.hb") +image := @use("image.hb") Size := struct { min_width: int, From 1345f294b849e7f11cbafede8c1845433ba27b70 Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 8 Nov 2024 14:42:58 -0600 Subject: [PATCH 56/66] changes --- Cargo.lock | 6 +++--- sysdata/libraries/horizon_api/src/widgets/image.hb | 10 +++++----- sysdata/programs/horizon/src/main.hb | 8 -------- sysdata/system_config.toml | 12 ++++++------ 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bdd8f050..d2ffccca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#29367d8f8bdfc23d8662a74edcecf859285ce265" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#29367d8f8bdfc23d8662a74edcecf859285ce265" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#a299bad75b068f565e6e10b6c3501a9422e283c4" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#29367d8f8bdfc23d8662a74edcecf859285ce265" dependencies = [ "hbbytecode", ] diff --git a/sysdata/libraries/horizon_api/src/widgets/image.hb b/sysdata/libraries/horizon_api/src/widgets/image.hb index faa0d995..67e53915 100644 --- a/sysdata/libraries/horizon_api/src/widgets/image.hb +++ b/sysdata/libraries/horizon_api/src/widgets/image.hb @@ -1,5 +1,5 @@ -Image := struct { - magic: uint, - is_dirty: bool, - surface: Surface, -} \ No newline at end of file +// Image := struct { +// magic: uint, +// is_dirty: bool, +// surface: Surface, +// } \ No newline at end of file diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 33d3185b..5ac1a3ac 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -46,14 +46,6 @@ main := fn(): int { // widgets := "()\0" // ui := sexpr_parser(widgets) - mouse_event := intouch.recieve_mouse_event() - - if mouse_event == null { - log.warn("null\0") - } else { - log.warn("not null\0") - } - loop { // Clear the screen render.clear(screen, render.black) diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index dfe4619d..34b3a7ba 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -28,8 +28,8 @@ resolution = "1024x768x24" # [boot.limine.ableos.modules.diskio_driver] # path = "boot:///diskio_driver.hbf" -# [boot.limine.ableos.modules.render_example] -# path = "boot:///render_example.hbf" +[boot.limine.ableos.modules.render_example] +path = "boot:///render_example.hbf" # [boot.limine.ableos.modules.serial_driver] # path = "boot:///serial_driver.hbf" @@ -37,8 +37,8 @@ resolution = "1024x768x24" # [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" @@ -58,8 +58,8 @@ path = "boot:///horizon.hbf" # [boot.limine.ableos.modules.pumpkin_print] # path = "boot:///pumpkin_print.hbf" -[boot.limine.ableos.modules.ps2_mouse_driver] -path = "boot:///ps2_mouse_driver.hbf" +# [boot.limine.ableos.modules.ps2_mouse_driver] +# path = "boot:///ps2_mouse_driver.hbf" # [boot.limine.ableos.modules.app_bar] # path = "boot:///app_bar.hbf" From 8b04b275f3e110648f65677e3c3f2a7ee82b70e7 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sat, 9 Nov 2024 01:54:33 +0000 Subject: [PATCH 57/66] text editor is actually a text editor --- Cargo.lock | 10 +- .../render_example/src/examples/text.hb | 201 +++++++++++++----- 2 files changed, 149 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2ffccca..f408d391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" [[package]] name = "anyhow" @@ -228,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#29367d8f8bdfc23d8662a74edcecf859285ce265" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#65e9f272a85cc9d46c31072af9d0f2bc43ef1217" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#29367d8f8bdfc23d8662a74edcecf859285ce265" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#65e9f272a85cc9d46c31072af9d0f2bc43ef1217" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#29367d8f8bdfc23d8662a74edcecf859285ce265" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#65e9f272a85cc9d46c31072af9d0f2bc43ef1217" dependencies = [ "hbbytecode", ] diff --git a/sysdata/programs/render_example/src/examples/text.hb b/sysdata/programs/render_example/src/examples/text.hb index 2f2c2993..b9301c7d 100644 --- a/sysdata/programs/render_example/src/examples/text.hb +++ b/sysdata/programs/render_example/src/examples/text.hb @@ -1,13 +1,15 @@ -.{memory, log, string} := @use("../../../../libraries/stn/src/lib.hb") +.{memory, log, string, math} := @use("../../../../libraries/stn/src/lib.hb") render := @use("../../../../libraries/render/src/lib.hb") -/* expected result: almost-not-trash notepad app - very jank +/* expected result: pretty decent notepad app + slightly jank ----------------- features: - basic keys - holding support with DAS - visible cursor + - l+r arrow key support + - proper insertion and deletion - shift key support */ @@ -15,12 +17,17 @@ psf := @embed("../../../../consolefonts/tamsyn/10x20r.psf") is_shift_pressed := false is_ctrl_pressed := false +is_extended := false $initial_delay := 50 $repeat_delay := 7 +$left_arrow := 0x4B +$right_arrow := 0x4D +$up_arrow := 0x48 +$down_arrow := 0x50 example := fn(): void { screen := render.init(true) - window := render.new_surface(480, 340) + window := render.new_surface(600, 300) font := render.text.font_from_psf2(@bitcast(&psf), false) if font == null { @@ -35,12 +42,10 @@ example := fn(): void { bottom := buf + msg_len @inline(memory.copy, u8, msg, buf, msg_len) - cursor := buf + msg_len; + cursor := bottom - *cursor = 95 - draw_window(window, font, buf) - draw_screen(screen, window); - *cursor = 32 + draw_window(window, font, buf, cursor) + draw_screen(screen, window) memory.outb(96, 238) memory.outb(96, 238) @@ -53,24 +58,35 @@ example := fn(): void { loop { input := memory.inb(96) if input != prev_input { - if (input & 0x80) != 0 { - if (input & 0x7F) == current_key { - current_key = 0 - holding_timer = 0 - } else if input == 0xAA | input == 0xB6 { - is_shift_pressed = false - } else if input == 0x9D { - is_ctrl_pressed = false - } + if input == 0xE0 { + is_extended = true } else { - if input == 0x2A | input == 0x36 { - is_shift_pressed = true - } else if input == 0x1D { - is_ctrl_pressed = true + if (input & 0x80) != 0 { + if (input & 0x7F) == current_key { + current_key = 0 + holding_timer = 0 + } else if input == 0xAA | input == 0xB6 { + is_shift_pressed = false + } else if input == 0x9D { + is_ctrl_pressed = false + } + is_extended = false } else { - current_key = input - holding_timer = 1 - cursor = handle_char(map_keys(current_key), cursor, bottom) + if is_extended { + current_key = input + holding_timer = 1 + cursor = handle_extended_key(input, cursor, bottom, font) + } else { + if input == 0x2A | input == 0x36 { + is_shift_pressed = true + } else if input == 0x1D { + is_ctrl_pressed = true + } else { + current_key = input + holding_timer = 1 + cursor = handle_char(map_keys(current_key), cursor, bottom) + } + } } } prev_input = input @@ -80,14 +96,16 @@ example := fn(): void { holding_timer += 1 if holding_timer >= initial_delay { - cursor = handle_char(map_keys(current_key), cursor, bottom) + if is_extended { + cursor = handle_extended_key(current_key, cursor, bottom, font) + } else { + cursor = handle_char(map_keys(current_key), cursor, bottom) + } holding_timer = initial_delay - repeat_delay } - }; - *cursor = 95 - draw_window(window, font, buf) - draw_screen(screen, window); - *cursor = 32 + } + draw_window(window, font, buf, cursor) + draw_screen(screen, window) if holding_timer > 0 & current_key != 0 { if (memory.inb(96) & 0x80) != 0 { @@ -96,17 +114,87 @@ example := fn(): void { } } } +} - return +handle_extended_key := fn(scancode: u8, cursor: ^u8, bottom: ^u8, font: render.text.Font): ^u8 { + if scancode == left_arrow { + if cursor > bottom { + return cursor - 1 + } + } else if scancode == right_arrow { + if *cursor != 0 { + return cursor + 1 + } + } + return cursor +} + +padding := 3 * @sizeof(render.Color) + +draw_window := fn(window: render.Surface, font: render.text.Font, buf: ^u8, cursor: ^u8): void { + render.clear(window, render.light_yellow) + line := font.height + padding - 1 + render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.black) + + loop if line >= window.height break else { + render.put_hline(window, line, padding, window.width - padding, render.yellow) + line += font.height + } + + render.put_text(window, font, .(padding, padding), render.black, buf) + + cursor_offset := cursor - buf + + y_pos := padding + x_pos := padding + i := 0 + + loop if i >= cursor_offset break else { + if *(buf + i) == 10 { + y_pos += font.height + font.line_gap + x_pos = padding + } else { + if x_pos + font.width >= window.width - padding { + y_pos += font.height + font.line_gap + x_pos = padding - font.width + } + x_pos += font.width + } + i += 1 + } + + render.put_rect(window, .(x_pos, y_pos), .(1, font.height - 1), render.black) +} + +draw_screen := fn(screen: render.Surface, window: render.Surface): void { + render.clear(screen, render.light_blue) + render.put_surface(screen, window, .(100, 100), false) + render.sync(screen) } handle_char := fn(char: u8, cursor: ^u8, bottom: ^u8): ^u8 { if char == 0 { return cursor } + if is_ctrl_pressed & char == 48 { cursor = bottom } else if char != 0x8 { + end := cursor + loop if *end == 0 break else { + end += 1 + } + + if cursor < end { + src := end + dst := end + 1 + loop if src < cursor break else { + *dst = *src + dst -= 1 + src -= 1 + } + }; + *cursor = char return cursor + 1 } else if char == 0xA { @@ -115,37 +203,36 @@ handle_char := fn(char: u8, cursor: ^u8, bottom: ^u8): ^u8 { *cursor = 92 return cursor + 1 } else if cursor > bottom { - cursor -= 1; - *cursor = 32 - return cursor + if cursor == bottom { + return cursor + } + + end := cursor + loop if *end == 0 break else { + end += 1 + } + + if cursor < end { + src := cursor + dst := cursor - 1 + loop if src > end break else { + *dst = *src + dst += 1 + src += 1 + } + return cursor - 1 + } else { + cursor -= 1; + *cursor = 32 + return cursor + } } return cursor } - map_keys := fn(scancode: u8): u8 { if is_shift_pressed { return ps2_table[scancode + 0x40] } return ps2_table[scancode] } - -ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -$padding := 7 - -draw_window := fn(window: render.Surface, font: render.text.Font, buf: ^u8): void { - render.clear(window, render.light_yellow) - line := font.height + font.line_gap + padding - 1 - render.put_rect(window, .(0, 0), .(window.width - 1, window.height - 1), render.black) - loop if line >= window.height break else { - render.put_hline(window, line, padding, window.width - padding, render.yellow) - line += font.height + font.line_gap - } - - render.put_text(window, font, .(padding, padding), render.black, buf) -} - -draw_screen := fn(screen: render.Surface, window: render.Surface): void { - render.clear(screen, render.light_blue) - render.put_surface(screen, window, .(100, 100), false) - render.sync(screen) -} +ps2_table := [u8].(0x0, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x8, 0x9, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0xA, 0x0, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x0, 0x5C, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x0, 0x2A, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x8, 0x9, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0xA, 0x0, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x0, 0x7C, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x0, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) \ No newline at end of file From 7723799e765d33e2232b48e770be3fba8dee619e Mon Sep 17 00:00:00 2001 From: Able Date: Sat, 9 Nov 2024 20:44:08 -0600 Subject: [PATCH 58/66] changes or something idk --- sysdata/libraries/intouch/src/events.hb | 4 +-- sysdata/libraries/intouch/src/lib.hb | 6 ++-- sysdata/programs/horizon/src/main.hb | 29 +++++++++---------- sysdata/programs/ps2_mouse_driver/src/main.hb | 6 ++-- sysdata/programs/tests/src/main.hb | 16 ---------- 5 files changed, 20 insertions(+), 41 deletions(-) diff --git a/sysdata/libraries/intouch/src/events.hb b/sysdata/libraries/intouch/src/events.hb index 1d544238..0ede6d5f 100644 --- a/sysdata/libraries/intouch/src/events.hb +++ b/sysdata/libraries/intouch/src/events.hb @@ -12,8 +12,8 @@ KeyEvent := struct { } MouseEvent := struct { - x_change: u8, - y_change: u8, + x_change: i8, + y_change: i8, left: u8, middle: u8, right: u8, diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index ebfde1bf..a0e9b4d7 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -16,7 +16,7 @@ recieve_key_event := fn(): ?KeyEvent { key_event := KeyEvent.(0, 0, 2) // return key_event - return null + // return null } recieve_mouse_event := fn(): ?MouseEvent { @@ -28,8 +28,8 @@ recieve_mouse_event := fn(): ?MouseEvent { buffer.recv(MouseEvent, buf_id, mem_page) if *mem_page != 0 { log.info("Mouse events\0") - dx := *mem_page - dy := *mem_page + 1 + dx := @as(i8, @bitcast(*mem_page)) + dy := @as(i8, @bitcast(*mem_page + 1)) mevent := MouseEvent.(dx, dy, 0, 0, 0) return mevent } diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 5ac1a3ac..624a659b 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -65,14 +65,11 @@ main := fn(): int { // key_event := intouch.recieve_key_event(); // log.info("before mouse event check\0"); { - // Note: MLokis, this inline halts the compiler forever - // mouse_event := @inline(intouch.recieve_mouse_event) - // Note: MLokis, this function returns null unless the mouse is moving mouse_event := intouch.recieve_mouse_event() // if mouse_event != null { - log.warn("Mouse event recieved\0") + // log.warn("Mouse event recieved\0") mouse_x += mouse_event.x_change mouse_y += mouse_event.y_change @@ -85,20 +82,20 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through - render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - { - // Scroll bar :ThumbsUp: - render.put_rect(screen, .(100, 100), .(100, 10), render.white) - render.put_filled_rect(screen, .(110, 100), .(20, 10), render.white) + // render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + // { + // // Scroll bar :ThumbsUp: + // render.put_rect(screen, .(100, 100), .(100, 10), render.white) + // render.put_filled_rect(screen, .(110, 100), .(20, 10), render.white) - render.put_rect(screen, .(90, 110), .(10, 100), render.white) - render.put_filled_rect(screen, .(90, 120), .(10, 20), render.white) - } + // render.put_rect(screen, .(90, 110), .(10, 100), render.white) + // render.put_filled_rect(screen, .(90, 120), .(10, 20), render.white) + // } - { - pos := Vec2(uint).(1, screen.height - 21) - render_label_to_surface(screen, text_label, font, pos) - } + // { + // pos := Vec2(uint).(1, screen.height - 21) + // render_label_to_surface(screen, text_label, font, pos) + // } // Sync the screen render.sync(screen) } diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 1f66adad..5e74c076 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -144,12 +144,10 @@ main := fn(): int { dy.sign = (status & 0x20) == 0 if dy.value != 0 & dx.value != 0 { - y_change := dy.value - x_change := dx.value + y_change := @as(i8, @bitcast(dy.value)) + x_change := @as(i8, @bitcast(dx.value)) event := MouseEvent.(x_change, y_change, 0, 0, 0) buffer.write(MouseEvent, &event, mouse_buffer) - - // mouse_moved(.(dx, dy)) } } diff --git a/sysdata/programs/tests/src/main.hb b/sysdata/programs/tests/src/main.hb index 049e404d..02cc2784 100644 --- a/sysdata/programs/tests/src/main.hb +++ b/sysdata/programs/tests/src/main.hb @@ -1,19 +1,3 @@ -.{log, string, memory, buffer} := @use("../../../libraries/stn/src/lib.hb") - -service_search := fn(): void { - a := "\{01}\0" - @eca(3, 0, a, 2) - - return -} - main := fn(): int { - //service_search() - buf := "\0\0\0\0" - x := @as(int, 0) - loop if x > 255 break else { - log.info(string.display_int(x, buf, 2)) - x += 1 - } return 0 } \ No newline at end of file From d2152537ad8ff3342633a5995780438ddeab2c38 Mon Sep 17 00:00:00 2001 From: Able Date: Sat, 9 Nov 2024 22:34:24 -0600 Subject: [PATCH 59/66] clean up --- sysdata/programs/test_abc/README.md | 1 - sysdata/programs/test_abc/meta.toml | 11 -------- sysdata/programs/test_abc/src/main.hb | 19 ------------- sysdata/programs/tests/meta.toml | 11 -------- sysdata/programs/tests/src/main.hb | 3 -- sysdata/system_config.toml | 40 ++------------------------- 6 files changed, 2 insertions(+), 83 deletions(-) delete mode 100644 sysdata/programs/test_abc/README.md delete mode 100644 sysdata/programs/test_abc/meta.toml delete mode 100644 sysdata/programs/test_abc/src/main.hb delete mode 100644 sysdata/programs/tests/meta.toml delete mode 100644 sysdata/programs/tests/src/main.hb diff --git a/sysdata/programs/test_abc/README.md b/sysdata/programs/test_abc/README.md deleted file mode 100644 index f569e632..00000000 --- a/sysdata/programs/test_abc/README.md +++ /dev/null @@ -1 +0,0 @@ -# test_abc \ No newline at end of file diff --git a/sysdata/programs/test_abc/meta.toml b/sysdata/programs/test_abc/meta.toml deleted file mode 100644 index 45bb1221..00000000 --- a/sysdata/programs/test_abc/meta.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "test_abc" -authors = [""] - -[dependants.libraries] - -[dependants.binaries] -hblang.version = "1.0.0" - -[build] -command = "hblang src/main.hb" diff --git a/sysdata/programs/test_abc/src/main.hb b/sysdata/programs/test_abc/src/main.hb deleted file mode 100644 index f00414c0..00000000 --- a/sysdata/programs/test_abc/src/main.hb +++ /dev/null @@ -1,19 +0,0 @@ -stn := @use("../../../libraries/stn/src/lib.hb"); -.{log} := stn - -Structure := struct {} - -returner_fn := fn(): ?Structure { - structure := Structure.() - return structure -} - -main := fn(): int { - ret := returner_fn() - if ret != null { - log.info("not null\0") - return 1 - } - - return 0 -} \ No newline at end of file diff --git a/sysdata/programs/tests/meta.toml b/sysdata/programs/tests/meta.toml deleted file mode 100644 index a94c6669..00000000 --- a/sysdata/programs/tests/meta.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "tests" -authors = ["able"] - -[dependants.libraries] - -[dependants.binaries] -hblang.version = "1.0.0" - -[build] -command = "hblang src/main.hb" diff --git a/sysdata/programs/tests/src/main.hb b/sysdata/programs/tests/src/main.hb deleted file mode 100644 index 02cc2784..00000000 --- a/sysdata/programs/tests/src/main.hb +++ /dev/null @@ -1,3 +0,0 @@ -main := fn(): int { - return 0 -} \ No newline at end of file diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index 34b3a7ba..c46e0a39 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -22,47 +22,11 @@ resolution = "1024x768x24" [boot.limine.ableos.modules] -# [boot.limine.ableos.modules.tests] -# path = "boot:///tests.hbf" - -# [boot.limine.ableos.modules.diskio_driver] -# path = "boot:///diskio_driver.hbf" - [boot.limine.ableos.modules.render_example] path = "boot:///render_example.hbf" -# [boot.limine.ableos.modules.serial_driver] -# path = "boot:///serial_driver.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_testing_program] -# path = "boot:///horizon_testing_program.hbf" - -# [boot.limine.ableos.modules.dt_buffer_test] -# path = "boot:///dt_buffer_test.hbf" - -# [boot.limine.ableos.modules.svga_driver] -# path = "boot:///svga_driver.hbf" - -# [boot.limine.ableos.modules.ps2_keyboard_driver] -# path = "boot:///ps2_keyboard_driver.hbf" - -# [boot.limine.ableos.modules.filesystem_fat32] -# path = "boot:///filesystem_fat32.hbf" - -# [boot.limine.ableos.modules.pumpkin_print] -# path = "boot:///pumpkin_print.hbf" - -# [boot.limine.ableos.modules.ps2_mouse_driver] -# path = "boot:///ps2_mouse_driver.hbf" - -# [boot.limine.ableos.modules.app_bar] -# path = "boot:///app_bar.hbf" - -# [boot.limine.ableos.modules.test_abc] -# path = "boot:///test_abc.hbf" +[boot.limine.ableos.modules.ps2_mouse_driver] +path = "boot:///ps2_mouse_driver.hbf" From eea23d967bc275b00312d02c902c90cb7dbd95f3 Mon Sep 17 00:00:00 2001 From: Able Date: Sun, 10 Nov 2024 02:36:37 -0600 Subject: [PATCH 60/66] mouse cursor functional --- .cargo/config.toml | 2 +- Cargo.lock | 6 +- repbuild/src/main.rs | 1 + sysdata/libraries/intouch/src/lib.hb | 2 +- sysdata/programs/horizon/src/main.hb | 62 +++++++++++++------ sysdata/programs/ps2_mouse_driver/src/main.hb | 21 ++++--- sysdata/system_config.toml | 8 +-- 7 files changed, 64 insertions(+), 38 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index b3353930..c5b12ffd 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,3 @@ [alias] -repbuild = "run --manifest-path ./repbuild/Cargo.toml -r --" +repbuild = "run --manifest-path ./repbuild/Cargo.toml -r -- " dev = "run --manifest-path ./dev/Cargo.toml -r --" diff --git a/Cargo.lock b/Cargo.lock index f408d391..f776aab8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#65e9f272a85cc9d46c31072af9d0f2bc43ef1217" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#42a713aeaef11ca86d96083915191fbe456c47e5" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#65e9f272a85cc9d46c31072af9d0f2bc43ef1217" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#42a713aeaef11ca86d96083915191fbe456c47e5" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#65e9f272a85cc9d46c31072af9d0f2bc43ef1217" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#42a713aeaef11ca86d96083915191fbe456c47e5" dependencies = [ "hbbytecode", ] diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 05522e20..39569a92 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -421,6 +421,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { "-bios", &ovmf_path.change_context(Error::OvmfFetch)?, "-drive", "file=target/disk.img,format=raw", "-device", "vmware-svga", + // "-serial", "stdio", "-m", "2G", "-smp", "1", "-parallel", "none", diff --git a/sysdata/libraries/intouch/src/lib.hb b/sysdata/libraries/intouch/src/lib.hb index a0e9b4d7..e6a5dc78 100644 --- a/sysdata/libraries/intouch/src/lib.hb +++ b/sysdata/libraries/intouch/src/lib.hb @@ -29,7 +29,7 @@ recieve_mouse_event := fn(): ?MouseEvent { if *mem_page != 0 { log.info("Mouse events\0") dx := @as(i8, @bitcast(*mem_page)) - dy := @as(i8, @bitcast(*mem_page + 1)) + dy := @as(i8, @bitcast(*(mem_page + 1))) mevent := MouseEvent.(dx, dy, 0, 0, 0) return mevent } diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb index 624a659b..d1da4b25 100644 --- a/sysdata/programs/horizon/src/main.hb +++ b/sysdata/programs/horizon/src/main.hb @@ -40,8 +40,8 @@ main := fn(): int { // really we should null check but it is a bit broked font := @unwrap(render.text.font_from_psf2(@bitcast(&psf), false)) - mouse_x := 0 - mouse_y := 0 + mouse_x := @as(i16, 0) + mouse_y := @as(i16, 0) text_label := new_label("Hi\0") // widgets := "()\0" // ui := sexpr_parser(widgets) @@ -52,7 +52,7 @@ main := fn(): int { // TODO: Read the window buffer here { - ret := buffer.recv([u8; 4096], win_buff, mem_buf) + // ret := buffer.recv([u8; 4096], win_buff, mem_buf) // for some reason this null check causes the compiler to spin forever // if ret == null { // log.info("No messages\0") @@ -64,6 +64,22 @@ main := fn(): int { // get input events from drivers via intouch // key_event := intouch.recieve_key_event(); // log.info("before mouse event check\0"); + + render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) + { + // Scroll bar :ThumbsUp: + render.put_rect(screen, .(100, 100), .(100, 10), render.white) + render.put_filled_rect(screen, .(110, 100), .(20, 10), render.white) + + render.put_rect(screen, .(90, 110), .(10, 100), render.white) + render.put_filled_rect(screen, .(90, 120), .(10, 20), render.white) + } + + { + pos := Vec2(uint).(1, screen.height - 21) + render_label_to_surface(screen, text_label, font, pos) + } + { mouse_event := intouch.recieve_mouse_event() // @@ -71,8 +87,30 @@ main := fn(): int { if mouse_event != null { // log.warn("Mouse event recieved\0") - mouse_x += mouse_event.x_change - mouse_y += mouse_event.y_change + change_x := @as(i16, mouse_event.x_change) + change_x = change_x << 8 + change_x = change_x >> 8 + + mouse_x += change_x + if mouse_x < 0 { + mouse_x = 0 + } + if mouse_x >= screen.width - 20 { + mouse_x = @intcast(screen.width - 21) + } + + change_y := @as(i16, mouse_event.y_change) + change_y = change_y << 8 + change_y = change_y >> 8 + + if mouse_y < 0 { + mouse_y = 0 + } + if mouse_y >= screen.height - 20 { + mouse_y = @intcast(screen.height - 21) + } + mouse_y -= change_y + set_label_text(text_label, "Mouse Moved\0") } // render mouse @@ -82,20 +120,6 @@ main := fn(): int { // TODO: Get windows out of a collection and iter through - // render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white) - // { - // // Scroll bar :ThumbsUp: - // render.put_rect(screen, .(100, 100), .(100, 10), render.white) - // render.put_filled_rect(screen, .(110, 100), .(20, 10), render.white) - - // render.put_rect(screen, .(90, 110), .(10, 100), render.white) - // render.put_filled_rect(screen, .(90, 120), .(10, 20), render.white) - // } - - // { - // pos := Vec2(uint).(1, screen.height - 21) - // render_label_to_surface(screen, text_label, font, pos) - // } // Sync the screen render.sync(screen) } diff --git a/sysdata/programs/ps2_mouse_driver/src/main.hb b/sysdata/programs/ps2_mouse_driver/src/main.hb index 5e74c076..d2343808 100644 --- a/sysdata/programs/ps2_mouse_driver/src/main.hb +++ b/sysdata/programs/ps2_mouse_driver/src/main.hb @@ -111,6 +111,7 @@ main := fn(): int { loop if (memory.inb(0x64) & 0x20) == 0x20 break status := memory.inb(0x60) + if status == 0xAA { loop if memory.inb(0x60) == 0 break log.info("Mouse plugged in!\0") @@ -132,23 +133,23 @@ main := fn(): int { button_states ^= changes - // log.info(string.display_int(status, format_page, 10)) - dx := i9.(false, 0) dy := i9.(false, 0) dx.value = memory.inb(0x60) dx.sign = (status & 0x10) > 0 - dy.value = -memory.inb(0x60) - dy.sign = (status & 0x20) == 0 + dy.value = memory.inb(0x60) + dy.sign = (status & 0x20) != 0 - if dy.value != 0 & dx.value != 0 { - y_change := @as(i8, @bitcast(dy.value)) - x_change := @as(i8, @bitcast(dx.value)) - event := MouseEvent.(x_change, y_change, 0, 0, 0) - buffer.write(MouseEvent, &event, mouse_buffer) - } + y_change := @as(i8, @bitcast(dy.value)) + x_change := @as(i8, @bitcast(dx.value)) + + event := MouseEvent.(0, 0, 0, 0, 0) + event.x_change = x_change + event.y_change = y_change + + buffer.write(MouseEvent, &event, mouse_buffer) } return 0 diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml index c46e0a39..2e9996ce 100644 --- a/sysdata/system_config.toml +++ b/sysdata/system_config.toml @@ -22,11 +22,11 @@ resolution = "1024x768x24" [boot.limine.ableos.modules] -[boot.limine.ableos.modules.render_example] -path = "boot:///render_example.hbf" +# [boot.limine.ableos.modules.render_example] +# path = "boot:///render_example.hbf" -# [boot.limine.ableos.modules.horizon] -# path = "boot:///horizon.hbf" +[boot.limine.ableos.modules.horizon] +path = "boot:///horizon.hbf" [boot.limine.ableos.modules.ps2_mouse_driver] path = "boot:///ps2_mouse_driver.hbf" From 68d3236cc0bdc54d3c88330f231a19ee7d56ab21 Mon Sep 17 00:00:00 2001 From: koniifer Date: Sun, 10 Nov 2024 10:09:02 +0000 Subject: [PATCH 61/66] misc --- Cargo.lock | 6 ++-- sysdata/libraries/render/src/image/lib.hb | 13 +++----- sysdata/libraries/stn/src/math.hb | 23 +++++++------- .../src/examples/assets/wallpaper.qoi | Bin 0 -> 677655 bytes .../render_example/src/examples/image.hb | 29 ++++++++---------- .../render_example/src/examples/text.hb | 15 ++++++--- 6 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 sysdata/programs/render_example/src/examples/assets/wallpaper.qoi diff --git a/Cargo.lock b/Cargo.lock index f776aab8..ca14e5d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,12 +228,12 @@ dependencies = [ [[package]] name = "hbbytecode" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#42a713aeaef11ca86d96083915191fbe456c47e5" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#7865d692a156c1d31f63b8f04b5d51472d1ff30a" [[package]] name = "hblang" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#42a713aeaef11ca86d96083915191fbe456c47e5" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#7865d692a156c1d31f63b8f04b5d51472d1ff30a" dependencies = [ "hashbrown 0.15.1", "hbbytecode", @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "hbvm" version = "0.1.0" -source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#42a713aeaef11ca86d96083915191fbe456c47e5" +source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#7865d692a156c1d31f63b8f04b5d51472d1ff30a" dependencies = [ "hbbytecode", ] diff --git a/sysdata/libraries/render/src/image/lib.hb b/sysdata/libraries/render/src/image/lib.hb index 5b8765ed..eecf6d87 100644 --- a/sysdata/libraries/render/src/image/lib.hb +++ b/sysdata/libraries/render/src/image/lib.hb @@ -2,26 +2,23 @@ .{Surface} := @use("../lib.hb") bmp := @use("bmp.hb") qoi := @use("qoi.hb") +$BMP := 0x4D42 +$QOI := 0x66696F71 -BMP := 0x4D42 -QOI := 0x66696F71 -// stand-in for null until bugfix -DOES_NOT_EXIST := 1 << 32 - -get_format := fn(file: ^u8): uint { +get_format := fn(file: ^u8): ?uint { if *@as(^u16, @bitcast(file)) == BMP { return BMP } else if *@as(^u32, @bitcast(file)) == QOI { return QOI } else { - return DOES_NOT_EXIST + return null } } from := fn(file: ^u8): ?Surface { format := get_format(file) - if format == DOES_NOT_EXIST { + if format == null { log.error("Could not detect image format.\0") return null } else if format == BMP { diff --git a/sysdata/libraries/stn/src/math.hb b/sysdata/libraries/stn/src/math.hb index 64ba6b82..f22e17fb 100644 --- a/sysdata/libraries/stn/src/math.hb +++ b/sysdata/libraries/stn/src/math.hb @@ -1,14 +1,14 @@ abs := fn($Expr: type, x: Expr): Expr { - mask := x >> @intcast(@sizeof(Expr) - 1) + mask := x >> @bitcast(@sizeof(Expr) - 1) return (x ^ mask) - mask } min := fn($Expr: type, a: Expr, b: Expr): Expr { c := a - b - return b + (c & c >> @intcast(@sizeof(Expr) - 1)) + return b + (c & c >> @bitcast(@sizeof(Expr) - 1)) } max := fn($Expr: type, a: Expr, b: Expr): Expr { c := a - b - return a - (c & c >> @intcast(@sizeof(Expr) - 1)) + return a - (c & c >> @bitcast(@sizeof(Expr) - 1)) } signum := fn($Expr: type, x: Expr): int { if x > @as(Expr, @intcast(0)) { @@ -32,33 +32,32 @@ Vec2 := fn($Expr: type): type { /* source: https://github.com/baker-Xie/FastMath/blob/master/include/fast_math.h */ - -PI := 3.14159265358979323846 +$PI := 3.14159265358979323846 SIN_TABLE := [f32].(0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245) TAN_TABLE := [f32].(0.0, 0.01227246237956628, 0.02454862210892544, 0.03683218099484564, 0.04912684976946725, 0.06143635258159376, 0.07376443152244928, 0.08611485119762791, 0.09849140335716425, 0.110897911595913, 0.1233382361367387, 0.1358162787093877, 0.1483359875383474, 0.1609013624534892, 0.1735164601378558, 0.1861853995275837, 0.198912367379658, 0.2117016240239833, 0.2245575093171293, 0.2374844488160702, 0.2504869601913055, 0.263569659899918, 0.2767372701404143, 0.2899946261126061, 0.3033466836073424, 0.3167985269526038, 0.330355377344334, 0.3440226015924263, 0.3578057213145241, 0.3717104226127435, 0.3857425662711212, 0.3999081985145372, 0.414213562373095, 0.4286651096994995, 0.4432695138908643, 0.4580336833706724, 0.4729647758913199, 0.4880702137228629, 0.5033576997992942, 0.5188352348999757, 0.5345111359507916, 0.5503940555372639, 0.566493002730344, 0.5828173653349761, 0.5993769336819237, 0.616181926094866, 0.6332430161775691, 0.6505713620801533, 0.6681786379192989, 0.6860770675448629, 0.7042794608650442, 0.7227992529642059, 0.7416505462720354, 0.7608481560702512, 0.7804076596539435, 0.8003454494993202, 0.8206787908286602, 0.8414258840072547, 0.8626059322567399, 0.8842392152253498, 0.906347169019147, 0.9289524733703675, 0.9520791467009252, 0.9757526499323765, 0.9999999999999999, 1.024849894150227, 1.05033284623986, 1.076481336415266, 1.103329975733476, 1.130915687498827, 1.159277907333434, 1.188458804282966, 1.218503525587976, 1.249460468133579, 1.281381580036554, 1.31432269635108, 1.34834391348672, 1.383510007652874, 1.419890903494092, 1.457562200087105, 1.496605762665489, 1.537110389861882, 1.579172567960209, 1.622897325693455, 1.668399205583507, 1.715803370795664, 1.765246870094191, 1.816880087892402, 1.870868411789389, 1.927394156630064, 1.986658792343365, 2.04888553303075, 2.11432235754864, 2.183245547884151, 2.255963851929159, 2.33282340310135, 2.414213562373095, 2.500573890994256, 2.592402517738071, 2.690266237279613, 2.794812772490477, 2.906785761665535, 3.027043204317773, 3.156580333940787, 3.296558208938321, 3.448339762033025, 3.613535681307428, 3.7940634000883, 3.992223783770083, 4.210802033502797, 4.453202224414411, 4.723629327882301, 5.027339492125846, 5.370990435003726, 5.763142005118804, 6.21498777108904, 6.741452405414988, 7.362887641324242, 8.107785803676903, 9.017302360424724, 10.15317038760884, 11.61239886143525, 13.55666924235242, 16.27700795993539, 20.35546762498714, 27.15017066569958, 40.73548387208334, 81.48324020654604, 1633123935319537.0, -81.48324020654685, -40.73548387208354, -27.15017066569967, -20.35546762498719, -16.27700795993542, -13.55666924235244, -11.61239886143527, -10.15317038760886, -9.017302360424734, -8.10778580367691, -7.362887641324249, -6.741452405414994, -6.214987771089044, -5.763142005118809, -5.37099043500373, -5.02733949212585, -4.723629327882303, -4.453202224414413, -4.2108020335028, -3.992223783770084, -3.794063400088302, -3.61353568130743, -3.448339762033026, -3.296558208938323, -3.156580333940789, -3.027043204317775, -2.906785761665536, -2.794812772490478, -2.690266237279614, -2.592402517738072, -2.500573890994257, -2.414213562373095, -2.332823403101351, -2.25596385192916, -2.183245547884153, -2.114322357548642, -2.048885533030752, -1.986658792343365, -1.927394156630064, -1.870868411789389, -1.816880087892402, -1.765246870094192, -1.715803370795664, -1.668399205583508, -1.622897325693455, -1.57917256796021, -1.537110389861883, -1.49660576266549, -1.457562200087105, -1.419890903494092, -1.383510007652874, -1.34834391348672, -1.31432269635108, -1.281381580036555, -1.249460468133579, -1.218503525587977, -1.188458804282967, -1.159277907333435, -1.130915687498827, -1.103329975733476, -1.076481336415266, -1.05033284623986, -1.024849894150228, -1.0, -0.9757526499323768, -0.9520791467009256, -0.9289524733703679, -0.9063471690191476, -0.8842392152253504, -0.8626059322567398, -0.8414258840072547, -0.8206787908286604, -0.8003454494993202, -0.7804076596539438, -0.7608481560702515, -0.7416505462720356, -0.7227992529642062, -0.7042794608650446, -0.6860770675448633, -0.6681786379192988, -0.6505713620801532, -0.6332430161775691, -0.6161819260948661, -0.5993769336819238, -0.5828173653349762, -0.5664930027303442, -0.5503940555372643, -0.5345111359507919, -0.5188352348999761, -0.5033576997992947, -0.4880702137228627, -0.4729647758913199, -0.4580336833706724, -0.4432695138908644, -0.4286651096994996, -0.4142135623730952, -0.3999081985145373, -0.3857425662711215, -0.3717104226127437, -0.3578057213145244, -0.3440226015924267, -0.3303553773443338, -0.3167985269526037, -0.3033466836073424, -0.2899946261126062, -0.2767372701404144, -0.2635696598999182, -0.2504869601913056, -0.2374844488160704, -0.2245575093171296, -0.2117016240239837, -0.1989123673796584, -0.1861853995275837, -0.1735164601378557, -0.1609013624534892, -0.1483359875383475, -0.1358162787093878, -0.1233382361367388, -0.1108979115959132, -0.09849140335716448, -0.08611485119762818, -0.0737644315224496, -0.06143635258159368, -0.04912684976946721, -0.03683218099484564, -0.02454862210892548, -0.01227246237956636) -TABLE_SIZE := @as(i32, 256) +$TABLE_SIZE := @as(i32, 256) sin := fn(theta: f32): f32 { si := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) - d := theta - @itf(si) * 2.0 * PI / @itf(TABLE_SIZE) + d := theta - @floatcast(@itf(si)) * 2.0 * PI / @itf(TABLE_SIZE) ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1 si &= TABLE_SIZE - 1 - return SIN_TABLE[si] + (SIN_TABLE[ci] - 0.5 * SIN_TABLE[si] * d) * d + return SIN_TABLE[@bitcast(si)] + (SIN_TABLE[@bitcast(ci)] - 0.5 * SIN_TABLE[@bitcast(si)] * d) * d } cos := fn(theta: f32): f32 { ci := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI) - d := theta - @itf(ci) * 2.0 * PI / @itf(TABLE_SIZE) + d := theta - @floatcast(@itf(ci)) * 2.0 * PI / @itf(TABLE_SIZE) si := ci + TABLE_SIZE / 4 & TABLE_SIZE - 1 ci &= TABLE_SIZE - 1 - return SIN_TABLE[si] + (SIN_TABLE[ci] - 0.5 * SIN_TABLE[si] * d) * d + return SIN_TABLE[@bitcast(si)] + (SIN_TABLE[@bitcast(ci)] - 0.5 * SIN_TABLE[@bitcast(si)] * d) * d } tan := fn(theta: f32): f32 { a := @fti(theta * @itf(TABLE_SIZE) / PI) - d := theta - @itf(a) * PI / @itf(TABLE_SIZE) + d := theta - @floatcast(@itf(a)) * PI / @itf(TABLE_SIZE) d = d + 1.0 / 3.0 * d * d * d a &= TABLE_SIZE - 1 - return (TAN_TABLE[a] + d) / (1.0 - TAN_TABLE[a] * d) + return (TAN_TABLE[@bitcast(a)] + d) / (1.0 - TAN_TABLE[@bitcast(a)] * d) } \ No newline at end of file diff --git a/sysdata/programs/render_example/src/examples/assets/wallpaper.qoi b/sysdata/programs/render_example/src/examples/assets/wallpaper.qoi new file mode 100644 index 0000000000000000000000000000000000000000..76dc853bac3ae3b233dbad99dd34b78ca9ea6f66 GIT binary patch literal 677655 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