News:

Building a 3D Ray Tracer  By stevmjon

Main Menu

PlayBASIC To DLL (Development Blog)

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

Previous topic - Next topic

kevin

#30
   PlayBASIC To Dll  -  Function Calling - Recursion Done

         Why it is always the last item on any 'to do' list, where problems always seem to occur.   No sooner had I ticked the recursive box off the list,  do we discover that the returns don't work with recursion in most cases.    Initially it didn't seem like much of a drama, but then you find yourself sitting in front of the PC two days later, with no solution in sight.  Then you know it's not going to be easy.   The Issue(s) just come from how instruction set in the original VM's work.  What I had been doing is trying to unify the data into one clean system within the DLL, but the order in which things need doing in the classic VM (pushing and popped)  are at odds with that.   Forcing me to the emulate it's VM behavior.     

          Another sort of related issue, was returning strings from functions had a nasty habit of crashing the DLL.     This is was just plain odd, since strings are passed into a function the same way they're returned.    If it's exactly the same code, then  what's was the problem ? -  Well.. the issue was due to not protecting the EAX register when cleaning the string up for return.   Which could make it crash and burn..  


          But anyway, here's the latest function calling tidbit..  

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

oldptr=DLLDebug_STACKPC()
result=Compiled_ExportMe(a,b#,s$)
ptr= DLLDebug_STACKPC()
; cls
print oldptr
print ptr

; print PeekInt(ptr+0)
; print PeekInt(ptr+4)
; print PeekInt(ptr+8)
; print PeekInt(ptr+12)

; Address=PeekInt(ptr+4)

; for lp =0 to 50
; a=b
; #print PeekInt(address+lp*4)
; next

Recursion(0,123.456,"First")


sync
waitkey
end


print result
print ""
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 "Recursion Test]------------------------"

ThisisMyString$=TestReturnString()
print ThisIsMyString$


Recursion(0,123.456,"First")

print ""

EndFunction result


Function TestReturnString()
result$="--->> Retured this string from a function"
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


Type WallyStructure
x,y,z
endType


Function Recursion(ThisValue,Cool#,abba$)

dim Wally as WallyStructure pointer

Wally = new WallyStructure

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

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

if ThisValue<10
; Recursion(ThisValue+1,Cool#+11.2233,abba$+"abba")
abba$+="A"
ReturnedString$=Recursion(ThisValue+1,Cool#+11.2233,abba$+"C")+">>>>"
print " returned string="+ReturnedString$

endif


print " End Int="+digits$(ThisValue,2)+ " Float="+str$(Cool#)+" string="+Abba$+" Ptr="+str$(int(Wally))
free Wally

EndFunction Abba$





 

         




kevin


  PlayBASIC To Dll  -  Cleaning Up The Code

          It's finally cooled down around here, so today has become clean up day for this project.   The current source code weighs in at around 21,000+ lines of PlayBASIC code.  So it's a pretty big project on it's own (without GUI ), and like all big projects bloat creeps into the code over time.  You know things like alternative functions / methods that are no longer in use, that sort of thing.   Handy when you're testing something, but beyond that they're pretty useless.    Should be able to strip a 1000 plus lines, maybe more..  Need to start getting it ready for putting a front end on it, not that it'll be all that exciting to look at..     

          Command set wise,  been looking over the missing instruction list last night and there's really only the real dribs and drabs left now.  Probably the main ones would be the CallFunction  set.   While they're not used too much by the community, I tend to use them  when abstracting some code like GUI behaviors.   To support them, means building a function table on the DLL's side.    You wont be able to call functions outside of the DLL though.  For much the same reasoning as the DATA commands can't see outside of the DLL they live in.     It'd be nice to call a function back in the VM from the DLL, but that seems impossible at this point. 

         

kevin


  PlayBASIC To Dll  -  Bound Function Calling (linkDLL/EndLinkDLL)

      If your code calls external DLL's, then in PB you'd either write a LinkDLL/EndLInkDLL block or use the CallDLL functions from the DLL command set.  Linking the DLL is generally the better solution, since everything can be solve prior to runtime.    Moveover the VM supports loading/  and executing DLL functions directly from memory.    Exported DLL can't do that for the moment, but they can now prepare calls to other linked external dll's.   So if you linked some windows functions or use some external physics library etc etc, then those calls resolve just like any other now.

      In more recent years,  many of the new commands added to PlayBASIC have been internally bound, that means they're not part of the VM instruction set at all.   Rather they're just linked via LinkDLL/EndLinkDLL blocks declared in the startup code.     The startup code is what your code is appended after.   

      There's about 80 internally bound functions in V1.64P,  some examples of them would be Interval functions, resources, platette mapping etc
     
PlayBASIC Code: [Select]
Function DLL_BOUND_FUNCTION_TEST()

STARTINTERVAL(1)
for lp =0 to 1000000
a=a+b*3
next
print EndInterval(1)
print "Cpus In System:"+Str$(CPUCOUNT())

EndFUnction






kevin

#33
  PlayBASIC To Dll  -  Odds and Ends

      Still working on the LinkDLL->>Call Function stuff, but have hit something of an stale mate for the moment, but rather than dwell on it for too long i've been looking for more commands that have been missed.  Found a few, the first was the GetArrayPtr()  function.  This function pulls the address of the arrays buffer in memory, it includes two modes the first gets the first byte of the arrays header and second is the first byte in the arrays data.    It's very handy if you want to roll your own array functions to man handle array data.     The others were pointer casting functions.  Which are obscure enough that i've be amazed if they've ever been used.    

      The sample chunk bellow calls some linked dll functions and messes around with the missing functions.  Most GetArrayPtr and the pointer cast functions end up inlined into the output assembly, so we avoid some more function calling overhead.  Meaning faster execution of those operations.


PlayBASIC Code: [Select]
Function DLL_BOUND_FUNCTION_TEST()

; hideMouse(0)
STARTINTERVAL(1)
for lp =0 to 1000000
a=a+b*3
next
print EndInterval(1)
print "Cpus In System:"+Str$(CPUCOUNT())


Dim Table(100)

print GetArrayPtr(Table(),0)
print GetArrayPtr(Table(),1)

print GetArrayPtr(Table(),Value)
Value++

Dim Ptr as integer pointer

Ptr=GetArrayPtr(Table(),Value)

v1=BytePtr(Ptr)
v2=WordPtr(Ptr)
v3=IntPtr(Ptr)
v4=FloatPtr(Ptr)


print Int(ptr) ;

print "recast pointers"
print v1
print v2
print v3
print v4

undim Table()
value=0
print GetArrayPtr(Table(),Value)
Value++
print GetArrayPtr(Table(),Value)


EndFUnction





PlayBASIC To Dll  -  RCE (Retro Computer Environment) Test conversions to machine code

    The RCE is just an emulation of a mock up 8bit system (Read More),  written as a kind of learning tool for those unfamiliar with binary operations.     The emulation is pretty heavy in terms of computations, making it a good candidate for conversion to a DLL.     On my old  athlon system the runtime can draw the test screen in about 60fps,  which is actually fast enough for that particular program, but when we convert it to machine code, it's running an 230Fps on the same system.  

     To really put in perspective, the VM version takes about 16.7 milliseconds to render each frame, while the dll version takes about 4.3 milliseconds, that's a very healthy return given all I had to do is point PlayBASIC to DLL at it.   You could probably get this particular demo even faster, by replacing the some of the external function calls with your own binary operations..    

   Demo project (DLL) is attached is bellow the piccy.. It's not very pretty but food for thought :)

PlayBASIC Code: [Select]
linkdll "ByteCode.dll"

RCE_CLS() alias "rce_cls"
RCE_MouseX() alias "rce_mousex" as integer
RCE_MouseY() alias "rce_mousey" as integer

RCE_ScreenWidth() alias "rce_screenwidth" as integer
;' RCE_ScreenHeight() alias "rce_screenheight" as integer ; forget to export this function :)
RCE_ScreenPtr() alias "rce_screenpointer" as integer

RCE_Dot(x,y,color) alias "rce_dot"
RCE_Box(x1,y1,x2,y2,color) alias "rce_box"
RCE_Sync() alias "rce_sync"

endlinkdll


Do
cls 0


; calc mouse position over the simulated video memory
MX=RCE_MouseX()
MY=RCE_MouseY()

; get mouse state
State=MouseButton()

; if left mouse is pressed, draw a dot in colour 1
if State=1
RCE_Dot(mx,my,1)
endif

; if right mouse is pressed we draw a box
if State=2
RCE_Box(mx,my,mx+50,my+50,1)
endif


; if space key is pressed
if Spacekey()=true
RCE_CLS()
endif

; draw the RCE screen and flip PB screen
RCE_SYNC()

print RCE_ScreenWidth()
; print RCE_ScreenHeight()
print RCE_ScreenPtr()


Sync
loop esckey()




kevin

#34
  PlayBASIC To Dll  -  Swap, SwapIfLower & SwapIfHigher

      The swap commands are really low level VM instructions, In other words they're not actually functions/command at all.   Support for  Swap was added previously, but it only supported situations when the two fields are of the same type.  Ie  Swap  A,B    SWap  A#,B#,  But the VM actually allows any combination to be swapped, dunno why you would.. but it allows it.  This means the translator has to be support this also.

      After looking at the Swap translator, ended up doing down the macro approach again.  It just means the  translator code inserts a standard macro for each possible situation, allowing the same generator code to be used for the Swap, SwapIfLower and SwapIfHigher opcodes.    The SwapIF variants are less known, but they're the same as code like this   If A<B  then swap A,B  .  Just that operation is packed into  a single operation in the VM.  They exist since it's generally quicker to have the VM do higher level combinations of logic than lots of small lower level micro logic..

      To export the SWapIF opcodes to assembly, means the translator has to create an the IF/THEN styled block above.  So output code compares the parameters and branches accordingly.   Only swapping the when the condition  (less than or greater than) are meet.  Thankfully I can just hide that away in the macros and exported code looks almost like BASIC..  


PlayBASIC Code: [Select]
   v1=100
v2=200

v1#=v1+0.5566
v2#=v2+0.1235


print "SwapIfLower Operator II"

a=v1
b=v2
print a
print b
swapiflower a,b
print a
print b


print "SwapIfLower Operator IF"
a=v1
b#=v2#
print a
print b#

swapiflower a,b#
print a
print b#


print "SwapIfLower Operator FI"
a#=v1#
b=v2
print a#
print b
swapiflower a#,b
print a#
print b


print "SwapIfLower Operator FF"
a#=v1#
b#=v2#
print a#
print b#
swapiflower a#,b#
print a#
print b#



     

    PlayBASIC To Dll  -  MinVal/MaxVal functions

        These are another 'function' that doesn't actually exist in the bytecode, but they use one of the lower level decision instructions that didn't support mixed data types.   So code like  MaxVal(100,123.456)  would fail.


PlayBASIC Code: [Select]
   v1=100
v2=200

v1#=v1+0.5566
v2#=v2+0.1235

print "MInValue Functions"

print MinVal(v1,v2)
print MinVal#(v1#,v2#)

print "MaxValue Functions"
print MaxVal(v1,v2)
print MaxVal#(v1#,v2#)
print MaxVal#(100.5,200.5)









kevin

#35
  PlayBASIC To Dll  -  Range / ClipRange functions

      Added support for the integer Range instruction yesterday, you can't mix types at this point.  For expressions that pass Range a mix of floats, it's probably going to be best solved by calling a function (the call size is smaller than my inlined code).    Which is something I've been trying to avoid for maths functions where practical.  

      The ClipRange functions are borderline also, I suspect there's situations where the output code could be simplified more than calling a one size fits all function , like where the range terms are literal.   If that occurs, it could output a compare block in it's place.  Which would be faster at runtime !

      ie.
 
      Result=ClipRange(SomeValue, 100,200)

      could become something like this (in BASIC styled logic)

       Register=SomeValue
       if Register<100 then Register =100
       if Register>200 then Register =200
       Result =Register
       



      I could just take the easy way and cookie cut everything, but there's plenty of BASIC compilers that already do that..  :)




PlayBASIC To Dll  -  For/Next Problems
       
        It's not all rosey though as one of the problems i've been running into recently, stems from the very nature of the byte code itself.  The VM uses self modification in places, which is very handy for removing decisions from certain opcode pairs.   But can make translating the logic back something of a pain.

        For/Next loops are one of those areas, where the compiler produces all number of combinations at the start the loop sequence, but these opcodes are generic, there's nothing special about them and they appear in other constructs as well, so detecting exacting what's going on isn't turning out to be that easy..    The result of which, is sometimes the translator misses the FOR loop set up code, when that occurs the for/next doesn't work in the exported DLL.  I'm hoping there will be some rainbow moment where I can figure out a bullet proof solution, but nothing coming to mind at this point.

        There's a few solutions, worse case would be adding bread crumb opcodes to the byte code in order to help any translator(s) process down the line.   Another approach would be change how the VM works and use add loop start opcode which would make it easier to translate and possibly at runtime for VM also.    I kind of favoring the latter, but we'll see..


kevin

#36
   PlayBASIC To Dll  -  Continued Misc functions & operators clean up

        Last week was spent shuffling opcodes in the PB runtime & compiler, so anything that could be blocked into a standard command set, was..  This allows the translator to simply export the command/function calls via table.    This doesn't work for other core operations though and there's a bunch of them that haven't been fleshed out completely.  Meaning some input combinations would break the exported code, in particular when passing 'floats' into a function/operator that's expecting integers.  

        So this morning->afternoon i've been picking through and have added support for Powers,  WrapAngle, CurveAngle,CurveValue and few others.    Really starting to struggle for operations that aren't implemented fully actually.   Which means we can start looking at some real live testing.  


  Benchmarking - ABS function
     
         Many of the core functions resolve inline in PLayBASIC-To-DLL, which can really make a lot of difference to the end performance.     The following code contains a for/next loop computes two ABS() results and adds them together.    Over 1000000 loops the code runs consistently around the 45->50 millisecond mark,  which is over twice as fast as one competitor (105->110 milliseconds) and  executes in around 48 times faster than another competitor.  (2400+ milliseconds)


PlayBASIC Code: [Select]
Function DLL_ABS_TEST()

i = -396
print Wrapangle(i)
print Wrapangle(i,45)



r#=curveangle(I,i2,45)
print r#

r#=curvevalue(I,i2,45)
print r#

r#=cliprange#(I,i2,45)
print r#

r#=cliprange#(I,99.45,45)
print r#


r#=cliprange(I,99.45,45)
print r#

r#=cliprange(I,99.45,45)
print r#


i=2
abba=2233445


print i^8
print i^4.5

print "ABS TEST"

count = 10000000

Value1=1
Value2=-1
t = timer()
for i = 1 to count
x = abs(Value1) + abs(Value2)
next
print timer()-t : sync
print x
print i


u=1
print RoundUP(u)
print RoundDown(u)

print RoundUP(1)
print RoundDown(1)


EndFUnction





kevin

#37
  PlayBASIC To Dll  -  GUI time

      The translation engine is pretty solid really and has been for a while, but the conversion is one thing, testing the the final products output is another.   So we need to set this up in a way that's as simple as possible really.   In the current builds, there's no GUI at all and to build anything, I've got to specifically layout the file names and  paths.   Once set though, the engine takes care of the entire process.  It's not pretty as we're first calling PlayBASIC with your source code, which will then compile it,  then grabbing the byte code module, translating that and sending to the assembler.    So there's a few pop up's in the build process.  Can probably clean that up a little, but it'd be purely cosmetic..

      The front end i'm just dragging from the Amos-To-PlayBASIC project.  I can think of few settings it needs, but beyond that, there's not really much input needed from the user, other than picking a source file.   Generally when I make these types of tools, I write the 'guts' of the project first, then cut'n'paste the GUI together around it.  So the projects merge into one big project.   The thing is, it's sometimes handy to be able to work on the engine code without the GUI, so do this we build two projects.    There's the 'engine' project an the 'Gui' project.   These sit it two separate folders and the GUI project would reference the source files from the engine project.   The thing is if you sprinkle any GUI code in the engine project, then it'll no longer build on it's own.  To get around this, any GUI code in the engine project need to query the GUIs existence.  If it doesn't exist, then have the compiler remove the code completely

      Something like this,

PlayBASIC Code: [Select]
   #ifdef GUI_PRESENT

GUI-CODE-GOES IN HERE

#Endif

Sync
waitkey




       So code that's talking with GUI side can be effective removed depending upon what project you compile.

       Another method would be via say by using the FunctionIndex to detect if a function is present at compile time and  set up a the constant in this project,  it'll do the same thing.

PlayBASIC Code: [Select]
   constant GUI_EXIST = functionIndex("NAME OF GUI ONLY FUNCTION GOES HERE")>1

#if GUI_EXIST
GUI-CODE-GOES IN HERE
#Endif

Sync
waitkey





       One of the down sides of using lots and lots of includes files in the legacy IDE,  is they tend to slow down the IDE'S pre-processor dramatically.   The compile time might only be a couple of seconds when it's actually built, but IDE might take 10, 20 seconds to parse everything out initially.    So sometimes as projects get bigger, it can just be easier / faster to just bundle all files you want from one(or more) projects into one/two big includes.   Then attach the bundled code to the project.    The down side means you can't 'change' the that source code, as it's physically a different copy of the original external source, but a small price to pay once you get your head around it.  

       So far in terms of the GUI i've got the frame work running, and are in the process of setting the translator engine code so there's no naming collisions.   Will probably use a combination of the above methods,  the translator needs a legacy PB project file parser/loader, which could in turn be used to bundle itself for the gui in the need takes me.    The loader works pretty well, loading the entire PlayBASIC to DLL project (main sources) in 25 milliseconds.   Which is peanuts..    Will have to add pre-processor to it though and that'd slow it down a bit..  


kevin


  PlayBASIC To Dll  -  GUI continued

       Been having a hardware issues lately, namely my main dev box has been BSOD randomly, been trying to isolate the drama for a while now, and seems like they've subsided for the time being, even so I'm not willing to put a lot of faith in it as yet and are setup a another dev box to move across to.   Which as i'm sure we're all well aware can be quite painful.  In the mean time I've been working on mainly stream lining the PB2DLL sources and build  process.

       Up until now, the PB2DLL build  process would use a hack in the PB compiler to dump the compiled byte code prior to the handing execution over to the VM.   The hack is really legacy addition from the disassembler project, which was useful at the time, but PB2LL needs an official way of calling the compiler to get the required response.  So this means adding a command line directive to the PB compiler.   The directive just make the compiler build the project with the required output settings so it can save the byte code module and exit.   The PB2DLL tool really has to check the status of the build process here.   As it's possible the code you pass it might fail to compile, which kill the process.   

      The other chore has been in cleaning up the translator code so that the GUI and translator engine can exist either separately or merged.  Since the project was based on the disassembler code, this wasn't too difficult to achieve, just needed some renaming to make sure there's no clashing of function/constants between the  projects.   Been able to strip out a fair amount code from the translator that's become obselete.  Even so, it still weighs in at around the 850K mark.     

      Today's little task is to make a customized version of the project merger that will build the complete translator source code and then drop the output in the GUI folder.   


  Edit:

       It's now about 10 hours later and i'm in the process rescuing my latest source codes for basically everything from this dying vessel after another frustrating afternoon.

kevin

#39
  PlayBASIC To Dll  -  GUI - Up And Running !


     After a frustrating week of playing hard drive roulette, yesterday i've continued putting all the parts together.  The build process is broken into two halves, there's the GUI side and translation engine side.  Unlike other helper apps, these are two separate projects, which are combined using a slightly more customized version of the project loader/source merger.   This version of the tool lets me wedge the Engine into the GUI easily and rips unwanted blocks of the code from the engine.  The pre-processing tool works via looking for tokens inside comments.  So the tokens mean nothing to PB, but the pre-processor tool just skims through looking token pairs and rips anything between them.  


    Example,

PlayBASIC Code: [Select]
; [REMOVECODE]
function Some_Function_Not_USed_By_GUI()

EndFunction
; [/REMOVEDCODE]





      So the code (in this case a function) is included when engine when testing separately, but would be ripped when the engine is merged for inclusion into the GUI.  I often use such approach to substitute methods in the final source codes for example.     Once the engine source is built, I just include it into the GUI project and off we go.  The GUI side holds the high level properties and just calls the conversion functions from the engine.  To make the engine project output to the GUI rather than the odd print statement we just wrap up our own print function.  So depending upon the context, the wrapped function either uses the GUI console or a print to screen.


      When running the final program the user has to initially locate their PlayBASIC.exe compiler file.   This will generally be in your My Programs or  Program Files folder it's different between OS's.  I can sniff this out only when install in the most common location, but can't if you install to a custom location.  To find the file go to 'SETTINGS->CONVERSION'  and click the locate button.   The dialog is set up to only accept one file.  It's just matter of you finding it.  Once the location has been set, it remembers this path.  

      To convert a PB source (PBA file) we go to FILE->CONVERT FILE, this pops a file locate dialog where all you do is select the file.   PB2DLL will take over from there, there's is one little issue though and that's what your DLL name is going to be.  Unfortunately, you can't create the DLL then rename it later, that won't work as there's naming dependency.  So the name has to be given to the DLL up front.  There's a few ways around the issue i guess, the DLL name could become the source files name.  This is fine for those people that give their files meaningful names,  but unfortunately a lot of people don't.     One way around the problem would to be search through the compiled byte codes constant table for a constant called "PB2DLL_NAME$" or something similar, another would be to use a DLLNAME_ prefix in a function/psub declarations .   Either way the programmer can then simply add the name string to their source code and then not have to enter a name during translation.  Which would get old really fast if your constantly editing -> building -> repeat..      

      So anyway, it's up and running.. just matter of tweaking everything now.



kevin


  PlayBASIC To Dll  -  Pricing & Back End

         PB2LL is something of a first around here, up until now all the helper tools have been freebies, PB2DLL is a commercial tool.   The initial price will be for the early adopter version !   Which will be similar to what PlayBASIC retail current costs, so somewhere between $20->$30 US.   The final will most likely cost around $50.   

         One of the most frustrating things when releasing new software is all the secondary stuff that's required.  It's not just the software you have to write,  it's the ordering infrastructure it requires as well.   Will probably go with shareit again, but contemplating just using PayPAL.  Much like the DTAB ordering process, the KEY creation side of the ordering process won't be automated.  You'll most likely order, then your registration will be sent to you once the order has processed locally.   Which is required, since the key builder process is written in PB.  Shouldn't be a drama really, as we're unlikely to see more than a few sales.   
           
         So today's little chore has been working on the activation system library which is just matter of dragging some of the code around from older libs,  was a little painfully setting it up, but the process seems to be working.  With other tools i've build pre-processing tools to do the entire process, which is tempting, but again can't really see any great demand.. 



kevin


  PlayBASIC To Dll  -  Final Approach

     Much of the week I've working on the activation libraries and user interface.  After a few trails ended up going for a system that's cross between PB's and what's in DTAB.  When ordering you're given a unique key as well as a user name and email address,  the later come from your order details obviously and much match.   The system requires you enter the information provided to unlock the retail edition of the software.  Had a few drama's initially with some determinism issues but those seem solved now, as the tester gets zero fails over 10000 random checks.  That's not say it's impossible to fail, but it'd seem unlikely now.

     GUI wise there's few dialogs with some chumpy buttons in, there's not really that much to select.  Although It's difficult to know how many controls add really, but I think the higher level (ie. vague)  they are the better really.   Like you could add controls to control the instruction set usage, where you could target particular types of cpu's, but I've a feeling that might asking for trouble really.   Ironically that's one area where JIT is much better idea.. But ya get that.

     There's still a some things missing that come to mind, like getting it convert a folder of sources and history dialog of some sort.   Could add some command line support, so you can set up a short cut or batch file to do a bunch of conversions as once.

     Anyway, here's a piccy of what it looks like.  Surprise Surprise it looks like all the other tools... :)



kevin

#42
 PlayBASIC To Dll - Joining The Dots

       The GUI side of the app is looking fairly complete now (apart from some tweaks), wrote (largely cut'n'pasted) a history dialog the other day  from one of the other apps.     Turned it up a bit, one of my pet peeves is programs that only remember the last couple of files you worked on.    The current solution has no max size,  but really it's unlikely you'll ever have more than a page or so loaded.     There's a clean up button to remove files that no longer exist on disk.  So when stuff get moved or delete or whatever.    

      The other remaining GUI tidbit that was left over, was the batch / folder scanning stuff,  which was just transplanted across from the Amos2BASIC converter.   The batch processing stuff just scans the folder you provide looking for PBA files.   A PBA file for those who don't know, is the PlayBASIC source code file.    Each source it finds it'll attempt to build into a DLL.    The translator  expects the sources to be stand alone at this point, so they currently can't have #INCLUDE statements in them.   Which is limitation, but It should be able to be overcome.

      In order to get the best out of the tool,  it's highly advisable programmers adopt a parent->child structure to their machine code accelerated projects.    So your main project folder contains the game / tool source + media as normal,  in here you'd also have any secondary library sources stored inside that main folder.     Your libraries folder would include your PlayBASIC DLL projects sources.   So if you have a couple of lib's you can use the build folder option in PB2DLL to run through and build everything for you in one go.  The build process will write the created DLL's to those folders and ideally generate linkDLL wrapper code for them.  So in your main source you just #Include those wrappers and your away.  .  

      So if we had an imagery a game called "PONG" , we'd make a project folder called PONG and hopefully give it a meaningful project name such as PONG.PBP (PlayBASIC Project file), rather than the default 'Project.pbp' which the IDE will assign those lazy people.       Inside that folder we'd create a MEDIA folder for all the games graphics and sound assets.     This simple structure immediately helps keep everything more organized from just file management through our actual source code.      If for some reason we wanted to add build a DLL library to help our pong game,  we're create another folder inside the PONG folder.  It could be called anything, but giving it a name such a DLL's or  Libs, or Libraries or something, something that should be  obvious to what the folder contains

      Now let's say in your pong game you're loading media files that for of a different format, you can load them in your main source code, but there's some VM overhead in doing that.   This would be an ideal situation for converting the loader/conversion code to a DLL.    To do so, you start a new project in PB,  then we save this project (with a meaningful name) into our games / dlls folder.   So if our library was to be a packing library we might call it "Packer",  where it's path might be  PONG/DLL/PACKER/.    Once we have a project, we'd cut'n'paste all the required code for the DLL functions to operate separately.    So any structures  arrays etc your packer functions need, need to be inside that project.  The project should compile without any external requirements.   Once you have that, directly PB2DLL to that folder/and source and translate it.


     Once the functions we want are externally built into machine code, we need to convert our parent project to use the DLL version of the functions, rather than the original versions that might be in our code.  In this case, we'd pull all the Packer functions of our game.  We don't need them in the main source anymore since all that code is now hidden away in our machine code dll.    However,  if you're passing types between the sides you would still need those Types to be declared within your main source.   PB can't get this information back from the DLL.    Moreover the DLL is like a black box,  so the only functions you can see inside the dll are those that you exposed in your code.    To expose a function we rename it with the "DLL_"  prefix.  Only these will be exported.   Any function not containing the prefix, will be included inside the final code but not visible from the outside, so we can't access it.    



kevin

#43
 PlayBASIC To Dll - The last 10% is really more like 90%

      One of the many frustrating things about programming is the almost there sensation, where the last 10 or so  percent of the project, feels like it takes more like 90% of the development time.    Testing is notoriously difficult and not to mention time consuming, one of the challenges with a project like this, is the near infinite amount of input situations the program has to deal with.     One little issue I've been having is getting PB2DLL to control and understand the responses from both the PB compiler and FASM.     It's fairly easy trapping a compiler errors (Shown bellow ) from the PB and returning this information back to the user, but from assembler is still a bit iffy trying to sense of what it returns, which soon goes beyond the scope of the product.   Obviously it makes more sense to trap errors during generation, rather than after assembly.   Since it's entirely possible a broken translation source could assemble.  

      Another problem that's been appearing is with batched conversions, where on some sets of sources, it's happy to compile and convert them, but there's the odd program that doesn't like being compiled in a batch, they'll compile fine alone, but not in a group.    So there's something wrong with the loader state not being refreshed, but just can't find the actual smoking gun.



kevin

#44
 PlayBASIC To Dll - Eureka, It's Finally Working

        There's a funny thing that happens when your chasing down bugs, where you'll follow a hunch down a rabbit hole almost infinitely,  regardless of if there's real basis in it or not.   You know it seems like this, so it must be this!   Now the past week or so, I've tracking strange faults in the translator where compiling the same program twice in a row would work, then it'd fail when changing sources in a batch.    Initially it seem GUI related, since the test code worked fine in the stand alone translator, but generally failed when you batched a folder out (translated a group of sources).    There's a few obvious things come to mind, like the translators not initializing it's internal structures properly, or there's some unforeseen collision somewhere.

        Initially focused on tuning up the initialization code, but the problem  persisted.. So off I go looking for any collisions or potential issues between the two slabs of code, such as clash of variable scopes.  Even found a couple of such issues, which looked potentially lethal, but turned out to be a bust..   Next logically conclusion, oh there's must some bug in PB that's at fault..   But right before diving into the runtimes, it seemed it might be nice to get a clearer picture of what the translator is looking at to begin with.  If that code isn't valid, or has some bogus data in it, then there's possible smoking gun  and wouldn't ya know it :)  when comparing the byte code state being compiles, there's an odd similarity in where the errors occur in them, in particular in the map that flags byte code as data and to be skipped.

        So off I go looking at the byte code loader function and hey presto,  the function REDIM's the ignore table.   This creates a problem as  Redim preserves the contents of that array,  the array is used in parallel to the main byte code to screen stuff out, so compiling the same program would work but if you compile program A then program B..  Program B has Programs A's ignore list applied to it, the more programs to built in sequences the of lists get merged creating all sorts of strange artifacts.      But right on 5PM that's bee been fixed now and it's working nicely !

       Haven't just been searching for bugs in the translator,  I've also added more error trapping abilities.    Earlier this week, added support to trap error codes form PlayBASIC compile process.  This returned a line number / error number and error message, but it couldn't get the source line, until now.  So when a compile error occurs, it'll dump that to the console also.   Which should give you some idea as that's wrong with the source.    Beyond that, there was no error trapping in the translator side of the tool as all. In fact if an error occurred,  it'd still call the assembler and try build your DLL.    PB2DLL has limit on the amount of errors it'll accept, unlike PB.    This is because it's possible the error message you're getting is just a warning, so the code may assemble, but it may not also..    If there's too many errors though, it just considers this fatal case and aborts the process completely.

        Anyway, prior to this week I was hoping to have the first commercial releases up and running by the this weekend,  the delays this week make that very doubtful now, but it is just around the corner !