Skip to content

Commit b2c8952

Browse files
committed
fix(sdk): add eater example target
1 parent fe34b5b commit b2c8952

3 files changed

Lines changed: 112 additions & 2 deletions

File tree

build.zig

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const SdkLibs = struct {
3030
atari5200: ?sdk_mod.Libs = null,
3131
a8megacart: ?sdk_mod.Libs = null,
3232
a8xegs: ?sdk_mod.Libs = null,
33+
eater: ?sdk_mod.Libs = null,
3334
};
3435

3536
pub fn build(b: *std.Build) void {
@@ -78,6 +79,7 @@ pub fn build(b: *std.Build) void {
7879
.{ .name = "atari5200-supercart", .query = .{ .cpu_arch = .mos, .os_tag = .atari5200 } },
7980
.{ .name = "atari8-cart-megacart", .query = .{ .cpu_arch = .mos, .os_tag = .atari8 } },
8081
.{ .name = "atari8-cart-xegs", .query = .{ .cpu_arch = .mos, .os_tag = .atari8 } },
82+
.{ .name = "eater", .query = .{ .cpu_arch = .mos, .os_tag = .eater } },
8183
}) |pd| {
8284
const libs = sdk_mod.buildPlatform(b, sdk_src_raw, pd, optimize);
8385
const dest = b.fmt("mos-platform/{s}/lib", .{pd.name});
@@ -114,6 +116,7 @@ pub fn build(b: *std.Build) void {
114116
if (std.mem.eql(u8, pd.name, "atari5200-supercart")) sdk_libs.atari5200 = libs;
115117
if (std.mem.eql(u8, pd.name, "atari8-cart-megacart")) sdk_libs.a8megacart = libs;
116118
if (std.mem.eql(u8, pd.name, "atari8-cart-xegs")) sdk_libs.a8xegs = libs;
119+
if (std.mem.eql(u8, pd.name, "eater")) sdk_libs.eater = libs;
117120
}
118121

119122
// Translate neslib.h and nesdoug.h from the MOS SDK into Zig modules.
@@ -620,6 +623,16 @@ pub fn build(b: *std.Build) void {
620623
run_bininfo.addFileArg(exe.getEmittedBin());
621624
}
622625

626+
// ---- Ben Eater 6502 hello-lcd ----
627+
{
628+
const step = b.step("eater-hello-lcd", "Build Ben Eater 6502 LCD hello example");
629+
const exe = addEaterExe(b, sdk_dep, sdk_src, sdk_libs.eater orelse @panic("eater libs not built"), optimize, "eater-hello-lcd", "eater/hello-lcd/hello-lcd.zig");
630+
const install = b.addInstallArtifact(exe, .{ .dest_sub_path = "eater-hello-lcd.rom" });
631+
step.dependOn(&install.step);
632+
b.getInstallStep().dependOn(&install.step);
633+
run_bininfo.addFileArg(exe.getEmittedBin());
634+
}
635+
623636
// ---- PC Engine color-cycle ----
624637
{
625638
const step = b.step("pce-color-cycle", "Build PC Engine color-cycle example");
@@ -1854,6 +1867,49 @@ fn addLynxBllExe(
18541867
return exe;
18551868
}
18561869

1870+
fn addEaterExe(
1871+
b: *std.Build,
1872+
sdk_dep: *std.Build.Dependency,
1873+
sdk_src: []const u8,
1874+
libs: sdk_mod.Libs,
1875+
opt: std.builtin.OptimizeMode,
1876+
name: []const u8,
1877+
root_src: []const u8,
1878+
) *std.Build.Step.Compile {
1879+
const target = b.resolveTargetQuery(.{ .cpu_arch = .mos, .os_tag = .eater });
1880+
1881+
const wf = b.addWriteFiles();
1882+
const wrapper_ld = wf.add("eater-wrapper.ld", b.fmt(
1883+
\\SEARCH_DIR("{s}/mos-platform/eater");
1884+
\\SEARCH_DIR("{s}/mos-platform/common/ldscripts");
1885+
\\INCLUDE "{s}/mos-platform/eater/link.ld"
1886+
, .{ sdk_src, sdk_src, sdk_src }));
1887+
1888+
const exe = b.addExecutable(.{
1889+
.name = name,
1890+
.root_module = b.createModule(.{
1891+
.root_source_file = b.path(root_src),
1892+
.target = target,
1893+
.optimize = opt,
1894+
.sanitize_c = .off,
1895+
}),
1896+
});
1897+
exe.bundle_compiler_rt = false;
1898+
exe.lto = .full;
1899+
exe.root_module.addIncludePath(sdk_dep.path("mos-platform/eater"));
1900+
exe.root_module.addIncludePath(sdk_dep.path("mos-platform/common/include"));
1901+
exe.root_module.linkLibrary(libs.crt);
1902+
exe.root_module.linkLibrary(libs.crt0);
1903+
exe.root_module.linkLibrary(libs.c);
1904+
if (libs.crt0_obj) |obj| exe.root_module.addObject(obj);
1905+
exe.forceUndefinedSymbol("__zig_call_main_section");
1906+
exe.forceUndefinedSymbol("main");
1907+
exe.setLinkerScript(wrapper_ld);
1908+
addMosPanicImport(b, exe, target, opt);
1909+
1910+
return exe;
1911+
}
1912+
18571913
fn addPceExe(
18581914
b: *std.Build,
18591915
sdk_dep: *std.Build.Dependency,

eater/hello-lcd/hello-lcd.zig

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2024 Matheus C. França
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! Scrolls "Hello, World!" across a 16x2 HD44780 LCD on Ben Eater's 6502 computer.
4+
//! LCD driver (lcd_init / lcd_instruction / lcd_puts) lives in mos-platform/eater/lcd.c;
5+
//! delay() lives in mos-platform/eater/delay.c — both compiled into libc by the SDK.
6+
pub const panic = @import("mos_panic");
7+
8+
extern fn lcd_init() void;
9+
extern fn lcd_instruction(insn: u8) void;
10+
extern fn lcd_puts(str: [*:0]const u8) void;
11+
extern fn delay(ms: c_uint) void;
12+
13+
const LCD_I_DDRAM: u8 = 0x80; // Set DDRAM address
14+
const LCD_I_HOME: u8 = 0x02; // Move cursor to home position
15+
const LCD_I_SHIFT_L: u8 = 0x18; // Shift display left
16+
17+
const message: [*:0]const u8 = "Hello, World!";
18+
const message_len: c_int = 13;
19+
20+
pub export fn main() callconv(.c) void {
21+
lcd_init();
22+
23+
// Write message just off-screen (DDRAM address 16 = second half of row 1)
24+
lcd_instruction(LCD_I_DDRAM | 16);
25+
lcd_puts(message);
26+
27+
// Scroll the message across the visible 16-character window
28+
const count: c_int = 16 + message_len;
29+
var x: c_int = count;
30+
while (true) {
31+
delay(350);
32+
if (x <= 0) {
33+
lcd_instruction(LCD_I_HOME);
34+
x = count;
35+
} else {
36+
lcd_instruction(LCD_I_SHIFT_L);
37+
x -= 1;
38+
}
39+
}
40+
}

sdk/build.zig

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,13 +1090,27 @@ fn buildEater(
10901090
});
10911091
libcrt0.root_module.addCSourceFiles(.{
10921092
.root = .{ .cwd_relative = crt0_dir },
1093-
.files = &.{ "crt0.S", "init-stack.S", "copy-data.c", "zero-bss.c" },
1093+
.files = &.{ "init-stack.S", "copy-data.c", "zero-bss.c" },
10941094
});
10951095
libcrt0.root_module.addCSourceFiles(.{
10961096
.root = .{ .cwd_relative = b.fmt("{s}/exit", .{crt0_dir}) },
10971097
.files = &.{"exit-loop.c"},
10981098
});
10991099

1100+
// crt0.S provides .call_main (jsr main). Must be a TRUE object — section-only
1101+
// contribution with no exported symbol is silently skipped by ld.lld from archives.
1102+
const crt0_obj = b.addObject(.{
1103+
.name = "crt0",
1104+
.root_module = b.createModule(.{ .target = target, .optimize = opt }),
1105+
});
1106+
crt0_obj.root_module.addIncludePath(.{ .cwd_relative = com_asm });
1107+
crt0_obj.root_module.addIncludePath(.{ .cwd_relative = com_inc });
1108+
crt0_obj.root_module.addCSourceFiles(.{
1109+
.root = .{ .cwd_relative = crt0_dir },
1110+
.files = &.{"crt0.S"},
1111+
});
1112+
crt0_obj.lto = .none;
1113+
11001114
const libc = addLib(b, "c", target, opt);
11011115
libc.root_module.addIncludePath(.{ .cwd_relative = plat_dir });
11021116
libc.root_module.addIncludePath(.{ .cwd_relative = com_inc });
@@ -1105,7 +1119,7 @@ fn buildEater(
11051119
.files = &.{ "delay.c", "getchar.c", "lcd.c", "putchar.c" },
11061120
});
11071121

1108-
return .{ .crt = libcrt, .crt0 = libcrt0, .c = libc };
1122+
return .{ .crt = libcrt, .crt0 = libcrt0, .c = libc, .crt0_obj = crt0_obj };
11091123
}
11101124

11111125
fn buildGeosCbm(

0 commit comments

Comments
 (0)