KO
|
EN
gitlite — search
Search
#javascript
#python
#hacktoberfest
#react
#ai
#typescript
#llm
#go
#golang
#android
#machine-learning
#rust
#deep-learning
#linux
swift-embedded-nds
★ 25
Open GitHub ↗
Embedded Swift for Nintendo DS
Download README (.md)
Explore Similar Repositories
TrueNAS-Tricks
:
Prevent constant waking of TrueNAS HDDs for smartctl tasks, and other TrueNAS stuff
luci-app-oxidns
:
luci-app-oxidns is the LuCI management app for OxiDNS on OpenWrt
stabledaw.pinokio
:
No description available.
Bespok3d-desktop
:
Besok3d is an app store and package manager for Klipper 3D printers. Install plugins, adapters, and features with one tap. No terminal, no custom firmware.
virtual-cell-tutorial
:
No description available.
// repository documentation
Was this content helpful?
★ 0
(0 ratings)
Select Rating:
★
★
★
★
★
Submit Feedback
Recent Feedback
×
Download README
Do you want to download the
README.md
file for
swift-embedded-nds
?
Download (.md)
# swift-nds Nintendo DS homebrew written in **Embedded Swift**, ported from the official [libnds examples](https://github.com/devkitPro/nds-examples). Each example is a faithful translation of its C/C++ original that compiles with an Embedded Swift toolchain and runs on real hardware / emulators (melonDS, DeSmuME). ## Examples | Directory | Ported from | Demonstrates | |-----------|-------------|--------------| | [hello_world](hello_world) | `hello_world` | Console output, VBlank IRQ handler, touch read | | [ansi_console](ansi_console) | `Graphics/Printing/ansi_console` | ANSI cursor escape sequences | | [print_both_screens](print_both_screens) | `Graphics/Printing/print_both_screens` | A text console on each screen (main + sub) | | [console_windows](console_windows) | `Graphics/Printing/console_windows` | Multiple windowed consoles over one map | | [stopwatch](stopwatch) | `time/stopwatch` | Hardware timer elapsed/pause, formatted output | | [timercallback](timercallback) | `time/timercallback` | Timer IRQ callback (C function pointer), PSG sound | | [realtimeclock](realtimeclock) | `time/RealTimeClock` | RTC via `time()`/`gmtime`, digital + 3D analog watch face, day-of-week calc | | [pxi](pxi) | `pxi` | **Dual-CPU**: Swift ARM9 ↔ custom C ARM7 over a PXI channel (firmware JEDEC ID, touch pressure) | | [keyboard_stdin](keyboard_stdin) | `input/keyboard/keyboard_stdin` | On-screen keyboard, callback field, `iscanf` stdin | | [keyboard_async](keyboard_async) | `input/keyboard/keyboard_async` | Polled keyboard via `keyboardUpdate` | | [sprites_simple](sprites_simple) | `Graphics/Sprites/simple` | OAM sprites on both engines, VRAM banks, palettes | | [touch_test](touch_test) | `input/Touch_Pad/touch_test` | **grit-converted sprite**, OAM, touch min/max tracking | | [custom_font](custom_font) | `Graphics/Printing/custom_font` | **grit bitmap font** loaded into a console | | [bg_256color](bg_256color) | `Graphics/Backgrounds/256_color_bmp` | grit 8bpp bitmap background | | [bg_16bit](bg_16bit) | `Graphics/Backgrounds/16bit_color_bmp` | grit 16bpp bitmap, LZ77 `decompress` into VRAM | | [double_buffer](double_buffer) | `Graphics/Backgrounds/Double_Buffer` | Double-buffered bitmap BG via map-base flip | | [tilemap](tilemap) | `Graphics/grit/256colorTilemap` | grit tiles+map+palette, `BGCTRL`/char-base | | [effects_windows](effects_windows) | `Graphics/Effects/windows` | Hardware display window over a BG | | [sprite_rotate](sprite_rotate) | `Graphics/Sprites/sprite_rotate` | `oamRotateScale`, size-doubling | | [bitmap_sprites](bitmap_sprites) | `Graphics/Sprites/bitmap_sprites` | Sprites in bitmap/256/16-colour formats | | [sprite_ext_palettes](sprite_ext_palettes) | `Graphics/Sprites/sprite_extended_palettes` | Extended sprite palettes | | [animate_simple](animate_simple) | `Graphics/Sprites/animate_simple` | Two sprite-animation strategies (copy vs. preload) | | [allocation_test](allocation_test) | `Graphics/Sprites/allocation_test` | Sprite-gfx allocator stress test, `oamAllocateGfx`/`Free` | | [ext_palettes_bg](ext_palettes_bg) | `Graphics/Ext_Palettes/backgrounds` | Extended BG palettes, 8bpp tiled BGs | | [rotscale_text](rotscale_text) | `Graphics/Printing/rotscale_text` | Rotating/scaling text on an ExRotation BG | | [ortho](ortho) | `Graphics/3D/Ortho` | Orthographic vs. perspective projection toggle | | [both_screens_3d](both_screens_3d) | `Graphics/3D/3D_Both_Screens` | 3D on both screens via display capture (cube + pyramid) | | [nehe1](nehe1) | `Graphics/3D/nehe/lesson01` | Minimal NeHe 3D setup | | [nehe2](nehe2) | `Graphics/3D/nehe/lesson02` | First polygons (triangle + quad) | | [nehe3](nehe3) | `Graphics/3D/nehe/lesson03` | Per-vertex colour | | [nehe4](nehe4) | `Graphics/3D/nehe/lesson04` | Rotation (`glRotatef`) | | [nehe5](nehe5) | `Graphics/3D/nehe/lesson05` | Solid 3D pyramid + colour cube | | [nehe6](nehe6) | `Graphics/3D/nehe/lesson06` | **PCX texture** (bin2s blob + runtime `loadPCX`) on a cube | | [nehe7](nehe7) | `Graphics/3D/nehe/lesson07` | Lit textured cube, normals, toggleable lighting | | [nehe8](nehe8) | `Graphics/3D/nehe/lesson08` | Alpha-blended translucent cube | | [nehe9](nehe9) | `Graphics/3D/nehe/lesson09` | Blended colour-cycling star field | | [nehe10](nehe10) | `Graphics/3D/nehe/lesson10` | Textured world walkthrough (runtime-parsed `World.txt`), hardware fog + shadow-polygon cube | | [nehe10b](nehe10b) | `Graphics/3D/nehe/lesson10b` | All-fixed-point variant: v16/t16 geometry, integer LUT camera (`sinLerp`/`glRotatef32i`) | | [nehe11](nehe11) | `Graphics/3D/nehe/lesson11` | Waving-flag texture grid mesh | | [mixed_text_3d](mixed_text_3d) | `Graphics/3D/Mixed_Text_3D` | 3D + text console sharing one screen, `%f` printf | | [display_list](display_list) | `Graphics/3D/Display_List` | Hand-built display list (packed FIFO commands) | | [simple_tri](simple_tri) | `Graphics/3D/Simple_Tri` | 3D engine, GL pipeline, fixed-point + float GL calls | | [simple_quad](simple_quad) | `Graphics/3D/Simple_Quad` | 3D `GL_QUAD`, D-pad rotation | | [display_list_2](display_list_2) | `Graphics/3D/Display_List_2` | **bin2o** display list (teapot), hardware lighting | | [toon_shading](toon_shading) | `Graphics/3D/Toon_Shading` | Display-list blob, toon table, stylus rotate | | [textured_quad](textured_quad) | `Graphics/3D/Textured_Quad` | **Texture blob**, `glTexImage2D`, texcoords/normals | | [textured_cube](textured_cube) | `Graphics/3D/Textured_Cube` | Texture blob, vertex/uv tables, display-capture motion blur | | [env_mapping](env_mapping) | `Graphics/3D/Env_Mapping` | `TEXGEN_NORMAL` reflection mapping, texture matrix | | [picking](picking) | `Graphics/3D/Picking` | 3D picking via `gluPickMatrix` + position test | | [boxtest](boxtest) | `Graphics/3D/BoxTest` | Hardware frustum `BoxTest`/`BoxTestf`, cull modes, vertex/poly RAM + `cpuTiming` readout | | [capture](capture) | `Graphics/capture/ScreenShot` | Rotating bitmap/256/16-colour sprites, display capture → 24-bit BMP saved via **libfat** | | [rotation](rotation) | `Graphics/Backgrounds/rotation` | Raw bitmap+palette blobs, rotation/scale background | | [all_in_one](all_in_one) | `Graphics/Backgrounds/all_in_one` | ~40-demo menu: every BG type/size, HW+SW scrolling, mosaic/rotate/scale, ext palettes, multilayer — pre-assembled grit `.s` assets | | [paletted_cube](paletted_cube) | `Graphics/3D/Paletted_Cube` | **All DS texture formats** (grit `.tga` + compressed blobs), palette swap | | [gl2d_primitives](gl2d_primitives) | `Graphics/gl2d/primitives` | **Easy GL2D**: boxes/triangles/lines/pixels, `sinLerp` | | [gl2d_dual_screen](gl2d_dual_screen) | `Graphics/gl2d/dual_screen` | GL2D mirrored to both screens via display capture | | [gl2d_fonts](gl2d_fonts) | `Graphics/gl2d/fonts` | GL2D sprite-set bitmap fonts (grit atlas + uvcoord tables) | | [gl2d_scrolling](gl2d_scrolling) | `Graphics/gl2d/scrolling` | GL2D tile-set scrolling engine + animated sprite, camera | | [gl2d_sprites](gl2d_sprites) | `Graphics/gl2d/sprites` | GL2D sprite rotate/scale/stretch/flip, palette-swap tint | | [gl2d_2dplus3d](gl2d_2dplus3d) | `Graphics/gl2d/2Dplus3D` | 3D textured mesh (ported vertex buffer) + GL2D 2D sprites, fixed-point atan2 | | [wifi_autoconnect](wifi_autoconnect) | `dswifi/autoconnect` | **dswifi**: connect via firmware WFC settings, print DHCP IP/gateway/DNS | | [wifi_httpget](wifi_httpget) | `dswifi/httpget` | **dswifi**: DNS resolve + TCP socket + live HTTP/1.1 GET, prints response | | [wifi_ap_search](wifi_ap_search) | `dswifi/ap_search` | **dswifi**: AP scan/select UI, signal-strength icon (VBlank IRQ), WEP/WPA key entry via on-screen keyboard, connect + DNS | | [exception_test](exception_test) | `debugging/exceptionTest` | Default exception handler, raw memory access | ## Building Requirements: - **devkitPro** with `devkitARM`, `libnds`, `calico`, and `ndstool` (`dkp-pacman -S nds-dev`). - An **Embedded-Swift-capable** Swift toolchain (a recent `DEVELOPMENT-SNAPSHOT`). ```sh export DEVKITPRO=/opt/devkitpro export DEVKITARM=$DEVKITPRO/devkitARM cd hello_world && make # or any other example dir # -> hello_world.nds make SWIFTC=/path/to/swiftc # if swiftc isn't the Embedded toolchain on PATH ``` ## The `NDS` Swift package The repo is a Swift package ([Package.swift](Package.swift)) that vends an idiomatic Swift overlay over libnds, so an example can `import NDS` and write ```swift import NDS IRQ.vblank.set { frame += 1 } Console.demoInit() Console.print("Hello DS dev'rs\n") while System.mainLoop { System.waitForVBlank() Keys.scan() if Keys.down.contains(.start) { break } } ``` instead of the raw C (`videoSetMode(MODE_0_2D.rawValue)`, `irqSet`, `nds_puts`, …). - **`Sources/CNDS`** is a systemLibrary target: the C interop (module map, umbrella header, and the [shim](Sources/CNDS/shim.c)) that exposes `<nds.h>` + gl2d + dswifi as `import CNDS`. This is the single source of truth the Makefiles also use. - **`Sources/NDS`** is the overlay — one file per libnds subsystem ([Video](Sources/NDS/Video.swift), [Background](Sources/NDS/Background.swift), [Sprite](Sources/NDS/Sprite.swift), [Console](Sources/NDS/Console.swift), [VideoGL](Sources/NDS/VideoGL.swift), [GL2D](Sources/NDS/GL2D.swift), [Sound](Sources/NDS/Sound.swift), [Input](Sources/NDS/Input.swift), [Timer](Sources/NDS/Timer.swift), [Interrupt](Sources/NDS/Interrupt.swift), [DMA](Sources/NDS/DMA.swift), and the rest). Each C prefix becomes a caseless `enum` namespace, C flag masks become `OptionSet`s (`Key`, `IRQ`), and the macro-only APIs the importer drops (`RGB15`, `inttov16`, the pointer/register macros) get real Swift types. Every wrapper is `@inline(__always)`, so it compiles down to the exact underlying libnds call — zero overhead. `Sources/NDS/Exports.swift` does `@_exported import CNDS`, so `import NDS` also re-exports the full raw libnds API: nothing is hidden, and the two styles mix freely. **Every example is ported to the overlay** — each `source/main.swift` does `import NDS` and uses the idiomatic wrappers (`Video`, `Background`, `Sprite`/`OAM`, `GL`, `GL2D`, `Keys`, `Console`, `Wifi`, …), dropping to raw `CNDS`/`_Volatile` only for the handful of genuinely low-level bits (custom hardware registers in [capture](capture) / [all_in_one](all_in_one), the `glColorTableEXT` palette calls, raw sockets in [wifi_httpget](wifi_httpget)). > **Note:** SwiftPM cannot cross-compile Embedded Swift to the DS's bare-metal > `armv5te`, so `swift build` does not produce a `.nds`. The package is a *source > library*; the Makefiles below compile the `NDS` target into a standalone > Embedded Swift module and link it into each ROM. ## How it works Everything shared lives in [common/](common) and [Sources/](Sources); each example is just a `source/main.swift` plus a three-line Makefile that sets `TARGET` and `include`s [common/common.mk](common/common.mk). Examples with assets set `GRAPHICS := gfx` (a directory of `.png`/`.bmp`/`.tga` + `.grit` pairs) and/or `DATA := data` (a directory of `.bin` blobs); both can be set at once. `EXTRA_HEADERS := …` lists hand-written headers (e.g. texture-packer uvcoord tables) to expose to Swift with the same `nds_asset_*()` accessor treatment. `ASM_ASSETS := asmdata` points at a directory of ready-made grit/Cearn `.s` data files (assembled and linked as-is); pair it with `EXTRA_HEADERS` for the matching `extern const …[]` declarations so the symbols reach Swift via the same `nds_asset_*()` accessors (used by [all_in_one](all_in_one)). `ARM7_SRC := arm7` points at a directory of ARM7 C sources: instead of packaging calico's prebuilt ARM7, [common.mk](common/common.mk) compiles them with `ds7.specs` + `-lcalico_ds7` into a custom ARM7 binary and feeds that to `ndstool -7`. Only the ARM9 is Embedded Swift; the ARM7 stays C (low-level system code). Used by the dual-CPU [pxi](pxi) example. ### Assets (grit + bin2o) For examples that set `GRAPHICS` or `DATA`, [common.mk](common/common.mk) runs **grit** on each image and **bin2s** on each `.bin` blob, compiles the generated data, and gathers the generated headers into a single bridging header handed to Swift — so the symbols (`ballTiles`, `fontPal`, `teapot_bin`, …) are visible alongside `import CNDS`. grit images become tile/palette tables; blobs become raw byte arrays for display lists, textures, bitmaps, etc. A C global array imports into Swift as a **tuple** (a *copy*), not a pointer. Two ways to use the data: - **Copying it yourself** (e.g. into sprite VRAM): `withUnsafeBytes(of: ballTiles) { … }` and loop — the temporary is valid for the closure body, which is all a synchronous copy needs. - **Handing a pointer to libnds** (e.g. `ConsoleFont.gfx` for `consoleSetFont`): use the generated `nds_asset_<symbol>()` accessor, which returns the address of the *real linked symbol*. Passing a `withUnsafeBytes` temporary here is a bug — the pointer dangles once the closure returns and libnds renders garbage (a black screen, in the custom_font case). ### The build pipeline ([common.mk](common/common.mk)) 1. **`NDS` overlay module → object + `.swiftmodule`.** The [Sources/NDS](Sources/NDS) files are compiled once as a standalone Embedded Swift module (`-module-name NDS -parse-as-library -emit-module`), producing `NDS.o` and `NDS.swiftmodule` in the example's `build/`. 2. **Embedded Swift `main.swift` → object.** Targets `armv5te-none-none-eabi`, matching the DS's ARM946E-S, and imports the `NDS` module built in step 1. Release toolchains only ship an armv4t Embedded stdlib slice, so this needs a patched toolchain with an armv5te slice (set `SWIFTC` in common.mk or on the command line). Embedded clang is pointed at newlib's headers, libnds, calico, and the [Sources/CNDS](Sources/CNDS) module map. 3. **C shim → object** with devkitARM ([Sources/CNDS/shim.c](Sources/CNDS/shim.c)). 4. **Link** `main.swift.o` + `NDS.o` + `shim.o` against the modern calico-based libnds (`-specs=…/ds9.specs`, `-lnds9 -lcalico_ds9`). 5. **Package** the `.nds` with `ndstool` (calico's prebuilt ARM7 + default icon). ### The C shim ([Sources/CNDS/shim.c](Sources/CNDS/shim.c), [shim.h](Sources/CNDS/shim.h)) Bridges the gaps between Embedded Swift and libnds: - **Variadic stdio.** Swift imports C variadic functions but cannot call them, so `nds_puts` / `nds_printf_1i` / `nds_printf_2i` forward into `iprintf`, `nds_printf_1f` / `nds_printf_2f` into `printf` (for `%f`), and `nds_scanf_str` into `iscanf` — all formatting stays inside libnds. - **Macro-only APIs.** Preprocessor macros the Swift importer can't surface are re-exported as real functions: `nds_timer_freq_1024` (`TIMER_FREQ_1024`), `nds_bus_clock` (`BUS_CLOCK`), the `SPRITE_PALETTE*` / `SPRITE_GFX*` / `BG_PALETTE` pointer macros, the `GFX_TEX_COORD` / `GFX_BUSY` / `GFX_POLYGON_RAM_USAGE` 3D registers, and the display-capture motion-blur registers (`nds_motion_blur_*`). - **Runtime support devkitARM doesn't provide for this target:** - `posix_memalign` (Swift's allocator wants it; newlib only has `memalign`). - `__atomic_*` outline helpers — armv5te has no atomic instructions, so LLVM emits libcalls; implemented with a short interrupt lock (safe on the single ARM9 core). - `arc4random_buf` — referenced by Swift's runtime; the NDS has no entropy source, so a small non-cryptographic xorshift fills the buffer. ### Swift ↔ libnds interop notes - `import CNDS` ([Sources/CNDS/module.modulemap](Sources/CNDS/module.modulemap)) exposes `<nds.h>` + the shim; `import NDS` adds the idiomatic overlay on top (and re-exports `CNDS`). - A non-capturing top-level Swift function bridges automatically to the C function-pointer types used by `irqSet` / `timerStart`, and can be assigned to a callback *field* on a struct too (e.g. `kbd.pointee.OnKeyPressed = …`). - libnds C enums (`VideoMode`, …) import as Swift types — pass `.rawValue` where a function wants the underlying `u32` (e.g. `videoSetMode(MODE_0_2D.rawValue)`). - `touchRead` returns `Bool`; some libnds calls return distinct enum types, so match Swift's stricter typing rather than the C "everything is an int" style. - A few constants use the function-like `BIT(n)` macro (e.g. `KEY_TOUCH`), which the importer drops — spell the bit out in Swift (`1 << 14`). - **3D / GL:** the float GL entry points (`glRotateX`, `gluPerspective`, `gluLookAt`, …) are real inline functions and import fine — Embedded Swift's soft-float calls them directly. `POLY_ALPHA(n)` is also an inline function, but `inttov16` / `floattof32` are function-like macros, so they're reimplemented as tiny Swift helpers. Functions typed to take a GL enum (`glMatrixMode`, `glBegin`) accept the enum value directly; where one wants a raw `int`/`u32` (`glEnable`, an OR with `POLY_CULL_NONE`), pass `.rawValue`. - In a file named `main.swift`, top-level code **is** the entry point — no `@main`. - `swiWaitForVBlank` is a macro alias; call the underlying `threadWaitForVBlank()`. - `KEY_*` are `#define`d as plain integers, so `keysDown() & KEY_START` works directly (no `.rawValue`). `DutyCycle_50` is a macro alias for the importable enum `SoundDuty_50`. ## Remaining The ported set above covers every major interop pattern: console, timers, IRQ callbacks, PSG sound, input/keyboard, sprites/OAM, 3D/GL, grit graphics, and **bin2o binary blobs** (display lists, textures, all DS texture formats). What's left in the upstream example tree still needs its *own* toolchain or runtime work, not just more of the same translation: - **Audio** (`audio/maxmod/*`) — needs the `mmutil` soundbank pipeline. - **Filesystem** (`filesystem/*`) — libfat + DLDI / NitroFS. (The libfat *write* path is already exercised by [capture](capture); the dedicated `filesystem/*` demos additionally need NitroFS / directory traversal.) - **Wi-Fi** (`dswifi/*`) — **done**: all three examples ([wifi_autoconnect](wifi_autoconnect), [wifi_httpget](wifi_httpget), [wifi_ap_search](wifi_ap_search)) are ported on the dswifi stack. - **Dual-CPU** — the [pxi](pxi) example is **done** (Swift ARM9 + a custom C ARM7, via `ARM7_SRC`); the `combined` templates would reuse the same path. - **Large GL2D demos** (`Graphics/gl2d/*`) — big, asset-heavy (but buildable on the existing grit + bin2o + GL foundations). The `common/` scaffolding (grit + bin2o pipelines) applies to the asset-heavy graphics ones unchanged; the others are each a distinct piece of plumbing.