Skip to content

Commit d566e32

Browse files
committed
fix(sdk): geos-cbm support
1 parent 493a409 commit d566e32

4 files changed

Lines changed: 173 additions & 3 deletions

File tree

build.zig

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const SdkLibs = struct {
2323
a2600_3e: ?sdk_mod.Libs = null,
2424
a8cart: ?sdk_mod.Libs = null,
2525
snes: ?sdk_mod.Libs = null,
26+
geos_cbm: ?sdk_mod.Libs = null,
2627
};
2728

2829
pub fn build(b: *std.Build) void {
@@ -64,6 +65,7 @@ pub fn build(b: *std.Build) void {
6465
.{ .name = "atari2600-3e", .query = .{ .cpu_arch = .mos, .os_tag = .atari2600 } },
6566
.{ .name = "atari8-cart-std", .query = .{ .cpu_arch = .mos, .os_tag = .atari8 } },
6667
.{ .name = "snes", .query = .{ .cpu_arch = .mos, .os_tag = .snes } },
68+
.{ .name = "geos-cbm", .query = .{ .cpu_arch = .mos, .os_tag = .geos_cbm } },
6769
}) |pd| {
6870
const libs = sdk_mod.buildPlatform(b, sdk_src_raw, pd, optimize);
6971
const dest = b.fmt("mos-platform/{s}/lib", .{pd.name});
@@ -93,6 +95,7 @@ pub fn build(b: *std.Build) void {
9395
if (std.mem.eql(u8, pd.name, "atari2600-3e")) sdk_libs.a2600_3e = libs;
9496
if (std.mem.eql(u8, pd.name, "atari8-cart-std")) sdk_libs.a8cart = libs;
9597
if (std.mem.eql(u8, pd.name, "snes")) sdk_libs.snes = libs;
98+
if (std.mem.eql(u8, pd.name, "geos-cbm")) sdk_libs.geos_cbm = libs;
9699
}
97100

98101
// Translate neslib.h and nesdoug.h from the MOS SDK into Zig modules.
@@ -434,6 +437,16 @@ pub fn build(b: *std.Build) void {
434437
run_bininfo.addFileArg(exe.getEmittedBin());
435438
}
436439

440+
// ---- GEOS CBM hello ----
441+
{
442+
const step = b.step("geos-hello", "Build GEOS CBM hello example");
443+
const exe = addGeosExe(b, sdk_src, sdk_dep, sdk_libs.geos_cbm orelse @panic("geos-cbm libs not built"), optimize, "geos-hello", "geos/hello/hello.zig");
444+
const install = b.addInstallArtifact(exe, .{ .dest_sub_path = "geos-hello.cvt" });
445+
step.dependOn(&install.step);
446+
b.getInstallStep().dependOn(&install.step);
447+
run_bininfo.addFileArg(exe.getEmittedBin());
448+
}
449+
437450
// ---- MEGA65 (mega65-libc fetched automatically via build.zig.zon) ----
438451
if (b.lazyDependency("mega65-libc", .{})) |m65_dep| {
439452
{
@@ -2039,6 +2052,84 @@ fn simIoHeaderMod(
20392052
return tc.createModule();
20402053
}
20412054

2055+
fn geosHeaderMod(
2056+
b: *std.Build,
2057+
sdk_dep: *std.Build.Dependency,
2058+
target: std.Build.ResolvedTarget,
2059+
opt: std.builtin.OptimizeMode,
2060+
) *std.Build.Module {
2061+
const tc = b.addTranslateC(.{
2062+
// geos_zig.h suppresses _Static_assert (struct-size checks fail during translation)
2063+
.root_source_file = b.path("geos/geos_zig.h"),
2064+
.target = target,
2065+
.optimize = opt,
2066+
.link_libc = false,
2067+
});
2068+
tc.addIncludePath(sdk_dep.path("mos-platform/geos-cbm"));
2069+
tc.addIncludePath(sdk_dep.path("mos-platform/common/include"));
2070+
return tc.createModule();
2071+
}
2072+
2073+
fn addGeosExe(
2074+
b: *std.Build,
2075+
sdk_src: []const u8,
2076+
sdk_dep: *std.Build.Dependency,
2077+
libs: sdk_mod.Libs,
2078+
opt: std.builtin.OptimizeMode,
2079+
name: []const u8,
2080+
root_src: []const u8,
2081+
) *std.Build.Step.Compile {
2082+
const target = b.resolveTargetQuery(.{ .cpu_arch = .mos, .os_tag = .geos_cbm });
2083+
2084+
const wf = b.addWriteFiles();
2085+
const libc_txt = wf.add("libc.txt", b.fmt(
2086+
"include_dir={s}/mos-platform/geos-cbm\n" ++
2087+
"sys_include_dir={s}/mos-platform/common/include\n" ++
2088+
"crt_dir={s}/mos-platform/geos-cbm\n" ++
2089+
"msvc_lib_dir=\nkernel32_lib_dir=\ngcc_dir=\n",
2090+
.{ sdk_src, sdk_src, sdk_src },
2091+
));
2092+
// vlir.ld is the GEOS VLIR linker script (produces .cvt binary via OUTPUT_FORMAT FULL blocks).
2093+
// It includes c.ld (common sections) and geos.ld (GEOS symbol table) via INCLUDE directives,
2094+
// both resolved through the SEARCH_DIRs below.
2095+
const wrapper_ld = wf.add("geos-cbm-wrapper.ld", b.fmt(
2096+
\\SEARCH_DIR("{s}/mos-platform/geos-cbm");
2097+
\\SEARCH_DIR("{s}/mos-platform/common/ldscripts");
2098+
\\INCLUDE "vlir.ld"
2099+
, .{ sdk_src, sdk_src }));
2100+
2101+
const exe = b.addExecutable(.{
2102+
.name = name,
2103+
.root_module = b.createModule(.{
2104+
.root_source_file = b.path(root_src),
2105+
.target = target,
2106+
.optimize = opt,
2107+
.sanitize_c = .off,
2108+
}),
2109+
});
2110+
exe.bundle_compiler_rt = false;
2111+
exe.lto = .full;
2112+
exe.forceUndefinedSymbol("__zig_call_main_section");
2113+
exe.forceUndefinedSymbol("main");
2114+
if (libs.crt0_obj) |obj| exe.root_module.addObject(obj);
2115+
exe.root_module.linkLibrary(libs.crt);
2116+
exe.root_module.linkLibrary(libs.crt0);
2117+
exe.root_module.linkLibrary(libs.c);
2118+
if (libs.mem) |mem_obj| exe.root_module.addObject(mem_obj);
2119+
exe.setLibCFile(libc_txt);
2120+
exe.root_module.link_libc = true;
2121+
exe.setLinkerScript(wrapper_ld);
2122+
exe.root_module.addImport("mos_panic", b.createModule(.{
2123+
.root_source_file = b.path("sdk/panic.zig"),
2124+
.target = target,
2125+
.optimize = opt,
2126+
.sanitize_c = .off,
2127+
}));
2128+
exe.root_module.addImport("geos", geosHeaderMod(b, sdk_dep, target, opt));
2129+
2130+
return exe;
2131+
}
2132+
20422133
fn addApple2Exe(
20432134
b: *std.Build,
20442135
sdk_src: []const u8,

geos/geos_zig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Wrapper header for translate-c.
2+
// _Static_assert with struct-size checks fails during translation because
3+
// the host-side sizeof differs from the target layout. Suppress it here.
4+
#define _Static_assert(cond, msg)
5+
#include "geos.h"

geos/hello/hello.zig

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2024 Matheus C. França
2+
// SPDX-License-Identifier: Apache-2.0
3+
pub const panic = @import("mos_panic");
4+
5+
const geos = @import("geos");
6+
7+
// Clear screen: pattern 2 (white), rectangle from (0,0) to (319,199)
8+
// WORD(x) expands to lo,hi bytes: WORD(0)=0,0 WORD(319)=0x3F,0x01
9+
const clear_screen = [_]u8{
10+
geos.NEWPATTERN, 2,
11+
geos.MOVEPENTO, 0,
12+
0, 0,
13+
geos.RECTANGLETO, 0x3F,
14+
0x01, 199,
15+
0,
16+
};
17+
18+
const hello_str = "Hello, Zig!" ++ [_]u8{0};
19+
20+
// GEOS zero-page argument registers (geos.ld)
21+
fn r0() *volatile u16 {
22+
return @ptrFromInt(0x0002);
23+
} // __r0 = str ptr / GraphicsString arg
24+
fn r1H() *volatile u8 {
25+
return @ptrFromInt(0x0005);
26+
} // __r1H = y
27+
fn r11() *volatile u16 {
28+
return @ptrFromInt(0x0018);
29+
} // __r11 = x
30+
31+
// Raw GEOS kernel jump table entries (geos.ld)
32+
extern fn __GraphicsString() void; // 0xc136
33+
extern fn __PutString() void; // 0xc148
34+
extern fn __UseSystemFont() void; // 0xc14b
35+
extern fn __MainLoop() void; // 0xc1c3
36+
37+
fn graphicsString(graph_string: [*]const u8) void {
38+
r0().* = @intFromPtr(graph_string);
39+
__GraphicsString();
40+
}
41+
42+
fn putString(x: u16, y: u8, str: [*:0]const u8) void {
43+
r11().* = x;
44+
r1H().* = y;
45+
r0().* = @intFromPtr(str);
46+
__PutString();
47+
}
48+
49+
export fn main() void {
50+
geos.dispBufferOn = geos.ST_WR_FORE | geos.ST_WR_BACK;
51+
graphicsString(&clear_screen);
52+
__UseSystemFont();
53+
putString(80, 90, hello_str[0.. :0].ptr);
54+
__MainLoop();
55+
}

sdk/build.zig

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -977,11 +977,11 @@ fn buildGeosCbm(
977977
libcrt0.root_module.addIncludePath(.{ .cwd_relative = com_inc });
978978
libcrt0.root_module.addCSourceFiles(.{
979979
.root = .{ .cwd_relative = plat_dir },
980-
.files = &.{ "crt0.c", "geos_crt.c" },
980+
.files = &.{"crt0.c"},
981981
});
982982
libcrt0.root_module.addCSourceFiles(.{
983983
.root = .{ .cwd_relative = crt0_dir },
984-
.files = &.{ "crt0.S", "init-stack.S", "copy-zp-data.c", "zero-bss.c" },
984+
.files = &.{ "init-stack.S", "copy-zp-data.c", "zero-bss.c" },
985985
});
986986

987987
const libc = addLib(b, "c", target, opt);
@@ -991,7 +991,26 @@ fn buildGeosCbm(
991991
.files = &.{"mem.c"},
992992
});
993993

994-
return .{ .crt = libcrt, .crt0 = libcrt0, .c = libc };
994+
const mem_obj = b.addObject(.{
995+
.name = "mem",
996+
.root_module = b.createModule(.{ .target = target, .optimize = opt }),
997+
});
998+
mem_obj.root_module.addCSourceFiles(.{ .root = b.path("sdk"), .files = &.{"mem.s"} });
999+
mem_obj.lto = .none;
1000+
1001+
const crt0_obj = b.addObject(.{
1002+
.name = "crt0",
1003+
.root_module = b.createModule(.{ .target = target, .optimize = opt }),
1004+
});
1005+
crt0_obj.root_module.addIncludePath(.{ .cwd_relative = com_asm });
1006+
crt0_obj.root_module.addIncludePath(.{ .cwd_relative = com_inc });
1007+
crt0_obj.root_module.addCSourceFiles(.{
1008+
.root = .{ .cwd_relative = crt0_dir },
1009+
.files = &.{"crt0.S"},
1010+
});
1011+
crt0_obj.lto = .none;
1012+
1013+
return .{ .crt = libcrt, .crt0 = libcrt0, .c = libc, .mem = mem_obj, .crt0_obj = crt0_obj };
9951014
}
9961015

9971016
fn buildC128(

0 commit comments

Comments
 (0)