English Amiga Board

English Amiga Board (https://eab.abime.net/index.php)
-   Coders. Nextgen (https://eab.abime.net/forumdisplay.php?f=120)
-   -   AROS startup code (https://eab.abime.net/showthread.php?t=86902)

patrik 24 April 2017 00:16

AROS startup code
 
Hi,

I would like to learn how to write my own startup code for AROS.

On classic AmigaOS, it is very simple - it executes the first function found in an executable file, you just need to setup SysBase before calling any exec.library functions. An hello world example with own startup code would look like this:
Code:

#include <exec/exec.h>
#include <dos/dos.h>

#include <proto/exec.h>
#include <proto/dos.h>

struct ExecBase *SysBase = NULL;
struct DosLibrary *DOSBase = NULL;

LONG Something(void) {
        SysBase = *(struct ExecBase **) 4;
        DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 36);
        if(NULL != DOSBase) {
                Printf("Hello Amiga!\n");
        }
        CloseLibrary((struct Library *) DOSBase);
        return 0;
}

Compiles with:
Code:

vc +aos68k -c99 -nostdlib -o helloamiga helloamiga.c
For MorphOS, you just need to add the __abox__ symbol, for AmigaOS4 the function needs to be named _start and have a specific signature and there is some more code required for the exec iface, but basically it is no big problem, atleast not with vbcc.

With gcc things seem a bit trickier, but it is doable - there is for example an excellent guide for writing your own startup code in MorphOS using gcc, with a complete example attached at the end.

If I try something similar on AROS and compile the above code with:
Code:

gcc -nostdlib -nostartfiles -o helloamiga helloamiga.c
I do get an executable without producing any warnings or anything, but it will crash if I try to execute it, so obviously I'm missing something required which I am unaware of.

Haven't been able to find any information similar to the MorphOS guide for AROS, so I'm hoping someone can point me in the right direction.

patrik 24 April 2017 23:09

Responding to myself in case someone else searches for this in the future. This is how you do it for the above example:
Code:

#include <exec/exec.h>
#include <dos/dos.h>

#include <proto/exec.h>
#include <proto/dos.h>

struct ExecBase *SysBase = NULL;
struct DosLibrary *DOSBase = NULL;

__startup AROS_PROCH(Something, arguments, argumentsLength, sysBase) {
        AROS_PROCFUNC_INIT

        SysBase = sysBase;
        DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 0);
        if(NULL != DOSBase) {
                Printf("Hello world!\n");
        }
        CloseLibrary((struct Library *) DOSBase);
        return 0;

        AROS_PROCFUNC_EXIT
}

And compile with:
Code:

gcc -nostartfiles -o helloamiga helloamiga.c

phx 28 April 2017 18:57

As you mentioned vbcc, here is the vclib startup code for the i386-aros target, which I had to abandon about 10 years ago. Maybe declaring __startup_entry as global is important for the loader, but I don't remember.

Code:

#
# vbcc/AROS386 startup
# Written by Frank Wille <frank@phoenix.owl.de> 2006
#

# Version
.set    VERSION,1
.set    REVISION,01
.macro  VBCC_VER
        .byte  "VBCC 0.9"
.endm

        .extern __cstart

        .text

        .globl  __startup_entry
__startup_entry:
# Entered with:
#  0(%esp)  return address to DOS
#  4(%esp)  argument string pointer
#  8(%esp)  argument string length
# 12(%esp)  SysBase pointer
        movl    $__save_regs,%eax
        mov    %esp,(%eax)
        mov    %ebx,4(%eax)
        mov    %ebp,8(%eax)
        mov    %esi,12(%eax)
        mov    %edi,16(%eax)
        mov    12(%esp),%eax
        mov    %eax,SysBase
        jmp    __cstart

        VBCC_VER

        .type  __startup_entry,@function
        .size  __startup_entry,.-__startup_entry


        .globl  __asmexit
__asmexit:
        mov    4(%esp),%eax
        movl    $__save_regs,%edx
        mov    (%edx),%esp
        mov    4(%edx),%ebx
        mov    8(%edx),%ebp
        mov    12(%edx),%esi
        mov    16(%edx),%edi
        ret

        .type  __asmexit,@function
        .size  __asmexit,.-__asmexit

        .byte  "startup V"
        .byte  '0'+VERSION,'.','0'+(REVISION/10),'0'+(REVISION%10),0


        .lcomm  __save_regs,20
        .comm  SysBase,4

As I am quite unexperienced in x86 assembler, I switched to C as soon as possible. Command line parsing and WB-startup follows in __cstart():
Code:

/*
** vbcc/AROS386 startup
** Written by Frank Wille <frank@phoenix.owl.de> 2006
*/
#include <dos/dosextens.h>
#include <workbench/startup.h>
#include <proto/exec.h>
#include <proto/dos.h>

struct SVar {
  BPTR WbOutput;
  char *ArgvBufPtr;
  char *ArgvArray[1];
};

struct DosLibrary *DOSBase;
struct WBStartup *WBenchMsg,*_WBenchMsg;
BPTR _stdin,_stdout,_stderr;
static struct SVar *svar;

extern void __asmexit(int);
extern void __main(int,char **);


static int strlen(__reg("%edi") const char *) =
  "\tcld\n"
  "\txor\t%eax,%eax\n"
  "\tmov\t%eax,%ecx\n"
  "\tdec\t%ecx\n"
  "\trepne\n"
  "\tscasb\n"
  "\tnot\t%ecx\n"
  "\tlea\t-1(%ecx),%eax";


void __exit(int code)
{
  if (DOSBase) {
    if (svar) {
      if (svar->WbOutput)
        Close(svar->WbOutput);
      FreeVec(svar);
    }
    CloseLibrary((struct Library *)DOSBase);
  }
  if (WBenchMsg) {
    Forbid();
    ReplyMsg((struct Message *)WBenchMsg);
  }
  __asmexit(code);
}


static void getWbMsg(struct Process *this_proc)
{
  WaitPort(&this_proc->pr_MsgPort);
  WBenchMsg = _WBenchMsg = (struct WBStartup *)GetMsg(&this_proc->pr_MsgPort);
}


static void startup_failed(struct Process *this_proc)
{
  if (!this_proc->pr_CLI)
    getWbMsg(this_proc);

  __exit(RETURN_FAIL);
}


static int command_len(void)
{
  char buf[1024];

  if (GetProgramName(buf,1024))
    return strlen(buf) + 1;
  return 0;
}


void __cstart(unsigned char *argstr,int arglen)
{
  struct Process *this_proc = (struct Process *)FindTask(NULL);
  int argc,argc_est,len;
  char **argv;
  unsigned char *p;

  if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",39)))
    startup_failed(this_proc);

  /* estimate number of argv-slots */
  if (this_proc->pr_CLI) {
    for (argc_est=3,len=arglen,p=argstr; len>0 && *p!='\0'; len--,p++) {
      if (*p <= ' ')
        argc_est++;
    }
    len = command_len();
  }
  else {
    argc_est = 2;  /* two dummy arguments for WB-start */
    arglen = 8;
    len = 0;
  }

  if (!(svar = AllocVec(sizeof(struct SVar) +
                        argc_est*sizeof(char *) + len + arglen + 1,
                        MEMF_PUBLIC | MEMF_CLEAR))) {
    startup_failed(this_proc);
  }
  svar->ArgvBufPtr = (char *)&svar->ArgvArray[argc_est+1];

  if (this_proc->pr_CLI) {
    /* started from CLI */
    if (len)
      GetProgramName(svar->ArgvBufPtr,len);
    argv = svar->ArgvArray;
    *argv++ = svar->ArgvBufPtr;
    svar->ArgvBufPtr += len;
    argc = 1;

    /* eat trailing control characters on command line */
    for (len=arglen,p=argstr+arglen-1; len>0 && *p<=' '; len--,p--);
    *(p+1) = '\0';

    for (p=svar->ArgvBufPtr; *argstr!='\0' && argc<argc_est; ) {
      while (*argstr!='\0' && *argstr<=' ')
        argstr++;
      if (*argstr == '\0')
        break;
      argc++;
      *argv++ = p;

      if (*argstr == '\"') {
        unsigned char c;

        argstr++;
        while ((c = *argstr++) != '\"') {
          if (c == '\0') {
            argstr--;
            break;
          }
          else if (c == '*') {  /* BCPL escape character */
            switch (c = *argstr++) {
              case '\0': argstr--; break;
              case 'N': *p++ = '\n'; break;
              case 'E': *p++ = 27; break;
              default: *p++ = c; break;
            }
          }
          else
            *p++ = c;
        }
      }
      else {
        while (*argstr > ' ')
          *p++ = *argstr++;
      }
      *p++ = '\0';
    }
    *argv = NULL;
    argv = svar->ArgvArray;

    _stdin = Input();
    _stdout = Output();
    if (!(_stderr = this_proc->pr_CES))
      _stderr = _stdout;
  }

  else {
    /* started from Workbench */
    struct WBArg *wbarg;

    getWbMsg(this_proc);
    argv = (char **)WBenchMsg;
    argc = 0;
    if (wbarg = WBenchMsg->sm_ArgList)
      CurrentDir(wbarg->wa_Lock);

    /* set I/O to NIL: */
    if (svar->WbOutput = Open("NIL:",MODE_OLDFILE))
      _stdin = _stdout = _stderr = svar->WbOutput;
    else
      startup_failed(this_proc);

    #if 0 /* @@@ how to do this under AROS ??? */
    /* set console task, so Open("*",mode) will work */
    this_proc->pr_CIS = _stdin;
    this_proc->pr_COS = _stdout;
    this_proc->pr_ConsoleTask = ...
    #endif
  }

  __main(argc,argv);  /* vclib init, call main() */
  __exit(0);          /* although __main() should never return */
}


patrik 29 April 2017 19:14

Interesting!

If I would compile the 80x86 versions of vbcc and vasm, could they be used to create an aros executable for something like the helloamiga above?

phx 29 April 2017 23:06

The hello-world in your first posting could work, provided you have got the proto and inline headers of vbcc's i386-aros target.

But you will run into all sorts of problems as soon as you include the original AROS headers files, which depend on gcc-specific extensions. This was the reason I abandoned AROS support.

patrik 30 April 2017 21:13

Can the protos and inlines for aros-i386 be generated with the fd2pragma which is included with vbcc?


All times are GMT +2. The time now is 21:28.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.

Page generated in 0.04274 seconds with 11 queries