#export CPU = z80
#export TARGET = dragon
#export CPU = 6809
-export TARGET = tgl6502
-export CPU = 6502
+#export TARGET = tgl6502
+#export CPU = 6502
+export TARGET=atarist
+export CPU = 68000
export VERSION = "0.1"
export SUBVERSION = "ac1"
export CROSS_CC_FONT=--constseg FONT
export CROSS_CC_VIDEO=--codeseg VIDEO
export ASOPTS=-plosff
+export ASMEXT = .s
export BINEXT = .rel
+export BITS=16
#
# Adjust this as needed for your platform (or contribute a script
# to look in the usual places !)
# 6502 we need a real SEG3 to make it fit
export CROSS_CC_SEG3=--code-name SEG3
export CROSS_CC_SEGDISC=--code-name DISCARD --rodata-name DISCARDDATA
+export ASMEXT = .s
export BINEXT = .o
-else
+export BITS=16
+else ifeq ($(CPU),6809)
export CROSS_AS=m6809-unknown-as
export CROSS_LD=lwlink
export CROSS_CC = m6809-unknown-gcc
export CROSS_CC_SEGDISC=-mcode-section=.discard -mfar-code-page=3
export CROSS_CC_VIDEO=-mcode-section=.video -mdata-section=.videodata -mfar-code-page=4
export ASOPTS=
+export ASMEXT = .s
export BINEXT = .o
+export BITS=16
+else
+export CROSS_LD=m68k-linux-gnu-ld
+export CROSS_CC = m68k-linux-gnu-gcc
+export CROSS_CCOPTS=-c -fno-builtin -Wall -Os -m68000 -mshort -I$(ROOT_DIR)/cpu-68000 -I$(ROOT_DIR)/platform-$(TARGET) -I$(ROOT_DIR)/include
+export CROSS_AS=$(CROSS_CC) $(CROSS_CCOPTS) #-Wa,-M
+export CROSS_CC_SEG1=
+export CROSS_CC_SEG2=
+export CROSS_CC_SEG3=
+# Fixme: we should split discard off
+export CROSS_CC_SEGDISC=
+export CROSS_CC_VIDEO=
+export ASOPTS=
+export ASMEXT = .S
+export BINEXT = .o
+export BITS=32
endif
ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
# Keep all of the syscalls in C2SRCS
#
C2SRCS = syscall_proc.c syscall_fs.c
-C2SRCS += syscall_fs2.c syscall_other.c syscall_exec.c process.c
-C2SRCS += simple.c single.c bank16k.c bank16k_low.c bank32k.c bankfixed.c
+C2SRCS += syscall_fs2.c syscall_other.c syscall_exec$(BITS).c process.c malloc.c
+C2SRCS += simple.c single.c bank16k.c bank16k_low.c bank32k.c bankfixed.c flat.c
#
# Drop some bits into CODE3 so the 6502 banks fit nicely. May well
# need to do this on Z80 as well
CFONTSRCS = font4x6.c font6x8.c font8x8.c
-ASRCS = lowlevel-$(CPU).s usermem_std-$(CPU).s
+ASRCS = lowlevel-$(CPU)$(ASMEXT) usermem_std-$(CPU)$(ASMEXT)
CDOBJS = $(CDSRCS:.c=$(BINEXT))
C3OBJS = $(C3SRCS:.c=$(BINEXT))
CFONTOBJS = $(CFONTSRCS:.c=$(BINEXT))
CVIDEOOBJS = $(CVIDEOSRCS:.c=$(BINEXT))
-AOBJS = $(ASRCS:.s=$(BINEXT))
+AOBJS = $(ASRCS:$(ASMEXT)=$(BINEXT))
CSRCS = $(CDSRCS) $(C1SRCS) $(C2SRCS) $(C3SRCS) $(CFONTSRCS) $(CVIDEOSRCS)
COBJS = $(CDOBJS) $(C1OBJS) $(C2OBJS) $(C3OBJS) $(CFONTOBJS) $(CVIDEOOBJS)
OBJS = $(COBJS) $(AOBJS)
-
-JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.s) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(CSRCS:.c=.o) $(ASRCS:.s=.rst)
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.s) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:$(ASMEXT)=.lst) $(ASRCS:$(ASMEXT)=.sym) $(CSRCS:.c=.rst) $(CSRCS:.c=.o) $(ASRCS:$(ASMEXT)=.rst)
all: fuzix.bin
.SUFFIXES: # delete the default suffixes
-.SUFFIXES: .c .s .rel
+.SUFFIXES: .c .s .rel .S
usermem_std-z180.rel: usermem_std-z180.s usermem_std-z80.s
$(CVIDEOOBJS): %$(BINEXT): %.c
$(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_VIDEO) $<
-$(AOBJS): %$(BINEXT): %.s
+$(AOBJS): %$(BINEXT): %$(ASMEXT)
$(CROSS_AS) $(ASOPTS) -o $*$(BINEXT) $<
version.c: makeversion
--- /dev/null
+typedef unsigned long uint32_t;
+typedef signed long int32_t;
+typedef unsigned short uint16_t;
+typedef signed short int16_t;
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef unsigned long size_t;
+
+typedef uint16_t irqflags_t;
+
+typedef int32_t arg_t;
+typedef uint32_t uarg_t; /* Holds arguments */
+typedef uint32_t usize_t; /* Largest value passed by userspace */
+typedef int32_t susize_t;
+typedef uint32_t uaddr_t;
+
+extern void ei(void);
+extern irqflags_t di(void);
+extern void irqrestore(irqflags_t f);
+
+extern void *memcpy(void *, const void *, size_t);
+extern void *memset(void *, int, size_t);
+extern int memcmp(const void *, const void *, size_t);
+extern size_t strlen(const char *);
+
+#define EMAGIC 0x4C /* Header of executable (JMP) */
+#define EMAGIC_2 0x38 /* SEC BCS foo */
+
+#define brk_limit() ((udata.u_syscall_sp) - 512)
+
+#define staticfast
+
+/* User's structure for times() system call */
+typedef unsigned long clock_t;
+
+typedef struct {
+ uint32_t low;
+ uint32_t high;
+} time_t;
+
+typedef union { /* this structure is endian dependent */
+ clock_t full; /* 32-bit count of ticks since boot */
+ struct {
+ uint16_t high;
+ uint16_t low; /* 16-bit count of ticks since boot */
+ } h;
+} ticks_t;
+
+/* We don't need any banking bits really */
+#define CODE1
+#define CODE2
+#define COMMON
+#define VIDEO
+#define DISCARD
+
+/* Sane behaviour for unused parameters */
+#define used(x)
+
+#define __fastcall__
+
+/* Our udata is handled slightly quirkily - use a register global */
+
+register struct u_data *udata_ptr asm ("a5");
+
+#define udata (*udata_ptr)
for(;;)
{
udata.u_count = DIR_LEN;
- udata.u_base =(char *)&curentry;
+ udata.u_base =(unsigned char *)&curentry;
udata.u_sysio = true;
readi(wd, 0);
}
udata.u_count = DIR_LEN;
- udata.u_base = (char*)&curentry;
+ udata.u_base = (unsigned char*)&curentry;
udata.u_sysio = true;
writei(wd, 0);
#include <stdbool.h>
-extern char *cmdline;
+extern unsigned char *cmdline;
#define BOOTLINE_LEN 6
-extern char bootline[BOOTLINE_LEN];
+extern unsigned char bootline[BOOTLINE_LEN];
extern struct u_block ub;
+
+/* Some platforms define udata for things like register globals */
+#ifndef udata
extern struct u_data udata;
+#endif
extern uint16_t maxproc; /* Actual max number of processes */
extern uint16_t ramsize;
typedef int (*dev_init_t)(void);
typedef int (*dev_open_t)(uint8_t minor, uint16_t flag);
typedef int (*dev_close_t)(uint8_t minor);
-typedef int (*dev_ioctl_t)(uint8_t minor, uint16_t request, char *data); // note: data is in userspace
+typedef int (*dev_ioctl_t)(uint8_t minor, uarg_t request, char *data); // note: data is in userspace
typedef struct devsw {
dev_open_t dev_open; /* The routines for reading, etc */
// the system call dispatch table
#define FUZIX_SYSCALL_COUNT 63
-typedef int16_t (*syscall_t)(void);
+typedef arg_t (*syscall_t)(void);
extern const syscall_t syscall_dispatch[FUZIX_SYSCALL_COUNT];
#endif
typedef struct s_queue {
unsigned char *q_base; /* Pointer to data */
unsigned char *q_head; /* Pointer to addr of next char to read. */
- char *q_tail; /* Pointer to where next char to insert goes. */
+ unsigned char *q_tail; /* Pointer to where next char to insert goes. */
int q_size; /* Max size of queue */
int q_count; /* How many characters presently in queue */
int q_wakeup; /* Threshold for waking up processes waiting on queue */
void * p_wait; /* Address of thing waited for */
uint16_t p_page; /* Page mapping data */
uint16_t p_page2; /* It's really four bytes for the platform */
+#ifdef udata
+ struct u_data *p_udata; /* Udata pointer for platforms using dynamic udata */
+#endif
/* Update kernel.def if you change fields above this comment */
/* Everything below here is overlaid by time info at exit */
uint16_t p_priority; /* Process priority */
int (*u_sigvec[NSIGS])(int); /* Array of signal vectors */
/**** If you change this top section, also update offsets in "kernel.def" ****/
- char * u_base; /* Source or dest for I/O */
+ uint8_t * u_base; /* Source or dest for I/O */
usize_t u_count; /* Amount for I/O */
off_t u_offset; /* Place in file for I/O */
struct blkbuf *u_buf;
CODE2 int stcpy(inoptr ino, char *buf);
CODE2 bool rargs (char **userspace_argv, struct s_argblk *argbuf);
CODE2 char **wargs(char *userspace_ptr, struct s_argblk *argbuf, int *cnt);
-CODE2 extern int16_t unlinki(inoptr ino, inoptr pino, char *fname);
+CODE2 extern arg_t unlinki(inoptr ino, inoptr pino, char *fname);
/* timer.c */
CODE2 void rdtime(time_t *tloc);
CODE2 void pagemap_add(uint8_t page); /* FIXME: may need a page type for big boxes */
CODE2 void pagemap_free(ptptr p);
CODE2 int pagemap_alloc(ptptr p);
-CODE2 int pagemap_realloc(uint16_t p);
-CODE2 uint16_t pagemap_mem_used(void);
+CODE2 int pagemap_realloc(usize_t p);
+CODE2 uaddr_t pagemap_mem_used(void);
CODE2 uint8_t *swapout_prepare_uarea(ptptr p);
CODE2 uint8_t *swapin_prepare_uarea(ptptr p);
CODE2 void map_init(void);
CODE2 uint8_t rtc_secs(void);
/* Will need a uptr_t eventually */
-extern uint16_t ramtop; /* Note: ramtop must be in common in some cases */
+extern uaddr_t ramtop; /* Note: ramtop must be in common in some cases */
CODE2 extern void platform_interrupt(void);
COMMON void invalidate_cache(uint16_t page);
COMMON void flush_cache(ptptr p);
CODE2 arg_t _getpgrp(void); /* FUZIX system call 61 */
CODE2 arg_t _sched_yield(void); /* FUZIX system call 62 */
+#if defined(CONFIG_32BIT)
+#include "kernel32.h"
+#endif
+
#endif /* __FUZIX__KERNEL_DOT_H__ */
--- /dev/null
+/*
+ * Functionality only used in the 32bit ports
+ */
+
+#ifndef __FUZIX__KERNEL_32_H
+#define __FUZIX__KERNEL_32_H
+
+/* malloc.c */
+extern void *kmalloc(size_t);
+extern void *kzalloc(size_t);
+extern void kfree(void *);
+extern void kmemaddblk(void *, size_t);
+extern unsigned long kmemavail(void);
+extern unsigned long kmemused(void);
+
+/* flat.c */
+extern void pagemap_switch(ptptr p);
+extern void *pagemap_base(void);
+#define PROGLOAD (uint32_t)pagemap_base()
+extern arg_t _memalloc(void);
+extern arg_t _memfree(void);
+extern uint32_t ugetl(void *uaddr, int *err);
+extern int uputl(uint32_t val, void *uaddr);
+
+/* Dummy */
+#define PROGTOP 0
+
+#endif
struct oft *oftp;
udata.u_sysio = false; /* I/O to user data space */
- udata.u_base = (char *) udata.u_argn1; /* buf */
+ udata.u_base = (unsigned char *) udata.u_argn1; /* buf */
udata.u_count = (susize_t) udata.u_argn2; /* nbytes */
if ((ino = getinode(udata.u_argn)) == NULLINODE) {
#include <kdata.h>
p_tab *init_process;
-char *cmdline = (char *) CMDLINE;
-char bootline[BOOTLINE_LEN];
+unsigned char *cmdline = (unsigned char *) CMDLINE;
+unsigned char bootline[BOOTLINE_LEN];
uint16_t ramsize, procmem, maxproc, nproc, nready;
uint16_t runticks;
bool inint;
/* to sensibly parse device names this needs to be platform-specific,
so for now it only parses device numbers */
-uint16_t bootdevice(char *s)
+uint16_t bootdevice(unsigned char *s)
{
unsigned int r = 0;
uputw(0, argv); /*;;26Feb- Add Null Pointer to end of array */
return ((char **) argbase);
}
+
+/*
+ * Stub the 32bit only allocator calls
+ */
+
+arg_t _memalloc(void)
+{
+ udata.u_error = ENOMEM;
+ return -1;
+}
+
+arg_t _memfree(void)
+{
+ udata.u_error = ENOMEM;
+ return -1;
+}
--- /dev/null
+/*
+ * Implement binary loading for 32bit platforms. We use the ucLinux binflat
+ * format with a simple magic number tweak to avoid confusion with ucLinux
+ */
+
+#include <kernel.h>
+#include <kernel32.h>
+#include <version.h>
+#include <kdata.h>
+#include <printf.h>
+
+#define ARGBUF_SIZE 2048
+
+struct binfmt_flat {
+ uint8_t magic[4];
+ uint32_t rev;
+ uint32_t entry;
+ uint32_t data_start;
+ uint32_t data_end;
+ uint32_t bss_end;
+ uint32_t stack_size;
+ uint32_t reloc_start;
+ uint32_t reloc_count;
+ uint32_t flags;
+ uint32_t filler[6];
+};
+
+static int bload(inoptr i, uint16_t bl, void *base, uint32_t len)
+{
+ blkno_t blk;
+ while(len) {
+ uint16_t cp = min(len, 512);
+ blk = bmap(i, bl, 1);
+ if (blk == NULLBLK)
+ uzero(base, 512);
+ else {
+ udata.u_offset = (off_t)blk << 9;
+ udata.u_count = 512;
+ udata.u_base = base;
+ if (cdread(i->c_dev, 0) < 0) {
+ kputs("bload failed.\n");
+ return -1;
+ }
+ }
+ base += cp;
+ len -= cp;
+ bl++;
+ }
+ return 0;
+}
+
+static void close_on_exec(void)
+{
+ int j;
+ for (j = UFTSIZE - 1; j >= 0; --j) {
+ if (udata.u_cloexec & (1 << j))
+ doclose(j);
+ }
+ udata.u_cloexec = 0;
+}
+
+static int valid_hdr(inoptr ino, struct binfmt_flat *bf)
+{
+ if (bf->rev != 4)
+ return 0;
+ if (bf->entry >= bf->data_start)
+ return 0;
+ if (bf->data_start > bf->data_end)
+ return 0;
+ if (bf->data_end < bf->bss_end)
+ return 0;
+ if (bf->bss_end + bf->stack_size < bf->bss_end)
+ return 0;
+ if (bf->data_end > ino->c_node.i_size)
+ return 0;
+ if (bf->bss_end - bf->data_end < 4 * bf->reloc_count)
+ bf->bss_end = bf->data_end + 4 * bf->reloc_count;
+ if (bf->reloc_start + bf->reloc_count * 4 > ino->c_node.i_size ||
+ bf->reloc_start + bf->reloc_count * 4 < bf->reloc_start)
+ return 0;
+ if (bf->flags != 1)
+ return 0;
+ return 1;
+}
+
+/* For now we load the binary in one block, including code/data/bss. We can
+ look at better formats, split binaries etc later maybe */
+static void relocate(struct binfmt_flat *bf, void *progbase, uint32_t size)
+{
+ uint32_t *rp = progbase + bf->reloc_start;
+ uint32_t n = bf->reloc_count;
+ while (n--) {
+ uint32_t v = *rp++;
+ if (v < size && !(v&1)) /* Revisit for non 68K */
+ *((uint32_t *)(rp + v)) += (uint32_t)progbase;
+ }
+}
+
+
+/* User's execve() call. All other flavors are library routines. */
+/*******************************************
+execve (name, argv, envp) Function 23
+char *name;
+char *argv[];
+char *envp[];
+********************************************/
+#define name (char *)udata.u_argn
+#define argv (char **)udata.u_argn1
+#define envp (char **)udata.u_argn2
+
+arg_t _execve(void)
+{
+ struct binfmt_flat *binflat;
+ inoptr ino;
+ unsigned char *buf;
+ char **nargv; /* In user space */
+ char **nenvp; /* In user space */
+ struct s_argblk *abuf, *ebuf;
+ int argc;
+ uint32_t bin_size; /* Will need to be bigger on some cpus */
+ void *progbase, *top;
+ uaddr_t go;
+
+ if (!(ino = n_open(name, NULLINOPTR)))
+ return (-1);
+
+ if (!((getperm(ino) & OTH_EX) &&
+ (ino->c_node.i_mode & F_REG) &&
+ (ino->c_node.i_mode & (OWN_EX | OTH_EX | GRP_EX)))) {
+ udata.u_error = EACCES;
+ goto nogood;
+ }
+
+ setftime(ino, A_TIME);
+
+ if (ino->c_node.i_size == 0) {
+ udata.u_error = ENOEXEC;
+ goto nogood;
+ }
+
+ /* Read in the first block of the new program */
+ buf = bread(ino->c_dev, bmap(ino, 0, 1), 0);
+ binflat = (struct binfmt_flat *)buf;
+
+ /* Hard coded for our 68K format. We don't quite use the ucLinux
+ names, we don't want to load a ucLinux binary in error! */
+ if (buf == NULL || memcmp(buf, "bF68", 4) ||
+ !valid_hdr(ino, binflat)) {
+ udata.u_error = ENOEXEC;
+ goto nogood2;
+ }
+
+ /* Memory needed */
+ bin_size = binflat->bss_end + binflat->stack_size;
+
+ /* Gather the arguments, and put them in temporary buffers. */
+ abuf = (struct s_argblk *) kmalloc(ARGBUF_SIZE);
+ if (abuf == NULL) {
+ udata.u_error = ENOMEM;
+ goto nogood2;
+ }
+ /* Put environment in another buffer. */
+ ebuf = (struct s_argblk *) kmalloc(ARGBUF_SIZE);
+ if (ebuf == NULL) {
+ kfree(abuf);
+ udata.u_error = ENOMEM;
+ goto nogood2;
+ }
+
+ /* Read args and environment from process memory */
+ if (rargs(argv, abuf) || rargs(envp, ebuf))
+ goto nogood3;
+
+ /* This must be the last test as it makes changes if it works */
+ if (pagemap_realloc(bin_size))
+ goto nogood3;
+
+ /* From this point on we are commmited to the exec() completing */
+
+ /* setuid, setgid if executable requires it */
+ if (ino->c_node.i_mode & SET_UID)
+ udata.u_euid = ino->c_node.i_uid;
+
+ if (ino->c_node.i_mode & SET_GID)
+ udata.u_egid = ino->c_node.i_gid;
+
+ /* We are definitely going to succeed with the exec,
+ * so we can start writing over the old program
+ */
+
+ progbase = pagemap_base();
+ top = progbase + bin_size;
+
+ uput(buf, (uint8_t *)progbase, 512); /* Move 1st Block to user bank */
+
+ /* At this point, we are committed to reading in and
+ * executing the program. */
+
+ close_on_exec();
+
+ /*
+ * Read in the rest of the program, block by block
+ * We use bufdiscard so that we load the entire app through the
+ * same buffer to avoid cycling our small cache on this. Indirect blocks
+ * will still be cached. - Hat tip to Steve Hosgood's OMU for that trick
+ */
+
+ /* Compute this once otherwise each loop we must recalculate this
+ as the compiler isn't entitled to assume the loop didn't change it */
+
+ bin_size = binflat->reloc_start + 4 * binflat->reloc_count;
+ if (bin_size > 512)
+ bload(ino, 1, progbase + 512, bin_size - 512);
+
+ go = (uint32_t)progbase + binflat->entry;
+
+ relocate(binflat, progbase, bin_size);
+ /* This may wipe the relocations */
+ uzero(progbase + binflat->data_end,
+ binflat->bss_end - binflat->data_end + binflat->stack_size);
+
+ brelse(buf);
+
+ /* brk eats into the stack allocation */
+ udata.u_break = (uaddr_t)(progbase + binflat->bss_end);
+
+ /* Turn off caught signals */
+ memset(udata.u_sigvec, 0, sizeof(udata.u_sigvec));
+
+ /* place the arguments, environment and stack at the top of userspace memory. */
+
+ /* Write back the arguments and the environment */
+ nargv = wargs(((char *) top - 4), abuf, &argc);
+ nenvp = wargs((char *) (nargv), ebuf, NULL);
+
+ /* Fill in udata.u_name with Program invocation name */
+ uget((void *) ugetl(nargv, NULL), udata.u_name, 8);
+ memcpy(udata.u_ptab->p_name, udata.u_name, 8);
+
+ kfree(abuf);
+ kfree(ebuf);
+
+ /* Shove argc and the address of argv just below envp */
+ uputl((uint32_t) nargv, nenvp - 1);
+ uputl((uint32_t) argc, nenvp - 2);
+
+ // Set stack pointer for the program
+ udata.u_isp = nenvp - 4;
+
+ // Start execution (never returns)
+ doexec(go);
+
+ // tidy up in various failure modes:
+nogood3:
+ kfree(abuf);
+ kfree(ebuf);
+nogood2:
+ brelse(buf);
+nogood:
+ i_deref(ino);
+ return (-1);
+}
+
+#undef name
+#undef argv
+#undef envp
+
+/* TODO max (1024) 512 bytes for argv
+ * and max 512 bytes for environ
+ */
+
+bool rargs(char **userspace_argv, struct s_argblk * argbuf)
+{
+ char *ptr; /* Address of base of arg strings in user space */
+ uint8_t c;
+ uint8_t *bufp;
+ int err;
+
+ argbuf->a_argc = 0; /* Store argc in argbuf */
+ bufp = argbuf->a_buf;
+
+ while ((ptr = (char *) ugetl(userspace_argv++, &err)) != NULL) {
+ if (err)
+ return true;
+ ++(argbuf->a_argc); /* Store argc in argbuf. */
+ do {
+ *bufp++ = c = ugetc(ptr++);
+ if (bufp > argbuf->a_buf + ARGBUF_SIZE - 12) {
+ udata.u_error = E2BIG;
+ return true; // failed
+ }
+ }
+ while (c);
+ }
+ /*Store total string size. */
+ argbuf->a_arglen = bufp - (uint8_t *)argbuf->a_buf;
+ /* Success */
+ return false;
+}
+
+
+char **wargs(char *ptr, struct s_argblk *argbuf, int *cnt) // ptr is in userspace
+{
+ char **argv; /* Address of users argv[], just below ptr */
+ int argc, arglen;
+ char **argbase;
+ uint8_t *sptr;
+
+ sptr = argbuf->a_buf;
+
+ /* Move them into the users address space, at the very top */
+ ptr -= (arglen = argbuf->a_arglen);
+
+ if (arglen) {
+ uput(sptr, ptr, arglen);
+ }
+
+ /* Set argv to point below the argument strings */
+ argc = argbuf->a_argc;
+ argbase = argv = (char **) ptr - (argc + 1);
+
+ if (cnt) {
+ *cnt = argc;
+ }
+
+ /* Set each element of argv[] to point to its argument string */
+ while (argc--) {
+ uputl((uint32_t) ptr, argv++);
+ if (argc) {
+ do
+ ++ptr;
+ while (*sptr++);
+ }
+ }
+ uputl(0, argv);
+ return ((char **) argbase);
+}
+