initial commit - doom port
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
out/
|
||||||
|
obj/
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.a
|
||||||
|
*.elf
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "games-fps/doom/upstream"]
|
||||||
|
path = games-fps/doom/upstream
|
||||||
|
url = https://github.com/ozkl/doomgeneric.git
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Makefile - MontaukOS ports tree
|
||||||
|
#
|
||||||
|
# make Build every port into out/apps/<name>/
|
||||||
|
# make doom Build a single port (shorthand for its category path)
|
||||||
|
# make games-fps/doom Same, by full path
|
||||||
|
# make install Copy built bundles into the MontaukOS ramdisk tree
|
||||||
|
# make clean Remove build artifacts
|
||||||
|
#
|
||||||
|
# Ports build against a MontaukOS checkout; override its location with
|
||||||
|
# MONTAUKOS=/path/to/MontaukOS (default ~/dev/MontaukOS).
|
||||||
|
|
||||||
|
MAKEFLAGS += -rR
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
MTKPORTS_ROOT := $(CURDIR)
|
||||||
|
export MTKPORTS_ROOT
|
||||||
|
|
||||||
|
MONTAUKOS ?= $(HOME)/dev/MontaukOS
|
||||||
|
export MONTAUKOS
|
||||||
|
|
||||||
|
OUTDIR := $(MTKPORTS_ROOT)/out
|
||||||
|
export OUTDIR
|
||||||
|
|
||||||
|
# Ports to build, as <category>/<port>, in dependency order: library ports
|
||||||
|
# come before the applications that link them. Categories follow Gentoo
|
||||||
|
# naming (dev-libs, media-libs, net-libs, www-client, games-fps, ...).
|
||||||
|
PORTS := \
|
||||||
|
games-fps/doom
|
||||||
|
|
||||||
|
# Bundle directories are keyed by port name, not category path.
|
||||||
|
PORT_NAMES := $(notdir $(PORTS))
|
||||||
|
|
||||||
|
.PHONY: all clean install $(PORTS) $(PORT_NAMES)
|
||||||
|
|
||||||
|
all: $(PORTS)
|
||||||
|
|
||||||
|
$(PORTS):
|
||||||
|
$(MAKE) -C $@
|
||||||
|
|
||||||
|
# Shorthand target per port name, so `make doom` works as well as
|
||||||
|
# `make games-fps/doom`.
|
||||||
|
define PORT_ALIAS
|
||||||
|
$(notdir $(1)): $(1)
|
||||||
|
endef
|
||||||
|
$(foreach p,$(PORTS),$(eval $(call PORT_ALIAS,$(p))))
|
||||||
|
|
||||||
|
# Drop finished bundles into the OS ramdisk staging tree. install_apps.sh
|
||||||
|
# builds in-tree app bundles at the same path; ports simply add to it.
|
||||||
|
install: all
|
||||||
|
@mkdir -p $(MONTAUKOS)/programs/bin/apps
|
||||||
|
@for p in $(PORTS); do \
|
||||||
|
n=$$(basename $$p); \
|
||||||
|
if [ -d "$(OUTDIR)/apps/$$n" ]; then \
|
||||||
|
cp -r "$(OUTDIR)/apps/$$n" "$(MONTAUKOS)/programs/bin/apps/"; \
|
||||||
|
echo "installed port: $$p"; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
@echo "ports: installed into $(MONTAUKOS)/programs/bin/apps/"
|
||||||
|
@echo "ports: run 'make ramdisk' (or 'make') in $(MONTAUKOS) to rebuild the image"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@for p in $(PORTS); do $(MAKE) -C $$p clean; done
|
||||||
|
rm -rf $(OUTDIR)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# mtkports
|
||||||
|
|
||||||
|
MontaukOS software ports
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone --recurse-submodules <url> ~/dev/mtkports
|
||||||
|
cd ~/dev/mtkports
|
||||||
|
make # build every port into out/apps/<name>/
|
||||||
|
make doom # build one port
|
||||||
|
make install # copy bundles into MontaukOS/programs/bin/apps/
|
||||||
|
```
|
||||||
|
|
||||||
|
Then from the MontaukOS tree:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make ports # builds + installs ports (equivalent to the above)
|
||||||
|
make ramdisk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
| Port | Upstream | Notes |
|
||||||
|
|------|----------|-------|
|
||||||
|
| `doom` | [ozkl/doomgeneric](https://github.com/ozkl/doomgeneric) | `doom1.wad` is proprietary and deliberately not bundled. |
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# DOOM (doomgeneric) for MontaukOS
|
||||||
|
#
|
||||||
|
# Upstream: https://github.com/ozkl/doomgeneric (submodule at upstream/,
|
||||||
|
# pinned; no local patches). MontaukOS support lives entirely in src/:
|
||||||
|
# doomgeneric_montauk.c (video/input), i_sound_montauk.c (audio),
|
||||||
|
# doom_wad_check.cpp (missing-WAD dialog).
|
||||||
|
#
|
||||||
|
# doom1.wad is proprietary id Software content and is deliberately NOT
|
||||||
|
# bundled; the engine (GPLv2) ships without it.
|
||||||
|
|
||||||
|
PORT_NAME := doom
|
||||||
|
PORT_ICON := doom.svg
|
||||||
|
|
||||||
|
include ../../mk/port.mk
|
||||||
|
|
||||||
|
# ==== Paths ===========================================================
|
||||||
|
|
||||||
|
DOOM_SRC := upstream/doomgeneric
|
||||||
|
LOCAL_SRC := src
|
||||||
|
|
||||||
|
# ==== Flags ===========================================================
|
||||||
|
|
||||||
|
CFLAGS := \
|
||||||
|
-std=gnu11 \
|
||||||
|
$(PORT_CFLAGS_COMMON) \
|
||||||
|
-Wall \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-Wno-unused-variable \
|
||||||
|
-Wno-missing-field-initializers \
|
||||||
|
-Wno-sign-compare \
|
||||||
|
-Wno-pointer-sign \
|
||||||
|
-DNORMALUNIX \
|
||||||
|
-DLINUX \
|
||||||
|
-DFEATURE_SOUND \
|
||||||
|
-I $(LOCAL_SRC) \
|
||||||
|
-I $(DOOM_SRC) \
|
||||||
|
-isystem $(shell $(CC) -print-file-name=include)
|
||||||
|
|
||||||
|
CXXFLAGS := \
|
||||||
|
-std=gnu++20 \
|
||||||
|
$(PORT_CFLAGS_COMMON) \
|
||||||
|
-Wall \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-fno-rtti \
|
||||||
|
-fno-exceptions \
|
||||||
|
-I $(LOCAL_SRC)
|
||||||
|
|
||||||
|
LDFLAGS := $(PORT_LDFLAGS_COMMON)
|
||||||
|
|
||||||
|
LIBS := $(LIBLOADER_A) $(LIBC_A)
|
||||||
|
|
||||||
|
# ==== Sources =========================================================
|
||||||
|
# Upstream engine sources, excluding platform ports and sound backends.
|
||||||
|
|
||||||
|
DOOM_SRCS := \
|
||||||
|
am_map.c d_event.c d_items.c d_iwad.c d_loop.c d_main.c d_mode.c \
|
||||||
|
d_net.c doomdef.c doomgeneric.c doomstat.c dstrings.c dummy.c \
|
||||||
|
f_finale.c f_wipe.c g_game.c gusconf.c hu_lib.c hu_stuff.c i_cdmus.c \
|
||||||
|
i_endoom.c i_input.c i_joystick.c i_scale.c i_sound.c i_system.c \
|
||||||
|
i_timer.c i_video.c info.c m_argv.c m_bbox.c m_cheat.c m_config.c \
|
||||||
|
m_controls.c m_fixed.c m_menu.c m_misc.c m_random.c memio.c mus2mid.c \
|
||||||
|
p_ceilng.c p_doors.c p_enemy.c p_floor.c p_inter.c p_lights.c p_map.c \
|
||||||
|
p_maputl.c p_mobj.c p_plats.c p_pspr.c p_saveg.c p_setup.c p_sight.c \
|
||||||
|
p_spec.c p_switch.c p_telept.c p_tick.c p_user.c r_bsp.c r_data.c \
|
||||||
|
r_draw.c r_main.c r_plane.c r_segs.c r_sky.c r_things.c s_sound.c \
|
||||||
|
sha1.c sounds.c st_lib.c st_stuff.c statdump.c tables.c v_video.c \
|
||||||
|
w_checksum.c w_file.c w_file_stdc.c w_main.c w_wad.c wi_stuff.c \
|
||||||
|
z_zone.c
|
||||||
|
|
||||||
|
LOCAL_SRCS := doomgeneric_montauk.c i_sound_montauk.c
|
||||||
|
LOCAL_CXX_SRCS := doom_wad_check.cpp
|
||||||
|
|
||||||
|
DOOM_OBJS := $(addprefix $(OBJDIR)/upstream/,$(DOOM_SRCS:.c=.o))
|
||||||
|
LOCAL_OBJS := $(addprefix $(OBJDIR)/local/,$(LOCAL_SRCS:.c=.o))
|
||||||
|
LOCAL_CXX_OBJS := $(addprefix $(OBJDIR)/local/,$(LOCAL_CXX_SRCS:.cpp=.o))
|
||||||
|
ALL_OBJS := $(DOOM_OBJS) $(LOCAL_OBJS) $(LOCAL_CXX_OBJS)
|
||||||
|
|
||||||
|
# ==== Rules ===========================================================
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
all: bundle
|
||||||
|
|
||||||
|
$(TARGET): $(ALL_OBJS) $(SDK_LINK_LD) Makefile $(LIBS) | $(BUNDLE_DIR)
|
||||||
|
$(LD) $(CFLAGS) $(LDFLAGS) $(ALL_OBJS) $(LIBS) -o $@
|
||||||
|
@echo "Built: $@ (SDK apiVersion $(SDK_API_VERSION))"
|
||||||
|
|
||||||
|
$(OBJDIR)/upstream/%.o: $(DOOM_SRC)/%.c Makefile
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/local/%.o: $(LOCAL_SRC)/%.c Makefile
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/local/%.o: $(LOCAL_SRC)/%.cpp Makefile
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
|
clean: clean-bundle
|
||||||
|
|
||||||
|
-include $(ALL_OBJS:.o=.d)
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,8 @@
|
|||||||
|
[app]
|
||||||
|
name = "DOOM"
|
||||||
|
binary = "doom.elf"
|
||||||
|
icon = "doom.svg"
|
||||||
|
|
||||||
|
[menu]
|
||||||
|
category = "Games"
|
||||||
|
visible = true
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/* Dummy SDL_mixer.h - not needed for MontaukOS sound backend */
|
||||||
|
#ifndef SDL_MIXER_H
|
||||||
|
#define SDL_MIXER_H
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* doom_wad_check.cpp
|
||||||
|
* Warns the user and exits cleanly if no DOOM WAD is present
|
||||||
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gui/dialogs.hpp>
|
||||||
|
#include <montauk/syscall.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern "C" [[noreturn]] void doom_warn_missing_wad(const char* path) {
|
||||||
|
char message[400];
|
||||||
|
snprintf(message, sizeof(message),
|
||||||
|
"No DOOM WAD file was found at %s.\n\n"
|
||||||
|
"MontaukOS cannot ship proprietary DOOM WADs (doom1.wad, doom.wad, "
|
||||||
|
"freedoom1.wad, etc.) on its ISO images due to copyright restrictions.\n\n"
|
||||||
|
"Copy a legally obtained WAD file to that path and relaunch DOOM.",
|
||||||
|
path);
|
||||||
|
|
||||||
|
gui::dialogs::message_box("DOOM", message, gui::dialogs::MESSAGE_BOX_OK);
|
||||||
|
montauk::exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* doomgeneric_montauk.c
|
||||||
|
* DOOM platform implementation for MontaukOS (standalone window server client)
|
||||||
|
* Copyright (c) 2025 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "doomgeneric.h"
|
||||||
|
#include "doomkeys.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* ---- Raw syscall interface (C versions) ---- */
|
||||||
|
|
||||||
|
static inline long _zos_syscall0(long nr) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile("syscall" : "=a"(ret) : "a"(nr)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long _zos_syscall1(long nr, long a1) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long _zos_syscall2(long nr, long a1, long a2) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"mov %[a2], %%rsi\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"mov %[a2], %%rsi\n\t"
|
||||||
|
"mov %[a3], %%rdx\n\t"
|
||||||
|
"mov %[a4], %%r10\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3), [a4] "r"(a4)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syscall numbers (must match kernel/src/Api/Syscall.hpp) */
|
||||||
|
#define SYS_EXIT 0
|
||||||
|
#define SYS_SLEEP_MS 2
|
||||||
|
#define SYS_PRINT 4
|
||||||
|
#define SYS_GETMILLISECONDS 14
|
||||||
|
#define SYS_WINCREATE 54
|
||||||
|
#define SYS_WINDESTROY 55
|
||||||
|
#define SYS_WINPRESENT 56
|
||||||
|
#define SYS_WINPOLL 57
|
||||||
|
|
||||||
|
/* Window server structs (must match montauk::abi::WinCreateResult and montauk::abi::WinEvent) */
|
||||||
|
struct WinCreateResult {
|
||||||
|
int id; /* -1 on failure */
|
||||||
|
unsigned _pad;
|
||||||
|
unsigned long pixelVa; /* VA of pixel buffer in caller's address space */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WinEvent {
|
||||||
|
unsigned char type; /* 0=key, 1=mouse, 2=resize, 3=close */
|
||||||
|
unsigned char _pad[3];
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
unsigned char scancode;
|
||||||
|
char ascii;
|
||||||
|
unsigned char pressed;
|
||||||
|
unsigned char shift;
|
||||||
|
unsigned char ctrl;
|
||||||
|
unsigned char alt;
|
||||||
|
} key;
|
||||||
|
struct { int x, y, scroll; unsigned char buttons, prev_buttons; } mouse;
|
||||||
|
struct { int w, h; } resize;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- Window state ---- */
|
||||||
|
|
||||||
|
static int g_winId = -1;
|
||||||
|
static uint32_t* g_pixBuf = 0;
|
||||||
|
|
||||||
|
/* ---- Circular key queue ---- */
|
||||||
|
|
||||||
|
#define KEY_QUEUE_SIZE 64
|
||||||
|
|
||||||
|
struct KeyQueueEntry {
|
||||||
|
int pressed;
|
||||||
|
unsigned char doomkey;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct KeyQueueEntry g_keyQueue[KEY_QUEUE_SIZE];
|
||||||
|
static int g_keyQueueRead = 0;
|
||||||
|
static int g_keyQueueWrite = 0;
|
||||||
|
|
||||||
|
static void key_queue_push(int pressed, unsigned char doomkey) {
|
||||||
|
int next = (g_keyQueueWrite + 1) % KEY_QUEUE_SIZE;
|
||||||
|
if (next == g_keyQueueRead) return; /* full, drop */
|
||||||
|
g_keyQueue[g_keyQueueWrite].pressed = pressed;
|
||||||
|
g_keyQueue[g_keyQueueWrite].doomkey = doomkey;
|
||||||
|
g_keyQueueWrite = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int key_queue_pop(int* pressed, unsigned char* doomkey) {
|
||||||
|
if (g_keyQueueRead == g_keyQueueWrite) return 0;
|
||||||
|
*pressed = g_keyQueue[g_keyQueueRead].pressed;
|
||||||
|
*doomkey = g_keyQueue[g_keyQueueRead].doomkey;
|
||||||
|
g_keyQueueRead = (g_keyQueueRead + 1) % KEY_QUEUE_SIZE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- PS/2 scancode to ASCII table (set 1, unshifted) ---- */
|
||||||
|
|
||||||
|
static const char scancode_to_ascii[128] = {
|
||||||
|
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=','\b',
|
||||||
|
'\t','q','w','e','r','t','y','u','i','o','p','[',']','\n',
|
||||||
|
0, 'a','s','d','f','g','h','j','k','l',';','\'','`',
|
||||||
|
0, '\\','z','x','c','v','b','n','m',',','.','/', 0,
|
||||||
|
'*', 0, ' '
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- PS/2 scancode to DOOM key mapping ---- */
|
||||||
|
|
||||||
|
static unsigned char scancode_to_doomkey(unsigned char scancode, char ascii) {
|
||||||
|
switch (scancode) {
|
||||||
|
case 0x48: return KEY_UPARROW;
|
||||||
|
case 0x50: return KEY_DOWNARROW;
|
||||||
|
case 0x4B: return KEY_LEFTARROW;
|
||||||
|
case 0x4D: return KEY_RIGHTARROW;
|
||||||
|
case 0x1C: return KEY_ENTER;
|
||||||
|
case 0x01: return KEY_ESCAPE;
|
||||||
|
case 0x39: return KEY_USE; /* Space = use */
|
||||||
|
case 0x1D: return KEY_FIRE; /* LCtrl = fire */
|
||||||
|
case 0x2A: return KEY_RSHIFT; /* LShift = run */
|
||||||
|
case 0x36: return KEY_RSHIFT; /* RShift = run */
|
||||||
|
case 0x38: return KEY_RALT; /* Alt = strafe */
|
||||||
|
case 0x0E: return KEY_BACKSPACE;
|
||||||
|
case 0x0F: return KEY_TAB;
|
||||||
|
/* F1-F10 */
|
||||||
|
case 0x3B: return KEY_F1;
|
||||||
|
case 0x3C: return KEY_F2;
|
||||||
|
case 0x3D: return KEY_F3;
|
||||||
|
case 0x3E: return KEY_F4;
|
||||||
|
case 0x3F: return KEY_F5;
|
||||||
|
case 0x40: return KEY_F6;
|
||||||
|
case 0x41: return KEY_F7;
|
||||||
|
case 0x42: return KEY_F8;
|
||||||
|
case 0x43: return KEY_F9;
|
||||||
|
case 0x44: return KEY_F10;
|
||||||
|
case 0x57: return KEY_F11;
|
||||||
|
case 0x58: return KEY_F12;
|
||||||
|
/* Equals and minus for screen size */
|
||||||
|
case 0x0D: return KEY_EQUALS;
|
||||||
|
case 0x0C: return KEY_MINUS;
|
||||||
|
default:
|
||||||
|
/* Pass through printable ASCII as lowercase */
|
||||||
|
if (ascii >= 'a' && ascii <= 'z') return (unsigned char)ascii;
|
||||||
|
if (ascii >= '0' && ascii <= '9') return (unsigned char)ascii;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Poll window events and enqueue key events ---- */
|
||||||
|
|
||||||
|
static void poll_keyboard(void) {
|
||||||
|
struct WinEvent evt;
|
||||||
|
while (_zos_syscall2(SYS_WINPOLL, (long)g_winId, (long)&evt) > 0) {
|
||||||
|
if (evt.type == 0) {
|
||||||
|
/* Key event */
|
||||||
|
unsigned char baseSc = evt.key.scancode & 0x7F;
|
||||||
|
|
||||||
|
char ascii = 0;
|
||||||
|
if (baseSc < 128)
|
||||||
|
ascii = scancode_to_ascii[baseSc];
|
||||||
|
|
||||||
|
unsigned char dk = scancode_to_doomkey(baseSc, ascii);
|
||||||
|
if (dk != 0) {
|
||||||
|
key_queue_push(evt.key.pressed ? 1 : 0, dk);
|
||||||
|
}
|
||||||
|
} else if (evt.type == 3) {
|
||||||
|
/* Close event — exit the process */
|
||||||
|
_zos_syscall1(SYS_EXIT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- DG platform functions ---- */
|
||||||
|
|
||||||
|
void DG_Init(void) {
|
||||||
|
struct WinCreateResult result;
|
||||||
|
_zos_syscall4(SYS_WINCREATE, (long)"DOOM", (long)DOOMGENERIC_RESX,
|
||||||
|
(long)DOOMGENERIC_RESY, (long)&result);
|
||||||
|
|
||||||
|
if (result.id < 0) {
|
||||||
|
_zos_syscall1(SYS_EXIT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_winId = result.id;
|
||||||
|
g_pixBuf = (uint32_t*)result.pixelVa;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DG_DrawFrame(void) {
|
||||||
|
/* Poll keyboard first */
|
||||||
|
poll_keyboard();
|
||||||
|
|
||||||
|
/* Copy DG_ScreenBuffer into the shared pixel buffer */
|
||||||
|
if (g_pixBuf == 0 || DG_ScreenBuffer == 0) return;
|
||||||
|
|
||||||
|
memcpy(g_pixBuf, DG_ScreenBuffer,
|
||||||
|
DOOMGENERIC_RESX * DOOMGENERIC_RESY * sizeof(uint32_t));
|
||||||
|
|
||||||
|
/* Mark window as dirty so the compositor picks it up */
|
||||||
|
_zos_syscall1(SYS_WINPRESENT, (long)g_winId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DG_SleepMs(uint32_t ms) {
|
||||||
|
_zos_syscall1(SYS_SLEEP_MS, (long)ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DG_GetTicksMs(void) {
|
||||||
|
return (uint32_t)_zos_syscall0(SYS_GETMILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DG_GetKey(int* pressed, unsigned char* doomKey) {
|
||||||
|
return key_queue_pop(pressed, doomKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DG_SetWindowTitle(const char* title) {
|
||||||
|
(void)title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Entry point ---- */
|
||||||
|
|
||||||
|
/* Shows a warning dialog explaining that MontaukOS cannot ship proprietary
|
||||||
|
* DOOM WADs and does not return (implemented in doom_wad_check.cpp, which
|
||||||
|
* links against the shared dialog library). */
|
||||||
|
extern void doom_warn_missing_wad(const char* path);
|
||||||
|
|
||||||
|
void _start(void) {
|
||||||
|
static const char* wadPath = "0:/apps/doom/doom1.wad";
|
||||||
|
|
||||||
|
FILE* wad = fopen(wadPath, "rb");
|
||||||
|
if (!wad) {
|
||||||
|
doom_warn_missing_wad(wadPath);
|
||||||
|
/* unreachable: doom_warn_missing_wad() exits the process */
|
||||||
|
}
|
||||||
|
fclose(wad);
|
||||||
|
|
||||||
|
char *argv[] = { "doom", "-iwad", (char*)wadPath, 0 };
|
||||||
|
doomgeneric_Create(3, argv);
|
||||||
|
for (;;) {
|
||||||
|
doomgeneric_Tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
/*
|
||||||
|
* i_sound_montauk.c
|
||||||
|
* DOOM sound effects and music modules for MontaukOS
|
||||||
|
* Implements software mixing of 16 SFX channels into the HDA audio device.
|
||||||
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "deh_str.h"
|
||||||
|
#include "i_sound.h"
|
||||||
|
#include "i_system.h"
|
||||||
|
#include "i_swap.h"
|
||||||
|
#include "m_argv.h"
|
||||||
|
#include "m_misc.h"
|
||||||
|
#include "w_wad.h"
|
||||||
|
#include "z_zone.h"
|
||||||
|
#include "doomtype.h"
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Raw syscall interface for audio
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static inline long _snd_syscall1(long nr, long a1) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long _snd_syscall3(long nr, long a1, long a2, long a3) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"mov %[a2], %%rsi\n\t"
|
||||||
|
"mov %[a3], %%rdx\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYS_AUDIOOPEN 80
|
||||||
|
#define SYS_AUDIOCLOSE 81
|
||||||
|
#define SYS_AUDIOWRITE 82
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Audio mixing engine
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
/* Required by i_sound.c config binding */
|
||||||
|
int use_libsamplerate = 0;
|
||||||
|
float libsamplerate_scale = 0.65f;
|
||||||
|
|
||||||
|
#define NUM_CHANNELS 16
|
||||||
|
#define MIX_RATE 44100
|
||||||
|
#define MIX_CHANNELS 2
|
||||||
|
#define MIX_BITS 16
|
||||||
|
|
||||||
|
/* Samples per mix frame (~35 fps, slightly oversized for timing jitter) */
|
||||||
|
#define MIX_SAMPLES 1260
|
||||||
|
#define MIX_BUF_BYTES (MIX_SAMPLES * MIX_CHANNELS * (MIX_BITS / 8))
|
||||||
|
|
||||||
|
/* Channel state */
|
||||||
|
struct snd_channel {
|
||||||
|
const uint8_t *data; /* 8-bit unsigned PCM samples */
|
||||||
|
uint32_t length; /* number of samples */
|
||||||
|
uint32_t pos; /* playback position (16.16 fixed-point) */
|
||||||
|
uint32_t step; /* step per output sample (16.16 fixed-point) */
|
||||||
|
int vol_left; /* left volume (0-127) */
|
||||||
|
int vol_right; /* right volume (0-127) */
|
||||||
|
sfxinfo_t *sfxinfo; /* NULL = inactive */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_channel channels[NUM_CHANNELS];
|
||||||
|
static int audio_handle = -1;
|
||||||
|
static boolean sound_initialized = false;
|
||||||
|
static boolean use_sfx_prefix;
|
||||||
|
|
||||||
|
/* Static mix buffer (stereo interleaved int16_t) */
|
||||||
|
static int16_t mix_buf[MIX_SAMPLES * MIX_CHANNELS];
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Sound effect loading (DMX format from WAD lumps)
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len) {
|
||||||
|
if (sfx->link != NULL)
|
||||||
|
sfx = sfx->link;
|
||||||
|
|
||||||
|
if (use_sfx_prefix)
|
||||||
|
M_snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name));
|
||||||
|
else
|
||||||
|
M_StringCopy(buf, DEH_String(sfx->name), buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a DMX sound lump. Returns the raw 8-bit PCM data and fills in
|
||||||
|
samplerate and length. Returns NULL on failure. */
|
||||||
|
static const uint8_t *ParseDmxSound(int lumpnum, int *out_rate, uint32_t *out_len) {
|
||||||
|
unsigned int lumplen = W_LumpLength(lumpnum);
|
||||||
|
const uint8_t *data = W_CacheLumpNum(lumpnum, PU_STATIC);
|
||||||
|
|
||||||
|
if (lumplen < 8 || data[0] != 0x03 || data[1] != 0x00)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int samplerate = (data[3] << 8) | data[2];
|
||||||
|
uint32_t length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
|
||||||
|
|
||||||
|
if (length > lumplen - 8 || length <= 48)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* DMX skips first 16 and last 16 bytes of sample data */
|
||||||
|
*out_rate = samplerate;
|
||||||
|
*out_len = length - 32;
|
||||||
|
return data + 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Volume/separation helpers
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static void SetChannelVolSep(int ch, int vol, int sep) {
|
||||||
|
/* vol: 0-127, sep: 0-254 (0=full left, 127=center, 254=full right) */
|
||||||
|
channels[ch].vol_left = ((254 - sep) * vol) / 127;
|
||||||
|
channels[ch].vol_right = (sep * vol) / 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Software mixer - mix all active channels into mix_buf
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static void MixChannels(int num_samples) {
|
||||||
|
/* Clear mix buffer */
|
||||||
|
memset(mix_buf, 0, (size_t)(num_samples * MIX_CHANNELS) * sizeof(int16_t));
|
||||||
|
|
||||||
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
|
struct snd_channel *c = &channels[ch];
|
||||||
|
if (c->sfxinfo == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int16_t *out = mix_buf;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_samples; i++) {
|
||||||
|
uint32_t ipos = c->pos >> 16;
|
||||||
|
|
||||||
|
if (ipos >= c->length) {
|
||||||
|
/* Sound finished */
|
||||||
|
c->sfxinfo = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert 8-bit unsigned (0-255, 128=silence) to signed (-128..127)
|
||||||
|
then scale to 16-bit range */
|
||||||
|
int sample = ((int)c->data[ipos] - 128) << 8;
|
||||||
|
|
||||||
|
/* Apply volume and accumulate (additive mixing) */
|
||||||
|
int left = out[i * 2 + 0] + ((sample * c->vol_left) >> 7);
|
||||||
|
int right = out[i * 2 + 1] + ((sample * c->vol_right) >> 7);
|
||||||
|
|
||||||
|
/* Clamp to int16_t range */
|
||||||
|
if (left > 32767) left = 32767;
|
||||||
|
else if (left < -32768) left = -32768;
|
||||||
|
if (right > 32767) right = 32767;
|
||||||
|
else if (right < -32768) right = -32768;
|
||||||
|
|
||||||
|
out[i * 2 + 0] = (int16_t)left;
|
||||||
|
out[i * 2 + 1] = (int16_t)right;
|
||||||
|
|
||||||
|
c->pos += c->step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
sound_module_t implementation
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static boolean I_Montauk_InitSound(boolean _use_sfx_prefix) {
|
||||||
|
use_sfx_prefix = _use_sfx_prefix;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
memset(&channels[i], 0, sizeof(channels[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_handle = (int)_snd_syscall3(SYS_AUDIOOPEN,
|
||||||
|
(long)MIX_RATE, (long)MIX_CHANNELS, (long)MIX_BITS);
|
||||||
|
|
||||||
|
if (audio_handle < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound_initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void I_Montauk_ShutdownSound(void) {
|
||||||
|
if (!sound_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_CHANNELS; i++)
|
||||||
|
channels[i].sfxinfo = NULL;
|
||||||
|
|
||||||
|
if (audio_handle >= 0) {
|
||||||
|
_snd_syscall1(SYS_AUDIOCLOSE, (long)audio_handle);
|
||||||
|
audio_handle = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int I_Montauk_GetSfxLumpNum(sfxinfo_t *sfx) {
|
||||||
|
char namebuf[9];
|
||||||
|
GetSfxLumpName(sfx, namebuf, sizeof(namebuf));
|
||||||
|
return W_GetNumForName(namebuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void I_Montauk_UpdateSound(void) {
|
||||||
|
if (!sound_initialized || audio_handle < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Mix one frame of audio and submit to the device */
|
||||||
|
MixChannels(MIX_SAMPLES);
|
||||||
|
|
||||||
|
const uint8_t *ptr = (const uint8_t *)mix_buf;
|
||||||
|
int remaining = MIX_BUF_BYTES;
|
||||||
|
|
||||||
|
/* Write as much as the device accepts (non-blocking) */
|
||||||
|
while (remaining > 0) {
|
||||||
|
int written = (int)_snd_syscall3(SYS_AUDIOWRITE,
|
||||||
|
(long)audio_handle, (long)ptr, (long)remaining);
|
||||||
|
if (written <= 0)
|
||||||
|
break;
|
||||||
|
ptr += written;
|
||||||
|
remaining -= written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void I_Montauk_UpdateSoundParams(int channel, int vol, int sep) {
|
||||||
|
if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)
|
||||||
|
return;
|
||||||
|
if (channels[channel].sfxinfo == NULL)
|
||||||
|
return;
|
||||||
|
SetChannelVolSep(channel, vol, sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int I_Montauk_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) {
|
||||||
|
if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Stop any sound already on this channel */
|
||||||
|
channels[channel].sfxinfo = NULL;
|
||||||
|
|
||||||
|
/* Load and parse the sound lump */
|
||||||
|
if (sfxinfo->lumpnum == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int samplerate;
|
||||||
|
uint32_t length;
|
||||||
|
const uint8_t *data = ParseDmxSound(sfxinfo->lumpnum, &samplerate, &length);
|
||||||
|
if (data == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Set up the channel */
|
||||||
|
channels[channel].data = data;
|
||||||
|
channels[channel].length = length;
|
||||||
|
channels[channel].pos = 0;
|
||||||
|
/* Resampling step: source_rate / MIX_RATE in 16.16 fixed-point */
|
||||||
|
channels[channel].step = ((uint32_t)samplerate << 16) / MIX_RATE;
|
||||||
|
|
||||||
|
SetChannelVolSep(channel, vol, sep);
|
||||||
|
channels[channel].sfxinfo = sfxinfo;
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void I_Montauk_StopSound(int channel) {
|
||||||
|
if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)
|
||||||
|
return;
|
||||||
|
channels[channel].sfxinfo = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean I_Montauk_SoundIsPlaying(int channel) {
|
||||||
|
if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)
|
||||||
|
return false;
|
||||||
|
return channels[channel].sfxinfo != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void I_Montauk_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) {
|
||||||
|
/* No-op: we load on demand */
|
||||||
|
(void)sounds;
|
||||||
|
(void)num_sounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static snddevice_t sound_montauk_devices[] = {
|
||||||
|
SNDDEVICE_SB,
|
||||||
|
SNDDEVICE_PAS,
|
||||||
|
SNDDEVICE_GUS,
|
||||||
|
SNDDEVICE_WAVEBLASTER,
|
||||||
|
SNDDEVICE_SOUNDCANVAS,
|
||||||
|
SNDDEVICE_AWE32,
|
||||||
|
};
|
||||||
|
|
||||||
|
sound_module_t DG_sound_module = {
|
||||||
|
sound_montauk_devices,
|
||||||
|
arrlen(sound_montauk_devices),
|
||||||
|
I_Montauk_InitSound,
|
||||||
|
I_Montauk_ShutdownSound,
|
||||||
|
I_Montauk_GetSfxLumpNum,
|
||||||
|
I_Montauk_UpdateSound,
|
||||||
|
I_Montauk_UpdateSoundParams,
|
||||||
|
I_Montauk_StartSound,
|
||||||
|
I_Montauk_StopSound,
|
||||||
|
I_Montauk_SoundIsPlaying,
|
||||||
|
I_Montauk_PrecacheSounds,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
music_module_t implementation (stub - no MIDI synthesis)
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static boolean I_Montauk_InitMusic(void) { return true; }
|
||||||
|
static void I_Montauk_ShutdownMusic(void) {}
|
||||||
|
static void I_Montauk_SetMusicVolume(int vol) { (void)vol; }
|
||||||
|
static void I_Montauk_PauseMusic(void) {}
|
||||||
|
static void I_Montauk_ResumeMusic(void) {}
|
||||||
|
static void *I_Montauk_RegisterSong(void *data, int len) { (void)data; (void)len; return (void *)1; }
|
||||||
|
static void I_Montauk_UnRegisterSong(void *h) { (void)h; }
|
||||||
|
static void I_Montauk_PlaySong(void *h, boolean loop) { (void)h; (void)loop; }
|
||||||
|
static void I_Montauk_StopSong(void) {}
|
||||||
|
static boolean I_Montauk_MusicIsPlaying(void) { return false; }
|
||||||
|
static void I_Montauk_PollMusic(void) {}
|
||||||
|
|
||||||
|
static snddevice_t music_montauk_devices[] = {
|
||||||
|
SNDDEVICE_SB,
|
||||||
|
SNDDEVICE_ADLIB,
|
||||||
|
SNDDEVICE_GUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
music_module_t DG_music_module = {
|
||||||
|
music_montauk_devices,
|
||||||
|
arrlen(music_montauk_devices),
|
||||||
|
I_Montauk_InitMusic,
|
||||||
|
I_Montauk_ShutdownMusic,
|
||||||
|
I_Montauk_SetMusicVolume,
|
||||||
|
I_Montauk_PauseMusic,
|
||||||
|
I_Montauk_ResumeMusic,
|
||||||
|
I_Montauk_RegisterSong,
|
||||||
|
I_Montauk_UnRegisterSong,
|
||||||
|
I_Montauk_PlaySong,
|
||||||
|
I_Montauk_StopSong,
|
||||||
|
I_Montauk_MusicIsPlaying,
|
||||||
|
I_Montauk_PollMusic,
|
||||||
|
};
|
||||||
Submodule
+1
Submodule games-fps/doom/upstream added at fc60163949
+131
@@ -0,0 +1,131 @@
|
|||||||
|
# port.mk - Shared rules for MontaukOS ports.
|
||||||
|
#
|
||||||
|
# A port builds against a MontaukOS checkout (the SDK) and produces an app
|
||||||
|
# bundle in $(OUTDIR)/apps/<name>/ containing:
|
||||||
|
#
|
||||||
|
# <name>.elf the binary
|
||||||
|
# manifest.toml launcher metadata (name, binary, icon, category)
|
||||||
|
# <icon>.svg the launcher icon
|
||||||
|
# .port marker so the OS release step can tell ports from
|
||||||
|
# in-tree apps (see MontaukOS scripts/install_apps.sh)
|
||||||
|
#
|
||||||
|
# The desktop readdirs 0:/apps/ at runtime and parses each manifest.toml, so a
|
||||||
|
# bundle dropped into the ramdisk needs no changes anywhere in the OS tree.
|
||||||
|
#
|
||||||
|
# A port Makefile sets PORT_NAME, PORT_ICON and its own sources/flags, then
|
||||||
|
# includes this file and uses $(BUNDLE_DIR) as its link target directory.
|
||||||
|
|
||||||
|
MAKEFLAGS += -rR
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
# This file defines rules before the including Makefile does, so pin the
|
||||||
|
# default goal rather than letting the first bundle rule claim it.
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
|
||||||
|
# ==== Repo root =======================================================
|
||||||
|
# Derived from this file's own location so a port builds the same whether it
|
||||||
|
# is invoked from the top-level Makefile or directly with `make -C <cat>/<port>`,
|
||||||
|
# and regardless of how deeply categories nest it.
|
||||||
|
|
||||||
|
PORT_MK_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
|
||||||
|
MTKPORTS_ROOT := $(abspath $(PORT_MK_DIR)/..)
|
||||||
|
|
||||||
|
# ==== SDK location ====================================================
|
||||||
|
# Ports build against a live MontaukOS checkout rather than a copied
|
||||||
|
# sysroot, so libc/header changes are picked up without a sync step.
|
||||||
|
|
||||||
|
MONTAUKOS ?= $(HOME)/dev/MontaukOS
|
||||||
|
|
||||||
|
ifeq ($(wildcard $(MONTAUKOS)/programs/link.ld),)
|
||||||
|
$(error MontaukOS tree not found at $(MONTAUKOS). Pass MONTAUKOS=/path/to/MontaukOS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ==== Toolchain =======================================================
|
||||||
|
|
||||||
|
TOOLCHAIN_PREFIX := $(MONTAUKOS)/toolchain/local/bin/x86_64-montauk-
|
||||||
|
|
||||||
|
ifeq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||||
|
$(error x86_64-montauk toolchain not found. Run $(MONTAUKOS)/toolchain/build-montauk-toolchain.sh first)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CC := $(TOOLCHAIN_PREFIX)gcc
|
||||||
|
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||||
|
AR := $(TOOLCHAIN_PREFIX)ar
|
||||||
|
LD := $(TOOLCHAIN_PREFIX)gcc
|
||||||
|
|
||||||
|
# ==== SDK paths =======================================================
|
||||||
|
|
||||||
|
SDK_INC := $(MONTAUKOS)/programs/include
|
||||||
|
SDK_LIBC_INC:= $(MONTAUKOS)/programs/include/libc
|
||||||
|
SDK_LIB := $(MONTAUKOS)/programs/lib
|
||||||
|
SDK_LINK_LD := $(MONTAUKOS)/programs/link.ld
|
||||||
|
|
||||||
|
LIBC_A := $(SDK_LIB)/libc/liblibc.a
|
||||||
|
LIBLOADER_A := $(SDK_LIB)/libloader/liblibloader.a
|
||||||
|
LIBTLS_A := $(SDK_LIB)/tls/libtls.a
|
||||||
|
LIBBEARSSL_A:= $(SDK_LIB)/bearssl/libbearssl.a
|
||||||
|
LIBJPEG_A := $(SDK_LIB)/libjpeg/libjpeg.a
|
||||||
|
|
||||||
|
# The kernel ABI version this port is being built against. Recorded in the
|
||||||
|
# bundle so a stale port can be spotted after a syscall/ABI bump rather than
|
||||||
|
# crashing mysteriously at runtime.
|
||||||
|
SDK_API_VERSION := $(shell sed -n 's/.*apiVersion = \([0-9]\+\);.*/\1/p' \
|
||||||
|
$(MONTAUKOS)/kernel/src/Api/Info.hpp 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
# ==== Output ==========================================================
|
||||||
|
|
||||||
|
OUTDIR ?= $(MTKPORTS_ROOT)/out
|
||||||
|
BUNDLE_DIR := $(OUTDIR)/apps/$(PORT_NAME)
|
||||||
|
OBJDIR ?= obj
|
||||||
|
TARGET := $(BUNDLE_DIR)/$(PORT_NAME).elf
|
||||||
|
|
||||||
|
# ==== Common flags ====================================================
|
||||||
|
# Mirrors the in-tree convention: freestanding, no stdlib, linked with the
|
||||||
|
# shared program link script.
|
||||||
|
|
||||||
|
PORT_CFLAGS_COMMON := \
|
||||||
|
-g -O2 -pipe \
|
||||||
|
-ffreestanding \
|
||||||
|
-fno-stack-protector \
|
||||||
|
-fno-stack-check \
|
||||||
|
-ffunction-sections \
|
||||||
|
-fdata-sections \
|
||||||
|
-msse \
|
||||||
|
-msse2 \
|
||||||
|
-isystem $(SDK_LIBC_INC) \
|
||||||
|
-I $(SDK_INC)
|
||||||
|
|
||||||
|
PORT_LDFLAGS_COMMON := \
|
||||||
|
-nostdlib \
|
||||||
|
-Wl,--gc-sections \
|
||||||
|
-T $(SDK_LINK_LD)
|
||||||
|
|
||||||
|
# ==== Bundle assembly =================================================
|
||||||
|
# Ports depend on $(BUNDLE_FILES) to get manifest + icon + marker staged
|
||||||
|
# alongside the ELF their link rule produces.
|
||||||
|
|
||||||
|
BUNDLE_FILES := $(BUNDLE_DIR)/manifest.toml $(BUNDLE_DIR)/.port
|
||||||
|
ifneq ($(PORT_ICON),)
|
||||||
|
BUNDLE_FILES += $(BUNDLE_DIR)/$(PORT_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(BUNDLE_DIR)/manifest.toml: manifest.toml | $(BUNDLE_DIR)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
$(BUNDLE_DIR)/$(PORT_ICON): $(PORT_ICON) | $(BUNDLE_DIR)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
# Marker file consumed by the MontaukOS release step to prune ports from a
|
||||||
|
# base ISO. Carries the ABI version so mismatches are diagnosable.
|
||||||
|
$(BUNDLE_DIR)/.port: Makefile | $(BUNDLE_DIR)
|
||||||
|
printf 'port = "%s"\napi_version = "%s"\n' '$(PORT_NAME)' '$(SDK_API_VERSION)' > $@
|
||||||
|
|
||||||
|
$(BUNDLE_DIR):
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
.PHONY: bundle
|
||||||
|
bundle: $(TARGET) $(BUNDLE_FILES)
|
||||||
|
|
||||||
|
.PHONY: clean-bundle
|
||||||
|
clean-bundle:
|
||||||
|
rm -rf $(OBJDIR) $(BUNDLE_DIR)
|
||||||
Reference in New Issue
Block a user