From fca748ffba4518a205e3ebb46ff6ea062dc5fdcc Mon Sep 17 00:00:00 2001 From: GasInfinity Date: Wed, 29 Oct 2025 14:34:58 +0100 Subject: [PATCH 1/2] fix: add `i86` cpu in `update_cpu_features` --- lib/std/Target/x86.zig | 4 +++- tools/update_cpu_features.zig | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/std/Target/x86.zig b/lib/std/Target/x86.zig index fb76bb757f..94dad2a673 100644 --- a/lib/std/Target/x86.zig +++ b/lib/std/Target/x86.zig @@ -3084,7 +3084,9 @@ pub const cpu = struct { pub const @"i86": CpuModel = .{ .name = "i86", .llvm_name = null, - .features = featureSet(&[_]Feature{}), + .features = featureSet(&[_]Feature{ + .@"16bit_mode", + }), }; pub const @"i386": CpuModel = .{ .name = "i386", diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index 29877fff15..c077b16c0a 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -1541,6 +1541,13 @@ const targets = [_]ArchTarget{ .deps = &.{}, }, }, + .extra_cpus = &.{ + .{ + .llvm_name = null, + .zig_name = "i86", + .features = &.{"16bit_mode"}, + }, + }, .omit_cpus = &.{ // LLVM defines a bunch of dumb aliases with foreach loops in X86.td. "pentium_mmx", From ef4f6e6c0578288144760437231620efbdb1edd6 Mon Sep 17 00:00:00 2001 From: GasInfinity Date: Wed, 29 Oct 2025 14:35:33 +0100 Subject: [PATCH 2/2] feat: add `x86_16` debug `cpu_context` --- lib/std/debug/cpu_context.zig | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index 9d460a1797..9a0e00d20b 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -19,6 +19,7 @@ else switch (native_arch) { .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, .ve => Ve, .s390x => S390x, + .x86_16 => X86_16, .x86 => X86, .x86_64 => X86_64, else => noreturn, @@ -1350,6 +1351,46 @@ const Ve = extern struct { } }; +const X86_16 = struct { + pub const Register = enum { + // zig fmt: off + sp, bp, ss, + ip, cs, + // zig fmt: on + }; + + regs: std.enums.EnumArray(Register, u16), + + pub inline fn current() X86_16 { + var ctx: X86_16 = undefined; + asm volatile ( + \\ movw %%sp, 0x00(%%di) + \\ movw %%bp, 0x02(%%di) + \\ movw %%ss, 0x04(%%di) + \\ pushw %%cs + \\ call 1f + \\1: + \\ popw 0x06(%%di) + \\ popw 0x08(%%di) + : + : [gprs] "{di}" (&ctx.regs.values), + : .{ .memory = true }); + return ctx; + } + + // NOTE: There doesn't seem to be any standard for DWARF x86-16 so we'll just reuse the ones for x86. + pub fn dwarfRegisterBytes(ctx: *X86_16, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 4 => return @ptrCast(ctx.regs.getPtr(.sp)), + 5 => return @ptrCast(ctx.regs.getPtr(.bp)), + 6 => return @ptrCast(ctx.regs.getPtr(.ip)), + 41 => return @ptrCast(ctx.regs.getPtr(.cs)), + 42 => return @ptrCast(ctx.regs.getPtr(.ss)), + else => return error.InvalidRegister, + } + } +}; + const X86 = struct { /// The first 8 registers here intentionally match the order of registers in the x86 instruction /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,