|-----------------------------------------------------------
|
|       Simple C runtime startup for Human68k
|
|         o no stdio support (DOS/IOCS only)
|         o HUPAIR support
|
|       written by ITOH Yasufumi
|
|       This file is in the public domain
|
|       $NetBSD: start.S,v 1.4 2024/01/07 07:58:34 isaki Exp $

#include <machine/asm.h>

|-----------------------------------------------------------
|
|       configuration
|
#ifndef STACK_SIZE
#define STACK_SIZE      65536   /* stack size in bytes */
#endif

#ifndef STACK_SYMBOL
#ifdef  __ELF__
#define STACK_SYMBOL    _stack  /* stack top symbol name */
#else   /* a.out */
#define STACK_SYMBOL    stack_8K_hUMAn6 /* has largest hash val on NetBSD ld */
#endif                                  /* and will be at the end of bss */
#endif

#ifndef DUMMY___main
#define DUMMY___main    1       /* define dummy __main() for a.out */
#endif

#ifndef SUPPORT_R_EXEC          /* support  ".r"  relocatable executable */
#define SUPPORT_R_EXEC  0       /* (clear bss, don't use a1 at startup) */
#endif                          /* XXX impossible for a.out */

#ifndef SUPPORT_HUPAIR
#define SUPPORT_HUPAIR  1       /* HUPAIR argument interface support */
#endif

#ifndef HUPAIR_ARGV0
#define HUPAIR_ARGV0    1       /* use argv[0] passed in HUPAIR manner */
#endif

#ifndef ADD_PATHNAME
#define ADD_PATHNAME    0       /* add command path to argv[0] if not HUPAIR */
#endif

#ifndef STRICT_SETBLOCK
#define STRICT_SETBLOCK 1       /* free unused memory after creating args */
#endif

#ifndef C_REGPARM
#define C_REGPARM       0       /* main() arguments are passed in registers */
#endif                          /* (for  gcc -mregparm) */

#ifndef NEED_MEMCP
#define NEED_MEMCP      0       /* __memcp: MCB address */
#endif
#ifndef NEED_PROCP
#define NEED_PROCP      0       /* __procp: PDB address */
#endif
#ifndef NEED_VERNUM
#define NEED_VERNUM     1       /* __vernum: Human68k version */
#endif
#ifndef NEED_PROGNAME
#define NEED_PROGNAME   1       /* ___progname: program basename */
#endif
#ifndef NEED_ENVIRON
#define NEED_ENVIRON    1       /* _environ: environment vector */
#endif

|-----------------------------------------------------------
|
|       DOS call
|
#define DOS(x)          .word x

#define __FPUTS         0xFF1E
#define __VERNUM        0xFF30
#define __SETBLOCK      0xFF4A
#define __EXIT2         0xFF4C

|
|       seed to estimate argument string/vector and environment vector size
|       (max nohupair argv[0](92+4) + NULLs(8) + alignment(3))  <- donburi?
|
#define estimated_argsz 107
#define estimated_com   92      /* estimated command name length (included) */

|
|       other constants
|
#define char_tab        0x09
#define char_space      0x20
#define char_dquote     0x22
#define char_squote     0x27
#define char_slash      0x2f
#define char_backslash  0x5c

#define pdb_mcb         0x10    /* PDB address - MCB address */
#define drvpath_pdb     0x070   /* drive and path address - PDB address */
#define command_pdb     0x0b4   /* command name address - PDB address */
#define top_pdb         0xf0    /* program load address - PDB address */

#define stderr          2       /* stderr  file handle */
#define exit_nomem      127     /* exit status on SETBLOCK failure */

|-----------------------------------------------------------
|
|       execution start
|
|       a0: MCB address, a1: program end + 1,
|       a2: command line, a3: environ, a4: execution start
|
       |.cpu   68000
       .text
       .even

       .globl  _C_LABEL(main)

#ifdef  __ELF__
ASENTRY_NOPROFILE(_start)
#else
ASENTRY_NOPROFILE(start)
#endif
#if SUPPORT_HUPAIR
       .word   0x611e,0x2348,0x5550,0x4149,0x5200
#else
       .word   0x6016
#endif
#if SUPPORT_R_EXEC
       .word   0x7263
#else
       .word   0x7863
#endif
       .long   0x72743020,0x56312E31,0x42206279,0x20596173,0x68610000

|
|       check if hupair
|
#if SUPPORT_HUPAIR
       moveal  %a7@+,%a4
       lea     %a2@(-8),%a6
       moveql  #7,%d3
chkhupair:
       cmpmb   %a6@+,%a4@+
       dbne    %d3,chkhupair
                               | d3.l: 0xFFFF: hupair, 0x000x: not hupair
       addqw   #1,%d3
       beqs    ishupair
#endif
       moveql  #char_tab,%d3           | tab (= 9)
ishupair:                               | d3.l: 0: hupair, 9: not hupair

|
|       (over)estimate and allocate argument/environ area beforehand
|
       addql   #1,%a2                  | skip byte count
       moveql  #estimated_argsz,%d1    | byte counter
       moveal  %a2,%a6
       moveql  #char_space,%d4         | space
acou1:  addql   #1,%d1
       moveb   %a6@+,%d0
       beqs    acou2
       cmpb    %d4,%d0                 | space
       beqs    acous
       cmpb    %d3,%d0                 | tab (if not hupair)
       bnes    acou1
acous:  addql   #4,%d1                  | for argv area
       bras    acou1

acou2:
#if SUPPORT_HUPAIR && HUPAIR_ARGV0
       tstb    %d3
       bnes    anohp
       moveql  #-estimated_com,%d2     | reset argv[0] length
       moveal  %a6,%a4                 | preserve argv[0] string address
acouhp: addql   #1,%d2
       tstb    %a6@+
       bnes    acouhp
       addl    %d2,%d1
anohp:
#endif
                                       | d1: estimated argument bytes

#if NEED_ENVIRON
       addql   #4,%a3                  | skip length field
       moveal  %a3,%a6
ecou1:  addql   #4,%d1
       tstb    %a6@+
       beqs    ecoue
ecou2:  tstb    %a6@+
       bnes    ecou2
       bras    ecou1
ecoue:
#endif
                                       | d1: estimated byte count

|
|       free memory
|       and ensure the bss/stack (for .r executable) and argument areas valid
|
       lea     %a0@(pdb_mcb),%a5       | a5: PDB address
       subl    %a5,%d1
#if SUPPORT_R_EXEC
#define RELOC(sym, reg)         lea sym+top_pdb,reg; addl %a5,reg
       moveal  %a1,%a6                 | end of data
       RELOC(_end, %a1)                | end of bss
#endif
       pea     %a1@(0,%d1:l)           | _end + size - pdb
       movel   %a5,%a7@-
       DOS(__SETBLOCK)
       tstl    %d0
       bpls    sbnoerr

setblock_err:
       movew   #stderr,%a7@
       bsrs    sberr1                  | pea %pc@
       .asciz  "setblock failed\r\n"
       .even
sberr1: DOS(__FPUTS)
       movew   #exit_nomem,%a7@
       DOS(__EXIT2)                    | _exit(exit_nomem)

sbnoerr:

|       here, the bss, stack, and argument/environ areas are certainly valid

|
|       set stack
|
       moveal  #STACK_SYMBOL+STACK_SIZE,%a7

#if SUPPORT_R_EXEC
|
|       clear bss section
|
loop_clrbss:
       clrl    %a6@+
       cmpal   %a1,%a6
       bcss    loop_clrbss
#endif

|
|       save MCB address
|
#if NEED_MEMCP
# if SUPPORT_R_EXEC
       RELOC(_C_LABEL(_memcp), %a6)
       movel   %a0,%a6@
# else
       movel   %a0,_C_LABEL(_memcp)
# endif
#endif

|
|       save PDB address
|
#if NEED_PROCP
# if SUPPORT_R_EXEC
       RELOC(_C_LABEL(_procp), %a6)
       movel   %a5,%a6@
# else
       movel   %a5,_C_LABEL(_procp)
# endif
#endif

|
|       get version no of Human
|
#if NEED_VERNUM
       DOS(__VERNUM)
# if SUPPORT_R_EXEC
       RELOC(_C_LABEL(_vernum), %a6)
       movel   %d0,%a6@
# else
       movel   %d0,_C_LABEL(_vernum)
# endif
#endif

|
|       create argv[0]
|
       moveal  %a1,%a0                 | top of argument strings
#if SUPPORT_HUPAIR && HUPAIR_ARGV0
       tstb    %d3
       beqs    arg0lp
#endif
#if ADD_PATHNAME
       lea     %a5@(drvpath_pdb),%a4   | drive and path name
arg0path:
       moveb   %a4@+,%a1@+
       bnes    arg0path
       subql   #1,%a1                  | remove nul char
#endif
       lea     %a5@(command_pdb),%a4   | command name
arg0lp: moveb   %a4@+,%a1@+
       bnes    arg0lp

#if NEED_PROGNAME
|
|       find program basename
|
       moveal  %a1,%a4
prognlp:
       cmpal   %a0,%a4
       beqs    prognexit
       moveb   %a4@-,%d0
       cmpib   #char_slash,%d0
       beqs    prognfou
       cmpib   #char_backslash,%d0
       bnes    prognlp
prognfou:
       addql   #1,%a4                  | next of slash
prognexit:
# if SUPPORT_R_EXEC
       RELOC(_C_LABEL(__progname), %a6)
       movel   %a4,%a6@
# else
       movel   %a4,_C_LABEL(__progname)
# endif
#endif

|
|       create argument strings
|
       moveql  #1,%d0                  | (d0:l) # arg

spskip: moveb   %a2@+,%d2
       beqs    comline_end
       cmpb    %d4,%d2                 | space
       beqs    spskip
       cmpb    %d3,%d2                 | tab (if not hupair)
       beqs    spskip

       | create an arg
       clrb    %d1                     | no quote here
       addql   #1,%d0                  | increment argc

arglp:  tstb    %d1
       bnes    in_quote
       cmpib   #char_dquote,%d2
       beqs    quote
       cmpib   #char_squote,%d2
       bnes    notquote
quote:  moveb   %d2,%d1                 | save quote character
       bras    argnextc

in_quote:
       cmpb    %d1,%d2
       bnes    argcopyc
       clrb    %d1                     | quote ended
       bras    argnextc

notquote:
       cmpb    %d4,%d2                 | space
       beqs    arg_end
       cmpb    %d3,%d2                 | tab (if not hupair)
       bnes    argcopyc
arg_end:
       clrb    %a1@+
       bras    spskip

argcopyc:
       moveb   %d2,%a1@+               | copy char

argnextc:
       moveb   %a2@+,%d2
       bnes    arglp
       clrb    %a1@+

comline_end:

|
|       create argv vector
|
       addql   #3,%a1
       movel   %a1,%d1
       andib   #0xfc,%d1               | long alignment
       moveal  %d1,%a1                 | argv
       movel   %d0,%d4                 | argc
                                       | a0 is at argument strings
mkargv:
       movel   %a0,%a1@+               | argv[0] ...
nxtarg: tstb    %a0@+
       bnes    nxtarg
#if STRICT_SETBLOCK
       subqw   #1,%d0
#else
       subqw   #1,%d4
#endif
       bnes    mkargv

       clrl    %a1@+                   | argv[argc] should be NULL

|
|       create envp vector
|
#if NEED_ENVIRON
       movel   %a1,%d2
envlp:  tstb    %a3@
       beqs    envend
       movel   %a3,%a1@+
envskp: tstb    %a3@+
       bnes    envskp
       bras    envlp
envend: clrl    %a1@+                   | NULL termination
# if SUPPORT_R_EXEC
       RELOC(_C_LABEL(environ), %a0)
       movel   %d2,%a0@
# else
       movel   %d2,_C_LABEL(environ)
# endif
#endif

|
|       free unused memory
|
#if STRICT_SETBLOCK
       subal   %a5,%a1
       movel   %a1,%a7@-
       movel   %a5,%a7@-
       DOS(__SETBLOCK)                 | reset donburi-kanjo (never fails)
       addql   #8,%a7
       movel   %d4,%d0                 | argc
#endif

|
|       make parameter
|
#if NEED_ENVIRON
       movel   %d2,%a7@-               | arg #3 --- envp
#endif
#if !C_REGPARM
       movel   %d1,%a7@-               | arg #2 --- argv
       movel   %d0,%a7@-               | arg #1 --- argc
#endif

#if SUPPORT_R_EXEC
       RELOC(_C_LABEL(main), %a0)
       jsr     %a0@
#else
       jsr     _C_LABEL(main)
#endif

#if !C_REGPARM || NEED_ENVIRON
       movew   %d0,%a7@
#else
       movew   %d0,%a7@-
#endif
       DOS(__EXIT2)

#if !defined(__ELF__) && DUMMY___main
ENTRY_NOPROFILE(__main)
       rts
#endif

|-----------------------------------------------------------
|
|       variables
|
#if NEED_MEMCP
       .comm   _C_LABEL(_memcp),4
#endif

#if NEED_PROCP
       .comm   _C_LABEL(_procp),4              | PDB address
#endif

#if NEED_VERNUM
       .comm   _C_LABEL(_vernum),4
#endif

#if NEED_PROGNAME
       .comm   _C_LABEL(__progname),4
#endif

#if NEED_ENVIRON
       .comm   _C_LABEL(environ),4             | environ address
#endif

|-----------------------------------------------------------
|
|       stack
|
#ifdef  __ELF__
       .section        .stack,"aw",@nobits
       .align  4
STACK_SYMBOL:
       .space  STACK_SIZE
#else
       .comm   STACK_SYMBOL,STACK_SIZE
#endif