summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/man/scons.12
-rw-r--r--etc/TestSCons.py20
-rw-r--r--src/CHANGES.txt2
-rw-r--r--src/engine/MANIFEST.in3
-rw-r--r--src/engine/SCons/Tool/386asm.py72
-rw-r--r--src/engine/SCons/Tool/PharLapCommon.py124
-rw-r--r--src/engine/SCons/Tool/PharLapCommonTests.py55
-rw-r--r--src/engine/SCons/Tool/__init__.py6
-rw-r--r--src/engine/SCons/Tool/linkloc.py107
-rw-r--r--test/PharLap.py309
10 files changed, 697 insertions, 3 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 002793c..c10b729 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -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
diff --git a/etc/TestSCons.py b/etc/TestSCons.py
index 658496b..e8bac90 100644
--- a/etc/TestSCons.py
+++ b/etc/TestSCons.py
@@ -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
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 83dbc9a..1f5a6d5 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -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
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index cf27274..a51277b 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -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
index 0000000..c07f856
--- /dev/null
+++ b/src/engine/SCons/Tool/386asm.py
@@ -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
index 0000000..ddcaba6
--- /dev/null
+++ b/src/engine/SCons/Tool/PharLapCommon.py
@@ -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
index 0000000..663b25e
--- /dev/null
+++ b/src/engine/SCons/Tool/PharLapCommonTests.py
@@ -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)
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index 3621826..50ab456 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -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
index 0000000..51ce60c
--- /dev/null
+++ b/src/engine/SCons/Tool/linkloc.py
@@ -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
index 0000000..d9f8993
--- /dev/null
+++ b/test/PharLap.py
@@ -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()