Add support for the PharLap ETS tools. (Charles Crain)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 22 Jan 2003 13:51:44 +0000 (13:51 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 22 Jan 2003 13:51:44 +0000 (13:51 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@562 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
etc/TestSCons.py
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Tool/386asm.py [new file with mode: 0644]
src/engine/SCons/Tool/PharLapCommon.py [new file with mode: 0644]
src/engine/SCons/Tool/PharLapCommonTests.py [new file with mode: 0644]
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/linkloc.py [new file with mode: 0644]
test/PharLap.py [new file with mode: 0644]

index 002793cfd6e9edb070871444250c6be6855c0164..c10b7295faeec44aa096e4a8a28ad86b24565def 100644 (file)
@@ -788,6 +788,7 @@ be redetected.
 SCons supports the following tool specifications
 out of the box on all platforms:
 .ES
+386asm
 ar
 dvipdf
 dvips
@@ -801,6 +802,7 @@ gcc
 gnulink
 latex
 lex
+linkloc
 masm
 mingw
 mslib
index 658496b5e849663b05b7f5a142081db4a733907d..e8bac905003717209c4e7e09e2a5cf827562c2b2 100644 (file)
@@ -167,6 +167,26 @@ class TestSCons(TestCmd.TestCmd):
         except KeyError:
             return None
 
+    def detect_tool(self, tool, prog=None):
+        """
+        Given a tool (i.e., tool specification that would be passed
+        to the "tools=" parameter of Environment()) and one a program that
+        corresponds to that tool, return true if and only if we can find
+        that tool using Environment.Detect().
+
+        By default, progs is set to the value passed into the tools parameter.
+        """
+
+        if not prog:
+            prog = tool
+        import SCons.Environment
+        import SCons.Errors
+        try:
+            env=SCons.Environment.Environment(tools=[tool])
+        except (SCons.Errors.UserError, SCons.Errors.InternalError):
+            return None
+        return env.Detect([prog])
+
     def wrap_stdout(self, build_str = "", read_str = ""):
         """Wraps standard output string(s) in the normal
         "Reading ... done" and "Building ... done" strings
index 83dbc9acb6f90a62129abed193b46084e3c89680..1f5a6d514511c4780666f80540b6b9b44e49f33a 100644 (file)
@@ -15,6 +15,8 @@ RELEASE 0.11 - XXX
   - Added new AddPreAction() and AddPostAction() functions that support
     taking additional actions before or after building specific targets.
 
+  - Add support for the PharLap ETS tool chain.
+
   From Steven Knight:
 
   - Allow Python function Actions to specify a list of construction
index cf272744e1f2d2760acb7418321fcdf6bcdbef40..a51277bbcbebfcd6b06bcc548b1fddda595b98c1 100644 (file)
@@ -30,6 +30,7 @@ SCons/Sig/MD5.py
 SCons/Sig/TimeStamp.py
 SCons/Taskmaster.py
 SCons/Tool/__init__.py
+SCons/Tool/386asm.py
 SCons/Tool/ar.py
 SCons/Tool/default.py
 SCons/Tool/dvipdf.py
@@ -44,6 +45,7 @@ SCons/Tool/ifl.py
 SCons/Tool/ilink.py
 SCons/Tool/latex.py
 SCons/Tool/lex.py
+SCons/Tool/linkloc.py
 SCons/Tool/masm.py
 SCons/Tool/mingw.py
 SCons/Tool/mslib.py
@@ -52,6 +54,7 @@ SCons/Tool/msvc.py
 SCons/Tool/nasm.py
 SCons/Tool/pdflatex.py
 SCons/Tool/pdftex.py
+SCons/Tool/PharLapCommon.py
 SCons/Tool/tar.py
 SCons/Tool/tex.py
 SCons/Tool/yacc.py
diff --git a/src/engine/SCons/Tool/386asm.py b/src/engine/SCons/Tool/386asm.py
new file mode 100644 (file)
index 0000000..c07f856
--- /dev/null
@@ -0,0 +1,72 @@
+"""SCons.Tool.386asm
+
+Tool specification for the 386ASM assembler for the Phar Lap ETS embedded
+operating system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os.path
+import string
+import re
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Tool
+
+from SCons.Tool.PharLapCommon import addPharLapPaths
+
+ASSuffixes = ['.s', '.asm', '.ASM']
+ASPPSuffixes = ['.spp', '.SPP']
+if os.path.normcase('.s') == os.path.normcase('.S'):
+    ASSuffixes.extend(['.S'])
+else:
+    ASPPSuffixes.extend(['.S'])
+
+def generate(env, platform):
+    """Add Builders and construction variables for ar to an Environment."""
+    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
+
+    for suffix in ASSuffixes:
+        static_obj.add_action(suffix, SCons.Defaults.ASAction)
+
+    for suffix in ASPPSuffixes:
+        static_obj.add_action(suffix, SCons.Defaults.ASPPAction)
+
+    env['AS']        = '386asm'
+    env['ASFLAGS']   = ''
+    env['ASCOM']     = '$AS $ASFLAGS $SOURCES -o $TARGET'
+    env['ASPPCOM']   = '$CC $ASFLAGS $CPPFLAGS $SOURCES -o $TARGET'
+
+    addPharLapPaths(env)
+
+def exists(env):
+    return env.Detect('386asm')
diff --git a/src/engine/SCons/Tool/PharLapCommon.py b/src/engine/SCons/Tool/PharLapCommon.py
new file mode 100644 (file)
index 0000000..ddcaba6
--- /dev/null
@@ -0,0 +1,124 @@
+"""SCons.Tool.PharLapCommon
+
+This module contains common code used by all Tools for the
+Phar Lap ETS tool chain.  Right now, this is linkloc and
+386asm.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import os.path
+import SCons.Errors
+import SCons.Util
+import re
+import string
+
+def getPharLapPath():
+    """Reads the registry to find the installed path of the Phar Lap ETS
+    development kit.
+
+    Raises UserError if no installed version of Phar Lap can
+    be found."""
+
+    if not SCons.Util.can_read_reg:
+        raise SCons.Errors.InternalError, "No Windows registry module was found"
+    try:
+        k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
+                                  'SOFTWARE\\Pharlap\\ETS')
+        val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir')
+
+        # The following is a hack...there is (not surprisingly)
+        # an odd issue in the Phar Lap plug in that inserts
+        # a bunch of junk data after the phar lap path in the
+        # registry.  We must trim it.
+        idx=val.find('\0')
+        if idx >= 0:
+            val = val[:idx]
+                    
+        return os.path.normpath(val)
+    except SCons.Util.RegError:
+        raise SCons.Errors.UserError, "Cannot find Phar Lap ETS path in the registry.  Is it installed properly?"
+
+REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)')
+
+def getPharLapVersion():
+    """Returns the version of the installed ETS Tool Suite as a
+    decimal number.  This version comes from the ETS_VER #define in
+    the embkern.h header.  For example, '#define ETS_VER 1010' (which
+    is what Phar Lap 10.1 defines) would cause this method to return
+    1010. Phar Lap 9.1 does not have such a #define, but this method
+    will return 910 as a default.
+
+    Raises UserError if no installed version of Phar Lap can
+    be found."""
+
+    include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h"))
+    if not os.path.exists(include_path):
+        raise SCons.Errors.UserError, "Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?"
+    mo = REGEX_ETS_VER.search(open(include_path, 'r').read())
+    if mo:
+        return int(mo.group(1))
+    # Default return for Phar Lap 9.1
+    return 910
+
+def addPathIfNotExists(env_dict, key, path, sep=os.pathsep):
+    """This function will take 'key' out of the dictionary
+    'env_dict', then add the path 'path' to that key if it is not
+    already there.  This treats the value of env_dict[key] as if it
+    has a similar format to the PATH variable...a list of paths
+    separated by tokens.  The 'path' will get added to the list if it
+    is not already there."""
+    try:
+        paths = string.split(env_dict[key], sep)
+        if not os.path.normcase(path) in map(os.path.normcase, paths):
+            env_dict[key] = string.join([ path ] + paths, sep)
+    except KeyError:
+        env_dict[key] = path
+
+def addPharLapPaths(env):
+    """This function adds the path to the Phar Lap binaries, includes,
+    and libraries, if they are not already there."""
+    ph_path = getPharLapPath()
+
+    try:
+        env_dict = env['ENV']
+    except KeyError:
+        env_dict = {}
+        env['ENV'] = env_dict
+    addPathIfNotExists(env_dict, 'PATH',
+                       os.path.join(ph_path, 'bin'))
+    addPathIfNotExists(env_dict, 'INCLUDE',
+                       os.path.join(ph_path, 'include'))
+    addPathIfNotExists(env_dict, 'LIB',
+                       os.path.join(ph_path, 'lib'))
+    addPathIfNotExists(env_dict, 'LIB',
+                       os.path.join(ph_path, os.path.normpath('lib/vclib')))
+    
+    env['PHARLAP_PATH'] = getPharLapPath()
+    env['PHARLAP_VERSION'] = str(getPharLapVersion())
+    
diff --git a/src/engine/SCons/Tool/PharLapCommonTests.py b/src/engine/SCons/Tool/PharLapCommonTests.py
new file mode 100644 (file)
index 0000000..663b25e
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import unittest
+import os.path
+import os
+import sys
+
+import SCons.Errors
+from SCons.Tool.PharLapCommon import *
+
+class PharLapCommonTestCase(unittest.TestCase):
+    def test_addPathIfNotExists(self):
+        """Test the addPathIfNotExists() function"""
+        env_dict = { 'FOO' : os.path.normpath('/foo/bar') + os.pathsep + \
+                     os.path.normpath('/baz/blat'),
+                     'BAR' : os.path.normpath('/foo/bar') + os.pathsep + \
+                     os.path.normpath('/baz/blat') }
+        addPathIfNotExists(env_dict, 'FOO', os.path.normpath('/foo/bar'))
+        addPathIfNotExists(env_dict, 'BAR', os.path.normpath('/bar/foo'))
+        addPathIfNotExists(env_dict, 'BAZ', os.path.normpath('/foo/baz'))
+
+        assert env_dict['FOO'] == os.path.normpath('/foo/bar') + os.pathsep + \
+               os.path.normpath('/baz/blat'), env_dict['FOO']
+        assert env_dict['BAR'] == os.path.normpath('/bar/foo') + os.pathsep + \
+               os.path.normpath('/foo/bar') + os.pathsep + \
+               os.path.normpath('/baz/blat'), env_dict['BAR']
+        assert env_dict['BAZ'] == os.path.normpath('/foo/baz'), env_dict['BAZ']
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(PharLapCommonTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
index 36218261384edb6c514c9fd428172dfe7ac5770d..50ab4564786363e3ad31422bc935c994951328ac 100644 (file)
@@ -145,9 +145,9 @@ def tool_list(platform, env):
     #     the tool files themselves.
     if str(platform) == 'win32':
         "prefer Microsoft tools on Windows"
-        linkers = ['mslink', 'gnulink', 'ilink']
-        c_compilers = ['msvc', 'mingw', 'gcc', 'icc']
-        assemblers = ['masm', 'nasm', 'gas']
+        linkers = ['mslink', 'gnulink', 'ilink', 'linkloc' ]
+        c_compilers = ['msvc', 'mingw', 'gcc', 'icc' ]
+        assemblers = ['masm', 'nasm', 'gas', '386asm' ]
         fortran_compilers = ['g77', 'ifl']
         ars = ['mslib', 'ar']
     elif str(platform) == 'os2':
diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py
new file mode 100644 (file)
index 0000000..51ce60c
--- /dev/null
@@ -0,0 +1,107 @@
+"""SCons.Tool.linkloc
+
+Tool specification for the LinkLoc linker for the Phar Lap ETS embedded
+operating system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os.path
+import string
+import re
+
+import SCons.Action
+import SCons.Defaults
+import SCons.Errors
+import SCons.Util
+
+from SCons.Platform.win32 import TempFileMunge
+from SCons.Tool.msvc import get_msdev_paths
+from SCons.Tool.PharLapCommon import addPharLapPaths
+
+_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)')
+
+def repl_linker_command(m):
+    # Replaces any linker command file directives (e.g. "@foo.lnk") with
+    # the actual contents of the file.
+    try:
+        f=open(m.group(2), "r")
+        return m.group(1) + f.read()
+    except IOError:
+        # the linker should return an error if it can't
+        # find the linker command file so we will remain quiet.
+        # However, we will replace the @ with a # so we will not continue
+        # to find it with recursive substitution
+        return m.group(1) + '#' + m.group(2)
+
+class LinklocGenerator:
+    def __init__(self, cmdline):
+        self.cmdline = cmdline
+
+    def __call__(self, env, target, source, for_signature):
+        if for_signature:
+            # Expand the contents of any linker command files recursively
+            subs = 1
+            strsub = env.subst(self.cmdline)
+            while subs:
+                strsub, subs = _re_linker_command.subn(repl_linker_command, strsub)
+            return strsub
+        else:
+            return TempFileMunge(env, string.split(self.cmdline), 0)
+
+_linklocLinkAction = SCons.Action.Action(SCons.Action.CommandGenerator(LinklocGenerator("$LINK $LINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -exe $TARGET $SOURCES")))
+_linklocShLinkAction = SCons.Action.Action(SCons.Action.CommandGenerator(LinklocGenerator("$SHLINK $SHLINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -dll $TARGET $SOURCES")))
+
+def generate(env, platform):
+    """Add Builders and construction variables for ar to an Environment."""
+    env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
+    env['BUILDERS']['Program'] = SCons.Defaults.Program
+
+    env['SHLINK']      = '$LINK'
+    env['SHLINKFLAGS'] = '$LINKFLAGS'
+    env['SHLINKCOM']   = _linklocShLinkAction
+    env['SHLIBEMITTER']= None
+    env['LINK']        = "linkloc"
+    env['LINKFLAGS']   = ''
+    env['LINKCOM']     = _linklocLinkAction
+    env['LIBDIRPREFIX']='-libpath '
+    env['LIBDIRSUFFIX']=''
+    env['LIBLINKPREFIX']='-lib '
+    env['LIBLINKSUFFIX']='$LIBSUFFIX'
+
+    include_path, lib_path, exe_path = get_msdev_paths()
+    env['ENV']['LIB']            = lib_path
+    env['ENV']['PATH']           = exe_path
+
+    addPharLapPaths(env)
+
+def exists(env):
+    return env.Detect('linkloc')
diff --git a/test/PharLap.py b/test/PharLap.py
new file mode 100644 (file)
index 0000000..d9f8993
--- /dev/null
@@ -0,0 +1,309 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import os.path
+import string
+import sys
+import TestSCons
+import time
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+    test.pass_test()
+
+test.no_result(not test.detect_tool('linkloc'))
+test.no_result(not test.detect_tool('386asm'))
+
+# From the Phar Lap minasm example program...
+test.write("minasm.asm", r"""
+; 
+; MINASM.ASM - A minimal assembly language program which runs
+;      under ToolSuite.  You can use this program as a framework
+;      for large assembly language programs.
+;
+.386
+
+;
+; Segmentation and segment ordering.
+;
+; First comes the code segment.
+;
+_TEXT  segment use32 byte public 'CODE'
+_TEXT  ends
+
+;
+; The data segment contains initialized RAM based data.  It will automatically
+; be placed in the ROM at link time and unpacked into RAM at run-time
+; by the __pl_unpackrom function.
+;
+; If you do not need any initialized data in your assembly language program,
+; you can leave this segment empty and remove the call to __pl_unpackrom.
+;
+;
+_DATA  segment use32 dword public 'DATA'
+
+loopcount      dd 10d
+rammessage     db 'This message is in RAM memory',0dh,0ah,0
+
+_DATA  ends
+
+;
+; The BSS segment contains RAM based variables which
+; are initialized to zero at run-time.  Putting unitialized
+; variables which should start at zero here saves space in
+; the ROM.
+;
+; If you do not need any zero-initialized data in your assembly language
+; program, you can leave this segment empty (and optionally remove the
+; instructions below which initialize it).
+;
+; The segment name must be lower case for compatibility with the linker
+;
+_bss   segment use32 dword public 'BSS'
+dummy_bss db 32 dup(?) ; Use a little bit of BSS just to test it
+_bss   ends
+
+;
+; The const segment contains constants which will never
+; change.  It is put in the ROM and never copied to RAM.
+;
+; If you do not need any ROM based constants in your assembly language
+; program, you can leave this segment empty.
+;
+_CONST segment use32 dword public 'CONST'
+rommessage     db 'This message is in ROM memory',0dh,0ah,0
+_CONST ends
+
+;
+; We're in flat model, so we'll put all the read-only segments we know about
+; in a code group, and the writeable segments in a data group, so that
+; we can use assume to easily get addressability to the segments.
+;
+CGROUP group _TEXT, _CONST
+DGROUP group _DATA, _bss
+
+       assume cs:CGROUP,ds:DGROUP
+_TEXT  segment
+
+;
+; _main - the main routine of this program.
+;
+; We will display the RAM and ROM messages the number of times
+; specified in the loopcount variable.  This proves that we can
+; initialize RAM data out of ROM and the fact that we can
+; modify the loop count in memory verifies that it actually ends
+; up in RAM.
+;
+       public _main
+_main proc near
+
+       mov     cl,0ah                  ; Skip a line before we start
+       call    PutCharTarget           ;
+main_loop:
+       cmp     loopcount,0             ; Are we at the end of our loop?
+       je      short done_main         ;  yes.
+       lea     edx,rommessage          ; EDX -> ROM message
+       call    WriteStringTarget       ; Display it
+       lea     edx,rammessage          ; EDX -> RAM message
+       call    WriteStringTarget       ; Display it
+       dec     loopcount               ;
+       jmp     main_loop               ; Branch back for next loop iteration
+done_main:
+       ret                             ; That's it!
+
+_main endp
+
+;
+; WriteStringTarget - Display a string on the target console
+;
+; Inputs:
+;      EDX -> Null terminated ASCII string to display
+;
+; Outputs:
+;      All registers preserved
+;
+WriteStringTarget proc near
+
+       push    ecx                     ; Save registers
+       push    edx                     ;
+
+write_loop:
+       movzx   ecx,byte ptr [edx]      ; Get a character
+       jecxz   done_str                ; Branch if end of string
+       call    PutCharTarget           ; Display this character
+       inc     edx                     ; Bump scan pointer
+       jmp     write_loop              ; And loop back for next character
+
+done_str:
+       pop     edx                     ; Restore registers
+       pop     ecx                     ;
+       ret                             ;  and return
+
+WriteStringTarget endp
+
+;
+; PutCharTarget - Write a character on the target console
+;
+; This routine displays a character on the target console by using
+; the PutChar kernel service available through int 254.
+;
+; Inputs:
+;      CL = character to display
+;
+; Outputs:
+;      All registers preserved
+;
+PutCharTarget proc near
+
+       push    eax             ; Save registers
+       push    ebx             ;
+       push    edx             ;
+
+       mov     ax,254Ah        ; Request Kernel Service
+       mov     bx,1            ; service code 1 = PutChar
+       movzx   edx,cl          ; EDX = character to display
+       int     0FEh            ; Int 254 is for kernel services
+
+       pop     edx             ; Restore registers
+       pop     ebx             ;
+       pop     eax             ;
+       ret                     ;  and return
+
+PutCharTarget endp
+
+;
+; The __pl_unpackrom unpacks initialized RAM based data variables
+; out of the ROMINIT segment into their RAM area.  They are put
+; in the ROMINIT segment with the -ROMINIT switch in the link file.
+;
+extrn __pl_unpackrom:near
+
+;
+; The _EtsExitProcess function is used to terminate our program
+;
+extrn _EtsExitProcess:near
+
+;
+; The linker will define symbols for the beginning and end of the
+; BSS segment.
+;
+extrn   __p_SEG__bss_BEGIN:dword
+extrn   __p_SEG__bss_END:dword
+
+;
+; __p_start -- The entry point for our assembly language program.
+; We unpack the RAM based variables out of the ROM and clear the
+; BSS to zero, then call _main where the real work happens.  When
+; _main returns, we call EtsExitProcess(0) to terminate.
+;
+public __p_start
+__p_start proc near
+       pushad                          ; save initial regs
+       push    es                              ;
+       call    __pl_unpackrom          ; Call the unpacker 
+       cld                             ; Clear direction flag
+
+       lea     eax,__p_SEG__bss_END    ; load end address and 
+       lea     ebx,__p_SEG__bss_BEGIN  ; subtract start to get size
+       sub     eax,ebx
+       mov     ecx,eax                 ; This is size
+       inc     ecx
+       lea     edi,__p_SEG__bss_BEGIN  ; Zero from start address
+       mov     al,0                    ;Zero out BSS and C_COMMON      
+       rep     stosb
+
+       pop     es                      ; restore initial regs
+       popad
+       call    _main                   ; go do some work
+stopme:
+       xor     eax,eax                 ; Call _EtsExitProcess(0)
+       push    eax                     ;
+       call    _EtsExitProcess         ;
+       pop     eax                     ;
+       jmp     stopme                  ;  .. in a loop just in case it ever
+                                       ;  comes back
+
+__p_start endp
+
+TD_hack:
+       mov     eax, __p_tdhack         ; force reference to TD-hack symbol
+
+_TEXT  ends
+
+;
+;      Hack for Turbo Debugger/TDEMB - TD will fault if the .exe being
+;      debugged doesn't have an import table.  (TD looks for the address of
+;      the table, then dereferences that address wihtout checking for NULL).
+;
+;      This symbol, __p_tdhack, must be declared as an import in all the
+;      .emb files shipped.  IE:
+;
+;              -implib embkern.lib
+;              -import __p_tdhack
+;
+;      This forces the creation of an import table within the .EXE.
+_DATA  segment
+extrn  __p_tdhack:dword
+_DATA  ends
+       end     __p_start
+""")
+
+test.write("foo.lnk","""
+@baz\\bar.lnk
+""")
+
+test.subdir("baz")
+test.write([ "baz", "bar.lnk"],"""
+@asm.emb
+""")
+
+test.write("SConstruct", """
+env=Environment(tools = [ 'linkloc', '386asm' ],
+                ASFLAGS='-twocase -cvsym',
+                LINKFLAGS='@foo.lnk')
+env.Program(target='minasm', source='minasm.asm')
+""")
+
+test.run(arguments='.')
+
+# Assume .exe extension...this test is for Win32 only.
+test.fail_test(not os.path.exists('minasm.exe'))
+test.up_to_date(arguments='.')
+
+# Updating a linker command file should cause a rebuild!
+test.write([ "baz", "bar.lnk"],"""
+-cvsym
+@asm.emb
+""")
+
+oldtime = os.path.getmtime(test.workpath('minasm.exe'))
+time.sleep(2) # Give the time stamp time to change
+test.run(arguments = '.')
+test.fail_test(oldtime == os.path.getmtime(test.workpath('minasm.exe')))
+
+test.pass_test()