diff --git a/CREDITS b/CREDITS index f93beb13..9c82c8ed 100644 --- a/CREDITS +++ b/CREDITS @@ -60,6 +60,12 @@ The Authors - Thomas Pfaff * OpenBSD sndio driver +- Alfonso Ranieri, codesets.library Open Source Team + * Amiga StackSwap assembly routine + +- Alice Rowan + * Various features, sound driver enhancements and fixes + - Johan Samuelsson * Amiga port and fixes diff --git a/Changelog b/Changelog index b589cccc..b1d9fb3a 100644 --- a/Changelog +++ b/Changelog @@ -8,6 +8,10 @@ Stable versions sndio, WinMM, AIFF, file, WAV. - Use XMP_MAX_SRATE as the limit for -f instead of 48000 (requires libxmp 4.7.0+). + - Amiga: automatically expand stack via stack cookie (OS 4), + NewStackSwap (AROS), or StackSwap (Workbench 2.04+). The + Workbench and AROS behavior can be disabled by defining + XMP_NO_STACKSWAP. - Report pan value "---" for instruments/samples without a default panning value (sub->pan < 0). - Don't unmute muted IT channels unless explicitly unmuted by diff --git a/src/Makefile.am b/src/Makefile.am index de7a91e0..6cb88c62 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,8 +8,9 @@ bin_PROGRAMS = xmp xmp_SOURCES = \ commands.c common.h delay.c getopt_long.h getopt_long.c info.c \ - main.c options.c read_config.c sound.c sound.h sound_file.c sound_null.c \ - sound_wav.c sound_aiff.c terminal.c util.c xmp_version.h + main.c main_amiga.h options.c read_config.c sound.c sound.h \ + sound_aiff.c sound_file.c sound_null.c sound_wav.c terminal.c util.c \ + xmp_version.h xmp_LDADD = ${LIBXMP_LIBS} xmp_LDFLAGS = ${XMP_DARWIN_LDFLAGS} diff --git a/src/main.c b/src/main.c index f1694dfe..9d8e6cae 100644 --- a/src/main.c +++ b/src/main.c @@ -20,6 +20,7 @@ #if defined(XMP_AMIGA) #include #include +#include "main_amiga.h" #endif #if defined(__WATCOMC__) #include diff --git a/src/main_amiga.h b/src/main_amiga.h new file mode 100644 index 00000000..b9ced894 --- /dev/null +++ b/src/main_amiga.h @@ -0,0 +1,183 @@ +/*************************************************************************** + + codesets.library - Amiga shared library for handling different codesets + Copyright (C) 2001-2005 by Alfonso [alfie] Ranieri . + Copyright (C) 2005-2021 codesets.library Open Source Team + + Extended Module Player modifications: + Copyright (C) 2026 Lachesis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + codesets.library project: http://sourceforge.net/projects/codesetslib/ + +***************************************************************************/ + +/* Workbench, AmigaOS, AROS, MorphOS require manually setting the + * size of the stack. Include in main.c and nowhere else. + * Partially based on libinit.c code from codesets.library. + * + * Define XMP_NO_STACKSWAP to disable StackSwap/NewStackSwap/NewPPCStackSwap. + */ +#ifndef XMP_MAIN_AMIGA_H +#define XMP_MAIN_AMIGA_H + +#include "common.h" + +#if defined(XMP_AMIGA) + +#include + +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 1)) +#define XMP_USED __attribute__((used)) +#else +#define XMP_USED +#endif + +#define MIN_STACK_SIZE 32768 + +/* AmigaOS 3.2, 4.x */ +extern const char stack_cookie[]; +const char XMP_USED stack_cookie[] = "$STACK: 32768"; + +/* libnix, when swapstack.o is included (must be located manually...). */ +extern int __stack; +int XMP_USED __stack = MIN_STACK_SIZE; + +/* Workbench 2.04 through 3.1: manually set stack size via StackSwap. + * This needs to be performed through inline ASM to be safe. + * + * AROS: use NewStackSwap instead. + * MorphOS: use NewPPCStackSwap instead. + */ +#if !defined(__amigaos4__) && !defined(XMP_NO_STACKSWAP) + +int real_main(int argc, char **argv); + +/* The inline ASM from codesets.library expects one function argument, so + * it's easier to just use its semantics for now instead of rewriting it. */ +struct main_args +{ + int argc; + char **argv; +}; + +static ULONG main_wrapper(struct main_args *args) +{ + return (ULONG)real_main(args->argc, args->argv); +} + +#if defined(__mc68000__) && !defined(__AROS__) + +ULONG xmp_stackswap_call(struct StackSwapStruct *stack, + ULONG (*main_fn)(struct main_args *args), + struct main_args *args); + +asm(" .text \n\ + .even \n\ + .globl _xmp_stackswap_call \n\ +_xmp_stackswap_call: \n\ + moveml #0x3022,sp@- \n\ + movel sp@(20),d3 \n\ + movel sp@(24),a2 \n\ + movel sp@(28),d2 \n\ + movel _SysBase,a6 \n\ + movel d3,a0 \n\ + jsr a6@(-732:W) \n\ + movel d2,sp@- \n\ + jbsr a2@ \n\ + movel d0,d2 \n\ + addql #4,sp \n\ + movel _SysBase,a6 \n\ + movel d3,a0 \n\ + jsr a6@(-732:W) \n\ + movel d2,d0 \n\ + moveml sp@+,#0x440c \n\ + rts" +); + +#elif defined(__AROS__) + +static ULONG xmp_stackswap_call(struct StackSwapStruct *stack, + ULONG (*main_fn)(struct main_args *args), + struct main_args *args) +{ + struct StackSwapArgs sa; + + sa.Args[0] = (IPTR)args; + + return NewStackSwap(stack, main_fn, &sa); +} + +#elif defined(__MORPHOS__) + +static ULONG xmp_stackswap_call(struct StackSwapStruct *stack, + ULONG (*main_fn)(struct main_args *args), + struct main_args *args) +{ + struct PPCStackSwapArgs sa; + + sa.Args[0] = (IPTR)args; + + return NewPPCStackSwap(stack, main_fn, &sa); +} + +#else +#error Unknown Amiga OS variant +#endif + +int main(int argc, char **argv) +{ + struct StackSwapStruct sw; + struct Task *tc; + ULONG sz; + + tc = FindTask(NULL); + +#ifdef __MORPHOS__ + NewGetTaskAttrsA(tc, &sz, sizeof(ULONG), TASKINFOTYPE_STACKSIZE, NULL); +#else + sz = (UBYTE *)tc->tc_SPUpper - (UBYTE *)tc->tc_SPLower; +#endif + + if (sz < MIN_STACK_SIZE) { + sw.stk_Lower = AllocVec(MIN_STACK_SIZE, MEMF_PUBLIC); + if (sw.stk_Lower != NULL) { + struct main_args args; + int ret; + + sw.stk_Upper = (ULONG)sw.stk_Lower + MIN_STACK_SIZE; + sw.stk_Pointer = (APTR)sw.stk_Upper; + + /* + printf("Swapping stack to size %d...\n", MIN_STACK_SIZE); + fflush(stdout); + */ + + args.argc = argc; + args.argv = argv; + ret = (int)xmp_stackswap_call(&sw, main_wrapper, &args); + + FreeVec(sw.stk_Lower); + return ret; + } + } + + return real_main(argc, argv); +} + +#define main real_main + +#endif /* !AmigaOS4 && !defined(XMP_NO_STACKSWAP) */ + +#endif /* XMP_AMIGA */ + +#endif /* XMP_MAIN_AMIGA_H */