summaryrefslogtreecommitdiff
path: root/ficlplatform/win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'ficlplatform/win32.c')
-rw-r--r--ficlplatform/win32.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/ficlplatform/win32.c b/ficlplatform/win32.c
new file mode 100644
index 000000000000..d019eddddb49
--- /dev/null
+++ b/ficlplatform/win32.c
@@ -0,0 +1,413 @@
+/*
+** win32.c
+** submitted to Ficl by Larry Hastings, larry@hastings.org
+**/
+
+#include <sys/stat.h>
+#include "ficl.h"
+
+
+/*
+**
+** Heavy, undocumented wizardry here.
+**
+** In Win32, like most OSes, the buffered file I/O functions in the
+** C API (functions that take a FILE * like fopen()) are implemented
+** on top of the raw file I/O functions (functions that take an int,
+** like open()). However, in Win32, these functions in turn are
+** implemented on top of the Win32 native file I/O functions (functions
+** that take a HANDLE, like CreateFile()). This behavior is undocumented
+** but easy to deduce by reading the CRT/SRC directory.
+**
+** The below mishmash of typedefs and defines were copied from
+** CRT/SRC/INTERNAL.H from MSVC.
+**
+** --lch
+*/
+typedef struct {
+ long osfhnd; /* underlying OS file HANDLE */
+ char osfile; /* attributes of file (e.g., open in text mode?) */
+ char pipech; /* one char buffer for handles opened on pipes */
+#ifdef _MT
+ int lockinitflag;
+ CRITICAL_SECTION lock;
+#endif /* _MT */
+ } ioinfo;
+extern _CRTIMP ioinfo * __pioinfo[];
+
+#define IOINFO_L2E 5
+#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
+#define _pioinfo(i) ( __pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - \
+ 1)) )
+#define _osfhnd(i) ( _pioinfo(i)->osfhnd )
+
+
+int ficlFileTruncate(ficlFile *ff, ficlUnsigned size)
+{
+ HANDLE hFile = (HANDLE)_osfhnd(_fileno(ff->f));
+ if (SetFilePointer(hFile, size, NULL, FILE_BEGIN) != size)
+ return 0;
+ return !SetEndOfFile(hFile);
+}
+
+
+int ficlFileStatus(char *filename, int *status)
+{
+ /*
+ ** The Windows documentation for GetFileAttributes() says it returns
+ ** INVALID_FILE_ATTRIBUTES on error. There's no such #define. The
+ ** return value for error is -1, so we'll just use that.
+ */
+ DWORD attributes = GetFileAttributes(filename);
+ if (attributes == -1)
+ {
+ *status = GetLastError();
+ return -1;
+ }
+ *status = attributes;
+ return 0;
+}
+
+
+long ficlFileSize(ficlFile *ff)
+{
+ struct stat statbuf;
+ if (ff == NULL)
+ return -1;
+
+ statbuf.st_size = -1;
+ if (fstat(fileno(ff->f), &statbuf) != 0)
+ return -1;
+
+ return statbuf.st_size;
+}
+
+
+
+
+
+void *ficlMalloc(size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc(void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree(void *p)
+{
+ free(p);
+}
+
+void ficlCallbackDefaultTextOut(ficlCallback *callback, char *message)
+{
+ FICL_IGNORE(callback);
+ if (message != NULL)
+ fputs(message, stdout);
+ else
+ fflush(stdout);
+ return;
+}
+
+
+
+/*
+**
+** Platform-specific functions
+**
+*/
+
+
+/*
+** m u l t i c a l l
+**
+** The be-all, end-all, swiss-army-chainsaw of native function call methods in Ficl.
+**
+** Usage:
+** ( x*argumentCount [this] [vtable] argumentCount floatArgumentBitfield cstringArgumentBitfield functionAddress flags -- returnValue | )
+** Note that any/all of the arguments (x*argumentCount) and the return value can use the
+** float stack instead of the data stack.
+**
+** To call a simple native function:
+** call with flags = MULTICALL_CALLTYPE_FUNCTION
+** To call a method on an object:
+** pass in the "this" pointer just below argumentCount,
+** call with flags = MULTICALL_CALLTYPE_METHOD
+** *do not* include the "this" pointer for the purposes of argumentCount
+** To call a virtual method on an object:
+** pass in the "this" pointer just below argumentCount,
+** call with flags = MULTICALL_CALLTYPE_VIRTUAL_METHOD
+** *do not* include the "this" pointer for the purposes of argumentCount
+** the function address must be the offset into the vtable for that function
+** It doesn't matter whether the function you're calling is "stdcall" (caller pops
+** the stack) or "fastcall" (callee pops the stack); for robustness, multicall
+** always restores the original stack pointer anyway.
+**
+**
+** To handle floating-point arguments:
+** To thunk an argument from the float stack instead of the data stack, set the corresponding bit
+** in the "floatArgumentBitfield" argument. Argument zero is bit 0 (1), argument one is bit 1 (2),
+** argument 2 is is bit 2 (4), argument 3 is bit 3 (8), etc. For instance, to call this function:
+** float greasyFingers(int a, float b, int c, float d)
+** you would call
+** 4 \ argumentCount
+** 2 8 or \ floatArgumentBitfield, thunk argument 2 (2) and 4 (8)
+** 0 \ cstringArgumentBitfield, don't thunk any arguments
+** (addressOfGreasyFingers) MULTICALL-CALLTYPE-FUNCTION MULTICALL-RETURNTYPE-FLOAT or multicall
+**
+** To handle automatic conversion of addr-u arguments to C-style strings:
+** This is much like handling float arguments. The bit set in cstringArgumentBitfield specifies
+** the *length* argument (the higher of the two arguments) for each addr-u you want converted.
+** You must count *both* arguments for the purposes of the argumentCount parameter.
+** For instance, to call the Win32 function MessageBoxA:
+**
+** 0 "Howdy there!" "Title" 0
+** 6 \ argument count is 6! flags text-addr text-u title-addr title-u hwnd
+** 0 \ floatArgumentBitfield, don't thunk any float arguments
+** 2 8 or \ cstringArgumentBitfield, thunk for title-u (argument 2, 2) and text-u (argument 4, 8)
+** (addressOfMessageBoxA) MULTICALL-CALLTYPE-FUNCTION MULTICALL-RETURNTYPE-INTEGER or multicall
+** The strings are copied to temporary storage and appended with a zero. These strings are freed
+** before multicall returns. If you need to call functions that write to these string buffers,
+** you'll need to handle thunking those arguments yourself.
+**
+** (If you want to call a function with more than 32 parameters, and do thunking, you need to hit somebody
+** in the head with a rock. Note: this could be you!)
+**
+** Note that, big surprise, this function is really really really dependent
+** on predefined behavior of Win32 and MSVC. It would be non-zero amounts of
+** work to port to Win64, Linux, other compilers, etc.
+**
+** --lch
+*/
+static void ficlPrimitiveMulticall(ficlVm *vm)
+{
+ int flags;
+ int functionAddress;
+ int argumentCount;
+ int *thisPointer;
+ int integerReturnValue;
+#if FICL_WANT_FLOAT
+ float floatReturnValue;
+#endif /* FICL_WANT_FLOAT */
+ int cstringArguments;
+ int floatArguments;
+ int i;
+ char **fixups;
+ int fixupCount;
+ int fixupIndex;
+ int *argumentPointer;
+ int finalArgumentCount;
+ int argumentDirection;
+ int *adjustedArgumentPointer;
+ int originalESP;
+ int vtable;
+
+ flags = ficlStackPopInteger(vm->dataStack);
+
+ functionAddress = ficlStackPopInteger(vm->dataStack);
+ if (FICL_MULTICALL_GET_CALLTYPE(flags) == FICL_MULTICALL_CALLTYPE_VIRTUAL_METHOD)
+ functionAddress *= 4;
+
+ cstringArguments = ficlStackPopInteger(vm->dataStack);
+ floatArguments = ficlStackPopInteger(vm->dataStack);
+#if !FICL_WANT_FLOAT
+ FICL_VM_ASSERT(vm, !floatArguments);
+ FICL_VM_ASSERT(vm, FICL_MULTICALL_GET_RETURNTYPE(flags) != FICL_MULTICALL_RETURNTYPE_FLOAT);
+#endif /* !FICL_WANT_FLOAT */
+ argumentCount = ficlStackPopInteger(vm->dataStack);
+
+ fixupCount = 0;
+ if (cstringArguments)
+ {
+ for (i = 0; i < argumentCount; i++)
+ if (cstringArguments & (1 << i))
+ fixupCount++;
+ fixups = (char **)malloc(fixupCount * sizeof(char *));
+ }
+ else
+ {
+ fixups = NULL;
+ }
+
+
+ /* argumentCount does *not* include the *this* pointer! */
+ if (FICL_MULTICALL_GET_CALLTYPE(flags) != FICL_MULTICALL_CALLTYPE_FUNCTION)
+ {
+ if (flags & FICL_MULTICALL_EXPLICIT_VTABLE)
+ vtable = ficlStackPopInteger(vm->dataStack);
+
+ __asm push ecx
+ thisPointer = (int *)ficlStackPopPointer(vm->dataStack);
+
+ if ((flags & FICL_MULTICALL_EXPLICIT_VTABLE) == 0)
+ vtable = *thisPointer;
+ }
+
+
+ __asm mov originalESP, esp
+
+ fixupIndex = 0;
+ finalArgumentCount = argumentCount - fixupCount;
+ __asm mov argumentPointer, esp
+ adjustedArgumentPointer = argumentPointer - finalArgumentCount;
+ __asm mov esp, adjustedArgumentPointer
+ if (flags & FICL_MULTICALL_REVERSE_ARGUMENTS)
+ {
+ argumentDirection = -1;
+ argumentPointer--;
+ }
+ else
+ {
+ argumentPointer = adjustedArgumentPointer;
+ argumentDirection = 1;
+ }
+
+ for (i = 0; i < argumentCount; i++)
+ {
+ int argument;
+
+ /* a single argument can't be both a float and a cstring! */
+ FICL_VM_ASSERT(vm, !((floatArguments & 1) && (cstringArguments & 1)));
+
+#if FICL_WANT_FLOAT
+ if (floatArguments & 1)
+ argument = ficlStackPopInteger(vm->floatStack);
+ else
+#endif /* FICL_WANT_FLOAT */
+ argument = ficlStackPopInteger(vm->dataStack);
+
+ if (cstringArguments & 1)
+ {
+ int length;
+ char *address;
+ char *buffer;
+ address = ficlStackPopPointer(vm->dataStack);
+ length = argument;
+ buffer = malloc(length + 1);
+ memcpy(buffer, address, length);
+ buffer[length] = 0;
+ fixups[fixupIndex++] = buffer;
+ argument = (int)buffer;
+ argumentCount--;
+ floatArguments >>= 1;
+ cstringArguments >>= 1;
+ }
+
+ *argumentPointer = argument;
+ argumentPointer += argumentDirection;
+
+ floatArguments >>= 1;
+ cstringArguments >>= 1;
+ }
+
+
+ /*
+ ** note! leave the "mov ecx, thisPointer" code where it is.
+ ** yes, it's duplicated in two spots.
+ ** however, MSVC likes to use ecx as a scratch variable,
+ ** so we want to set it as close as possible before the call.
+ */
+ if (FICL_MULTICALL_GET_CALLTYPE(flags) == FICL_MULTICALL_CALLTYPE_VIRTUAL_METHOD)
+ {
+ __asm
+ {
+ /* push thisPointer */
+ mov ecx, thisPointer
+ /* put vtable into eax. */
+ mov eax, vtable
+ /* pull out the address of the function we want... */
+ add eax, functionAddress
+ /* and call it. */
+ call [eax]
+ }
+ }
+ else
+ {
+ FICL_VM_ASSERT(vm, functionAddress != 0);
+ if (FICL_MULTICALL_GET_CALLTYPE(flags))
+ {
+ __asm mov ecx, thisPointer
+ }
+ __asm call functionAddress
+ }
+
+ /* save off the return value, if there is one */
+ __asm mov integerReturnValue, eax
+#if FICL_WANT_FLOAT
+ __asm fst floatReturnValue
+#endif /* FICL_WANT_FLOAT */
+
+ __asm mov esp, originalESP
+
+ if (FICL_MULTICALL_GET_CALLTYPE(flags))
+ {
+ __asm pop ecx
+ }
+
+ if (FICL_MULTICALL_GET_RETURNTYPE(flags) == FICL_MULTICALL_RETURNTYPE_INTEGER)
+ ficlStackPushInteger(vm->dataStack, integerReturnValue);
+ else if (FICL_MULTICALL_GET_RETURNTYPE(flags) == FICL_MULTICALL_RETURNTYPE_CSTRING)
+ {
+ char *str = (char *)(void *)integerReturnValue;
+ ficlStackPushInteger(vm->dataStack, integerReturnValue);
+ ficlStackPushInteger(vm->dataStack, strlen(str));
+ }
+#if FICL_WANT_FLOAT
+ else if (FICL_MULTICALL_GET_RETURNTYPE(flags) == FICL_MULTICALL_RETURNTYPE_FLOAT)
+ ficlStackPushFloat(vm->floatStack, floatReturnValue);
+#endif /* FICL_WANT_FLOAT */
+
+ if (fixups != NULL)
+ {
+ for (i = 0; i < fixupCount; i++)
+ if (fixups[i] != NULL)
+ free(fixups[i]);
+ free(fixups);
+ }
+
+ return;
+}
+
+
+
+
+/**************************************************************************
+ f i c l C o m p i l e P l a t f o r m
+** Build Win32 platform extensions into the system dictionary
+**************************************************************************/
+void ficlSystemCompilePlatform(ficlSystem *system)
+{
+ HMODULE hModule;
+ ficlDictionary *dictionary = system->dictionary;
+ FICL_SYSTEM_ASSERT(system, dictionary);
+
+ /*
+ ** one native function call to rule them all, one native function call to find them,
+ ** one native function call to bring them all and in the darkness bind them.
+ ** --lch (with apologies to j.r.r.t.)
+ */
+ ficlDictionarySetPrimitive(dictionary, "multicall", ficlPrimitiveMulticall, FICL_WORD_DEFAULT);
+ ficlDictionarySetConstant(dictionary, "multicall-calltype-function", FICL_MULTICALL_CALLTYPE_FUNCTION);
+ ficlDictionarySetConstant(dictionary, "multicall-calltype-method", FICL_MULTICALL_CALLTYPE_METHOD);
+ ficlDictionarySetConstant(dictionary, "multicall-calltype-virtual-method", FICL_MULTICALL_CALLTYPE_VIRTUAL_METHOD);
+ ficlDictionarySetConstant(dictionary, "multicall-returntype-void", FICL_MULTICALL_RETURNTYPE_VOID);
+ ficlDictionarySetConstant(dictionary, "multicall-returntype-integer", FICL_MULTICALL_RETURNTYPE_INTEGER);
+ ficlDictionarySetConstant(dictionary, "multicall-returntype-cstring", FICL_MULTICALL_RETURNTYPE_CSTRING);
+ ficlDictionarySetConstant(dictionary, "multicall-returntype-float", FICL_MULTICALL_RETURNTYPE_FLOAT);
+ ficlDictionarySetConstant(dictionary, "multicall-reverse-arguments", FICL_MULTICALL_REVERSE_ARGUMENTS);
+ ficlDictionarySetConstant(dictionary, "multicall-explit-vtable", FICL_MULTICALL_EXPLICIT_VTABLE);
+
+ /*
+ ** Every other Win32-specific word is implemented in Ficl, with multicall or whatnot.
+ ** (Give me a lever, and a place to stand, and I will move the Earth.)
+ ** See softcore/win32.fr for details. --lch
+ */
+ hModule = LoadLibrary("kernel32.dll");
+ ficlDictionarySetConstantPointer(dictionary, "kernel32.dll", hModule);
+ ficlDictionarySetConstantPointer(dictionary, "(get-proc-address)", GetProcAddress(hModule, "GetProcAddress"));
+ FreeLibrary(hModule);
+
+ return;
+}