ci: add Linux build workflow + los4 GUI fixes + bump to 0.1.1
- Add .github/workflows/build-linux.yml: auto-build self-contained linux-x64 paze on push/tag, smoke-test with hello.pe, publish tar.gz+zip artifacts and GitHub Release on v* tags. - Fix LeonOS 4 (los4) syscalls: use int 0x80 (0xCD 0x80) instead of syscall (0x0F 0x05) in Los4Runtime and StartupStubs, matching the real doomlauncher.elf ABI. - Fix X64CodeGenerator GenCallArgs: allocate spill slots for register arguments under SysV ABI to avoid overwriting caller stack temporaries (was clobbering &gui_fb during malloc call). - Fix los4 malloc/calloc: switch from unsupported SYS_mmap to a 1MB BSS static heap pool with bump pointer allocation. - Fix ElfWriterLos4 WritePhdr: resolve Write64At overload ambiguity that corrupted the program header table and left holes in .text. - Fix los4 GUI: use PRESENT_WINDOW ioctl with self-rendered framebuffer (8x16 font + ARGB32 color format), correct app_event struct layout (35 bytes, no trailing reserved field). - Bump version 0.1.0 -> 0.1.1 (csproj, wixproj, Product.wxs install dir + registry paths).
这个提交包含在:
@@ -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" <<EOF
|
||||
PazeE Compiler
|
||||
Version: ${{ steps.meta.outputs.version }}
|
||||
Channel: ${{ steps.meta.outputs.channel }}
|
||||
Target: linux-x64 (self-contained .NET 10)
|
||||
Commit: ${GITHUB_SHA}
|
||||
Built: ${GITHUB_RUN_ID}
|
||||
EOF
|
||||
# 打包
|
||||
cd staging
|
||||
tar -czf ../paze-${{ steps.meta.outputs.version }}-linux-x64.tar.gz *
|
||||
zip -r -q ../paze-${{ steps.meta.outputs.version }}-linux-x64.zip *
|
||||
cd ..
|
||||
ls -lh paze-*.tar.gz paze-*.zip
|
||||
|
||||
- name: Smoke test — compile hello.pe
|
||||
run: |
|
||||
BIN="staging/paze-${{ steps.meta.outputs.version }}-linux-x64/bin"
|
||||
if [ -f "$BIN/paze" ]; then
|
||||
"$BIN/paze" tests/hello.pe --target linux -o /tmp/hello.elf
|
||||
file /tmp/hello.elf
|
||||
/tmp/hello.elf || true
|
||||
elif [ -f "$BIN/paze.dll" ]; then
|
||||
dotnet "$BIN/paze.dll" tests/hello.pe --target linux -o /tmp/hello.elf
|
||||
file /tmp/hello.elf
|
||||
/tmp/hello.elf || true
|
||||
else
|
||||
echo "WARN: paze binary not found, listing bin/:"
|
||||
ls -la "$BIN"
|
||||
fi
|
||||
|
||||
- name: Upload artifact (tar.gz)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: paze-${{ steps.meta.outputs.version }}-linux-x64-tar
|
||||
path: paze-${{ steps.meta.outputs.version }}-linux-x64.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload artifact (zip)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: paze-${{ steps.meta.outputs.version }}-linux-x64-zip
|
||||
path: paze-${{ steps.meta.outputs.version }}-linux-x64.zip
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
name: Publish GitHub Release
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: PazeE ${{ github.ref_name }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
artifacts/**/*.tar.gz
|
||||
artifacts/**/*.zip
|
||||
@@ -309,12 +309,12 @@ public sealed class ElfWriter : 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 void WriteSym(List<byte> s, int name, int info, int other, int shndx, long value)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace PazeE.Compiler.Binary;
|
||||
|
||||
/// <summary>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。</summary>
|
||||
public sealed class ElfWriterLos4 : IExecutableWriter
|
||||
{
|
||||
@@ -26,7 +26,7 @@ public sealed class ElfWriterLos4 : IExecutableWriter
|
||||
var data = new List<byte>(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);
|
||||
|
||||
+313
-127
@@ -1,8 +1,9 @@
|
||||
namespace PazeE.Compiler.Binary;
|
||||
|
||||
/// <summary>LeonOS 4 静态运行时:通过 int 0x80 系统调用提供标准 C 库函数实现。
|
||||
/// <summary>LeonOS 4 静态运行时:通过 syscall 指令提供标准 C 库函数实现。
|
||||
/// 所有函数以 System V AMD64 ABI 调用,注入到 .text 段末尾,由 ElfWriterLos4 解析外部符号引用。
|
||||
/// LeonOS 4 系统调用约定与 Linux x86_64 一致(rax=调用号, rdi/rsi/rdx...=参数),用 int 0x80 而非 syscall。</summary>
|
||||
/// 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 行为一致)。</summary>
|
||||
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<string, int> offsets, List<(int off, string content)> rdataRefs, List<(int off, string name, int bssOff)> bssRefs, int bssSize) Generate()
|
||||
/// <summary>PSF1 8×16 位图字体的 ASCII 部分(128 字形 × 16 字节 = 2048 字节),
|
||||
/// 从 lat15_vga16.psf 提取,用于 los2w 模拟器像素文本渲染(PRESENT_WINDOW)。</summary>
|
||||
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<string, int> 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<string, int>();
|
||||
void Reg(string n, Action<Rt> 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。
|
||||
/// </summary>
|
||||
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。
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
// 把寄存器参数从溢出槽载入对应寄存器
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<!-- 版本通道:release | beta | alpha,可经 -p:BuildChannel=beta 覆盖 -->
|
||||
<BuildChannel Condition="'$(BuildChannel)' == ''">release</BuildChannel>
|
||||
<BuildChannel Condition="'$(BuildChannel)' != 'release' And '$(BuildChannel)' != 'beta' And '$(BuildChannel)' != 'alpha'">release</BuildChannel>
|
||||
<VersionPrefix>0.1.0</VersionPrefix>
|
||||
<VersionPrefix>0.1.1</VersionPrefix>
|
||||
<Version Condition="'$(BuildChannel)' == 'release'">$(VersionPrefix)</Version>
|
||||
<Version Condition="'$(BuildChannel)' != 'release'">$(VersionPrefix)-$(BuildChannel)</Version>
|
||||
<FileVersion>0.1.0.0</FileVersion>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class StartupStubs
|
||||
}
|
||||
|
||||
/// <summary>LeonOS 4 _start:call main;mov edi,eax;mov eax,60;int 0x80。
|
||||
/// LeonOS 4 使用 int 0x80(非 syscall 指令)进行系统调用,寄存器约定同 Linux x86_64。</summary>
|
||||
/// LeonOS 4 使用 int 0x80 指令(0xCD 0x80),寄存器约定同 Linux x86_64。</summary>
|
||||
public static (byte[] code, List<StubFixup> fixups) Los4(bool hasArgcArgv)
|
||||
{
|
||||
var b = new List<byte>();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<Project Sdk="WixToolset.Sdk/6.0.2">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputName>PazeE-0.1.0-alpha-win-x64</OutputName>
|
||||
<!-- BundleCulture: zh-cn(默认,无后缀) / en-us / zh-tw。用 -p:BundleCulture=en-us 切换 -->
|
||||
<BundleCulture Condition="'$(BundleCulture)' == ''">zh-cn</BundleCulture>
|
||||
<CultureSuffix Condition="'$(BundleCulture)' == 'zh-cn'"></CultureSuffix>
|
||||
<CultureSuffix Condition="'$(BundleCulture)' != 'zh-cn'">-$(BundleCulture)</CultureSuffix>
|
||||
<OutputName>PazeE-0.1.1-alpha-win-x64$(CultureSuffix)</OutputName>
|
||||
<OutputType>Bundle</OutputType>
|
||||
<OutputPath>..\..\publish\0.1.0-alpha\installer\</OutputPath>
|
||||
<DefineConstants>PazeEVersion=0.1.0-alpha;MsiVersion=0.1.0;MsiTargetPath=$(MSBuildProjectDirectory)\obj\Release\zh-cn\PazeE-0.1.0-alpha-win-x64.msi</DefineConstants>
|
||||
<Cultures>zh-cn</Cultures>
|
||||
<DefaultCulture>zh-cn</DefaultCulture>
|
||||
<OutputPath>..\..\publish\0.1.1-alpha\installer\</OutputPath>
|
||||
<!-- 引用对应 culture 的单语言 MSI(zh-cn/en-us/zh-tw 子目录中的原始 MSI) -->
|
||||
<DefineConstants>PazeEVersion=0.1.1-alpha;MsiVersion=0.1.1;MsiTargetPath=$(MSBuildProjectDirectory)\obj\Release\$(BundleCulture)\PazeE-0.1.1-alpha-win-x64.msi</DefineConstants>
|
||||
<Cultures>$(BundleCulture)</Cultures>
|
||||
<DefaultCulture>$(BundleCulture)</DefaultCulture>
|
||||
<!-- 两个项目共享同一目录:排除 Product.wxs 和 .wxl 本地化文件(由 MSI 项目处理) -->
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);Product.wxs;*.wxl</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Project Sdk="WixToolset.Sdk/6.0.2">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputName>PazeE-0.1.0-alpha-win-x64</OutputName>
|
||||
<OutputName>PazeE-0.1.1-alpha-win-x64</OutputName>
|
||||
<OutputType>Package</OutputType>
|
||||
<!-- MSI 作为中间产物输出到 obj(不单独发布,仅供 Bundle 内嵌) -->
|
||||
<OutputPath>obj\Release\</OutputPath>
|
||||
<!-- MSBuild 属性(供 HarvestDirectory 引用) -->
|
||||
<PublishBinDir>..\..\publish\0.1.0-alpha\bin</PublishBinDir>
|
||||
<PublishBinDir>..\..\publish\0.1.1-alpha\bin</PublishBinDir>
|
||||
<!-- 同时作为 WiX 预处理器变量传入 .wxs -->
|
||||
<DefineConstants>PazeEVersion=0.1.0-alpha;MsiVersion=0.1.0;PublishBinDir=$(PublishBinDir)</DefineConstants>
|
||||
<DefineConstants>PazeEVersion=0.1.1-alpha;MsiVersion=0.1.1;PublishBinDir=$(PublishBinDir)</DefineConstants>
|
||||
<!-- x64 平台:使 heat 生成的组件标记为 64 位,匹配 ProgramFiles64Folder -->
|
||||
<Platform>x64</Platform>
|
||||
<!-- 抑制 .NET 自包含运行时常见的 ICE 校验警告 -->
|
||||
@@ -17,7 +17,7 @@
|
||||
<!-- 两个项目共享同一目录:排除 Bundle.wxs,SDK 自动包含 Product.wxs + heat 生成文件 -->
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);Bundle.wxs</DefaultItemExcludes>
|
||||
<!-- MSI 输出目录(同时发布 MSI 到 installer 目录,与 .exe 并列) -->
|
||||
<PublishDir>..\..\publish\0.1.0-alpha\installer\</PublishDir>
|
||||
<PublishDir>..\..\publish\0.1.1-alpha\installer\</PublishDir>
|
||||
<!-- 多语言构建:简体中文(默认) + English + 繁體中文 -->
|
||||
<Cultures>zh-cn;en-us;zh-tw</Cultures>
|
||||
<DefaultCulture>zh-cn</DefaultCulture>
|
||||
@@ -52,11 +52,47 @@
|
||||
</HarvestDirectory>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 构建后复制 MSI 到 publish 目录(与 Bundle .exe 并列) -->
|
||||
<!-- 多语言构建:每个 culture 生成独立 MSI,复制默认语言(zh-cn)到 publish -->
|
||||
<Target Name="PublishMsi" AfterTargets="Build" Condition="'$(PublishDir)' != ''">
|
||||
<!-- 生成语言 transforms 并嵌入到多语言 MSI(独立文件,不影响原始 zh-cn MSI 供 Bundle 引用) -->
|
||||
<!-- 流程:Build 后 → 复制 zh-cn MSI 为多语言 MSI → 生成 .mst → 嵌入 transforms + 改 Summary Template -->
|
||||
<!-- 结果:obj\Release\<name>.msi = 多语言 MSI(publish 分发);obj\Release\zh-cn\<name>.msi = 单语言(Bundle 引用) -->
|
||||
<Target Name="EmbedLanguageTransforms" AfterTargets="Build" BeforeTargets="PublishMsi">
|
||||
<PropertyGroup>
|
||||
<WixExe Condition="'$(WixExe)' == '' And '$(PkgWixToolset_Sdk)' != ''">$([System.IO.Path]::Combine('$(PkgWixToolset_Sdk)', 'tools\net472\x64\wix.exe'))</WixExe>
|
||||
<WixExe Condition="'$(WixExe)' == ''">$(USERPROFILE)\.nuget\packages\wixtoolset.sdk\6.0.2\tools\net472\x64\wix.exe</WixExe>
|
||||
<ObjOut>$(OutputPath)</ObjOut>
|
||||
<MsiFile>$(OutputName).msi</MsiFile>
|
||||
<ZhCnMsi>$(ObjOut)zh-cn\$(MsiFile)</ZhCnMsi>
|
||||
<EnUsMsi>$(ObjOut)en-us\$(MsiFile)</EnUsMsi>
|
||||
<ZhTwMsi>$(ObjOut)zh-tw\$(MsiFile)</ZhTwMsi>
|
||||
<MultiLangMsi>$(ObjOut)$(MsiFile)</MultiLangMsi>
|
||||
<EnMst>$(ObjOut)en-us.mst</EnMst>
|
||||
<TwMst>$(ObjOut)zh-tw.mst</TwMst>
|
||||
<EmbedScript>$(MSBuildProjectDirectory)\embed_lang.ps1</EmbedScript>
|
||||
<PsExe>$(SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe</PsExe>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- 复制 zh-cn MSI 为多语言版本(原始 zh-cn MSI 保持单语言供 Bundle 引用) -->
|
||||
<Copy SourceFiles="$(ZhCnMsi)" DestinationFiles="$(MultiLangMsi)" SkipUnchangedFiles="false" />
|
||||
|
||||
<!-- 生成 en-us 语言 transform(相对于 zh-cn 基础 MSI) -->
|
||||
<Exec Command='"$(WixExe)" msi transform -t language "$(ZhCnMsi)" "$(EnUsMsi)" -out "$(EnMst)"'
|
||||
ConsoleToMSBuild="true" />
|
||||
|
||||
<!-- 生成 zh-tw 语言 transform -->
|
||||
<Exec Command='"$(WixExe)" msi transform -t language "$(ZhCnMsi)" "$(ZhTwMsi)" -out "$(TwMst)"'
|
||||
ConsoleToMSBuild="true" />
|
||||
|
||||
<!-- 嵌入 transforms 到多语言 MSI:storage '1033'=en-us, '1028'=zh-tw;Summary Template=x64;2052,1033,1028 -->
|
||||
<Exec Command='"$(PsExe)" -ExecutionPolicy Bypass -File "$(EmbedScript)" -MsiPath "$(MultiLangMsi)" -EnMst "$(EnMst)" -TwMst "$(TwMst)"'
|
||||
ConsoleToMSBuild="true" />
|
||||
|
||||
<Message Text="Multi-language MSI built: $(MultiLangMsi) (zh-cn + en-us + zh-tw, auto language selection)" Importance="high" />
|
||||
</Target>
|
||||
|
||||
<!-- 构建后复制多语言 MSI 到 publish 目录(与 Bundle .exe 并列) -->
|
||||
<Target Name="PublishMsi" AfterTargets="EmbedLanguageTransforms" Condition="'$(PublishDir)' != ''">
|
||||
<ItemGroup>
|
||||
<_MsiFiles Include="$(OutputPath)$(DefaultCulture)\*.msi" />
|
||||
<_MsiFiles Include="$(OutputPath)$(OutputName).msi" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(_MsiFiles)"
|
||||
DestinationFolder="$(MSBuildProjectDirectory)\$(PublishDir)"
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
<!-- 默认语言:简体中文(如果用户未选择) -->
|
||||
<Property Id="UILanguage" Value="zh-cn" />
|
||||
|
||||
<!-- 安装目录:C:\Program Files\PazeE\0.1.0-alpha\ -->
|
||||
<!-- 安装目录:C:\Program Files\PazeE\0.1.1-alpha\ -->
|
||||
<StandardDirectory Id="ProgramFiles64Folder">
|
||||
<Directory Id="PazeEFolder" Name="PazeE">
|
||||
<Directory Id="INSTALLFOLDER" Name="0.1.0-alpha" />
|
||||
<Directory Id="INSTALLFOLDER" Name="0.1.1-alpha" />
|
||||
</Directory>
|
||||
</StandardDirectory>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
Action="set"
|
||||
System="yes" />
|
||||
|
||||
<RegistryKey Root="HKLM" Key="SOFTWARE\PazeE\0.1.0-alpha">
|
||||
<RegistryKey Root="HKLM" Key="SOFTWARE\PazeE\0.1.1-alpha">
|
||||
<RegistryValue Type="string" Name="InstallPath" Value="[INSTALLFOLDER]" />
|
||||
<RegistryValue Type="string" Name="Version" Value="$(var.PazeEVersion)" />
|
||||
</RegistryKey>
|
||||
|
||||
@@ -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 <zh-cn.msi> -EnMst <en-us.mst> -TwMst <zh-tw.mst>
|
||||
|
||||
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)
|
||||
+127
-20
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
在新工单中引用
屏蔽一个用户