Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NDS/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/
*.elf
*.nds
*.map
175 changes: 175 additions & 0 deletions NDS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#---------------------------------------------------------------------------------
# NDS/ -- Nintendo DS port of the Fueling app, built with Embedded Swift.
#
# Build system copied from ClassicUI's ports/NDS (branch feature/nds), itself
# modeled on MillerTechnologyPeru/swift-embedded-nds: everything is compiled
# whole-module for armv5te-none-none-eabi (an exact match for the DS's
# ARM946E-S), linked against calico-based libnds, and packaged with ndstool
# using calico's prebuilt ARM7.
#
# The port reuses the real model stack as Embedded Swift modules, compiled in
# dependency order: FoundationEmbedded (PureSwift/swift-embedded-foundation)
# -> CoreModel (in-memory store + FetchRequest evaluation engine)
# -> CoreFueling (the shared domain entities and search predicates from this
# repository) -> the port sources in source/.
#
# Screen layout (the inverse of ClassicUI's port): the *top* screen hosts the
# UI through the software rasterizer in source/Renderer.swift, drawn into a
# double-buffered 16bpp bitmap background on the main engine; the *bottom*
# (touch) screen hosts libnds' on-screen keyboard on the sub engine, used to
# type into the locations search filter.
#
# Prerequisites:
# - devkitPro with devkitARM, libnds, calico, ndstool
# (export DEVKITPRO=/opt/devkitpro, DEVKITARM=$DEVKITPRO/devkitARM)
# - A Swift toolchain whose Embedded stdlib ships an armv5te-none-none-eabi
# slice. Release toolchains only ship armv4t; a recent `main` development
# snapshot is needed. Override with `make SWIFTC=/path/to/swiftc`.
# - python3 with Pillow (tools/gen_font.py rasterizes the UI font from the
# host's Helvetica at build time)
#---------------------------------------------------------------------------------
.SUFFIXES:

ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitpro")
endif
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=$$DEVKITPRO/devkitARM")
endif

SWIFTC ?= swiftc

# Dependency source checkouts (Embedded modules built from source; override
# for a different layout). CoreModel needs the embedded-no-concurrency
# support (PureSwift/CoreModel PR #29).
FOUNDATION_EMBEDDED_DIR ?= $(HOME)/Developer/swift-embedded-foundation
COREMODEL_DIR ?= $(HOME)/Developer/CoreModel
COREFUELING_DIR := ../Sources/CoreFueling

TARGET := Fueling
NDS_TITLE := Fueling
NDS_SUBTITLE := Fueling locations
BUILD := build
COMMON := common/

LIBNDS := $(DEVKITPRO)/libnds
CALICO := $(DEVKITPRO)/calico

PREFIX := $(DEVKITARM)/bin/arm-none-eabi-
CC := $(PREFIX)gcc
LD := $(PREFIX)gcc

#---------------------------------------------------------------------------------
# code generation -- ARM946E-S / armv5te, soft float, no FPU.
#---------------------------------------------------------------------------------
ARCH := -march=armv5te -mtune=arm946e-s -mthumb

NDSDEFS := -DARM9 -D__NDS__ -I$(COMMON) \
-I$(LIBNDS)/include -I$(CALICO)/include

CFLAGS := -g -Wall -O2 -ffunction-sections -fdata-sections $(ARCH) \
$(NDSDEFS)

LDFLAGS := -specs=$(CALICO)/share/ds9.specs -g $(ARCH) \
-Wl,-Map,$(TARGET).map -Wl,--gc-sections \
-L$(LIBNDS)/lib -L$(CALICO)/lib

# dswifi must precede libnds.
LIBS := -ldswifi9 -lnds9 -lcalico_ds9 -lm

#---------------------------------------------------------------------------------
# Embedded Swift flags, targeting armv5te-none-none-eabi.
#---------------------------------------------------------------------------------
SWIFTFLAGS := -target armv5te-none-none-eabi \
-enable-experimental-feature Embedded \
-wmo -Osize \
-module-name Fueling \
-Xcc -DARM9 -Xcc -D__NDS__ \
-Xcc -march=armv5te -Xcc -mfloat-abi=soft \
-Xcc -isystem -Xcc $(DEVKITARM)/arm-none-eabi/include \
-Xcc -I$(COMMON) \
-Xcc -I$(LIBNDS)/include \
-Xcc -I$(CALICO)/include \
-Xcc -fmodule-map-file=$(COMMON)module.modulemap

PORT_SWIFT := $(wildcard source/*.swift)
GEN_SWIFT := $(BUILD)/FontAssets.swift $(BUILD)/ServerConfig.swift

FOUNDATION_EMBEDDED_SRC := $(wildcard $(FOUNDATION_EMBEDDED_DIR)/Sources/FoundationEmbedded/*.swift)
COREMODEL_SRC := $(shell find $(COREMODEL_DIR)/Sources/CoreModel -name '*.swift')
COREFUELING_SRC := $(wildcard $(COREFUELING_DIR)/*.swift)

# Swift string operations (search predicates, sorting) need the Embedded
# stdlib's Unicode data tables at link time.
TOOLCHAIN_LIB := $(dir $(shell which $(SWIFTC)))../lib/swift/embedded/armv5te-none-none-eabi
LDFLAGS += -L$(TOOLCHAIN_LIB)
LIBS += -lswiftUnicodeDataTables

OFILES := $(BUILD)/fueling.swift.o $(BUILD)/CoreFueling.o $(BUILD)/CoreModel.o \
$(BUILD)/FoundationEmbedded.o $(BUILD)/shim.o

ARM7_ELF := $(CALICO)/bin/ds7_maine.elf

#---------------------------------------------------------------------------------
.PHONY: all clean

all: $(TARGET).nds

$(BUILD):
@mkdir -p $@

# Font conversion: the host's Helvetica at 13 px -> antialiased
# 8-bit-coverage glyph table (same face and size ClassicUI resolves).
$(BUILD)/FontAssets.swift: tools/gen_font.py | $(BUILD)
@echo generating font assets
python3 tools/gen_font.py $(BUILD)

# Injected server URL (FUELING_SERVER_URL, default http://10.0.2.2:8080).
# Re-evaluated every build; the file is only rewritten when the URL changes.
$(BUILD)/ServerConfig.swift: tools/gen_server.py FORCE | $(BUILD)
@python3 tools/gen_server.py $(BUILD)

FORCE:

# Model-layer modules, compiled in dependency order (each emits its
# .swiftmodule into $(BUILD) for the next stage's -I $(BUILD)).
$(BUILD)/FoundationEmbedded.o: $(FOUNDATION_EMBEDDED_SRC) | $(BUILD)
@echo compiling FoundationEmbedded
$(SWIFTC) $(SWIFTFLAGS) -module-name FoundationEmbedded -parse-as-library \
-emit-module -emit-module-path $(BUILD)/FoundationEmbedded.swiftmodule \
-c $(FOUNDATION_EMBEDDED_SRC) -o $@

$(BUILD)/CoreModel.o: $(COREMODEL_SRC) $(BUILD)/FoundationEmbedded.o | $(BUILD)
@echo compiling CoreModel
$(SWIFTC) $(SWIFTFLAGS) -module-name CoreModel -parse-as-library -I $(BUILD) \
-emit-module -emit-module-path $(BUILD)/CoreModel.swiftmodule \
-c $(COREMODEL_SRC) -o $@

$(BUILD)/CoreFueling.o: $(COREFUELING_SRC) $(BUILD)/CoreModel.o | $(BUILD)
@echo compiling CoreFueling
$(SWIFTC) $(SWIFTFLAGS) -module-name CoreFueling -parse-as-library -I $(BUILD) \
-emit-module -emit-module-path $(BUILD)/CoreFueling.swiftmodule \
-c $(COREFUELING_SRC) -o $@

# All port Swift (+ generated font) -> one object, whole-module.
$(BUILD)/fueling.swift.o: $(PORT_SWIFT) $(GEN_SWIFT) $(BUILD)/CoreFueling.o \
$(COMMON)module.modulemap $(COMMON)nds_umbrella.h $(COMMON)shim.h | $(BUILD)
@echo compiling Swift
$(SWIFTC) $(SWIFTFLAGS) -I $(BUILD) -c $(PORT_SWIFT) $(GEN_SWIFT) -o $@

$(BUILD)/shim.o: $(COMMON)shim.c $(COMMON)shim.h | $(BUILD)
@echo $(notdir $<)
$(CC) $(CFLAGS) -c $< -o $@

$(TARGET).elf: $(OFILES)
@echo linking $(notdir $@)
$(LD) $(LDFLAGS) $(OFILES) $(LIBS) -o $@

$(TARGET).nds: $(TARGET).elf
@echo packaging $(notdir $@)
ndstool -c $@ -9 $(TARGET).elf -7 $(ARM7_ELF) \
-b $(CALICO)/share/nds-icon.bmp "$(NDS_TITLE);$(NDS_SUBTITLE);swift-nds"

clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds $(TARGET).map
5 changes: 5 additions & 0 deletions NDS/common/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Exposes libnds (nds.h) plus our shim to Swift as `import NDS`.
module NDS {
header "nds_umbrella.h"
export *
}
20 changes: 20 additions & 0 deletions NDS/common/nds_umbrella.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//---------------------------------------------------------------------------------
// nds_umbrella.h -- single header exposed to Swift as the `NDS` module.
//
// Trimmed from MillerTechnologyPeru/swift-embedded-nds's umbrella: Fueling
// only needs core libnds (video/backgrounds/input) -- the UI is a
// software rasterizer into a 16bpp bitmap background (source/Renderer.swift),
// same shape as junkbot-swift's ports/NDS.
//---------------------------------------------------------------------------------
#ifndef FUELING_NDS_UMBRELLA_H
#define FUELING_NDS_UMBRELLA_H

#include <nds.h>
#include <stdlib.h>
#include <dswifi9.h> // Wifi_InitDefault / Wifi_GetIPInfo
#include <arpa/inet.h> // struct in_addr
#include <sys/socket.h> // socket / connect / send / recv
#include <netdb.h> // gethostbyname, struct hostent
#include "shim.h"

#endif // FUELING_NDS_UMBRELLA_H
132 changes: 132 additions & 0 deletions NDS/common/shim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//---------------------------------------------------------------------------------
// shim.c -- C support for the Fueling NDS port (see shim.h).
//
// Runtime helpers mirror junkbot-swift's ports/NDS shim (itself derived from
// MillerTechnologyPeru/swift-embedded-nds).
//---------------------------------------------------------------------------------
#include <nds.h>
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "shim.h"

void nds_puts(const char *s) {
iprintf("%s", s);
}

//---------------------------------------------------------------------------------
// Runtime support the Embedded Swift object needs but devkitARM's libc/libgcc
// do not provide for this target.
//---------------------------------------------------------------------------------

// Embedded Swift's runtime can reference arc4random_buf (e.g. for the system
// RNG). The NDS has no entropy source and newlib's getentropy fallback is not
// wired up here, so provide a small xorshift PRNG. NOT cryptographically
// secure -- nothing in this port relies on randomness at all.
void arc4random_buf(void *buf, size_t n) {
static uint32_t s = 0x2545F491u;
uint8_t *p = (uint8_t *)buf;
for (size_t i = 0; i < n; i++) {
s ^= s << 13;
s ^= s >> 17;
s ^= s << 5;
p[i] = (uint8_t)s;
}
}

// The Embedded stdlib's Double(String)/Float(String) parsing calls these
// libc-locale-pinned wrappers; newlib's plain strtod/strtof are already
// locale-free here.
double _swift_stdlib_strtod_clocale(const char *nptr, char **outEnd) {
return strtod(nptr, outEnd);
}

float _swift_stdlib_strtof_clocale(const char *nptr, char **outEnd) {
return strtof(nptr, outEnd);
}

// Swift's allocator calls posix_memalign; newlib only ships memalign.
int posix_memalign(void **memptr, size_t alignment, size_t size) {
void *p = memalign(alignment, size);
if (!p) return ENOMEM;
*memptr = p;
return 0;
}

// ARMv5TE has no atomic instructions, so LLVM emits __atomic_* libcalls.
// The Swift code runs only on the single ARM9 core, so a short interrupt
// lock makes each operation atomic with respect to IRQ handlers.

uint16_t __atomic_load_2(const volatile void *ptr, int memorder) {
(void)memorder;
ArmIrqState st = armIrqLockByPsr();
uint16_t v = *(const volatile uint16_t *)ptr;
armIrqUnlockByPsr(st);
return v;
}

void __atomic_store_2(volatile void *ptr, uint16_t val, int memorder) {
(void)memorder;
ArmIrqState st = armIrqLockByPsr();
*(volatile uint16_t *)ptr = val;
armIrqUnlockByPsr(st);
}

uint32_t __atomic_load_4(const volatile void *ptr, int memorder) {
(void)memorder;
ArmIrqState st = armIrqLockByPsr();
uint32_t v = *(const volatile uint32_t *)ptr;
armIrqUnlockByPsr(st);
return v;
}

void __atomic_store_4(volatile void *ptr, uint32_t val, int memorder) {
(void)memorder;
ArmIrqState st = armIrqLockByPsr();
*(volatile uint32_t *)ptr = val;
armIrqUnlockByPsr(st);
}

uint32_t __atomic_fetch_add_4(volatile void *ptr, uint32_t val, int memorder) {
(void)memorder;
ArmIrqState st = armIrqLockByPsr();
volatile uint32_t *p = ptr;
uint32_t old = *p;
*p = old + val;
armIrqUnlockByPsr(st);
return old;
}

uint32_t __atomic_fetch_sub_4(volatile void *ptr, uint32_t val, int memorder) {
(void)memorder;
ArmIrqState st = armIrqLockByPsr();
volatile uint32_t *p = ptr;
uint32_t old = *p;
*p = old - val;
armIrqUnlockByPsr(st);
return old;
}

_Bool __atomic_compare_exchange_4(volatile void *ptr, void *expected,
uint32_t desired, _Bool weak,
int success, int failure) {
(void)weak; (void)success; (void)failure;
ArmIrqState st = armIrqLockByPsr();
volatile uint32_t *p = ptr;
uint32_t *exp = expected;
_Bool ok = (*p == *exp);
if (ok) {
*p = desired;
} else {
*exp = *p;
}
armIrqUnlockByPsr(st);
return ok;
}

int nds_errno(void) {
return errno;
}
21 changes: 21 additions & 0 deletions NDS/common/shim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//---------------------------------------------------------------------------------
// shim.h -- C support for the Fueling NDS port.
//
// The Swift-visible surface is tiny (a fixed-arity print wrapper around
// libnds' variadic iprintf, which Embedded Swift can import but not call).
// The bulk of shim.c is runtime support referenced only by the linker:
// posix_memalign, arc4random_buf, the _swift_stdlib_strto* wrappers, and
// the __atomic_* outline helpers ARMv5TE needs (no atomic instructions on
// the ARM946E-S; see junkbot-swift's ports/NDS and
// MillerTechnologyPeru/swift-embedded-nds).
//---------------------------------------------------------------------------------
#ifndef FUELING_NDS_SHIM_H
#define FUELING_NDS_SHIM_H

// Print an already-formatted string (no varargs).
void nds_puts(const char *s);

#endif // FUELING_NDS_SHIM_H

// Current errno value (errno is a macro over __errno(), which doesn't import).
int nds_errno(void);
Loading
Loading