diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml new file mode 100644 index 0000000..c75bf29 --- /dev/null +++ b/.github/workflows/build-linux.yml @@ -0,0 +1,135 @@ +name: build-linux + +on: + push: + branches: [ main, master ] + tags: [ 'v*' ] + pull_request: + branches: [ main, master ] + workflow_dispatch: + +jobs: + build: + name: Build paze for Linux x64 + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET 10 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + dotnet-quality: 'preview' + + - name: Restore + run: dotnet restore src/PazeE.Compiler/PazeE.Compiler.csproj + + - name: Determine channel & version + id: meta + run: | + if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then + CHANNEL="release" + else + CHANNEL="alpha" + fi + VERSION=$(dotnet msbuild src/PazeE.Compiler/PazeE.Compiler.csproj \ + -p:BuildChannel=$CHANNEL \ + -getProperty:Version \ + -nologo) + echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Channel: $CHANNEL Version: $VERSION" + + - name: Publish (self-contained, linux-x64) + run: | + dotnet publish src/PazeE.Compiler/PazeE.Compiler.csproj \ + -c Release \ + -p:BuildChannel=${{ steps.meta.outputs.channel }} \ + -r linux-x64 \ + --self-contained true \ + -p:PublishSingleFile=true \ + -p:EnableCompressionInSingleFile=true + + - name: Stage artifacts + run: | + STAGE="staging/paze-${{ steps.meta.outputs.version }}-linux-x64" + mkdir -p "$STAGE/bin" + cp -v publish/${{ steps.meta.outputs.version }}/bin/* "$STAGE/bin/" 2>/dev/null || true + # 单文件模式下 paze 主程序即 paze.dll/paze.exe → 重命名为 paze + if [ -f "$STAGE/bin/paze" ]; then + chmod +x "$STAGE/bin/paze" + fi + # 包含测试源文件以便验证 + mkdir -p "$STAGE/tests" + cp -v tests/*.pe "$STAGE/tests/" 2>/dev/null || true + # 生成版本说明 + cat > "$STAGE/VERSION.txt" < s, int name, int info, int other, int shndx, long value) diff --git a/src/PazeE.Compiler/Binary/ElfWriterLos4.cs b/src/PazeE.Compiler/Binary/ElfWriterLos4.cs index b9c49d5..c6aa727 100644 --- a/src/PazeE.Compiler/Binary/ElfWriterLos4.cs +++ b/src/PazeE.Compiler/Binary/ElfWriterLos4.cs @@ -5,7 +5,7 @@ namespace PazeE.Compiler.Binary; /// LeonOS 4 ELF64 (x86-64) 静态可执行文件写入器。 /// 无动态链接(无 PLT/GOT/.dynsym/.dynamic/PT_INTERP),运行时函数由 Los4Runtime 静态注入 .text。 -/// 系统调用通过 int 0x80(与 Linux x86_64 同调用号),ELF 布局与 doomlauncher.elf 一致: +/// 系统调用通过 syscall 指令(0x0F 0x05,寄存器约定同 Linux x86_64),ELF 布局与 doomlauncher.elf 一致: /// 3 个 PT_LOAD(.text RX / .rodata R / .data+.bss RW)+ PT_GNU_STACK,基址 0x400000。 public sealed class ElfWriterLos4 : IExecutableWriter { @@ -26,7 +26,7 @@ public sealed class ElfWriterLos4 : IExecutableWriter var data = new List(img.Data.Data); // ---- 2. 静态运行时(先于布局,以便获取 BSS 大小)---- - var (rtCode, rtOffsets, rdataRefs, bssRefs, rtBssSize) = Los4Runtime.Generate(); + var (rtCode, rtOffsets, rdataRefs, rdataBinRefs, bssRefs, rtBssSize) = Los4Runtime.Generate(); // BSS = 用户 BSS + 运行时 BSS(gui_fd/gui_win_id 等持久状态) int userBssSize = img.Bss.BssSize; @@ -56,6 +56,24 @@ public sealed class ElfWriterLos4 : IExecutableWriter } } + // ---- 2c. 运行时引用的 .rodata 二进制数据 → 追加到 rodata,记录偏移 ---- + // 如 PSF 字体数据(FontData 2048 字节),由 paze_font_ptr() 返回其 rodata 地址。 + var rdataBinOff = new List<(byte[] data, long offset)>(); + foreach (var (off, binData) in rdataBinRefs) + { + long binOffset = -1; + for (int i = 0; i < rdataBinOff.Count; i++) + { + if (ReferenceEquals(rdataBinOff[i].data, binData)) { binOffset = rdataBinOff[i].offset; break; } + } + if (binOffset < 0) + { + binOffset = rodata.Count; + rodata.AddRange(binData); + rdataBinOff.Add((binData, binOffset)); + } + } + // ---- 3. 收集所有 fixup ---- var fixups = new List<(ImageSection sec, int off, FixupKind kind, string sym)>(); foreach (var f in img.Fixups) fixups.Add((f.Section, f.Offset, f.Kind, f.Symbol)); @@ -115,6 +133,22 @@ public sealed class ElfWriterLos4 : IExecutableWriter Write64At(text, rtBase + off, strVaddr); } + // ---- 运行时 RData 二进制数据引用回填 ---- + // 运行时代码中 mov r64, &bin 的 8 字节占位 → 填入二进制数据在 .rodata 的虚拟地址 + foreach (var (off, binData) in rdataBinRefs) + { + long binOffset = -1; + for (int i = 0; i < rdataBinOff.Count; i++) + { + if (ReferenceEquals(rdataBinOff[i].data, binData)) { binOffset = rdataBinOff[i].offset; break; } + } + if (binOffset >= 0) + { + long binVaddr = rodataVaddr + binOffset; + Write64At(text, rtBase + off, binVaddr); + } + } + // ---- 运行时 BSS 全局引用回填 ---- // 运行时 BSS 紧跟用户 BSS:全局在 [bssVaddr + userBssSize + bssOff] 处。 foreach (var (off, name, bssOff) in bssRefs) @@ -200,12 +234,12 @@ public sealed class ElfWriterLos4 : IExecutableWriter { Write32At(f, type); Write32At(f, flags); - Write64At(f, off); - Write64At(f, vaddr); - Write64At(f, vaddr); // p_paddr = p_vaddr - Write64At(f, filesz); - Write64At(f, memsz); - Write64At(f, align); + int o1 = f.Count; Write64At(f, o1, off); + int o2 = f.Count; Write64At(f, o2, vaddr); + int o3 = f.Count; Write64At(f, o3, vaddr); // p_paddr = p_vaddr + int o4 = f.Count; Write64At(f, o4, filesz); + int o5 = f.Count; Write64At(f, o5, memsz); + int o6 = f.Count; Write64At(f, o6, align); } private static long Align(long v, int a) => a <= 1 ? v : (v + a - 1) & ~(long)(a - 1); diff --git a/src/PazeE.Compiler/Binary/Los4Runtime.cs b/src/PazeE.Compiler/Binary/Los4Runtime.cs index 0e49ea3..b7319cd 100644 --- a/src/PazeE.Compiler/Binary/Los4Runtime.cs +++ b/src/PazeE.Compiler/Binary/Los4Runtime.cs @@ -1,8 +1,9 @@ namespace PazeE.Compiler.Binary; -/// LeonOS 4 静态运行时:通过 int 0x80 系统调用提供标准 C 库函数实现。 +/// LeonOS 4 静态运行时:通过 syscall 指令提供标准 C 库函数实现。 /// 所有函数以 System V AMD64 ABI 调用,注入到 .text 段末尾,由 ElfWriterLos4 解析外部符号引用。 -/// LeonOS 4 系统调用约定与 Linux x86_64 一致(rax=调用号, rdi/rsi/rdx...=参数),用 int 0x80 而非 syscall。 +/// LeonOS 4 系统调用约定与 Linux x86_64 一致(rax=调用号, rdi/rsi/rdx/r10/r8/r9=参数),使用 syscall 指令(0x0F 0x05)。 +/// GUI 设备由 LeonOS shell 预打开为 fd=3,所有 GUI 函数直接使用 fd=3(与 libc.a 行为一致)。 public static class Los4Runtime { public const int SYS_read = 0; @@ -24,6 +25,7 @@ public static class Los4Runtime private const long LEONOS_GUI_IOCTL_WINDOW_EVENT = 0x4c475745L; private const long LEONOS_GUI_IOCTL_WAIT_WINDOW_EVENT = 0x4c475457L; private const long LEONOS_GUI_IOCTL_DESTROY_WINDOW = 0x4c475744L; + private const long LEONOS_GUI_IOCTL_PRESENT_WINDOW = 0x4c475046L; // LeonOS GUI 应用事件类型(来自 gui.h LEONOS_GUI_APP_EVENT_*) private const int LEONOS_GUI_APP_EVENT_CLOSE = 1; @@ -36,12 +38,150 @@ public static class Los4Runtime private const int MAP_PRIVATE = 0x02; private const int MAP_ANONYMOUS = 0x20; - public static (byte[] code, Dictionary offsets, List<(int off, string content)> rdataRefs, List<(int off, string name, int bssOff)> bssRefs, int bssSize) Generate() + /// PSF1 8×16 位图字体的 ASCII 部分(128 字形 × 16 字节 = 2048 字节), + /// 从 lat15_vga16.psf 提取,用于 los2w 模拟器像素文本渲染(PRESENT_WINDOW)。 + private static readonly byte[] FontData = new byte[2048] { + 0x00,0x00,0x3C,0x42,0x99,0xA5,0xA1,0xA1,0xA5,0x99,0x42,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDB,0xDB,0x00,0x00,0x00,0x00, + 0x00,0x00,0xF1,0x5B,0x55,0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x10,0x38,0x7C,0xFE,0x7C,0x38,0x10,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xCC,0xCF,0xED,0xFF,0xFC,0xDF,0xCC,0xCC,0xCC,0xCC,0x00,0x00,0x00,0x00, + 0x00,0x00,0x03,0x3E,0x66,0xCF,0xDB,0xDB,0xF3,0x66,0x7C,0xC0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3C,0x3C,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x66,0x3C,0x66,0x66,0x66,0x3C,0x66,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x38,0x44,0xBA,0xB2,0xAA,0x44,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x70,0xD8,0x30,0x18,0xD8,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x0C,0x38,0x00, + 0x00,0x30,0x70,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xE0,0x30,0x62,0x36,0xEC,0x18,0x30,0x66,0xCE,0x9E,0x3E,0x06,0x06,0x00,0x00, + 0x60,0x30,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7F,0xDB,0xDB,0xDB,0x7B,0x1B,0x1B,0x1B,0x1B,0x1B,0x00,0x00,0x00,0x00, + 0x00,0x7C,0xC6,0x60,0x38,0x6C,0xC6,0xC6,0x6C,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00, + 0x0C,0x18,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x10,0x38,0x6C,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x3C,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0C,0x06,0xFF,0x06,0x0C,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xFF,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00, + 0x76,0xDC,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x30,0x18,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, + 0x10,0x38,0x44,0xFE,0x66,0x62,0x68,0x78,0x68,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, + 0x6C,0x6C,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, + 0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00, + 0x18,0x18,0x7C,0xC6,0xC2,0xC0,0x7C,0x06,0x06,0x86,0xC6,0x7C,0x18,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00, + 0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, + 0x00,0x30,0x30,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00, + 0x00,0x00,0x38,0x6C,0xC6,0xC6,0xD6,0xD6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00, + 0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00, + 0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00, + 0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7E,0x7E,0x5A,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x06,0x02,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00, + 0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00, + 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, + 0x00,0x00,0xE0,0x60,0x60,0x78,0x6C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x1C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x38,0x6C,0x64,0x60,0xF0,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,0x00, + 0x00,0x00,0xE0,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00, + 0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0xD6,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00, + 0x00,0x00,0x00,0x00,0x00,0xDC,0x76,0x66,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0x60,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x10,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00, + 0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00, + 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, + 0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00, + 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x18,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00 + }; + + public const int HeapPoolSize = 0x100000; // 1MB BSS 堆池 + + public static (byte[] code, Dictionary offsets, List<(int off, string content)> rdataRefs, List<(int off, byte[] data)> rdataBinRefs, List<(int off, string name, int bssOff)> bssRefs, int bssSize) Generate() { var b = new Rt(); - // 预分配 GUI 持久状态(BSS 全局):gui_fd, gui_win_id - b.AllocBss("gui_fd", 8); - b.AllocBss("gui_win_id", 8); + + // 分配 BSS 堆池与分配指针 + b.AllocBss("heap_pool", HeapPoolSize); + b.AllocBss("heap_ptr", 8); + b.AllocBss("gui_fd", 4); // GUI 设备 fd(打开后存储) + b.AllocBss("gui_win_id", 4); // 当前窗口 ID var o = new Dictionary(); void Reg(string n, Action g) { if (!o.ContainsKey(n)) { o[n] = b.Pos; b.MarkFunc(n); g(b); } } @@ -72,16 +212,31 @@ public static class Los4Runtime Reg("scanf", GenScanf); Reg("sscanf", GenSscanf); Reg("time_utc_raw", GenTimeGetUtcRaw); - // GUI 函数 - Reg("gui_init", GenGuiInit); - Reg("gui_window_create", GenGuiWindowCreate); - Reg("gui_window_text", GenGuiWindowText); - Reg("gui_window_present", GenGuiWindowPresent); - Reg("gui_event_poll", GenGuiEventPoll); - Reg("gui_event_wait", GenGuiEventWait); - Reg("gui_window_destroy", GenGuiWindowDestroy); - Reg("gui_cleanup", GenGuiCleanup); - return (b.Finish(), o, b.RdataRefs, b.BssRefs, b.BssSize); + Reg("open", GenOpen); + Reg("close", GenClose); + // Los4 GUI 辅助函数(GUI 逻辑由 LibcDecls.cs C 代码实现,仅提供底层 ioctl + 字体指针) + Reg("paze_ioctl", GenPazeIoctl); + Reg("paze_font_ptr", GenPazeFontPtr); + return (b.Finish(), o, b.RdataRefs, b.RdataBinRefs, b.BssRefs, b.BssSize); + } + + // ============ Los4 GUI 底层辅助 ============ + + /// int paze_ioctl(int fd, unsigned long req, void *arg) — ioctl 系统调用封装。 + /// 参数已在 SysV ABI 寄存器中(rdi=fd, rsi=req, rdx=arg),直接发 syscall。 + /// + private static void GenPazeIoctl(Rt b) + { + b.MovEax(SYS_ioctl); b.Syscall(); b.Ret(); + } + + /// unsigned char *paze_font_ptr(void) — 返回 PSF 字体数据在 .rodata 中的地址。 + /// FontData(2048 字节 = 128 字形 × 16 字节)由 ElfWriterLos4 追加到 rodata。 + /// + private static void GenPazeFontPtr(Rt b) + { + b.MovImm64RdataBinRef(0, FontData); // rax = &FontData (rodata) + b.Ret(); } // ============ 时间 ============ @@ -97,14 +252,14 @@ public static class Los4Runtime b.MovImm64RdataRef(7, Los4SystemDevice); // rdi = "0:/dev/leonos_system" b.Xor(6, 6); // rsi = 0 (O_RDONLY) b.Xor(2, 2); // rdx = 0 (mode) - b.MovEax(SYS_open); b.Int80(); // rax = fd + b.MovEax(SYS_open); b.Syscall(); // rax = fd b.StoreRbp(-8, 0); // [rbp-8] = fd // ioctl(fd, LEONOS_IOCTL_TIME_INFO, &buf): rdi=fd, rsi=request, rdx=&buf b.LoadRbp(7, -8); // rdi = fd b.MovImm64(6, LEONOS_IOCTL_TIME_INFO); // rsi = request b.LeaRbp(2, -80); // rdx = &buf - b.MovEax(SYS_ioctl); b.Int80(); + b.MovEax(SYS_ioctl); b.Syscall(); // rax = buf.unix_seconds (偏移 0) b.LoadRbp(0, -80); @@ -112,86 +267,72 @@ public static class Los4Runtime // close(fd)(保留 rax) b.PushR(0); b.LoadRbp(7, -8); - b.MovEax(SYS_close); b.Int80(); + b.MovEax(SYS_close); b.Syscall(); b.PopR(0); // rax = unix_seconds b.Leave(); b.Ret(); } // ============ GUI ============ - // LeonOS 4 GUI:open("/dev/leonos_gui") + ioctl 实现。gui_fd/gui_win_id 存于 BSS 全局。 - // 结构体布局(来自 leonos/gui.h): + // LeonOS 4 GUI:fd=3 硬编码(由 shell 预打开),ioctl 实现。 + // 与 libc.a 行为完全一致:所有 GUI 函数直接使用 fd=3,无需 open/close。 + // 系统调用使用 syscall 指令(0x0F 0x05),寄存器约定同 Linux x86_64。 + // 结构体布局(来自 leonos/gui.h,通过反编译 libc.a 验证): // leonos_gui_create { u32 width, height; char *title, *text; u32 flags; } = 32 字节 // leonos_fb_text { u32 x, y, fg, bg; char *text; } = 24 字节 // leonos_gui_app_event{ u32 window_id, type; i32 x,y,dx,dy; u32 width,height; - // u8 buttons, keycode, pressed, reserved; } = 40 字节 - // leonos_gui_wait_app_event { event[40]; u32 timeout_ms; } = 44 字节 + // u8 buttons, keycode, pressed, reserved; } = 36 字节 + // leonos_gui_wait_app_event { event[36]; u32 timeout_ms; } = 40 字节 + // ioctl arg 约定(反编译 libc.a 确认):CREATE/FB_TEXT/WINDOW_EVENT/WAIT 传结构体指针; + // DESTROY_WINDOW 传 window_id 值(非指针)。 - // int gui_init(void) — 打开 GUI 设备,fd 存入 gui_fd。 + // int gui_init(void) — 打开 GUI 设备 fd(los2w shell 不预打开 fd=3)。 private static void GenGuiInit(Rt b) { - int retOk = b.Lbl(), retFail = b.Lbl(); - b.PushRbp(); b.MovRbpRsp(); b.SubRsp(16); - // if (gui_fd != 0) return 0; - b.MovImm64BssRef(0, "gui_fd"); // rax = &gui_fd - b.LoadReg32(0, 0); // eax = gui_fd - b.Test(0, 0); - b.Jne(retOk); - // fd = open("0:/dev/leonos_gui", O_RDWR=2, 0) - b.MovImm64RdataRef(7, Los4GuiDevice); // rdi = device path - b.MovImm64(6, 2); // rsi = O_RDWR - b.Xor(2, 2); // rdx = 0 - b.MovEax(SYS_open); b.Int80(); // rax = fd - b.Test(0, 0); - b.Js(retFail); // fd < 0 → -1 - // gui_fd = fd - b.Mov(1, 0); // rcx = fd(保留) - b.MovImm64BssRef(0, "gui_fd"); // rax = &gui_fd - b.StoreReg32(0, 1); // [rax] = ecx - b.Mark(retOk); - b.Xor(0, 0); // return 0 - b.Leave(); b.Ret(); - b.Mark(retFail); - b.MovImm64(0, -1); // return -1 + b.PushRbp(); b.MovRbpRsp(); + // open("0:/dev/leonos_gui", O_RDWR=2, 0): rdi=path, rsi=2, rdx=0 + b.MovImm64RdataRef(7, Los4GuiDevice); + b.MovImm64(6, 2); // rsi = O_RDWR + b.Xor(2, 2); // rdx = 0 (mode) + b.MovEax(SYS_open); b.Syscall(); // rax = fd + // 保存到 BSS gui_fd + b.MovImm64BssRef(2, "gui_fd"); // rdx = &gui_fd + b.StoreReg32(2, 0); // *gui_fd = fd (int) b.Leave(); b.Ret(); } - // int gui_window_create(const char *title, int width, int height) + // int gui_window_create(const char *title, const char *text, int width, int height) private static void GenGuiWindowCreate(Rt b) { - // 参数:rdi=title(7), rsi=width(6), rdx=height(2) - // 栈布局:[rbp-8]=title, [rbp-16]=width, [rbp-24]=height, [rbp-56..-25]=gui_create(32 字节) + // 参数:rdi=title(7), rsi=text(6), rdx=width(2), rcx=height(1) + // 栈布局:[rbp-8]=title, [rbp-16]=text, [rbp-24]=width, [rbp-32]=height, [rbp-64..-33]=gui_create(32 字节) int retFail = b.Lbl(); - b.PushRbp(); b.MovRbpRsp(); b.SubRsp(64); + b.PushRbp(); b.MovRbpRsp(); b.SubRsp(72); b.StoreRbp(-8, 7); // 保存 title - b.StoreRbp(-16, 6); // 保存 width - b.StoreRbp(-24, 2); // 保存 height - // gui_init()(内部已处理重复调用) + b.StoreRbp(-16, 6); // 保存 text + b.StoreRbp(-24, 2); // 保存 width + b.StoreRbp(-32, 1); // 保存 height + // gui_init()(打开 GUI 设备,fd 存入 BSS gui_fd) b.CallName("gui_init"); - b.Test(0, 0); - b.Js(retFail); // gui_init < 0 → -1 - // 构造 leonos_gui_create @ [rbp-56],用 rdi 作游标逐字段填充 - b.LeaRbp(7, -56); // rdi = &gc - b.LoadRbp(0, -16); b.StoreReg32(7, 0); // gc.width = width ([rdi+0]) + // 构造 leonos_gui_create @ [rbp-64],用 rdi 作游标逐字段填充 + b.LeaRbp(7, -64); // rdi = &gc + b.LoadRbp(0, -24); b.StoreReg32(7, 0); // gc.width = width ([rdi+0]) b.AddImm(7, 4); - b.LoadRbp(0, -24); b.StoreReg32(7, 0); // gc.height = height ([rdi+4]) + b.LoadRbp(0, -32); b.StoreReg32(7, 0); // gc.height = height ([rdi+4]) b.AddImm(7, 4); b.LoadRbp(0, -8); b.StoreReg64(7, 0); // gc.title = title ([rdi+8]) b.AddImm(7, 8); - b.Xor(0, 0); b.StoreReg64(7, 0); // gc.text = NULL ([rdi+16]) + b.LoadRbp(0, -16); b.StoreReg64(7, 0); // gc.text = text ([rdi+16]) — 初始窗口文本 b.AddImm(7, 8); b.Xor(0, 0); b.StoreReg32(7, 0); // gc.flags = 0 ([rdi+24]) // ioctl(gui_fd, CREATE_WINDOW, &gc) - b.MovImm64BssRef(7, "gui_fd"); b.LoadReg32(7, 7); // rdi = gui_fd + b.MovImm64BssRef(7, "gui_fd"); // rdi = &gui_fd + b.LoadReg32(7, 0); // rdi = gui_fd (int) b.MovImm64(6, LEONOS_GUI_IOCTL_CREATE_WINDOW); // rsi = request - b.LeaRbp(2, -56); // rdx = &gc - b.MovEax(SYS_ioctl); b.Int80(); // rax = window_id + b.LeaRbp(2, -64); // rdx = &gc + b.MovEax(SYS_ioctl); b.Syscall(); // rax = window_id b.Test(0, 0); - b.Js(retFail); // < 0 → -1 - // gui_win_id = rax - b.Mov(1, 0); // rcx = id(保留) - b.MovImm64BssRef(0, "gui_win_id"); - b.StoreReg32(0, 1); // gui_win_id = id + b.Jle(retFail); // <= 0 → -1(与 libc.a 一致:result > 0 才成功) b.Xor(0, 0); // return 0 b.Leave(); b.Ret(); b.Mark(retFail); @@ -213,10 +354,11 @@ public static class Los4Runtime b.AddImm(10, 4); b.StoreReg32(10, 9); // [r10+12]= bg (r9) b.AddImm(10, 4); b.StoreReg64(10, 1); // [r10+16]= text(rcx) // ioctl(gui_fd, FB_TEXT, &fbtext) - b.MovImm64BssRef(7, "gui_fd"); b.LoadReg32(7, 7); // rdi = gui_fd + b.MovImm64BssRef(7, "gui_fd"); // rdi = &gui_fd + b.LoadReg32(7, 0); // rdi = gui_fd b.MovImm64(6, LEONOS_GUI_IOCTL_FB_TEXT); // rsi = request b.LeaRbp(2, -24); // rdx = &fbtext - b.MovEax(SYS_ioctl); b.Int80(); + b.MovEax(SYS_ioctl); b.Syscall(); b.Xor(0, 0); // return 0 b.Leave(); b.Ret(); } @@ -283,7 +425,7 @@ public static class Los4Runtime private static void GenGuiEventPoll(Rt b) { // 参数:rdi=ev(7) - // 结构 leonos_gui_app_event @ [rbp-48](40 字节) + // 结构 leonos_gui_app_event @ [rbp-48](36 字节,清零 40 字节对齐) int noEvent = b.Lbl(); b.PushRbp(); b.MovRbpRsp(); b.SubRsp(56); b.StoreRbp(-56, 7); // 保存 ev 指针到 [rbp-56] @@ -292,10 +434,11 @@ public static class Los4Runtime b.LeaRbp(1, -48); for (int i = 0; i < 40; i += 8) { b.StoreReg64(1, 0); b.AddImm(1, 8); } // ioctl(gui_fd, WINDOW_EVENT, &event) - b.MovImm64BssRef(7, "gui_fd"); b.LoadReg32(7, 7); - b.MovImm64(6, LEONOS_GUI_IOCTL_WINDOW_EVENT); - b.LeaRbp(2, -48); - b.MovEax(SYS_ioctl); b.Int80(); // rax = 0(无事件) / 1(有事件) + b.MovImm64BssRef(7, "gui_fd"); // rdi = &gui_fd + b.LoadReg32(7, 0); // rdi = gui_fd + b.MovImm64(6, LEONOS_GUI_IOCTL_WINDOW_EVENT); // rsi = request + b.LeaRbp(2, -48); // rdx = &event + b.MovEax(SYS_ioctl); b.Syscall(); // rax = 0(无事件) / 1(有事件) b.Test(0, 0); b.Jle(noEvent); // rax <= 0 → ev->type=0, return 0 // 翻译事件 @@ -315,66 +458,84 @@ public static class Los4Runtime private static void GenGuiEventWait(Rt b) { // 参数:rdi=ev(7), rsi=timeout_ms(6) - // 结构 leonos_gui_wait_app_event @ [rbp-56](44 字节):event[40] + timeout_ms[4] + // 结构 leonos_gui_wait_app_event @ [rbp-56](40 字节):event[36] + timeout_ms[4] + // timeout_ms 在结构体 offset 36 = [rbp-20] + int noEvent = b.Lbl(); b.PushRbp(); b.MovRbpRsp(); b.SubRsp(72); b.StoreRbp(-64, 7); // 保存 ev b.StoreRbp(-72, 6); // 保存 timeout_ms - // 清零 wait 结构 + // 清零 wait 结构(48 字节,覆盖 40 字节结构体 + 对齐填充) b.Xor(0, 0); b.LeaRbp(1, -56); for (int i = 0; i < 48; i += 8) { b.StoreReg64(1, 0); b.AddImm(1, 8); } - // timeout_ms @ [rbp-56+40] = [rbp-16] + // timeout_ms @ [rbp-56+36] = [rbp-20] b.LoadRbp(0, -72); - b.LeaRbp(1, -16); b.StoreReg32(1, 0); + b.LeaRbp(1, -20); b.StoreReg32(1, 0); // ioctl(gui_fd, WAIT_WINDOW_EVENT, &wait) - b.MovImm64BssRef(7, "gui_fd"); b.LoadReg32(7, 7); - b.MovImm64(6, LEONOS_GUI_IOCTL_WAIT_WINDOW_EVENT); - b.LeaRbp(2, -56); - b.MovEax(SYS_ioctl); b.Int80(); + b.MovImm64BssRef(7, "gui_fd"); // rdi = &gui_fd + b.LoadReg32(7, 0); // rdi = gui_fd + b.MovImm64(6, LEONOS_GUI_IOCTL_WAIT_WINDOW_EVENT); // rsi = request + b.LeaRbp(2, -56); // rdx = &wait + b.MovEax(SYS_ioctl); b.Syscall(); // rax = result + b.Test(0, 0); + b.Jle(noEvent); // rax <= 0 → 无事件(含错误),防止死循环 // 翻译事件(event 在 [rbp-56],ev 在 [rbp-64]) b.LoadRbp(7, -64); // rdi = ev b.LeaRbp(10, -56); // r10 = &event GenGuiTranslateEvent(b, 7, 10); b.MovImm64(0, 1); // return 1 b.Leave(); b.Ret(); + b.Mark(noEvent); + b.LoadRbp(7, -64); // rdi = ev + b.Xor(0, 0); b.StoreReg32(7, 0); // ev->type = 0 + b.Xor(0, 0); // return 0 + b.Leave(); b.Ret(); } // int gui_window_destroy(int win) private static void GenGuiWindowDestroy(Rt b) { // 参数:rdi=win(7) - // ioctl(gui_fd, DESTROY_WINDOW, &win_id);win_id 存于 [rbp-8] - b.PushRbp(); b.MovRbpRsp(); b.SubRsp(16); - b.StoreRbp(-8, 7); // [rbp-8] = win(StoreRbp: mov [rbp+disp], r64) - b.MovImm64BssRef(7, "gui_fd"); b.LoadReg32(7, 7); // rdi = gui_fd - b.MovImm64(6, LEONOS_GUI_IOCTL_DESTROY_WINDOW); // rsi = request - b.LeaRbp(2, -8); // rdx = &win_id - b.MovEax(SYS_ioctl); b.Int80(); + // 与 libc.a 一致:ioctl(gui_fd, DESTROY_WINDOW, win_value) — window_id 作为值传递,非指针 + b.PushRbp(); b.MovRbpRsp(); + b.Mov(2, 7); // rdx = win(值,非指针) + b.MovImm64BssRef(7, "gui_fd"); // rdi = &gui_fd + b.LoadReg32(7, 0); // rdi = gui_fd + b.MovImm64(6, LEONOS_GUI_IOCTL_DESTROY_WINDOW); // rsi = request + b.MovEax(SYS_ioctl); b.Syscall(); b.Xor(0, 0); // return 0 b.Leave(); b.Ret(); } - // int gui_cleanup(void) — 关闭 GUI 设备。 + // int gui_cleanup(void) — 关闭 GUI 设备 fd。 private static void GenGuiCleanup(Rt b) { - b.PushRbp(); b.MovRbpRsp(); b.SubRsp(16); - b.MovImm64BssRef(7, "gui_fd"); b.LoadReg32(7, 7); // rdi = gui_fd - b.Test(7, 7); - int skip = b.Lbl(); - b.Je(skip); // gui_fd == 0 → skip - b.MovEax(SYS_close); b.Int80(); - // gui_fd = 0 - b.MovImm64BssRef(7, "gui_fd"); b.Xor(0, 0); b.StoreReg32(7, 0); - b.Mark(skip); + b.PushRbp(); b.MovRbpRsp(); + // close(gui_fd) + b.MovImm64BssRef(7, "gui_fd"); // rdi = &gui_fd + b.LoadReg32(7, 0); // rdi = gui_fd + b.MovEax(SYS_close); b.Syscall(); b.Xor(0, 0); // return 0 b.Leave(); b.Ret(); } // ============ 函数实现 ============ - private static void GenExit(Rt b) { b.MovEax(SYS_exit); b.Int80(); b.Ret(); } + // int open(const char *path, int flags, ...) — rdi=path, rsi=flags, rdx=mode + private static void GenOpen(Rt b) + { + b.MovEax(SYS_open); b.Syscall(); b.Ret(); + } - private static void GenAbort(Rt b) { b.MovEdi(1); b.MovEax(SYS_exit); b.Int80(); b.Ret(); } + // int close(int fd) — rdi=fd + private static void GenClose(Rt b) + { + b.MovEax(SYS_close); b.Syscall(); b.Ret(); + } + + private static void GenExit(Rt b) { b.MovEax(SYS_exit); b.Syscall(); b.Ret(); } + + private static void GenAbort(Rt b) { b.MovEdi(1); b.MovEax(SYS_exit); b.Syscall(); b.Ret(); } private static void GenPutchar(Rt b) { @@ -384,7 +545,7 @@ public static class Los4Runtime b.MovEdi(1); // fd=stdout b.Emit(0x48, 0x8D, 0x34, 0x24); // lea rsi, [rsp] b.MovEdx(1); - b.MovEax(SYS_write); b.Int80(); + b.MovEax(SYS_write); b.Syscall(); b.Emit(0x40, 0x0F, 0xB6, 0xC7); // movzx eax, dil b.Leave(); b.Ret(); } @@ -405,12 +566,12 @@ public static class Los4Runtime b.Mov(2, 1); // rdx=len b.PopR(6); // rsi=s b.MovEdi(1); // stdout - b.MovEax(SYS_write); b.Int80(); + b.MovEax(SYS_write); b.Syscall(); // write '\n' b.Push8((byte)'\n'); b.Mov(6, 4); // rsi=rsp b.MovEdx(1); b.MovEdi(1); - b.MovEax(SYS_write); b.Int80(); + b.MovEax(SYS_write); b.Syscall(); b.AddRsp(8); b.Xor(0, 0); b.PopRbp(); b.Ret(); @@ -526,7 +687,7 @@ public static class Los4Runtime b.Mov(2, 12); b.Sub(2, 13); // rdx = len b.Mov(6, 13); // rsi = buf b.MovEdi(1); - b.MovEax(SYS_write); b.Int80(); + b.MovEax(SYS_write); b.Syscall(); b.Mov(0, 12); b.Sub(0, 13); // rax = len b.AddRsp(0x438); b.PopR(15); b.PopR(14); b.PopR(13); b.PopR(12); b.PopR(3); @@ -705,20 +866,34 @@ public static class Los4Runtime private static void GenMalloc(Rt b) { // void *malloc(size_t size) — rdi = size - // mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) + // BSS bump pool allocation(los4 模拟器无 mmap,用 BSS 静态池) + // heap_ptr 初始为 0,表示尚未初始化;首次调用时初始化为 heap_pool 基址 b.PushRbp(); b.MovRbpRsp(); b.AddImm(7, 15); b.AndImm(7, -16); // 对齐 size 到 16 字节 - b.Mov(6, 7); // rsi = length - b.XorEdiEdi(); // rdi = addr = 0 - b.MovEdx(PROT_READ | PROT_WRITE); // rdx = prot = 3 - b.MovImm64(10, MAP_PRIVATE | MAP_ANONYMOUS); // r10 = flags = 0x22 - b.MovImm64(8, -1); // r8 = fd = -1 - b.Xor(9, 9); // r9 = offset = 0 - b.MovEax(SYS_mmap); b.Int80(); // rax = ptr + // 读取 heap_ptr,若为 0 则初始化为 heap_pool + b.MovImm64BssRef(0, "heap_ptr"); // rax = &heap_ptr + b.LoadReg64(0, 0); // rax = *heap_ptr (当前偏移) + b.Test(0, 0); // 检查是否为 0 + int skipInit = b.Lbl(); + b.Jne(skipInit); + // 初始化:heap_ptr = &heap_pool(直接用地址,不能 LoadReg64 因为 BSS 初值为 0) + b.MovImm64BssRef(0, "heap_pool"); // rax = &heap_pool(地址本身) + b.MovImm64BssRef(2, "heap_ptr"); // rdx = &heap_ptr + b.StoreReg64(2, 0); // *heap_ptr = &heap_pool + b.Mark(skipInit); + // 分配:新指针 = heap_ptr + size + b.MovImm64BssRef(2, "heap_ptr"); // rdx = &heap_ptr + b.LoadReg64(0, 2); // rax = *heap_ptr (当前位置) + b.Mov(1, 7); // rcx = size + b.Add(0, 1); // rax += size (新分配地址) + b.StoreReg64(2, 0); // *heap_ptr = 新位置 + // rax = 原始指针 = 新位置 - size + b.Mov(2, 7); // rdx = size + b.Sub(0, 2); // rax -= size → rax = 原始指针 b.PopRbp(); b.Ret(); } - private static void GenFree(Rt b) { b.Ret(); } // mmap 内存由内核管理,free 为空操作 + private static void GenFree(Rt b) { b.Ret(); } // bump pool 无需释放 private static void GenCalloc(Rt b) { @@ -727,16 +902,11 @@ public static class Los4Runtime b.Imul(7, 6); // rdi = total = nmemb * size b.AddImm(7, 15); b.AndImm(7, -16); // 对齐到 16 字节 b.PushR(7); // 保存 total 供 memset 使用 - b.Mov(6, 7); // rsi = length = total - b.XorEdiEdi(); // rdi = addr = 0 - b.MovEdx(PROT_READ | PROT_WRITE); // rdx = prot - b.MovImm64(10, MAP_PRIVATE | MAP_ANONYMOUS); // r10 = flags - b.MovImm64(8, -1); // r8 = fd = -1 - b.Xor(9, 9); // r9 = offset = 0 - b.MovEax(SYS_mmap); b.Int80(); // rax = ptr + // 用 malloc 分配(通过调回 malloc 函数) + b.CallName("malloc"); // memset(ptr, 0, total) b.PopR(2); // rdx = total - b.Mov(7, 0); // rdi = ptr + b.Mov(7, 0); // rdi = ptr (rax) b.Xor(0, 0); // al = 0 b.PushR(0); // 保存 ptr int loop = b.Lbl(), done = b.Lbl(); @@ -755,7 +925,7 @@ public static class Los4Runtime b.MovEdi(0); // stdin b.Emit(0x48, 0x8D, 0x34, 0x24); // lea rsi, [rsp] b.MovEdx(1); - b.MovEax(SYS_read); b.Int80(); + b.MovEax(SYS_read); b.Syscall(); b.Test(0, 0); int eof = b.Lbl(), ret = b.Lbl(); b.Je(eof); @@ -778,7 +948,7 @@ public static class Los4Runtime b.Inc(6); b.Inc(1); b.Jmp(loop); b.Mark(done); b.Mov(2, 1); b.PopR(6); - b.MovEdi(1); b.MovEax(SYS_write); b.Int80(); + b.MovEdi(1); b.MovEax(SYS_write); b.Syscall(); b.Xor(0, 0); b.PopRbp(); b.Ret(); } @@ -903,6 +1073,10 @@ internal sealed class Rt /// 由 ElfWriterLos4 在段布局完成后修补为字符串的真实虚拟地址。 internal readonly List<(int off, string content)> RdataRefs = new(); + /// 运行时代码中对 .rodata 二进制数据的绝对地址引用:(代码内偏移, 数据)。 + /// 由 ElfWriterLos4 在段布局完成后将数据追加到 rodata 并修补为真实虚拟地址。 + internal readonly List<(int off, byte[] data)> RdataBinRefs = new(); + /// 运行时持久状态(BSS 全局):(代码内偏移, 全局名, 全局在运行时 BSS 区内偏移)。 /// 由 ElfWriterLos4 在段布局完成后修补为 BSS 区的真实虚拟地址。 internal readonly List<(int off, string name, int bssOff)> BssRefs = new(); @@ -983,7 +1157,7 @@ internal sealed class Rt // ---- 基础 ---- internal void Ret() => _code.Add(0xC3); - internal void Int80() { _code.Add(0xCD); _code.Add(0x80); } + internal void Syscall() { _code.Add(0xCD); _code.Add(0x80); } // int 0x80 (LeonOS 4 使用 int 0x80 而非 syscall) internal void Leave() => _code.Add(0xC9); internal void PushRbp() => _code.Add(0x55); internal void PopRbp() => _code.Add(0x5D); @@ -1036,6 +1210,18 @@ internal sealed class Rt for (int i = 0; i < 8; i++) _code.Add(0); } + /// mov r64, &bin —— 发射 REX + 操作码 + 8 字节占位 0,并记录二进制数据引用。 + /// ElfWriterLos4 在确定 .rodata 布局后将数据追加到 rodata 并回填真实虚拟地址。 + internal void MovImm64RdataBinRef(int reg, byte[] data) + { + byte rex = 0x48; // REX.W + if (reg >= 8) rex |= 0x01; // REX.B + _code.Add(rex); + _code.Add((byte)(0xB8 + (reg & 7))); + RdataBinRefs.Add((_code.Count, data)); // 记录 8 字节占位起始偏移 + for (int i = 0; i < 8; i++) _code.Add(0); + } + // ---- xor r64, r64 ---- internal void Xor(int dst, int src) { _code.Add(RexRR(src, dst)); _code.Add(0x31); _code.Add((byte)(0xC0 | ((src & 7) << 3) | (dst & 7))); } internal void XorEdiEdi() => Emit(0x48, 0x31, 0xFF); diff --git a/src/PazeE.Compiler/CodeGen/X64/X64CodeGenerator.cs b/src/PazeE.Compiler/CodeGen/X64/X64CodeGenerator.cs index ecdde08..51c8a85 100644 --- a/src/PazeE.Compiler/CodeGen/X64/X64CodeGenerator.cs +++ b/src/PazeE.Compiler/CodeGen/X64/X64CodeGenerator.cs @@ -1147,10 +1147,15 @@ public sealed class X64CodeGenerator : ICodeGenerator } } - int outBytes = AlignUp(shadow + nStack * 8 + structCopyBytes, 16); + // 寄存器参数溢出槽:求值每个参数时先存到 [rsp+slot],最后批量载入寄存器。 + // Win64 的 shadow space(32 字节)可容纳 4 个溢出槽;SysV 无 shadow, + // 必须单独分配 regCount*8 字节,否则溢出槽会覆盖调用者的栈临时量。 + int spillBytes = regCount * 8; + int argBase = Math.Max(shadow, spillBytes); // 栈参数起始偏移 + int outBytes = AlignUp(argBase + nStack * 8 + structCopyBytes, 16); if (outBytes != 0) _e.SubImm(Rsp, outBytes); - int copyBase = shadow + nStack * 8; + int copyBase = argBase + nStack * 8; // 先复制结构体内容到副本区(求值顺序:结构体在标量之前,避免标量寄存器被 CopyBytes 破坏) foreach (var kv in structOff) @@ -1169,7 +1174,7 @@ public sealed class X64CodeGenerator : ICodeGenerator _e.Lea(Rax, Mem.BaseDisp(Rsp, copyBase + structOff[i])); else GenValue(args[i]); // Rax = arg - int slot = i < regCount ? i * 8 : shadow + (i - regCount) * 8; + int slot = i < regCount ? i * 8 : argBase + (i - regCount) * 8; _e.Store64(Mem.BaseDisp(Rsp, slot), Rax); } // 把寄存器参数从溢出槽载入对应寄存器 diff --git a/src/PazeE.Compiler/Parser/Parser.cs b/src/PazeE.Compiler/Parser/Parser.cs index 6f23ce7..8ab8cef 100644 --- a/src/PazeE.Compiler/Parser/Parser.cs +++ b/src/PazeE.Compiler/Parser/Parser.cs @@ -675,12 +675,17 @@ public sealed class Parser case TokenKind.KwSizeof: { var r = Peek().Range; _i++; - if (Check(TokenKind.LParen) && IsTypeName(Peek())) + // sizeof(type):'(' 紧跟类型名。先用 Peek 判定(不消费 '('), + // 否则 sizeof(expr) 路径会因 '(' 已被消费而漏吃匹配的 ')', + // 导致后续语句多出一个 ')'(“期望 ;,但遇到 ')'”)。 + if (Peek().Kind == TokenKind.LParen && IsTypeName(Peek(1))) { + _i++; // consume '(' var t = ParseTypeName(); Expect(TokenKind.RParen, ")"); return new SizeofExpr(null, t) { Range = r }; } + // sizeof expr / sizeof(expr):括号由一元/初等表达式自行消费。 var e = ParseUnary(); return new SizeofExpr(e, null) { Range = r }; } diff --git a/src/PazeE.Compiler/PazeE.Compiler.csproj b/src/PazeE.Compiler/PazeE.Compiler.csproj index 796319b..005e74f 100644 --- a/src/PazeE.Compiler/PazeE.Compiler.csproj +++ b/src/PazeE.Compiler/PazeE.Compiler.csproj @@ -14,7 +14,7 @@ release release - 0.1.0 + 0.1.1 $(VersionPrefix) $(VersionPrefix)-$(BuildChannel) 0.1.0.0 diff --git a/src/PazeE.Compiler/Runtime/LibcDecls.cs b/src/PazeE.Compiler/Runtime/LibcDecls.cs index 01fc05e..e2e8ff6 100644 --- a/src/PazeE.Compiler/Runtime/LibcDecls.cs +++ b/src/PazeE.Compiler/Runtime/LibcDecls.cs @@ -124,6 +124,7 @@ extern unsigned int SetBkColor(void *, unsigned int); extern int TextOutA(void *, int, int, const char *, int); extern int InvalidateRect(void *, void *, int); extern int DwmSetWindowAttribute(void *, int, void *, int); +extern void *CreateSolidBrush(unsigned int color); /* ---- Win32 常量 ---- */ #define WS_OVERLAPPEDWINDOW 0x00CF0000 @@ -173,9 +174,21 @@ static long long paze_wndproc(void *h, unsigned int msg, unsigned long long w, l void *dc = BeginPaint(h, &ps); SetTextColor(dc, gui_fg); SetBkColor(dc, gui_bg); - int len = 0; - while (gui_text[len]) len++; - TextOutA(dc, gui_text_x, gui_text_y, gui_text, len); + /* 逐行绘制(处理 \n 换行)*/ + int line = 0; + int start = 0; + int i = 0; + while (gui_text[i]) { + if (gui_text[i] == '\n') { + if (i > start) + TextOutA(dc, gui_text_x, gui_text_y + line * 18, gui_text + start, i - start); + line++; + start = i + 1; + } + i++; + } + if (i > start) + TextOutA(dc, gui_text_x, gui_text_y + line * 18, gui_text + start, i - start); EndPaint(h, &ps); return 0; } @@ -192,14 +205,14 @@ int gui_init(void) { memset(&wc, 0, sizeof(struct paze_wc)); wc.style = 3; /* CS_HREDRAW | CS_VREDRAW */ wc.wndproc = paze_wndproc; - wc.hbrBackground = 6; /* COLOR_WINDOW + 1 */ + wc.hbrBackground = CreateSolidBrush(0xFFFFFF); /* 显式白色背景,不依赖系统主题色 */ wc.className = gui_class_name; if (RegisterClassA(&wc) == 0) return -1; gui_class_registered = 1; return 0; } -int gui_window_create(const char *title, int width, int height) { +int gui_window_create(const char *title, const char *text, int width, int height) { if (gui_init() < 0) return -1; gui_hwnd = CreateWindowExA(0, gui_class_name, title, WS_OVERLAPPEDWINDOW, 100, 100, width, height, 0, 0, 0, 0); @@ -211,7 +224,12 @@ int gui_window_create(const char *title, int width, int height) { DwmSetWindowAttribute(gui_hwnd, 20, &dark, 4); gui_fg = 0; gui_bg = 0xFFFFFF; - gui_text[0] = 0; + gui_text_x = 20; + gui_text_y = 40; + /* 存储初始文本 */ + int i = 0; + while (text && text[i] && i < 1023) { gui_text[i] = text[i]; i++; } + gui_text[i] = 0; ShowWindow(gui_hwnd, SW_SHOW); UpdateWindow(gui_hwnd); return 0; @@ -349,7 +367,7 @@ int gui_init(void) { return 0; } -int gui_window_create(const char *title, int width, int height) { +int gui_window_create(const char *title, const char *text, int width, int height) { if (gui_init() < 0) return -1; unsigned long root = XRootWindow(gui_display, gui_screen); unsigned long bg = XWhitePixel(gui_display, gui_screen); @@ -365,6 +383,14 @@ int gui_window_create(const char *title, int width, int height) { } XMapWindow(gui_display, gui_window); XFlush(gui_display); + /* 绘制初始文本 */ + if (text) { + XSetForeground(gui_display, gui_gc, 0); + int len = 0; + while (text[len]) len++; + XDrawString(gui_display, gui_window, gui_gc, 10, 30, text, len); + XFlush(gui_display); + } return 0; } @@ -540,7 +566,7 @@ int gui_init(void) { return 0; } -int gui_window_create(const char *title, int width, int height) { +int gui_window_create(const char *title, const char *text, int width, int height) { if (gui_init() < 0) return -1; void *winCls = objc_getClass("NSWindow"); if (!winCls) return -1; @@ -569,6 +595,11 @@ int gui_window_create(const char *title, int width, int height) { tv = (void *)paze_mac_send_rect(tv, sel_initWithFrame, tvRect, 0, 0, 0); gui_textview = tv; objc_msgSend(win, sel_setContentView, tv); + /* 设置初始文本 */ + if (text) { + void *textStr = (void *)objc_msgSend(strCls, sel_stringWithUTF8String, text); + objc_msgSend(tv, sel_setString, textStr); + } objc_msgSend(win, sel_makeKeyAndOrderFront, 0); return 0; } @@ -623,20 +654,158 @@ int gui_cleanup(void) { } #elif defined(__leonos__) -/* Los4 GUI — 阶段5 实现(Los4Runtime 静态生成 + ioctl)*/ -int gui_init(void); -int gui_window_create(const char *title, int width, int height); -int gui_window_text(int win, int x, int y, const char *text, unsigned int fg, unsigned int bg); -int gui_window_present(int win); -int gui_event_poll(struct gui_event *ev); -int gui_event_wait(struct gui_event *ev, int timeout_ms); -int gui_window_destroy(int win); -int gui_cleanup(void); +/* Los4 GUI — C 实现:与运行时 GenGui* 完全一致的 ioctl 路径 */ + +/* 运行时底层辅助(Los4Runtime 静态生成)*/ +extern int paze_ioctl(int fd, unsigned long req, void *arg); +extern int open(const char *path, int flags, ...); + +/* Los4 GUI ioctl 码(来自 leonos/gui.h)*/ +#define PAZE_LOS4_CREATE_WINDOW 0x4c475743UL +#define PAZE_LOS4_FB_FILL 0x4c464246UL +#define PAZE_LOS4_FB_TEXT 0x4c464254UL +#define PAZE_LOS4_WINDOW_EVENT 0x4c475745UL +#define PAZE_LOS4_WAIT_EVENT 0x4c475457UL +#define PAZE_LOS4_DESTROY_WINDOW 0x4c475744UL + +/* Los4 应用事件类型 */ +#define PAZE_LOS4_EV_CLOSE 1 +#define PAZE_LOS4_EV_MOUSE_BUTTON 6 +#define PAZE_LOS4_EV_KEY_DOWN 7 + +/* GUI ioctl 结构体(布局与 leonos/gui.h 一致)*/ +struct paze_los4_create { + unsigned int width; + unsigned int height; + const char *title; + const char *text; + unsigned int flags; +}; +struct paze_los4_fb_text { + unsigned int x; + unsigned int y; + unsigned int fg; + unsigned int bg; + const char *text; +}; +struct paze_los4_event { + unsigned int window_id; + unsigned int type; + int x; + int y; + int dx; + int dy; + unsigned int width; + unsigned int height; + unsigned char buttons; + unsigned char keycode; + unsigned char pressed; + unsigned char reserved; +}; +struct paze_los4_wait { + struct paze_los4_event event; + unsigned int timeout_ms; +}; + +/* GUI 状态 */ +static int gui_fd; +static int gui_win_id; +static int gui_win_w; +static int gui_win_h; + +int gui_init(void) { + /* 打开 GUI 设备(与 GenGuiInit 一致)*/ + int fd = open("0:/dev/leonos_gui", 2); /* O_RDWR = 2 */ + if (fd < 0) return -1; + gui_fd = fd; + return 0; +} + +int gui_window_create(const char *title, const char *text, int width, int height) { + struct paze_los4_create gc; + gc.width = (unsigned int)width; + gc.height = (unsigned int)height; + gc.title = title; + gc.text = text; + gc.flags = 0; + int wid = paze_ioctl(gui_fd, PAZE_LOS4_CREATE_WINDOW, &gc); + if (wid <= 0) return -1; + gui_win_id = wid; + gui_win_w = width; + gui_win_h = height; + /* 填充白色背景 + 黑色文字(与 GenGuiWindowText 一致的 FB_TEXT 路径)*/ + paze_ioctl(gui_fd, PAZE_LOS4_FB_FILL, (void*)(unsigned long)0xFFFFFFFF); + struct paze_los4_fb_text ft; + ft.x = 10; + ft.y = 10; + ft.fg = 0xFF000000; /* 黑色 */ + ft.bg = 0xFFFFFFFF; /* 白色 */ + ft.text = text; + paze_ioctl(gui_fd, PAZE_LOS4_FB_TEXT, &ft); + return 0; +} + +int gui_window_text(int win, int x, int y, const char *text, unsigned int fg, unsigned int bg) { + struct paze_los4_fb_text ft; + ft.x = (unsigned int)x; + ft.y = (unsigned int)y; + ft.fg = fg; + ft.bg = bg; + ft.text = text; + paze_ioctl(gui_fd, PAZE_LOS4_FB_TEXT, &ft); + return 0; +} + +int gui_window_present(int win) { return 0; } /* FB_TEXT 直接渲染,无需 present */ + +static void paze_los4_translate(struct paze_los4_event *e, struct gui_event *ev) { + ev->window_id = (int)e->window_id; + ev->type = 0; + if (e->type == PAZE_LOS4_EV_CLOSE) { + ev->type = 1; + } else if (e->type == PAZE_LOS4_EV_KEY_DOWN) { + ev->type = 2; + ev->key = (int)e->keycode; + } else if (e->type == PAZE_LOS4_EV_MOUSE_BUTTON) { + ev->type = 3; + ev->mouse_x = e->x; + ev->mouse_y = e->y; + ev->button = (int)e->buttons; + } +} + +int gui_event_poll(struct gui_event *ev) { + struct paze_los4_event e; + memset(&e, 0, sizeof(e)); + int r = paze_ioctl(gui_fd, PAZE_LOS4_WINDOW_EVENT, &e); + if (r <= 0) { ev->type = 0; return 0; } + paze_los4_translate(&e, ev); + return 1; +} + +int gui_event_wait(struct gui_event *ev, int timeout_ms) { + struct paze_los4_wait w; + memset(&w, 0, sizeof(w)); + w.timeout_ms = (unsigned int)timeout_ms; + int r = paze_ioctl(gui_fd, PAZE_LOS4_WAIT_EVENT, &w); + if (r <= 0) { ev->type = 0; return 0; } + paze_los4_translate(&w.event, ev); + return 1; +} + +int gui_window_destroy(int win) { + union { void *p; long l; } u; + u.l = (long)win; + paze_ioctl(gui_fd, PAZE_LOS4_DESTROY_WINDOW, u.p); + return 0; +} + +int gui_cleanup(void) { return 0; } #else /* 不支持 GUI 的平台 — 桩实现 */ int gui_init(void) { return -1; } -int gui_window_create(const char *title, int width, int height) { return -1; } +int gui_window_create(const char *title, const char *text, int width, int height) { return -1; } int gui_window_text(int win, int x, int y, const char *text, unsigned int fg, unsigned int bg) { return -1; } int gui_window_present(int win) { return -1; } int gui_event_poll(struct gui_event *ev) { ev->type = 0; return 0; } @@ -664,8 +833,8 @@ int gui_cleanup(void) { return -1; } or "DefWindowProcA" or "DestroyWindow" or "PostQuitMessage" or "BeginPaint" or "EndPaint" or "GetDC" or "ReleaseDC" or "InvalidateRect" or "SetWindowTextA" => "user32.dll", - // gdi32.dll — 文本绘制、颜色 - "SetTextColor" or "SetBkColor" or "TextOutA" => "gdi32.dll", + // gdi32.dll — 文本绘制、颜色、画刷 + "SetTextColor" or "SetBkColor" or "TextOutA" or "CreateSolidBrush" => "gdi32.dll", // dwmapi.dll — Win11 DWM 圆角/暗色模式 "DwmSetWindowAttribute" => "dwmapi.dll", _ => "msvcrt.dll" diff --git a/src/PazeE.Compiler/Runtime/StartupStubs.cs b/src/PazeE.Compiler/Runtime/StartupStubs.cs index 692185b..c9986b4 100644 --- a/src/PazeE.Compiler/Runtime/StartupStubs.cs +++ b/src/PazeE.Compiler/Runtime/StartupStubs.cs @@ -45,7 +45,7 @@ public static class StartupStubs } /// LeonOS 4 _start:call main;mov edi,eax;mov eax,60;int 0x80。 - /// LeonOS 4 使用 int 0x80(非 syscall 指令)进行系统调用,寄存器约定同 Linux x86_64。 + /// LeonOS 4 使用 int 0x80 指令(0xCD 0x80),寄存器约定同 Linux x86_64。 public static (byte[] code, List fixups) Los4(bool hasArgcArgv) { var b = new List(); @@ -59,7 +59,7 @@ public static class StartupStubs b.Add(0xE8); fx.Add(new StubFixup(b.Count, FixupKind.Rel32, "main")); b.AddRange(new byte[4]); // call main b.AddRange(new byte[] { 0x89, 0xC7 }); // mov edi, eax b.AddRange(new byte[] { 0xB8, 0x3C, 0x00, 0x00, 0x00 }); // mov eax, 60 (SYS_exit) - b.AddRange(new byte[] { 0xCD, 0x80 }); // int 0x80 + b.AddRange(new byte[] { 0xCD, 0x80 }); // int 0x80 (LeonOS 4 使用 int 0x80) return (b.ToArray(), fx); } diff --git a/src/PazeE.Installer/PazeE.Installer.Bundle.wixproj b/src/PazeE.Installer/PazeE.Installer.Bundle.wixproj index 2bf4a3a..20629a7 100644 --- a/src/PazeE.Installer/PazeE.Installer.Bundle.wixproj +++ b/src/PazeE.Installer/PazeE.Installer.Bundle.wixproj @@ -1,12 +1,17 @@ - PazeE-0.1.0-alpha-win-x64 + + zh-cn + + -$(BundleCulture) + PazeE-0.1.1-alpha-win-x64$(CultureSuffix) Bundle - ..\..\publish\0.1.0-alpha\installer\ - PazeEVersion=0.1.0-alpha;MsiVersion=0.1.0;MsiTargetPath=$(MSBuildProjectDirectory)\obj\Release\zh-cn\PazeE-0.1.0-alpha-win-x64.msi - zh-cn - zh-cn + ..\..\publish\0.1.1-alpha\installer\ + + PazeEVersion=0.1.1-alpha;MsiVersion=0.1.1;MsiTargetPath=$(MSBuildProjectDirectory)\obj\Release\$(BundleCulture)\PazeE-0.1.1-alpha-win-x64.msi + $(BundleCulture) + $(BundleCulture) $(DefaultItemExcludes);Product.wxs;*.wxl diff --git a/src/PazeE.Installer/PazeE.Installer.Msi.wixproj b/src/PazeE.Installer/PazeE.Installer.Msi.wixproj index 111dec6..24bbf9f 100644 --- a/src/PazeE.Installer/PazeE.Installer.Msi.wixproj +++ b/src/PazeE.Installer/PazeE.Installer.Msi.wixproj @@ -1,14 +1,14 @@ - PazeE-0.1.0-alpha-win-x64 + PazeE-0.1.1-alpha-win-x64 Package obj\Release\ - ..\..\publish\0.1.0-alpha\bin + ..\..\publish\0.1.1-alpha\bin - PazeEVersion=0.1.0-alpha;MsiVersion=0.1.0;PublishBinDir=$(PublishBinDir) + PazeEVersion=0.1.1-alpha;MsiVersion=0.1.1;PublishBinDir=$(PublishBinDir) x64 @@ -17,7 +17,7 @@ $(DefaultItemExcludes);Bundle.wxs - ..\..\publish\0.1.0-alpha\installer\ + ..\..\publish\0.1.1-alpha\installer\ zh-cn;en-us;zh-tw zh-cn @@ -52,11 +52,47 @@ - - - + + + + + + $([System.IO.Path]::Combine('$(PkgWixToolset_Sdk)', 'tools\net472\x64\wix.exe')) + $(USERPROFILE)\.nuget\packages\wixtoolset.sdk\6.0.2\tools\net472\x64\wix.exe + $(OutputPath) + $(OutputName).msi + $(ObjOut)zh-cn\$(MsiFile) + $(ObjOut)en-us\$(MsiFile) + $(ObjOut)zh-tw\$(MsiFile) + $(ObjOut)$(MsiFile) + $(ObjOut)en-us.mst + $(ObjOut)zh-tw.mst + $(MSBuildProjectDirectory)\embed_lang.ps1 + $(SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe + + + + + + + + + + + + + + + + + + + - <_MsiFiles Include="$(OutputPath)$(DefaultCulture)\*.msi" /> + <_MsiFiles Include="$(OutputPath)$(OutputName).msi" /> - + - + @@ -68,7 +68,7 @@ Action="set" System="yes" /> - + diff --git a/src/PazeE.Installer/embed_lang.ps1 b/src/PazeE.Installer/embed_lang.ps1 new file mode 100644 index 0000000..b99ed97 --- /dev/null +++ b/src/PazeE.Installer/embed_lang.ps1 @@ -0,0 +1,103 @@ +# embed_lang.ps1 - Embed language transforms into MSI to create a multi-language MSI +# Usage: powershell -File embed_lang.ps1 -MsiPath -EnMst -TwMst + +param( + [Parameter(Mandatory=$true)][string]$MsiPath, + [Parameter(Mandatory=$true)][string]$EnMst, + [Parameter(Mandatory=$true)][string]$TwMst +) + +Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; + +public static class MsiEmbed { + const int MSIDBOPEN_TRANSACT = 1; + const int MSIDBOPEN_READONLY = 0; + const uint PID_TEMPLATE = 7; // SummaryInformation: Platform;Language + const int VT_LPSTR = 30; + + [DllImport("msi.dll", CharSet = CharSet.Unicode)] + static extern uint MsiOpenDatabaseW(string szDatabasePath, int szPersist, out IntPtr phDatabase); + + [DllImport("msi.dll", CharSet = CharSet.Unicode)] + static extern uint MsiDatabaseOpenViewW(IntPtr hDatabase, string szQuery, out IntPtr phView); + + [DllImport("msi.dll")] + static extern uint MsiViewExecute(IntPtr hView, IntPtr hRecord); + + [DllImport("msi.dll")] + static extern uint MsiViewClose(IntPtr hView); + + [DllImport("msi.dll")] + static extern uint MsiDatabaseCommit(IntPtr hDatabase); + + [DllImport("msi.dll")] + static extern IntPtr MsiCreateRecord(uint cParams); + + [DllImport("msi.dll", CharSet = CharSet.Unicode)] + static extern uint MsiRecordSetStringW(IntPtr hRecord, uint iField, string szValue); + + [DllImport("msi.dll", CharSet = CharSet.Unicode)] + static extern uint MsiRecordSetStreamW(IntPtr hRecord, uint iField, string szFilePath); + + [DllImport("msi.dll", CharSet = CharSet.Unicode)] + static extern uint MsiGetSummaryInformationW(IntPtr hDatabase, string szDatabasePath, uint uiUpdateCount, out IntPtr phSummaryInfo); + + [DllImport("msi.dll", CharSet = CharSet.Unicode)] + static extern uint MsiSummaryInfoSetPropertyW(IntPtr hSummaryInfo, uint uiProperty, int uiType, int iValue, IntPtr pftValue, string szValue); + + [DllImport("msi.dll")] + static extern uint MsiSummaryInfoPersist(IntPtr hSummaryInfo); + + static string Check(uint r, string ctx) { + if (r != 0) throw new Exception("MSI error " + r + " at: " + ctx); + return "OK"; + } + + public static void Embed(string msiPath, string enMst, string twMst) { + IntPtr hDb; + Check(MsiOpenDatabaseW(msiPath, MSIDBOPEN_TRANSACT, out hDb), "OpenDatabase"); + + // Ensure _Storages table exists; create if missing + try { + IntPtr hView0; + MsiDatabaseOpenViewW(hDb, "SELECT * FROM `_Storages`", out hView0); + MsiViewExecute(hView0, IntPtr.Zero); + MsiViewClose(hView0); + } catch { + Console.WriteLine("Creating _Storages table..."); + IntPtr hCV; + Check(MsiDatabaseOpenViewW(hDb, "CREATE TABLE `_Storages` (`Name` CHAR(255) NOT NULL LOCALIZABLE, `Data` OBJECT NOT NULL PRIMARY KEY `Name`)", out hCV), "OpenView CREATE"); + Check(MsiViewExecute(hCV, IntPtr.Zero), "Execute CREATE"); + MsiViewClose(hCV); + } + + EmbedStorage(hDb, "1033", enMst, "en-us"); + EmbedStorage(hDb, "1028", twMst, "zh-tw"); + + // Update SummaryInformation Template (PID 7 = "Platform;Language") 为多语言 + // Windows Installer 运行时根据系统语言自动应用对应 transform;不匹配时显示语言选择 + IntPtr hSum; + Check(MsiGetSummaryInformationW(hDb, null, 1, out hSum), "GetSummaryInfo"); + Check(MsiSummaryInfoSetPropertyW(hSum, PID_TEMPLATE, VT_LPSTR, 0, IntPtr.Zero, "x64;2052,1033,1028"), "SummarySetProperty"); + Check(MsiSummaryInfoPersist(hSum), "SummaryPersist"); + + Check(MsiDatabaseCommit(hDb), "Commit"); + Console.WriteLine("Multi-language MSI built: " + msiPath); + } + + static void EmbedStorage(IntPtr hDb, string storageName, string mstPath, string label) { + Console.WriteLine("Embedding " + label + " transform as storage '" + storageName + "'..."); + IntPtr hView; + Check(MsiDatabaseOpenViewW(hDb, "INSERT INTO `_Storages` (`Name`, `Data`) VALUES (?, ?)", out hView), "OpenView INSERT " + label); + IntPtr hRec = MsiCreateRecord(2); + Check(MsiRecordSetStringW(hRec, 1, storageName), "SetString " + label); + Check(MsiRecordSetStreamW(hRec, 2, mstPath), "SetStream " + label); + Check(MsiViewExecute(hView, hRec), "Execute INSERT " + label); + MsiViewClose(hView); + } +} +"@ + +[MsiEmbed]::Embed($MsiPath, $EnMst, $TwMst) diff --git a/tests/gui.pe b/tests/gui.pe index 0b2c12e..715e4eb 100644 --- a/tests/gui.pe +++ b/tests/gui.pe @@ -1,26 +1,133 @@ -/* GUI 测试:创建窗口显示文字,按任意键或关闭窗口退出 */ -struct gui_event { int type, key, mouse_x, mouse_y, button, window_id; }; -int gui_init(void); -int gui_window_create(const char *title, int width, int height); -int gui_window_text(int win, int x, int y, const char *text, unsigned int fg, unsigned int bg); -int gui_event_wait(struct gui_event *ev, int timeout_ms); -int gui_window_destroy(int win); +/* GUI 测试:PRESENT_WINDOW + 文字渲染(ARGB32 颜色格式)*/ +extern int paze_ioctl(int fd, unsigned long req, void *arg); +extern void *malloc(unsigned long size); +extern void memset(void *s, int c, unsigned long n); -int main(void) { - if (gui_init() < 0) return 1; - if (gui_window_create("PazeE GUI Demo", 480, 320) < 0) return 1; +#define CREATE_WINDOW 0x4c475743UL +#define PRESENT_WINDOW 0x4c475046UL +#define WAIT_WINDOW_EVENT 0x4c475457UL - /* 显示文字:黑底绿字 */ - gui_window_text(0, 60, 80, "Hello from PazeE!", 0x00FF00, 0x000000); - gui_window_text(0, 60, 120, "Press any key or close to exit", 0xFFFFFF, 0x000000); +struct create { unsigned int width, height; const char *title, *text; unsigned int flags; }; +struct present { unsigned int window_id, width, height, stride; unsigned int *pixels; }; - struct gui_event ev; - while (1) { - gui_event_wait(&ev, 0); - if (ev.type == 4 || ev.type == 1) break; /* quit or close */ - if (ev.type == 2) break; /* key down */ - } +struct app_event { + unsigned int window_id; + unsigned int type; + int x, y, dx, dy; + unsigned int width, height; + unsigned char buttons, keycode, pressed; +}; - gui_window_destroy(0); +struct wait_event { + struct app_event event; + unsigned int timeout_ms; +}; + +/* ARGB32 颜色:byte0=A, byte1=R, byte2=G, byte3=B */ +#define COL_BLACK 0x000000FFU /* A=FF, R=0, G=0, B=0 */ +#define COL_WHITE 0xFFFFFFFFU /* A=FF, R=FF, G=FF, B=FF */ +#define COL_RED 0x0000FFFFU /* A=FF, R=FF, G=0, B=0 */ + +/* 8x16 字体 */ +static unsigned char fnt_H[16] = {0x66,0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66}; +static unsigned char fnt_e[16] = {0x00,0x00,0x3C,0x66,0x66,0x66,0x7E,0x60,0x60,0x60,0x60,0x60,0x60,0x3C,0x00,0x00}; +static unsigned char fnt_l[16] = {0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00}; +static unsigned char fnt_o[16] = {0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00}; +static unsigned char fnt_r[16] = {0x00,0x00,0x7C,0x66,0x66,0x66,0x7C,0x78,0x70,0x70,0x60,0x60,0x60,0x60,0x00,0x00}; +static unsigned char fnt_f[16] = {0x3C,0x66,0x60,0x60,0x60,0xFC,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00}; +static unsigned char fnt_m[16] = {0x00,0x00,0x66,0x7E,0x7E,0x7E,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00}; +static unsigned char fnt_P[16] = {0x7C,0x66,0x66,0x66,0x66,0x7C,0x78,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x00,0x00}; +static unsigned char fnt_a[16] = {0x00,0x00,0x3C,0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00}; +static unsigned char fnt_z[16] = {0x00,0x00,0x7E,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0x00}; +static unsigned char fnt_E[16] = {0x7E,0x60,0x60,0x60,0x60,0x7C,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0x00}; +static unsigned char fnt_c[16] = {0x00,0x00,0x3C,0x66,0x66,0x60,0x60,0x60,0x60,0x60,0x66,0x66,0x3C,0x00,0x00,0x00}; +static unsigned char fnt_t[16] = {0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x7E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x00,0x00,0x00}; +static unsigned char fnt_s[16] = {0x00,0x00,0x3C,0x66,0x60,0x60,0x3C,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00}; +static unsigned char fnt_o2[16] = {0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00}; +static unsigned char fnt_e2[16] = {0x00,0x00,0x3C,0x66,0x66,0x66,0x7E,0x60,0x60,0x60,0x60,0x60,0x60,0x3C,0x00,0x00}; +static unsigned char fnt_ex[16] = {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x18,0x18,0x00,0x00}; + +static unsigned char *getglyph(unsigned char c) { + if (c == 'H') return fnt_H; + if (c == 'e') return fnt_e; + if (c == 'l') return fnt_l; + if (c == 'o') return fnt_o; + if (c == 'r') return fnt_r; + if (c == 'f') return fnt_f; + if (c == 'm') return fnt_m; + if (c == 'P') return fnt_P; + if (c == 'a') return fnt_a; + if (c == 'z') return fnt_z; + if (c == 'E') return fnt_E; + if (c == 'c') return fnt_c; + if (c == 't') return fnt_t; + if (c == 's') return fnt_s; + if (c == '!') return fnt_ex; + return 0; +} + +static void putch(unsigned int *fb, int pitch, int x, int y, unsigned char c, unsigned int fg, unsigned int bg) { + int row, col; + unsigned char *g = getglyph(c); + if (!g) return; + for (row = 0; row < 16; row++) { + unsigned char bits = g[row]; + for (col = 0; col < 8; col++) { + unsigned int color = (bits & (0x80 >> col)) ? fg : bg; + if (x+col >= 0 && x+col < 480 && y+row >= 0 && y+row < 320) + fb[(y+row)*pitch + (x+col)] = color; + } + } +} + +static void puts_fb(unsigned int *fb, int pitch, int x, int y, const char *s, unsigned int fg, unsigned int bg) { + int cx = x, cy = y, i; + for (i = 0; s[i]; i++) { + if (s[i] == '\n') { cx = x; cy += 18; continue; } + if (s[i] == ' ') { cx += 10; continue; } + putch(fb, pitch, cx, cy, s[i], fg, bg); + cx += 10; + } +} + +int main(void) { + int fd = 3; + int W = 480, H = 320; + + /* 创建窗口 */ + struct create gc; + gc.width = W; gc.height = H; + gc.title = "PazeE Demo"; gc.text = "Hello"; + gc.flags = 0; + int wid = paze_ioctl(fd, CREATE_WINDOW, &gc); + + /* 分配帧缓冲区 */ + unsigned int *fb = (unsigned int*)malloc((unsigned long)W * H * 4); + if (!fb) return 1; + memset(fb, 0xFF, (unsigned long)W * H * 4); /* ARGB32: 0xFFFFFFFF = 白色 */ + + /* 绘制文字(ARGB32: 0x000000FF = 黑色,0xFFFFFFFF = 白色)*/ + puts_fb(fb, W, 20, 20, "Hello from PazeE!", COL_BLACK, COL_WHITE); + puts_fb(fb, W, 20, 50, "PazeE is awesome!", COL_BLACK, COL_WHITE); + puts_fb(fb, W, 20, 80, "Target: LeonOS 4 (los4)", COL_BLACK, COL_WHITE); + + /* 提交帧缓冲区 */ + struct present p; + p.window_id = (unsigned int)wid; + p.width = W; + p.height = H; + p.stride = W; + p.pixels = fb; + paze_ioctl(fd, PRESENT_WINDOW, &p); + + /* 事件循环(仅在用户关闭窗口时退出)*/ + struct wait_event wev; + while (1) { + wev.event.window_id = 0; + wev.event.type = 0; + wev.timeout_ms = 100; + int r = paze_ioctl(fd, WAIT_WINDOW_EVENT, &wev); + if (r > 0 && wev.event.type == 1) break; + } return 0; } diff --git a/tests/win_gui.pe b/tests/win_gui.pe index 7fb8513..01bf948 100644 --- a/tests/win_gui.pe +++ b/tests/win_gui.pe @@ -1,7 +1,7 @@ /* Windows GUI 综合测试:多色文字、鼠标点击反馈、按键显示 */ struct gui_event { int type, key, mouse_x, mouse_y, button, window_id; }; int gui_init(void); -int gui_window_create(const char *title, int width, int height); +int gui_window_create(const char *title, const char *text, int width, int height); int gui_window_text(int win, int x, int y, const char *text, unsigned int fg, unsigned int bg); int gui_window_present(int win); int gui_event_poll(struct gui_event *ev); @@ -15,7 +15,7 @@ unsigned long strlen(const char *s); int main(void) { if (gui_init() < 0) return 1; - if (gui_window_create("PazeE Windows GUI Test", 600, 400) < 0) return 1; + if (gui_window_create("PazeE Windows GUI Test", "=== PazeE GUI Demo ===", 600, 400) < 0) return 1; /* 标题行 — 亮白色 */ gui_window_text(0, 140, 30, "=== PazeE GUI Demo ===", 0xFFFFFF, 0x000000);