From dd4ae0d793baf96c6947896c27b3a42b3cc6f5ac Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Fri, 7 Apr 2017 21:19:03 +1000 Subject: [PATCH] Add rom loading, fixed aspect ratio into win32 --- src/build.bat | 2 +- src/dchip8.cpp | 37 ++++--- src/dchip8.h | 2 + src/dchip8_platform.h | 3 + src/dqnt.h | 3 + src/win32_dchip8.cpp | 229 +++++++++++++++++++++++++++++++++++++++--- 6 files changed, 242 insertions(+), 34 deletions(-) diff --git a/src/build.bat b/src/build.bat index 7ae8cf9..f4bd47e 100644 --- a/src/build.bat +++ b/src/build.bat @@ -43,7 +43,7 @@ REM Include directories set IncludeFlags= REM Link libraries -set LinkLibraries=user32.lib gdi32.lib msimg32.lib +set LinkLibraries=user32.lib gdi32.lib msimg32.lib Comdlg32.lib REM incrmenetal:no, turn incremental builds off REM opt:ref, try to remove functions from libs that are referenced at all diff --git a/src/dchip8.cpp b/src/dchip8.cpp index 63a3463..212b7cb 100644 --- a/src/dchip8.cpp +++ b/src/dchip8.cpp @@ -8,11 +8,9 @@ enum Chip8State { - chip8state_init, - chip8state_load_file, + chip8state_off, chip8state_await_input, chip8state_running, - chip8state_off, }; typedef struct Chip8Controller @@ -20,10 +18,9 @@ typedef struct Chip8Controller bool key[0x10]; } Chip8Controller; +#define INIT_ADDRESS 0x200 typedef struct Chip8CPU { - const u16 INIT_ADDRESS = 0x200; - union { u8 registerArray[16]; struct @@ -80,8 +77,11 @@ typedef struct Chip8CPU FILE_SCOPE Chip8CPU cpu; FILE_SCOPE RandPCGState pcgState; -FILE_SCOPE void dchip8_init_memory(u8 *memory) +FILE_SCOPE void dchip8_init_memory(u8 *memory, u32 size) { + for (u32 i = 0; i < size; i++) + memory[i] = 0; + const u8 PRESET_FONTS[] = { // "0" @@ -203,15 +203,15 @@ FILE_SCOPE void dchip8_init_memory(u8 *memory) FILE_SCOPE void dchip8_init_cpu(Chip8CPU *chip8CPU) { + memset(chip8CPU, 0, sizeof(*chip8CPU)); + // NOTE: Everything before 0x200 is reserved for the actual emulator - chip8CPU->programCounter = chip8CPU->INIT_ADDRESS; + chip8CPU->programCounter = INIT_ADDRESS; chip8CPU->I = 0; chip8CPU->stackPointer = 0; const u32 SEED = 0x8293A8DE; dqnt_rnd_pcg_seed(&pcgState, SEED); - - chip8CPU->state = chip8state_load_file; } FILE_SCOPE @@ -323,22 +323,19 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, u8 *mainMem = (u8 *)memory.permanentMem; Chip8Controller controller = dchip8_controller_map_input(input); - if (cpu.state == chip8state_init) + + if (input.loadNewRom) { + dchip8_init_memory(mainMem, memory.permanentMemSize); dchip8_init_cpu(&cpu); - dchip8_init_memory(mainMem); dchip8_init_display(renderBuffer); - } - if (cpu.state == chip8state_load_file) - { PlatformFile file = {}; - if (platform_open_file(L"roms/brix", &file)) + if (platform_open_file(input.rom, &file)) { - DQNT_ASSERT((cpu.INIT_ADDRESS + file.size) <= - memory.permanentMemSize); + DQNT_ASSERT((INIT_ADDRESS + file.size) <= memory.permanentMemSize); - void *loadToAddr = (void *)(&mainMem[cpu.INIT_ADDRESS]); + void *loadToAddr = (void *)(&mainMem[INIT_ADDRESS]); if (platform_read_file(file, loadToAddr, (u32)file.size)) { cpu.state = chip8state_running; @@ -347,9 +344,10 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, { cpu.state = chip8state_off; } - platform_close_file(&file); } + + input.loadNewRom = false; } if (cpu.state == chip8state_await_input) @@ -832,6 +830,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, break; }; } + // IMPORTANT: Timers need to be decremented at a rate of 60hz. Since we // can run the interpreter faster than that, make sure we decrement // timers at the fixed rate. diff --git a/src/dchip8.h b/src/dchip8.h index 6cd45e6..34d2157 100644 --- a/src/dchip8.h +++ b/src/dchip8.h @@ -3,6 +3,8 @@ #include "dchip8_platform.h" +bool dchip8_load_rom(wchar_t *filePath); + void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, PlatformMemory memory, u32 cyclesToEmulate); diff --git a/src/dchip8_platform.h b/src/dchip8_platform.h index 1e84aba..7b533fb 100644 --- a/src/dchip8_platform.h +++ b/src/dchip8_platform.h @@ -54,6 +54,9 @@ typedef struct PlatformInput { f32 deltaForFrame; + bool loadNewRom; + wchar_t rom[260]; + union { KeyState key[key_count]; struct diff --git a/src/dqnt.h b/src/dqnt.h index 51de3bd..6279fec 100644 --- a/src/dqnt.h +++ b/src/dqnt.h @@ -29,6 +29,9 @@ typedef float f32; #define DQNT_ASSERT(expr) if (!(expr)) { (*((i32 *)0)) = 0; } #define DQNT_MATH_ABS(x) (((x) < 0) ? (-(x)) : (x)) +#define DQNT_MATH_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define DQNT_MATH_MAX(x, y) (((x) < (y)) ? (y) : (x)) + //////////////////////////////////////////////////////////////////////////////// // Vec2 //////////////////////////////////////////////////////////////////////////////// diff --git a/src/win32_dchip8.cpp b/src/win32_dchip8.cpp index c40787a..8c1cdba 100644 --- a/src/win32_dchip8.cpp +++ b/src/win32_dchip8.cpp @@ -6,6 +6,7 @@ #endif #include +#include #include #include @@ -23,11 +24,85 @@ typedef struct Win32RenderBitmap void *memory; } Win32RenderBitmap; +#define MIN_WIDTH 256; +#define MIN_HEIGHT 128; +typedef struct Win32State +{ + bool useCorrectAspectRatio = true; +} Win32State; + FILE_SCOPE bool globalRunning = false; FILE_SCOPE Win32RenderBitmap globalRenderBitmap; FILE_SCOPE LARGE_INTEGER globalQueryPerformanceFrequency; +FILE_SCOPE Win32State globalState; #define win32_error_box(text, title) MessageBox(nullptr, text, title, MB_OK); +enum Win32Menu +{ + win32menu_file_exit, + win32menu_file_open, + + win32menu_video_correct_aspect_ratio, + win32menu_video_4x, + win32menu_video_8x, + win32menu_video_12x, + win32menu_video_16x, +}; + +FILE_SCOPE v2 constrain_to_aspect_ratio(u32 width, u32 height, f32 ratioX, + f32 ratioY) +{ + v2 result = {}; + f32 numRatioIncrementsToWidth = (f32)(width / ratioX); + f32 numRatioIncrementsToHeight = (f32)(height / ratioY); + + f32 leastIncrementsToSide = + DQNT_MATH_MIN(numRatioIncrementsToHeight, numRatioIncrementsToWidth); + + result.w = (f32)(ratioX * leastIncrementsToSide); + result.h = (f32)(ratioY * leastIncrementsToSide); + return result; +} + +FILE_SCOPE void win32_create_menu(HWND window) +{ + HMENU menuBar = CreateMenu(); + { // File Menu + HMENU menu = CreatePopupMenu(); + AppendMenu(menuBar, MF_STRING | MF_POPUP, (UINT)menu, L"File"); + AppendMenu(menu, MF_STRING, win32menu_file_open, L"Open ROM"); + AppendMenu(menu, MF_STRING, win32menu_file_exit, L"Exit"); + } + + { // Video Menu + HMENU menu = CreatePopupMenu(); + AppendMenu(menuBar, MF_STRING | MF_POPUP, (UINT)menu, L"Video"); + AppendMenu(menu, MF_STRING | MF_CHECKED, + win32menu_video_correct_aspect_ratio, + L"Use Correct Aspect Ratio"); + AppendMenu(menu, MF_SEPARATOR, 0, nullptr); + AppendMenu(menu, MF_STRING, win32menu_video_4x, L"4x"); + AppendMenu(menu, MF_STRING, win32menu_video_8x, L"8x"); + AppendMenu(menu, MF_STRING, win32menu_video_12x, L"12x"); + AppendMenu(menu, MF_STRING, win32menu_video_16x, L"16x"); + } + + SetMenu(window, menuBar); + + /* + MENUITEMINFO testMenuInfo = {}; + testMenuInfo.cbSize = sizeof(MENUITEMINFO); + testMenuInfo.fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_ID; + testMenuInfo.fType = MFT_STRING; + testMenuInfo.fState = 0; + testMenuInfo.wID = win32menu_file_test; + testMenuInfo.hSubMenu = fileMenu; + testMenuInfo.dwTypeData = L"Test"; + testMenuInfo.cch = dqnt_wstrlen(testMenuInfo.dwTypeData); + InsertMenuItem(menu, 0, true, &testMenuInfo); + */ +} + inline FILE_SCOPE void win32_get_client_dim(HWND window, LONG *width, LONG *height) { @@ -70,6 +145,12 @@ FILE_SCOPE LRESULT CALLBACK win32_main_proc_callback(HWND window, UINT msg, LRESULT result = 0; switch (msg) { + case WM_CREATE: + { + win32_create_menu(window); + } + break; + case WM_CLOSE: case WM_DESTROY: { @@ -81,14 +162,34 @@ FILE_SCOPE LRESULT CALLBACK win32_main_proc_callback(HWND window, UINT msg, { PAINTSTRUCT paint; HDC deviceContext = BeginPaint(window, &paint); - LONG clientWidth, clientHeight; - win32_get_client_dim(window, &clientWidth, &clientHeight); + + LONG renderWidth, renderHeight; + win32_get_client_dim(window, &renderWidth, &renderHeight); + if (globalState.useCorrectAspectRatio) + { + f32 ratioX = (f32)globalRenderBitmap.width; + f32 ratioY = (f32)globalRenderBitmap.height; + v2 newDim = constrain_to_aspect_ratio(renderWidth, renderHeight, + ratioX, ratioY); + + renderWidth = (LONG)newDim.w; + renderHeight = (LONG)newDim.h; + } + win32_display_render_bitmap(globalRenderBitmap, deviceContext, - clientWidth, clientHeight); + renderWidth, renderHeight); EndPaint(window, &paint); break; } + case WM_GETMINMAXINFO: + { + MINMAXINFO *mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = MIN_WIDTH; + mmi->ptMaxSize.y = MIN_HEIGHT; + } + break; + default: { result = DefWindowProc(window, msg, wParam, lParam); @@ -124,6 +225,92 @@ FILE_SCOPE inline void win32_update_key(KeyState *key, bool isDown) } } +FILE_SCOPE void win32_handle_menu_messages(HWND window, MSG msg, + PlatformInput *input) +{ + switch (LOWORD(msg.wParam)) + { + case win32menu_file_exit: + { + globalRunning = false; + } + break; + + case win32menu_file_open: + { + wchar_t fileBuffer[MAX_PATH] = {}; + OPENFILENAME openDialogInfo = {}; + openDialogInfo.lStructSize = sizeof(OPENFILENAME); + openDialogInfo.hwndOwner = window; + openDialogInfo.lpstrFile = fileBuffer; + openDialogInfo.nMaxFile = DQNT_ARRAY_COUNT(fileBuffer); + openDialogInfo.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + + if (GetOpenFileName(&openDialogInfo) != 0) + { + wchar_t candidateRom[MAX_PATH] = {}; + wchar_t currentRom[MAX_PATH] = {}; + GetFullPathName(fileBuffer, DQNT_ARRAY_COUNT(candidateRom), + candidateRom, nullptr); + GetFullPathName(input->rom, DQNT_ARRAY_COUNT(currentRom), + currentRom, nullptr); + + if (dqnt_wstrcmp(candidateRom, currentRom) != 0) + { + input->loadNewRom = true; + + for (i32 i = 0; i < dqnt_wstrlen(candidateRom); i++) + input->rom[i] = candidateRom[i]; + } + } + } + break; + + case win32menu_video_4x: + case win32menu_video_8x: + case win32menu_video_12x: + case win32menu_video_16x: + { + u32 startingWidth = 64; + u32 startingHeight = 32; + + u32 modifier; + switch (LOWORD(msg.wParam)) + { + default: + case win32menu_video_4x: modifier = 4; break; + case win32menu_video_8x: modifier = 8; break; + case win32menu_video_12x: modifier = 12; break; + case win32menu_video_16x: modifier = 16; break; + } + + RECT rect = {}; + rect.right = startingWidth * modifier; + rect.bottom = startingHeight * modifier; + + DWORD windowStyle = (DWORD)GetWindowLong(window, GWL_STYLE); + AdjustWindowRect(&rect, windowStyle, true); + + SetWindowPos(window, NULL, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER); + } + break; + + case win32menu_video_correct_aspect_ratio: + { + globalState.useCorrectAspectRatio = + (globalState.useCorrectAspectRatio) ? false : true; + } + break; + + default: + { + DQNT_ASSERT(DQNT_INVALID_CODE_PATH); + } + break; + } +} + FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input) { MSG msg; @@ -131,6 +318,12 @@ FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input) { switch (msg.message) { + case WM_COMMAND: + { + win32_handle_menu_messages(window, msg, input); + } + break; + case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_KEYDOWN: @@ -173,7 +366,8 @@ FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input) default: break; } - }; + } + break; default: { @@ -200,7 +394,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, hInstance, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), - NULL, // GetSysColorBrush(COLOR_3DFACE), + GetSysColorBrush(COLOR_3DFACE), L"", // LPCTSTR lpszMenuName L"dchip-8Class", NULL, // HICON hIconSm @@ -219,12 +413,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, // 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 = 256; - rect.bottom = 128; - + rect.right = MIN_WIDTH; + rect.bottom = MIN_HEIGHT; DWORD windowStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - AdjustWindowRect(&rect, windowStyle, FALSE); + AdjustWindowRect(&rect, windowStyle, true); HWND mainWindow = CreateWindowEx( WS_EX_COMPOSITED, wc.lpszClassName, L"dchip-8", windowStyle, @@ -307,14 +500,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, // Rendering //////////////////////////////////////////////////////////////////////// { - RECT clientRect = {}; - GetClientRect(mainWindow, &clientRect); - LONG clientWidth = clientRect.right - clientRect.left; - LONG clientHeight = clientRect.bottom - clientRect.top; + LONG renderWidth, renderHeight; + win32_get_client_dim(mainWindow, &renderWidth, &renderHeight); + if (globalState.useCorrectAspectRatio) + { + f32 ratioX = (f32)globalRenderBitmap.width; + f32 ratioY = (f32)globalRenderBitmap.height; + v2 newDim = constrain_to_aspect_ratio(renderWidth, renderHeight, + ratioX, ratioY); + + renderWidth = (LONG)newDim.w; + renderHeight = (LONG)newDim.h; + } HDC deviceContext = GetDC(mainWindow); win32_display_render_bitmap(globalRenderBitmap, deviceContext, - clientWidth, clientHeight); + renderWidth, renderHeight); ReleaseDC(mainWindow, deviceContext); }