-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExtern.fth
3232 lines (2877 loc) · 98 KB
/
Extern.fth
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\ Extern.fth - DLL and shared library access for VFX Forth
((
Copyright (c) 2001-2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2017
MicroProcessor Engineering
133 Hill Lane
Southampton SO15 5AF
England
tel: +44 (0)23 8063 1441
net: [email protected]
web: www.mpeforth.com
From North America, our telephone and fax numbers are:
011 44 23 8063 1441
To do
=====
Change history
==============
20170223 JD_024 Added more Windows types.
Added _Reserved_, [in] for Windows.
20140521 SFP023 Enhanced parsing of *** types.
20131119 SFP022 Made LOCATE go to first line of EXTERN:
20130520 SFP021 Exposed INIT-LIB. Added CLEAR-LIB.
2012xxyy SFP020 Added compatibility words for other Forths.
20120418 SFP019 Added FRAMEWORK and FRAMEWORK: for OSX.
Allowed library and framework redefinitions
to be ignored.
20120411 SFP018 Prevented inlining of words containing EXTERNs.
20120312 SFP017 Added EXTERNVAR and ALIASEDEXTERNVAR.
20111104 SFP016 More testing for tabs.
20111021 SFP016 Added #BADEXTERNS #BADLIBS and .BADLIBS.
20111004 SFP015 Added PLONG to Windows types.
20110912 SFP014 Added library mode options.
20110427 SFP013 Added additional pointer parsing.
Added __IN_OPT, __INOUT_OPT and __OUT_OPT for Windows.
20110415 SFP012 Supports C // and /*...*/ comments in argument
parsing.
Added __IN, __INOUT and __OUT for Windows.
Added ** and ***.
20100223 SFP011 Added protection mechanism to Externs.
20100222 SFP010 Moved PREEXTERN and POSTEXTERN to Forth voc.
20091123 SFP009 Refactored with startup.fth.
20090610 SFP008 Added documentation for FLAG from Marcel Hendrix.
20090609 SFP007 Added .BADEXTERNS.
20090502 SFP006 Added code for Windows protection with floats.
20020323 MPE005 Refactored client specific code in prologue and
epilogue.
20090319 MPE004 Modified after distribution list discussions.
20090310 MPE003 Refactored.
20090131 MPE002 Tightened up.
Put most words in separate vocabulary.
20090121 MPE001 Ported from the Windows/Linux version.
))
\ =========
\ *! extern
\ *T Functions in DLLs and shared libraries
\ =========
((
Licence
=======
This code can be used as the basis of an implementation of the
EXTERN: interface for other Forth systems. Copyright of the source
notation and behaviour is retained by MicroProcessor Engineering Ltd.
until another copyright has been assigned to a third party by
MicroProcessor Engineering Ltd..
Controlled words can be identified by comments starting
\ *G
below the name line.
If you implement the notation and behaviour and release the source
code, this licence must be included in the source code.
Rationale and Requests
======================
The rationale for releasing this code is to encourage portability
of Forth source code that accesses shared libraries.
The point of retaining copyright at MPE is not to prevent you
changing and improving the system, it is to provide a central
point of management for the notation and its behaviour. In the
long term, copyright of the EXTERN: notation will be assigned
to a third party.
If your code is better, faster, shorter or more functional, we
request that you contribute it back to the maintainer, currently
Stephen Pelc, [email protected]. Similarly ports for currently
unsupported Forth systems will be gratefully received.
VFXisms
=======
DONOTSIN can be removed or be a NOOP.
.LWORD ( x -- ) and .DWORD ( x -- )
Display x as an unsigned hex number.
: -leading bl skip ;
: -white -leading -trailing ;
The code is based around three words which can be implemented
for any Windows or Unix-style host.
LoadLibrary \ zaddr -- handle|0
FreeLibrary \ handle -- status
GetProcAddress \ handle zaddr -- addr|0
Legacy code
===========
The words L>R and R>L are for compiling library code from other
Forth systems which may use a right-to-left stack order.
))
only forth definitions decimal
[undefined] externals [if]
vocabulary externals
\ Where library and imported function definitions live.
[then]
vocabulary types \ --
\ Where the "C" style types go for the *\fo{EXTERN:} notation.
vocabulary Extern.Voc
\ Where the guts of the EXTERN: mechanism live.
also Extern.Voc definitions
\ ***************
\ *S Introduction
\ ***************
\ *P VFX Forth supports calling external API calls in dynamic link
\ ** libraries (DLLs) for Windows and shared libraries in Linux and
\ ** other Unix-derived operating systems.
\ ** Various API libraries export functions in a variety of methods
\ ** mostly transparent to programmers in languages such as C,
\ ** Pascal and Fortran. Floating point data is supported for use
\ ** with *\i{Lib\x86\Ndp387.fth}.
\ *P Before a library function can be used, the library itself must
\ ** be declared, e.g.
\ *E LIBRARY: Kernel32.dll
\ *P Access to functions in a library is provided by the
\ ** *\fo{EXTERN:} syntax which is similar to a C style function
\ ** prototype, e.g.
\ *E EXTERN: int PASCAL SendMessage(
\ ** HWND hwnd, DWORD mesg, WPARAM wparam, LPARAM lparam
\ ** );
\ *P This can be used to prototype the function *\b{SendMessage}
\ ** from the Microsoft Windows API, and produces a Forth word
\ ** *\fo{SendMessage}.
\ *C SendMessage \ hwnd mesg wparam lparam -- int
\ *P For Linux and other Unices, the same notation is used.
\ ** The default calling convention is nearly always applicable.
\ ** The following example shows that definitions can occupy more
\ ** than one line. It also indicates that some token separation may
\ ** be necessary for pointers:
\ *E Library: libc.so.6
\ **
\ ** Extern: int execve(
\ ** const char * path,
\ ** char * const argv[],
\ ** char * const envp[]
\ ** );
\ *P This produces a Forth word *\fo{execve}.
\ *C execve \ path argv envp -- int
\ *P The parser used to separate the tokens is not ideal. If you
\ ** have problems with a definition, make sure that *\fo{*}
\ ** tokens are white-space separated. Formal parameter names,
\ ** e.g. *\i{argv} above are ignored. Array indicators, *\i{[]}
\ ** above, are also ignored when part of the names.
\ *P The input types may be followed by a dummy name which is
\ ** discarded. Everything on the source line after the closing
\ ** ')' is discarded.
\ *P From VFX Forth v4.3 onwards, PASCAL is the default calling
\ ** convention in the Windows version. The default for the Linux
\ ** and OS X versions is "C". The default is always used unless
\ ** overridden in the declaration.
\ *********
\ *S Format
\ *********
\ *E EXTERN: <return> [ <callconv> ] <name> '(' <arglist> ')' ';'
\ **
\ ** <return> := { <type> [ '*' ] | void }
\ ** <arg> := { <type> [ '*' ] [ <name> ] }
\ ** <args> := { [ <arg>, ]* <arg> }
\ ** <arglist> := { <args> | void } Note: "void, void" etc. is illegal.
\ ** <callconv> := { PASCAL | WINAPI | STDCALL | "PASCAL" | "C" }
\ ** <name> := <any Forth acceptable namestring>
\ ** <type> := ... (see below, "void" is a valid type)
\ *P Note that during searches <name> is passed to the operating
\ ** system exactly as it is written, i.e. case sensitive. The
\ ** Forth name is case-insensitive.
\ *P As a standard Forth's string length for dictionary names is
\ ** only guaranteed up to 31 characters for portable source code,
\ ** very long API names can cause problems. Therefore the word
\ ** *\fo{AliasedExtern:} allows separate specification of API
\ ** and Forth names (see below). *\fo{AliasedExtern:} also
\ ** solves problems when API functions only differ in case
\ ** or their names conflict with existing Forth word names.
\ **********************
\ *S Calling Conventions
\ **********************
\ *P In the discussion *\b{caller} refers to the Forth system
\ ** (below the application layer and *\b{callee} refers to a
\ ** a function in a DLL or shared library. The *\fo{EXTERN:}
\ ** mechanism supports three calling conventions.
\ *(
\ *B C-Language: *\fo{"C"} *\br{}
\ ** Caller tidies the stack-frame.
\ ** The arguments (parameters) which are passed to the library
\ ** are reordered. This convention can be specified by using
\ ** *\fo{"C"} after the return type specifier and
\ ** before the function name. For Linux and most Unix-derived
\ ** operating systems, this is the default.
\ *B Pascal language: *\fo{"PASCAL"} *\br{}
\ ** Callee removes arguments from the stack frame. This is
\ ** invisible to the programmer at the application layer
\ ** The arguments (parameters) which are passed to the library are
\ ** not reordered. This convention is specified by
\ ** *\fo{"PASCAL"} after the return type specifier and
\ ** before the function name.
\ *B Windows API: *\fo{WINAPI | PASCAL | STDCALL} *\br{}
\ ** In nearly all cases (but *\b{not all}), calls to
\ ** Windows API functions require C style argument reversal and
\ ** the called function cleans up. Specify this convention with
\ ** *\fo{PASCAL}, *\fo{WinAPI} or *\fo{StdCall} after the
\ ** return type specifier and before the function name. For
\ ** Windows, this is the default.
\ *)
\ *P Unless otherwise specified, the Forth system's default
\ ** convention is used. Under Windows this is *\fo{WINAPI} and
\ ** under Linux and other Unices it is *\fo{"C"}.
\ *************************
\ *S Promotion and Demotion
\ *************************
\ *P The system generates code to either promote or demote non-CELL
\ ** sized arguments and return results which can be either signed or
\ ** unsigned. Although Forth is an un-typed language it must deal with
\ ** libraries which do have typed calling conventions. In general
\ ** the use of non-CELL arguments should be avoided but return
\ ** results should be declared in Forth with the same size as the C or
\ ** PASCAL convention documented.
\ ********************
\ *S Argument Reversal
\ ********************
\ *P The default calling convention for the host operating system
\ ** is used. The right-most argument/parameter in the C-style
\ ** prototype is on the top the Forth data stack.
\ ** When calling an external function the parameters are reordered
\ ** if required by the operating system; this is to enable the
\ ** argument list to read left to right in Forth source as well
\ ** as in the C-style operating system documentation.
\ *P Under certain conditions, the order can be reversed. See the
\ ** words *\fo{"C"} and *\fo{"PASCAL"} which define the order for
\ ** the operating system. See *\fo{L>R} and *\fo{R>L} which define
\ ** the Forth stack order with respect to the arguments in the
\ ** prototype.
\ *****************************
\ *S C comments in declarations
\ *****************************
\ *P Very rudimentary support for C comments in declarations is
\ ** provided, but is good enough for the vast majority of
\ ** declarations.
\ *(
\ *B Comments can be *\fo{// ...} or *\fo{/* ... */},
\ *B Comments must be at the end of the line,
\ *B Comments are treated as extending to the end of the line,
\ *B Comments must not contain the ')' character.
\ *)
\ *P The example below is taken from a *\i{SQLite} interface.
\ *E Extern: "C" int sqlite3_open16(
\ ** const void * filename, /* Database filename [UTF-16] */
\ ** sqlite3 ** ppDb /* OUT: SQLite db handle */
\ ** );
\ **********************************
\ *S Controlling external references
\ **********************************
also forth definitions
1 value ExternWarnings? \ -- n
\ *G Set this true to get warning messages when an external reference
\ ** is redefined.
0 value ExternRedefs? \ -- n
\ *G If non-zero, redefinitions of existing imports are permitted.
\ ** Zero is the default for VFX Forth so that redefinitions of
\ ** existing imports are ignored.
1 value LibRedefs? \ -- n
\ *G If non-zero, redefinitions of existing libraries are permitted.
\ ** Non-zero is the default for VFX Forth so that redefinitions of
\ ** existing libraries and OS X frameworks are permitted. When set
\ ** to zero, redefinitions are silently ignored.
1 value InExternals? \ -- n
\ *G Set this true if following import definitions are to be in
\ ** the *\fo{EXTERNALS} vocabulary, false if they are to go into
\ ** the wordlist specified in *\fo{CURRENT}. Non-Zero is the
\ ** default for VFX Forth.
: InExternals \ --
\ *G External imports are created in the *\fo{EXTERNALS} vocabulary.
1 -> InExternals? ;
: InCurrent \ --
\ *G External imports are created in the wordlist specified by
\ ** *\fo{CURRENT}.
0 -> InExternals? ;
previous definitions
' externals voc>wid constant Externals.wid
\ The wordlist ID of the EXTERNALS vocabulary.
\ ******************
\ *S Library Imports
\ ******************
\ *P In VFX Forth, libraries are held in the *\fo{EXTERNALS}
\ ** vocabulary, which is part of the minimum search order.
\ ** Other Forth systems may use the *\fo{CURRENT} wordlist.
\ *P For turnkey applications, initialisation, release and
\ ** reload of required libraries is handled at start up.
(( for DocGen
variable lib-link \ -- addr
\ *G Anchors the chain of dynamic/shared libraries.
))
variable lib-mask \ -- addr
\ *G If non-zero, this value is used as the mode for *\fo{dlopen()}
\ ** calls in Linux and OS X.
struct /libstr \ -- size
\ *G The structure used by a *\fo{Library:} definition.
\ *[
int >liblink \ link to previous library
int >libaddr \ library Id/handle/address, depends on O/S
int >libmask \ mask for dlopen()
0 field >libname \ zero terminated string of library name
end-struct
\ *]
struct /funcstr \ -- size
\ *G The structure used by an imported function.
int >funclink \ link to previous function
int >funcaddr \ function address
0 field >funcname \ zero terminated string of function name
end-struct
also forth definitions
[defined] Target_386_Windows [if]
1 constant RTLD_LAZY
: init-lib \ libstr --
\ *G Given the address of a library structure, load the library.
dup >libname LoadLibrary swap >libaddr ! ; doNotSin
[then]
[defined] Target_386_Linux [if]
((
\ from /usr/include/bits/dlfcn.h
/* The MODE argument to `dlopen' contains one of the following: */
#define RTLD_LAZY 0x00001 /* Lazy function call binding. */
#define RTLD_NOW 0x00002 /* Immediate function call binding. */
#define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */
#define RTLD_NOLOAD 0x00004 /* Do not load the object. */
#define RTLD_DEEPBIND 0x00008 /* Use deep binding. */
/* If the following bit is set in the MODE argument to `dlopen',
the symbols of the loaded object and its dependencies are made
visible as if the object were linked directly into the program. */
#define RTLD_GLOBAL 0x00100
/* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
The implementation does this by default and so we can define the
value to zero. */
#define RTLD_LOCAL 0
/* Do not delete object when closed. */
#define RTLD_NODELETE 0x01000
))
1 constant RTLD_LAZY \ -- x
$0102 constant RTLD_NOW_GLOBAL \ -- x
: init-lib \ libstr --
\ Given the address of a library structure, load the library.
dup >libmask @
if dup >libmask @ loadLibMask ! then
dup >libname LoadLibrary swap >libaddr ! ; doNotSin
: isRTLD_Now_Global \ --
RTLD_NOW_GLOBAL lib-mask ! ;
[then]
[defined] Target_386_OSX [if]
(( \ from /usr/include/dlfcn.h 10.7
#define RTLD_LAZY 0x1
#define RTLD_NOW 0x2
#define RTLD_LOCAL 0x4
#define RTLD_GLOBAL 0x8
))
1 constant RTLD_LAZY
$000A constant RTLD_NOW_GLOBAL \ -- x
: init-lib \ libstr --
\ Given the address of a library structure, load the library.
dup >libname LoadLibrary swap >libaddr ! ; doNotSin
: isRTLD_Now_Global \ --
RTLD_NOW_GLOBAL lib-mask ! ;
[then]
: clear-lib \ libstr --
\ *G Unload the given library and zero its load address.
dup >libaddr @ FreeLibrary drop >libaddr off
; doNotSin
: clear-libs \ --
\ *G Clear all library addresses.
lib-link
begin
@ dup
while
0 over >libaddr !
>liblink
repeat
drop
; \ doNotSin
: init-libs \ --
\ *G Release and reload the required libraries.
clear-libs lib-link
begin
@ dup
while
dup init-lib >liblink
repeat
drop
;
' init-libs AtCold
: find-libfunction \ z-addr -- address|0
\ *G Given a zero terminated function name, attempt to find the
\ ** function somewhere within the already active libraries.
>r lib-link
begin \ -- link ; R: -- z$
@ dup
while \ -- struct ; R: -- z$
dup >libaddr @ r@ GetProcAddress dup \ -- struct addr|0 addr|0 ; R: -- z$
if nip r> drop exit endif
drop >liblink
repeat
drop r> drop 0
;
: .Libs \ --
\ *G Display the list of declared libraries.
lib-link
begin
@ dup
while
dup >libname cr .z$
out @ 31 and 32 swap - spaces
dup >libaddr @ .lword
>liblink
repeat
drop
;
: #BadLibs \ -- u
\ *G Return the number of declared libraries that have not yet been
\ ** loaded.
0 lib-link
begin
@ dup
while
dup >libaddr @ 0= if
swap 1+ swap
then
>liblink
repeat
drop
;
: .BadLibs \ --
\ *G Display a list of declared libraries that have not yet been
\ ** loaded.
0 lib-link
begin
@ dup
while
dup >libaddr @ 0= if
dup >libname cr .z$
swap 1+ swap
then
>liblink
repeat
drop
cr . ." Unresolved Libraries"
;
: Library: \ "<name>" -- ; -- loadaddr|0
\ *G Register a new library by name.
\ ** If *\fo{LibRedefs?} is set to zero, redefinitions are silently
\ ** ignored.
\ ** Use in the form:
\ *C LIBRARY: <name>
\ *P Executing *\fo{<name>} later will return its load address.
\ ** This is useful when checking for libraries that may not be
\ ** present. After definition, the library is the first one
\ ** searched by import declarations.
LibRedefs? 0= if \ ignore redefinitions?
>in @ parse-name search-context \ already defined?
if drop drop exit endif
>in !
endif
>in @ get-current InExternals? \ save >IN, which wordlist?
if Externals.wid set-current then \ -- >in wid
create
set-current >in ! \ restore wordlist and >IN
here \ start of structure
lib-link link, \ link to previous library
0 , \ load address, filled in later
lib-mask @ , lib-mask off \ mask for dlopen(), 0 = use default
bl word $>z, \ lay name as zero terminated string
init-lib
does>
>libaddr @
;
: topLib \ libstr --
\ *G Make the library structure the top/first in the library
\ ** search order.
lib-link 2dup delLink AddLink ;
: firstLib \ "<name>" --
\ *G Make the library first in the library search order. Use during
\ ** interpretation in the form:
\ *C FirstLib <name>
\ *P to make the library first in the search order. This is useful
\ ** when you know that there may be several functions of the
\ ** same name in different libraries.
' >body topLib ;
: [firstLib] \ "<name>" --
\ *G Make the library first in the library search order. Use during
\ ** compilation in the form:
\ *C [firstLib] <name>
\ *P to make the library first in the search order. This is useful
\ ** when you know that there may be several functions of the
\ ** same name in different libraries.
' >body postpone literal postpone topLib
; immediate
previous definitions
\ ======================
\ *N Mac OS X extensions
\ ======================
\ *P The phrase *\fo{Framework <name.framework>} creates two Forth words,
\ ** one for the library access, the other to make that library top in
\ ** the search order. For example:
\ *C framework Cocoa.framework
\ *P produces two words
\ *C Cocoa.framework/Cocoa
\ *C Cocoa.framework
\ *P The first word is the library definition itself, which
\ ** behaves in the normal VFX Forth way, returning its load
\ ** address or zero if not loaded. The second word forces
\ ** the library to be top/first in the library search order.
\ ** Thanks to Roelf Toxopeus.
\ *P As of OSX 10.7, *\fo{FRAMEWORK} (actually *\b{dlopen()})
\ ** will search for frameworks in all the default Frameworks
\ ** directories:
\ *(
\ *B /Library/Frameworks
\ *B /System/Library/Frameworks
\ *B ~/Library/Frameworks
\ *)
[defined] Target_386_OSX [if]
also forth definitions
: framework \ --
\ *G Build the two framework words. See above for more details.
\ ** If *\fo{LibRedefs?} is set to zero, redefinitions are silently
\ ** ignored.
{: | buff[ #256 ] -- :}
LibRedefs? 0= if \ ignore redefinitions?
>in @ parse-name search-context \ already defined?
if drop drop exit endif
>in !
endif
>in @
s" Library: " buff[ place \ build library: name.framework/name
parse-name 2dup buff[ append \ Library: name.framework
s" /" buff[ append \ Library: name.framework/
#10 - buff[ append \ Library: name.framework/name
buff[ count evaluate
>in !
create
lib-link @ , \ address of last library structure
does>
@ toplib
;
previous definitions
[then]
\ *******************
\ *S Function Imports
\ *******************
\ *P Function declarations in shared libraries are compiled into
\ ** the *\fo{EXTERNALS} vocabulary. They form a single linked
\ ** list. When a new function is declared, the list of previously
\ ** declared libraries is scanned to find the function. If the
\ ** function has already been declared, the new definition is
\ ** ignored if *\fo{ExternRedefs?} is set to zero. Otherwise,
\ ** the new definition overrides the old one as is usual
\ ** in Forth.
\ *P In VFX Forth, *\fo{ExternRedefs?} is zero by default.
(( for DocGen
variable import-func-link \ -- addr
\ *G Anchors the chain of imported functions in shared libraries.
))
[defined] Target_386_Windows [if]
: resolve-libfunction \ z-addr -- address|0
\ +G Look up a definition in loaded libraries and return XT or 0.
\ +* Automatically attempts to find a version with 'A' appended for
\ +* Windows.
{ | tn[ MAX_PATH ] -- }
[ also system ]
dup find-libfunction dup \ look for raw name
if nip exit then
drop
tn[ MAX_PATH erase \ look for name with 'A' appended
zcount tn[ swap move
[char] A tn[ zcount + c!
tn[ find-libfunction
[ previous ]
;
[else]
: resolve-libfunction \ z-addr -- address|0
[ also system ]
find-libfunction \ look for raw name
[ previous ]
;
[then]
: resolveImport \ struct --
\ Resolve the imported function whose structure is given.
dup >funcname resolve-libfunction swap >funcaddr ! ;
also forth definitions
: ExternLinked \ c-addr u -- address|0
\ *G Given a string, attempt to find the named function in the
\ ** already active libraries. Returns zero when the function is
\ ** not found.
{ | temp[ 256 ] -- }
255 min temp[ zplace
temp[ resolve-libfunction
;
: init-imports \ --
\ *G Initialise Import libraries. *\fo{INIT-IMPORTS} is called by
\ ** the system cold chain.
import-func-link @
begin
dup
while
dup resolveImport
>funclink @
repeat
drop
;
' init-imports AtCold
previous definitions
\ ********************************
\ +S Import parsing and generation
\ ********************************
\ ======================
\ +N Data and structures
\ ======================
\ These are constants used as THROW codes with associated text messages.
SysErrdef err_ImpType "Invalid type in EXTERN: import"
SysErrDef err_CGsize "Invalid size in EXTERN: argument preparation"
SysErrDef err_RetType "Invalid return type"
SysErrDef err_RetSize "Invalid return size"
\ argument types - BOOL could be considered a form of UINT, but
\ is O/S specific.
0 constant SINTtype \ -- n ; default is signed int
1 constant UINTtype \ -- n ; signed int
2 constant BOOLtype \ -- n ; C boolean
3 constant FLOATtype \ -- n ; float/double/ext ....
struct /argItem \ -- len
int ai.Size \ argument size in bytes, 0=void, 1=byte/char, 2=short, 4=int ...
int ai.type \ 0=uint, 1=sint, 2=bool, 3=float
int ai.FrameOff \ offset on operating system frame to data
int ai.PSPoff \ offset on Forth parameter stack as if TOS uncached
end-struct
#32 /argitem * constant /argList \ size of the argument list, max 32 items
\ Operating system dependent items. These VALUEs are set to
\ defaults at the start of each external import declaration.
0 value CalleeCleaned? \ -- flag
\ 0 = "C", 1 = "PASCAL", used for cleanup only. If the calling
\ mechanism preserves the Forth stack pointers, this value is not
\ needed.
16 value FrameAlignment \ -- u
\ Set to alignment of stack frame in memory. Must be a power of two.
4 value FrameBackup \ -- u
\ If the stack must be aligned after the call, set this to the
\ number of byes used by the call
true value AlignInFrame? \ -- flag
\ True if items on the stack frame are padded for data alignment.
true value REQUIRE_NREV \ -- flag
\ Set true if arguments are reversed by default (C argument order),
\ i.e. the leftmost argument in the argument declaration list is
\ on the top of the O/S stack.
true value L>R? \ -- flag
\ Set true when the Forth stack order matches the argument
\ declaration order.
0 value ObjPtr? \ -- flag
\ True if an object-oriented call is required, i.e. a hidden
\ "this" pointer is used. The location of "this" is compiler and
\ OS dependent.
\ 0 = none
\ 1 = VC++, pointer in ECX
\ 2 = gcc, pointer on top, but below struct pointer (see below)
0 value StructRet? \ -- flag
\ Set this true if the import returns a structure and requires a
\ hidden structure pointer. The location of the structure pointer
\ is compiler and OS dependent.
\ 0 = none
\ 1 = VC++
\ 2 = gcc
0 value varargs? \ -- flag
\ Set this true if the parameter list is of variable length.
defer setDefExt \ --
\ Set the call type defaults in the VALUEs above.
#256 buffer: importName \ name of DLL function
#256 buffer: externName \ name of Forth word
variable usesFloats \ non-zero if FP arguments or return ; SFP006
variable NumArgs \ number of input arguments
variable NumInts \ number of arguments on Forth data stack
variable NumFloats \ number of arguments on Forth Float stack
variable /DataFrame \ total size of data frame with padding
variable /ForthFrame \ number of bytes taken from Forth stack
/argItem buffer: ReturnRes \ structure for return data
/argList buffer: ArgList \ argument data during compilation
0 value LastImport \ address of current/last import structure
: .ArgType \ n --
case
SINTtype of ." SINT" endof
UINTtype of ." UINT" endof
BOOLtype of ." BOOL" endof
FLOATtype of ." Float" endof
." ???"
endcase
;
: .ArgData \ ^ai --
\ Display the argument data
cr ." Size: " dup ai.size @ dup 0 .r
0= if
drop ." (void)"
else
." , Type: " dup ai.Type @ .ArgType
." , PSP: " dup ai.PSPoff @ 0 .r
." , Frame: " ai.FrameOff @ 0 .r
endif
;
: ArgList[] \ u -- addr
\ Return the address of the uth item in argList
/argItem * argList + ;
: .ArgList \ --
\ Display the calculated argument and return data.
cr ." Arguments" numArgs @ 0
?do i ArgList[] .ArgData loop
cr ." --" cr ." Returns"
ReturnRes .ArgData
;
: CalcSource \ ^ai --
\ Generate the source Forth data for the given argument.
dup ai.type @ FLOATtype = if
drop numFloats incr
else
/ForthFrame @ over ai.PSPoff !
ai.size @ aligned /ForthFrame +!
numInts incr
endif
; doNotSin
: CalcForthOff \ --
\ Calculate the offsets of data items on the Forth input stack.
\ The Forth order is only affected by the choice of L>R or R>L
\ ordering. By default we use L>R.
numArgs @ if
L>R? if
0 numArgs @ 1-
do i ArgList[] CalcSource -1 +loop
else
numArgs @ 0
do i ArgList[] CalcSource loop
endif
endif
; doNotSin
: AlignFrame \ n --
\ Force frame alignment of a item of the given size to an N byte
\ boundary where N is a power of two.
/DataFrame @ FrameBackup + \ offset w.r.t frame after call
over 1- + swap negate and \ -- dp'
FrameBackup - /DataFrame !
; doNotSin
: ?AlignFrame \ size --
\ Force frame alignment of a item of the given size to an N byte
\ boundary where N is a power of two. Ignore void items.
?dup if
aligned AlignInFrame?
if AlignFrame else drop endif
endif
; doNotSin
: CalcDest \ ^ai --
\ Generate the destination frame data for the given argument.
dup ai.size @ ?AlignFrame
/DataFrame @ over ai.FrameOff !
ai.size @ aligned /DataFrame +!
; doNotSin
: CalcFrameOff \ --
\ Calculate the O/S stack offsets. These are from the C-style
\ argument list, and so are unaffected by R>L, which only affects
\ the source offsets on the Forth stack with respect to the
\ declaration list.
REQUIRE_NREV if
numArgs @ 0
?do i ArgList[] CalcDest loop
else
numArgs @ if
0 numArgs @ 1-
do i ArgList[] CalcDest -1 +loop
endif
endif
; doNotSin
: initOffsets \ --
\ Initialise the stack and frame offsets required.
numInts off numFloats off /DataFrame off /ForthFrame off
; doNotSin
\ ******************
\ +S Code generation
\ ******************
\ ========
\ +N Tools
\ ========
: [a \ -- ; start of assembler sequence
also asm-access ; immediate
: a] \ -- ; end of assembler sequence
previous ; immediate
: [a] \ -- ; flush asm sequence
[ also asm-core ] asm-end asm-start [ previous ] ; \ flush assembler
\ ==============================
\ +N Prologue, call and epilogue
\ ==============================
\ The process of making the external call is to:
\ 1) Save Forth system registers on the return (ESP) stack.
\ 2) If required save the FP state.
\ 3) Preserve the Forth TOS (normally cached in EBX) on the
\ deep stack so that the Forth stack is all in memory.
\ 4) Preserve ESP by extending the deep Forth stack.
\ 5) Adjust the return stack (ESP) to operating system requirements
\ with space allocated for the function arguments.
\ This may involve aligment to a 16 byte boundary.
\ 6) Copy the Forth and FP parameters to the ESP stack locations
\ allowing for the calling convention.
\ 7) Call the external function
\ 8) Restore ESP for "C" call convention
\ 9) Save the return data and process EBX for void returns
\ 10) Restore the NDP as required
\ 11) Restore the Forth stack
\ 12) Restore Forth registers
2 cells constant /PSPextend \ -- u
\ The number of bytes by which the Forth stack is dropped to
\ extend it. The high cell contains the cached TOS. The bottom
\ cell (offset 0) contains the saved ESP.
\ MUST BE at least the size of a LongLong.
/PSPextend cell - constant /PSPtos \ -- u
\ The offset from the dropped Forth stack to the cell containing
\ TOS.
: FrameDrop \ -- n
\ The amount by which the frame is dropped before alignment
/DataFrame @ FrameAlignment + negate ;
: FrameMask \ -- mask
\ The mask applied to ESP after FrameDrop has been applied.
FrameAlignment negate ;
: -FrameCall \ -- -n
\ The number of bytes that allow for the call data after the
\ frame has been dropped.
FrameBackup negate ;
: -ReturnForth \ n -- n-x
\ Remove the size of data returned on the Forth data stack.
ReturnRes ai.Size @ aligned - ;
: #PSPrestore \ -- n
\ generate code to restore TOS for void/float returns. This is
\ the number of bytes added to restore the Forth frame.
/ForthFrame @ \ size of Forth stack arguments
returnRes ai.size @ if
case ReturnRes ai.Type @ \ Return type
SINTtype of -ReturnForth endof
UINTtype of -ReturnForth endof
BOOLtype of -ReturnForth endof
endcase
endif
/PSPextend + \ frame overhead, >= largest return type
;
: ^efn \ -- addr
\ address holding function address
LastImport >funcaddr ;
[defined] Target_386_Windows [if]
#108 constant /fsave \ -- u
\ space required to save the NDP state
/fsave negate constant -/fsave \ -- u
also Forth definitions
(( \ moved to STARTUP.FTH ; SFP009
defer preExtCall \ --
\ *G *\b{Windows only}. A hook provided for debugging and extending
\ ** external calls without floating point parameters or return
\ ** items. It is executed at the start of the external
\ ** call before any parameter processing.
assign noop to-do preExtCall
defer postExtCall \ --
\ *G *\b{Windows only}. A hook provided for debugging and extending
\ ** external calls without floating point parameters or return
\ ** items. It is executed at the end of the external
\ ** call after return data processing.
assign noop to-do postExtCall
))
\ SFP006...
defer preFPExtCall \ --
\ *G *\b{Windows only}. A hook provided for debugging and extending