diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ca2246a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "makefile.launchConfigurations": [ + { + "cwd": "/home/patrick/Sylva/build", + "binaryPath": "/home/patrick/Sylva/build/Kernel.elf", + "binaryArgs": [] + } + ] +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 2bd274d..cb49691 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,28 +7,52 @@ x86_64 UEFI hobby OS. C17 boot → C++17 kernel, ELF64 → PE32+ via objcopy. | Command | What | |---------|------| | `make` | Build `build/BOOTX64.EFI` + `build/Kernel.elf` | -| `make run` | QEMU + OVMF, serial 1 → `serial.log`, serial 2 → stdio, paused for GDB (`:1234`) | -| `make vdir` | Stage both binaries into `vdir/EFI/BOOT/` for manual boot | -| `make clean` | **Always before commit** — untracked build artifacts leak outside `build/` | +| `make disk` | Build FAT32 disk image (128 MiB) only — no QEMU | +| `make run` | `make disk` + QEMU/OVMF. Serial 1 → `serial.log` (gitignored), paused for GDB (`:1234`) | +| `make vdir` | Stage `BOOTX64.EFI` + `Kernel.elf` + `resources/` into `vdir/EFI/BOOT/` for manual boot | +| `make clean` | wipes `build/`, `vdir/`, and `serial.log` | + +**Prereqs for `make run`:** `qemu-system-x86_64`, OVMF at `/usr/share/ovmf/OVMF.fd`, `mtools` (`mformat`/`mmd`/`mcopy`). QEMU starts paused — connect GDB to `:1234` to proceed. Kernel debug output goes to `serial.log`. ## Architecture -- Two-stage boot: `boot.c` (UEFI app) loads `Kernel.elf` from FAT volume, parses ELF PHDRs, jumps to entry -- `kernel/entry.cpp` — `_start` saves `SystemTable`, calls `kernel_main()` -- `kernel/main.cpp` — `extern "C" kernel_main()`: init GOP → serial → PMM → heap → idle (`hlt`) -- `kernel/serial.cpp` — UEFI serial protocol wrapper (write/read/hex) -- `kernel/memory/pmm.cpp` — bitmap + free-list physical page allocator -- `kernel/memory/heap.cpp` — kmalloc/kfree/kcalloc/krealloc (first-fit w/ coalescing) -- `graphics/` — GOP framebuffer, `fonts/` — Hankaku 8×16 font via `pf_print()` -- `efi/` — bundled gnu-efi sources: `gnuefi/` (crt0, lds, reloc), `lib/`, `inc/` -- Kernel linked at `0x100000` (`kernel/kernel.ld`) +`boot.c` is legacy UEFI glue — loads `Kernel.elf` from the FAT volume, parses ELF PHDRs, jumps to entry. Don't refactor unless touching the boot stage. Bundled gnu-efi sources live in `efi/`. + +**Kernel** lives in `kernel/`. Boot calls `_start` (`kernel/entry.cpp:8`), which saves `EFI_SYSTEM_TABLE*` in global `ST`, then `extern "C" kernel_main()` (`kernel/main.cpp:73`) runs: + +1. `init_gop()` → `gfx_init(GOP)` framebuffer (`graphics/context.cpp`) +2. `init_serial()` → locate SERIAL_IO protocol +3. `pmm_init()` — bitmap + free-list page allocator (`kernel/memory/pmm.cpp`) +4. `init_heap()` — kmalloc/kfree/kcalloc/krealloc, first-fit w/ coalescing (`kernel/memory/heap.cpp`) +5. `fs_init()` / `fs_list()` — reads FAT volume via UEFI (`kernel/fs.cpp`) +6. `gdt_init()` → `idt_init()` → `pic_init()` → `pit_init()` (`kernel/interrupt/`) +7. `ASM("sti")` — interrupts on +8. `layer_init()` + `layer_create(...)` for desktop + windows (`kernel/graphics/layer.cpp`) +9. `task_create("compositor", layer_compositor_task)` +10. `scheduler_run()` — never returns. Preemptive, 100 Hz PIT, 5-tick time slice (`kernel/scheduler/`) + +PIT IRQ 0 dispatches to `pit_irq_handler` → `scheduler_tick` via the IRQ handler in `kernel/main.cpp:56`. + +## `include/common.h` — use these, not raw equivalents + +Always `#include ` (it pulls in `` + `` and defines the `ASM` macro): + +| Provided | Use instead of | +|----------|----------------| +| `SSINT8/16/32/64`, `SUINT8/16/32/64` | raw `int*_t` / `uint*_t` | +| `String` (`const char*`), `WString` (`const uint16_t*`) | raw `char*` / `CHAR16*` | +| `str_len`, `str_cmp`, `str_eq`, `str_copy` | hand-rolled loops on `char*` | +| `wstr_len`, `wstr_cmp`, `wstr_eq`, `wstr_copy`, `wstr_to_ascii` | hand-rolled loops on `CHAR16*` | +| `mem_set`, `mem_copy` | hand-rolled byte loops | +| `ASM(...)` | raw `asm volatile(...)` — e.g. `ASM("cli")`, `ASM("hlt")`, `ASM("sti")` | + +All `string_utils.h` helpers are `static inline` — header-only, no link issues. New kernel code should reach for these before adding anything new to `string_utils.h`. ## Conventions -- Commits: `[type] message` with types `feat`, `fix`, `chore`, `docs` (git log) -- `#define ASM asm volatile` in `include/common.h` -- C17 (`boot.c`), C++17 (`kernel/`) — `-ffreestanding -fno-stack-protector -fshort-wchar -mno-red-zone` -- `uefi_call_wrapper` required for all UEFI protocol calls (omission → page-fault) -- `kernel_main` must be `extern "C"` (C→C++ linkage) -- QEMU starts paused: connect GDB to `:1234` to proceed -- No tests, no CI, no lint config +- **Commits:** `[type] message` — types: `feat`, `fix`, `chore`, `docs`, `refactor` (see `git log`). +- **Languages:** C17 (`boot.c`), C++17 (`kernel/`) — `-ffreestanding -fno-stack-protector -fshort-wchar -mno-red-zone -fcf-protection=none`. Kernel adds `-DGNU_EFI_USE_MS_ABI -fno-pic`. +- **`kernel_main` must be `extern "C"`** — `boot.c` (C) calls into C++ (`kernel/entry.cpp:6`). +- **All UEFI protocol calls must use `uefi_call_wrapper`** — calling the member fn directly page-faults in long mode. See `kernel/main.cpp:23-24` and throughout. +- **Kernel linked at `0x100000`** (`kernel/kernel.ld`); `.bss` start/end symbols exposed as `__bss_start` / `__bss_end`. +- **No tests, no CI, no lint config** — `make` is the only verification step. Boot by hand in QEMU to confirm kernel output in `serial.log`. diff --git a/Makefile b/Makefile index 9852364..99bcf29 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -std=c17 -Wwrite-strings -fcf-protection=none -CXXFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -std=c++17 -Wwrite-strings -fcf-protection=none +CFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -std=c17 -Wwrite-strings -fcf-protection=none -g +CXXFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -std=c++17 -Wwrite-strings -fcf-protection=none -g LDFLAGS = -shared -Bsymbolic -Tefi/gnuefi/elf_x86_64_efi.lds LDLIBS = --no-undefined @@ -15,16 +15,14 @@ EFI_CFLAGS = -Iefi/inc -Iefi/inc/x86_64 -Iefi/inc/protocol \ BOOT_OBJ = build/boot.o -KERNEL_CPP = kernel/entry.cpp kernel/main.cpp kernel/serial.cpp kernel/fs.cpp \ - kernel/memory/heap.cpp kernel/memory/pmm.cpp \ - kernel/scheduler/scheduler.cpp \ - kernel/interrupt/gdt.cpp kernel/interrupt/idt.cpp \ - kernel/interrupt/pic.cpp kernel/interrupt/pit.cpp \ - kernel/graphics/layer.cpp \ - graphics/context.cpp graphics/draw.cpp \ - fonts/pixel_font.cpp -KERNEL_ASM = kernel/scheduler/context_switch.S kernel/interrupt/isr.S kernel/interrupt/idt_helpers.S -KERNEL_OBJ = $(KERNEL_CPP:%.cpp=build/%.o) $(KERNEL_ASM:%.S=build/%.o) +KERNEL_ASMFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check \ + -fshort-wchar -mno-red-zone -fcf-protection=none -g + +KERNEL_CPP := $(shell find kernel graphics fonts -name '*.cpp' -type f) +KERNEL_ASM := $(shell find kernel -name '*.S' -type f) + +KERNEL_OBJ := $(KERNEL_CPP:%.cpp=build/%.o) $(KERNEL_ASM:%.S=build/%.o) +KERNEL_DIRS := $(sort $(dir $(KERNEL_OBJ))) EFI_TOP_C = $(wildcard efi/lib/*.c) EFI_TOP_S = $(wildcard efi/lib/*.S) @@ -48,9 +46,7 @@ all: _bd $(EFI_OBJ) $(BOOT_OBJ) $(KERNEL_OBJ) @echo "Done." _bd: - @mkdir -p build/graphics build/kernel build/fonts build/kernel/memory \ - build/kernel/scheduler build/kernel/interrupt build/kernel/graphics \ - build/efi/lib build/efi/lib/x86_64 build/efi/lib/runtime build/efi/gnuefi + @mkdir -p $(KERNEL_DIRS) build/efi/gnuefi build/efi/lib build/efi/lib/x86_64 build/efi/lib/runtime $(EFI_CRT0_OBJ): efi/gnuefi/crt0-efi-x86_64.S | _bd @echo "Compile AS $<" @@ -84,51 +80,35 @@ build/%.o: %.c @echo "Compile C $<" @gcc $(CFLAGS) -c $< -o $@ -build/kernel/%.o: kernel/%.cpp | _bd +build/%.o: %.cpp | _bd @echo "Compile CPP $<" @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ -build/kernel/memory/%.o: kernel/memory/%.cpp | _bd - @echo "Compile CPP $<" - @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ - -build/kernel/scheduler/%.o: kernel/scheduler/%.cpp | _bd - @echo "Compile CPP $<" - @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ - -build/kernel/scheduler/%.o: kernel/scheduler/%.S | _bd +build/%.o: %.S | _bd @echo "Compile AS $<" - @gcc -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check \ - -fshort-wchar -mno-red-zone -fcf-protection=none -c $< -o $@ - -build/kernel/interrupt/%.o: kernel/interrupt/%.cpp | _bd - @echo "Compile CPP $<" - @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ - -build/kernel/interrupt/%.o: kernel/interrupt/%.S | _bd - @echo "Compile AS $<" - @gcc -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check \ - -fshort-wchar -mno-red-zone -fcf-protection=none -c $< -o $@ - -build/graphics/%.o: graphics/%.cpp | _bd - @echo "Compile CPP $<" - @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ - -build/fonts/%.o: fonts/%.cpp | _bd - @echo "Compile CPP $<" - @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ + @gcc $(KERNEL_ASMFLAGS) -c $< -o $@ vdir: all - @mkdir -p vdir/EFI/BOOT + @mkdir -p vdir/EFI/BOOT vdir/sys @cp build/BOOTX64.EFI vdir/EFI/BOOT @cp build/Kernel.elf vdir/ + @cp -r resources vdir/sys/ -run: vdir +disk: vdir + @echo "* Building FAT32 disk image (128 MiB)..." + @dd if=/dev/zero of=build/disk.img bs=1M count=128 status=none + @mformat -i build/disk.img -F -T 262144 -h 16 -s 32 :: + @mmd -i build/disk.img ::/EFI ::/EFI/BOOT ::/sys + @mcopy -i build/disk.img -s vdir/EFI/BOOT/BOOTX64.EFI ::/EFI/BOOT/ + @mcopy -i build/disk.img -s vdir/Kernel.elf ::/ + @mcopy -i build/disk.img -s vdir/sys/resources ::/sys/ + +run: disk @echo "Launching QEMU" - @qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -net none -drive file=fat:rw:vdir,index=0,format=vvfat -serial file:serial.log -serial stdio -s -S + @qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -net none -drive file=build/disk.img,index=0,format=raw -serial stdio -serial file:serial.log -s -S clean: @echo "Cleaning old files" @rm -rf build vdir -.PHONY: all vdir run clean _bd +.PHONY: all vdir disk run clean _bd diff --git a/fonts/README.md b/fonts/README.md index de30350..173ea05 100644 --- a/fonts/README.md +++ b/fonts/README.md @@ -3,5 +3,6 @@ 本系统引用了下列字体: - Hankaku +- 霞鹜文楷 Light 并在相关协议下使用。 \ No newline at end of file diff --git a/fonts/pixel_font.cpp b/fonts/pixel_font.cpp index 37b7e68..874caf1 100644 --- a/fonts/pixel_font.cpp +++ b/fonts/pixel_font.cpp @@ -8,13 +8,7 @@ void pf_print_char(char c, SUINT32 basex, SUINT32 basey, EFI_GRAPHICS_OUTPUT_BLT for (SUINT32 y = 0; y < 16; y++) { SUINT8 data = hankaku_pixels[c][y]; for (SSINT32 x = 7; x >= 0; x--) { - // 解码Hankaku字体 - /* - 既然都在这了,就讲一下Hankaku字体是如何解码的 - 比如一个 - {0x00, 0x82, 0x82, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00} - 每一个Hex代表一行,比如0x82就是一行,转换成Bin得到10000010,1代表有像素,0代表没像素 - */ + // Hankaku 字体解码:每个字节代表一行,低位在右 SUINT32 current = data & 1; data >>= 1; if (current) @@ -26,6 +20,6 @@ void pf_print_char(char c, SUINT32 basex, SUINT32 basey, EFI_GRAPHICS_OUTPUT_BLT void pf_print(const char* text, SUINT32 basex, SUINT32 basey, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) { for (SUINT32 i = 0; i < str_len(text); i++) { char c = text[i]; - pf_print_char(c, basex + i * 8, basey, color); // 只要 字数 * 8 + basex 不爆hr就没事 + pf_print_char(c, basex + i * 8, basey, color); } } \ No newline at end of file diff --git a/fonts/ttf/ttf.cpp b/fonts/ttf/ttf.cpp new file mode 100644 index 0000000..c6b31e4 --- /dev/null +++ b/fonts/ttf/ttf.cpp @@ -0,0 +1,120 @@ +#include +#include "ttf_internal.h" +#include +#include +#include + +// 逐字形临时缓冲区 — 静态分配以避免大中文字形的栈压力 +static ttf_outline_t s_outline; +static ttf_seg_t s_segs[4096]; +static SUINT8 s_coverage[256 * 256]; + +// 在屏幕 (px, py) 处渲染单个字形位图,py 为字形顶部(已从基线转换) +static void blit_glyph(SSINT32 px, SSINT32 py, SUINT32 w, SUINT32 h, + const SUINT8* coverage, SUINT32 N, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) +{ + SUINT32 scale = 255 / N; + for (SUINT32 y = 0; y < h; y++) { + SSINT32 sy = py + (SSINT32)y; + if (sy < 0) continue; + for (SUINT32 x = 0; x < w; x++) { + SSINT32 sx = px + (SSINT32)x; + if (sx < 0) continue; + SUINT8 c = coverage[y * w + x]; + if (c == 0) continue; + SUINT8 a = (SUINT8)(c * scale); + draw_pixel_alpha(sx, sy, color, a); + } + } +} + +// 在基线 (x, y) 处渲染单个码点,返回进宽(26.6 定点数) +static f26_6 render_codepoint(ttf_face_t* face, SSINT32 cp, + SSINT32 x, SSINT32 y, SUINT32 pixel_size, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) +{ + SUINT16 gid = ttf_cmap_lookup(face, cp); + + if (!ttf_load_glyph(face, gid, pixel_size, &s_outline)) { + return 0; // composite / parse error + } + + // Bitmap dims in pixels + SSINT32 bbox_w = s_outline.xmax - s_outline.xmin; + SSINT32 bbox_h = s_outline.ymax - s_outline.ymin; + if (bbox_w <= 0 || bbox_h <= 0) { + return s_outline.advance; // whitespace or zero-size + } + SUINT32 pw = (SUINT32)((bbox_w + 63) >> 6); + SUINT32 ph = (SUINT32)((bbox_h + 63) >> 6); + if (pw > 256 || ph > 256) { + return s_outline.advance; // too big for scratch + } + + // Translate outline to bitmap-local: bx = fx - xmin, by = ymax - fy + ttf_outline_t local; + local.num_points = s_outline.num_points; + local.num_contours = s_outline.num_contours; + for (SUINT16 i = 0; i < s_outline.num_contours; i++) { + local.first[i] = s_outline.first[i]; + local.last[i] = s_outline.last[i]; + } + for (SUINT16 i = 0; i < s_outline.num_points; i++) { + local.x[i] = s_outline.x[i] - s_outline.xmin; + local.y[i] = s_outline.ymax - s_outline.y[i]; + local.on_curve[i] = s_outline.on_curve[i]; + } + local.xmin = 0; + local.ymin = 0; + local.xmax = bbox_w; + local.ymax = bbox_h; + + SUINT32 n_segs = 0; + ttf_outline_to_segments(&local, s_segs, &n_segs); + + // Clear coverage + for (SUINT32 i = 0; i < pw * ph; i++) s_coverage[i] = 0; + + const SUINT32 N = 5; // subsamples per pixel row + ttf_rasterize(s_segs, n_segs, 0, 0, pw, ph, s_coverage, N); + + // Screen origin of bitmap + SSINT32 px_screen = x + (s_outline.xmin >> 6); + SSINT32 py_screen = y - (s_outline.ymax >> 6); + + blit_glyph(px_screen, py_screen, pw, ph, s_coverage, N, color); + + return s_outline.advance; +} + +SSINT32 ttf_draw_text(ttf_face_t* face, const char* utf8, + SSINT32 x, SSINT32 y, SUINT32 pixel_size, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) +{ + if (!face || !utf8) return 0; + const char* p = utf8; + f26_6 pen = 0; + while (*p) { + SSINT32 cp = ttf_utf8_decode(&p); + if (cp < 0) continue; + f26_6 adv = render_codepoint(face, cp, + x + (pen >> 6), y, pixel_size, color); + pen += adv; + } + return pen; +} + +SSINT32 ttf_text_width(ttf_face_t* face, const char* utf8, SUINT32 pixel_size) { + if (!face || !utf8) return 0; + const char* p = utf8; + f26_6 pen = 0; + while (*p) { + SSINT32 cp = ttf_utf8_decode(&p); + if (cp < 0) continue; + SUINT16 gid = ttf_cmap_lookup(face, cp); + if (!ttf_load_glyph(face, gid, pixel_size, &s_outline)) continue; + pen += s_outline.advance; + } + return pen; +} diff --git a/fonts/ttf/ttf_internal.h b/fonts/ttf/ttf_internal.h new file mode 100644 index 0000000..ac6f58a --- /dev/null +++ b/fonts/ttf/ttf_internal.h @@ -0,0 +1,93 @@ +#pragma once + +#include +#include +#include "ttf_math.h" + +typedef struct ttf_face ttf_face_t; + +typedef struct ttf_face { + const SUINT8* data; + UINTN size; + + // head + SUINT32 units_per_em; + SSINT16 index_to_loc_format; // 0 = short, 1 = long + + // hhea + SSINT16 hhea_ascender; + SSINT16 hhea_descender; + SSINT16 hhea_line_gap; + SUINT16 num_long_hor_metrics; + + // maxp + SUINT16 num_glyphs; + SUINT16 max_points; + SUINT16 max_contours; + + // os/2 + SSINT16 os2_ascender; + SSINT16 os2_descender; + SSINT16 os2_line_gap; + + // Table directory (sorted by tag at open time) + struct { + char tag[4]; + SUINT32 offset; + SUINT32 length; + } tables[32]; + SUINT16 num_tables; + + // Cached pointers into data[] + const SUINT8* loca; // size depends on num_glyphs and format + const SUINT8* glyf; + SUINT32 glyf_len; + const SUINT8* hmtx; + SUINT32 hmtx_len; + const SUINT8* cmap; +} ttf_face_t; + +// Decomposed glyph: contours in pixel space (26.6 fp) ready to scan. +typedef struct ttf_outline { + f26_6 x[1024]; + f26_6 y[1024]; + SUINT8 on_curve[1024]; + SUINT16 first[64]; + SUINT16 last[64]; + SUINT16 num_contours; + SUINT16 num_points; + + // Bounding box in pixel space (26.6 fp). + SSINT32 xmin, ymin, xmax, ymax; + + // Pen advance in pixel space (26.6 fp). + f26_6 advance; +} ttf_outline_t; + +typedef struct ttf_seg { + f26_6 x0, y0, x1, y1; // line / quad end points + f26_6 cx, cy; // quadratic control (set to x0/y0 if is_line) + SUINT8 is_line; // 1 = line, 0 = quad +} ttf_seg_t; + +// Parse the glyf entry for glyph_id. Fills outline already +// scaled to pixel_size_px. Returns false on composite / parse error. +bool ttf_load_glyph(ttf_face_t* face, SUINT16 glyph_id, + SUINT32 pixel_size_px, ttf_outline_t* out); + +// UTF-8 decoder. *p advances past the codepoint. Returns -1 on error. +SSINT32 ttf_utf8_decode(const char** p); + +// cmap lookup — returns glyph_id (0 = notdef). +SUINT16 ttf_cmap_lookup(ttf_face_t* face, SSINT32 cp); + +// Decompose outline into a flat array of line/quadratic segments. +// out must have room for 2*num_points + num_contours entries. +void ttf_outline_to_segments(const ttf_outline_t* outline, + ttf_seg_t* segs, SUINT32* num_segs); + +// Scanline rasterize segments into per-pixel coverage. +// coverage[w*h] gets values in [0, N] where N = supersample count. +void ttf_rasterize(const ttf_seg_t* segs, SUINT32 num_segs, + SSINT32 x0, SSINT32 y0, SUINT32 w, SUINT32 h, + SUINT8* coverage, SUINT32 N); diff --git a/fonts/ttf/ttf_math.h b/fonts/ttf/ttf_math.h new file mode 100644 index 0000000..26c2c1d --- /dev/null +++ b/fonts/ttf/ttf_math.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +// 26.6 fixed point. 1.0 = 64, -32.0 .. 31.999 range in SSINT32. +typedef SSINT32 f26_6; + +#define F26_ONE ((f26_6)64) +#define F26_HALF ((f26_6)32) +#define F26_FROM_INT(x) ((f26_6)((x) * 64)) +#define F26_FLOOR(x) ((x) >> 6) +#define F26_ROUND(x) (((x) + 32) >> 6) +#define F26_FRAC(x) ((x) & 63) + +static inline f26_6 f26_mul(f26_6 a, f26_6 b) { + return (f26_6)(((SSINT64)a * b) >> 6); +} + +static inline f26_6 f26_div(f26_6 a, f26_6 b) { + if (b == 0) return 0; + return (f26_6)(((SSINT64)a << 6) / b); +} + +// Linear interpolation. t in [0, 64]. +static inline f26_6 f26_lerp(f26_6 a, f26_6 b, f26_6 t) { + return a + f26_mul(b - a, t); +} + +// Floor-of-division helper for bezier root solving. +static inline SSINT32 isign(SSINT32 x) { return (x > 0) - (x < 0); } diff --git a/fonts/ttf/ttf_parse.cpp b/fonts/ttf/ttf_parse.cpp new file mode 100644 index 0000000..24273c3 --- /dev/null +++ b/fonts/ttf/ttf_parse.cpp @@ -0,0 +1,461 @@ +#include "ttf_internal.h" +#include +#include +#include + +// 大端读取器(TTF 为大端格式) +static inline SUINT16 rd16(const SUINT8* p) { + return ((SUINT16)p[0] << 8) | p[1]; +} +static inline SSINT16 rd16s(const SUINT8* p) { + return (SSINT16)(((SUINT16)p[0] << 8) | p[1]); +} +static inline SUINT32 rd32(const SUINT8* p) { + return ((SUINT32)p[0] << 24) | ((SUINT32)p[1] << 16) | + ((SUINT32)p[2] << 8) | (SUINT32)p[3]; +} + +static const SUINT8* find_table(ttf_face_t* face, const char tag[4]) { + for (SUINT16 i = 0; i < face->num_tables; i++) { + if (face->tables[i].tag[0] == tag[0] && + face->tables[i].tag[1] == tag[1] && + face->tables[i].tag[2] == tag[2] && + face->tables[i].tag[3] == tag[3]) { + return face->data + face->tables[i].offset; + } + } + return NULL; +} + +// UTF-8 解码 +SSINT32 ttf_utf8_decode(const char** p) { + const SUINT8* s = (const SUINT8*)*p; + SUINT8 b0 = s[0]; + if (b0 < 0x80) { (*p)++; return b0; } + if ((b0 & 0xE0) == 0xC0) { (*p) += 2; return ((b0 & 0x1F) << 6) | (s[1] & 0x3F); } + if ((b0 & 0xF0) == 0xE0) { (*p) += 3; return ((b0 & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); } + if ((b0 & 0xF8) == 0xF0) { (*p) += 4; return ((b0 & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F); } + (*p)++; + return -1; +} + +// cmap 子表查找 +static const SUINT8* find_cmap_subtable(ttf_face_t* face) { + const SUINT8* cmap = face->cmap; + SUINT16 num = rd16(cmap + 2); + const SUINT8* best = NULL; + SSINT32 best_score = -1; + for (SUINT16 i = 0; i < num; i++) { + const SUINT8* rec = cmap + 4 + i * 8; + SUINT16 platform = rd16(rec + 0); + SUINT16 encoding = rd16(rec + 2); + SUINT32 offset = rd32(rec + 4); + SSINT32 score = -1; + if (platform == 3 && encoding == 10) score = 30; + else if (platform == 3 && encoding == 1) score = 20; + else if (platform == 0 && encoding == 4) score = 10; + else if (platform == 0 && encoding == 3) score = 5; + if (score > best_score) { best_score = score; best = cmap + offset; } + } + return best; +} + +static SUINT16 cmap4_lookup(const SUINT8* sub, SSINT32 cp) { + SUINT16 segCountX2 = rd16(sub + 6); + SUINT16 segCount = segCountX2 / 2; + const SUINT8* endCode = sub + 14; + const SUINT8* startCode = endCode + segCountX2 + 2; + const SUINT8* idDelta = startCode + segCountX2; + const SUINT8* idRangeOff = idDelta + segCountX2; + for (SUINT16 i = 0; i < segCount; i++) { + SUINT16 ec = rd16(endCode + i * 2); + SUINT16 sc = rd16(startCode + i * 2); + if (cp < sc) break; + if (cp <= ec) { + SUINT16 dro = rd16(idRangeOff + i * 2); + if (dro == 0) { + return (SUINT16)((SSINT16)cp + (SSINT16)rd16(idDelta + i * 2)); + } + SUINT16 gidx = rd16(idRangeOff + i * 2 + dro + (cp - sc) * 2); + if (gidx == 0) return 0; + return (SUINT16)((SSINT16)gidx + (SSINT16)rd16(idDelta + i * 2)); + } + } + return 0; +} + +static SUINT16 cmap12_lookup(const SUINT8* sub, SSINT32 cp) { + // Format 12 头:format(2) reserved(2) length(4) language(4) nGroups(4) + SUINT32 nGroups = rd32(sub + 12); + const SUINT8* g = sub + 16; + for (SUINT32 i = 0; i < nGroups; i++) { + SUINT32 sc = rd32(g + 0); + SUINT32 ec = rd32(g + 4); + SUINT32 sG = rd32(g + 8); + if (cp < (SSINT32)sc) break; + if (cp <= (SSINT32)ec) return (SUINT16)(sG + (cp - sc)); + g += 12; + } + return 0; +} + +SUINT16 ttf_cmap_lookup(ttf_face_t* face, SSINT32 cp) { + const SUINT8* sub = find_cmap_subtable(face); + if (!sub) return 0; + SUINT16 fmt = rd16(sub); + if (fmt == 4) return cmap4_lookup(sub, cp); + if (fmt == 12) return cmap12_lookup(sub, cp); + return 0; +} + +// glyf 解码 +bool ttf_load_glyph(ttf_face_t* face, SUINT16 glyph_id, + SUINT32 pixel_size_px, ttf_outline_t* out) +{ + mem_set(out, 0, sizeof(*out)); + if (glyph_id >= face->num_glyphs) return false; + + // Loca 索引 + SUINT32 off0, off1; + if (face->index_to_loc_format == 0) { + off0 = ((SUINT32)rd16(face->loca + glyph_id * 2)) * 2; + off1 = ((SUINT32)rd16(face->loca + (glyph_id + 1) * 2)) * 2; + } else { + off0 = rd32(face->loca + glyph_id * 4); + off1 = rd32(face->loca + (glyph_id + 1) * 4); + } + if (off0 >= face->glyf_len) return false; + + // 进宽(始终从 hmtx 读取,无论 glyf 是否存在) + { + SUINT16 aw; + if (glyph_id < face->num_long_hor_metrics) { + aw = rd16(face->hmtx + glyph_id * 4); + } else { + aw = rd16(face->hmtx + (face->num_long_hor_metrics - 1) * 4); + } + out->advance = (f26_6)(((SUINT64)aw * (SUINT64)pixel_size_px * 64) / face->units_per_em); + } + + if (off0 == off1) { + // 空白字形(如空格) + return true; + } + + const SUINT8* g = face->glyf + off0; + SSINT16 numContours = rd16s(g + 0); + if (numContours < 0) { + // 复合字形 — 解析组件记录并合并轮廓 + const SUINT8* cp = g + 10; + SSINT32 all_xmin = 0x7FFFFFFF, all_ymin = 0x7FFFFFFF; + SSINT32 all_xmax = -0x7FFFFFFF, all_ymax = -0x7FFFFFFF; + + // 复合字形标志(OpenType 规范): + // 0x0001 = ARG_1_AND_2_ARE_WORDS(16 位参数;否则 8 位) + // 0x0002 = ARGS_ARE_XY_VALUES(偏移量;否则点索引) + // 0x0008 = WE_HAVE_A_SCALE + // 0x0040 = WE_HAVE_A_2x2 + // 0x0080 = WE_HAVE_AN_X_AND_Y_SCALE + // 0x0020 = MORE_COMPONENTS + + for (;;) { + SUINT16 comp_flags = rd16(cp); cp += 2; + SUINT16 comp_glyph = rd16(cp); cp += 2; + + // 读取参数(大小取决于 ARG_1_AND_2_ARE_WORDS) + SSINT32 arg1, arg2; + if (comp_flags & 0x0001) { + // 16 位有符号字 + arg1 = rd16s(cp); cp += 2; + arg2 = rd16s(cp); cp += 2; + } else { + // 8 位有符号字节 + arg1 = (SSINT8)cp[0]; + arg2 = (SSINT8)cp[1]; + cp += 2; + } + + // 如果存在则读取缩放比例 + f26_6 scale = F26_ONE; + if (comp_flags & 0x0008) { + // WE_HAVE_A_SCALE:16.16 定点数 + SSINT16 s16 = rd16s(cp); cp += 2; + scale = (f26_6)s16; // already in f26.6 from 16.16 + } + + // 如果存在则读取 x/y 缩放 + f26_6 scaleX = F26_ONE, scaleY = F26_ONE; + if (comp_flags & 0x0080) { + // WE_HAVE_AN_X_AND_Y_SCALE:两个 16.16 值 + SSINT16 sx16 = rd16s(cp); cp += 2; + SSINT16 sy16 = rd16s(cp); cp += 2; + scaleX = (f26_6)sx16; + scaleY = (f26_6)sy16; + } + + // 如果存在则读取 2x2 矩阵 + f26_6 m00 = F26_ONE, m01 = 0, m10 = 0, m11 = F26_ONE; + if (comp_flags & 0x0040) { + // WE_HAVE_A_2x2:四个 2.14 值 + SSINT16 a = rd16s(cp); cp += 2; + SSINT16 b = rd16s(cp); cp += 2; + SSINT16 c = rd16s(cp); cp += 2; + SSINT16 d = rd16s(cp); cp += 2; + m00 = (f26_6)(a >> 8); // 2.14 -> 26.6: shift right 8 + m01 = (f26_6)(b >> 8); + m10 = (f26_6)(c >> 8); + m11 = (f26_6)(d >> 8); + } + + // 加载组件字形 + ttf_outline_t comp; + if (!ttf_load_glyph(face, comp_glyph, pixel_size_px, &comp)) + return false; + + // 判断参数是偏移量(XY 值)还是点索引 + SSINT32 offset_x = 0, offset_y = 0; + if (comp_flags & 0x0002) { + // ARGS_ARE_XY_VALUES:参数是像素偏移量(已缩放) + // 从字体单位缩放到像素空间 + offset_x = (SSINT32)(((SSINT64)arg1 * (SSINT64)pixel_size_px * 64 / face->units_per_em)); + offset_y = (SSINT32)(((SSINT64)arg2 * (SSINT64)pixel_size_px * 64 / face->units_per_em)); + } + + // 变换并合并组件点 + for (SUINT16 i = 0; i < comp.num_points; i++) { + f26_6 fx = comp.x[i]; + f26_6 fy = comp.y[i]; + f26_6 tx, ty; + + if (comp_flags & 0x0040) { + // 2x2 矩阵变换 + tx = f26_mul(m00, fx) + f26_mul(m01, fy); + ty = f26_mul(m10, fx) + f26_mul(m11, fy); + } else if (comp_flags & 0x0008) { + // 均匀缩放 + tx = f26_mul(scale, fx); + ty = f26_mul(scale, fy); + } else if (comp_flags & 0x0080) { + // x/y 独立缩放 + tx = f26_mul(scaleX, fx); + ty = f26_mul(scaleY, fy); + } else { + tx = fx; + ty = fy; + } + + f26_6 final_x = tx + offset_x; + f26_6 final_y = ty + offset_y; + + out->x[out->num_points] = final_x; + out->y[out->num_points] = final_y; + out->on_curve[out->num_points] = comp.on_curve[i]; + out->num_points++; + if (out->num_points > 1024) return false; + + SSINT32 xi = F26_FLOOR(final_x); + SSINT32 yi = F26_FLOOR(final_y); + if (xi < all_xmin) all_xmin = xi; + if (xi > all_xmax) all_xmax = xi; + if (yi < all_ymin) all_ymin = yi; + if (yi > all_ymax) all_ymax = yi; + } + + for (SUINT16 i = 0; i < comp.num_contours; i++) { + if (out->num_contours >= 64) return false; + out->first[out->num_contours] = (SUINT16)(out->num_points - comp.num_points + comp.first[i]); + out->last[out->num_contours] = (SUINT16)(out->num_points - comp.num_points + comp.last[i]); + out->num_contours++; + } + + if (!(comp_flags & 0x0020)) break; // no more components + } + + if (out->num_points == 0) return true; + out->xmin = all_xmin; + out->ymin = all_ymin; + out->xmax = all_xmax; + out->ymax = all_ymax; + return true; + } + if (numContours == 0) return true; + if (numContours > 64) return false; + + SSINT16 gxMin = rd16s(g + 2); + SSINT16 gyMin = rd16s(g + 4); + SSINT16 gxMax = rd16s(g + 6); + SSINT16 gyMax = rd16s(g + 8); + + const SUINT8* p = g + 10; + SUINT16 endPts[64]; + for (SSINT16 i = 0; i < numContours; i++) { endPts[i] = rd16(p); p += 2; } + SUINT16 numPoints = (SUINT16)(endPts[numContours - 1] + 1); + if (numPoints > 1024) return false; + + SUINT16 instrLen = rd16(p); p += 2; + p += instrLen; + + // 解码标志(含 REPEAT) + SUINT8 flags[1024]; + SUINT16 pi = 0; + while (pi < numPoints) { + SUINT8 f = *p++; + flags[pi++] = f; + if (f & 0x08) { + SUINT8 rep = *p++; + for (SUINT16 k = 1; k <= rep && pi < numPoints; k++) + flags[pi++] = f; + } + } + + // 解码 x 坐标到 x_raw[] + SSINT32 x_raw[1024]; + { + SSINT32 x = 0; + for (SUINT16 i = 0; i < numPoints; i++) { + SUINT8 f = flags[i]; + if (f & 0x02) { + SUINT8 b = *p++; + x += (f & 0x10) ? b : -(SSINT32)b; + } else if (!(f & 0x10)) { + x += (SSINT16)((SUINT16)p[0] << 8 | p[1]); + p += 2; + } + x_raw[i] = x; + } + } + // 解码 y 坐标到 y_raw[] + SSINT32 y_raw[1024]; + { + SSINT32 y = 0; + for (SUINT16 i = 0; i < numPoints; i++) { + SUINT8 f = flags[i]; + if (f & 0x04) { + SUINT8 b = *p++; + y += (f & 0x20) ? b : -(SSINT32)b; + } else if (!(f & 0x20)) { + y += (SSINT16)((SUINT16)p[0] << 8 | p[1]); + p += 2; + } + y_raw[i] = y; + } + } + + // 缩放到像素空间(f26_6) + SUINT64 scale_num = (SUINT64)pixel_size_px * 64; + for (SUINT16 i = 0; i < numPoints; i++) { + out->x[i] = (f26_6)(((SSINT64)x_raw[i] * (SSINT64)scale_num) / face->units_per_em); + out->y[i] = (f26_6)(((SSINT64)y_raw[i] * (SSINT64)scale_num) / face->units_per_em); + out->on_curve[i] = (flags[i] & 0x01) ? 1 : 0; + } + + out->num_contours = (SUINT16)numContours; + out->num_points = numPoints; + SUINT16 start = 0; + for (SSINT16 i = 0; i < numContours; i++) { + out->first[i] = start; + out->last[i] = endPts[i]; + start = endPts[i] + 1; + } + + out->xmin = (SSINT32)(((SSINT64)gxMin * (SSINT64)scale_num) / face->units_per_em); + out->ymin = (SSINT32)(((SSINT64)gyMin * (SSINT64)scale_num) / face->units_per_em); + out->xmax = (SSINT32)(((SSINT64)gxMax * (SSINT64)scale_num) / face->units_per_em); + out->ymax = (SSINT32)(((SSINT64)gyMax * (SSINT64)scale_num) / face->units_per_em); + return true; +} + +// 打开 / 关闭 +ttf_face_t* ttf_open(const void* data, UINTN size) { + if (!data || size < 12) return NULL; + const SUINT8* d = (const SUINT8*)data; + + SUINT32 sfVersion = rd32(d); + if (sfVersion != 0x00010000 && sfVersion != 0x74727565) { + serial_write("ttf: bad sfVersion\n"); + return NULL; + } + SUINT16 numTables = rd16(d + 4); + if (numTables == 0 || numTables > 32) return NULL; + + ttf_face_t* face = (ttf_face_t*)kcalloc(1, sizeof(ttf_face_t)); + if (!face) return NULL; + face->data = d; + face->size = size; + face->num_tables = numTables; + + for (SUINT16 i = 0; i < numTables; i++) { + const SUINT8* r = d + 12 + i * 16; + face->tables[i].tag[0] = r[0]; + face->tables[i].tag[1] = r[1]; + face->tables[i].tag[2] = r[2]; + face->tables[i].tag[3] = r[3]; + face->tables[i].offset = rd32(r + 8); + face->tables[i].length = rd32(r + 12); + } + + const SUINT8* head = find_table(face, "head"); + if (!head) { kfree(face); return NULL; } + face->units_per_em = rd16(head + 18); + face->index_to_loc_format = rd16s(head + 50); + + const SUINT8* hhea = find_table(face, "hhea"); + if (!hhea) { kfree(face); return NULL; } + face->hhea_ascender = rd16s(hhea + 4); + face->hhea_descender = rd16s(hhea + 6); + face->hhea_line_gap = rd16s(hhea + 8); + face->num_long_hor_metrics = rd16(hhea + 34); + + const SUINT8* maxp = find_table(face, "maxp"); + if (!maxp) { kfree(face); return NULL; } + face->num_glyphs = rd16(maxp + 4); + face->max_points = rd16(maxp + 6); + face->max_contours = rd16(maxp + 8); + + const SUINT8* os2 = find_table(face, "OS/2"); + if (os2) { + face->os2_ascender = rd16s(os2 + 68); + face->os2_descender = rd16s(os2 + 70); + face->os2_line_gap = rd16s(os2 + 72); + } else { + face->os2_ascender = face->hhea_ascender; + face->os2_descender = face->hhea_descender; + face->os2_line_gap = face->hhea_line_gap; + } + + const SUINT8* loca = find_table(face, "loca"); + const SUINT8* glyf = find_table(face, "glyf"); + const SUINT8* hmtx = find_table(face, "hmtx"); + if (!loca || !glyf || !hmtx) { kfree(face); return NULL; } + face->loca = loca; + face->glyf = glyf; + face->hmtx = hmtx; + for (SUINT16 i = 0; i < numTables; i++) { + if (face->tables[i].tag[0]=='g'&&face->tables[i].tag[1]=='l'&& + face->tables[i].tag[2]=='y'&&face->tables[i].tag[3]=='f') + face->glyf_len = face->tables[i].length; + if (face->tables[i].tag[0]=='h'&&face->tables[i].tag[1]=='m'&& + face->tables[i].tag[2]=='t'&&face->tables[i].tag[3]=='x') + face->hmtx_len = face->tables[i].length; + } + + face->cmap = find_table(face, "cmap"); + if (!face->cmap) { kfree(face); return NULL; } + + return face; +} + +void ttf_close(ttf_face_t* face) { + if (face) kfree(face); +} + +// 度量值(缩放到 pixel_size,返回 26.6 定点数) +SSINT32 ttf_ascender (ttf_face_t* face, SUINT32 px) { + return (SSINT32)(((SSINT64)face->os2_ascender * (SSINT64)px * 64) / face->units_per_em); +} +SSINT32 ttf_descender(ttf_face_t* face, SUINT32 px) { + return (SSINT32)(((SSINT64)face->os2_descender * (SSINT64)px * 64) / face->units_per_em); +} +SSINT32 ttf_line_gap (ttf_face_t* face, SUINT32 px) { + return (SSINT32)(((SSINT64)face->os2_line_gap * (SSINT64)px * 64) / face->units_per_em); +} diff --git a/fonts/ttf/ttf_render.cpp b/fonts/ttf/ttf_render.cpp new file mode 100644 index 0000000..3aede4d --- /dev/null +++ b/fonts/ttf/ttf_render.cpp @@ -0,0 +1,226 @@ +#include "ttf_internal.h" +#include + +// 轮廓 → 线段转换 +// +// TrueType 轮廓遍历:对每对连续点,发射直线或二次贝塞尔曲线。 +// 两个连续的非曲线点会触发合成一个曲线中点。 +// +// 所有坐标全程使用 26.6 定点数。 +void ttf_outline_to_segments(const ttf_outline_t* outline, + ttf_seg_t* segs, SUINT32* num_segs) +{ + *num_segs = 0; + f26_6 syn_x[256]; + f26_6 syn_y[256]; + int n_syn = 0; + + for (SUINT16 ci = 0; ci < outline->num_contours; ci++) { + SUINT16 first = outline->first[ci]; + SUINT16 last = outline->last[ci]; + int n_pts = last - first + 1; + if (n_pts < 2) continue; + + f26_6 lx[1024]; + f26_6 ly[1024]; + SUINT8 on_c[1024]; + for (int i = 0; i < n_pts; i++) { + lx[i] = outline->x[first + i]; + ly[i] = outline->y[first + i]; + on_c[i] = outline->on_curve[first + i]; + } + + // First on-curve point + int start = 0; + while (start < n_pts && !on_c[start]) start++; + if (start == n_pts) start = 0; // all off-curve (rare) — keep going + + int anchor = start; + int pending = -1; + + #define GET_X(i) ((i) < n_pts ? lx[(i)] : syn_x[(i) - n_pts]) + #define GET_Y(i) ((i) < n_pts ? ly[(i)] : syn_y[(i) - n_pts]) + #define PUSH_LINE(a, b) do { \ + ttf_seg_t* __s = &segs[(*num_segs)++]; \ + __s->x0 = GET_X(a); __s->y0 = GET_Y(a); \ + __s->x1 = GET_X(b); __s->y1 = GET_Y(b); \ + __s->cx = __s->x0; __s->cy = __s->y0; __s->is_line = 1; \ + } while (0) + #define PUSH_QUAD(a, c, b) do { \ + ttf_seg_t* __s = &segs[(*num_segs)++]; \ + __s->x0 = GET_X(a); __s->y0 = GET_Y(a); \ + __s->x1 = GET_X(b); __s->y1 = GET_Y(b); \ + __s->cx = GET_X(c); __s->cy = GET_Y(c); __s->is_line = 0; \ + } while (0) + + for (int step = 0; step < n_pts; step++) { + int cur = (start + step) % n_pts; + if (step == 0) { anchor = cur; continue; } + if (on_c[cur]) { + if (pending < 0) { + PUSH_LINE(anchor, cur); + } else { + PUSH_QUAD(anchor, pending, cur); + pending = -1; + } + anchor = cur; + } else { + if (pending < 0) { + pending = cur; + } else { + int syn_idx = n_pts + n_syn; + syn_x[n_syn] = (GET_X(pending) + GET_X(cur)) >> 1; + syn_y[n_syn] = (GET_Y(pending) + GET_Y(cur)) >> 1; + n_syn++; + PUSH_QUAD(anchor, pending, syn_idx); + anchor = syn_idx; + pending = cur; + } + } + } + if (pending >= 0) { + PUSH_QUAD(anchor, pending, start); + } else if (anchor != start) { + PUSH_LINE(anchor, start); + } + + #undef GET_X + #undef GET_Y + #undef PUSH_LINE + #undef PUSH_QUAD + } +} + +// 整数平方根(牛顿法) +static SUINT32 isqrt_u64(SUINT64 n) { + if (n == 0) return 0; + SUINT32 x = (n > 0xFFFFFFFFu) ? 0xFFFFu : (SUINT32)n; + SUINT32 y = (x + 1) >> 1; + while (y < x) { x = y; y = (x + (SUINT32)(n / x)) >> 1; } + return x; +} + +// 扫描线填充 + 子像素超采样 +// +// 对每个输出行,运行 N 条子扫描线,偏移为 (k+0.5)/N。 +// 对每条子扫描线 y,收集所有 x 交点,排序后交替填充 x 对。 +// 对 N 个子采样求和得到每个像素的覆盖率 [0, N]。 +void ttf_rasterize(const ttf_seg_t* segs, SUINT32 num_segs, + SSINT32 x0, SSINT32 y0, SUINT32 w, SUINT32 h, + SUINT8* coverage, SUINT32 N) +{ + // 清空覆盖率缓冲区 + for (SUINT32 i = 0; i < w * h; i++) coverage[i] = 0; + + // 交点 x 缓冲区(每扫描线,最大可能数 = num_segs) + f26_6 xs[2048]; + if (num_segs > 2048) num_segs = 2048; + + for (SUINT32 row = 0; row < h; row++) { + for (SUINT32 k = 0; k < N; k++) { + // Sub-scanline y, in 26.6, with y = 0 at glyph top + f26_6 sy = (f26_6)((row * 64) + ((k * 2 + 1) * 64) / (SSINT32)(N * 2)); + // ^ y center of subpixel k: (k + 0.5) * 64 / N + // = ((2k+1) * 64) / (2N) + // For N=5: k=0 -> 6.4, k=1 -> 19.2, etc. + + SUINT32 nxs = 0; + for (SUINT32 s = 0; s < num_segs; s++) { + const ttf_seg_t* g = &segs[s]; + if (g->is_line) { + f26_6 y0s = g->y0; + f26_6 y1s = g->y1; + if (y0s == y1s) continue; // horizontal — skip + // t = (sy - y0s) / (y1s - y0s) in (0, 1) + if (((y0s < sy) && (y1s < sy)) || + ((y0s > sy) && (y1s > sy))) continue; + f26_6 t = f26_div(sy - y0s, y1s - y0s); + if (t <= 0 || t >= F26_ONE) continue; + f26_6 x = g->x0 + f26_mul(t, g->x1 - g->x0); + xs[nxs++] = x; + } else { + // Quadratic: y(t) = (1-t)^2 y0 + 2(1-t)t cy + t^2 y1 + // a t^2 + b t + c = 0 + // a = y1 - 2 cy + y0 + // b = 2 (cy - y0) + // c = y0 - sy + SSINT64 a = (SSINT64)g->y1 - 2 * (SSINT64)g->cy + (SSINT64)g->y0; + SSINT64 b = 2 * ((SSINT64)g->cy - (SSINT64)g->y0); + SSINT64 c = (SSINT64)g->y0 - (SSINT64)sy; + SSINT32 t0_valid = 0, t1_valid = 0; + f26_6 t0 = 0, t1 = 0; + if (a == 0) { + // Linear + if (b == 0) continue; + f26_6 t = f26_div((f26_6)(-c), (f26_6)b); + if (t > 0 && t < F26_ONE) { t0 = t; t0_valid = 1; } + } else { + SSINT64 disc = b * b - 4 * a * c; + if (disc < 0) continue; + SUINT32 sq = isqrt_u64((SUINT64)disc); + // 26.6 roots: t = (-b ± sqrt(disc)) / (2a) + // All in 26.6 fp. + // t = (-b ± sq) / (2a); both numerator and denom in 26.6 units + // Use f26_div: t_26_6 = ((-b ± sq) << 6) / (2a) + SSINT64 denom = 2 * a; + if (denom == 0) continue; + SSINT64 num0 = -b + (SSINT64)sq; + SSINT64 num1 = -b - (SSINT64)sq; + t0 = (f26_6)(num0 == 0 ? 0 : (num0 << 6) / denom); + t1 = (f26_6)(num1 == 0 ? 0 : (num1 << 6) / denom); + if (t0 > 0 && t0 < F26_ONE) t0_valid = 1; + if (t1 > 0 && t1 < F26_ONE) t1_valid = 1; + } + if (t0_valid) { + // x(t) = (1-t)^2 x0 + 2(1-t)t cx + t^2 x1 + f26_6 omt = F26_ONE - t0; + f26_6 x = f26_mul(f26_mul(omt, omt), g->x0) + + f26_mul(2 * f26_mul(omt, t0), g->cx) + + f26_mul(f26_mul(t0, t0), g->x1); + xs[nxs++] = x; + } + if (t1_valid) { + f26_6 omt = F26_ONE - t1; + f26_6 x = f26_mul(f26_mul(omt, omt), g->x0) + + f26_mul(2 * f26_mul(omt, t1), g->cx) + + f26_mul(f26_mul(t1, t1), g->x1); + xs[nxs++] = x; + } + } + } + + if (nxs < 2) continue; + // Insertion sort + for (SUINT32 i = 1; i < nxs; i++) { + f26_6 v = xs[i]; SUINT32 j = i; + while (j > 0 && xs[j-1] > v) { xs[j] = xs[j-1]; j--; } + xs[j] = v; + } + // Deduplicate: merge intersections within 1/16 pixel (4 in 26.6) + SUINT32 nxd = 0; + for (SUINT32 i = 0; i < nxs; i++) { + if (nxd > 0 && (xs[i] - xs[nxd - 1]) < 4) { + continue; + } + xs[nxd++] = xs[i]; + } + nxs = nxd; + // Fill alternating pairs + for (SUINT32 i = 0; i + 1 < nxs; i += 2) { + f26_6 xa = xs[i]; + f26_6 xb = xs[i+1]; + if (xa == xb) continue; + SSINT32 c0 = F26_FLOOR(xa); + SSINT32 c1 = F26_FLOOR(xb); + if (c0 == c1) continue; + if (c0 < 0) c0 = 0; + if (c1 > (SSINT32)w) c1 = (SSINT32)w; + for (SSINT32 x = c0; x < c1; x++) { + if (x >= 0 && x < (SSINT32)w) coverage[row * w + x]++; + } + } + } + } + // 覆盖率转换在调用端通过 N 完成 + (void)x0; (void)y0; +} diff --git a/graphics/context.cpp b/graphics/context.cpp index 86bf3db..fc4d2d0 100644 --- a/graphics/context.cpp +++ b/graphics/context.cpp @@ -1,7 +1,4 @@ -// GFX 存在的意义是什么? -// 每一次想要draw pixel,都需要传入GOP的各种参数, -// 加入 GFX 后,GOP 的context就是全局的,可以直接用 -// 而不用显示传递参数到draw的函数里 +// GFX 全局图形上下文,避免每次绘制都传递 GOP 参数 #include @@ -21,7 +18,10 @@ void gfx_init(EFI_GRAPHICS_OUTPUT_PROTOCOL *GOP) { void gfx_clear(void) { EFI_GRAPHICS_OUTPUT_BLT_PIXEL black = {0, 0, 0, 0}; - g_gfx.GOP->Blt(g_gfx.GOP, &black, EfiBltVideoFill, 0, 0, 0, 0, g_gfx.hr, g_gfx.vr, 0); + uefi_call_wrapper(g_gfx.GOP->Blt, 10, + g_gfx.GOP, &black, EfiBltVideoFill, + (UINTN)0, (UINTN)0, (UINTN)0, (UINTN)0, + (UINTN)g_gfx.hr, (UINTN)g_gfx.vr, (UINTN)0); } void draw_set_target(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, SUINT32 w, SUINT32 h) { diff --git a/graphics/draw.cpp b/graphics/draw.cpp index 1077a9e..3ba6dcd 100644 --- a/graphics/draw.cpp +++ b/graphics/draw.cpp @@ -34,3 +34,21 @@ void draw_rect(SUINT32 bx, SUINT32 by, SUINT32 ex, SUINT32 ey, for (SUINT32 y = by; y <= ey; y++) draw_pixel(x, y, color); } + +void draw_pixel_alpha(SUINT32 x, SUINT32 y, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color, SUINT8 alpha) { + if (x >= g_draw_target.w || y >= g_draw_target.h) return; + if (alpha == 0) return; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p = g_draw_target.buf + (g_draw_target.w * y) + x; + if (alpha == 255) { + p->Blue = color.Blue; + p->Green = color.Green; + p->Red = color.Red; + p->Reserved = color.Reserved; + return; + } + SUINT32 inv = 255 - alpha; + p->Blue = (SUINT8)((color.Blue * alpha + p->Blue * inv) / 255); + p->Green = (SUINT8)((color.Green * alpha + p->Green * inv) / 255); + p->Red = (SUINT8)((color.Red * alpha + p->Red * inv) / 255); + p->Reserved = color.Reserved; +} diff --git a/include/fonts/hankaku.h b/include/fonts/hankaku.h index d4dd12a..672610b 100644 --- a/include/fonts/hankaku.h +++ b/include/fonts/hankaku.h @@ -1,7 +1,7 @@ #pragma once #include -// Hankaku 字体,不动 +// Hankaku 8x16 点阵字体数据 static SUINT8 hankaku_pixels[256][16] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00}, diff --git a/include/fonts/pixel_font.h b/include/fonts/pixel_font.h index 0436bf1..0e6fb97 100644 --- a/include/fonts/pixel_font.h +++ b/include/fonts/pixel_font.h @@ -3,6 +3,8 @@ #include #include +// 打印单个字符 void pf_print_char(char c, SUINT32 basex, SUINT32 basey, - EFI_GRAPHICS_OUTPUT_BLT_PIXEL color = {255, 255, 255, 255}); // Pixel Font 打印字符 -void pf_print(String text, SUINT32 basex, SUINT32 basey, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color = {255, 255, 255, 255}); // Pixel Font 打印string \ No newline at end of file + EFI_GRAPHICS_OUTPUT_BLT_PIXEL color = {255, 255, 255, 255}); +// 打印字符串 +void pf_print(String text, SUINT32 basex, SUINT32 basey, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color = {255, 255, 255, 255}); \ No newline at end of file diff --git a/include/fonts/ttf.h b/include/fonts/ttf.h new file mode 100644 index 0000000..3c95483 --- /dev/null +++ b/include/fonts/ttf.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +// Opaque font face. Created by ttf_open() over a memory-mapped TTF blob. +typedef struct ttf_face ttf_face_t; + +// Open a TrueType font from a memory buffer. Buffer must remain valid +// for the lifetime of the face. Returns NULL on parse failure. +ttf_face_t* ttf_open(const void* data, UINTN size); + +// Free face. Does NOT free the source buffer. +void ttf_close(ttf_face_t* face); + +// Vertical metrics in 26.6 fixed point, scaled to pixel_size. +SSINT32 ttf_ascender (ttf_face_t* face, SUINT32 pixel_size); +SSINT32 ttf_descender(ttf_face_t* face, SUINT32 pixel_size); +SSINT32 ttf_line_gap (ttf_face_t* face, SUINT32 pixel_size); + +// Measure total advance width of a UTF-8 string at pixel_size (26.6 fp). +SSINT32 ttf_text_width(ttf_face_t* face, const char* utf8, SUINT32 pixel_size); + +// Render a UTF-8 string onto the current draw target. +// (x, y) = baseline origin in TARGET coordinates +// pixel_size = nominal glyph height in pixels (e.g. 16, 24, 48) +// color = foreground; alpha-blended over the current buffer +// Returns the total pen advance (26.6 fp) consumed. +SSINT32 ttf_draw_text(ttf_face_t* face, const char* utf8, + SSINT32 x, SSINT32 y, SUINT32 pixel_size, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color); diff --git a/include/fs.h b/include/fs.h index 59a1657..0e0645b 100644 --- a/include/fs.h +++ b/include/fs.h @@ -5,3 +5,4 @@ EFI_STATUS fs_init(); void fs_list(); EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size); +EFI_STATUS fs_write(WString Path, const void *Data, UINTN Size, UINTN Offset); diff --git a/include/graphics/context.h b/include/graphics/context.h index 218c242..214f14e 100644 --- a/include/graphics/context.h +++ b/include/graphics/context.h @@ -1,7 +1,5 @@ #pragma once -// 这个文件存在的目的是让graphics的draw功能不用每次传 GOP hr vr base - #include #include diff --git a/include/graphics/draw.h b/include/graphics/draw.h index f80db3e..995247c 100644 --- a/include/graphics/draw.h +++ b/include/graphics/draw.h @@ -10,3 +10,6 @@ void global_draw_rect(SUINT32 bx, SUINT32 by, SUINT32 ex, SUINT32 ey, void draw_pixel(SUINT32 x, SUINT32 y, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color); void draw_rect(SUINT32 bx, SUINT32 by, SUINT32 ex, SUINT32 ey, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color); + +// Alpha blend (alpha 0..255). dest = src*alpha/255 + dest*(255-alpha)/255. +void draw_pixel_alpha(SUINT32 x, SUINT32 y, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color, SUINT8 alpha); diff --git a/include/gdt.h b/include/interrupt/gdt.h similarity index 100% rename from include/gdt.h rename to include/interrupt/gdt.h diff --git a/include/idt.h b/include/interrupt/idt.h similarity index 100% rename from include/idt.h rename to include/interrupt/idt.h diff --git a/include/pic.h b/include/interrupt/pic.h similarity index 100% rename from include/pic.h rename to include/interrupt/pic.h diff --git a/include/pit.h b/include/interrupt/pit.h similarity index 100% rename from include/pit.h rename to include/interrupt/pit.h diff --git a/include/serial.h b/include/serial.h index 72f296c..0297d18 100644 --- a/include/serial.h +++ b/include/serial.h @@ -4,14 +4,20 @@ #include #include -struct serial_context { // 串行内容结构体 +// 串行通信上下文 +struct serial_context { EFI_SERIAL_IO_PROTOCOL *SerialIo; }; extern serial_context g_serial; -void serial_init(EFI_SERIAL_IO_PROTOCOL *SerialIo); // 初始化串行驱动 -void serial_write(String str); // 往串行写string -void serial_write_char(char c); // 往串行写char(不推荐使用) -void serial_write_hex(UINTN val); // 往串行写十六进制数字 -char serial_read_char(); // 读串行 \ No newline at end of file +// 初始化串行驱动 +void serial_init(EFI_SERIAL_IO_PROTOCOL *SerialIo); +// 写字符串到串行 +void serial_write(String str); +// 写单个字符到串行(不推荐直接使用) +void serial_write_char(char c); +// 写十六进制数字到串行 +void serial_write_hex(UINTN val); +// 从串行读取一个字符 +char serial_read_char(); \ No newline at end of file diff --git a/kernel/entry.cpp b/kernel/entry.cpp index 616824e..7ce3fd4 100644 --- a/kernel/entry.cpp +++ b/kernel/entry.cpp @@ -8,7 +8,8 @@ extern "C" void kernel_main(); extern "C" void _start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { (void)ImageHandle; ST = SystemTable; - ASM("cli"); // disable interrupts until IDT is ready + // 在 IDT 就绪前禁用中断 + ASM("cli"); kernel_main(); while (1) ASM ("hlt"); } diff --git a/kernel/fs.cpp b/kernel/fs.cpp index a1b1ffd..ac45b23 100644 --- a/kernel/fs.cpp +++ b/kernel/fs.cpp @@ -144,6 +144,13 @@ static EFI_STATUS blk_read(struct block_dev *dev, UINT64 LBA, Sectors * dev->BlockSize, Buf); } +static EFI_STATUS blk_write(struct block_dev *dev, UINT64 LBA, + UINTN Sectors, const void *Buf) { + return uefi_call_wrapper(dev->Bio->WriteBlocks, 5, + dev->Bio, dev->Bio->Media->MediaId, LBA, + Sectors * dev->BlockSize, (void*)Buf); +} + // ---- Partition detection ---- static EFI_STATUS find_gpt_partition(struct block_dev *dev, UINT64 *StartLBA) { @@ -372,28 +379,32 @@ static UINT8 lfn_checksum(const UINT8 *SFN) { struct lfn_state { CHAR16 frags[LFN_MAX_FRAGS][LFN_FRAG_SIZE + 1]; UINTN count; + UINT8 checksum; }; static void lfn_reset(struct lfn_state *lfn) { lfn->count = 0; + lfn->checksum = 0; } static void lfn_add(struct lfn_state *lfn, const UINT8 *E) { if (lfn->count >= LFN_MAX_FRAGS) return; + lfn->checksum = E[13]; UINTN pos = 0; - for (SSINT32 i = 0; i < 5 && pos < LFN_FRAG_SIZE; i++) { + BOOLEAN done = FALSE; + for (SSINT32 i = 0; i < 5 && pos < LFN_FRAG_SIZE && !done; i++) { CHAR16 c = *(const UINT16*)(E + 1 + i * 2); - if (c == 0x0000 || c == 0xFFFF) { lfn->frags[lfn->count][pos] = 0; return; } + if (c == 0x0000 || c == 0xFFFF) { done = TRUE; break; } lfn->frags[lfn->count][pos++] = c; } - for (SSINT32 i = 0; i < 6 && pos < LFN_FRAG_SIZE; i++) { + for (SSINT32 i = 0; i < 6 && pos < LFN_FRAG_SIZE && !done; i++) { CHAR16 c = *(const UINT16*)(E + 14 + i * 2); - if (c == 0x0000 || c == 0xFFFF) { lfn->frags[lfn->count][pos] = 0; return; } + if (c == 0x0000 || c == 0xFFFF) { done = TRUE; break; } lfn->frags[lfn->count][pos++] = c; } - for (SSINT32 i = 0; i < 2 && pos < LFN_FRAG_SIZE; i++) { + for (SSINT32 i = 0; i < 2 && pos < LFN_FRAG_SIZE && !done; i++) { CHAR16 c = *(const UINT16*)(E + 28 + i * 2); - if (c == 0x0000 || c == 0xFFFF) { lfn->frags[lfn->count][pos] = 0; return; } + if (c == 0x0000 || c == 0xFFFF) { done = TRUE; break; } lfn->frags[lfn->count][pos++] = c; } lfn->frags[lfn->count][pos] = 0; @@ -431,10 +442,10 @@ static void sfn_to_name(const UINT8 *E, CHAR16 *out, UINTN out_size) { // ---- Directory reading ---- typedef void (*dir_cb)(void *Ctx, const CHAR16 *Name, UINT8 Attr, - UINT32 Size, UINT32 FirstClus); + UINT32 Size, UINT32 FirstClus, UINTN EntryOff); // Returns TRUE when end-of-directory reached -static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, +static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, UINTN SectBase, struct lfn_state *lfn, dir_cb Callback, void *Ctx) { for (UINTN off = 0; off < fs->BytsPerSec; off += 32) { @@ -445,7 +456,7 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, UINT8 Attr = E[11]; - if (Attr == 0x0F) { + if ((Attr & 0x3F) == 0x0F) { if (E[0] & 0x40) lfn_reset(lfn); lfn_add(lfn, E); continue; @@ -454,7 +465,7 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, // SFN entry CHAR16 Name[256]; BOOLEAN use_lfn = FALSE; - if (lfn->count > 0 && lfn_checksum(E) == E[13]) { + if (lfn->count > 0 && lfn_checksum(E) == lfn->checksum) { lfn_build(lfn, Name, 256); use_lfn = TRUE; } @@ -471,7 +482,7 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, else FirstClus = *(const UINT16*)(E + 26); - Callback(Ctx, Name, Attr, Size, FirstClus); + Callback(Ctx, Name, Attr, Size, FirstClus, SectBase + off); lfn_reset(lfn); } return FALSE; @@ -479,7 +490,8 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, static void read_directory(struct fat_fs *fs, UINT32 Cluster, dir_cb Callback, void *Ctx) { - UINT8 *Buf = (UINT8*)kmalloc(fs->BytsPerSec); + UINTN ClusBytes = fs->ClusSize; + UINT8 *Buf = (UINT8*)kmalloc(ClusBytes); if (!Buf) return; struct lfn_state lfn; @@ -489,10 +501,12 @@ static void read_directory(struct fat_fs *fs, UINT32 Cluster, UINT32 Clus = Cluster; while (Clus >= 2 && Clus < 0x0FFFFFF8) { UINT64 BaseLBA = clus_to_lba(fs, Clus); + if (EFI_ERROR(blk_read(fs->Dev, BaseLBA, fs->SecPerClus, Buf))) + goto done; for (UINTN s = 0; s < fs->SecPerClus; s++) { - if (EFI_ERROR(blk_read(fs->Dev, BaseLBA + s, 1, Buf))) - goto done; - if (process_sector(fs, Buf, &lfn, Callback, Ctx)) + if (process_sector(fs, Buf + s * fs->BytsPerSec, + s * fs->BytsPerSec, + &lfn, Callback, Ctx)) goto done; } Clus = fat_next(fs, Clus); @@ -501,19 +515,25 @@ static void read_directory(struct fat_fs *fs, UINT32 Cluster, UINT32 RootSecs = ((fs->RootEntCnt * 32) + fs->BytsPerSec - 1) / fs->BytsPerSec; UINT64 RootLBA = fs->PartLBA + fs->RsvdSecCnt + fs->NumFATs * fs->FATSz; for (UINTN s = 0; s < RootSecs; s++) { - if (EFI_ERROR(blk_read(fs->Dev, RootLBA + s, 1, Buf))) + if (EFI_ERROR(blk_read(fs->Dev, RootLBA + s, 1, Buf + s * fs->BytsPerSec))) goto done; - if (process_sector(fs, Buf, &lfn, Callback, Ctx)) + } + for (UINTN s = 0; s < RootSecs; s++) { + if (process_sector(fs, Buf + s * fs->BytsPerSec, + s * fs->BytsPerSec, + &lfn, Callback, Ctx)) goto done; } } else { UINT32 Clus = Cluster; while (Clus >= 2 && Clus < 0x0FFFFFF8) { UINT64 BaseLBA = clus_to_lba(fs, Clus); + if (EFI_ERROR(blk_read(fs->Dev, BaseLBA, fs->SecPerClus, Buf))) + goto done; for (UINTN s = 0; s < fs->SecPerClus; s++) { - if (EFI_ERROR(blk_read(fs->Dev, BaseLBA + s, 1, Buf))) - goto done; - if (process_sector(fs, Buf, &lfn, Callback, Ctx)) + if (process_sector(fs, Buf + s * fs->BytsPerSec, + s * fs->BytsPerSec, + &lfn, Callback, Ctx)) goto done; } Clus = fat_next(fs, Clus); @@ -541,7 +561,7 @@ static void name_to_ascii(const CHAR16 *Name, char *Ascii, UINTN ascii_sz) { } static void list_callback(void *ctx, const CHAR16 *Name, UINT8 Attr, - UINT32 Size, UINT32 FirstClus) { + UINT32 Size, UINT32 FirstClus, UINTN EntryOff) { struct list_ctx *lc = (struct list_ctx*)ctx; for (SSINT32 i = 0; i < lc->depth; i++) serial_write(" "); @@ -626,11 +646,18 @@ struct find_ctx { }; static BOOLEAN name_match(const CHAR16 *a, const CHAR16 *b) { - return wstr_eq((WString)a, (WString)b); + while (*a && *b) { + CHAR16 ca = *a, cb = *b; + if (ca >= L'A' && ca <= L'Z') ca += 32; + if (cb >= L'A' && cb <= L'Z') cb += 32; + if (ca != cb) return FALSE; + a++; b++; + } + return *a == 0 && *b == 0; } static void find_callback(void *ctx, const CHAR16 *Name, UINT8 Attr, - UINT32 Size, UINT32 FirstClus) { + UINT32 Size, UINT32 FirstClus, UINTN EntryOff) { struct find_ctx *fc = (struct find_ctx*)ctx; if (fc->Found) return; if (name_match(fc->Target, Name)) { @@ -673,16 +700,24 @@ EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) { UINTN ClusBytes = g_fs.ClusSize; while (Clus >= 2 && Clus < 0x0FFFFFF8 && Offset < FileSz) { - UINT64 LBA = clus_to_lba(&g_fs, Clus); - for (UINTN s = 0; s < g_fs.SecPerClus && Offset < FileSz; s++) { - UINTN Part = g_fs.BytsPerSec; - if (Part > FileSz - Offset) Part = FileSz - Offset; - EFI_STATUS st = blk_read(g_fs.Dev, LBA + s, 1, - (UINT8*)Buf + Offset); - if (EFI_ERROR(st)) { kfree(Buf); return st; } - Offset += Part; + // Find longest contiguous run starting at Clus + UINT32 RunStart = Clus; + UINT32 RunEnd = Clus; + UINT32 Next = fat_next(&g_fs, Clus); + while (Next == RunEnd + 1 && Next >= 2 && Next < 0x0FFFFFF8) { + RunEnd = Next; + Next = fat_next(&g_fs, RunEnd); } - Clus = fat_next(&g_fs, Clus); + UINTN RunClus = (UINTN)(RunEnd - RunStart + 1); + UINT64 LBA = clus_to_lba(&g_fs, RunStart); + UINTN Want = RunClus * ClusBytes; + if (Want > FileSz - Offset) Want = FileSz - Offset; + UINTN NSecs = (Want + g_fs.BytsPerSec - 1) / g_fs.BytsPerSec; + EFI_STATUS st = blk_read(g_fs.Dev, LBA, NSecs, + (UINT8*)Buf + Offset); + if (EFI_ERROR(st)) { kfree(Buf); return st; } + Offset += Want; + Clus = Next; } } @@ -697,3 +732,246 @@ EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) { return EFI_NOT_FOUND; } + +// ---- FAT allocation ---- + +static BOOLEAN fat_entry_free(struct fat_fs *fs, UINT32 Clus) { + if (Clus >= fs->FATEntries) return FALSE; + if (fs->IsFAT32) return (fs->FAT32[Clus] & 0x0FFFFFFF) == 0; + return fs->FAT16[Clus] == 0; +} + +static void fat_entry_set(struct fat_fs *fs, UINT32 Clus, UINT32 Val) { + if (Clus >= fs->FATEntries) return; + if (fs->IsFAT32) fs->FAT32[Clus] = Val; + else fs->FAT16[Clus] = (UINT16)Val; +} + +// Allocate `Count` free clusters and link them into a chain. +// Returns the first cluster of the new chain; the last entry is marked EOF. +static EFI_STATUS fat_alloc_chain(struct fat_fs *fs, UINT32 Count, UINT32 *OutFirst) { + UINT32 Found = 0; + UINT32 First = 0, Prev = 0; + UINT32 EOC = fs->IsFAT32 ? 0x0FFFFFFF : 0xFFFF; + + for (UINT32 i = 2; i < fs->FATEntries && Found < Count; i++) { + if (!fat_entry_free(fs, i)) continue; + if (Found == 0) First = i; + else fat_entry_set(fs, Prev, i); + Found++; + Prev = i; + } + if (Found < Count) return EFI_OUT_OF_RESOURCES; + fat_entry_set(fs, Prev, EOC); + *OutFirst = First; + return EFI_SUCCESS; +} + +// Write the in-memory FAT cache back to all FAT copies on disk. +static EFI_STATUS fat_flush(struct fat_fs *fs) { + for (UINTN f = 0; f < fs->NumFATs; f++) { + EFI_STATUS st = blk_write(fs->Dev, + fs->PartLBA + fs->RsvdSecCnt + f * fs->FATSz, + fs->FATSz, fs->FatBuf); + if (EFI_ERROR(st)) return st; + } + return EFI_SUCCESS; +} + +// ---- File writing ---- + +struct find_write_ctx { + const CHAR16 *Target; + BOOLEAN Found; + UINT32 Cluster; + UINT32 Size; + UINT8 Attr; + UINTN DirOffset; +}; + +static void find_write_callback(void *ctx, const CHAR16 *Name, UINT8 Attr, + UINT32 Size, UINT32 FirstClus, UINTN EntryOff) { + struct find_write_ctx *fc = (struct find_write_ctx*)ctx; + if (fc->Found) return; + if (name_match(fc->Target, Name)) { + fc->Found = TRUE; + fc->Cluster = FirstClus; + fc->Size = Size; + fc->Attr = Attr; + fc->DirOffset = EntryOff; + } +} + +// Update a dirent's size + first-cluster fields in-place inside `DirBuf` +// at the given byte offset. For FAT32 we also patch the high cluster word. +static void dirent_update_size(void *DirBuf, UINTN EntryOff, + UINT32 NewSize, UINT32 FirstClus, + BOOLEAN PatchFirstClus, BOOLEAN IsFAT32) { + UINT8 *E = (UINT8*)DirBuf + EntryOff; + *(UINT32*)(E + 28) = NewSize; + if (PatchFirstClus) { + if (IsFAT32) { + *(UINT16*)(E + 20) = (UINT16)(FirstClus >> 16); + *(UINT16*)(E + 26) = (UINT16)(FirstClus & 0xFFFF); + } else { + *(UINT16*)(E + 26) = (UINT16)(FirstClus & 0xFFFF); + } + } +} + +// Write [Data, Data+Size) into the file at Path, starting at byte Offset. +// Semantics: existing file only (no create); Offset must be <= current size. +// New file size = Offset + Size. Trailing clusters beyond the new size are +// left allocated (lost) — no truncate support. +EFI_STATUS fs_write(WString Path, const void *Data, UINTN Size, UINTN Offset) { + if (!g_fs_inited) return EFI_NOT_READY; + if (Data == NULL && Size > 0) return EFI_INVALID_PARAMETER; + + const CHAR16 *p = Path; + while (*p == L'\\') p++; + + UINT32 CurClus = g_fs.IsFAT32 ? g_fs.RootClus : 0; + + CHAR16 Comp[256]; + BOOLEAN Resolved = FALSE; + UINT32 FileFirst = 0, FileSize = 0, FileAttr = 0; + UINTN FileDirOff = 0; + + while (*p) { + UINTN ci = 0; + while (*p && *p != L'\\' && ci < 255) Comp[ci++] = *p++; + Comp[ci] = 0; + while (*p == L'\\') p++; + + struct find_write_ctx fc; + fc.Target = Comp; + fc.Found = FALSE; + read_directory(&g_fs, CurClus, find_write_callback, &fc); + if (!fc.Found) return EFI_NOT_FOUND; + + if (*p == 0) { + if (fc.Attr & 0x10) return EFI_INVALID_PARAMETER; + Resolved = TRUE; + FileFirst = fc.Cluster; + FileSize = fc.Size; + FileAttr = fc.Attr; + FileDirOff = fc.DirOffset; + } else { + if (!(fc.Attr & 0x10)) return EFI_NOT_FOUND; + CurClus = fc.Cluster; + } + } + if (!Resolved) return EFI_NOT_FOUND; + if (Offset > FileSize) return EFI_INVALID_PARAMETER; + + UINT32 NewSize = (UINT32)(Offset + Size); + UINT32 OldClusCount = (FileSize + g_fs.ClusSize - 1) / g_fs.ClusSize; + UINT32 NewClusCount = (NewSize + g_fs.ClusSize - 1) / g_fs.ClusSize; + UINT32 AddClusCount = NewClusCount - OldClusCount; + BOOLEAN FileWasEmpty = (FileSize == 0); + + // --- Extend cluster chain if we need more clusters --- + if (AddClusCount > 0) { + UINT32 NewFirst; + EFI_STATUS st = fat_alloc_chain(&g_fs, AddClusCount, &NewFirst); + if (EFI_ERROR(st)) return st; + + if (FileWasEmpty) { + FileFirst = NewFirst; + } else { + // Walk to the last cluster of the existing chain + UINT32 Cur = FileFirst; + UINT32 Next; + while (TRUE) { + Next = fat_next(&g_fs, Cur); + if (Next >= 0x0FFFFFF8) break; + Cur = Next; + } + // Cur is the last cluster; link to new chain + fat_entry_set(&g_fs, Cur, NewFirst); + } + } + + // --- Write data clusters --- + if (Size > 0) { + // Walk to the cluster containing byte Offset + UINT32 Clus = FileFirst; + UINTN SkipBytes = Offset; + while (SkipBytes >= g_fs.ClusSize) { + Clus = fat_next(&g_fs, Clus); + SkipBytes -= g_fs.ClusSize; + } + + void *ClusBuf = kmalloc(g_fs.ClusSize); + if (!ClusBuf) return EFI_OUT_OF_RESOURCES; + + const UINT8 *Src = (const UINT8*)Data; + UINTN Remaining = Size; + UINTN InClusOff = SkipBytes; + + while (Remaining > 0) { + UINT64 LBA = clus_to_lba(&g_fs, Clus); + UINTN SpaceInClus = g_fs.ClusSize - InClusOff; + UINTN Chunk = Remaining < SpaceInClus ? Remaining : SpaceInClus; + + if (InClusOff > 0 || Chunk < g_fs.ClusSize) { + EFI_STATUS rst = blk_read(g_fs.Dev, LBA, g_fs.SecPerClus, ClusBuf); + if (EFI_ERROR(rst)) { kfree(ClusBuf); return rst; } + mem_copy((UINT8*)ClusBuf + InClusOff, Src, Chunk); + EFI_STATUS wst = blk_write(g_fs.Dev, LBA, g_fs.SecPerClus, ClusBuf); + if (EFI_ERROR(wst)) { kfree(ClusBuf); return wst; } + } else { + EFI_STATUS wst = blk_write(g_fs.Dev, LBA, g_fs.SecPerClus, Src); + if (EFI_ERROR(wst)) { kfree(ClusBuf); return wst; } + } + + Src += Chunk; + Remaining -= Chunk; + InClusOff = 0; + if (Remaining > 0) { + Clus = fat_next(&g_fs, Clus); + } + } + + kfree(ClusBuf); + } + + // --- Update dirent (size, and first cluster if file was empty) --- + { + void *DirBuf; + UINT64 DirLBA; + UINTN DirNSecs; + UINTN DirBufBytes; + + if (g_fs.IsFAT32) { + DirLBA = clus_to_lba(&g_fs, CurClus); + DirNSecs = g_fs.SecPerClus; + DirBufBytes = g_fs.ClusSize; + } else if (CurClus == 0) { + UINT32 RootSecs = ((g_fs.RootEntCnt * 32) + g_fs.BytsPerSec - 1) / g_fs.BytsPerSec; + DirLBA = g_fs.PartLBA + g_fs.RsvdSecCnt + g_fs.NumFATs * g_fs.FATSz; + DirNSecs = RootSecs; + DirBufBytes = RootSecs * g_fs.BytsPerSec; + } else { + DirLBA = clus_to_lba(&g_fs, CurClus); + DirNSecs = g_fs.SecPerClus; + DirBufBytes = g_fs.ClusSize; + } + + DirBuf = kmalloc(DirBufBytes); + if (!DirBuf) return EFI_OUT_OF_RESOURCES; + + EFI_STATUS rst = blk_read(g_fs.Dev, DirLBA, DirNSecs, DirBuf); + if (EFI_ERROR(rst)) { kfree(DirBuf); return rst; } + + dirent_update_size(DirBuf, FileDirOff, NewSize, FileFirst, + FileWasEmpty, g_fs.IsFAT32); + + EFI_STATUS wst = blk_write(g_fs.Dev, DirLBA, DirNSecs, DirBuf); + kfree(DirBuf); + if (EFI_ERROR(wst)) return wst; + } + + // --- Flush FAT --- + return fat_flush(&g_fs); +} diff --git a/kernel/graphics/layer.cpp b/kernel/graphics/layer.cpp index 3d51a59..dd193a0 100644 --- a/kernel/graphics/layer.cpp +++ b/kernel/graphics/layer.cpp @@ -5,40 +5,39 @@ #include #include #include -#include -#include +#include +#include #include -// --- Layer list (sorted by z, lowest first) --- +// 图层列表(按 z 排序,最低在前) static layer_t g_layers[LAYER_MAX]; static UINT32 g_layer_count = 0; static layer_t* g_layer_list = NULL; -// Compositor back buffer +// 合成器后台缓冲区 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL* g_back_buffer = NULL; -// Focus tracking +// 焦点追踪 static layer_t* g_focused = NULL; -// Shift+F10 state (set by IRQ handler, consumed by compositor) +// Shift+F10 状态(由 IRQ 处理函数设置,合成器消费) static volatile bool g_shift_held = false; static volatile bool g_switch_pending = false; static volatile layer_t* g_switch_target = NULL; -// PS/2 scan code set 1 +// PS/2 扫描码集 1 #define PS2_F10 0x44 #define PS2_LSHIFT 0x2A #define PS2_RSHIFT 0x36 #define PS2_BREAK_BIT 0x80 -// Forward declare +// 前向声明 static void layer_insert_sorted(layer_t* layer); static void layer_remove(layer_t* layer); static layer_t* find_next_window(layer_t* from); -// --- PS/2 keyboard IRQ handler --- - +// PS/2 键盘 IRQ 处理 static void ps2_irq_handler(trap_frame* frame) { (void)frame; pic_send_eoi(1); @@ -63,8 +62,7 @@ static void ps2_irq_handler(trap_frame* frame) { } } -// --- Layer management --- - +// 图层管理 layer_t* layer_create(const char* name, layer_type_t type, UINT32 w, UINT32 h) { if (g_layer_count >= LAYER_MAX) { serial_write("LAYER: limit reached\n"); @@ -162,8 +160,7 @@ void layer_set_visible(layer_t* layer, bool visible) { layer->visible = visible; } -// --- Sorted insert/remove --- - +// 有序插入/移除 static void layer_insert_sorted(layer_t* layer) { layer->next = NULL; @@ -216,8 +213,7 @@ static layer_t* find_next_window(layer_t* from) { return NULL; } -// --- Initialization --- - +// 初始化 void layer_init(void) { UINT32 hr = g_gfx.hr; UINT32 vr = g_gfx.vr; @@ -235,7 +231,7 @@ void layer_init(void) { p++; } - // Register keyboard IRQ and unmask + // 注册键盘 IRQ 并取消屏蔽 idt_set_handler(PIC_IRQ_BASE + 1, ps2_irq_handler); pic_unmask_irq(1); @@ -244,8 +240,7 @@ void layer_init(void) { serial_write(" bytes)\n"); } -// --- Compositor task --- - +// 合成器任务 void layer_compositor_task(void) { serial_write("LAYER: compositor task running\n"); @@ -253,7 +248,7 @@ void layer_compositor_task(void) { UINT32 vr = g_gfx.vr; while (1) { - // Process deferred Shift+F10 switch (safe: not inside IRQ) + // 处理延迟的 Shift+F10 窗口切换 if (g_switch_pending) { g_switch_pending = false; layer_t* target = (layer_t*)g_switch_target; @@ -266,13 +261,13 @@ void layer_compositor_task(void) { } } - // Clear back buffer + // 清除后台缓冲区 EFI_GRAPHICS_OUTPUT_BLT_PIXEL black = {0, 0, 0, 0}; draw_set_target(g_back_buffer, hr, vr); draw_rect(0, 0, hr, vr, black); draw_set_default_target(); - // Composite layers from lowest z to highest + // 按 z 从低到高合成图层 layer_t* cur = g_layer_list; while (cur) { if (cur->visible && cur->buffer) { @@ -302,13 +297,13 @@ void layer_compositor_task(void) { cur = cur->next; } - // Blit to screen - g_gfx.GOP->Blt( + // Blit 到屏幕 + uefi_call_wrapper(g_gfx.GOP->Blt, 10, g_gfx.GOP, g_back_buffer, EfiBltBufferToVideo, - 0, 0, 0, 0, - hr, vr, 0 + (UINTN)0, (UINTN)0, (UINTN)0, (UINTN)0, + (UINTN)hr, (UINTN)vr, (UINTN)0 ); yield(); diff --git a/kernel/interrupt/gdt.cpp b/kernel/interrupt/gdt.cpp index 139446c..a570336 100644 --- a/kernel/interrupt/gdt.cpp +++ b/kernel/interrupt/gdt.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/kernel/interrupt/idt.cpp b/kernel/interrupt/idt.cpp index 3379b89..a9f3d84 100644 --- a/kernel/interrupt/idt.cpp +++ b/kernel/interrupt/idt.cpp @@ -1,10 +1,10 @@ -#include +#include #include #include -#include +#include #include -// Defined in isr.S — 256 ISR stubs +// isr.S 中定义的 256 个 ISR 桩函数 extern "C" void* isr_stub_table[256]; static idt_entry g_idt[256]; @@ -15,7 +15,7 @@ void idt_set_handler(UINT8 vector, isr_handler_t handler) { g_handlers[vector] = handler; } -// Called from isr.S common handler +// 由 isr.S 通用处理函数调用 extern "C" void isr_dispatch(trap_frame* frame) { UINT8 vector = (UINT8)frame->vector; @@ -31,14 +31,14 @@ extern "C" void isr_dispatch(trap_frame* frame) { } } -// IDT helpers (defined in idt_helpers.S) +// IDT 辅助函数(定义在 idt_helpers.S) extern "C" void idt_load(UINT64 base, UINT16 limit); static void idt_set_entry(UINT8 vector, UINT64 handler_addr) { g_idt[vector].offset_low = handler_addr & 0xFFFF; - g_idt[vector].selector = 0x08; // kernel code segment + g_idt[vector].selector = 0x08; // 内核代码段 g_idt[vector].ist = 0; - g_idt[vector].type_attr = 0x8E; // present, DPL=0, 64-bit interrupt gate + g_idt[vector].type_attr = 0x8E; // 存在,DPL=0,64 位中断门 g_idt[vector].offset_mid = (handler_addr >> 16) & 0xFFFF; g_idt[vector].offset_high = (handler_addr >> 32) & 0xFFFFFFFF; g_idt[vector].reserved = 0; @@ -47,18 +47,18 @@ static void idt_set_entry(UINT8 vector, UINT64 handler_addr) { void idt_init(void) { serial_write("IDT: initializing 256 entries\n"); - // Clear IDT + // 清空 IDT for (SSINT32 i = 0; i < 256; i++) { g_idt[i] = {0}; g_handlers[i] = NULL; } - // Install all 256 ISR stubs + // 安装所有 256 个 ISR 桩函数 for (SSINT32 i = 0; i < 256; i++) { idt_set_entry(i, (UINT64)isr_stub_table[i]); } - // Load IDT + // 加载 IDT g_idt_ptr.limit = sizeof(g_idt) - 1; g_idt_ptr.base = (UINT64)&g_idt[0]; idt_load(g_idt_ptr.base, g_idt_ptr.limit); diff --git a/kernel/interrupt/pic.cpp b/kernel/interrupt/pic.cpp index 298232a..b4af28b 100644 --- a/kernel/interrupt/pic.cpp +++ b/kernel/interrupt/pic.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -13,37 +13,37 @@ static inline UINT8 inb(UINT16 port) { return ret; } +// 慢速 PIC 的小延迟 static void pic_wait(void) { - // Small delay for slow PICs ASM("jmp 1f\n\t1: jmp 1f\n\t1:"); } void pic_init(void) { serial_write("PIC: remapping 8259 PIC\n"); - // Save masks + // 保存掩码 UINT8 mask1 = inb(PIC1_DATA); UINT8 mask2 = inb(PIC2_DATA); - // ICW1: begin initialization, ICW4 needed + // ICW1:开始初始化,需要 ICW4 outb(PIC1_CMD, 0x11); pic_wait(); outb(PIC2_CMD, 0x11); pic_wait(); - // ICW2: vector offset - outb(PIC1_DATA, PIC_IRQ_BASE); // IRQ 0-7 → vector 0x20-0x27 + // ICW2:向量偏移 + outb(PIC1_DATA, PIC_IRQ_BASE); // IRQ 0-7 → 向量 0x20-0x27 pic_wait(); - outb(PIC2_DATA, PIC_IRQ_BASE + 8); // IRQ 8-15 → vector 0x28-0x2F + outb(PIC2_DATA, PIC_IRQ_BASE + 8); // IRQ 8-15 → 向量 0x28-0x2F pic_wait(); - // ICW3: cascading - outb(PIC1_DATA, 0x04); pic_wait(); // slave on IRQ 2 - outb(PIC2_DATA, 0x02); pic_wait(); // cascade identity + // ICW3:级联 + outb(PIC1_DATA, 0x04); pic_wait(); // 从片在 IRQ 2 + outb(PIC2_DATA, 0x02); pic_wait(); // 级联标识 - // ICW4: 8086 mode + // ICW4:8086 模式 outb(PIC1_DATA, 0x01); pic_wait(); outb(PIC2_DATA, 0x01); pic_wait(); - // Restore masks + // 恢复掩码 outb(PIC1_DATA, mask1); outb(PIC2_DATA, mask2); diff --git a/kernel/interrupt/pit.cpp b/kernel/interrupt/pit.cpp index 4871437..322635e 100644 --- a/kernel/interrupt/pit.cpp +++ b/kernel/interrupt/pit.cpp @@ -1,7 +1,7 @@ -#include +#include #include #include -#include +#include #include static inline void outb(UINT16 port, UINT8 val) { @@ -25,14 +25,14 @@ void pit_init(void) { UINT32 divisor = PIT_BASE_FREQ / PIT_TICK_HZ; - // Command byte: channel 0, lobyte/hibyte, rate generator, binary + // 命令字节:通道 0,低/高字节,速率生成器,二进制 outb(PIT_COMMAND_PORT, 0x36); - // Send divisor (low byte first, then high byte) + // 发送除数(先低字节后高字节) outb(PIT_CHANNEL0_DATA, (UINT8)(divisor & 0xFF)); outb(PIT_CHANNEL0_DATA, (UINT8)((divisor >> 8) & 0xFF)); - // Unmask IRQ 0 (timer) + // 取消屏蔽 IRQ 0(定时器) pic_unmask_irq(0); serial_write("PIT: divisor = "); diff --git a/kernel/main.cpp b/kernel/main.cpp index 0aa185f..cc31259 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -10,10 +11,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include extern EFI_SYSTEM_TABLE *ST; @@ -49,16 +50,15 @@ inline void init_serial() { } } -// External: PIT IRQ handler defined in pit.cpp +// 外部 PIT 中断处理函数,定义在 pit.cpp extern "C" void pit_irq_handler(void); -// PIC IRQ handler — dispatches IRQ 0 (timer) +// PIC 中断处理 — 分发 IRQ 0(定时器) static void irq_handler(trap_frame* frame) { UINT8 vector = (UINT8)frame->vector; UINT8 irq = vector - PIC_IRQ_BASE; - // Send EOI BEFORE handling, so PIC can deliver new interrupts - // immediately after a context switch inside the handler. + // 先发送 EOI 再处理,这样上下文切换后 PIC 可以立即投递新中断 pic_send_eoi(irq); switch (irq) { @@ -80,7 +80,7 @@ extern "C" void kernel_main() { uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); serial_write("\n\n"); - // init memory managers + // 初始化内存管理器 serial_write("Sylva: init PMM...\n"); EFI_STATUS st = pmm_init(); if (EFI_ERROR(st)) { @@ -95,7 +95,7 @@ extern "C" void kernel_main() { serial_write("Sylva: init heap...\n"); init_heap(); - // test kmalloc/kfree + // 测试 kmalloc/kfree serial_write("Sylva: kmalloc test...\n"); void* p1 = kmalloc(64); void* p2 = kmalloc(128); @@ -133,7 +133,7 @@ extern "C" void kernel_main() { // pf_print("Welcome to Sylva OS!\n"); serial_write(" Kernel prepared well.\n"); - // --- Interrupt infrastructure --- + // 初始化中断基础设施 serial_write("Sylva: init GDT...\n"); gdt_init(); @@ -143,24 +143,24 @@ extern "C" void kernel_main() { serial_write("Sylva: init PIC...\n"); pic_init(); - // Register IRQ handler (vector 0x20 = PIC_IRQ_BASE + 0) + // 注册 IRQ 处理函数(向量 0x20 = PIC_IRQ_BASE + 0) idt_set_handler(PIC_IRQ_BASE + 0, irq_handler); serial_write("Sylva: init PIT...\n"); pit_init(); pit_set_tick_handler(scheduler_tick); - // Enable interrupts + // 启用中断 ASM("sti"); serial_write("Sylva: interrupts enabled\n"); - // --- Multitasking demo --- + // 创建多任务演示 serial_write("Sylva: creating tasks...\n"); - // Init compositor (allocates back buffer, registers keyboard handler) + // 初始化合成器(分配后台缓冲区,注册键盘处理) layer_init(); - // Create desktop layer (full screen, z=0) + // 创建桌面图层(全屏,z=0) layer_t* desktop = layer_create("desktop", LAYER_TYPE_DESKTOP, g_gfx.hr, g_gfx.vr); if (desktop) { layer_set_z(desktop, 0); @@ -171,7 +171,7 @@ extern "C" void kernel_main() { layer_set_pos(desktop, 0, 0); } - // Create window 1 (centered) + // 创建窗口 1(居中) layer_t* win1 = layer_create("window_1", LAYER_TYPE_WINDOW, 300, 200); if (win1) { layer_set_pos(win1, (SSINT32)(g_gfx.hr / 2) - 150, (SSINT32)(g_gfx.vr / 2) - 100); @@ -182,7 +182,7 @@ extern "C" void kernel_main() { draw_set_default_target(); } - // Create window 2 (offset from center) + // 创建窗口 2(偏离中心) layer_t* win2 = layer_create("window_2", LAYER_TYPE_WINDOW, 250, 180); if (win2) { layer_set_pos(win2, (SSINT32)(g_gfx.hr / 2) - 50, (SSINT32)(g_gfx.vr / 2) - 40); @@ -193,9 +193,74 @@ extern "C" void kernel_main() { draw_set_default_target(); } - // Compositor task (replaces the old demo tasks) + // 合成器任务 task_create("compositor", layer_compositor_task); + serial_write("Sylva: disk read benchmark...\n"); + void *ttf_buf = NULL; + UINTN ttf_size = 0; + UINT64 t0 = pit_get_ticks(); + EFI_STATUS rd_st = fs_read((WString)L"sys\\resources\\LXGWWenKai-Light.ttf", &ttf_buf, &ttf_size); + UINT64 t1 = pit_get_ticks(); + if (EFI_ERROR(rd_st)) { + serial_write("Sylva: fs_read FAILED: "); + serial_write_hex(rd_st); + serial_write("\n"); + serial_write("Test done.\n\n"); + } else { + UINT64 ticks = t1 - t0; + UINT64 ms = ticks * (1000 / PIT_TICK_HZ); + UINT64 kbps = ms ? (ttf_size * 1000ULL) / (ms * 1024ULL) : 0; + serial_write("Sylva: read "); + serial_write_hex(ttf_size); + serial_write(" bytes in "); + serial_write_hex(ms); + serial_write(" ms ("); + serial_write_hex(kbps); + serial_write(" KiB/s)\n"); + + // TTF 渲染演示 + serial_write("Sylva: ttf_open...\n"); + ttf_face_t* face = ttf_open(ttf_buf, ttf_size); + if (!face) { + serial_write("Sylva: ttf_open FAILED\n"); + } else { + serial_write("Sylva: ttf_open OK\n"); + + // 创建 TTF 文本覆盖图层 + const UINT32 TL_W = 500, TL_H = 200; + layer_t* text_layer = layer_create("ttf_text", LAYER_TYPE_WINDOW, TL_W, TL_H); + if (text_layer) { + layer_set_z(text_layer, 3); + layer_set_pos(text_layer, 100, 300); + EFI_GRAPHICS_OUTPUT_BLT_PIXEL clear = {0, 0, 0, 0}; + draw_set_target(text_layer->buffer, TL_W, TL_H); + draw_rect(0, 0, TL_W - 1, TL_H - 1, clear); + + // 渲染多种字号和中日韩字符 + EFI_GRAPHICS_OUTPUT_BLT_PIXEL white = {255, 255, 255, 0}; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL yellow = {255, 240, 80, 0}; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL cyan = {80, 220, 255, 0}; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL pink = {255, 160, 200, 0}; + + UINT32 ttf_t0 = pit_get_ticks(); + ttf_draw_text(face, "Hello, Sylva OS!", + 40, 60, 24, white); + ttf_draw_text(face, "欢迎来到Sylva系统!", + 40, 110, 32, yellow); + UINT32 ttf_t1 = pit_get_ticks(); + draw_set_default_target(); + serial_write("Sylva: ttf render in "); + serial_write_hex((UINT64)((ttf_t1 - ttf_t0) * (1000 / PIT_TICK_HZ))); + serial_write(" ms\n"); + } + ttf_close(face); + } + kfree(ttf_buf); + } + serial_write("Test done.\n\n"); + + serial_write("Sylva: starting preemptive scheduler\n"); scheduler_run(); // never returns } diff --git a/kernel/memory/heap.cpp b/kernel/memory/heap.cpp index cc581bd..a3adae8 100644 --- a/kernel/memory/heap.cpp +++ b/kernel/memory/heap.cpp @@ -42,7 +42,7 @@ static void heap_expand(UINTN min_size) { new_block->size = pages * PAGE_SIZE; new_block->next = NULL; - // Add to free list (sorted by address for coalescing) + // 添加到空闲链表(按地址排序以便合并) struct heap_block** prev = &g_heap_free_list; while (*prev && (UINT8*)*prev < (UINT8*)new_block) { prev = &(*prev)->next; @@ -50,7 +50,7 @@ static void heap_expand(UINTN min_size) { new_block->next = *prev; *prev = new_block; - // Try to merge with the previous free block if adjacent + // 尝试与前一个空闲块合并(如果相邻) if (prev != &g_heap_free_list) { struct heap_block* prev_block = g_heap_free_list; while (prev_block->next != new_block) { @@ -104,21 +104,21 @@ void* kmalloc(UINTN size) { while (*prev) { UINTN block_sz = BLOCK_SIZE(*prev); if (block_sz >= alloc_size) { - // Found a suitable block + // 找到合适的块 struct heap_block* block = *prev; - // Split if remaining space is useful + // 如果剩余空间足够则分割 if (block_sz >= alloc_size + MIN_BLOCK_SIZE) { struct heap_block* split = (struct heap_block*)((UINT8*)block + alloc_size); split->size = block_sz - alloc_size; - // Insert split into free list + // 将分割后的块插入空闲链表 split->next = block->next; block->size = alloc_size | 1; *prev = split; } else { - // Use the whole block + // 使用整个块 *prev = block->next; - block->size = block_sz | 1; // mark used + block->size = block_sz | 1; // 标记为已使用 } if (size > 1024) { @@ -134,11 +134,11 @@ void* kmalloc(UINTN size) { prev = &(*prev)->next; } - // Out of memory in current heap — expand + // 当前堆空间不足,扩展堆 UINTN expand_size = alloc_size > PAGE_SIZE ? alloc_size : PAGE_SIZE; heap_expand(expand_size); - // Retry after expansion + // 扩展后重试 return kmalloc(size); } @@ -151,14 +151,14 @@ void kfree(void* ptr) { return; } - // Mark as free + // 标记为空闲 block->size &= ~(UINTN)1; - // Merge with next block if it's free + // 与下一个空闲块合并 struct heap_block* next = next_block(block); if ((UINT8*)next < (UINT8*)g_heap_end) { if (IS_FREE(next)) { - // Remove next from free list and merge + // 从空闲链表中移除 next 并合并 block->size += next->size; struct heap_block** prev = &g_heap_free_list; while (*prev && *prev != next) { @@ -168,7 +168,7 @@ void kfree(void* ptr) { } } - // Insert block into free list + // 将块插入空闲链表 struct heap_block** prev = &g_heap_free_list; while (*prev && (UINT8*)*prev < (UINT8*)block) { prev = &(*prev)->next; @@ -201,7 +201,7 @@ void* krealloc(void* ptr, UINTN new_size) { UINTN old_size = BLOCK_SIZE(block) - HEADER_SIZE; if (old_size >= new_size) { - // Can we split the shrinkage? + // 能否分割缩小的部分? UINTN shrink = old_size - new_size; if (shrink >= MIN_BLOCK_SIZE) { block->size = (new_size + HEADER_SIZE) | 1; diff --git a/kernel/memory/pmm.cpp b/kernel/memory/pmm.cpp index d3cec15..d92c632 100644 --- a/kernel/memory/pmm.cpp +++ b/kernel/memory/pmm.cpp @@ -19,7 +19,7 @@ static inline BOOLEAN bitmap_test(UINTN idx) { return (g_pmm.bitmap[idx / 8] >> (idx % 8)) & 1; } -// Clean stale entries from free list head +// 清理空闲链表头部的过期条目 static void clean_free_list() { while (g_pmm.free_list_head != NULL && bitmap_test((UINTN)g_pmm.free_list_head / PAGE_SIZE)) { @@ -58,7 +58,7 @@ EFI_STATUS pmm_init() { UINTN entry_count = map_size / desc_size; - // First pass: count total pages and find max physical address + // 第一遍:统计总页数并找到最大物理地址 UINT64 max_addr = 0; UINT64 total_free = 0; for (UINTN i = 0; i < entry_count; i++) { @@ -73,16 +73,16 @@ EFI_STATUS pmm_init() { g_pmm.base_addr = 0; g_pmm.max_addr = max_addr; - // How many pages does the bitmap cover? + // 位图覆盖的页数 UINTN total_pages = (UINTN)(max_addr / PAGE_SIZE); g_pmm.total_pages = total_pages; - // Bitmap size in bytes, rounded up to page boundary + // 位图大小(字节),向上取整到页边界 g_pmm.bitmap_size = ((total_pages + 7) / 8); UINTN bitmap_pages = (g_pmm.bitmap_size + PAGE_SIZE - 1) / PAGE_SIZE; g_pmm.bitmap_size = bitmap_pages * PAGE_SIZE; // round to full pages - // Place bitmap at the end of the highest free conventional memory region + // 将位图放在最高空闲常规内存区域的末尾 UINT64 bitmap_addr = 0; for (UINTN i = 0; i < entry_count; i++) { EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)mem_map + i * desc_size); @@ -105,12 +105,12 @@ EFI_STATUS pmm_init() { g_pmm.bitmap = (UINT8*)(UINTN)bitmap_addr; - // Init bitmap: mark ALL pages as used (0xFF) + // 初始化位图:将所有页标记为已使用 for (UINTN i = 0; i < g_pmm.bitmap_size; i++) { g_pmm.bitmap[i] = 0xFF; } - // Mark free pages (EfiConventionalMemory) as free in bitmap + // 将空闲页(EfiConventionalMemory)在位图中标记为空闲 g_pmm.free_pages = 0; UINT64 bm_start_page = bitmap_addr / PAGE_SIZE; UINT64 bm_end_page = (bitmap_addr + g_pmm.bitmap_size + PAGE_SIZE - 1) / PAGE_SIZE; @@ -123,19 +123,19 @@ EFI_STATUS pmm_init() { UINT64 end_page = start_page + desc->NumberOfPages; for (UINT64 p = start_page; p < end_page; p++) { - // Skip bitmap pages + // 跳过位图占用的页 if (p >= bm_start_page && p < bm_end_page) continue; bitmap_clear((UINTN)p); g_pmm.free_pages++; } } - // Mark bitmap pages as used + // 将位图占用的页标记为已使用 for (UINT64 p = bm_start_page; p < bm_end_page; p++) { bitmap_set((UINTN)p); } - // Reserve low memory (first 4 MB) — UEFI firmware may use it during BS calls + // 保留低内存(前 4MB)— 固件可能在 Boot Services 调用期间使用 UINT64 low_reserve_pages = 0x400; for (UINT64 p = 0; p < low_reserve_pages && p < g_pmm.total_pages; p++) { if (!bitmap_test((UINTN)p)) { @@ -144,7 +144,7 @@ EFI_STATUS pmm_init() { } } - // Build free list by linking free pages + // 通过链接空闲页构建空闲链表 g_pmm.free_list_head = NULL; void* prev = NULL; for (UINTN i = 0; i < entry_count; i++) { diff --git a/kernel/scheduler/scheduler.cpp b/kernel/scheduler/scheduler.cpp index 52e5c02..bfcb5cf 100644 --- a/kernel/scheduler/scheduler.cpp +++ b/kernel/scheduler/scheduler.cpp @@ -1,20 +1,20 @@ #include -#include -#include +#include +#include #include #include #include #include -// Assembly: context_switch(UINT64* old_rsp, UINT64 new_rsp) +// 汇编函数:context_switch(UINT64* old_rsp, UINT64 new_rsp) extern "C" void context_switch(UINT64* old_rsp, UINT64 new_rsp); static task_t g_tasks[TASK_MAX]; static UINT32 g_task_count = 0; static task_t* g_current = NULL; -static task_t* g_task_list = NULL; // circular linked list head +static task_t* g_task_list = NULL; // 循环链表头 -// Trampoline: first thing a new task runs after context_switch. +// 跳板函数:新任务在 context_switch 后首先执行的函数 static void (*g_task_entries[TASK_MAX])(void); extern "C" void task_entry_trampoline() { @@ -37,7 +37,7 @@ task_t* task_create(const char* name, void (*entry)(void)) { g_task_entries[id] = entry; - // Allocate kernel stack + // 分配内核栈 UINTN stack_pages = TASK_STACK_SIZE / PAGE_SIZE; void* stack = pmm_alloc_pages(stack_pages); if (!stack) { @@ -52,24 +52,21 @@ task_t* task_create(const char* name, void (*entry)(void)) { task->stack_base = stack; task->time_slice = TIME_SLICE_DEFAULT; - // Copy name + // 复制任务名称 str_copy(task->name, name, TASK_NAME_LEN); - // Set up initial stack for first context_switch into this task. - // Stack grows downward. context_switch will pop 6 regs then ret. + // 设置首次 context_switch 时的初始栈 + // 栈向下增长。context_switch 会弹出 6 个寄存器然后 ret。 // - // Layout (high addr -> low addr): - // [stack + TASK_STACK_SIZE] <- top - // return addr = task_entry_trampoline (ret goes here) + // 布局(高地址 → 低地址): + // [stack + TASK_STACK_SIZE] <- 栈顶 + // 返回地址 = task_entry_trampoline(ret 跳转到这里) // rbx = 0 // rbp = 0 // r12 = 0 // r13 = 0 // r14 = 0 - // r15 = 0 <- RSP points here initially - // - // When preempted by timer IRQ, the ISR stub saves a full trap_frame - // on the task's stack — that layout is only created by hardware+ISR. + // r15 = 0 <- RSP 初始指向这里 // UINT64* sp = (UINT64*)((UINT8*)stack + TASK_STACK_SIZE); @@ -84,7 +81,7 @@ task_t* task_create(const char* name, void (*entry)(void)) { task->rsp = (UINT64)sp; - // Insert into circular linked list + // 插入循环链表 if (g_task_list == NULL) { task->next = task; g_task_list = task; @@ -103,7 +100,7 @@ task_t* task_create(const char* name, void (*entry)(void)) { return task; } -// Find next READY task in the circular list, starting from g_current->next +// 在循环链表中查找下一个就绪任务 static task_t* find_next_ready(void) { if (g_current == NULL || g_task_list == NULL) return NULL; @@ -117,7 +114,7 @@ static task_t* find_next_ready(void) { next = next->next; } while (next != start); - return NULL; // no READY tasks + return NULL; // 没有就绪任务 } void yield(void) { @@ -137,27 +134,27 @@ void yield(void) { context_switch(&cur->rsp, next->rsp); } -// Timer tick handler — called from PIT IRQ 0 +// 定时器 tick 处理 — 由 PIT IRQ 0 调用 void scheduler_tick(void) { if (g_current == NULL) return; - // Decrement time slice + // 递减时间片 if (g_current->time_slice > 0) { g_current->time_slice--; } - // If time slice expired, preempt + // 时间片用完则抢占 if (g_current->time_slice == 0) { task_t* cur = g_current; task_t* next = find_next_ready(); if (next == NULL || next == cur) { - // No other task ready, or only this task — reload time slice + // 没有其他就绪任务,或仅此一个 — 重置时间片 cur->time_slice = TIME_SLICE_DEFAULT; return; } - // Preempt + // 抢占 cur->state = TASK_STATE_READY; cur->time_slice = TIME_SLICE_DEFAULT; next->state = TASK_STATE_RUNNING; @@ -174,7 +171,7 @@ void scheduler_run(void) { return; } - // Find first READY task + // 查找第一个就绪任务 task_t* start = g_task_list->next; task_t* t = start; do { @@ -197,12 +194,12 @@ void scheduler_run(void) { serial_write(t->name); serial_write("'\n"); - // First context switch — switch to the task's stack - // This will never return (until all tasks terminate) + // 首次上下文切换 — 切换到任务栈 + // 此后不会返回(直到所有任务终止) UINT64 dummy_rsp; context_switch(&dummy_rsp, t->rsp); - // We only return here when ALL tasks are terminated + // 只有所有任务终止后才会返回到这里 serial_write("SCHED: all tasks finished\n"); while (1) ASM ("hlt"); } @@ -216,10 +213,10 @@ void task_exit(void) { g_current->state = TASK_STATE_TERMINATED; - // Yield to next task — we won't come back + // 让出 CPU 给下一个任务 — 不会回来 yield(); - // Should never reach here + // 不应到达此处 while (1) ASM ("hlt"); } diff --git a/kernel/serial.cpp b/kernel/serial.cpp index 15e494e..7d59a56 100644 --- a/kernel/serial.cpp +++ b/kernel/serial.cpp @@ -34,18 +34,28 @@ void serial_write(String str) { void serial_write_hex(UINTN val) { char buf[19]; - buf[0] = '0'; buf[1] = 'x'; - for (SSINT32 i = 17; i >= 2; i--) { - UINTN digit = val & 0xF; - buf[i] = digit < 10 ? '0' + digit : 'A' + digit - 10; - val >>= 4; + buf[0] = '0'; + buf[1] = 'x'; + SSINT32 pos = 2; + if (val == 0) { + buf[pos++] = '0'; + } else { + char tmp[17]; + SSINT32 len = 0; + while (val) { + UINTN digit = val & 0xF; + tmp[len++] = digit < 10 ? '0' + digit : 'A' + digit - 10; + val >>= 4; + } + for (SSINT32 i = len - 1; i >= 0; i--) { + buf[pos++] = tmp[i]; + } } - buf[18] = '\0'; + buf[pos] = '\0'; serial_write(buf); } char serial_read_char() { - // 后面可能用的上,比如远程调试? if (!g_serial.SerialIo) return 0; char c = 0; UINTN size = 1; diff --git a/resources/LXGWWenKai-Light.ttf b/resources/LXGWWenKai-Light.ttf new file mode 100644 index 0000000..d1772b2 Binary files /dev/null and b/resources/LXGWWenKai-Light.ttf differ