├── .gitignore ├── README.md ├── build.zig ├── libc.txt ├── screenshot.webp └── src ├── 3ds ├── c.zig └── system.zig └── main.zig /.gitignore: -------------------------------------------------------------------------------- 1 | zig-out 2 | zig-cache -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zig-3ds 2 | 3 | ![](screenshot.webp) 4 | 5 | ## Getting started 6 | 7 | - [zig](https://ziglang.org/download/) 8 | - [devkitPro](https://devkitpro.org/wiki/Getting_Started) 9 | 10 | ``` 11 | pacman -S 3ds-dev 12 | git clone https://github.com/zig-homebrew/zig-3ds 13 | cd zig-3ds/ 14 | zig build # then run zig-out/zig-3ds.3dsx with citra 15 | ``` 16 | 17 | ## Resources 18 | 19 | - [3ds-examples](https://github.com/devkitPro/3ds-examples) 20 | - [libctru repository](https://github.com/devkitPro/libctru) 21 | - [libctru documentation](https://libctru.devkitpro.org/files.html) 22 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | const emulator = "citra"; 5 | const flags = .{"-lctru"}; 6 | const devkitpro = "/opt/devkitpro"; 7 | 8 | pub fn build(b: *std.build.Builder) void { 9 | const mode = b.standardReleaseOptions(); 10 | 11 | const obj = b.addObject("zig-3ds", "src/main.zig"); 12 | obj.setOutputDir("zig-out"); 13 | obj.linkLibC(); 14 | obj.setLibCFile(std.build.FileSource{ .path = "libc.txt" }); 15 | obj.addIncludeDir(devkitpro ++ "/libctru/include"); 16 | obj.addIncludeDir(devkitpro ++ "/portlibs/3ds/include"); 17 | obj.setTarget(.{ 18 | .cpu_arch = .arm, 19 | .os_tag = .freestanding, 20 | .abi = .eabihf, 21 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.mpcore }, 22 | }); 23 | obj.setBuildMode(mode); 24 | 25 | const extension = if (builtin.target.os.tag == .windows) ".exe" else ""; 26 | const elf = b.addSystemCommand(&(.{ 27 | devkitpro ++ "/devkitARM/bin/arm-none-eabi-gcc" ++ extension, 28 | "-g", 29 | "-march=armv6k", 30 | "-mtune=mpcore", 31 | "-mfloat-abi=hard", 32 | "-mtp=soft", 33 | "-Wl,-Map,zig-out/zig-3ds.map", 34 | "-specs=" ++ devkitpro ++ "/devkitARM/arm-none-eabi/lib/3dsx.specs", 35 | "zig-out/zig-3ds.o", 36 | "-L" ++ devkitpro ++ "/libctru/lib", 37 | "-L" ++ devkitpro ++ "/portlibs/3ds/lib", 38 | } ++ flags ++ .{ 39 | "-o", 40 | "zig-out/zig-3ds.elf", 41 | })); 42 | 43 | const dsx = b.addSystemCommand(&.{ 44 | devkitpro ++ "/tools/bin/3dsxtool" ++ extension, 45 | "zig-out/zig-3ds.elf", 46 | "zig-out/zig-3ds.3dsx", 47 | }); 48 | dsx.stdout_action = .ignore; 49 | 50 | b.default_step.dependOn(&dsx.step); 51 | dsx.step.dependOn(&elf.step); 52 | elf.step.dependOn(&obj.step); 53 | 54 | const run_step = b.step("run", "Run in Citra"); 55 | const citra = b.addSystemCommand(&.{ emulator, "zig-out/zig-3ds.3dsx" }); 56 | run_step.dependOn(&dsx.step); 57 | run_step.dependOn(&citra.step); 58 | } 59 | -------------------------------------------------------------------------------- /libc.txt: -------------------------------------------------------------------------------- 1 | # The directory that contains `stdlib.h`. 2 | # On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` 3 | include_dir=/opt/devkitpro/devkitARM/arm-none-eabi/include 4 | 5 | # The system-specific include directory. May be the same as `include_dir`. 6 | # On Windows it's the directory that includes `vcruntime.h`. 7 | # On POSIX it's the directory that includes `sys/errno.h`. 8 | sys_include_dir=/opt/devkitpro/devkitARM/arm-none-eabi/include 9 | 10 | # The directory that contains `crt1.o` or `crt2.o`. 11 | # On POSIX, can be found with `cc -print-file-name=crt1.o`. 12 | # Not needed when targeting MacOS. 13 | crt_dir=/opt/devkitpro/devkitARM/arm-none-eabi/lib/armv6k/fpu 14 | 15 | # The directory that contains `vcruntime.lib`. 16 | # Only needed when targeting MSVC on Windows. 17 | msvc_lib_dir= 18 | 19 | # The directory that contains `kernel32.lib`. 20 | # Only needed when targeting MSVC on Windows. 21 | kernel32_lib_dir= 22 | 23 | # The directory that contains `crtbeginS.o` and `crtendS.o` 24 | # Only needed when targeting Haiku. 25 | gcc_dir= 26 | -------------------------------------------------------------------------------- /screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zig-homebrew/zig-3ds/9cd3be3633da64124c6ea8b81baaac3f390cfa63/screenshot.webp -------------------------------------------------------------------------------- /src/3ds/c.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @cImport({ 2 | @cInclude("stdio.h"); 3 | @cInclude("3ds.h"); 4 | }); 5 | -------------------------------------------------------------------------------- /src/3ds/system.zig: -------------------------------------------------------------------------------- 1 | const c = @import("c.zig"); 2 | 3 | extern fn gspWaitForEvent(id: c.GSPGPU_Event, nextEvent: bool) void; // TODO: This should be removed 4 | pub fn gspWaitForVBlank() void { 5 | gspWaitForEvent(c.GSPGPU_EVENT_VBlank0, true); 6 | } 7 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const c = @import("3ds/c.zig"); 2 | const system = @import("3ds/system.zig"); 3 | 4 | export fn main(_: c_int, _: [*]const [*:0]const u8) void { 5 | c.gfxInitDefault(); 6 | defer c.gfxExit(); 7 | _ = c.consoleInit(c.GFX_TOP, null); 8 | 9 | _ = c.printf("Hello, Zig"); 10 | while (c.aptMainLoop()) { 11 | system.gspWaitForVBlank(); // TODO: This should be c.gspWaitForVBlank(); 12 | c.gfxSwapBuffers(); 13 | } 14 | } 15 | --------------------------------------------------------------------------------