News:

Function Finder  Find all the functions within source code files

Main Menu

PlayBASIC To DLL (Development Blog)

Started by kevin, November 14, 2013, 10:41:59 PM

Previous topic - Next topic

kevin

#15
  PlayBASIC To Dll  -  User Define Types - Addressing the same ground.

         I've been testing the translation tonight hunting down those little gotchas in some bare bones type demos.    Found one such issue where PB would crash on exit when a typed array created inside the dll was returned to the VM.   Initially was thinking it was caused due to some difference between the Undim process in the VM, which is called when the VM exists.   This was a close guess, but it turned out to be just the order in which some of the vm libraries release themselves.   VM would release any bound resources such as DLL's before it release the array memory.  This is no problem when all the array data is created locally in the VM, but it's a big issue when the arrays point to structure definitions inside the DLL.   Which would cause access violations and hence the crash.

         Today's build gets us to a  point where the DLL side and VM side can create a type and other side can release it.  This allows the flow of data from VM to DLL and DLL to VM more freely.   Even so, i'd still recommend at least trying to keep the data life cycle management to one side or the other, but it's workable if you can't wrap your head around that.    If this all sounds familiar it's because a few months back I had to rewrite how the VM deals with it's local types.   In the older editions the VM maintains one central list.   Which is fine in a world where 3rd party code isn't expected to plug into the VM, but bound DLL's need to be able to control data much the same way the vm does.   There's other solutions of course, but altering it to support host structures seems the most workable.  


        All this messing around means you can finally do this, bellow we have a function that we'd export to Dll and bellow that is some code that accepts the returned structure..
 
PlayBASIC Code: [Select]
  // Declare structure on DLL side.. The VM will need a copy of this also if you indent to look at the fields in the types.. 
Type Dude2
xxxx,yyyy,zzzz
string$
endtype


Function DLL_FillArray(Size)

Dim Cool(size) as Dude2

For lp =0 to size
Cool(lp)= new dude2
Cool(lp).xxxx=100+lp
Cool(lp).yyyy=200+lp
Cool(lp).zzzz=300+lp
Cool(lp).string$="This String "+Str$(lp)
next


EndFunction Cool() as Dude2






     The dll code just creates a junk array and fills it with some data, before returning the array handle upon completion back to the caller, the VM side.

PlayBASIC Code: [Select]
linkDLL  "testdll.dll"
DLL_FillArray(Size) alias "fillarray" as integer
EndLinkDLL

// Declare structure on VM side
Type Dude2
x,y,z
Name$
endtype


// Dim our reciever array as type DUDe2
Dim K(0) as dude2

// Call our DLL function and assign the array handle to our K() array
k()= DLL_FillArray(10)

// query the contents of it
if GetArrayStatus(k())
for lp=0 to GetArrayElements(k())
print k(lp)
print k(lp).x
print k(lp).y
print k(lp).z
print k(lp).name$
next
endif

print "done"
Sync
waitnokey
waitkey




      The VM side is just running through the contents of the array and dumping them to the screen.   Now much of an example, but it's fairly seamless.  There's a couple of oddity that appear in the debugger if you looked at the contents of the k() array in the VM after it was created externally, but those seem from the DLL's dimension instruction not classifying the arrays structure.  So it doesn't know the original 'name' of the type.


       Edit #1 - Debugger Name Resolving

        One problem i'd been having with structures passed back from a DLL was that if you clicked on them in the debugger the structure names would be wrong, but that seems to be now fixed..   As if you got to variables and click on k() array, it drops the info into the panel as normal.   







kevin

#16
  PlayBASIC To Dll  -  Redim - Life cycle of arrays & LIsts

        Pretty much all PB's data structures uses the same interface, that being the array interface.   Most of the legacy code i'm ok with, some of it i'm updating to bring in some of the newer ideas explored in it's descendants.     Haven't had much time to really chomp through it, but what I have been slowly working up the core routine with their support code.  One such replacement routine was the ReDIM function,  which is a lot more complicated than the DIM operation.

        Dim is fairly dumb, it just creates some empty (formatted) space for the array.  There's a little more to it than that, but conceptually that's about it.  ReDIM on the other had has to rebuild the structure when the sizes are different.   If you make an array larger, then it allocates some new memory and copies the old data across,  the same goes for shrinking an array.  It copies the existing data,not forgetting to deletes any excess.    Which can be an easy way to leak resources if you're not careful.   See-> Tips On Safe Resource Management:

        The other thing i've been working on is the back end support for the linked lists.   A link list is an optional extension to the array structure, so when a list is present the address resolver uses the structures internal current position.    So far it's in a very bare bones state, due to how interdependent some of the back end actually is when dealing with lists.   For example the list iterators are set up, but insertions don't work because that requires insertion management of the arrays size,  hence the need for ReDIM.      

        But here's a general idea of where it was at last night..  

PlayBASIC Code: [Select]
Function DLL_ResizeArray(Size)

Dim Cool(Size)

For lp =0 to Size
Cool(lp)=1+Rnd(10000)
next

redim Cool(Size+10)

EndFUnction COol()


Type Dude2
xxxx,yyyy,zzzz
string$
endtype


Function Dll_TestLIst(Size)

Dim Z as dude2
Dim L as dude2 list

For lp=1 to size
l=new dude2
l.xxxx=lp
print l.xxxx
next

EndFunction L.Dude2




       I think it should be smoother sailing once all the little inter-dependencies have been ironed out.      


 PlayBASIC To Dll  -  Macros make the world turn faster

       The back end assembly is built with flat assembler which has some very impressive macro support, most of which is way beyond the scope of the project and my understanding of it :).   However,  macro's can be a very powerful approach to programming, in particular in assembly, as they allows the user to dramatically reduce the lines of code, where a common behavior is wrapped as a macro and used throughout the project.  It's a bit like a function, except the macros are parsed out by the assembler at compile time. So all the code is inlined.  

       So what i've been doing is moving more of the code in the base template into macros.  You could pretty much make macro's for all the core operations,  which would simplify PlayBASIC to Dll's job by hiding how something works away in the template.   The current build of the generator is pretty reactionary, so when it sees some known byte code pattern it spits out the same' template assembly chunk in reply.  Such things could well be macro'd,  allowing improvement to be make to the template and not only via the translator.


kevin

#17
 PlayBASIC To Dll  -  Linked Lists

         The temperature has been unseasonable hot here lately, pushing 40 degrees outside yesterday  and it wasn't much better in the office.   Which has a pretty negative impact upon code productivity, but none the less we crawl forward in the little time I have left.    Since it's easier working at nights now, last nights session moved the translations to a point where Linked Lists controls started working.   There's still a few missing tidbits, but you can dim a list, fill it full of stuff and iterate back through it.     I think deletion is currently missing and the query style functions aren't added, but the guts of it seems to be functioning now.

 
PlayBASIC Code: [Select]
   Type Dude2
xxxx,yyyy,zzzz
string$
endtype


Function Dll_TestLIst(Size)

Dim Z as dude2
Dim L as dude2 list

For lp=1 to size
l=new dude2
l.xxxx=lp
l.string$="Stuff"+str$(lp)
next

Count=0
for each l()
print l.xxxx
print l.string$
next

EndFunction L.Dude2





       Edit #1

       Most of the list commands are now in the translated version.

PlayBASIC Code: [Select]
Function Dll_TestLIst(Size)

Dim Z as dude2
Dim L as dude2 list

For lp=1 to size
l=new dude2
l.xxxx=lp
l.string$="Stuff"+str$(lp)
next


print "List Size:"+Str$(GetListSize(l()))

print "List First:"+Str$(GetListFirst(l()))

Count=1
for each l()
Count++
if COunt=5
print "Deleting five from list"
l = null
endif

print str$(l.xxxx) +" "+ l.string$
print "List Pos:"+Str$(GetListPos(l())) + " Previous:"+Str$(GetListPrevious(l())) + " Next:"+Str$(GetListNext(l()))+ " Ptr:"+Str$(int(GetListPtr(l())))


next

print "List Size:"+Str$(GetListSize(l()))

EndFunction L.Dude2








kevin

#18
PlayBASIC To Dll  -  Array Functions

        The MoveArray & SwapArray functions were next on the list, so they're now in and running.


PlayBASIC Code: [Select]
Function DLL_SWAPANDMOVEARRAY(SIze)

DIm Test(Size)
Dim Dude(Size*2)

For lp=0 to size
test(lp)=lp
next

For lp=0 to size*2
dude(lp)=lp+2000
next

print "Sizes before swap"
print GetArrayElements(Test())
print GetArrayElements(Dude())

swaparray Test(),Dude()

print "Sizes after swap"
print GetArrayElements(Test())
print GetArrayElements(Dude())

print "Data in test array"
For lp =0 to GetArrayElements(Test())
print Test(lp)
next


print GetArrayStatus(Test())
print GetArrayStatus(Dude())

movearray Test(),Dude()

print GetArrayStatus(Test())
print GetArrayStatus(Dude())

print "Data in test array"
For lp =0 to GetArrayElements(Dude())
print Dude(lp)
next

EndFUnction Test()






        EDIT

          Added SortArray,  supports integer, float and string arrays.    The string version is still pretty slow (it's the same as VM version), will likely change that at some point in the future.

PlayBASIC Code: [Select]
Function DLL_SWAPANDMOVEARRAY(SIze)

DIm Test(Size)
Dim Dude(Size*2)

For lp=0 to size
test(lp)=lp
next

For lp=0 to size*2
dude(lp)=2000-lp
next

print "Sizes before swap"
print GetArrayElements(Test())
print GetArrayElements(Dude())

swaparray Test(),Dude()

print "Sizes after swap"
print GetArrayElements(Test())
print GetArrayElements(Dude())

print "Data in test array"
For lp =0 to GetArrayElements(Test())
print Test(lp)
next


print GetArrayStatus(Test())
print GetArrayStatus(Dude())

movearray Test(),Dude()

print GetArrayStatus(Test())
print GetArrayStatus(Dude())

print "Data in test array"
For lp =0 to GetArrayElements(Dude())
print Dude(lp)
next

SortArray Dude(),0,100

print "Sorted data in test array"
For lp =0 to GetArrayElements(Dude())
print Dude(lp)
next

EndFunction Test()




cybermind

I am trying to sort a string array with 830 elements. The program stops when it hits the sortarray command. If I change the last parameter of sortarray to 10 it works.

This is part of my inventory code, the program halts when it touches sortarray :-( Am I doing something wrong? Do you need more data?

if current_in_game_menu_selected$ = "Inventory"
u = 0
dim inventory_list$(830,2)
dim sortable_inventory_list$(830)
for t = 1 to 830
if item_holding(t) > 0
inc u
`transfer item name
inventory_list$(u,1) = item_text$(t,1)
`set pointer to item position in original inventory array
inventory_list$(u,2) = str$(t)
no_items_in_inventory = 0
current_selected_inventory_item = 1
first_item_to_show_in_inventory = 1
`make string for adding correct number of zero's to sortable inventory list number
if t < 10 then zero_string$ = "00"
if t > 9 and t < 100 then zero_string$ = "0"
if t > 99 then zero_string$ = ""
sortable_inventory_list$(t) = item_text$(t,1)+zero_string$
endif
next t
sortarray sortable_inventory_list$(),0,830
for t = 1 to 830
if sortable_inventory_list$(t) <> ""
length = len(sortable_inventory_list$(t)) - 3
inventory_list$(val(right$(sortable_inventory_list$(t),3)),1) = left$(sortable_inventory_list$(t),length)
inventory_list$(val(right$(sortable_inventory_list$(t),3)),2) = str$(val(right$(sortable_inventory_list$(t),3)))
endif
next t
if u = 0
current_selected_inventory_item = 0
no_items_in_inventory = 1
endif

kevin

#20
 why would you post that here ?  when here seems like a much more appropriate place

kevin

#21
PlayBASIC To Dll  -  Array Searching Functions

        Added a replacements for the  FindArrayCell & SearchLowest/Highest functions.    I'm not too keen on the behavior of the lowest / highest functions really, since they seem to skims the entire data set only returning when complete, was expecting it to only return when the data was bellow the users  threshold.    

       
PlayBASIC Code: [Select]
Function DLL_SearchArrayTest(Size)

Dim Table(100)

for lp =0 to Size
Table(lp)=1000+lp
next

Print FindArrayCell(Table(),0,1,size+1,1010)
Print FindArrayCell(Table(),Size,-1,size+1,1010)

Print SearchLowestArrayCell(Table(),0 ,1 ,size+1 ,1010)
Print SearchLowestArrayCell(Table(),Size ,-1,size+1 ,1010)

Print SearchHighestArrayCell(Table(),0 ,1 ,size+1 ,1010)
Print SearchHighestArrayCell(Table(),Size ,-1,size+1 ,1010)

EndFunction





      EDIT #1: 

         Added the replacement split to array function, this one is lifted from VM3 couldn't be bothered with writing it again :)

PlayBASIC Code: [Select]
Function DLL_SplitToArray(S$)

Dim Table(1)
Dim Table#(1)
Dim Table$(1)

Count=SplitToArray(S$,",",Table(),0)
Count=SplitToArray(S$,",",Table#(),0)
Count=SplitToArray(S$,",",Table$(),0)

print Count
For lp =0 to Count-1
print " "+str$(Table(lp))
print "#"+str$(Table#(lp))
print "String="+Table$(lp)
next


EndFunction







kevin


  PlayBASIC To Dll  -  CopyArray and CopyArrayCells

       It's summer time here and we're in the middle of a heat wave for our region (most of the country really), so long stints in the tiny office are virtually out of the question,  only short sessions for most of this week.  None the less, rewrote a couple more functions from the array library, those being the CopyArray and CopyArrayCells functions.  CopyArray does exactly that, it copies the entire source array (integer/float/string or typed) and places the newly created structure in the destination array.     CopyArrayCells lets the programmer copy a run of cells between arrays of the same type from one to another.  It also supports integer / float / string and typed arrays, but doesn't support combinations.     It's tempting to drop in some combo modes for the Cells variant, while i doubt it'd get used,   if you have an array full of numeric strings then could Copy all of them or section directly to the Integer and or float array, and it's convert them for you,  much like what SplitToArray does.

      Here's our regular junk test code. 

PlayBASIC Code: [Select]
   Type GoodStuff
a
b#
c$
endtype

makearray b().goodstuff

for lp =0 to 10
s$+=str$(rnd(100))+","
s$+=str$(rndrange#(1000,2000))+","
s$+="cool"+str$(rndrange#(10000,20000))+","
next

b()=DLL_CopyArrayCells(s$)

Sync
waitkey





      This is the function that's been exported to our test DLL.   The routine just splits the input string with SplitToArray, then copies those fragments into a local typed array,  where it copies some cells from that into another array.  The code doesn't  do anything at all useful, but it works and that's all that matters right now.

PlayBASIC Code: [Select]
Function DLL_CopyArrayCells(S$)

Dim Table$(256)
Count=SplitToArray(S$,",",Table$(),0)

print COunt

Dim d(Count/3) as goodstuff

for lp =0 to (count/3)-1
d(lp)= new goodstuff

d(lp).a =val(Table$(Index)) : Index++
d(lp).b# =val#(Table$(Index)) : Index++
d(lp).c$ =Table$(Index) : Index++
next

Dim output(100) as goodstuff

CopyArrayCells D(),0,1,Output(),50,1,Index

Endfunction Output().GoodStuff





kevin


  PlayBASIC To Dll  -  Data Statements


      There's only a few tidbits left in the entire conversion felt to do and DATA statement support is one of them.    Initially I wasn't going to bother with them, in favor of resource binding, but local data is probably easier for most people to wrap their heads around.  There's a few gotcha's with exporting data to our DLL's though and that's visibility.    In the VM data is global regardless of where it's defined, so the data heap exists outside of the program code.    Now if you compile a PlayBASIC source to DLL that has Data in it,  now the question is who should have access to that data.

      It's viable that the VM could retrieve any local data from the a bound dll and pull it into the global pool on the VM side, but then the dll's have to use the VM access.   Which is about as painful as it sounds !  - I think i'd much rather that any data be local to it's dll, which makes a lot more sense in terms of encapsulation anyway.   If the dll's shared data with the main program this could affect the users code in the VM or even in other bound dll's.   Making it a slippery slope.   

      The DATA command set was re-written for the VM3  prototypes, but after looking over the replacement code,  there's a few areas where it could be further improved.  I think some of those changes would have to be made at compiler level to really see the benefits, but one thing that comes to mind would be replacing how the string heap is set up.  If you have a lot of string data in a program and those strings share fragments/words, then there's some lost opportunities in terms of compression.   

       Ie.

        Data "Hello World", "Hello World2"

       The above data fragment would store two strings, since they're not a perfect match. But what it could do at build time is compare smaller fragments with the raw pool.  So any matching runs would compress away, both still exist, it's just one is a part of the other.   

       Stuff like this doesn't make any significant difference to small programs, but as programs get bigger the amount of partial collisions increases.   



ATLUS

When it will be released? And how much is it?

kevin

 As mentioned in the 2014 Blog (login required),  no decision has been made about cost or any release date.    It won't be before the next upgrade is released though.


kevin

#26
 PlayBASIC To Dll  -  Data Statements Cont.

       It was Australia Day yesterday, but here I am slaving away at this app.  Tonight's focus continues to be data commands, ran into something of a hiccup last session where For/Next loops would break if a restore command (or any data command) was used inside them, turned out to be how translator identifies the loop start  could break after a label was used.   For/Next loops don't actually exist in the byte code version of your programs so it's not as simple as i'd like detecting them.  Will have to add some tokens into the exported byte code as a  fail safe.  

       So far the Data heap is built locally into the dll with an interface to access it.  The scope of data is local to the DLL, so the VM can't see the data inside any DLL.   Nor can one dll look inside another.    Bellow we have a function that compiles and exports. The code just creates a simple data heap with a function to run through the data heap dynamically.    The DLL has no runtime errors.  So if types don't match, it's not going to hold your hand.   This is a none issue really since, you'll only ever be exporting code when it's fully tested.   Yeah right :)

   
PlayBASIC Code: [Select]
Label1:
Data 10
Label2:
data 20,30,40,50

Label3:
Data 111.111,222.222,333.333 ,444.444, 555.555

Label4:
data "hello world1","hello world2","hello world3","hello world4","hello world5"

; restore Label1
; restore Label2
; restore Label3
; restore Label4


Function DLL_DATATEST()

; restore 0
; restore 1
; restore 2

print GetDataPointer()


Label4=10
restore Label4

; print GetDataPointer()


print "Reading data types on heap"
Count=GetDataQuantity()
print "Count:"+str$(count)

for lp=0 to count
restore lp
DataType=getDataType()
s$= Str$(lp)+" ="+str$(getDataPointer())+" type="+str$(DataType)
Select DataType

case 0
s$+=" Data="+str$( ReadData())

case 1
s$+=" Data="+str$( ReadData#())

case 2
s$+=" Data="+ReadData$()
endselect
print s$
next


EndFunction





   Edit #1

       Restore in Pb supports three types of usages.   There's the absolute form, where the parameter is a label, a computed form where the parameter is a integer/float and a search form when it's passes a string.   All those are now in the dll back end.   Only thing left is the FindDATA function which is hooked up but not working at the moment.  


PlayBASIC Code: [Select]
   Data "StartBaby"

Label1:
Data 10
Label2:
data 20,30,40,50

Label3:
Data 111.111,222.222,333.333 ,444.444, 555.555

Label4:
data "hello world1","hello world2","hello world3","hello world4","hello world5"

; restore Label1
; restore Label2
; restore Label3
; restore Label4


;DLL_DATATEST()

; sync
; waitkey



Function DLL_DATATEST()

; restore 0
; restore 1
; restore 2

print GetDataPointer()


Label4=100
restore Label4

; print GetDataPointer()


print "Reading data types on heap"
Count=GetDataQuantity()
print "Count:"+str$(count)

for lp=0 to count
restore lp
DataType=getDataType()
s$= Str$(lp)+" ="+str$(getDataPointer())+" type="+str$(DataType)
Select DataType

case 0
s$+=" Data="+str$( ReadData())

case 1
s$+=" Data="+str$( ReadData#())

case 2
s$+=" Data="+ReadData$()
endselect
print s$
next



print "restore tests"
restore "hello world2"
print GetDataPointer()
restore "StartBaby"
print GetDataPointer()


print "find Data tests"
print FindData("hello world3",0)
print FindData(50,0)
print FindData(222.222,0)

EndFunction






     Edit #2

     FindData is in and working now.   Added a option to make it search from the current Data Pointer position to the end (or top) of the data.   To do this set the 'position' field -1.   Of course this is currently only available in the exported DLL's,  i'll have to add this functionality to PlayBASIC V1.64P later.     




kevin

#27
 PlayBASIC To Dll  -  Function Calling

      So far this has been in fairly steady development now for probably 5 months now and would you believe that up until only a few minutes ago, you couldn't call any user defined functions in the your DLL.    You can call PSUBS, they're all up and running, but only now can you call an exported user defined functions (those prefixed with DLL_ are public functions) inside your dll.  However you still can't call private functions due to some VM dependencies that need ironing out.    It's possible to hook up as it stands, but the VM is doing things that the DLL version doesn't need to, which might get in the way.


      One of the major changes in the newer prototypes of the VM (such as VM3) was a altering how it handled calling and returning from functions.   The legacy solution works fine, but the newer approach is cleaner and more inline with how it needs to work when exported.   So there's that annoying little voice in the back of my head wanting to 'update' it to bring everything inline.  Dunno we'll see how painful getting it work as is first I think.


      Bellow is the normal junk test to see if it's even working.


PlayBASIC Code: [Select]
   Data "StartBaby"

Label1:
Data 10
Label2:
data 20,30,40,50

Label3:
Data 111.111,222.222,333.333 ,444.444, 555.555

Label4:
data "hello world1","hello world2","hello world3","hello world4","hello world5"




Function DLL_TEST_FUNCTION_CALLING()

result=DLL_Passing_Parameters(100,123.456)
print "result after calling passing parameters function"
print result

result#=DLL_Passing_ParametersFloat(100,123.456)
print "result after calling passing parameters function"
print result#

result$=DLL_Passing_ParametersString(100,123.456,"This is a test string")
print "result after calling passing with string parameters function"
print result$
ink $ffff0000
print "DONE"
ink -1
result=DLL_DATATEST()
print "called wrapp function"
EndFUnction result


Function DLL_Passing_Parameters(A,B#)

print "Passed"
print a
print b#
result =a+b#

EndFunction result

Function DLL_Passing_ParametersFloat(A,B#)

print "Passed"
print a
print b#
result# =a+b#

EndFunction result#


Function DLL_Passing_ParametersString(A,B#,Cool$)

print "Passed these params in"
print a
print b#
print Cool$
result$ =str$(a+b#)+cool$
print result$

EndFunction result$

Function DLL_DATATEST()


print GetDataPointer()

Label4=100
restore Label4

; print GetDataPointer()

print "Reading data types on heap"
Count=GetDataQuantity()
print "Count:"+str$(count)

for lp=-10 to count
restore lp
DataType=getDataType()
s$= Str$(lp)+" ="+str$(getDataPointer())+" type="+str$(DataType)
Select DataType
case 0
s$+=" Data="+str$( ReadData())

case 1
s$+=" Data="+str$( ReadData#())

case 2
s$+=" Data="+ReadData$()
endselect
print s$
next

print "restore tests"
restore "hello world2"
print GetDataPointer()
restore "StartBaby"
print GetDataPointer()

print "find Data tests"
print FindData("hello world3",0)
print FindData(50,0)
print FindData(222.222,0)

print FindData(50)

EndFunction result


   


  There's one limitation that comes to mind with exporting strings from the DLL either back to the VM or another.   Which is that strings are returned by pointer, rather than handle.  Returning the pointer means that receiver has to copy from the pointer to the target string.   Since it doesn't know the size, it'll treat the string as null terminated.    This could  be an issue as PB supports binary safe strings, so a zero character is legal with a string.  One way around this potential problem would be to return a typed pointer with a string field in it.


PlayBASIC Code: [Select]
  Type MyString
Result$
EndType


DIm S as MyString Pointer

S= DOStuff()

print S.Result$

Sync
waitkey

Function DOStuff()
me = new MyString

Me.Result$="bla bla bla"
EndFunction Me as MyString POinter





kevin

#28
  PlayBASIC To Dll  -  Function Calling Cont.  

     Yesterday was get function calling done day, but things didn't turn out how I'd hoped.  In fact spent almost 12 hours on it, only to run into brick wall after brick wall.   Was initially going to wrap up some functionality in the library to handle an emulated VM stack, which I know would work and would be a perfectly fine solution, but it just doesn't sit well with me.  My preferred solution is to use the system stack, but that's where we hit a few dramas...   PB's internal calling convention has type casting wedged into it.  This makes sense in the VM, since it reduces the number of VM ocodes needed to execute a new scope.   But it means that any interface has to be data type aware when pushing or popping.   Macros to the rescue !

     The solution was to write a set of tiny macros that emulate the behavior of the VM in assembly. Since they're macros, the code is inlined into our exported dll code.  So there's no calling a VM function to do the task for us.   The up side is it's about as fast as you can get it, the down side is it adds a little extra bloat to the amount of code that's produced around a function and a function call, but personally it's not enough to worry about.

     So far, the caller only supports passing primitives such as Integer/Floats & Strings in and out.  Multiple returns (inside PB) are supported, but I haven't actually tested to see if they work correctly as yet.   Pointer and Array passing isn't currently hooked up..  

     Some test code:

PlayBASIC Code: [Select]
Function DLL_ExportMe(A,b#,s$)

Dim Dude(100)

Print "Calling Local PB function"

result=TestFunction(A,b#,s$,Dude())

print "result="+str$(result)

EndFunction result


Function TestFunction(A,b#,s$,Dude())

print a
print b#
print s$

result=A+b#

print "array Status"
if getarraystatus(Dude())

print GetArrayElements(dude())
endif

EndFunction result





    In this program we see two different types of functions. The first one DLL_ExportMe is our public / exported function.   This function is visible to other programs that load this compiled DLL.  Since it's a DLL function,  it uses the STDCALL calling convention rules.   The second function TestFunction (which is called from the first in this example) is a local function to the dll.  This function is not visible externally and uses the PlayBASIC's  calling convention internally, so even if you have a pointer to this code, it's not callable externally.



    EDIT #1 - testing multiple returns from functions

         This seemed like it initially would work, but there was problem with the translator using a previously defined instruction in an unexpected way.     Once that that was picked up it seems to work OK. 

         Recursion isn't currently supported though.

PlayBASIC Code: [Select]
      a=666
b#=123.456
s$="hello"


DLL_ExportMe(A,b#,s$)
Sync
waitkey



Function DLL_ExportMe(A,b#,s$)

Dim Dude(100)

Print "Calling Local PB function"

result=TestFunction(A,b#,s$,Dude())
print "result="+str$(result)

result,result2=TestFunction(A,b#,s$,Dude())

print "Multi returns"
print "result="+str$(result)
print "result2="+str$(result2)

print "------------------------"

EndFunction result


Function TestFunction(A,b#,s$,Dude())

print a
print b#
print s$

result=A+b#
result2=result+1000

print "array Status"
if getarraystatus(Dude())

print GetArrayElements(dude())
endif

EndFunction result,result2






kevin


  PlayBASIC To Dll  -  Function Calling - Recursion

     Parameter passing (for PB functions) was hooked up a few days ago now, but recursion wasn't supported until yesterday.   The only problem is that even through the stack alignment was correct,  the test code just wouldn't match the VM.    On the surface the procedures appeared to have same functionality, but it turned out one of the macros I'd written to save time :) , had some faulty logic in it.   Fixing it was easy, but it didn't solve the problem with recursion as I'd made a few opt's to try and reduce some string thrashing, so had to add some pre-screening to the translator to detect the problematic situation, which seems to have resorted it to working order. 


     Here's the function, in the screen shot it's being called from the exported DLL function (in a previous code snippet) so that's spitting out some other data..

 
   
PlayBASIC Code: [Select]
Function Recursion(ThisValue,Cool#,abba$)

dim Wally as integer pointer

print "STart Int="+digits$(ThisValue,2)+ " Float="+str$(Cool#)+" string="+Abba$

ThisValue+=1
Cool#+=11.2233
abba$+="abba"

if ThisValue<10
; Recursion(ThisValue+1,Cool#+11.2233,abba$+"abba")
Recursion(ThisValue+1,Cool#+11.2233,abba$+"abba")
endif
print " End Int="+digits$(ThisValue,2)+ " Float="+str$(Cool#)+" string="+Abba$
EndFunction