View Full Version : Further Developments in Asmosphere I.
Charles Pegge
28-05-2008, 23:09
To extend the capabilities of Asmosphere towards high level programming, I will be adding a few more ingredients to the preprocessor. Fortunately they are few in number but will support a wide range of data structures, local, static and host-shared variables. It will also be simpler to bind to DLL functions. These extras will also make OOP practical with assembler.
Here are the essentials:
def type incl define macros data structures including inherited structures (classes)
var declare variables
mem freemem persistant memory.for static objects
library freelibrary for DLLs.
bind for binding DLL functions to symbols.
copy0 copy00 copyn copying variables, string literals etc.
offset indexers ascending desceding building blocks of variables.
sizeof offsetof spanof referencing variable arrays and structures.
This I hope will complete Asmosphere as a programming layer.
Thanks Charles, I don't know what those things do, but I am sure they will come in handy!
Charles Pegge
29-05-2008, 05:54
These instructions aim to give you everything you need to built and manage high level data structures but however hard one tries to keep the system simple, it's yet more things to learn.
What we need is a script wizard for Asmosphere. My ideal would be something written in HTML/CHM using Javascript and forms - to generate content for pasting into a program. This could be integrated with the manual.
That is a wild idea for sure, use the manual or help for your creation wizard engine, wow!
Petr Schreiber
29-05-2008, 09:49
Thanks Charles,
looks very good as always!
Regarding the generator ... very interesting idea. I was thinking of some expression 2 asmoshpere convertor ( with help of tokenizer module maybe ?).
Petr
Charles Pegge
29-05-2008, 14:30
Petr, that is a compiler :). Definitely heading in the right direction.
sub hello()
.hello
local_space 16
dim a as long
var 4 a
dim s as string * 100
var 4 s
s=mem 100
s="Hello World!"
s="Hello World!"
for a=1 to 10
print s
next
mov a,1
(
cmp a,10
jg exit
proc print s
inc a
repeat
)
end sub
freemem s
free_local_space
ret
Petr Schreiber
29-05-2008, 16:07
Hi Charles,
thanks for the comparsion, very much appreciated.
Cannot wait to version which will allow me to do this tricks!
Petr
Charles Pegge
29-05-2008, 18:09
JIT Compiling Basic would not be too difficult with Asmosphere, - Keeping it simple to start with. I would give this priority over CO2, though they would have much in common.
Petr you will need to invent some new Basic instructions to handle SIMD :)
Petr Schreiber
29-05-2008, 18:21
I am not sure I am good enough to write JIT compiler now,
I am happy when I can sum 2 numbers correctly :D
But fact is Oxygen & Asmosphere makes lot of things easier, now with your planned addition ... hmm :)
Petr
Charles Pegge
29-05-2008, 23:06
JIT Basic:
Apart from rearranging words, the big extras are: automatic type conversion, operator precedence and a run-time library (borrowed from the module run-time ;D)
How about calling it H2O, (currently in the vapour phase of course).
Petr Schreiber
29-05-2008, 23:17
H20 ... currently in vapour phase ;D
That is brutal sentence ;D
Sounds good,
Petr
h2o in vaporware.... codename: steam :)
Charles Pegge
30-05-2008, 14:35
Vaporware can be very useful for planning :) I want to make sure that Asmosphere is complete as a programing layer, so looking ahead informs current development. It should be fairly easy to mix several languages in the same script - with their various syntax rules and paradigms as long as they can be rendered to machine code ( and always close their brackets! )
basic (
...
)
pascal (
)
or nested together:
basic (
asm (
o2 (
)
)
)
Totally wild things you put out Charles. Makes my love for computers even grow stronger in knowing that there is still so much I don't know after all these years!
Charles Pegge
31-05-2008, 10:12
The important thing is to adopt widely used standards wherever possible, so as to avoid total alienation :)
Currently asmosphere uses very simple macros without a parameter facility. For the next release, I'm going to extend it by adopting something that DOS batch files use since it is very simple and efficient to use: each parameter is represented by %1 %2 %3 etc.
To show how this will be implemented in Asmosphere
this macro returns the address of a single dimensional array element:
note that the expression v ( 42 ) has 4 elements represented inside the macro as: v %1 ( %2 42 %3 ) %4
Another thing to note is that the macro definition does not use a prototype, the macro simply 'eats' as many words from the invocation line as it requires.
array1_ptr v(42)
def array1_ptr
(
mov eax, sizeof %1
imul eax,eax,%3
lea edx,%1
add eax,edx
)
supposing v might be something like [ebp-1000] with elements of 8 bytes each;
then this macro would expand to:
[b]
mov eax,8
imul eax,eax,42
lea edx, [ebp-1000]
add eax,edx
[b]
These macros are cool. Reminds me of all the cool things they are doing with templates in c++, but nothing approaches supporting different languages in the same source. You are really thinking abstractly that is wonderful!
Charles Pegge
01-06-2008, 23:22
Thanks Kent, I had'nt really thought of it as abstraction, but its true. This exercise helps to identify the fundamental building blocks for programming.
So far we have:
language (...) ' embedded language.
def (...) ' define macros.
type (...) ' define data structures.
var (...) ' map variables.
skip (..) ' ignore block.
but thinking about a system for generating sound, there is a large amount of initial values and default data to set up. To do this efficiently, we will definitely need a data block to complete the set
data (..) ' define inline data block
This could be any form of embedded database but if it is set up as a table with fixed length columns then an array variable of matching Type can be mapped on to it, for simple access.
Looks very good.... was wondering... could you create something as following, where all of these building blocks could be like this:
language (...) ' embedded language.
def (...) ' define macros.
type (...) ' define data structures.
var (...) ' map variables.
skip (..) ' ignore block.
data(..)
class ()
...language()
...vars( these could also hold pointers to data((..))
...funcs()
Charles Pegge
02-06-2008, 00:52
Class is equivalent to Type which can include any kind of data elements including function pointers, or pointers to table of function pointers.
Types can embed previously defined types in themselves or include these types as a basic form of inheritance.
C++ classes of course have a lot more specialised items in them - much to do with restricting access.
To go a bit more abstract: def () type() var() are specialised forms of a block (..)
Unqualified blocks are used to define scope which means that labels, macros, structures as well as variables can be made local - indeed most words and symbols can be temporarily redefined, reverting to the original when the block ends. Local functions can also be defined, being visible only inside the block. ( This could be awesome or awful can't decide which :) )
Having that ability seems like a big plus to me, but I guess it could also be confusing... but when you think of how much you have to understand what you are doing with this stuff, you should be able to tell the scope of a variable in that point of the code :)
Charles Pegge
02-06-2008, 10:28
The code can contain easy parts and hard parts. It is possible to build "domain-specific" mini languages for handling particular tasks with an appropriate syntax.
The virtue is being able to build an ad hoc system that compiles into machine code, without the overhead of runtime interpretation
How about:
(
Sonics Language
very loud boom
12 reverberations
4.5 second envelope
go
)
A substantial piece of macro coding is hidden away in "Sonics Language" to make this possible of course.
Charles, what do I think? ... well every day you blow me away with what you come up with :)
That very loud boom in the sonics language is me being blown away!!
Charles Pegge
02-06-2008, 13:11
Well let's see how that might work syntactically. Here is a sketch to test the concept
first we have to invent a new macro parameter called %0 which is the word immediately before instead of after the macro word.
Some of these macros are single liners so brackets are not required.
def language include "lang_%0.asm"
' these defs hidden away in lang_sonics.asm...
var 4 this_sound,magnifier,amplitude,frequency,scaler,duration,reverbs,shape
set_initial_values
def very mov magnifier,10
def loud mov amplitude,100000
def boom (
this_sound =proc start_boom magnifier,amplitude,frequency
'restore defaults
mov magnifier,1
mov amplitude, 10000
mov frequency, 120
)
def second (
mov float dword duration, %0
mov scaler,1000
fld dword duration
fimul dword scaler
fstp dword duration
)
def reverberations (
mov reverbs,%0
)
def enveolope (
proc envelope1 this_sound,duration,shape
)
def go (
proc activate this_sound
)
The new sound is timesliced for each frame until it is completed
What can I say, wow... and I see a this_ in there. this_sound = proc...
Very very easy to understand and follow along!!
Petr Schreiber
02-06-2008, 19:08
Very nice Charles :),
can't wait to start code like that!
Petr
Charles Pegge
02-06-2008, 19:56
It won't be very long, Petr. I have almost finished the main coding - ready to debug. Talking it through has been very useful, exposing some of the hidden issues that have to be addressed eg
handling floating point literals.
Charles Pegge
08-06-2008, 09:07
Nearly ready:
After an intensive week of coding and testing, the enhanced Asmosphere is almost ready. My aspiration this time is to make it as concise to use as a higher level compiled language. So far it has passed all its operational tests and useabilitiy tests. Serious stress testing is yet to come with lengthy cruel and unusual coding practices
The piece below shows how to bind to functions in a DLL. In this example, Asmosphere does not return a string with the assembled code. It is retained in a buffer on the module side. Up to 512 buffers are available so thinBasic does not need to handle the binaries directly if it does not want to.
One further refinement is multiple entry points which enables an assembled string itself to become a library of related functions - like a DLL or thinBasic module.
'How to make an sdk/dll call
Uses "Oxygen"
dim src as string = "
indexers `esi` offset 0 ascending ' tell asm how to map variables
push esi ' save esi register
esi=dataspace 256 ' create some inline storage
var 4 user32 ' create a 4 byte variable
user32=loadlibrary `user32.dll` ' get libray handle
bind user32 `mbox MessageBoxA` ' bind procaddresses
mbox 0,`Hello World!`,`Greeting`,0 ' call the dll proc
freelibrary user32 ' free the library
pop esi ' restore esi register
ret ' return
"
o2_asmo src 'assemble but do not return string
o2_exec ' execute this
Petr Schreiber
08-06-2008, 10:45
Thanks Charles,
looks very promising!
How can we index the buffers? If I get it right, o2_asmo src compiles the code in string, and places it in buffer.
o2_exec probably executes this buffer?
Does it work like stack?:
o2_asmo src1
o2_asmo src2
o2_asmo src3
o2_exec ' -- Executes latest added, src3 and removes from machine code stack, shifts src2 mc as first now
o2_exec ' -- Executes src2, and removes it from stack
o2_exec ' -- Executes src1
Or does it work differentely?
Thanks,
Petr
Michael Hartlef
08-06-2008, 10:57
Wow, with so much power, I hope I'll get the time and knowledge to use this stuff. Thanks Charles for all your work.
Charles Pegge
08-06-2008, 11:50
Hi Petr, to use different buffers ther another function: o2_buf
To use several buffers:
o2_buf 1 : o2_asmo prog11
o2_buf 2 : o2_asmo prog12
o2_buf 3 : o2_asmo prog13
then executing them:
o2_buf 1: o2_exec
o2_buf 2 : o2_exec
...
If there are pieces of code that need to be preassembled then these can be done in the previous way with:
mycode=o2_asm mysrc
mc_exec mycode
Then the code trings can be stored somewhere in a file, and used later as long as they dont bind directly to thinBasic variables (#v etc) at assembly time. In most instances the code strings are relocatable and do not require fixups as EXEs do.
Hi Michael,
This opens a Pandora's box of possibilities, since there are none of the assumptions made by high level languages but there isn't much of safety net (apart from GPF :)). With its ability to localise both macros and functions I hope this will be suitable for large programs,- by avoiding name conflicts almost completely.
I am producing some small example scripts showing the new features, to go with the new release.
Petr Schreiber
08-06-2008, 14:12
Hi Charles,
I like that logic very much ... finally it is very close ( equal! ) to OpenGL binding of buffers.
Ideal!
Petr
Charles Pegge
08-06-2008, 15:32
Yes I was reminded of glBindTexture too, Petr :)
re: dynamic linking vs: static linking of Vars
During the course of our discussion, I began to realise that runtime addressing thinBasic variables is going to be essential for preassembled strings.
Something like ##vv to use instead of the static #vv.
in processing terms this binding process is going to be quite slow, so it should only be used once in an intialisation procedure to get the address of the variable, then store the result as a pointer for later use eg:
var 4 pvv
pvv=##vv
Charles Pegge
09-06-2008, 00:54
One further refinement to o2_exec
o2_exec
without parameters this executes code in the current buffer.
but with a string argument it will execute the string and ignore the buffer.
o2_exec (my_code)
Petr Schreiber
09-06-2008, 06:05
This is good,
thanks for making it flexible like that!
Petr
So much progress, it all sounds very exciting. I am very happy to hear there will be example scripts to give me a hope of trying to understand all of this cool stuff!
Charles Pegge
10-06-2008, 09:53
Spent much of yesterday considering multiple entry points whci are going to be essential for any systems that multi-task, like sound generation. To get the very best performance, there are 8 new o2 calls. They take no parameters but call a jump table location (like DLL calls) offset from the start of the code string at 8 byte intervals:
o2_proc1 .. o2_proc8
I think 8 should be sufficient. The procs ensure that the static variables setup by o2_exec are immediately accessible.
The other issue is being able to store pre-assembled strings in a file and reload them for later use. For this we need two more functions:
dim mycode as string
mycode=o2_get
o2_put mycode
These respectively retrieve from and store to the currently selected o2_buf.
Preassemble code strings Code strings cannot contain any static references like #variables, but ##variables are resolved at runtime albeit with a speed penalty.
This is how the new functions are used together
uses "File"
uses "Oxygen"
dim cod as string=File_Load "mycode.bin"
o2_put cod
o2_exec ' main procedure initialises
...
while ok
...
o2_proc1 ' service
...
loop
o2_proc2 ' release resources and terminate
Petr Schreiber
10-06-2008, 16:06
Very neat,
the next Oxygen module update will be a megabomb again.
Thanks,
Petr
Charles Pegge
11-06-2008, 01:07
Here it is at last!
http://community.thinbasic.com/index.php?topic=1637
This version covers major new ground so there is much to explore. I have endevoured to provide a comprehensive set of functions which should cover most needs, and make larger program development more manageable.
The examples at this stage are really test pieces - trying out different techniques to produce the same result, but I hope they will be useful and not too hard to adapt.
Writing assembly code is like making mayonnaise by hand: add very small amounts of code at first then test thoroughly. If your code curdles it may be quicker to start again :)
Petr Schreiber
11-06-2008, 01:14
Fantastic!
Sooner than I expected, very much appreciated!
Thanks,
Petr
Thanks Charles, Petr and I were google chatting when he said your new release is out. So I think we both rushed over to get it and look at the examples.
Thanks very excited to see what is in the beast, well the code that can make us write beast apps.
Petr Schreiber
11-06-2008, 01:55
Charles,
all samples ran great except Test_PF5.tbasic, no idea where is problem.
That example gives me run time error with a bit odd reason, does it occur on your pc with tB 1.6.0.9 too?
Petr
I get the same error in that file.
Charles Pegge
11-06-2008, 09:20
Hi Petr & Kent
Many thanks for picking that up. It worked on my PC but if I tried to rearrange the PF5 script it would fail on termination. This instability exposed a problem with using SysAllocString / SysFreeString directly in that particular environment, so I reverted to my previous internal system. It uses FB strings and even if these are not explicitly released, all is cleaned up when he Oxygen module terminates.
Currently up to 1024 getmemory allocations are supported - thereafter the oldest get recycled. But if the strings are freed with freememory in the reverse order in which they were created, (like a stack) then this limitation does not apply.
Here is the updated version, also including modified PF5 and SDK1 examples
http://community.thinbasic.com/index.php?topic=1637
Petr Schreiber
11-06-2008, 13:20
Thanks Charles,
now all works as it should
Petr
ErosOlmi
11-06-2008, 15:43
Will be included in next thinBasic release.
Thanks Charles
Charles Pegge
11-06-2008, 18:20
Thanks Eros, I think we have reached a point of stability. I will now try to write the manual :)
Petr Schreiber
11-06-2008, 21:09
Manual would be great,
I forget some things very fast, so good to have some reference and/or guide.
I tried to create logos for you, use them or not :)
Petr
Charles Pegge
11-06-2008, 21:56
Many thanks Petr, I will certainly use your logos!
Anything to make the manual more exciting.
- doing HTML by comparison to Assembler is like brick-laying. Sore fingers and numb brain.
Nice logos Petr!
Looking forward to the manual too Charles, I will definitely need it for sure.
Petr Schreiber
12-06-2008, 08:10
Hi Charles,
I must admit, that when I prepare documentation for TBGL, I feel almost physical pain :D
Solution could be to use HelpNDoc (http://www.ibe-software.com/products/software/helpndoc/index.php) tool, where you edit text like in Word and HTML is generated automatically.
Petr
Charles Pegge
12-06-2008, 16:12
Thanks Petr, will take a look. I seem to be getting the hang of this. The HHP Ide is no fun at all but at least the underlying scripts can be edited directly.
Efforts so far:
Petr Schreiber
12-06-2008, 18:58
Hi Charles,
looks good!
But I am sure you had quite hard time writing the docs - HHP does not even highlight HTML syntax.
If you want to continue with HTML editing, I recommend PSPad, it is very good editor.
But I still recommend doing it in free edition of HelpNDoc, less pain and no HTML struggle.
Petr
Charles Pegge
12-06-2008, 19:52
Petr, if I can see the scripts in Notepad, I can cope with it. Anything is better than those tedious dialog boxes. As long as there is the ability to cut and paste scripts, anything is possible.
As CHM supports Javascript, it might be fun to try out a code generating wizard. We could call it OMerlin. (I woke up this morning with this character in my head). OMerlin would be capable of producing pastable code snippets - data structures - outline procedures etc.
Petr Schreiber
12-06-2008, 20:01
:D
Incredible, but good idea!
Petr
Charles Pegge
12-06-2008, 20:40
Just something really simple to start with, to establish the basic mechanisms.
Charles Pegge
15-06-2008, 11:10
Here is the Oxygen help manual. It is very basic at this stage but covers the ground. I anticipate frequent updates but I won't announce them every time - only when there is a significant addition.
thinbasic_oxygen_help.zip added here:
http://community.thinbasic.com/index.php?topic=1637
Petr Schreiber
15-06-2008, 14:56
Very good,
thanks a lot, with such a reference I will be able to start my asm coding without too much blind guesses.
Very well done, please keep it up!
Petr
Charles Pegge
15-06-2008, 16:06
Please let me know right away if you encounter any odd behaviour.
Defs are going to be interesting because there are very few restrictions placed on them. They can take over almost any word or symbol - powerful in the hands of the wise but fraught with unintended consequences.
We will see if extra protection is needed against expressions such as def ( or def def or even def 1 2 :)
about Def, I would just mention it in the help that it is very powerful and not to do silly or abusive things to see if you can mess it up.
thanks for the help file, will download and look through right now:)
Charles Pegge
16-06-2008, 08:16
I am working on the "Omerlin Code Spells" section - no problems running Forms or Javascript inside chm :) so I hope to post a very simple prototype in the help file later today.
Charles Pegge
16-06-2008, 19:06
Just posted the updated help manual containing the beginnings of Omerlin's code spells. The experiment went well today and I hope this will be useful to all, enabling code to be generated without trying to work it out from scratch. . I certainly envisage using it myself to generate error-free code skeletons.
http://community.thinbasic.com/index.php?topic=1637
Petr Schreiber
16-06-2008, 21:14
Hi Charles,
thanks for OMerlin,
really useful - two clicks and code mini-template is ready to copy and paste :)
Petr
This is a first for me, seeing something interactive (beyond just clicking on a hyperlink) in a help file.
Charles you had mentioned it would be very easy, well if one knew what they were doing I suppose, to make classes with the new tools.
Can you write an example, when you have some time?
If it could show how a base class would be implemented and then inherited in a new class, which would add its own members that would be super.
I know it is asking a lot, but thought I would ask anyways :)
Charles Pegge
17-06-2008, 05:26
Hi Kent,
Inheriting properties is exremely simple - an example of double inheritance and then adding a couple of other members:
type color 1 blue 1 green 1 red 1 alpha
type xyzvec 4 x 4 y 4 z
type ColorPoint Color, xyzec, 4 id, 12 name
then mapping out the vars:
var ColorPoint c1 c2 c3 ca(100,2) etc
on the other hand :)
C++ style classes with methods and static members is a little more complicated and I have one further ingredient to add for building virtual function tables more easily called labeladdress
Do you have one or two examples in mind, Kent. Perhaps something you were doing in C++ or TankWars so we can make it more concrete.
I know this is going to be sweet once I understand it all.
About an idea for a more concrete example. Can I put up something written in C++ or would you like me to write pseudo code Charles?
I am excited by the possibilities so I thought I would go ahead and put something in c++ that I had:
Point.h :
#ifndef POINT_H
#define POINT_H
#include <iostream>
using namespace std;
class Point
{
public:
//constructor, same name as the class
Point(float f_x = 1.2, float f_y = 1.5, float f_z = 1.7); //this now eliminates the need for the above method
//destructor
~Point(); //never any arguements for the destructor
void getXYZ(float &X, float &Y, float &z);
float getX();
float getY();
float getZ();
void setXYZ(float X, float Y, float Z);
void setX(float X);
void setY(float Y);
void setZ(float Y);
Point operator =(Point &p);
//float x, y, z;
private:
float x, y, z;
protected:
//float x, y, z;
};
ostream &operator <<(ostream &stream, Point &P);
istream &operator >>(istream &stream, Point &P);
#endif
Vector.h :
#ifndef VECTOR_H
#define VECTOR_H
#include "Point.h"
class Vector : public Point
{
public:
Vector(float X=0,float Y=0, float Z=0);
Vector operator +(Vector &p);
Vector operator -(Vector &p);
Vector operator *(float scale);
};
#endif
Point.cpp :
#include <iostream>
using namespace std;
#include "Point.h"
Point::Point(float f_x, float f_y, float f_z)
{
x = f_x;//if no parameters the default values are used from the definition
y = f_y;
z = f_z;
cout << "we are in the constructor with arguements" << int(this) << endl;
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
}
Point::~Point()
{
cout << "we are in the destructor" << (int)this << endl;
}
Point Point::operator =(Point &p)
{
setXYZ(p.getX(),p.getY(),p.getZ());
return *this;
}
void Point::getXYZ(float &X, float &Y, float &Z)
{
X = getX();
Y = getY();
Z = getZ();
}
float Point::getX()
{
return x;
}
float Point::getY()
{
return y;
}
float Point::getZ()
{
return z;
}
void Point::setXYZ(float X, float Y, float Z)
{
setX(X);
setY(Y);
setZ(Z);
}
void Point::setX(float X)
{
x = X;
}
void Point::setY(float Y)
{
y = Y;
}
void Point::setZ(float Z)
{
z = Z;
}
ostream &operator <<(ostream &stream, Point &P)
{
stream << P.getX() << " " << P.getY() << " " << P.getZ();
return stream;
}
istream &operator >>(istream &stream, Point &P)
{
float x, y, z;
stream >> x >> y >> z;
P.setXYZ(x, y, z);
return stream;
}
Vector.cpp :
#include "Vector.h"
Vector::Vector(float X, float Y, float Z) : Point(X,Y,Z)
{
}
Vector Vector::operator +(Vector &p)
{
Vector vout;
vout.setX(getX() + p.getX());
vout.setY(getY() + p.getY());
vout.setZ(getZ() + p.getZ());
return vout;
}
Vector Vector::operator -(Vector &p)
{
Vector vout;
vout.setX(getX() - p.getX());
vout.setY(getY() - p.getY());
vout.setZ(getZ() - p.getZ());
return vout;
}
Vector Vector::operator *(float scale)
{
Vector vout;
vout.setX(getX() * scale);
vout.setY(getY() * scale);
vout.setZ(getZ() * scale);
return vout;
}
// Vector operator *(float scale, Vector v) // this worked also
Vector operator *(const float &scale, Vector &v)
{
Vector vout;
vout.setX(v.getX() * scale);
vout.setY(v.getY() * scale);
vout.setZ(v.getZ() * scale);
return vout;
}
This should cover a lot of what would be needed. It covers some of the great things about classes in c++. Let me know if this is Ok?
And THANKS!
In thinking about it, that c++ one is really hard because of the overloading of operators.
Here is a simpler no overloading of operators, perhaps operator overloading will be possible with Asmosphere, I really don't know. But just in case here is pseudo code situation.
type t_xyz
x as double
y as double
z as double
end type
type t_rgb
r as long
g as long
b as long
end type
class vector
public
getX(), returns double
getY(), returns double
getZ(), returns double
setX(x as double)
setY(y as double)
setZ(z as double)
getXYZ(), returns t_xyz
setXYZ(vec as t_xyz)
private
vec as t_xyz
end class
class Color
public
getR(), returns long
getG(), returns long
getB(), returns long
setR(x as long)
setG(y as long)
setB(z as long)
getRGB(), returns t_rgb
setRGB(col as t_rgb)
private
col as t_rgb
end class
class actor
public
getName(), returns string
setName(name as string)
private
name as string
pos as Vector
col as Color
end class
class enemy derived from class actor
public
setWeapon(name as string)
getWeapon(), returns string
setDamage(damage as long)
getDamage(), returns long
setShield(shield as long)
getShield(), returns long
private
weapon as string
damage as long
shield as long
end class
class player derived from class enemy
setMagic(magic as long)
getMagic(), returns long
private
magic as long
end class
Hope this is easier, and probably there are even better ways to do it, just off of the top of my head all I can come up with.
Of course player would inherit all of the properties and methods from previous classes that it is derived from.
So there would be:
dim p as new player
p.setName("Hero Henry")
p.pos.setXYZ(100,0,200)
p.col.setRGB(100,100,100)
p.setWeapon("Axe")
p.setMagic(50)
p.setDamage(10)
p.setShield(100)
Sorry for the mess of examples Charles, just wanted to make sure you had something before tomorrow as night time here now and early morning on your side of the Atlantic.
Charles Pegge
17-06-2008, 19:41
In deep thought all day :)
Classes and their virtual tables are dynamically created - some very interesting constructs without adding extra features to the Oxygen kernel so far.
C++ OOP hides considerable complexity.
Charles Pegge
18-06-2008, 00:29
This is a sketch to show what is entailed in setting up OOP with methods, hidden members and inheritance. There is a sample of three methods - enough to work on the principle anyway. It can be packed down quite a bit but shows how much work the CPU has to do in setting up the tables and other structures. But once the objects are up and running then the OOP overhead is not too bad compared with normal function calls.
Here, virtual tables are embedded in the code string but they could also be patched into thinBasic variables allowing other modules to access the methods.
mov edi,ecx ; code string base
type vecvta 4 ptr 4 len
indexers "esi" offset 0 ascending
esi=getmemory 1000
type t_xyz 8 x 8 y 8 z ; doubles
type t_rgb 4 r 4 g 4 b ; longs
type vector 4 ? t_xyz ? t_rgb ? ; public declaration
var vecvta vector_info parent_info
( ; capsule hiding the inner workings of vector
type vector
(
; private definition
4 vt_methods
t_xyz vec
t_rgb col
)
o2 !4 e8 00 00 00 00 e9 xl 90 90 90 ' where-am-I left on stack
;-------------------------------------
; 00 04 08 12
;-------------------------------------
o2 ga getX ga getY ga getZ ' virtual table
;
; instantiate class methods
.getX
indexers "esi" offset 0 ascending
var vector this
push ebp
mov ebp,esp
push esi
mov esi [ebp+8] ; this pointer
fld this.vec.x
pop esi
mov esp,ebp
pop ebp
ret 4
.getY
;...
ret 4
.getZ
; ...
ret 4
; store vt info
) ; end of vector encapsulation
;
pop eax ; store whereami
add eax,7
mov vector_info.ptr,eax
mov vector_info.len,12 ; store length of entries
mov edx,parent_info.len ; length of parant table
mov eax,vector_info.ptr
add eax,vector_info.len
copyn eax.ptr,parent_info.ptr,parent_info.len ' copy parental vt in edx
mov eax,vector_info.len
add eax,parent_info.len
mov vector_info.len,eax
; add offsets to table to make absolute addresses
mov ecx,12
shr ecx,2
mov edx,vector_info.ptr
mov eax,edx
(
dec ecx
jl exit
add eax,4
add [eax],edi
repeat
)
;
def create_vector
(
; calling methods
def %1_getX
(
lea eax,%1
push eax
mov eax %1
proc [eax+0]
)
def %1_getY
(
lea eax,%1
push eax
mov eax %1
proc [eax+4]
)
def %1_getZ
(
lea eax,%1
push eax
mov eax %1
proc [eax+8]
)
; now setup the variable with its vt address
var vector %1
mov eax,vector_info.ptr
mov %1,eax
) ; end of def
Create_vector p1
var 4 x y z
; try the methods
x=p1_getX
y=p1_getY
z=p1_getZ
freememory esi
ret
Charles, the first scan through your post my eyes came out of their sockets :) But I have been going through it and after many passes it is making more sense, could I write this from scratch, no, but I am able to follow along and kind of get how it all goes together.
One minor question:
Create_vector p1
var 4 x y z
; try the methods
x=p1_getX
y=p1_getY
z=p1_getZ
freememory esi
ret
Shouldn't var 4 xyz be var 8 xyz at that point in the code?
That is neat to see a behind the scenes of what goes on behind the scenes when working with oop, that is for sure!! Thanks.
Charles Pegge
18-06-2008, 05:55
Yes, well spotted Kent. I slipped back into 4 byte integer. In fact the = would not work either since the result is returned in the FPU. This is where overloading operators would be useful. If you dont mind RPN how about this:
Create_vector p1
var 8 x y z r
(
def to (
fstp qword %1
)
def + (
faddp st(1),st(0)
)
def squared (
fmul st(0),st(0)
)
def sqroot (
fsqrt
)
; try the methods
p1_getX to x
p1_getY to y
p1_getZ to z
p1_getX squared p1_getY squared p1_getZ squared + + sqroot to r
)
That looks cool. I am confused on the getZ squared + + sqroot to r
Is the first + summing the squares of x y and z and then adding that sum to the sqroot and storing it all into r?
Charles Pegge
18-06-2008, 11:52
Hi Kent,
You must be up very late or very early - (I have a random body clock :)).
The above expression is Pythagoras: sqr(x^2+y^2+z^2). Only it's Reverse Polish for the FPU which has a stack of 8 registers the topmost being the main accummulator.
Today I am experimenting with providing a few more overloadable items namely ( ) and =. So that these can be taken over temporarily for customised notation.
Thanks for explaining the RPN for me in the example. Good luck on the cool project and overloading!
Charles you might want to check out this page. It explains creating a class in Ruby, there are some neat ideas without confusing syntax that seem similar to what you are doing.
http://www.ruby-lang.org/en/documentation/quickstart/2/
Charles Pegge
18-06-2008, 23:26
Thanks Kent, I'm keeping half an eye on Ruby. It is a long way up the code pyramid, and o2 is way down at the bottom somewhere, where the bugs have sharper teeth.
O2 syntax is slightly complicated by having both a single line and multiline form and it has to be quite smart to detect the difference - usually by the presence of outer brackets, but we definitely need both forms when dealing with assembler.
I've now got it overloading = and even brackets, quotes and comments - occasionally useful. It can adopt curly braces {} or begin .. end, amongst other possibilities. - putting together some test examples.
That is neat how you are putting all of this together so quickly Charles. Good luck on the examples, am here when they are ready to test out!
Charles Pegge
20-06-2008, 21:53
Shrinking the OOP Monster
After trying out various OOP models, I think I've got one that comes out top, both in performance, flexibility and simplicity. The virtual table of functions has gone in favour of direct linkage but I dont think this will have any serious disadvantages.
For test purposes I piggy-backed this model onto test_PF6 in the latest release. It does not do very much but this environment is very sensitive to malformed code so it's a good testing ground.
Most the the work is done by macros which render the OOP into good old fashioned procedural code. The ecx register is used to carry the this pointer.
This is a rather abstract example showing all the construction macros which are used to create classes with 2 levels of inheritance and various methods associated with each. The object created has access to the methods of class0 class1 and class2.
The Generic OOP macros only have to be defined once.
;==========================
; classes
;==========================
;------------------
;GENERIC OOP MACROS
;------------------
def class type
def context
(
indexers `ecx` offset 0 ascending var %1 this
)
def params
(
def parambytes %1
indexers `ebp` offset 8 ascending
)
def locals
(
indexers `ebp` offset 0 descending
push ebp
mov ebp,esp
sub esp,%1
)
def release
(
mov esp,ebp
pop ebp
ret parambytes
)
def create
(
var %1 %2
%1_methods %2
)
def method
(
lea ecx, %1
proc )
;------------------
;define classes
;------------------
class class0
(
4 u 4 v 4 w
)
class class1
(
class0, 4 a 4 b 4 c
)
class class2
(
class1, 4 d 4 e
)
;define methods
;---------------
.init
(
context class0
params 0
locals 0
;...
release
)
.get
(
context class1
params 8
var 4 a b
locals 100
;...
release
)
.put
(
context class1
params 8
var 4 aa bb
locals 100
var 4 i j
def =
(
fld dword %1
fstp dword %:
)
this.w=aa
this.a=i
;...
release
)
.clear
(
context class2
params 0
locals 0
;...
release
)
;define method calls
;-------------------
def class0_methods
(
def %1_init method %1 init
)
def class1_methods
(
class0_methods %1
def %1_get method %1 get
def %1_put method %1 put
)
def class2_methods
(
class1_methods %1
def %1_clear method %1 clear
)
'------------
testclasses:
'------------
(
indexers `esi` offset 100 ascending
create class2 obj
obj_init
obj_get 1,2
obj_put 3,4
obj_clear
ret
)
;=========================
;=========================
This is how it looks in machine code: - there is very little overhead associated with the OOP
; ;==========================
; ; classes
; ;==========================
; ;------------------
; ;GENERIC OOP MACROS
; ;------------------
; ;------------------
; ;define classes
; ;------------------
; ;define methods
; ;---------------
.init ; .init
( ; (
55 ; push ebp
8B EC ; mov ebp,esp
83 EC 00 ; sub esp,0
8B E5 ; mov esp,ebp
5D ; pop ebp
C3 ; ret 0
) ; )
.get ; .get
( ; (
55 ; push ebp
8B EC ; mov ebp,esp
83 EC 64 ; sub esp,100
8B E5 ; mov esp,ebp
5D ; pop ebp
C2 08 00 ; ret 8
) ; )
.put ; .put
( ; (
55 ; push ebp
8B EC ; mov ebp,esp
83 EC 64 ; sub esp,100
D9 45 08 ; fld dword [ebp+8]
D9 59 08 ; fstp dword [ecx+8]
D9 45 FC ; fld dword [ebp-4]
D9 59 0C ; fstp dword [ecx+12]
8B E5 ; mov esp,ebp
5D ; pop ebp
C2 08 00 ; ret 8
) ; )
.clear ; .clear
( ; (
55 ; push ebp
8B EC ; mov ebp,esp
83 EC 00 ; sub esp,0
8B E5 ; mov esp,ebp
5D ; pop ebp
C3 ; ret 0
) ; )
; ;define method calls
; ;-------------------
; '------------
.testclasses ; .testclasses
; '------------
( ; (
8D 4E 64 ; lea ecx, [esi+100]
E8 gl init ; call init
8D 4E 64 ; lea ecx, [esi+100]
6A 02 ; push 2
6A 01 ; push 1
E8 gl get ; call get
8D 4E 64 ; lea ecx, [esi+100]
6A 04 ; push 4
6A 03 ; push 3
E8 gl put ; call put
8D 4E 64 ; lea ecx, [esi+100]
E8 gl clear ; call clear
C3 ; ret
) ; )
; ;=========================
; ;=========================
Charles I am anxious to play with this. Can you tell me where I put that code and play with it?
Thanks!