newlib - m68k-atari-elf: Correct handling of CRLF in stdio.
Commit Message
Atari computers uses CRLF as line endings the same way Windows do.
This patch enables text/binary mode for files and stdin/stdout/stderr streams.
It should work the same way as Cygwin do.
This patch have been tested successfully by the Atari community since beginning of March 2026.
Reconfigure is needed.
---
libgloss/m68k/Makefile.in | 2 +-
libgloss/m68k/atari/atari-open.c | 37 +++++----
libgloss/m68k/atari/atari-setmode.c | 74 ++++++++++++++++++
libgloss/m68k/atari/atari-tos.ld | 2 +-
libgloss/m68k/atari/atari-tos.specs | 3 +
libgloss/m68k/atari/atari-write.c | 61 +--------------
newlib/configure.host | 6 +-
newlib/libc/acinclude.m4 | 1 +
newlib/libc/stdio/flags.c | 2 +-
newlib/libc/stdio/stdio.c | 4 +
newlib/libc/sys/Makefile.inc | 3 +
newlib/libc/sys/atari/Makefile.inc | 1 +
newlib/libc/sys/atari/filemode.c | 94 +++++++++++++++++++++++
newlib/libc/sys/atari/include/io.h | 30 ++++++++
newlib/libc/sys/atari/include/sys/fcntl.h | 11 +++
newlib/libc/sys/atari/include/sys/stdio.h | 33 ++++++++
16 files changed, 283 insertions(+), 81 deletions(-)
create mode 100644 libgloss/m68k/atari/atari-setmode.c
create mode 100644 newlib/libc/sys/atari/Makefile.inc
create mode 100644 newlib/libc/sys/atari/filemode.c
create mode 100644 newlib/libc/sys/atari/include/io.h
create mode 100644 newlib/libc/sys/atari/include/sys/fcntl.h
create mode 100644 newlib/libc/sys/atari/include/sys/stdio.h
--
2.43.0
Comments
Bump.
Den torsdag 14 maj 2026 kl. 11:08:52 +02:00, skrev <micael@hildenborg.com>:
> Atari computers uses CRLF as line endings the same way Windows do.
> This patch enables text/binary mode for files and stdin/stdout/stderr streams.
> It should work the same way as Cygwin do.
> This patch have been tested successfully by the Atari community since beginning of March 2026.
> Reconfigure is needed.
>
> ---
> libgloss/m68k/Makefile.in | 2 +-
> libgloss/m68k/atari/atari-open.c | 37 +++++----
> libgloss/m68k/atari/atari-setmode.c | 74 ++++++++++++++++++
> libgloss/m68k/atari/atari-tos.ld | 2 +-
> libgloss/m68k/atari/atari-tos.specs | 3 +
> libgloss/m68k/atari/atari-write.c | 61 +--------------
> newlib/configure.host | 6 +-
> newlib/libc/acinclude.m4 | 1 +
> newlib/libc/stdio/flags.c | 2 +-
> newlib/libc/stdio/stdio.c | 4 +
> newlib/libc/sys/Makefile.inc | 3 +
> newlib/libc/sys/atari/Makefile.inc | 1 +
> newlib/libc/sys/atari/filemode.c | 94 +++++++++++++++++++++++
> newlib/libc/sys/atari/include/io.h | 30 ++++++++
> newlib/libc/sys/atari/include/sys/fcntl.h | 11 +++
> newlib/libc/sys/atari/include/sys/stdio.h | 33 ++++++++
> 16 files changed, 283 insertions(+), 81 deletions(-)
> create mode 100644 libgloss/m68k/atari/atari-setmode.c
> create mode 100644 newlib/libc/sys/atari/Makefile.inc
> create mode 100644 newlib/libc/sys/atari/filemode.c
> create mode 100644 newlib/libc/sys/atari/include/io.h
> create mode 100644 newlib/libc/sys/atari/include/sys/fcntl.h
> create mode 100644 newlib/libc/sys/atari/include/sys/stdio.h
>
> diff --git a/libgloss/m68k/Makefile.in b/libgloss/m68k/Makefile.in
> index b6d1868bc..43d716a2f 100644
> --- a/libgloss/m68k/Makefile.in
> +++ b/libgloss/m68k/Makefile.in
> @@ -216,7 +216,7 @@ ATARI_SRC_LIBGLOSS := atari-environ.c atari-execve.c atari-link.c atari-times.c
> atari-sbrk.c atari-stat.c atari-unlink.c atari-write.c \
> atari-chown.c atari-fork.c atari-getentropy.c atari-gettod.c atari-readlink.c \
> atari-symlink.c atari-chdir.c atari-getcwd.c atari-mkdir.c atari-rmdir.c \
> - atari-rename.c
> + atari-rename.c atari-setmode.c
>
> ATARI_OBJS_LIBGLOSS := $(foreach source,$(ATARI_SRC_LIBGLOSS),$(basename $(source)).o)
>
> diff --git a/libgloss/m68k/atari/atari-open.c b/libgloss/m68k/atari/atari-open.c
> index c61071881..6c960b6a6 100644
> --- a/libgloss/m68k/atari/atari-open.c
> +++ b/libgloss/m68k/atari/atari-open.c
> @@ -5,27 +5,15 @@
>
> #include <unistd.h>
> #include <_ansi.h>
> +#include <fcntl.h>
> +#include <io.h>
> #include "atari-gem_errno.h"
> #include "atari-traps.h"
>
> -// I really don't like having these defines here,
> -// fcntl.h where they are, also defines a function for "open",
> -// that is different from the one libgloss is supposed to provide...
> -#ifndef O_CREAT
> -#define O_CREAT 0x0200
> -#endif // O_CREAT
> -#ifndef O_APPEND
> -#define O_APPEND 0x0008
> -#endif // O_APPEND
> -#ifndef O_EXCL
> -#define O_EXCL 0x0800
> -#endif // O_EXCL
> -#ifndef O_TRUNC
> -#define O_TRUNC 0x0400
> -#endif // O_TRUNC
> +extern int atari_get_fmode(int* mode);
> +extern int atari_setmode(short f, int mode);
>
> -// mode is ignored. Those kind of settings is not supported by the st.
> -int open(const char *buf, int flags, int mode)
> +int open(const char *buf, int flags, ...)
> {
> int bios_handle = -1;
> unsigned short bios_mode = (unsigned short)(flags & 0x3); // bits 0-1 the same for st and linux.
> @@ -71,6 +59,21 @@ int open(const char *buf, int flags, int mode)
> gem_error_to_errno(bios_handle);
> bios_handle = -1;
> }
> + else
> + {
> + int fmode = flags & (O_TEXT | O_BINARY);
> + if (fmode == 0)
> + {
> + // Initializes the correct default mode.
> + atari_get_fmode(&fmode);
> + }
> + if (atari_setmode((short)bios_handle, fmode) == -1)
> + {
> + // Close file. errno is alredy set.
> + trap1_3e((unsigned short)bios_handle);
> + return -1;
> + }
> + }
> /*
> If bios_handle is positive, then the low word is the gemdos handle, and the high word is zero.
> */
> diff --git a/libgloss/m68k/atari/atari-setmode.c b/libgloss/m68k/atari/atari-setmode.c
> new file mode 100644
> index 000000000..89fe7511f
> --- /dev/null
> +++ b/libgloss/m68k/atari/atari-setmode.c
> @@ -0,0 +1,74 @@
> +/*
> + Copyright (C) 2026 Mikael Hildenborg
> + SPDX-License-Identifier: BSD-2-Clause
> +*/
> +#include <unistd.h>
> +#include <_ansi.h>
> +#include <fcntl.h>
> +#include <io.h>
> +#include <errno.h>
> +
> +/*
> + EmuTOS allows for a maximum of 75 open files, and ordinary TOS even less.
> + But MultiTOS and MagiC allows for much more, so to be safe we allow for even more.
> +*/
> +#define MAX_FILE_DESCRIPTORS 256
> +#define FILE_MODE_SHIFT 16
> +
> +static int _fmode = O_TEXT;
> +
> +unsigned char file_descriptor_modes[MAX_FILE_DESCRIPTORS] = {0};
> +
> +int atari_set_fmode(int mode)
> +{
> + _fmode = mode;
> + return 0;
> +}
> +
> +int atari_get_fmode(int* mode)
> +{
> + *mode = _fmode;
> + return 0;
> +}
> +
> +int atari_getmode(short fd)
> +{
> + if (fd < 0 || fd >= MAX_FILE_DESCRIPTORS)
> + {
> + errno = EINVAL;
> + return -1;
> + }
> + int mode = ((int)file_descriptor_modes[fd]) << FILE_MODE_SHIFT;
> + if (mode == 0 && fd < 2)
> + {
> + /*
> + stdin, stdout and stderr isn't opened and assumed to exist.
> + So the open function never gets the chance to set default mode for them.
> + That's why we do this check and return O_TEXT if nothing else is set.
> + */
> + mode = O_TEXT;
> + }
> + return mode;
> +}
> +
> +int atari_setmode(short fd, int mode)
> +{
> + int oldMode = atari_getmode(fd);
> + if (oldMode != -1)
> + {
> + // We cannot get here without having enough entries in the array.
> + file_descriptor_modes[fd] = (unsigned char)((mode & (O_TEXT | O_BINARY)) >> FILE_MODE_SHIFT);
> + }
> + return oldMode;
> +}
> +
> +int atari_istext_for_stdio (short fd)
> +{
> + int mode = atari_getmode(fd);
> + if (mode != -1)
> + {
> + // Treat anything else than binary mode as text mode.
> + return (mode & O_BINARY) == 0 ? 1 : 0;
> + }
> + return 1;
> +}
> diff --git a/libgloss/m68k/atari/atari-tos.ld b/libgloss/m68k/atari/atari-tos.ld
> index 6bbecc261..a2f0b7625 100644
> --- a/libgloss/m68k/atari/atari-tos.ld
> +++ b/libgloss/m68k/atari/atari-tos.ld
> @@ -85,7 +85,7 @@ SECTIONS
> LONG(__BSS_SEGMENT__ - __DATA_SEGMENT__); /* Length of the DATA segment */
> LONG(__BSS_SEGMENT_END - __BSS_SEGMENT__); /* Length of the BSS segment */
> LONG(0); /* Length of the symbol table */
> - LONG(0); /* Reserved, should be 0 */
> + LONG(0x68e1f001); /* __M68K_ATARI_ELF__ signature */
> LONG(0); /* Program flags */
> SHORT(0); /* 0 = Relocation info present */
> }
> diff --git a/libgloss/m68k/atari/atari-tos.specs b/libgloss/m68k/atari/atari-tos.specs
> index 02037132c..232afc3e8 100644
> --- a/libgloss/m68k/atari/atari-tos.specs
> +++ b/libgloss/m68k/atari/atari-tos.specs
> @@ -8,3 +8,6 @@
>
> *lib:
> + -latari-tos
> +
> +*cpp:
> ++ -D__M68K_ATARI_ELF__ -D__atari_os__
> diff --git a/libgloss/m68k/atari/atari-write.c b/libgloss/m68k/atari/atari-write.c
> index 9d6e59ed8..eba45038b 100644
> --- a/libgloss/m68k/atari/atari-write.c
> +++ b/libgloss/m68k/atari/atari-write.c
> @@ -8,30 +8,6 @@
> #include "atari-gem_errno.h"
> #include "atari-traps.h"
>
> -const char* lineEnding = "\r\n";
> -
> -int writeUntilDoneOrError(int fd, size_t len, const char* buf)
> -{
> - size_t written = 0;
> - if (len == 0)
> - {
> - return 0;
> - }
> - do
> - {
> - int n = trap1_40((unsigned short)fd, len, buf);
> - if (n < 0)
> - {
> - gem_error_to_errno(n);
> - return -1;
> - }
> - written += n;
> - buf += n;
> - } while (written < len);
> - return 0;
> -}
> -
> -
> _READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t nbytes)
> {
> int numWritten = GEM_EIHNDL;
> @@ -41,42 +17,7 @@ _READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t nbytes)
> {
> fd = GSH_CONOUT; // Use console out for stderr.
> }
> - if (fd == GSH_CONOUT)
> - {
> - // When we write to stdout on Atari, we must add a \r after \n to
> - // get the correct C output behaviour.
> - const char* stream = (const char*)buf;
> - size_t lastWrite = 0;
> - for (size_t i = 0; i < nbytes; ++i)
> - {
> - if (stream[i] == '\n')
> - {
> - int len = i - lastWrite; // length up to but not including \n
> - if (writeUntilDoneOrError(fd, len, stream + lastWrite) < 0)
> - {
> - return -1;
> - }
> - if (writeUntilDoneOrError(fd, 2, lineEnding) < 0)
> - {
> - return -1;
> - }
> - lastWrite = i + 1; // Include the \n
> - }
> - }
> - if (lastWrite < nbytes)
> - {
> - int len = nbytes - lastWrite;
> - if (writeUntilDoneOrError(fd, len, stream + lastWrite) < 0)
> - {
> - return -1;
> - }
> - }
> - numWritten = nbytes;
> - }
> - else
> - {
> - numWritten = trap1_40((unsigned short)fd, nbytes, buf);
> - }
> + numWritten = trap1_40((unsigned short)fd, nbytes, buf);
> }
> if (numWritten < 0)
> {
> diff --git a/newlib/configure.host b/newlib/configure.host
> index 960cca215..3c30bf8a7 100644
> --- a/newlib/configure.host
> +++ b/newlib/configure.host
> @@ -527,6 +527,10 @@ case "${host}" in
> m68hc11-*-*|m6811-*-*|m6812-*-*|m68hc12-*-*)
> ;;
>
> + m68k-atari-elf)
> + sys_dir=atari
> + have_crt0="no"
> + ;;
> m68k-sun-sunos*)
> unix_dir=unix
> ;;
> @@ -769,7 +773,7 @@ newlib_cflags="${newlib_cflags} -DCLOCK_PROVIDED -DMALLOC_PROVIDED -DEXIT_PROVID
> syscall_dir=
> ;;
> m68k-atari-elf)
> - newlib_cflags="${newlib_cflags} -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
> + newlib_cflags="${newlib_cflags} -D__M68K_ATARI_ELF__ -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
> syscall_dir=
> ;;
> mcore-*-*)
> diff --git a/newlib/libc/acinclude.m4 b/newlib/libc/acinclude.m4
> index a1040386b..c70a4aebb 100644
> --- a/newlib/libc/acinclude.m4
> +++ b/newlib/libc/acinclude.m4
> @@ -13,6 +13,7 @@ AM_CONDITIONAL(HAVE_UNIX_DIR, test x${unix_dir} != x)
> dnl We always recur into sys and machine, and let them decide what to do.
> m4_foreach_w([SYS_DIR], [
> a29khif amdgcn arm
> + atari
> d10v
> epiphany
> h8300hms h8500hms
> diff --git a/newlib/libc/stdio/flags.c b/newlib/libc/stdio/flags.c
> index d686fa046..7f3b15e2f 100644
> --- a/newlib/libc/stdio/flags.c
> +++ b/newlib/libc/stdio/flags.c
> @@ -72,7 +72,7 @@ __sflags (struct _reent *ptr,
> m |= O_BINARY;
> #endif
> break;
> -#ifdef __CYGWIN__
> +#if defined (__CYGWIN__) || defined (__M68K_ATARI_ELF__)
> case 't':
> m |= O_TEXT;
> break;
> diff --git a/newlib/libc/stdio/stdio.c b/newlib/libc/stdio/stdio.c
> index 6347949db..e3abdb0ce 100644
> --- a/newlib/libc/stdio/stdio.c
> +++ b/newlib/libc/stdio/stdio.c
> @@ -138,6 +138,10 @@ __stextmode (int fd)
> extern int _cygwin_istext_for_stdio (int);
> return _cygwin_istext_for_stdio (fd);
> #else
> + #ifdef __M68K_ATARI_ELF__
> + extern int atari_istext_for_stdio (int);
> + return atari_istext_for_stdio (fd);
> + #endif
> return 0;
> #endif
> }
> diff --git a/newlib/libc/sys/Makefile.inc b/newlib/libc/sys/Makefile.inc
> index 9f8758934..97ce59a36 100644
> --- a/newlib/libc/sys/Makefile.inc
> +++ b/newlib/libc/sys/Makefile.inc
> @@ -7,6 +7,9 @@ endif
> if HAVE_LIBC_SYS_ARM_DIR
> include %D%/arm/Makefile.inc
> endif
> +if HAVE_LIBC_SYS_ATARI_DIR
> +include %D%/atari/Makefile.inc
> +endif
> if HAVE_LIBC_SYS_D10V_DIR
> include %D%/d10v/Makefile.inc
> endif
> diff --git a/newlib/libc/sys/atari/Makefile.inc b/newlib/libc/sys/atari/Makefile.inc
> new file mode 100644
> index 000000000..5ca8259ee
> --- /dev/null
> +++ b/newlib/libc/sys/atari/Makefile.inc
> @@ -0,0 +1 @@
> +libc_a_SOURCES += %D%/filemode.c
> diff --git a/newlib/libc/sys/atari/filemode.c b/newlib/libc/sys/atari/filemode.c
> new file mode 100644
> index 000000000..ea8ff7455
> --- /dev/null
> +++ b/newlib/libc/sys/atari/filemode.c
> @@ -0,0 +1,94 @@
> +/*
> + Copyright (C) 2026 Mikael Hildenborg
> + SPDX-License-Identifier: BSD-2-Clause
> +*/
> +
> +#include <_ansi.h>
> +#include <sys/reent.h>
> +#include <stdio.h>
> +#include <sys/fcntl.h>
> +#include <sys/lock.h>
> +#include <errno.h>
> +#include "../../stdio/local.h"
> +
> +
> +/*
> + Thread safety in case support for MultiTOS or MagiC is added in the future.
> +*/
> +#ifndef __SINGLE_THREAD__
> +__LOCK_INIT(static, __setmode_mutex);
> +#endif
> +
> +short setmode_fd;
> +FILE* setmode_fp;
> +
> +int setmode_helper(struct _reent *ptr, FILE *f)
> +{
> + /*
> + We call __sfileno instead of fileno, as the latter tries to lock the filepointer.
> + And the filepointer is already locked if we came from fopen etc.
> + */
> + if (setmode_fd == __sfileno(f))
> + {
> + setmode_fp = f;
> + }
> + return 0;
> +}
> +
> +FILE* GetFilePointer(short fd)
> +{
> +#ifndef __SINGLE_THREAD__
> + __lock_acquire(__setmode_mutex);
> +#endif
> + setmode_fd = fd;
> + setmode_fp = 0;
> + _fwalk_sglue (_GLOBAL_REENT, setmode_helper, &__sglue);
> + FILE* fp = setmode_fp;
> +#ifndef __SINGLE_THREAD__
> + __lock_release(__setmode_mutex);
> +#endif
> + return fp;
> +}
> +
> +extern int atari_setmode(short f, int mode);
> +extern int atari_istext_for_stdio(short fd);
> +
> +int _setmode (short fd, int mode)
> +{
> + if (fd < 0)
> + {
> + errno = EBADF;
> + return -1;
> + }
> +
> +/*
> + We need to make sure that stdio is set up, as this function most likely will be called before any file operations.
> + The second parameter in the CHECK_INIT should be a file pointer, but paradoxically enough we cannot have one
> + before setting up stdio.
> + This is not a problem however as the file pointer never is used by the macro, so we set it to NULL.
> +*/
> + CHECK_INIT(_REENT, NULL);
> +
> + FILE* fp = GetFilePointer(fd);
> + if (fp == 0)
> + {
> + errno = EBADF;
> + return -1;
> + }
> +
> + int oldmode = atari_setmode(fd, mode);
> +
> + /*
> + Set or clear __SCLE for correct CRLF handling.
> + */
> + if (atari_istext_for_stdio(fd) != 0)
> + {
> + fp->_flags |= __SCLE;
> + }
> + else
> + {
> + fp->_flags &= ~__SCLE;
> + }
> +
> + return oldmode;
> +}
> diff --git a/newlib/libc/sys/atari/include/io.h b/newlib/libc/sys/atari/include/io.h
> new file mode 100644
> index 000000000..027ff1626
> --- /dev/null
> +++ b/newlib/libc/sys/atari/include/io.h
> @@ -0,0 +1,30 @@
> +/*
> + Copyright (C) 2026 Mikael Hildenborg
> + SPDX-License-Identifier: BSD-2-Clause
> +*/
> +
> +#ifndef _IO_H_
> +#define _IO_H_
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/*
> + fmode functions is in libgloss atari-setmode.c
> +*/
> +extern int atari_set_fmode(int mode);
> +extern int atari_get_fmode(int* mode);
> +extern int _setmode(short f, int mode);
> +
> +#define _set_fmode(mode) atari_set_fmode((mode))
> +#define _get_fmode(mode) atari_get_fmode((mode))
> +#define set_fmode(mode) atari_set_fmode((mode))
> +#define get_fmode(mode) atari_get_fmode((mode))
> +#define setmode(f, mode) _setmode((f), (mode))
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff --git a/newlib/libc/sys/atari/include/sys/fcntl.h b/newlib/libc/sys/atari/include/sys/fcntl.h
> new file mode 100644
> index 000000000..70c635c72
> --- /dev/null
> +++ b/newlib/libc/sys/atari/include/sys/fcntl.h
> @@ -0,0 +1,11 @@
> +#ifndef _SYS_FCNTL_H_
> +#define _SYS_FCNTL_H_
> +
> +#include <sys/_default_fcntl.h>
> +
> +#define _FBINARY 0x10000
> +#define _FTEXT 0x20000
> +#define O_BINARY _FBINARY
> +#define O_TEXT _FTEXT
> +
> +#endif /* _SYS_FCNTL_H_ */
> diff --git a/newlib/libc/sys/atari/include/sys/stdio.h b/newlib/libc/sys/atari/include/sys/stdio.h
> new file mode 100644
> index 000000000..8f3616a8c
> --- /dev/null
> +++ b/newlib/libc/sys/atari/include/sys/stdio.h
> @@ -0,0 +1,33 @@
> +/*
> + This file is a copy of libc/include/sys/stdio.h
> + The only difference is the __SCLE define at the bottom.
> +*/
> +#ifndef _NEWLIB_STDIO_H
> +#define _NEWLIB_STDIO_H
> +
> +#include <sys/lock.h>
> +#include <sys/reent.h>
> +
> +/* Internal locking macros, used to protect stdio functions. In the
> + general case, expand to nothing. Use __SSTR flag in FILE _flags to
> + detect if FILE is private to sprintf/sscanf class of functions; if
> + set then do nothing as lock is not initialised. */
> +#if !defined(_flockfile)
> +#ifndef __SINGLE_THREAD__
> +# define _flockfile(fp) (((fp)->_flags & __SSTR) ? 0 : __lock_acquire_recursive((fp)->_lock))
> +#else
> +# define _flockfile(fp) ((void) 0)
> +#endif
> +#endif
> +
> +#if !defined(_funlockfile)
> +#ifndef __SINGLE_THREAD__
> +# define _funlockfile(fp) (((fp)->_flags & __SSTR) ? 0 : __lock_release_recursive((fp)->_lock))
> +#else
> +# define _funlockfile(fp) ((void) 0)
> +#endif
> +#endif
> +
> +#define __SCLE 0x4000
> +
> +#endif /* _NEWLIB_STDIO_H */
> --
> 2.43.0
>
>
>
@@ -216,7 +216,7 @@ ATARI_SRC_LIBGLOSS := atari-environ.c atari-execve.c atari-link.c atari-times.c
atari-sbrk.c atari-stat.c atari-unlink.c atari-write.c \
atari-chown.c atari-fork.c atari-getentropy.c atari-gettod.c atari-readlink.c \
atari-symlink.c atari-chdir.c atari-getcwd.c atari-mkdir.c atari-rmdir.c \
- atari-rename.c
+ atari-rename.c atari-setmode.c
ATARI_OBJS_LIBGLOSS := $(foreach source,$(ATARI_SRC_LIBGLOSS),$(basename $(source)).o)
@@ -5,27 +5,15 @@
#include <unistd.h>
#include <_ansi.h>
+#include <fcntl.h>
+#include <io.h>
#include "atari-gem_errno.h"
#include "atari-traps.h"
-// I really don't like having these defines here,
-// fcntl.h where they are, also defines a function for "open",
-// that is different from the one libgloss is supposed to provide...
-#ifndef O_CREAT
-#define O_CREAT 0x0200
-#endif // O_CREAT
-#ifndef O_APPEND
-#define O_APPEND 0x0008
-#endif // O_APPEND
-#ifndef O_EXCL
-#define O_EXCL 0x0800
-#endif // O_EXCL
-#ifndef O_TRUNC
-#define O_TRUNC 0x0400
-#endif // O_TRUNC
+extern int atari_get_fmode(int* mode);
+extern int atari_setmode(short f, int mode);
-// mode is ignored. Those kind of settings is not supported by the st.
-int open(const char *buf, int flags, int mode)
+int open(const char *buf, int flags, ...)
{
int bios_handle = -1;
unsigned short bios_mode = (unsigned short)(flags & 0x3); // bits 0-1 the same for st and linux.
@@ -71,6 +59,21 @@ int open(const char *buf, int flags, int mode)
gem_error_to_errno(bios_handle);
bios_handle = -1;
}
+ else
+ {
+ int fmode = flags & (O_TEXT | O_BINARY);
+ if (fmode == 0)
+ {
+ // Initializes the correct default mode.
+ atari_get_fmode(&fmode);
+ }
+ if (atari_setmode((short)bios_handle, fmode) == -1)
+ {
+ // Close file. errno is alredy set.
+ trap1_3e((unsigned short)bios_handle);
+ return -1;
+ }
+ }
/*
If bios_handle is positive, then the low word is the gemdos handle, and the high word is zero.
*/
new file mode 100644
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2026 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+#include <unistd.h>
+#include <_ansi.h>
+#include <fcntl.h>
+#include <io.h>
+#include <errno.h>
+
+/*
+ EmuTOS allows for a maximum of 75 open files, and ordinary TOS even less.
+ But MultiTOS and MagiC allows for much more, so to be safe we allow for even more.
+*/
+#define MAX_FILE_DESCRIPTORS 256
+#define FILE_MODE_SHIFT 16
+
+static int _fmode = O_TEXT;
+
+unsigned char file_descriptor_modes[MAX_FILE_DESCRIPTORS] = {0};
+
+int atari_set_fmode(int mode)
+{
+ _fmode = mode;
+ return 0;
+}
+
+int atari_get_fmode(int* mode)
+{
+ *mode = _fmode;
+ return 0;
+}
+
+int atari_getmode(short fd)
+{
+ if (fd < 0 || fd >= MAX_FILE_DESCRIPTORS)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ int mode = ((int)file_descriptor_modes[fd]) << FILE_MODE_SHIFT;
+ if (mode == 0 && fd < 2)
+ {
+ /*
+ stdin, stdout and stderr isn't opened and assumed to exist.
+ So the open function never gets the chance to set default mode for them.
+ That's why we do this check and return O_TEXT if nothing else is set.
+ */
+ mode = O_TEXT;
+ }
+ return mode;
+}
+
+int atari_setmode(short fd, int mode)
+{
+ int oldMode = atari_getmode(fd);
+ if (oldMode != -1)
+ {
+ // We cannot get here without having enough entries in the array.
+ file_descriptor_modes[fd] = (unsigned char)((mode & (O_TEXT | O_BINARY)) >> FILE_MODE_SHIFT);
+ }
+ return oldMode;
+}
+
+int atari_istext_for_stdio (short fd)
+{
+ int mode = atari_getmode(fd);
+ if (mode != -1)
+ {
+ // Treat anything else than binary mode as text mode.
+ return (mode & O_BINARY) == 0 ? 1 : 0;
+ }
+ return 1;
+}
@@ -85,7 +85,7 @@ SECTIONS
LONG(__BSS_SEGMENT__ - __DATA_SEGMENT__); /* Length of the DATA segment */
LONG(__BSS_SEGMENT_END - __BSS_SEGMENT__); /* Length of the BSS segment */
LONG(0); /* Length of the symbol table */
- LONG(0); /* Reserved, should be 0 */
+ LONG(0x68e1f001); /* __M68K_ATARI_ELF__ signature */
LONG(0); /* Program flags */
SHORT(0); /* 0 = Relocation info present */
}
@@ -8,3 +8,6 @@
*lib:
+ -latari-tos
+
+*cpp:
++ -D__M68K_ATARI_ELF__ -D__atari_os__
@@ -8,30 +8,6 @@
#include "atari-gem_errno.h"
#include "atari-traps.h"
-const char* lineEnding = "\r\n";
-
-int writeUntilDoneOrError(int fd, size_t len, const char* buf)
-{
- size_t written = 0;
- if (len == 0)
- {
- return 0;
- }
- do
- {
- int n = trap1_40((unsigned short)fd, len, buf);
- if (n < 0)
- {
- gem_error_to_errno(n);
- return -1;
- }
- written += n;
- buf += n;
- } while (written < len);
- return 0;
-}
-
-
_READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t nbytes)
{
int numWritten = GEM_EIHNDL;
@@ -41,42 +17,7 @@ _READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t nbytes)
{
fd = GSH_CONOUT; // Use console out for stderr.
}
- if (fd == GSH_CONOUT)
- {
- // When we write to stdout on Atari, we must add a \r after \n to
- // get the correct C output behaviour.
- const char* stream = (const char*)buf;
- size_t lastWrite = 0;
- for (size_t i = 0; i < nbytes; ++i)
- {
- if (stream[i] == '\n')
- {
- int len = i - lastWrite; // length up to but not including \n
- if (writeUntilDoneOrError(fd, len, stream + lastWrite) < 0)
- {
- return -1;
- }
- if (writeUntilDoneOrError(fd, 2, lineEnding) < 0)
- {
- return -1;
- }
- lastWrite = i + 1; // Include the \n
- }
- }
- if (lastWrite < nbytes)
- {
- int len = nbytes - lastWrite;
- if (writeUntilDoneOrError(fd, len, stream + lastWrite) < 0)
- {
- return -1;
- }
- }
- numWritten = nbytes;
- }
- else
- {
- numWritten = trap1_40((unsigned short)fd, nbytes, buf);
- }
+ numWritten = trap1_40((unsigned short)fd, nbytes, buf);
}
if (numWritten < 0)
{
@@ -527,6 +527,10 @@ case "${host}" in
m68hc11-*-*|m6811-*-*|m6812-*-*|m68hc12-*-*)
;;
+ m68k-atari-elf)
+ sys_dir=atari
+ have_crt0="no"
+ ;;
m68k-sun-sunos*)
unix_dir=unix
;;
@@ -769,7 +773,7 @@ newlib_cflags="${newlib_cflags} -DCLOCK_PROVIDED -DMALLOC_PROVIDED -DEXIT_PROVID
syscall_dir=
;;
m68k-atari-elf)
- newlib_cflags="${newlib_cflags} -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
+ newlib_cflags="${newlib_cflags} -D__M68K_ATARI_ELF__ -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
syscall_dir=
;;
mcore-*-*)
@@ -13,6 +13,7 @@ AM_CONDITIONAL(HAVE_UNIX_DIR, test x${unix_dir} != x)
dnl We always recur into sys and machine, and let them decide what to do.
m4_foreach_w([SYS_DIR], [
a29khif amdgcn arm
+ atari
d10v
epiphany
h8300hms h8500hms
@@ -72,7 +72,7 @@ __sflags (struct _reent *ptr,
m |= O_BINARY;
#endif
break;
-#ifdef __CYGWIN__
+#if defined (__CYGWIN__) || defined (__M68K_ATARI_ELF__)
case 't':
m |= O_TEXT;
break;
@@ -138,6 +138,10 @@ __stextmode (int fd)
extern int _cygwin_istext_for_stdio (int);
return _cygwin_istext_for_stdio (fd);
#else
+ #ifdef __M68K_ATARI_ELF__
+ extern int atari_istext_for_stdio (int);
+ return atari_istext_for_stdio (fd);
+ #endif
return 0;
#endif
}
@@ -7,6 +7,9 @@ endif
if HAVE_LIBC_SYS_ARM_DIR
include %D%/arm/Makefile.inc
endif
+if HAVE_LIBC_SYS_ATARI_DIR
+include %D%/atari/Makefile.inc
+endif
if HAVE_LIBC_SYS_D10V_DIR
include %D%/d10v/Makefile.inc
endif
new file mode 100644
@@ -0,0 +1 @@
+libc_a_SOURCES += %D%/filemode.c
new file mode 100644
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2026 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include <sys/reent.h>
+#include <stdio.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include <errno.h>
+#include "../../stdio/local.h"
+
+
+/*
+ Thread safety in case support for MultiTOS or MagiC is added in the future.
+*/
+#ifndef __SINGLE_THREAD__
+__LOCK_INIT(static, __setmode_mutex);
+#endif
+
+short setmode_fd;
+FILE* setmode_fp;
+
+int setmode_helper(struct _reent *ptr, FILE *f)
+{
+ /*
+ We call __sfileno instead of fileno, as the latter tries to lock the filepointer.
+ And the filepointer is already locked if we came from fopen etc.
+ */
+ if (setmode_fd == __sfileno(f))
+ {
+ setmode_fp = f;
+ }
+ return 0;
+}
+
+FILE* GetFilePointer(short fd)
+{
+#ifndef __SINGLE_THREAD__
+ __lock_acquire(__setmode_mutex);
+#endif
+ setmode_fd = fd;
+ setmode_fp = 0;
+ _fwalk_sglue (_GLOBAL_REENT, setmode_helper, &__sglue);
+ FILE* fp = setmode_fp;
+#ifndef __SINGLE_THREAD__
+ __lock_release(__setmode_mutex);
+#endif
+ return fp;
+}
+
+extern int atari_setmode(short f, int mode);
+extern int atari_istext_for_stdio(short fd);
+
+int _setmode (short fd, int mode)
+{
+ if (fd < 0)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+/*
+ We need to make sure that stdio is set up, as this function most likely will be called before any file operations.
+ The second parameter in the CHECK_INIT should be a file pointer, but paradoxically enough we cannot have one
+ before setting up stdio.
+ This is not a problem however as the file pointer never is used by the macro, so we set it to NULL.
+*/
+ CHECK_INIT(_REENT, NULL);
+
+ FILE* fp = GetFilePointer(fd);
+ if (fp == 0)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ int oldmode = atari_setmode(fd, mode);
+
+ /*
+ Set or clear __SCLE for correct CRLF handling.
+ */
+ if (atari_istext_for_stdio(fd) != 0)
+ {
+ fp->_flags |= __SCLE;
+ }
+ else
+ {
+ fp->_flags &= ~__SCLE;
+ }
+
+ return oldmode;
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2026 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifndef _IO_H_
+#define _IO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ fmode functions is in libgloss atari-setmode.c
+*/
+extern int atari_set_fmode(int mode);
+extern int atari_get_fmode(int* mode);
+extern int _setmode(short f, int mode);
+
+#define _set_fmode(mode) atari_set_fmode((mode))
+#define _get_fmode(mode) atari_get_fmode((mode))
+#define set_fmode(mode) atari_set_fmode((mode))
+#define get_fmode(mode) atari_get_fmode((mode))
+#define setmode(f, mode) _setmode((f), (mode))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
new file mode 100644
@@ -0,0 +1,11 @@
+#ifndef _SYS_FCNTL_H_
+#define _SYS_FCNTL_H_
+
+#include <sys/_default_fcntl.h>
+
+#define _FBINARY 0x10000
+#define _FTEXT 0x20000
+#define O_BINARY _FBINARY
+#define O_TEXT _FTEXT
+
+#endif /* _SYS_FCNTL_H_ */
new file mode 100644
@@ -0,0 +1,33 @@
+/*
+ This file is a copy of libc/include/sys/stdio.h
+ The only difference is the __SCLE define at the bottom.
+*/
+#ifndef _NEWLIB_STDIO_H
+#define _NEWLIB_STDIO_H
+
+#include <sys/lock.h>
+#include <sys/reent.h>
+
+/* Internal locking macros, used to protect stdio functions. In the
+ general case, expand to nothing. Use __SSTR flag in FILE _flags to
+ detect if FILE is private to sprintf/sscanf class of functions; if
+ set then do nothing as lock is not initialised. */
+#if !defined(_flockfile)
+#ifndef __SINGLE_THREAD__
+# define _flockfile(fp) (((fp)->_flags & __SSTR) ? 0 : __lock_acquire_recursive((fp)->_lock))
+#else
+# define _flockfile(fp) ((void) 0)
+#endif
+#endif
+
+#if !defined(_funlockfile)
+#ifndef __SINGLE_THREAD__
+# define _funlockfile(fp) (((fp)->_flags & __SSTR) ? 0 : __lock_release_recursive((fp)->_lock))
+#else
+# define _funlockfile(fp) ((void) 0)
+#endif
+#endif
+
+#define __SCLE 0x4000
+
+#endif /* _NEWLIB_STDIO_H */