A few years ago I finally decided to see what I could do in 64 KB. I managed to get the basics of a first-person shooter up and running in around 16 KB, and I'll share a few of the things I learned along the way.
I wrote a framework in assembly language but I don't think that's necessarily required. What is necessary is to examine the assembly output of your compiler so you know where the bytes are going. Also, grab an executable compressor. I used UPX but I also recently found out about Crinkler.
One of the big space users when you're coding in C/C++ is the C Runtime Library, a set of commonly-used functions that are statically linked into every executable. Getting rid of this saves a big chunk of executable size.
The primary thing the C Runtime Library provides is a heap manager. Fortunately, the Windows API also provides a heap manager, so all that's necessary to replace it is to write a few glue functions to hook up C++ to the Windows heap. Here's my header file win32_new.h:
#include <new>
#include <exception>
// Overload global heap allocation functions
void* __cdecl operator new (size_t num_bytes);
void* __cdecl operator new[](size_t num_bytes);
void __cdecl operator delete (void* memory);
void __cdecl operator delete[](void* memory);
inline void __cdecl std::_Throw(const std::exception &)
{
error_exit("std::_Throw called.");
}
And the corresponding source file, win32_new.cpp:
#include "win32_new.h"
#include <windows.h>
#include <string>
typedef unsigned short wchar_t;
void* __cdecl operator new(size_t num_bytes)
{
return HeapAlloc(GetProcessHeap(), 0, num_bytes);
}
void* __cdecl operator new[](size_t num_bytes)
{
return HeapAlloc(GetProcessHeap(), 0, num_bytes);
}
void __cdecl operator delete(void* memory)
{
HeapFree(GetProcessHeap(), 0, memory);
}
void __cdecl operator delete[](void* memory)
{
HeapFree(GetProcessHeap(), 0, memory);
}
void __stdcall raise_handler(const std::exception &)
{
error_exit("raise_handler called.");
}
void (__stdcall* std::_Raise_handler)(const std::exception &) = raise_handler;
void* __cdecl memmove(void *dest, const void *src, size_t count)
{
return wmemmove(reinterpret_cast<wchar_t *> (dest), reinterpret_cast<const wchar_t *>(src), count);
}
void std::_String_base::_Xlen() const // report a length_error
{
error_exit("std::_String_base::_Xlen called.");
}
void std::_String_base::_Xran() const // report an out_of_range error
{
error_exit("std::_String_base::_Xran called.");
}
The C++ new and delete operators are overridden to call the appropriate Win32 functions. I also provided definitions for a few other functions.
No comments:
Post a Comment