diff --git a/dchip-8.vcxproj b/dchip-8.vcxproj
index 464bf8a..169c81f 100644
--- a/dchip-8.vcxproj
+++ b/dchip-8.vcxproj
@@ -18,6 +18,9 @@
x64
+
+
+
{01B5A474-4BA5-48A3-B645-7116ADF5585D}
dchip8
@@ -119,9 +122,6 @@
true
-
-
-
diff --git a/dchip-8.vcxproj.filters b/dchip-8.vcxproj.filters
index 9120b0a..c412874 100644
--- a/dchip-8.vcxproj.filters
+++ b/dchip-8.vcxproj.filters
@@ -15,7 +15,7 @@
-
+
Source Files
diff --git a/src/Common.h b/src/Common.h
index 5011a99..a1c8569 100644
--- a/src/Common.h
+++ b/src/Common.h
@@ -4,7 +4,6 @@
#include "stdint.h"
#define LOCAL_PERSIST static
-#define GLOBAL_VAR static
#define FILE_SCOPE static
typedef uint32_t u32;
diff --git a/src/build.bat b/src/build.bat
index d22a7c2..2d67660 100644
--- a/src/build.bat
+++ b/src/build.bat
@@ -2,7 +2,8 @@
@REM vcvarsall.bat to setup command-line compiler.
@echo OFF
-set ProjectName=dchip-8
+set ProjectName=dchip8
+set CompileEntryPoint=..\src\unity_build.cpp
ctime -begin %ProjectName%.ctm
@@ -47,7 +48,7 @@ REM incrmenetal:no, turn incremental builds off
REM opt:ref, try to remove functions from libs that are referenced at all
set LinkFlags=-incremental:no -opt:ref
-cl %CompileFlags% ..\src\%ProjectName%.cpp %IncludeFlags% /link -subsystem:WINDOWS,5.1 %LinkLibraries% %LinkFlags% /nologo /OUT:"%ProjectName%.exe"
+cl %CompileFlags% %CompileEntryPoint% %IncludeFlags% /link -subsystem:WINDOWS,5.1 %LinkLibraries% %LinkFlags% /nologo /OUT:"%ProjectName%.exe"
popd
ctime -end %ProjectName%.ctm
diff --git a/src/dchip-8.cpp b/src/dchip-8.cpp
deleted file mode 100644
index d85ac8d..0000000
--- a/src/dchip-8.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-#define UNICODE
-#define _UNICODE
-
-#ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
-#endif
-
-#include "Common.h"
-
-#include
-#include
-#include
-
-FILE_SCOPE HBITMAP bitmap;
-FILE_SCOPE BITMAPINFO bitmapInfo;
-
-const i32 WIDTH = 64;
-const i32 HEIGHT = 32;
-const i32 RESOLUTION = WIDTH * HEIGHT;
-FILE_SCOPE void *bitmapMemory;
-
-FILE_SCOPE bool globalRunning = false;
-
-FILE_SCOPE LRESULT CALLBACK mainWindowProcCallback(HWND window, UINT msg,
- WPARAM wParam, LPARAM lParam)
-{
- LRESULT result = 0;
- switch (msg)
- {
- case WM_CLOSE:
- case WM_DESTROY:
- {
- globalRunning = false;
- }
- break;
-
- default:
- {
- result = DefWindowProc(window, msg, wParam, lParam);
- }
- break;
- }
-
- return result;
-}
-
-GLOBAL_VAR LARGE_INTEGER globalQueryPerformanceFrequency;
-inline FILE_SCOPE f32 getTimeFromQueryPerfCounter(LARGE_INTEGER start,
- LARGE_INTEGER end)
-{
- f32 result = (f32)(end.QuadPart - start.QuadPart) /
- globalQueryPerformanceFrequency.QuadPart;
- return result;
-}
-
-inline FILE_SCOPE LARGE_INTEGER getWallClock()
-{
- LARGE_INTEGER result;
- QueryPerformanceCounter(&result);
-
- return result;
-}
-
-#define ErrorBox(text, title) MessageBox(nullptr, text, title, MB_OK);
-int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
- LPSTR lpCmdLine, int nShowCmd)
-{
- WNDCLASSEX wc =
- {
- sizeof(WNDCLASSEX),
- CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
- mainWindowProcCallback,
- 0, // int cbClsExtra
- 0, // int cbWndExtra
- hInstance,
- LoadIcon(NULL, IDI_APPLICATION),
- LoadCursor(NULL, IDC_ARROW),
- GetSysColorBrush(COLOR_3DFACE),
- L"", // LPCTSTR lpszMenuName
- L"dchip-8Class",
- NULL, // HICON hIconSm
- };
-
- if (!RegisterClassEx(&wc)) {
- ErrorBox(L"RegisterClassEx() failed.", nullptr);
- return -1;
- }
-
- globalRunning = true;
-
- // NOTE: Regarding Window Sizes
- // If you specify a window size, e.g. 800x600, Windows regards the 800x600
- // region to be inclusive of the toolbars and side borders. So in actuality,
- // when you blit to the screen blackness, the area that is being blitted to
- // is slightly smaller than 800x600. Windows provides a function to help
- // calculate the size you'd need by accounting for the window style.
- RECT rect = {};
- rect.right = 128;
- rect.bottom = 64;
-
- DWORD windowStyle =
- WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
- AdjustWindowRect(&rect, windowStyle, FALSE);
-
- HWND mainWindow = CreateWindowEx(
- WS_EX_COMPOSITED, wc.lpszClassName, L"dchip-8", windowStyle,
- CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left,
- rect.bottom - rect.top, nullptr, nullptr, hInstance, nullptr);
-
- if (!mainWindow)
- {
- ErrorBox(L"CreateWindowEx() failed.", nullptr);
- return -1;
- }
-
- QueryPerformanceFrequency(&globalQueryPerformanceFrequency);
- LARGE_INTEGER startFrameTime;
- const f32 targetFramesPerSecond = 16.0f;
- f32 targetSecondsPerFrame = 1 / targetFramesPerSecond;
- f32 frameTimeInS = 0.0f;
-
- {
- BITMAPINFOHEADER header = {};
- header.biSize = sizeof(BITMAPINFOHEADER);
- header.biWidth = WIDTH;
- header.biHeight = HEIGHT;
- header.biPlanes = 1;
- header.biBitCount = 32;
- header.biCompression = BI_RGB; // uncompressed bitmap
- header.biSizeImage = 0;
- header.biXPelsPerMeter = 0;
- header.biYPelsPerMeter = 0;
- header.biClrUsed = 0;
- header.biClrImportant = 0;
-
- bitmapInfo.bmiHeader = header;
-
- const i32 numPixels = header.biWidth * header.biHeight;
- const i32 bytesPerPixel = header.biBitCount / 8;
-#if 0
- bitmapMemory = calloc(1, numPixels * bytesPerPixel);
-
- if (!bitmapMemory)
- {
- ErrorBox(L"malloc() failed.", nullptr);
- return -1;
- }
-
-#else
- HDC deviceContext = GetDC(mainWindow);
- bitmap = CreateDIBSection(deviceContext, &bitmapInfo, DIB_RGB_COLORS,
- &bitmapMemory, NULL, NULL);
- ReleaseDC(mainWindow, deviceContext);
-
- if (!bitmapMemory)
- {
- ErrorBox(L"CreateDIBSection() failed.", nullptr);
- return -1;
- }
-#endif
-
- u32 *bitmapBuffer = (u32 *)bitmapMemory;
- for (i32 i = 0; i < numPixels; i++)
- {
- // NOTE: Win32 AlphaBlend requires the RGB components to be
- // premultiplied with alpha.
- f32 normA = 1.0f;
- f32 normR = (normA * 0.0f);
- f32 normG = (normA * 0.0f);
- f32 normB = (normA * 1.0f);
-
- u8 r = (u8)(normR * 255.0f);
- u8 g = (u8)(normG * 255.0f);
- u8 b = (u8)(normB * 255.0f);
- u8 a = (u8)(normA * 255.0f);
-
- u32 color = (a << 24) | (r << 16) | (g << 8) | (b << 0);
- bitmapBuffer[i] = color;
- }
- }
-
- MSG msg;
- while (globalRunning)
- {
- startFrameTime = getWallClock();
- while (PeekMessage(&msg, mainWindow, 0, 0, PM_REMOVE))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- RECT clientRect = {};
- GetClientRect(mainWindow, &clientRect);
-
- LONG clientWidth = clientRect.right - clientRect.left;
- LONG clientHeight = clientRect.bottom - clientRect.top;
-
- BLENDFUNCTION blend = {};
- blend.BlendOp = AC_SRC_OVER;
- blend.SourceConstantAlpha = 255;
- blend.AlphaFormat = AC_SRC_ALPHA;
-
- HDC deviceContext = GetDC(mainWindow);
- HDC alphaBlendDC = CreateCompatibleDC(deviceContext);
- SelectObject(alphaBlendDC, bitmap);
-
- AlphaBlend(deviceContext, 0, 0, WIDTH, HEIGHT, alphaBlendDC, 0, 0,
- WIDTH, HEIGHT, blend);
- StretchBlt(deviceContext, 0, 0, clientWidth, clientHeight,
- deviceContext, 0, 0, WIDTH, HEIGHT, SRCCOPY);
-
- DeleteDC(alphaBlendDC);
- ReleaseDC(mainWindow, deviceContext);
-
- ////////////////////////////////////////////////////////////////////////
- // Frame Limiting
- ////////////////////////////////////////////////////////////////////////
- LARGE_INTEGER endWorkTime = getWallClock();
- f32 workTimeInS =
- getTimeFromQueryPerfCounter(startFrameTime, endWorkTime);
- if (workTimeInS < targetSecondsPerFrame)
- {
- DWORD remainingTimeInMs =
- (DWORD)((targetSecondsPerFrame - workTimeInS) * 1000);
- Sleep(remainingTimeInMs);
- }
-
- LARGE_INTEGER endFrameTime = getWallClock();
- frameTimeInS =
- getTimeFromQueryPerfCounter(startFrameTime, endFrameTime);
- f32 msPerFrame = 1000.0f * frameTimeInS;
-
- wchar_t windowTitleBuffer[128] = {};
- _snwprintf_s(windowTitleBuffer, ARRAY_COUNT(windowTitleBuffer),
- ARRAY_COUNT(windowTitleBuffer), L"dchip-8 | %5.2f ms/f",
- msPerFrame);
- SetWindowText(mainWindow, windowTitleBuffer);
- }
-
- return 0;
-}
diff --git a/src/dchip8.cpp b/src/dchip8.cpp
new file mode 100644
index 0000000..b68ad88
--- /dev/null
+++ b/src/dchip8.cpp
@@ -0,0 +1,27 @@
+#include "common.h"
+#include "dchip8_platform.h"
+
+void dchip8_update(PlatformRenderBuffer *renderBuffer)
+{
+ ASSERT(renderBuffer.bytesPerPixel == 4);
+
+ const i32 numPixels = renderBuffer->width * renderBuffer->height;
+ u32 *bitmapBuffer = (u32 *)renderBuffer->memory;
+ for (i32 i = 0; i < numPixels; i++)
+ {
+ // NOTE: Win32 AlphaBlend requires the RGB components to be
+ // premultiplied with alpha.
+ f32 normA = 1.0f;
+ f32 normR = (normA * 0.0f);
+ f32 normG = (normA * 0.0f);
+ f32 normB = (normA * 1.0f);
+
+ u8 r = (u8)(normR * 255.0f);
+ u8 g = (u8)(normG * 255.0f);
+ u8 b = (u8)(normB * 255.0f);
+ u8 a = (u8)(normA * 255.0f);
+
+ u32 color = (a << 24) | (r << 16) | (g << 8) | (b << 0);
+ bitmapBuffer[i] = color;
+ }
+}
diff --git a/src/dchip8.h b/src/dchip8.h
new file mode 100644
index 0000000..4ed6d95
--- /dev/null
+++ b/src/dchip8.h
@@ -0,0 +1,8 @@
+#ifndef DCHIP8_H
+#define DCHIP8_H
+
+#include "dchip8_platform.h"
+
+void dchip8_update(PlatformRenderBuffer *renderBuffer);
+
+#endif
diff --git a/src/dchip8_platform.h b/src/dchip8_platform.h
new file mode 100644
index 0000000..cfa1d20
--- /dev/null
+++ b/src/dchip8_platform.h
@@ -0,0 +1,14 @@
+#ifndef DCHIP_8_PLATFORM
+#define DCHIP_8_PLATFORM
+
+#include "Common.h"
+
+typedef struct PlatformRenderBuffer
+{
+ i32 width;
+ i32 height;
+ i32 bytesPerPixel;
+ void *memory;
+} PlatformRenderBuffer;
+
+#endif
diff --git a/src/unity_build.cpp b/src/unity_build.cpp
new file mode 100644
index 0000000..223d70b
--- /dev/null
+++ b/src/unity_build.cpp
@@ -0,0 +1,3 @@
+#include "win32_dchip8.cpp"
+
+#include "dchip8.cpp"
diff --git a/src/win32_dchip8.cpp b/src/win32_dchip8.cpp
new file mode 100644
index 0000000..563b385
--- /dev/null
+++ b/src/win32_dchip8.cpp
@@ -0,0 +1,233 @@
+#define UNICODE
+#define _UNICODE
+
+#ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+#endif
+
+#include
+#include
+#include
+
+#include "common.h"
+#include "dchip8.h"
+#include "dchip8_platform.h"
+
+FILE_SCOPE bool globalRunning = false;
+FILE_SCOPE LARGE_INTEGER globalQueryPerformanceFrequency;
+#define win32_error_box(text, title) MessageBox(nullptr, text, title, MB_OK);
+
+FILE_SCOPE LRESULT CALLBACK win32_main_proc_callback(HWND window, UINT msg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LRESULT result = 0;
+ switch (msg)
+ {
+ case WM_CLOSE:
+ case WM_DESTROY:
+ {
+ globalRunning = false;
+ }
+ break;
+
+ default:
+ {
+ result = DefWindowProc(window, msg, wParam, lParam);
+ }
+ break;
+ }
+
+ return result;
+}
+
+inline FILE_SCOPE f32 win32_query_perf_counter_get_time(LARGE_INTEGER start,
+ LARGE_INTEGER end)
+{
+ f32 result = (f32)(end.QuadPart - start.QuadPart) /
+ globalQueryPerformanceFrequency.QuadPart;
+ return result;
+}
+
+inline FILE_SCOPE LARGE_INTEGER win32_query_perf_counter_time()
+{
+ LARGE_INTEGER result;
+ QueryPerformanceCounter(&result);
+
+ return result;
+}
+
+typedef struct Win32RenderBitmap
+{
+ BITMAPINFO info;
+ HBITMAP handle;
+ i32 width;
+ i32 height;
+ i32 bytesPerPixel;
+ void *memory;
+} Win32RenderBitmap;
+
+FILE_SCOPE void win32_display_render_bitmap(Win32RenderBitmap renderBitmap,
+ HDC deviceContext, LONG width,
+ LONG height)
+{
+ BLENDFUNCTION blend = {};
+ blend.BlendOp = AC_SRC_OVER;
+ blend.SourceConstantAlpha = 255;
+ blend.AlphaFormat = AC_SRC_ALPHA;
+
+ HDC alphaBlendDC = CreateCompatibleDC(deviceContext);
+ SelectObject(alphaBlendDC, renderBitmap.handle);
+
+ AlphaBlend(deviceContext, 0, 0, width, height, alphaBlendDC, 0, 0,
+ renderBitmap.width, renderBitmap.height, blend);
+ StretchBlt(deviceContext, 0, 0, width, height, deviceContext, 0, 0,
+ renderBitmap.width, renderBitmap.height, SRCCOPY);
+
+ DeleteDC(alphaBlendDC);
+}
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine, int nShowCmd)
+{
+ WNDCLASSEX wc =
+ {
+ sizeof(WNDCLASSEX),
+ CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
+ win32_main_proc_callback,
+ 0, // int cbClsExtra
+ 0, // int cbWndExtra
+ hInstance,
+ LoadIcon(NULL, IDI_APPLICATION),
+ LoadCursor(NULL, IDC_ARROW),
+ NULL, // GetSysColorBrush(COLOR_3DFACE),
+ L"", // LPCTSTR lpszMenuName
+ L"dchip-8Class",
+ NULL, // HICON hIconSm
+ };
+
+ if (!RegisterClassEx(&wc)) {
+ win32_error_box(L"RegisterClassEx() failed.", nullptr);
+ return -1;
+ }
+
+ // NOTE: Regarding Window Sizes
+ // If you specify a window size, e.g. 800x600, Windows regards the 800x600
+ // region to be inclusive of the toolbars and side borders. So in actuality,
+ // when you blit to the screen blackness, the area that is being blitted to
+ // is slightly smaller than 800x600. Windows provides a function to help
+ // calculate the size you'd need by accounting for the window style.
+ RECT rect = {};
+ rect.right = 128;
+ rect.bottom = 64;
+
+ DWORD windowStyle =
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+ AdjustWindowRect(&rect, windowStyle, FALSE);
+
+ HWND mainWindow = CreateWindowEx(
+ WS_EX_COMPOSITED, wc.lpszClassName, L"dchip-8", windowStyle,
+ CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left,
+ rect.bottom - rect.top, nullptr, nullptr, hInstance, nullptr);
+
+ if (!mainWindow)
+ {
+ win32_error_box(L"CreateWindowEx() failed.", nullptr);
+ return -1;
+ }
+
+ Win32RenderBitmap renderBitmap = {};
+ { // Initialise the renderbitmap
+ BITMAPINFOHEADER header = {};
+ header.biSize = sizeof(BITMAPINFOHEADER);
+ header.biWidth = 64;
+ header.biHeight = 32;
+ header.biPlanes = 1;
+ header.biBitCount = 32;
+ header.biCompression = BI_RGB; // uncompressed bitmap
+ header.biSizeImage = 0;
+ header.biXPelsPerMeter = 0;
+ header.biYPelsPerMeter = 0;
+ header.biClrUsed = 0;
+ header.biClrImportant = 0;
+
+ renderBitmap.info.bmiHeader = header;
+ renderBitmap.width = header.biWidth;
+ renderBitmap.height = header.biHeight;
+ renderBitmap.bytesPerPixel = header.biBitCount / 8;
+ ASSERT(renderBitmap.bytesPerPixel >= 1);
+
+ HDC deviceContext = GetDC(mainWindow);
+ renderBitmap.handle =
+ CreateDIBSection(deviceContext, &renderBitmap.info, DIB_RGB_COLORS,
+ &renderBitmap.memory, NULL, NULL);
+ ReleaseDC(mainWindow, deviceContext);
+ }
+
+ if (!renderBitmap.memory)
+ {
+ win32_error_box(L"CreateDIBSection() failed.", nullptr);
+ return -1;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Update Loop
+ ////////////////////////////////////////////////////////////////////////////
+ QueryPerformanceFrequency(&globalQueryPerformanceFrequency);
+ const f32 TARGET_FRAMES_PER_S = 60.0f;
+ f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S;
+ f32 frameTimeInS = 0.0f;
+ globalRunning = true;
+
+ while (globalRunning)
+ {
+ LARGE_INTEGER startFrameTime = win32_query_perf_counter_time();
+
+ MSG msg;
+ while (PeekMessage(&msg, mainWindow, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Rendering
+ ////////////////////////////////////////////////////////////////////////
+ PlatformRenderBuffer renderBuffer = {};
+ renderBuffer.memory = renderBitmap.memory;
+ renderBuffer.height = renderBitmap.height;
+ renderBuffer.width = renderBitmap.width;
+ dchip8_update(&renderBuffer);
+
+ HDC deviceContext = GetDC(mainWindow);
+ win32_display_render_bitmap(renderBitmap, deviceContext, clientWidth,
+ clientHeight);
+ ReleaseDC(mainWindow, deviceContext);
+
+ ////////////////////////////////////////////////////////////////////////
+ // Frame Limiting
+ ////////////////////////////////////////////////////////////////////////
+ LARGE_INTEGER endWorkTime = win32_query_perf_counter_time();
+ f32 workTimeInS =
+ win32_query_perf_counter_get_time(startFrameTime, endWorkTime);
+ if (workTimeInS < targetSecondsPerFrame)
+ {
+ DWORD remainingTimeInMs =
+ (DWORD)((targetSecondsPerFrame - workTimeInS) * 1000);
+ Sleep(remainingTimeInMs);
+ }
+
+ LARGE_INTEGER endFrameTime = win32_query_perf_counter_time();
+ frameTimeInS =
+ win32_query_perf_counter_get_time(startFrameTime, endFrameTime);
+ f32 msPerFrame = 1000.0f * frameTimeInS;
+
+ wchar_t windowTitleBuffer[128] = {};
+ _snwprintf_s(windowTitleBuffer, ARRAY_COUNT(windowTitleBuffer),
+ ARRAY_COUNT(windowTitleBuffer), L"dchip-8 | %5.2f ms/f",
+ msPerFrame);
+ SetWindowText(mainWindow, windowTitleBuffer);
+ }
+
+ return 0;
+}