Re: [PATCH 0/5] newlib: m68k-atari-dev fixes.

<[email protected]> Wed, 03 Dec 2025 17:00:07 +0100
Newsgroups gmane.comp.lib.newlib
Message-ID <1764777607237.7.40924@webmail-backend-production-5fcf5bc65c-hz6fx>
Hi Jeff,

I fixed the issue with macro names and did a git rebase, so here are a new set of patches that replaces the ones I previously mailed.

And as previously mentioned, they will only affect the m68k-atari-elf target.

/Mikael.



Den måndag 17 november 2025 kl. 21:50:59 +01:00, skrev <[email protected]>:

> Hi Jeff,
> 
> The value is correct, but the name of the define is wrong.
> SEC_1900_TO_1980 should be named SEC_1970_TO_1980.
> And the same goes for SEC_1900_TO_MAX.
> 
> I can fix it and mail another patch.
> 
> Good catch by the way.
> 
> /Mikael.
> 
> 
> 
> 
> 
> Den måndag 17 november 2025 kl. 18:54:53 +01:00, skrev Jeff Johnston <[email protected]>:
> 
> > Hi Micael,
> > 
> > Sorry for the delay. There appears to be something wrong with your change to the seconds from 1900-1980 value. According to a simple web search, it should be close to the original value (10 digits) while the new updated value is only 9 digits. Can you explain this?
> > 
> > -- Jeff J.
> > 
> > 
> > 
> > 
> > On Mon, Nov 17, 2025 at 4:15 AM <<[email protected]>> wrote:
> > 
> > > BUMP
> > > 
> > > Also, I wrote wrong below: The only target affected whould be "m68k-atari-elf".
> > > 
> > > /Mikael Hildenborg.
> > > 
> > > 
> > > Den måndag 29 september 2025 kl. 08:57:59 +02:00, skrev <<[email protected]>>:
> > > 
> > > > Collection of bugfixes after feedback from community.
> > > > Should only affect the target: m68k-atari-dev.
> > > > 
> > > > Mikael Hildenborg (5):
> > > > m68k-atari-elf: fixed wrong number of seconds between 1900 and 1980.
> > > > m68k-atari-elf: changed stack and heap handling to mimic mintlib.
> > > > m68k-atari-elf: updated readme
> > > > m68k-atari-elf: proper setup of environment variables and program
> > > > arguments.
> > > > m68k-atari-elf: specs file declares environ as undefined.
> > > > 
> > > > libgloss/m68k/atari/README | 26 +--
> > > > libgloss/m68k/atari/atari-crt0.S | 55 +-----
> > > > libgloss/m68k/atari/atari-crti.S | 123 +++++++++----
> > > > libgloss/m68k/atari/atari-environ.c | 267 +++++++++++++++++++++++++++-
> > > > libgloss/m68k/atari/atari-gettod.c | 2 +-
> > > > libgloss/m68k/atari/atari-sbrk.c | 18 +-
> > > > libgloss/m68k/atari/atari-tos.specs | 2 +-
> > > > 7 files changed, 389 insertions(+), 104 deletions(-)
> > > > 
> > > > --
> > > > 2.43.0
> > > > 
> > > > 
> > > 
>
0001-m68k-atari-elf-fixed-wrong-number-of-seconds-between.patch (text/x-patch, 1.8 KB)
From 514b75fe92455ccaf82ee39b09d0e2766e9abba0 Mon Sep 17 00:00:00 2001
From: Mikael Hildenborg <[email protected]>
Date: Sun, 10 Aug 2025 08:49:23 +0200
Subject: [PATCH 1/5] m68k-atari-elf: fixed wrong number of seconds between
 1900 and 1980.

---
 libgloss/m68k/atari/atari-gettod.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libgloss/m68k/atari/atari-gettod.c b/libgloss/m68k/atari/atari-gettod.c
index 5477916a2..c8227ae5e 100644
--- a/libgloss/m68k/atari/atari-gettod.c
+++ b/libgloss/m68k/atari/atari-gettod.c
@@ -14,8 +14,8 @@
 	The code below uses that limitation to simplify the code.
 */
 
-#define SEC_1900_TO_1980 2524521600
-#define SEC_1900_TO_MAX 0x7fffffff
+#define SEC_1970_TO_1980 315529200
+#define SEC_1970_TO_MAX 0x7fffffff
 #define SECONDS_IN_A_DAY (24 * 60 * 60)
 #define SEC_JAN_AND_FEB ((31 + 29) * SECONDS_IN_A_DAY)	// In a leap year
 #define SECONDS_IN_A_YEAR (365 * SECONDS_IN_A_DAY)
@@ -59,7 +59,7 @@ int gettimeofday(struct timeval* tv, void* __tz)
 		}
 
 		// Add it all together
-		tv->tv_sec = (((((days * 24) + hour) * 60) + min) * 60) + sec + SEC_1900_TO_1980;
+		tv->tv_sec = (((((days * 24) + hour) * 60) + min) * 60) + sec + SEC_1970_TO_1980;
 		tv->tv_usec = 0;
 	}
 	return 0;
@@ -70,14 +70,14 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
 	// Support for timezone have been removed from linux glibc, so we just ignore it.
 	if (tv != 0)
 	{
-		if (tv->tv_sec < SEC_1900_TO_1980 || tv->tv_sec >= SEC_1900_TO_MAX)
+		if (tv->tv_sec < SEC_1970_TO_1980 || tv->tv_sec >= SEC_1970_TO_MAX)
 		{
 			// Outside the ranges we can handle.
 			gem_error_to_errno(GEM_EBADRQ);
 			return -1;
 		}
 
-		time_t seconds = tv->tv_sec - SEC_1900_TO_1980;
+		time_t seconds = tv->tv_sec - SEC_1970_TO_1980;
 		int year = 0;
 		time_t ysec = SECONDS_IN_A_YEAR;
 		do
-- 
2.43.0
0002-m68k-atari-elf-changed-stack-and-heap-handling-to-mi.patch (text/x-patch, 3.9 KB)
From ec250d128e15d9bd601a149cc1269cadc22a8c71 Mon Sep 17 00:00:00 2001
From: Mikael Hildenborg <[email protected]>
Date: Wed, 13 Aug 2025 18:15:34 +0200
Subject: [PATCH 2/5] m68k-atari-elf: changed stack and heap handling to mimic
 mintlib.

---
 libgloss/m68k/atari/atari-crti.S | 83 +++++++++++++++++++++-----------
 libgloss/m68k/atari/atari-sbrk.c | 18 +++++--
 2 files changed, 70 insertions(+), 31 deletions(-)

diff --git a/libgloss/m68k/atari/atari-crti.S b/libgloss/m68k/atari/atari-crti.S
index 5e4c13b0e..e11c51cba 100644
--- a/libgloss/m68k/atari/atari-crti.S
+++ b/libgloss/m68k/atari/atari-crti.S
@@ -8,14 +8,12 @@
 #define _fini	__libc_fini_array
 #endif
 
-	.equ	DEFAULT_STACK, 2000
+	.equ	MINKEEP, 0x10000
 
 	.global _BasePage
-	.weak 	_STACK_SIZE
-	.weak 	_HEAP_SIZE
+	.weak 	_stksize
 	.global _HeapPtr
-	.global _HeapBottom
-	.global _HeapTop
+	.global _heapbase
 	.global _atari_4ba_at_prg_start
 	.global __BSS_SEGMENT_END
 
@@ -26,32 +24,62 @@ _init:
 	move.l	4(a7),a0
 	move.l	a0, _BasePage
 
-	| Init stack
+/*
+	Init stack and heap.
+	The size of the stack and heap together is specified in _stksize.
+	The stack is set to point at the top of the reserved memory.
+	The heap is set to point at the bottom of the reserved memory.
+	Usage of _stksize (mintlib functionally compatible):
+		if _stksize is undefined, then all available memory is used.
+		if _stksize is 0, then MINKEEP memory is used.
+		if _stksize > 0 && < 4, then _stksize/4 of all memory is used.
+		if _stksize >= 4, then use that much memory.
+		if _stksize == -1, then all available memory is used.
+		if _stksize < -1, then use -_stksize memory.
+*/
 	lea		__BSS_SEGMENT_END, a2
-	lea		_STACK_SIZE, a1
+	move.l	a2, _heapbase
+	move.l	a2, _HeapPtr
+
+	move.l	4(a0), d1		| mem top
+	sub.l	a2, d1			| free mem
+	lea		_stksize, a1
 	cmpa.w	#0, a1
-	jeq		default_stack_size
-	add.l	(a1), a2
-	jra		stack_size_selected
-default_stack_size:
-	lea		DEFAULT_STACK(a2), a2
-stack_size_selected:
-	move.l	a2, a7
+	jeq		stksize_selected
+	move.l	(a1), d0
+	jeq		stksize_zero
+	jmi		stksize_negative
+stksize_positive:
+	moveq	#4, d2
+	cmp.l	d2, d0
+	jpl		stksize_above_three
+stksize_one_to_three:
+	lsr.l	#2, d1
+	move.l	d1, d2
+1:
+	subq.w	#1, d0
+	jeq		stksize_selected
+	add.l	d2, d1
+	jra		1b
+stksize_zero:
+	move.l	#MINKEEP, d1
+	jra		stksize_selected
+stksize_negative:
+	not.l	d0
+	jeq		stksize_selected	| -1
+	addq.l	#1, d0				| d0 = -_stksize
+stksize_above_three:
+	move.l	d0, d1
+stksize_selected:
 
-	| Init heap
-	move.l	a7, d0
-	move.l	d0, _HeapBottom
-	move.l	d0, _HeapPtr
-	move.l	4(a0), _HeapTop
+	add.l	a2, d1
+	bclr	#0, d1		| even address.
+	move.l	d1, a7
 
-	lea		_HEAP_SIZE, a1
-	cmpa.w	#0, a1
-	jeq		heap_setup_done
-	add.l	(a1), d0
-	sub.l	(a0), d0			| d0 is now the TPA size
+	sub.l	a0, d1		| mem from basepage to top.
 
-	| Program do not want all memory, so we shrink it.
-	move.l	d0, -(a7)
+	| Shrink mem.
+	move.l	d1, -(a7)
 	move.l	a0, -(a7)
 	clr.w	-(a7)
 	move.w	#0x4a, -(a7)	| Mshrink()
@@ -84,7 +112,6 @@ heap_setup_done:
 	.bss
 	.lcomm 	_BasePage, 4
 	.lcomm 	_HeapPtr, 4
-	.lcomm 	_HeapBottom, 4
-	.lcomm 	_HeapTop, 4
+	.lcomm 	_heapbase, 4
 	.lcomm	_atari_4ba_at_prg_start, 4
 	.even
\ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-sbrk.c b/libgloss/m68k/atari/atari-sbrk.c
index 8839cc742..535395f3e 100644
--- a/libgloss/m68k/atari/atari-sbrk.c
+++ b/libgloss/m68k/atari/atari-sbrk.c
@@ -7,13 +7,25 @@
 #include <_ansi.h>
 
 extern char *_HeapPtr;
-extern char *_HeapBottom;
-extern char *_HeapTop;
+extern char *_heapbase;
 
 char *sbrk(int nbytes)
 {
 	char *newheap = _HeapPtr + nbytes;
-	if (newheap > _HeapTop)
+
+	/*
+		The user stack pointer is the top heap.
+		The behaviour is undefined if we are in supervisor mode.
+		But memory allocations in supervisor mode feels like a bad idea anyway.
+	*/
+	char *heaptop;
+	__asm__ volatile (
+		"move.l	%%a7, %0\n\t"
+		: "=g" (heaptop)
+		:
+		:);
+
+	if (newheap > heaptop)
 	{
 		errno = ENOMEM;
 		return ((char *)-1);
-- 
2.43.0
0003-m68k-atari-elf-updated-readme.patch (text/x-patch, 2.2 KB)
From 72232188a99320237b1b5edeb96bb561835a646d Mon Sep 17 00:00:00 2001
From: Mikael Hildenborg <[email protected]>
Date: Fri, 15 Aug 2025 17:31:13 +0200
Subject: [PATCH 3/5] m68k-atari-elf: updated readme

---
 libgloss/m68k/atari/README | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/libgloss/m68k/atari/README b/libgloss/m68k/atari/README
index 24d2a6ea6..20283dd44 100644
--- a/libgloss/m68k/atari/README
+++ b/libgloss/m68k/atari/README
@@ -4,19 +4,19 @@ SPDX-License-Identifier: BSD-2-Clause
 Atari 16/32 bit TOS (not MINT) target.
 Target name: m68k-atari-elf.
 
-Stack size:
-The default stack size is set at 2000 bytes.
-To change stack size, add the following line to your C/C++ code:
-unsigned int _STACK_SIZE = *WANTED_STACK_SIZE*;
-Where "*WANTED_STACK_SIZE*" is the size in bytes you want to allocate for stack space.
-Do not use odd size!
-
-Heap size:
-The default heap size is to use all available memory.
-If you want to leave memory for other programs to use, then add the following line to your C/C++ code:
-unsigned int _HEAP_SIZE = *WANTED_HEAP_SIZE*;
-Where "*WANTED_HEAP_SIZE*" is the size in bytes you want to allocate for heap space.
-Do not use odd size!
+Stack and heap size:
+The symbol: "_stksize" defines how much memory that is reserved for stack and heap using the following scheme:
+1. If "_stksize" is undefined, then all available memory is reserved.
+2. If `int _stksize = SIZE;` is defined anywhere in the code, then "SIZE" is used in this way:
+   * SIZE == 0: then MINKEEP (64KB) memory is reserved.
+   * SIZE == 1: then 1/4 of all memory is reserved.
+   * SIZE == 2: then 2/4 of all memory is reserved.
+   * SIZE == 3: then 3/4 of all memory is reserved.
+   * SIZE >= 4: then SIZE memory is reserved.
+   * SIZE == -1, then all available memory is reserved (for mintlib compatibillity).
+   * SIZE < -1, then -SIZE memory is reserved (for mintlib compatibillity).
+The heap starts at the bottom of the shared memory and the stack starts at the top, and they grow towards eachother.
+A safety check for memory allocations is done to make sure that memory is not allocated and overwriting the stack.
 
 Program base page:
 A pointer to the program base page is stored at: _BasePage
-- 
2.43.0
0004-m68k-atari-elf-proper-setup-of-environment-variables.patch (text/x-patch, 11.9 KB)
From 7ff5870d9de04bcbb5f06026ed4a1b3c6b5124e0 Mon Sep 17 00:00:00 2001
From: Mikael Hildenborg <[email protected]>
Date: Fri, 22 Aug 2025 08:13:39 +0200
Subject: [PATCH 4/5] m68k-atari-elf: proper setup of environment variables and
 program arguments.

---
 libgloss/m68k/atari/atari-crt0.S    |  55 +-----
 libgloss/m68k/atari/atari-crti.S    |  36 +++-
 libgloss/m68k/atari/atari-environ.c | 267 +++++++++++++++++++++++++++-
 3 files changed, 302 insertions(+), 56 deletions(-)

diff --git a/libgloss/m68k/atari/atari-crt0.S b/libgloss/m68k/atari/atari-crt0.S
index 6d1bbd1d9..882be54b0 100644
--- a/libgloss/m68k/atari/atari-crt0.S
+++ b/libgloss/m68k/atari/atari-crt0.S
@@ -4,55 +4,20 @@
 */
 
 	.global exit
+	.global _atari_argv		| in atari-environ.c
+	.global _atari_argc		| in atari-environ.c
 
 	.section	".init"
 	.global	__call_main_with_argc_argv
 	.type	__call_main_with_argc_argv,#function
 __call_main_with_argc_argv:
 
-/*
-	Need to implement "The Atari Extended Argument Specification".
-	Also, there is no guarantee that the command line is zero terminated.
-*/
     | this occurs after crtbegin.o have done all global constructors etc.
-
-	move.l	a7, a6
-	moveq	#0, d0
-	moveq	#0, d2
-	movea.l	_BasePage, a0
-	lea		128(a0), a0		| add offset to the cmdline
-	lea		_cmdline, a1
-	move.b	(a0)+, d2		| d2 contains number of bytes in command line (max 127)
-	beq.s	3f
-	clr.b	(a1, d2.w)		| end our decoded args with a zero.
-	bra.s	2f
-1:
-	move.b	(a0, d2.w), d1
-	cmp.b	#' ', d1
-	bne.s	4f
-	moveq	#0, d1
-	lea		1(a1, d2.w), a2
-	tst.b	(a2)
-	beq.s	4f
-	move.l	a2, -(a7)
-	addq.w	#1, d0
-4:
-	move.b	d1, (a1, d2.w)
-2:
-	subq.w	#1, d2
-	bcc.s	1b
-	move.l	a1, -(a7)
-	addq.w	#1, d0
-3:
-	pea		_procname		| first argument is always the proc name. That we do not know...
-	addq.w	#1, d0
-	move.l	a7, a5
-	move.l	a6, -(a7)		| To know where to move it back again.
-
-	move.l	a5, -(a7)		| argv
-	move.l	d0, -(a7)		| argc
+	| atari-crti.S have already set up _atari_argc, _atari_argv
+	move.l	_atari_argv, -(a7)
+	move.l	_atari_argc, -(a7)
 	jsr		main
-	move.l	8(a7), a7		| move it back.
+	addq.l	#8, a7
 	move.l	d0, -(a7)
 	jsr		exit			| not expected to return
 	illegal
@@ -64,11 +29,3 @@ _exit:
 	move.w	d0, _program_return_code
     | crtend.o follows here with global destructors etc.
 
-	.data
-_procname:
-	.asciz	"yourapp.lol"
-	.even
-
-	.bss
-	.lcomm	_cmdline, 128
-	.even
diff --git a/libgloss/m68k/atari/atari-crti.S b/libgloss/m68k/atari/atari-crti.S
index e11c51cba..caecacef0 100644
--- a/libgloss/m68k/atari/atari-crti.S
+++ b/libgloss/m68k/atari/atari-crti.S
@@ -15,6 +15,7 @@
 	.global _HeapPtr
 	.global _heapbase
 	.global _atari_4ba_at_prg_start
+	.global _atari_4be_at_prg_start
 	.global __BSS_SEGMENT_END
 
 	.section	".init"
@@ -24,6 +25,21 @@ _init:
 	move.l	4(a7),a0
 	move.l	a0, _BasePage
 
+/*
+	First do a quick jump into supervisor mode to get some needed pointers.
+*/
+	move.l	#super_init, -(a7)
+	move.w	#0x26, -(a7)
+	trap	#14
+	addq.l	#6, a7
+
+/*
+	Make an estimate for how much memory we need to reserve for environment and arguments.
+*/
+	jsr		estimate_env_and_args_memory				| in atari-environ.c
+	| d0 contains the number of bytes we need to reserve for environment and arguments.
+	move.l	d0, d7
+	
 /*
 	Init stack and heap.
 	The size of the stack and heap together is specified in _stksize.
@@ -38,11 +54,14 @@ _init:
 		if _stksize < -1, then use -_stksize memory.
 */
 	lea		__BSS_SEGMENT_END, a2
+	add.l	d7, a2			| environment and arguments
 	move.l	a2, _heapbase
 	move.l	a2, _HeapPtr
 
+	move.l	_BasePage, a0
 	move.l	4(a0), d1		| mem top
 	sub.l	a2, d1			| free mem
+	move.l	d1, d6
 	lea		_stksize, a1
 	cmpa.w	#0, a1
 	jeq		stksize_selected
@@ -72,6 +91,13 @@ stksize_above_three:
 	move.l	d0, d1
 stksize_selected:
 
+	| adjust for memory needed for environment and arguments
+	add.l	d7, d1
+	cmp.l	d1, d6
+	jpl		1f
+	move.l	d6, d1
+1:
+
 	add.l	a2, d1
 	bclr	#0, d1		| even address.
 	move.l	d1, a7
@@ -94,12 +120,13 @@ stksize_selected:
 super_init:
 	| Init stuff that needs supervisor mode set.
 	move.l	0x4ba, _atari_4ba_at_prg_start
+	move.l	0x4be, _atari_4be_at_prg_start
 	rts
 heap_setup_done:
-	move.l	#super_init, -(a7)
-	move.w	#0x26, -(a7)
-	trap	#14
-	addq.l	#6, a7
+
+	pea		__BSS_SEGMENT_END				| storage space for environment and arguments
+	jsr		setup_env_and_args				| in atari-environ.c
+
     | crtbegin.o follows here with global constructors etc. init.
 
 
@@ -114,4 +141,5 @@ heap_setup_done:
 	.lcomm 	_HeapPtr, 4
 	.lcomm 	_heapbase, 4
 	.lcomm	_atari_4ba_at_prg_start, 4
+	.lcomm	_atari_4be_at_prg_start, 4
 	.even
\ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-environ.c b/libgloss/m68k/atari/atari-environ.c
index 3f4970733..5588c2885 100644
--- a/libgloss/m68k/atari/atari-environ.c
+++ b/libgloss/m68k/atari/atari-environ.c
@@ -3,6 +3,267 @@
 	SPDX-License-Identifier: BSD-2-Clause
 */
 
-// Should point to a list of global environment variables.
-char *__env[1] = { 0 };
-char **environ = __env;
\ No newline at end of file
+#include "atari-gem_basepage.h"
+
+/*
+	The functions:
+		void setup_env_and_args(void *mem_ptr)
+		int estimate_env_and_args_memory(void)
+	are called from atari-crti.S and should be handled with care.
+*/
+
+extern char **environ;		// setup_env_and_args will set this up.
+
+// Atari TOS do not supply any method of finding out the name of the application that is running.
+// So we default a name here to have a proper arg1.
+const char _proc_name[] = "_undefin.ed_";
+
+extern const char* _atari_4be_at_prg_start;	// Global environment
+const char **_atari_argv;
+int _atari_argc;
+
+// If storage_list is 0, then vars is only counted.
+// If argv_arg is not 0, then the value of ARGV= is stored there if found.
+int extract_env_vars(const char* env, const char** storage_list, const char** argv_arg)
+{
+	int num = 0;
+	if (env != 0)
+	{
+		// If we find a double null without a previous '=', then the string from the last var
+		// up to the double null must be discarded (aes bug).
+		while (env[0] != 0)		// loop until double null (string beginning with null)
+		{
+			const char* cvar = env;
+			short found_equal = 0;
+			short len_since_equal = 0;
+			while (env[0] != 0)
+			{
+				if (found_equal != 0) {++len_since_equal;}
+				if (env[0] == '=') {found_equal = 1;}
+				++env;
+			}
+			++env;
+			if (found_equal == 0 || len_since_equal == 0)
+			{
+				// This string is not valid, we should ignore it.
+				if (env[0] == 0)
+				{
+					// Double null after invalid string, ignore it.
+					++env;
+				}
+			}
+			else
+			{
+				if (argv_arg != 0 && (env - cvar) > 5 && 
+					(cvar[0] == 'A' && cvar[1] == 'R' && cvar[2] == 'G' && cvar[3] == 'V' && cvar[4] == '='))
+				{
+					*argv_arg = cvar + 5;
+					// We do not count ARGV as it is a part of command line.
+				}
+				else
+				{
+					if (storage_list != 0)
+					{
+						storage_list[num] = cvar;
+					}
+					++num;
+				}
+			}
+		}
+	}
+	return num;
+}
+
+int extract_cmd_args(const char* prg_cmd_line, const char* argv_arg, int* cmd_line_chars, const char** storage_list, char* cmd_line_out)
+{
+	int num = 0;
+	if (storage_list != 0)
+	{
+		storage_list[num] = _proc_name; 
+	}
+	++num;
+
+	int cmd_line_len = ((const unsigned char*)prg_cmd_line)[0];
+	cmd_line_chars[0] = 0;
+	if (cmd_line_len == 0)
+	{
+		return num;
+	}
+	if (cmd_line_len != 127)
+	{
+		// Only use ARGV if length is 127
+		argv_arg = 0;
+	}
+
+	const char* cmdline = prg_cmd_line + 1;
+	char expect = 0;
+	char *arg_start = cmd_line_out;
+	int arglen = 0;
+	while (cmdline != 0)
+	{
+		char c;
+		while ((c = *cmdline++) != 0)
+		{
+			if (c == '\\' && expect == 0)
+			{
+				// Next char will be escaped.
+				expect = '\\';
+			}
+			else if ((c == '\'' || c == '\"') && (expect == c || expect == 0))
+			{
+				// chars between "" or '' should not be escaped nor spaces split into args.
+				expect ^= c;
+			}
+			else if (c == ' ' && expect == 0)
+			{
+				if (arglen != 0)
+				{
+					// found one arg
+					if (cmd_line_out != 0) {*cmd_line_out++ = 0;}
+					cmd_line_chars[0]++;
+					if (storage_list != 0)
+					{
+						storage_list[num] = arg_start; 
+					}
+					++num;
+					arglen = 0;	// Next arg
+					arg_start = cmd_line_out;
+				}
+			}
+			else
+			{
+				if (expect == '\\')
+				{
+					// Only escape one char.
+					expect = 0;
+				}
+				if (cmd_line_out != 0) {*cmd_line_out++ = c;}
+				cmd_line_chars[0]++;
+				++arglen;
+			}
+		}
+
+		// Continue with extended command line if we have one.
+		cmdline = argv_arg;
+		argv_arg = 0;	// To end loop
+	}
+	if (arglen != 0)
+	{
+		if (cmd_line_out != 0) {*cmd_line_out++ = 0;}
+		cmd_line_chars[0]++;
+		if (storage_list != 0)
+		{
+			storage_list[num] = arg_start; 
+		}
+		++num;
+	}
+	return num;
+}
+
+/*
+prg_cmd_line:
+	First byte = length of prg_cmd_line.
+	If length == 127 then "ARGV=" in prg_env contains a string that should be appended to prg_cmd_line.
+prg_env:
+	Contains a set of null terminated strings.
+	Double null ends set.
+global_env:
+	Contains a set of null terminated strings.
+	Double null ends set.
+	AES versions >= 1.4 may multi-args (PATH) with ',', before it was only ';'
+	A bug in AES can cause strings like: "PATH=[nul]A:\[nul][nul]". They should be ignored.
+
+Due to how existing software sets up environment variables on Atari, global_env and prg_env can
+contain duplicates.
+So duplicates will be discarded with prg_env having higher priority than global_env.
+*/
+void setup_env_and_args(void *mem_ptr)
+{
+	/*
+		mem_ptr is guaranteed have the number of bytes reserved that function
+		estimate_env_and_args_memory calculates.
+	*/
+	const char* argv_arg = 0;
+	const char* prg_cmd_line = _BasePage->p_cmdlin;
+	const char* prg_env = _BasePage->p_env;
+	const char* global_env = _atari_4be_at_prg_start;
+
+	// Collect all environment variables.
+	environ = (char**)mem_ptr;
+	int num_env_args = extract_env_vars(prg_env, (const char**)environ, &argv_arg);
+	num_env_args += extract_env_vars(global_env, (const char**)environ, 0);
+	/*
+		Remove duplicates.
+		Not fun as we cannot use any library functions here.
+	*/
+	for (int i = 0; i < num_env_args; ++i)
+	{
+		char* vari = environ[i];
+		if (vari != 0)
+		{
+			for (int j = i + 1; j < num_env_args; ++j)
+			{
+				char* varj = environ[j];
+				if (varj != 0)
+				{
+					char* tvari = vari;
+					while (*tvari == *varj && *tvari != 0 && *tvari != '=')
+					{
+						++tvari;
+						++varj;
+					}
+					if (*tvari == '=' && *varj == '=')
+					{
+						// Found duplicate, clear it with a null pointer.
+						environ[j] = 0;
+					}
+				}
+			}
+		}
+	}
+	// Remove all null variables.
+	int new_num_env_args = 0;
+	for (int i = 0; i < num_env_args; ++i)
+	{
+		if (environ[i] != 0)
+		{
+			environ[new_num_env_args++] = environ[i];
+		}	
+	}
+	environ[new_num_env_args++] = 0;
+
+	// Count command line args
+	int cmd_line_chars = 0;
+	int num_cmd_args = extract_cmd_args(prg_cmd_line, argv_arg, &cmd_line_chars, 0, 0);
+
+	_atari_argv = (const char**)environ + new_num_env_args;
+	char* cmd_line_out = (char*)(_atari_argv + num_cmd_args + 1); 
+	// Collect all command line args.
+	_atari_argc = extract_cmd_args(prg_cmd_line, argv_arg, &cmd_line_chars, _atari_argv, cmd_line_out);
+	_atari_argv[_atari_argc] = 0;
+}
+
+int estimate_env_and_args_memory(void)
+{
+	/*
+		Stack and heap are not properly set up when this function is called.
+		NO memory allocations or large stack usage allowed!
+	*/
+	const char* argv_arg = 0;
+	const char* prg_cmd_line = _BasePage->p_cmdlin;
+	const char* prg_env = _BasePage->p_env;
+	const char* global_env = _atari_4be_at_prg_start;
+
+	int num_env_args = extract_env_vars(prg_env, 0, &argv_arg);
+	num_env_args += extract_env_vars(global_env, 0, 0);
+	int cmd_line_chars = 0;
+	int num_cmd_args = extract_cmd_args(prg_cmd_line, argv_arg, &cmd_line_chars, 0, 0);
+
+	int bytes_to_reserve = (num_env_args + 1) * 4;
+	bytes_to_reserve += (num_cmd_args + 1) * 4;
+	bytes_to_reserve += cmd_line_chars;
+
+	// This is the number of bytes of heap space that will be reserved before
+	// stack and heap is initialized. 
+	return bytes_to_reserve;
+}
\ No newline at end of file
-- 
2.43.0
0005-m68k-atari-elf-specs-file-declares-environ-as-undefi.patch (text/x-patch, 847 B)
From 0171328334f09f319e89a5077350e4bcad08b917 Mon Sep 17 00:00:00 2001
From: Mikael Hildenborg <[email protected]>
Date: Fri, 26 Sep 2025 08:47:21 +0200
Subject: [PATCH 5/5] m68k-atari-elf: specs file declares environ as undefined.

---
 libgloss/m68k/atari/atari-tos.specs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libgloss/m68k/atari/atari-tos.specs b/libgloss/m68k/atari/atari-tos.specs
index 00781c4fc..02037132c 100644
--- a/libgloss/m68k/atari/atari-tos.specs
+++ b/libgloss/m68k/atari/atari-tos.specs
@@ -4,7 +4,7 @@
 # Atari gcc specs
 
 *link:
-+ --emit-relocs --no-warn-rwx-segments -static --no-warn-execstack --undefined=__errno -T atari-tos.ld
++ --emit-relocs --no-warn-rwx-segments -static --no-warn-execstack --undefined=environ --undefined=__errno -T atari-tos.ld
 
 *lib:
 + -latari-tos
-- 
2.43.0