1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
#!/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.skip_test('PharLap is only available on Windows; skipping test.\n')
if not test.detect_tool('linkloc'):
test.skip_test("Could not find 'linkloc', skipping test.\n")
if not test.detect_tool('386asm'):
test.skip_test("Could not find '386asm', skipping test.\n")
# 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 Windows 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()
|