🔒 Locked
Stack
Advertisement
Original Post
Im playing around with creating my own asembly language and designing a virtual machine. Im working on the stack at the moment and i have run into alittle problem. The user sets the size of the stack at the begining of the program. The stack is then created as an array of integers. And the stack pointer points to the last element of the array. pushing and poping integers works fine but how do i store floating point number in there? they are both 32bit so how do i cast a float as an int without loosing all the data?
with my virtual machine, i have a struct that holds the data (like SiCrane suggest) and use some union trickery to get it to work.
// GameDev.net CodeBox Formatter By Danushka "Silvermace"
// http://www.unsoundminds.com/codeformat/
typedef struct {
union { // let them share the same memory space
int ival;
float fval;
};
} vm_stackvar_t;
// here is the stack space
vm_stackvar_t *stack = malloc( sizeof(vm_stackvar_t) * stack_size );
// usage
stack[0].ival = ...;
EDIT: codeformatting :)
// GameDev.net CodeBox Formatter By Danushka "Silvermace"
// http://www.unsoundminds.com/codeformat/
typedef struct {
union { // let them share the same memory space
int ival;
float fval;
};
} vm_stackvar_t;
// here is the stack space
vm_stackvar_t *stack = malloc( sizeof(vm_stackvar_t) * stack_size );
// usage
stack[0].ival = ...;
EDIT: codeformatting :)
Just had alook at using union tell me if ive got this right
union stackData
{
int iData;
float fData;
}
stackData stack;
stack.iData = 10
stack.fData = 99.99
if i try and access stack as an integer it will return me 10, but if i try and access it as a float it will return 99.99
i hope i've got it right as i've never used union;s before
union stackData
{
int iData;
float fData;
}
stackData stack;
stack.iData = 10
stack.fData = 99.99
if i try and access stack as an integer it will return me 10, but if i try and access it as a float it will return 99.99
i hope i've got it right as i've never used union;s before
Not really. If you try accessing it as an int you'll get garbage, but if you try accessing it as a float you'll get back 99.99. If you set the int part to 10 again, then when you access it as an int you'll get back 10 and when you access it as a float you'll get garbage. The int and the float share storage; altering one will clobber the other. (So technically it won't be garbage, since it will be deterministic, but still not what you seemed to be thinking.)
What SiCrane said. What a union does is makes each member overlap eachother in memory, where a struct makes each member follow eachother in memory.
So, using your int/float union...
You give it the integer value 10, 0x0000000A.
You then give it the float value 99.99, which using IEEE float format comes out as 0x42C7FAE1.
If you try to read the integer value back out, you'll get 1,120,402,145, which definetely isn't 10.
So, using your int/float union...
You give it the integer value 10, 0x0000000A.
You then give it the float value 99.99, which using IEEE float format comes out as 0x42C7FAE1.
If you try to read the integer value back out, you'll get 1,120,402,145, which definetely isn't 10.
FWIW, Emacs Lisp has a Lisp_Object union that contains a uint and an int implementation. Then other types hold a Lisp_Object that describes their type.
This is in the emacs-21.3/src/lisp.h file.
The advantages of doing it this way are that you promote type safety and it is more extensible to structures and lists and other types that don't fit into the same union size. However, if you only want ints floats and other things that are your register size, then your union will be quicker.
/* Define the fundamental Lisp data structures. *//* This is the set of Lisp data types. */enum Lisp_Type { /* Integer. XINT (obj) is the integer value. */ Lisp_Int, /* Symbol. XSYMBOL (object) points to a struct Lisp_Symbol. */ Lisp_Symbol, /* Miscellaneous. XMISC (object) points to a union Lisp_Misc, whose first member indicates the subtype. */ Lisp_Misc, /* String. XSTRING (object) points to a struct Lisp_String. The length of the string, and its contents, are stored therein. */ Lisp_String, /* Vector of Lisp objects, or something resembling it. XVECTOR (object) points to a struct Lisp_Vector, which contains the size and contents. The size field also contains the type information, if it's not a real vector object. */ Lisp_Vectorlike, /* Cons. XCONS (object) points to a struct Lisp_Cons. */ Lisp_Cons, Lisp_Float, /* This is not a type code. It is for range checking. */ Lisp_Type_Limit };/* Lisp floating point type */struct Lisp_Float { Lisp_Object type; /* essentially used for mark-bit and chaining when on free-list */#ifdef HIDE_LISP_IMPLEMENTATION double data_;#else double data;#endif };...#define XTYPE(a) ((enum Lisp_Type) (a).u.type)#define FLOATP(x) (XTYPE ((x)) == Lisp_Float)The advantages of doing it this way are that you promote type safety and it is more extensible to structures and lists and other types that don't fit into the same union size. However, if you only want ints floats and other things that are your register size, then your union will be quicker.
it dosn't matter if the types are not the same size, unions will still work:
visualization:
REFERENCE: MSDN C/C++ Language Reference: Unions
Quote:
Original quote from MSDN Online
A union requires enough storage to hold the largest member in its member-list.
visualization:
Quote:
Original image from MSDN Online union NumericType{ int iValue; long lValue; double dValue;};
REFERENCE: MSDN C/C++ Language Reference: Unions
Yes, but if you want to make user defined types, then you don't know how large the user will want to make a type. The other problem is that you will be defaulting to worst case behaviour (if we assume that moving around double sized numbers when we only need an unsigned int sized number (on x86 systems, generally 64 and 32 respectively) is not as good as moving around 2 identifiers: type and value.
As we saw in the Emacs example, the type identifier strategy also allows the type to take up less room to make way for other meta information regarding garbage collection and the like.
I've seem projects use your strategy so I presume it works fairly well for it's intended purposes.
Edit: I realise it's a Microsoft example, but for what it's worth, naming a variable "lValue" is a really crap idea when implementing a parser/compiler/interpreter/VM. It is common nomenclaure for a left-hand-value of an expression and so this usage of Hungarian notation can become confusing when read.
As we saw in the Emacs example, the type identifier strategy also allows the type to take up less room to make way for other meta information regarding garbage collection and the like.
I've seem projects use your strategy so I presume it works fairly well for it's intended purposes.
Edit: I realise it's a Microsoft example, but for what it's worth, naming a variable "lValue" is a really crap idea when implementing a parser/compiler/interpreter/VM. It is common nomenclaure for a left-hand-value of an expression and so this usage of Hungarian notation can become confusing when read.
Quote:yes, i hate hungarian, waste of finger movement honestly
Original post by flangazor
I realise it's a Microsoft example, but for what it's worth, naming a variable "lValue" is a really crap idea when implementing a parser/compiler/interpreter/VM. It is common nomenclaure for a left-hand-value of an expression and so this usage of Hungarian notation can become confusing when read.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement
