Skip to main content
gamedev.net
🔒 Locked

Stack

Started by kapru Nov 20, 2004 at 11:48 PM 10 replies 1.5k views
Original Post
kapru
kapru
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?
SiCrane
SiCrane
What language are you using for implementation? In a C based language you can use a union for your elements, or do some evil pointer casting.
silvermace
silvermace
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 :)
kapru
kapru
The language will just be a basic language and im planning on just using it for a bit of in-game scripting. Mainly just to play around with. i'll have a look at using union's
kapru
kapru
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
SiCrane
SiCrane
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.)
kapru
kapru
ahhhh ok i get how it works now thanks heaps.

Ive decided not to use union. im just going to use a struct and when pushing a register is assignes an int and a float value quick and easy data type conversion in the language.

So my push and pop now works pritty well
Wyrframe
Wyrframe
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.
RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.
flangazor
flangazor
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.

/* 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)
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.
silvermace
silvermace
it dosn't matter if the types are not the same size, unions will still work:
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
flangazor
flangazor
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.
silvermace
silvermace
Quote:
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.
yes, i hate hungarian, waste of finger movement honestly

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.