aboutsummaryrefslogtreecommitdiff
path: root/libgloss/m68k/atari
diff options
context:
space:
mode:
Diffstat (limited to 'libgloss/m68k/atari')
-rw-r--r--libgloss/m68k/atari/README35
-rw-r--r--libgloss/m68k/atari/atari-chdir.c49
-rw-r--r--libgloss/m68k/atari/atari-chown.c20
-rw-r--r--libgloss/m68k/atari/atari-close.c23
-rw-r--r--libgloss/m68k/atari/atari-crt0.S74
-rw-r--r--libgloss/m68k/atari/atari-crti.S90
-rw-r--r--libgloss/m68k/atari/atari-crtn.S24
-rw-r--r--libgloss/m68k/atari/atari-environ.c8
-rw-r--r--libgloss/m68k/atari/atari-execve.c16
-rw-r--r--libgloss/m68k/atari/atari-fork.c19
-rw-r--r--libgloss/m68k/atari/atari-fstat.c51
-rw-r--r--libgloss/m68k/atari/atari-gem_basepage.h58
-rw-r--r--libgloss/m68k/atari/atari-gem_errno.c95
-rw-r--r--libgloss/m68k/atari/atari-gem_errno.h57
-rw-r--r--libgloss/m68k/atari/atari-getcwd.c32
-rw-r--r--libgloss/m68k/atari/atari-getentropy.c52
-rw-r--r--libgloss/m68k/atari/atari-getpid.c11
-rw-r--r--libgloss/m68k/atari/atari-gettod.c135
-rw-r--r--libgloss/m68k/atari/atari-isatty.c12
-rw-r--r--libgloss/m68k/atari/atari-kill.c16
-rw-r--r--libgloss/m68k/atari/atari-link.c19
-rw-r--r--libgloss/m68k/atari/atari-lseek.c43
-rw-r--r--libgloss/m68k/atari/atari-mkdir.c21
-rw-r--r--libgloss/m68k/atari/atari-open.c78
-rw-r--r--libgloss/m68k/atari/atari-read.c44
-rw-r--r--libgloss/m68k/atari/atari-readlink.c20
-rw-r--r--libgloss/m68k/atari/atari-rename.c20
-rw-r--r--libgloss/m68k/atari/atari-rmdir.c20
-rw-r--r--libgloss/m68k/atari/atari-sbrk.c24
-rw-r--r--libgloss/m68k/atari/atari-stat.c29
-rw-r--r--libgloss/m68k/atari/atari-symlink.c19
-rw-r--r--libgloss/m68k/atari/atari-times.c37
-rw-r--r--libgloss/m68k/atari/atari-tos.ld160
-rw-r--r--libgloss/m68k/atari/atari-tos.specs10
-rw-r--r--libgloss/m68k/atari/atari-traps.c311
-rw-r--r--libgloss/m68k/atari/atari-traps.h84
-rw-r--r--libgloss/m68k/atari/atari-unlink.c19
-rw-r--r--libgloss/m68k/atari/atari-wait.c16
-rw-r--r--libgloss/m68k/atari/atari-write.c87
39 files changed, 1938 insertions, 0 deletions
diff --git a/libgloss/m68k/atari/README b/libgloss/m68k/atari/README
new file mode 100644
index 0000000..24d2a6e
--- /dev/null
+++ b/libgloss/m68k/atari/README
@@ -0,0 +1,35 @@
+Copyright (C) 2025 Mikael Hildenborg
+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!
+
+Program base page:
+A pointer to the program base page is stored at: _BasePage
+
+ELF output:
+The executable elf output have four interesting segments:
+".text", ".data", ".bss" and ".prgheader" as described in "atari-tos.ld".
+All relevant code and data have been properly sorted into those segments, and the ".prgheader"
+contains a valid atari prg header pointing to relevant segments.
+Relocation data from the link switch "--emit-relocs" (defaultted by "atari-tos.specs") is
+included in the elf output so additional fixup information can be added to the prg file.
+Addresses in elf and prg files are the same and thus compatible for interchangeable symbol lookup.
+
+More information:
+Can be found at: https://github.com/hildenborg/m68k-atari-dev
+Where build scripts for complete toolchain, executables for converting elf to prg, and a remote debug server with full symbolic debugging are available.
diff --git a/libgloss/m68k/atari/atari-chdir.c b/libgloss/m68k/atari/atari-chdir.c
new file mode 100644
index 0000000..edb7a70
--- /dev/null
+++ b/libgloss/m68k/atari/atari-chdir.c
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+int chdir(const char *path)
+{
+ int err = GEM_E_OK;
+ if (path != 0)
+ {
+ if (path[1] == ':')
+ {
+ unsigned int drives = 0;
+ unsigned short drive = 0;
+ if (path[0] >= 'A' && path[0] <= 'Z')
+ {
+ drive = (unsigned short)(path[0] - 'A');
+ }
+ else if (path[0] >= 'a' && path[0] <= 'z')
+ {
+ drive = (unsigned short)(path[0] - 'a');
+ }
+ drives = trap1_e(drive);
+ if ((drives & (1 << drive)) == 0)
+ {
+ err = GEM_EDRIVE;
+ }
+ path += 2;
+ }
+ if (err == GEM_E_OK)
+ {
+ err = trap1_3b(path);
+ }
+ }
+ else
+ {
+ err = GEM_EPTHNF;
+ }
+ if (err < 0)
+ {
+ gem_error_to_errno(err);
+ return -1;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-chown.c b/libgloss/m68k/atari/atari-chown.c
new file mode 100644
index 0000000..62f8300
--- /dev/null
+++ b/libgloss/m68k/atari/atari-chown.c
@@ -0,0 +1,20 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <_ansi.h>
+#include <_syslist.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include "libnosys/warning.h"
+
+int chown(const char *path, uid_t owner, gid_t group)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(chown)
diff --git a/libgloss/m68k/atari/atari-close.c b/libgloss/m68k/atari/atari-close.c
new file mode 100644
index 0000000..a52049c
--- /dev/null
+++ b/libgloss/m68k/atari/atari-close.c
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+int close(int fd)
+{
+ int err = GEM_EIHNDL;
+ if (fd >= 0)
+ {
+ err = trap1_3e((unsigned short)fd);
+ }
+ if (err < 0)
+ {
+ gem_error_to_errno(err);
+ return -1;
+ }
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-crt0.S b/libgloss/m68k/atari/atari-crt0.S
new file mode 100644
index 0000000..6d1bbd1
--- /dev/null
+++ b/libgloss/m68k/atari/atari-crt0.S
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+ .global exit
+
+ .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
+ jsr main
+ move.l 8(a7), a7 | move it back.
+ move.l d0, -(a7)
+ jsr exit | not expected to return
+ illegal
+
+ .global _exit
+ .type _exit,#function
+_exit:
+ move.l 4(a7), d0
+ 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
new file mode 100644
index 0000000..5e4c13b
--- /dev/null
+++ b/libgloss/m68k/atari/atari-crti.S
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifdef _HAVE_INITFINI_ARRAY
+#define _init __libc_init_array
+#define _fini __libc_fini_array
+#endif
+
+ .equ DEFAULT_STACK, 2000
+
+ .global _BasePage
+ .weak _STACK_SIZE
+ .weak _HEAP_SIZE
+ .global _HeapPtr
+ .global _HeapBottom
+ .global _HeapTop
+ .global _atari_4ba_at_prg_start
+ .global __BSS_SEGMENT_END
+
+ .section ".init"
+ .global _init
+ .type _init,#function
+_init:
+ move.l 4(a7),a0
+ move.l a0, _BasePage
+
+ | Init stack
+ lea __BSS_SEGMENT_END, a2
+ lea _STACK_SIZE, 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
+
+ | Init heap
+ move.l a7, d0
+ move.l d0, _HeapBottom
+ move.l d0, _HeapPtr
+ move.l 4(a0), _HeapTop
+
+ 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
+
+ | Program do not want all memory, so we shrink it.
+ move.l d0, -(a7)
+ move.l a0, -(a7)
+ clr.w -(a7)
+ move.w #0x4a, -(a7) | Mshrink()
+ trap #1
+ lea 12(a7), a7
+ tst.l d0
+ jpl heap_setup_done
+ | Error, just quit with d0 as return code.
+ move.w d0,-(a7)
+ move.w #0x4c,-(a7)
+ trap #1
+super_init:
+ | Init stuff that needs supervisor mode set.
+ move.l 0x4ba, _atari_4ba_at_prg_start
+ rts
+heap_setup_done:
+ move.l #super_init, -(a7)
+ move.w #0x26, -(a7)
+ trap #14
+ addq.l #6, a7
+ | crtbegin.o follows here with global constructors etc. init.
+
+
+ .section ".fini"
+ /*
+ Empty.
+ */
+
+
+ .bss
+ .lcomm _BasePage, 4
+ .lcomm _HeapPtr, 4
+ .lcomm _HeapBottom, 4
+ .lcomm _HeapTop, 4
+ .lcomm _atari_4ba_at_prg_start, 4
+ .even \ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-crtn.S b/libgloss/m68k/atari/atari-crtn.S
new file mode 100644
index 0000000..6482a9c
--- /dev/null
+++ b/libgloss/m68k/atari/atari-crtn.S
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+ .global _program_return_code
+
+ .section ".init"
+ /*
+ Empty.
+ */
+
+
+ .section ".fini"
+ .global _fini
+ .type _fini,#function
+_fini:
+ | this occurs after crtend.o have done all global destructors etc.
+ move.w _program_return_code,-(a7)
+ move.w #0x4c,-(a7)
+ trap #1
+
+ .bss
+ .lcomm _program_return_code, 2
diff --git a/libgloss/m68k/atari/atari-environ.c b/libgloss/m68k/atari/atari-environ.c
new file mode 100644
index 0000000..3f49707
--- /dev/null
+++ b/libgloss/m68k/atari/atari-environ.c
@@ -0,0 +1,8 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ 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
diff --git a/libgloss/m68k/atari/atari-execve.c b/libgloss/m68k/atari/atari-execve.c
new file mode 100644
index 0000000..c7a5721
--- /dev/null
+++ b/libgloss/m68k/atari/atari-execve.c
@@ -0,0 +1,16 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <errno.h>
+#include "libnosys/warning.h"
+
+int execve(char *name, char **argv, char **env)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(execve)
diff --git a/libgloss/m68k/atari/atari-fork.c b/libgloss/m68k/atari/atari-fork.c
new file mode 100644
index 0000000..e16436f
--- /dev/null
+++ b/libgloss/m68k/atari/atari-fork.c
@@ -0,0 +1,19 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <_ansi.h>
+#include <_syslist.h>
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include "libnosys/warning.h"
+
+int fork(void)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(fork)
diff --git a/libgloss/m68k/atari/atari-fstat.c b/libgloss/m68k/atari/atari-fstat.c
new file mode 100644
index 0000000..a48a009
--- /dev/null
+++ b/libgloss/m68k/atari/atari-fstat.c
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <sys/stat.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+#include "atari-gem_basepage.h"
+
+/*
+ From man page:
+ "only the st_uid, st_gid, st_size, and st_mode fields,
+ and only the S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH file permission bits need be valid."
+*/
+
+int fstat(int fd, struct stat *buf)
+{
+ struct DTA *dta = (struct DTA *)GEM_EIHNDL;
+ // For now, we just support files.
+ if (fd >= 6)
+ {
+ buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ // files
+ dta = trap1_2f((unsigned int)fd);
+ if ((int)dta >= 0)
+ {
+ // Attribs
+ buf->st_mode |= (dta->d_attrib & FA_DIR) ? S_IFDIR : S_IFREG;
+ if ((dta->d_attrib & FA_READONLY) != 0)
+ {
+ buf->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+ buf->st_size = dta->d_length;
+ // st_uid, st_gid have no meaning for the st, so we ignore them.
+ }
+ }
+ else
+ {
+ // unsupported, fake something.
+ buf->st_mode = S_IFCHR;
+ buf->st_blksize = 0;
+ }
+ if ((int)dta < 0)
+ {
+ gem_error_to_errno((int)dta);
+ return -1;
+ }
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-gem_basepage.h b/libgloss/m68k/atari/atari-gem_basepage.h
new file mode 100644
index 0000000..461c3ce
--- /dev/null
+++ b/libgloss/m68k/atari/atari-gem_basepage.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifndef GEM_BASEPAGE_H
+#define GEM_BASEPAGE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ This is the process base page data, containing information about the executable.
+*/
+
+#define FA_READONLY 0x01 // Read-only flag
+#define FA_HIDDEN 0x02 // Hidden file flag
+#define FA_SYSTEM 0x04 // System file flag
+#define FA_VOLUME 0x08 // Volume label flag
+#define FA_DIR 0x10 // Subdirectory
+#define FA_ARCHIVE 0x20 // Archive flag
+
+struct DTA
+{
+ unsigned char d_reserved[21];
+ unsigned char d_attrib;
+ unsigned short d_time;
+ unsigned short d_date;
+ unsigned int d_length;
+ char d_fname[14];
+};
+
+struct BasePage
+{
+ unsigned char* p_lowtpa; // This LONG contains a pointer to the Transient Program Area (TPA).
+ unsigned char* p_hitpa; // This LONG contains a pointer to the top of the TPA + 1.
+ unsigned char* p_tbase; // This LONG contains a pointer to the base of the text segment
+ unsigned int p_tlen; // This LONG contains the length of the text segment.
+ unsigned char* p_dbase; // This LONG contains a pointer to the base of the data segment.
+ unsigned int p_dlen; // This LONG contains the length of the data segment.
+ unsigned char* p_bbase; // This LONG contains a pointer to the base of the BSS segment.
+ unsigned int p_blen; // This LONG contains the length of the BSS segment.
+ struct DTA* p_dta; // This LONG contains a pointer to the processes’ DTA.
+ struct BasePage* p_parent; // This LONG contains a pointer to the processes’ parent’s basepage.
+ unsigned int p_reserved; // This LONG is currently unused and is reserved.
+ char* p_env; // This LONG contains a pointer to the processes’ environment string.
+ unsigned char p_undef[80]; // This area contains 80 unused, reserved bytes.
+ char p_cmdlin[128]; // This area contains a copy of the 128 byte command line string.
+};
+
+extern struct BasePage* _BasePage;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GEM_BASEPAGE_H
diff --git a/libgloss/m68k/atari/atari-gem_errno.c b/libgloss/m68k/atari/atari-gem_errno.c
new file mode 100644
index 0000000..641a0b2
--- /dev/null
+++ b/libgloss/m68k/atari/atari-gem_errno.c
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include <_ansi.h>
+
+/*
+ The conversion between gem errors and errno is not a one to one match,
+ so the goal has been to try and report errors to errno in such way that
+ code depending on this will run as expected.
+ This remains to be proven correct.
+*/
+void gem_error_to_errno(int gem_error)
+{
+ errno = ENOSYS; // Use this as default error code.
+ switch (gem_error)
+ {
+ case GEM_EDRVNR:
+ case GEM_EMEDIA:
+ case GEM_EPAPER:
+ errno = EIO;
+ break;
+ case GEM_EBADRQ:
+ errno = EINVAL;
+ break;
+ case GEM_E_SEEK:
+ errno = ESPIPE;
+ break;
+ case GEM_ESECNF:
+ case GEM_EWRITF:
+ case GEM_EREADF:
+ errno = EIO;
+ break;
+ case GEM_EWRPRO:
+ errno = EROFS;
+ break;
+ case GEM_EUNDEV:
+ case GEM_EDRIVE:
+ errno = ENODEV;
+ break;
+ case GEM_EFILNF:
+ case GEM_EPTHNF:
+ case GEM_EMOUNT:
+ errno = ENOENT;
+ break;
+ case GEM_ENHNDL:
+ case GEM_ENMFIL:
+ errno = EMFILE;
+ break;
+ case GEM_EACCDN:
+ errno = EACCES;
+ break;
+ case GEM_EIHNDL:
+ errno = EBADF;
+ break;
+ case GEM_ENSMEM:
+ case GEM_EGSBF:
+ errno = ENOMEM;
+ break;
+ case GEM_EIMBA:
+ errno = EADDRNOTAVAIL;
+ break;
+ case GEM_ENSAME:
+ errno = EXDEV;
+ break;
+ case GEM_ELOCKED:
+ case GEM_ENSLOCK:
+ errno = ENOLCK;
+ break;
+ case GEM_ENAMETOOLONG:
+ errno = ENAMETOOLONG;
+ break;
+ case GEM_EPLFMT:
+ errno = ENOEXEC;
+ break;
+ case GEM_ELOOP:
+ errno = EMLINK;
+ break;
+ case GEM_E_OK:
+ case GEM_ERROR:
+ case GEM_EUNCMD:
+ case GEM_E_CRC:
+ case GEM_E_CHNG:
+ case GEM_EBADSF:
+ case GEM_EOTHER:
+ case GEM_EINVFN:
+ case GEM_EINTRN:
+ default:
+ errno = -gem_error;
+ break;
+ }
+}
diff --git a/libgloss/m68k/atari/atari-gem_errno.h b/libgloss/m68k/atari/atari-gem_errno.h
new file mode 100644
index 0000000..c61478d
--- /dev/null
+++ b/libgloss/m68k/atari/atari-gem_errno.h
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifndef GEM_ERRNO_DEFINED
+#define GEM_ERRNO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GEM_E_OK 0 // No error
+#define GEM_ERROR -1 // Generic error
+#define GEM_EDRVNR -2 // Drive not ready
+#define GEM_EUNCMD -3 // Unknown command
+#define GEM_E_CRC -4 // CRC error
+#define GEM_EBADRQ -5 // Bad request
+#define GEM_E_SEEK -6 // Seek error
+#define GEM_EMEDIA -7 // Unknown media
+#define GEM_ESECNF -8 // Sector not found
+#define GEM_EPAPER -9 // Out of paper
+#define GEM_EWRITF -10 // Write fault
+#define GEM_EREADF -11 // Read fault
+#define GEM_EWRPRO -12 // Device is write protected
+#define GEM_E_CHNG -14 // Media change detected
+#define GEM_EUNDEV -15 // Unknown device
+#define GEM_EBADSF -16 // Bad sectors on format
+#define GEM_EOTHER -17 // Insert other disk (request)
+#define GEM_EINVFN -32 // Invalid function
+#define GEM_EFILNF -33 // File not found
+#define GEM_EPTHNF -34 // Path not found
+#define GEM_ENHNDL -35 // No more handles
+#define GEM_EACCDN -36 // Access denied
+#define GEM_EIHNDL -37 // Invalid handle
+#define GEM_ENSMEM -39 // Insufficient memory
+#define GEM_EIMBA -40 // Invalid memory block address
+#define GEM_EDRIVE -46 // Invalid drive specification
+#define GEM_ENSAME -48 // Cross device rename
+#define GEM_ENMFIL -49 // No more files
+#define GEM_ELOCKED -58 // Record is already locked
+#define GEM_ENSLOCK -59 // Invalid lock removal request
+#define GEM_ERANGE -64 // Range error
+#define GEM_ENAMETOOLONG -64 // Range error
+#define GEM_EINTRN -65 // Internal error
+#define GEM_EPLFMT -66 // Invalid program load format
+#define GEM_EGSBF -67 // Memory block growth failure
+#define GEM_ELOOP -80 // Too many symbolic links
+#define GEM_EMOUNT -200 // Mount point crossed (indicator)
+
+void gem_error_to_errno(int gem_error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GEM_ERRNO_DEFINED
diff --git a/libgloss/m68k/atari/atari-getcwd.c b/libgloss/m68k/atari/atari-getcwd.c
new file mode 100644
index 0000000..37d778e
--- /dev/null
+++ b/libgloss/m68k/atari/atari-getcwd.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+char *getcwd(char *buf, size_t size)
+{
+ char *retbuf = buf;
+ if (retbuf == 0)
+ {
+ errno = EIO;
+ return 0;
+ }
+
+ unsigned short drive = trap1_19();
+ int result = trap1_47(retbuf + 2, drive + 1);
+ if (result < 0)
+ {
+ gem_error_to_errno(result);
+ return 0;
+ }
+ retbuf[0] = (char)('A' + drive);
+ retbuf[1] = ':';
+ return retbuf;
+}
diff --git a/libgloss/m68k/atari/atari-getentropy.c b/libgloss/m68k/atari/atari-getentropy.c
new file mode 100644
index 0000000..0b5b461
--- /dev/null
+++ b/libgloss/m68k/atari/atari-getentropy.c
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include <_syslist.h>
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+/*
+ Xorshift pseudorandom number generator.
+ https://en.wikipedia.org/wiki/Xorshift
+ Inited with random seed.
+*/
+
+int getentropy(void *buf, size_t buflen)
+{
+ unsigned int a;
+ unsigned int state[4];
+
+ // Init state
+ a = trap14_11(); // Atari Random func.
+ for (int i = 0; i < 4; ++i)
+ {
+ // xorshift32
+ a ^= (a << 13);
+ a ^= (a >> 17);
+ a ^= (a << 5);
+ state[i] = a;
+ }
+
+ for (size_t i = 0; i < buflen; ++i)
+ {
+ // xorshift128
+ unsigned int t = state[3];
+ unsigned int s = state[0];
+ state[3] = state[2];
+ state[2] = state[1];
+ state[1] = s;
+
+ t ^= t << 11;
+ t ^= t >> 7;
+ state[0] = t ^ s ^ (s >> 19);
+
+ // use lowest byte (could be optimized to adapt to buf len and output whole 32 bits when possible.)
+ ((unsigned char*)buf)[i] = (unsigned char)state[0];
+ }
+
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-getpid.c b/libgloss/m68k/atari/atari-getpid.c
new file mode 100644
index 0000000..98e001e
--- /dev/null
+++ b/libgloss/m68k/atari/atari-getpid.c
@@ -0,0 +1,11 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+
+int getpid(void)
+{
+ return 1;
+}
diff --git a/libgloss/m68k/atari/atari-gettod.c b/libgloss/m68k/atari/atari-gettod.c
new file mode 100644
index 0000000..5477916
--- /dev/null
+++ b/libgloss/m68k/atari/atari-gettod.c
@@ -0,0 +1,135 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <sys/time.h>
+#include <time.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+/*
+ Atari can only handle dates from 1980 to 2107.
+ time_t on m68k is 32bit signed, so that gives us an upper limit of 2038.
+ The code below uses that limitation to simplify the code.
+*/
+
+#define SEC_1900_TO_1980 2524521600
+#define SEC_1900_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)
+
+static const short month_to_day[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
+
+int gettimeofday(struct timeval* tv, void* __tz)
+{
+ struct timezone* tz = __tz;
+ if (tz != 0)
+ {
+ // Support for timezone have been removed from linux glibc, so we just fill in a zero timezone.
+ tz->tz_minuteswest = 0;
+ tz->tz_dsttime = 0;
+ }
+ if (tv != 0)
+ {
+ unsigned short date = trap1_2a();
+ unsigned short time = trap1_2c();
+
+ int year = ((date >> 9) & 0x7f); // from 1980
+ int month = ((date >> 5) & 0xf) - 1;
+ int day = date & 0x1f;
+ int hour = ((time >> 11) & 0x1f);
+ int min = ((time >> 5) & 0x3f);
+ int sec = (time & 0x1f) * 2;
+
+ // Days passed in current year
+ time_t days = day + month_to_day[month];
+ // Add days for every passed year
+ days += year * 365;
+
+ // Add days for every passed leap year
+ int months = year * 12 + month; // total months
+ months -= 2; // remove januari and februari
+ if (months > 0)
+ {
+ // We must have passed at least one leap day.
+ int leap_days = (months / (12*4)) + 1;
+ days += leap_days;
+ }
+
+ // Add it all together
+ tv->tv_sec = (((((days * 24) + hour) * 60) + min) * 60) + sec + SEC_1900_TO_1980;
+ tv->tv_usec = 0;
+ }
+ return 0;
+}
+
+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)
+ {
+ // Outside the ranges we can handle.
+ gem_error_to_errno(GEM_EBADRQ);
+ return -1;
+ }
+
+ time_t seconds = tv->tv_sec - SEC_1900_TO_1980;
+ int year = 0;
+ time_t ysec = SECONDS_IN_A_YEAR;
+ do
+ {
+ ysec = SECONDS_IN_A_YEAR;
+ if ((year % 4) == 0)
+ {
+ ysec += SECONDS_IN_A_DAY;
+ }
+ ++year;
+ seconds -= ysec;
+ } while (seconds >= 0);
+ --year;
+ seconds += ysec;
+
+ int minutes = seconds / 60;
+ int hours = minutes / 60;
+ int days = hours / 24;
+ int month = 0;
+ int leap = year % 4;
+ short ld = month_to_day[0];
+ for (int m = 0; m < 12; ++m)
+ {
+ short d = month_to_day[m + 1];
+ if (leap == 0 && m > 0) {d += 1;}
+ if (d > days)
+ {
+ month = m;
+ days -= ld;
+ break;
+ }
+ ld = d;
+ }
+
+ unsigned short date = (unsigned short)(year << 9);
+ date |= (unsigned short)((month + 1) << 5);
+ date |= (unsigned short)days;
+ unsigned short time = (unsigned short)((hours % 24) << 11);
+ time |= (unsigned short)((minutes % 60) << 5);
+ time |= (unsigned short)((seconds % 60) >> 1);
+
+ int err;
+ if ((err = trap1_2b(date)) < 0)
+ {
+ gem_error_to_errno(err);
+ return -1;
+ }
+ if ((err = trap1_2d(time)) < 0)
+ {
+ gem_error_to_errno(err);
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-isatty.c b/libgloss/m68k/atari/atari-isatty.c
new file mode 100644
index 0000000..5ab8b9a
--- /dev/null
+++ b/libgloss/m68k/atari/atari-isatty.c
@@ -0,0 +1,12 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include "atari-traps.h"
+
+int isatty(int fd)
+{
+ return fd == GSH_AUX ? 1 : 0;
+}
diff --git a/libgloss/m68k/atari/atari-kill.c b/libgloss/m68k/atari/atari-kill.c
new file mode 100644
index 0000000..cd1d160
--- /dev/null
+++ b/libgloss/m68k/atari/atari-kill.c
@@ -0,0 +1,16 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+
+/*
+ * kill -- go out via exit...
+ */
+int kill(int pid, int sig)
+{
+ _exit(sig);
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-link.c b/libgloss/m68k/atari/atari-link.c
new file mode 100644
index 0000000..e5a1485
--- /dev/null
+++ b/libgloss/m68k/atari/atari-link.c
@@ -0,0 +1,19 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <_ansi.h>
+#include <_syslist.h>
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include "libnosys/warning.h"
+
+int link(char *old, char *new)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(link)
diff --git a/libgloss/m68k/atari/atari-lseek.c b/libgloss/m68k/atari/atari-lseek.c
new file mode 100644
index 0000000..fa70325
--- /dev/null
+++ b/libgloss/m68k/atari/atari-lseek.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+ int newoff = GEM_EIHNDL;
+ if (fd >= 0 && fd <= 2)
+ {
+ // stdin, stdout and stderr just returns OK without doing anything.
+ return 0;
+ }
+ else if (fd >= 3)
+ {
+ // Any file descriptor
+ unsigned short bios_mode = 3; // invalid mode
+ switch (whence)
+ {
+ case SEEK_SET:
+ bios_mode = 0;
+ break;
+ case SEEK_CUR:
+ bios_mode = 1;
+ break;
+ case SEEK_END:
+ bios_mode = 2;
+ break;
+ }
+ newoff = trap1_42((unsigned int)offset, (unsigned short)fd, bios_mode);
+ }
+ if (newoff < 0)
+ {
+ gem_error_to_errno(newoff);
+ return (off_t)-1;
+ }
+ return (off_t)newoff;
+}
diff --git a/libgloss/m68k/atari/atari-mkdir.c b/libgloss/m68k/atari/atari-mkdir.c
new file mode 100644
index 0000000..fdff26b
--- /dev/null
+++ b/libgloss/m68k/atari/atari-mkdir.c
@@ -0,0 +1,21 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+int mkdir(const char *pathname, mode_t mode)
+{
+ // Ignore mode, it is not supported by the st.
+ int result = trap1_39(pathname);
+ if (result < 0)
+ {
+ gem_error_to_errno(result);
+ return -1;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-open.c b/libgloss/m68k/atari/atari-open.c
new file mode 100644
index 0000000..c610718
--- /dev/null
+++ b/libgloss/m68k/atari/atari-open.c
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.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
+
+// mode is ignored. Those kind of settings is not supported by the st.
+int open(const char *buf, int flags, int mode)
+{
+ int bios_handle = -1;
+ unsigned short bios_mode = (unsigned short)(flags & 0x3); // bits 0-1 the same for st and linux.
+ int create = flags & O_CREAT;
+ int append = flags & O_APPEND; // open doc says that seek end should be done before each write call. We assume that newlib handles that.
+ int excl = flags & O_EXCL; // File must be created by this call.
+ int trunc = flags & O_TRUNC; // File is forced to be created and thus truncated.
+
+ const char *bios_path = buf;
+ if (!trunc)
+ {
+ bios_handle = trap1_3d(bios_path, bios_mode);
+ }
+ if (bios_handle < 0 && (create || trunc))
+ {
+ unsigned short bios_attrib = 0;
+ bios_handle = trap1_3c(bios_path, bios_attrib);
+ }
+ else if (create && excl)
+ {
+ // We explicitly specified that file must be created, and it already existed, so error!
+ gem_error_to_errno(GEM_EACCDN);
+ // Close file.
+ trap1_3e((unsigned short)bios_handle);
+ return -1;
+ }
+
+ if (bios_handle >= 0 && append)
+ {
+ // Seek to end.
+ int new_file_pos = trap1_42(0, (unsigned short)bios_handle, 2);
+ if (new_file_pos < 0)
+ {
+ gem_error_to_errno(new_file_pos);
+ // Close file.
+ trap1_3e((unsigned short)bios_handle);
+ return -1;
+ }
+ }
+
+ if (bios_handle < 0)
+ {
+ gem_error_to_errno(bios_handle);
+ bios_handle = -1;
+ }
+ /*
+ If bios_handle is positive, then the low word is the gemdos handle, and the high word is zero.
+ */
+ return bios_handle;
+}
diff --git a/libgloss/m68k/atari/atari-read.c b/libgloss/m68k/atari/atari-read.c
new file mode 100644
index 0000000..cdff256
--- /dev/null
+++ b/libgloss/m68k/atari/atari-read.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+_READ_WRITE_RETURN_TYPE read(int fd, void *buf, size_t nbytes)
+{
+ int numRead = GEM_EIHNDL;
+ if (fd >= 0)
+ {
+ numRead = trap1_3f((unsigned short)fd, nbytes, buf);
+ if (numRead >= 0 && numRead < nbytes && fd == GSH_CONIN)
+ {
+ /*
+ When reading from console, the EOF character is "\n".
+ Atari tos do not return the EOF character, but posix read do.
+ So we assume that when the trap call have not reached the nbytes limit,
+ then it must have gotten an EOF.
+ So in that case we insert "\n" to the end of the buffer and increase lenth by 1.
+ */
+ ((char*)buf)[numRead] = '\n';
+ ++numRead;
+ /*
+ This is not enough however...
+ Atari tos requires a line feed to be "\r\n", and apparently, the "\r" is executed by tos
+ but not returned when reading. This is not a problem for us, but the "\n" as it is an EOF
+ is simply discarded and not executed. So we also need to send a "\n" to stdout to fully comply
+ with standard C behaviour.
+ */
+ trap1_40(GSH_CONOUT, 1, "\n");
+ }
+ }
+ if (numRead < 0)
+ {
+ gem_error_to_errno(numRead);
+ return -1;
+ }
+ return numRead;
+}
diff --git a/libgloss/m68k/atari/atari-readlink.c b/libgloss/m68k/atari/atari-readlink.c
new file mode 100644
index 0000000..23514a1
--- /dev/null
+++ b/libgloss/m68k/atari/atari-readlink.c
@@ -0,0 +1,20 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <_ansi.h>
+#include <_syslist.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include "libnosys/warning.h"
+
+int readlink(const char *path, char *buf, size_t bufsize)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(readlink)
diff --git a/libgloss/m68k/atari/atari-rename.c b/libgloss/m68k/atari/atari-rename.c
new file mode 100644
index 0000000..2d7e62d
--- /dev/null
+++ b/libgloss/m68k/atari/atari-rename.c
@@ -0,0 +1,20 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+int _rename(const char *old_filename, const char *new_filename)
+{
+ int err = trap1_56(old_filename, new_filename);
+ if (err < 0)
+ {
+ gem_error_to_errno(err);
+ return -1;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-rmdir.c b/libgloss/m68k/atari/atari-rmdir.c
new file mode 100644
index 0000000..647a5b6
--- /dev/null
+++ b/libgloss/m68k/atari/atari-rmdir.c
@@ -0,0 +1,20 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+int rmdir(const char *pathname)
+{
+ int result = trap1_3a(pathname);
+ if (result < 0)
+ {
+ gem_error_to_errno(result);
+ return -1;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-sbrk.c b/libgloss/m68k/atari/atari-sbrk.c
new file mode 100644
index 0000000..8839cc7
--- /dev/null
+++ b/libgloss/m68k/atari/atari-sbrk.c
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <errno.h>
+#include <_ansi.h>
+
+extern char *_HeapPtr;
+extern char *_HeapBottom;
+extern char *_HeapTop;
+
+char *sbrk(int nbytes)
+{
+ char *newheap = _HeapPtr + nbytes;
+ if (newheap > _HeapTop)
+ {
+ errno = ENOMEM;
+ return ((char *)-1);
+ }
+ char *retptr = _HeapPtr;
+ _HeapPtr = newheap;
+ return retptr;
+}
diff --git a/libgloss/m68k/atari/atari-stat.c b/libgloss/m68k/atari/atari-stat.c
new file mode 100644
index 0000000..a082732
--- /dev/null
+++ b/libgloss/m68k/atari/atari-stat.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+
+// I don't like this extern declaration here...
+extern int open(const char *buf, int flags, int mode);
+
+int stat(const char *path, struct stat *buf)
+{
+ int handle = open(path, 0, 0); // read only
+ if (handle >= 0)
+ {
+ int err = fstat(handle, buf);
+ close(handle);
+ handle = err;
+ }
+ if (handle < 0)
+ {
+ gem_error_to_errno(handle);
+ return -1;
+ }
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-symlink.c b/libgloss/m68k/atari/atari-symlink.c
new file mode 100644
index 0000000..49bc318
--- /dev/null
+++ b/libgloss/m68k/atari/atari-symlink.c
@@ -0,0 +1,19 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <_ansi.h>
+#include <_syslist.h>
+#include <errno.h>
+#include "atari-gem_errno.h"
+#include "libnosys/warning.h"
+
+int symlink(const char *path1, const char *path2)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(symlink)
diff --git a/libgloss/m68k/atari/atari-times.c b/libgloss/m68k/atari/atari-times.c
new file mode 100644
index 0000000..5c825c6
--- /dev/null
+++ b/libgloss/m68k/atari/atari-times.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <time.h>
+#include <sys/times.h>
+#include "atari-traps.h"
+#include "atari-gem_errno.h"
+
+// Defined and initialized in atari-crti.S
+extern unsigned int _atari_4ba_at_prg_start;
+
+
+// The memory location read is actually unsigned, so we cast that in the caller func.
+int read200hzMem(void)
+{
+ // 0x4ba is a privileged address only readable in supervisor mode.
+ return *((int*)0x4ba);
+}
+
+clock_t times(struct tms *buf)
+{
+ // Call callback in supervisor mode.
+ unsigned long long int ticks200hz = (unsigned long long int)trap14_26(read200hzMem);
+ unsigned long long int libcticks = (ticks200hz * CLK_TCK) / 200;
+
+ unsigned long long int processticks = ((unsigned long long int)_atari_4ba_at_prg_start * CLK_TCK) / 200;
+ processticks = libcticks - processticks;
+
+ // For a single threaded system like the atari, only tms_utime is meaningful.
+ buf->tms_utime = (clock_t)processticks;
+ buf->tms_stime = 0;
+ buf->tms_cutime = 0;
+ buf->tms_cstime = 0;
+ return (clock_t)libcticks;
+}
diff --git a/libgloss/m68k/atari/atari-tos.ld b/libgloss/m68k/atari/atari-tos.ld
new file mode 100644
index 0000000..6bbecc2
--- /dev/null
+++ b/libgloss/m68k/atari/atari-tos.ld
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k")
+OUTPUT_ARCH(m68k)
+INPUT (crti.o crtn.o crt0.o)
+
+SECTIONS
+{
+ /*
+ Start all addresses at 0.
+ That way, the relocation is a bit easier when converting to prg.
+ */
+ . = 0x0;
+ .text : {
+ *crti.o(.init)
+ *crtbegin.o(.init)
+ *crtend.o(.init)
+ *crtn.o(.init)
+ *crt0.o(.init)
+ *crti.o(.fini)
+ *crtbegin.o(.fini)
+ *crtend.o(.fini)
+ *crtn.o(.fini)
+ *(.text*)
+ *(.gnu.warning)
+ FILL(0x0000);
+ . = ALIGN(0x4);
+ }
+ /*
+ There is no rodata in a prg file, so we store all data in .data section.
+ */
+ .data BLOCK(0x4) : {
+ __DATA_SEGMENT__ = .;
+ *(.data*);
+ . = ALIGN(0x4);
+ *(.rodata*);
+ . = ALIGN(0x4);
+
+ *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*)
+ *(.eh_frame) *(.eh_frame.*)
+ *(.sframe) *(.sframe.*)
+ *(.gcc_except_table .gcc_except_table.*)
+ *(.gnu_extab*)
+
+ /*
+ All constructors
+ */
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*crtbegin?.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+
+ /*
+ All destructors
+ */
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+
+ FILL(0x0000);
+ . = ALIGN(0x4);
+ }
+ .bss BLOCK(0x4) : {
+ __BSS_SEGMENT__ = .;
+ *(.bss);
+ . = ALIGN(0x4);
+ *(COMMON);
+ . = ALIGN(0x4);
+ }
+ __BSS_SEGMENT_END = .;
+
+ /*
+ Atari Prg header.
+ We don't care what the address is for this.
+ */
+ .prgheader : {
+ SHORT(0x601a); /* Branch to start of the program (must be 0x601a, used as magic number!) */
+ LONG(__DATA_SEGMENT__); /* Length of the TEXT segment */
+ 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(0); /* Program flags */
+ SHORT(0); /* 0 = Relocation info present */
+ }
+
+ .note.gnu.build-id : { *(.note.gnu.build-id) }
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 (INFO) : { *(.comment); LINKER_VERSION; }
+ .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0.
+ */
+ /* DWARF 1. */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions. */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2. */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2. */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+
+ /* SGI/MIPS DWARF 2 extensions. */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ /* DWARF 3. */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+
+ /* DWARF 5. */
+ .debug_addr 0 : { *(.debug_addr) }
+ .debug_line_str 0 : { *(.debug_line_str) }
+ .debug_loclists 0 : { *(.debug_loclists) }
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_names 0 : { *(.debug_names) }
+ .debug_rnglists 0 : { *(.debug_rnglists) }
+ .debug_str_offsets 0 : { *(.debug_str_offsets) }
+ .debug_sup 0 : { *(.debug_sup) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) }
+
+} \ No newline at end of file
diff --git a/libgloss/m68k/atari/atari-tos.specs b/libgloss/m68k/atari/atari-tos.specs
new file mode 100644
index 0000000..00781c4
--- /dev/null
+++ b/libgloss/m68k/atari/atari-tos.specs
@@ -0,0 +1,10 @@
+# Copyright (C) 2025 Mikael Hildenborg
+# SPDX-License-Identifier: BSD-2-Clause
+
+# Atari gcc specs
+
+*link:
++ --emit-relocs --no-warn-rwx-segments -static --no-warn-execstack --undefined=__errno -T atari-tos.ld
+
+*lib:
++ -latari-tos
diff --git a/libgloss/m68k/atari/atari-traps.c b/libgloss/m68k/atari/atari-traps.c
new file mode 100644
index 0000000..c387ff1
--- /dev/null
+++ b/libgloss/m68k/atari/atari-traps.c
@@ -0,0 +1,311 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "atari-traps.h"
+
+// Store stack pointer in a3, that by Atari documentation will be left untouched by the trap call.
+#define TRAP_BEGIN "move.l %%a7, %%a3\n\t"
+// Make trap call and then restore the stack pointer from the stored value in a3
+#define TRAP_FUNC(num, func) "move.w #" #func ", %%a7@-\n\ttrap #" #num "\n\tmove.l %%a3, %%a7\n\t"
+// Registers d1,d2 and a0, a1, a2 may be affected by trap #1 calls. Register a3 is used to store/restore a7
+#define CLOBBER_REG "d1", "d2", "a0", "a1", "a2", "a3"
+
+unsigned int trap1_e(unsigned short bios_drive)
+{
+ register unsigned int bios_mounted_drives asm ("d0") = 0;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0xe)
+ : "=r" (bios_mounted_drives)
+ : "r" (bios_drive)
+ : CLOBBER_REG);
+ return bios_mounted_drives;
+}
+
+unsigned short trap1_19(void)
+{
+ register unsigned short bios_drive asm ("d0") = 0;
+ __asm__ volatile (
+ TRAP_BEGIN
+ TRAP_FUNC(1, 0x19)
+ : "=r" (bios_drive)
+ :
+ : CLOBBER_REG);
+ return bios_drive;
+}
+
+unsigned short trap1_2a(void)
+{
+ register unsigned short date asm ("d0") = 0;
+ __asm__ volatile (
+ TRAP_BEGIN
+ TRAP_FUNC(1, 0x2a)
+ : "=r" (date)
+ :
+ : CLOBBER_REG);
+ return date;
+}
+
+int trap1_2b(unsigned short date)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x2b)
+ : "=r" (result)
+ :"r" (date)
+ : CLOBBER_REG);
+ return result;
+}
+
+unsigned short trap1_2c(void)
+{
+ register unsigned short time asm ("d0") = 0;
+ __asm__ volatile (
+ TRAP_BEGIN
+ TRAP_FUNC(1, 0x2c)
+ : "=r" (time)
+ :
+ : CLOBBER_REG);
+ return time;
+}
+
+int trap1_2d(unsigned short time)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x2d)
+ : "=r" (result)
+ :"r" (time)
+ : CLOBBER_REG);
+ return result;
+}
+
+
+struct DTA* trap1_2f(unsigned short bios_handle)
+{
+ register struct DTA* result asm ("d0") = (struct DTA*)-1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x2f)
+ : "=r" (result)
+ : "r" (bios_handle)
+ : CLOBBER_REG);
+ return result;
+}
+
+
+int trap1_39(const char* bios_path)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x39)
+ : "=r" (result)
+ : "r" (bios_path)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_3a(const char* bios_path)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x3a)
+ : "=r" (result)
+ : "r" (bios_path)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_3b(const char* bios_path)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x3b)
+ : "=r" (result)
+ : "r" (bios_path)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_3c(const char* bios_path, unsigned short bios_attrib)
+{
+ register int bios_handle asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %2, %%a7@-\n\t"
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x3c)
+ : "=r" (bios_handle)
+ : "r" (bios_path), "r" (bios_attrib)
+ : CLOBBER_REG);
+ return bios_handle;
+}
+
+int trap1_3d(const char* bios_path, unsigned short bios_mode)
+{
+ register int bios_handle asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %2, %%a7@-\n\t"
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x3d)
+ : "=r" (bios_handle)
+ : "r" (bios_path), "r" (bios_mode)
+ : CLOBBER_REG);
+ return bios_handle;
+}
+
+int trap1_3e(unsigned short bios_handle)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x3e)
+ : "=r" (result)
+ : "r" (bios_handle)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_3f(unsigned short bios_handle, int length, void* buf)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %3, %%a7@-\n\t"
+ "move.l %2, %%a7@-\n\t"
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x3f)
+ : "=r" (result)
+ : "r" (bios_handle), "r" (length), "r" (buf)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_40(unsigned short bios_handle, int length, const void* buf)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %3, %%a7@-\n\t"
+ "move.l %2, %%a7@-\n\t"
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x40)
+ : "=r" (result)
+ : "r" (bios_handle), "r" (length), "r" (buf)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_41(const char* bios_path)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x41)
+ : "=r" (result)
+ : "r" (bios_path)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_42(unsigned int file_position, unsigned short bios_handle, unsigned short bios_mode)
+{
+ register int new_position asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %3, %%a7@-\n\t"
+ "move.w %2, %%a7@-\n\t"
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x42)
+ : "=r" (new_position)
+ : "r" (file_position), "r" (bios_handle), "r" (bios_mode)
+ : CLOBBER_REG);
+ return new_position;
+}
+
+int trap1_47(char* buf, unsigned short bios_drive)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.w %2, %%a7@-\n\t"
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x47)
+ : "=r" (result)
+ : "r" (buf), "r" (bios_drive)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_4b(unsigned short mode, const char* file_name, const char* cmdline, const char* envstring)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %4, %%a7@-\n\t"
+ "move.l %3, %%a7@-\n\t"
+ "move.l %2, %%a7@-\n\t"
+ "move.w %1, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x4b)
+ : "=r" (result)
+ : "r" (mode), "r" (file_name), "r" (cmdline), "r" (envstring)
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap1_56(const char* oldname, const char* newname)
+{
+ register int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %2, %%a7@-\n\t"
+ "move.l %1, %%a7@-\n\t"
+ "move.w #0, %%a7@-\n\t"
+ TRAP_FUNC(1, 0x56)
+ : "=r" (result)
+ : "r" (oldname), "r" (newname)
+ : CLOBBER_REG);
+ return result;
+}
+
+unsigned int trap14_11(void)
+{
+ register unsigned int result asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ TRAP_FUNC(14, 0x11)
+ : "=r" (result)
+ :
+ : CLOBBER_REG);
+ return result;
+}
+
+int trap14_26(int (*callback)(void))
+{
+ register int callback_return asm ("d0") = -1;
+ __asm__ volatile (
+ TRAP_BEGIN
+ "move.l %1, %%a7@-\n\t"
+ TRAP_FUNC(14, 0x26)
+ : "=r" (callback_return)
+ : "r" (callback)
+ : CLOBBER_REG);
+ return callback_return;
+}
diff --git a/libgloss/m68k/atari/atari-traps.h b/libgloss/m68k/atari/atari-traps.h
new file mode 100644
index 0000000..01edb70
--- /dev/null
+++ b/libgloss/m68k/atari/atari-traps.h
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifndef TRAPS_DEFINED
+#define TRAPS_DEFINED
+
+#include "atari-gem_basepage.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ Only trap calls used by atari libgloss is included here.
+*/
+
+unsigned int trap1_e(unsigned short bios_drive);
+
+// Returned drive starts at 0 for A
+unsigned short trap1_19(void);
+
+unsigned short trap1_2a(void);
+
+int trap1_2b(unsigned short date);
+
+unsigned short trap1_2c(void);
+
+int trap1_2d(unsigned short time);
+
+struct DTA* trap1_2f(unsigned short bios_handle);
+
+int trap1_39(const char* bios_path);
+
+int trap1_3a(const char* bios_path);
+
+int trap1_3b(const char* bios_path);
+
+int trap1_3c(const char* bios_path, unsigned short bios_attrib);
+
+int trap1_3d(const char* bios_path, unsigned short bios_mode);
+
+#define GSH_BIOSCON 0xFFFF
+#define GSH_BIOSAUX 0xFFFE
+#define GSH_BIOSPRN 0xFFFD
+#define GSH_BIOSMIDIIN 0xFFFC
+#define GSH_BIOSMIDIOUT 0xFFFB
+#define GSH_CONIN 0x00
+#define GSH_CONOUT 0x01
+#define GSH_AUX 0x02
+#define GSH_PRN 0x03
+
+int trap1_3e(unsigned short bios_handle);
+
+int trap1_3f(unsigned short bios_handle, int length, void* buf);
+
+int trap1_40(unsigned short bios_handle, int length, const void* buf);
+
+int trap1_41(const char* bios_path);
+
+int trap1_42(unsigned int file_position, unsigned short bios_handle, unsigned short bios_mode);
+
+// bios_drive 0 is default drive, and 1 and upwards is A...
+int trap1_47(char* buf, unsigned short bios_drive);
+
+#define PE_LOADGO 0
+#define PE_LOAD 3
+#define PE_GO 4
+#define PE_BASEPAGE 5
+#define PE_GOTHENFREE 6
+int trap1_4b(unsigned short mode, const char* file_name, const char* cmdline, const char* envstring);
+
+int trap1_56(const char* oldname, const char* newname);
+
+unsigned int trap14_11(void);
+
+int trap14_26(int (*callback)(void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TRAPS_DEFINED
diff --git a/libgloss/m68k/atari/atari-unlink.c b/libgloss/m68k/atari/atari-unlink.c
new file mode 100644
index 0000000..cacafcf
--- /dev/null
+++ b/libgloss/m68k/atari/atari-unlink.c
@@ -0,0 +1,19 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include "atari-gem_errno.h"
+#include "atari-traps.h"
+
+int unlink(char *path)
+{
+ int err = trap1_41(path);
+ if (err < 0)
+ {
+ gem_error_to_errno(err);
+ return -1;
+ }
+ return 0;
+}
diff --git a/libgloss/m68k/atari/atari-wait.c b/libgloss/m68k/atari/atari-wait.c
new file mode 100644
index 0000000..9464193
--- /dev/null
+++ b/libgloss/m68k/atari/atari-wait.c
@@ -0,0 +1,16 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include "config.h"
+#include <errno.h>
+#include "libnosys/warning.h"
+
+int wait(int *status)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+stub_warning(wait)
diff --git a/libgloss/m68k/atari/atari-write.c b/libgloss/m68k/atari/atari-write.c
new file mode 100644
index 0000000..9d6e59e
--- /dev/null
+++ b/libgloss/m68k/atari/atari-write.c
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2025 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <unistd.h>
+#include <_ansi.h>
+#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;
+ if (fd >= 0)
+ {
+ if (fd == 2)
+ {
+ 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);
+ }
+ }
+ if (numWritten < 0)
+ {
+ gem_error_to_errno(numWritten);
+ return -1;
+ }
+ return numWritten;
+}