summaryrefslogtreecommitdiffstats
path: root/test/PharLap.py
blob: 0455cc251b4c404c1050de12cf77ec3f05dae0f1 (plain)
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 win32; 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 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()