Added target m68k-atari-elf supporting Atari 16/32bit TOS systems.

This commit is contained in:
Mikael Hildenborg authored and Jeff Johnston committed 2025-08-07 13:59:52 -04:00
1 parent 45f9b48413
commit cac47030fb
45 files changed
+2043 -14

No files matched your search

+4
View File
@@ -497,3 +497,7 @@ and need not follow the licensing terms described here, provided that
the new terms are clearly indicated on the first page of each file where
they apply.
(26) - Atari 16/32 bit TOS (m68k-atari-elf target)
Copyright (C) 2025 Mikael Hildenborg
SPDX-License-Identifier: BSD-2-Clause
+4
View File
@@ -1570,3 +1570,7 @@ Software.
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(58) - Atari 16/32 bit TOS (m68k-atari-elf target)
Copyright (C) 2025 Mikael Hildenborg
SPDX-License-Identifier: BSD-2-Clause
+12 -12
View File
@@ -2857,6 +2857,11 @@ case "${target}" in
ac_config_files="$ac_config_files m68k/Makefile"
subdirs="$subdirs m68k"
case "${target}" in
m68k-atari-*)
config_libnosys=false
;;
esac
;;
hppa*-*-pro*)
ac_config_files="$ac_config_files pa/Makefile"
@@ -5461,16 +5466,18 @@ case "${target}" in
MIPS_SCRIPT_LIST="dve idt jmr3904app jmr3904dram jmr3904dram-java jmr3904app-java sde32 sde64 mti32 mti64 mti64_n32 mti64_64"
MIPS_BSP_LIST="libdve.a libidt.a libjmr3904.a"
;;
mipsisa32-*-* | mipsisa32el-*-* | \
mipsisa32r2-*-* | mipsisa32r2el-*-* | \
mipsisa64*-*-*)
MIPS_CRT0="crt0_cfe.o crt0_cygmon.o crt0.o"
MIPS_SCRIPT_LIST="idt32 idt64 cfe"
MIPS_BSP_LIST="libcygmon.a libidt.a libcfe.a"
;;
mips*-lsi*-*)
MIPS_PART_SPECIFIC_OBJ="entry.o"
MIPS_SCRIPT_LIST="lsi"
MIPS_BSP_LIST=liblsi.a
;;
mips*-mti*-* | mips*-img*-*)
MIPS_CRT0="crt0.o"
MIPS_SCRIPT_LIST="idt nullmon mti32 mti64_n32 mti64_64 uhi32 uhi64_64 uhi64_n32 malta32-yamon bootcode"
MIPS_BSP_LIST="libidt.a libnullmon.a libuhi.a libyamon.a libhal.a libcm3_impl.a"
;;
mips64vr5*-*-*)
MIPS_PART_SPECIFIC_OBJ="vr5xxx.o cma101.o"
MIPS_PART_SPECIFIC_DEFINES=-DR5000
@@ -5482,13 +5489,6 @@ case "${target}" in
MIPS_SCRIPT_LIST="ddb ddb-kseg0 nullmon"
MIPS_BSP_LIST="libpmon.a libnullmon.a"
;;
mipsisa32-*-* | mipsisa32el-*-* | \
mipsisa32r2-*-* | mipsisa32r2el-*-* | \
mipsisa64*-*-*)
MIPS_CRT0="crt0_cfe.o crt0_cygmon.o crt0.o"
MIPS_SCRIPT_LIST="idt32 idt64 cfe"
MIPS_BSP_LIST="libcygmon.a libidt.a libcfe.a"
;;
mips*)
MIPS_CRT0="crt0_cfe.o crt0.o"
MIPS_PART_SPECIFIC_OBJ="vr4300.o cma101.o"
+5
View File
@@ -79,6 +79,11 @@ case "${target}" in
fido-*-* | m68*-*-*)
AC_CONFIG_FILES([m68k/Makefile])
subdirs="$subdirs m68k"
case "${target}" in
m68k-atari-*)
config_libnosys=false
;;
esac
;;
hppa*-*-pro*)
AC_CONFIG_FILES([pa/Makefile])
+76 -1
View File
@@ -31,6 +31,7 @@ program_transform_name = @program_transform_name@
bindir = @bindir@
libdir = @libdir@
tooldir = $(exec_prefix)/$(target_alias)
specsdir = $(libdir)/gcc/$(target_alias)
# Multilib support variables.
# TOP is used instead of MULTI{BUILD,SRC}TOP.
@@ -71,6 +72,14 @@ OBJS = close.o fstat.o getpid.o isatty.o kill.o \
# What targets to build for. This is a list of {m68k,cf}
TARGET = @M68K_TARGET@
# Check if vendor is atari and if so, adjust the target
target_vendor = @target_vendor@
ifdef target_vendor
ifeq ($(target_vendor), atari)
TARGET := $(TARGET)_$(target_vendor)
endif
endif
# Here is all of the simulator stuff
SIM_SCRIPTS = sim.ld
SIM_LDFLAGS = -Tsim.ld
@@ -197,6 +206,29 @@ UNHOSTED_OBJS= unhosted-close.o unhosted-fstat.o \
HOSTED_BSP= libhosted.a
HOSTED_OBJS= hosted-gdb.o hosted-exit.o $(patsubst un%,%,$(UNHOSTED_OBJS))
#
# here's all the atari stuff
#
ATARI_SRC_LIBGLOSS := atari-environ.c atari-execve.c atari-link.c atari-times.c \
atari-wait.c atari-traps.c atari-gem_errno.c atari-close.c atari-fstat.c \
atari-getpid.c atari-isatty.c atari-kill.c atari-lseek.c atari-open.c atari-read.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_OBJS_LIBGLOSS := $(foreach source,$(ATARI_SRC_LIBGLOSS),$(basename $(source)).o)
ATARI_CRTI := atari-crti.o
ATARI_CRTN := atari-crtn.o
ATARI_CRT0 := atari-crt0.o
ATARI_NEWLIB := libatari-tos.a
ATARI_LINK := atari-tos.ld
ATARI_SPECS := atari-tos.specs
ATARI_ASFLAGS := -Xassembler --register-prefix-optional
# Host specific makefile fragment comes in here.
@host_makefile_frag@
@@ -206,9 +238,19 @@ all :: $(TARGET:%=all_%)
# here's where we build the board support packages for each target
#
.PHONY: all_m68k_atari
all_m68k_atari :: $(ATARI_CRTI) $(ATARI_CRTN) $(ATARI_CRT0) $(ATARI_NEWLIB)
.PHONY: all_cf_atari
all_cf_atari :: all_m68k_atari
.PHONY: all_fido_atari
all_fido_atari :: all_m68k_atari
.PHONY: all_m68k
all_m68k :: ${SIM_CRT0} ${SIM_BSP} ${CRT0} ${BCC_BSP} ${IDP_BSP} \
${IDPGDB_BSP} ${MVME135_BSP} ${MVME162_BSP}
.PHONY: all_cf
all_cf :: ${CF_CRT0S} ${CF_BSP} ${CF_SCRIPTS} ${HOSTED_BSP} ${UNHOSTED_BSP}
@@ -240,6 +282,23 @@ ${MVME162_BSP}: $(OBJS) ${MVME162_OBJS}
${AR} ${ARFLAGS} $@ $(OBJS) ${MVME162_OBJS}
${RANLIB} $@
# build atari library
$(ATARI_CRT0): atari/atari-crt0.S
$(CC) $(CFLAGS_FOR_TARGET) $(CFLAGS) $(ATARI_ASFLAGS) -c $< -o $@
$(ATARI_CRTI): atari/atari-crti.S
$(CC) $(CFLAGS_FOR_TARGET) $(CFLAGS) $(ATARI_ASFLAGS) -c $< -o $@
$(ATARI_CRTN): atari/atari-crtn.S
$(CC) $(CFLAGS_FOR_TARGET) $(CFLAGS) $(ATARI_ASFLAGS) -c $< -o $@
%.o: atari/%.c
$(CC) $(CFLAGS_FOR_TARGET) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(ATARI_NEWLIB): $(ATARI_OBJS_LIBGLOSS)
$(AR) $(ARFLAGS) $@ $^
$(RANLIB) $@
# build hosted library
$(HOSTED_OBJS): hosted%.o: io%.c io.h
$(CC) $(CFLAGS_FOR_TARGET) $(CFLAGS) -DHOSTED=1 $(INCLUDES) -c $< -o $@
@@ -409,6 +468,22 @@ unlink.o: $(srcdir)/../unlink.c
install:: $(TARGET:%=install_%)
.PHONY:install_m68k_atari
install_m68k_atari::
${mkinstalldirs} ${DESTDIR}${tooldir}/lib${MULTISUBDIR}
$(INSTALL_PROGRAM) $(ATARI_CRTI) $(DESTDIR)$(tooldir)/lib${MULTISUBDIR}/crti.o
$(INSTALL_PROGRAM) $(ATARI_CRTN) $(DESTDIR)$(tooldir)/lib${MULTISUBDIR}/crtn.o
$(INSTALL_PROGRAM) $(ATARI_CRT0) $(DESTDIR)$(tooldir)/lib${MULTISUBDIR}/crt0.o
$(INSTALL_PROGRAM) $(ATARI_NEWLIB) $(DESTDIR)$(tooldir)/lib${MULTISUBDIR}/$(ATARI_NEWLIB)
$(INSTALL_DATA) ${srcdir}/atari/$(ATARI_LINK) $(DESTDIR)$(tooldir)/lib${MULTISUBDIR}/$(ATARI_LINK)
$(INSTALL_DATA) ${srcdir}/atari/$(ATARI_SPECS) $(DESTDIR)$(specsdir)/specs
.PHONY:install_cf_atari
install_cf_atari:: install_m68k_atari
.PHONY:install_fido_atari
install_fido_atari:: install_m68k_atari
.PHONY:install_m68k
install_m68k::
${mkinstalldirs} ${DESTDIR}${tooldir}/lib${MULTISUBDIR}
@@ -473,7 +548,7 @@ install_io::
@target_makefile_frag@
clean mostlyclean:
rm -f a.out core *.i *~ *.a *.o *-test *.srec *.dis *.x *.map
rm -f a.out core *.i *~ *.a *.o *-test *.srec *.dis *.x *.map *.ld
distclean maintainer-clean realclean: clean
rm -f Makefile
+35
View File
@@ -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.
+49
View File
@@ -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;
}
+20
View File
@@ -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)
+23
View File
@@ -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;
}
+74
View File
@@ -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
+90
View File
@@ -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
+24
View File
@@ -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
+8
View File
@@ -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;
+16
View File
@@ -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)
+19
View File
@@ -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)
+51
View File
@@ -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;
}
+58
View File
@@ -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
+95
View File
@@ -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;
}
}
+57
View File
@@ -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
+32
View File
@@ -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;
}
+52
View File
@@ -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;
}
+11
View File
@@ -0,0 +1,11 @@
/*
Copyright (C) 2025 Mikael Hildenborg
SPDX-License-Identifier: BSD-2-Clause
*/
#include <_ansi.h>
int getpid(void)
{
return 1;
}
+135
View File
@@ -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;
}
+12
View File
@@ -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;
}
+16
View File
@@ -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;
}
+19
View File
@@ -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)
+43
View File
@@ -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;
}
+21
View File
@@ -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;
}
+78
View File
@@ -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;
}
+44
View File
@@ -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;
}
+20
View File
@@ -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)
+20
View File
@@ -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;
}
+20
View File
@@ -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;
}
+24
View File
@@ -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;
}
+29
View File
@@ -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;
}
+19
View File
@@ -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)
+37
View File
@@ -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;
}
+160
View File
@@ -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) }
}
+10
View File
@@ -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
+311
View File
@@ -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;
}
+84
View File
@@ -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
+19
View File
@@ -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;
}
+16
View File
@@ -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)
+87
View File
@@ -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;
}
+4 -1
View File
@@ -252,7 +252,6 @@ case "${host_cpu}" in
mips*)
machine_dir=mips
libm_machine_dir=mips
newlib_cflags="${newlib_cflags}"
;;
mmix)
;;
@@ -769,6 +768,10 @@ newlib_cflags="${newlib_cflags} -DCLOCK_PROVIDED -DMALLOC_PROVIDED -DEXIT_PROVID
newlib_cflags="${newlib_cflags} -DHAVE_RENAME -DHAVE_SYSTEM -DMISSING_SYSCALL_NAMES"
syscall_dir=
;;
m68k-atari-elf)
newlib_cflags="${newlib_cflags} -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
syscall_dir=
;;
mcore-*-*)
syscall_dir=syscalls
;;