add doom, disable warnings as errors, massive speedups in the fat32 driver, HAL_KEYBOARD Poll now returns the key and added IsKeyDown used by GetAsyncKeyState, slight upgrade in hal_main drain and pray, more accurate QueryTicks and by that GetTickCount, add GetTickCount64, boot.ini now dictates what executable should init launch, not the kernel, LaunchUserProcess in kmain.c now takes an argument, rearrange syscall numbers, add SysKbIsKeyDown syscall, use include windows.h over manual definitions, add fictional win32 api ClearConsoleScreenBuffer and real FlushConsoleInputBuffer and a clear command to the shell, print exit codes as signed int, StretchDIBits now can stretch, runa.bat now gives 256 MB.

This commit is contained in:
maksw committed 2026-05-11 10:10:21 +02:00
1 parent cdf7ed4cea
commit 0c7f461c6d
24 files changed
+50236 -1310

No files matched your search

+48605
View File
File diff suppressed because it is too large. Load diff
+329
View File
@@ -0,0 +1,329 @@
// doom.c — NT-Lite port of PureDOOM
// Win32 personality carrying all the weight, just like the real ntoskrnl does.
// Drop doom1.wad (or doom.wad) next to this binary on your FAT32 volume.
#include <windows.h>
#define DOOM_IMPLEMENTATION
#include "PureDOOM.h"
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
int _fltused;
// ---------------------------------------------------------------------------
// Globals
// ---------------------------------------------------------------------------
static HANDLE g_stdout;
// ---------------------------------------------------------------------------
// doom_print_fn
// ---------------------------------------------------------------------------
static void doom_print_wrapper(const char* msg)
{
UINT len = (UINT)strlen(msg);
WriteConsoleA(g_stdout, msg, len, 0, 0);
}
// ---------------------------------------------------------------------------
// doom_malloc_fn / doom_free_fn
// Heap handle 0, same convention as video.c.
// ---------------------------------------------------------------------------
static void* doom_malloc_wrapper(int size)
{
return HeapAlloc(0, HEAP_ZERO_MEMORY, size);
}
static void doom_free_wrapper(void* ptr)
{
HeapFree(0, 0, ptr);
}
// ---------------------------------------------------------------------------
// File I/O wrappers
// ---------------------------------------------------------------------------
static void* doom_open_wrapper(const char* path, const char* mode)
{
DWORD access = GENERIC_READ;
DWORD disp = OPEN_EXISTING;
if (mode[0] == 'w') {
access = GENERIC_WRITE;
disp = CREATE_ALWAYS;
} else if (mode[0] == 'r' && mode[1] == '+') {
access = GENERIC_READ | GENERIC_WRITE;
}
HANDLE h = CreateFileA(path, access, FILE_SHARE_READ, 0, disp, 0, 0);
return (h == INVALID_HANDLE_VALUE) ? 0 : h;
}
static void doom_close_wrapper(void* handle)
{
CloseHandle((HANDLE)handle);
}
static int doom_read_wrapper(void* handle, void* buf, int count)
{
DWORD read = 0;
ReadFile((HANDLE)handle, buf, (DWORD)count, &read, 0);
return (int)read;
}
static int doom_write_wrapper(void* handle, const void* buf, int count)
{
DWORD written = 0;
WriteFile((HANDLE)handle, buf, (DWORD)count, &written, 0);
return (int)written;
}
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
{
DWORD method;
switch (origin) {
case DOOM_SEEK_SET: method = FILE_BEGIN; break;
case DOOM_SEEK_CUR: method = FILE_CURRENT; break;
case DOOM_SEEK_END: method = FILE_END; break;
default: method = FILE_BEGIN; break;
}
SetFilePointer((HANDLE)handle, (LONG)offset, 0, method);
return 0;
}
static int doom_tell_wrapper(void* handle)
{
return (int)SetFilePointer((HANDLE)handle, 0, 0, FILE_CURRENT);
}
static int doom_eof_wrapper(void* handle)
{
DWORD size = GetFileSize((HANDLE)handle, 0);
DWORD pos = (DWORD)doom_tell_wrapper(handle);
return (pos >= size) ? 1 : 0;
}
// ---------------------------------------------------------------------------
// doom_gettime_fn
// ---------------------------------------------------------------------------
static void doom_gettime_wrapper(int* sec, int* usec)
{
DWORD t = GetTickCount(); // milliseconds
*sec = (int)(t / 1000);
*usec = (int)((t % 1000) * 1000); // ms remainder → us
}
// ---------------------------------------------------------------------------
// doom_exit_fn
// ---------------------------------------------------------------------------
static void doom_exit_wrapper(int code)
{
ExitProcess((UINT)code);
}
// ---------------------------------------------------------------------------
// doom_getenv_fn — we don't have environment variables
// ---------------------------------------------------------------------------
static char* doom_getenv_wrapper(const char* name)
{
if (strcmp(name, "HOME") == 0) {
static char home[] = "fs0:\\";
doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n");
return home;
}
doom_print_wrapper("getenv: Unknown variable "); doom_print_wrapper(name); doom_print_wrapper("\n");
return NULL;
}
// ---------------------------------------------------------------------------
// Input — poll GetAsyncKeyState every frame
// Generates key_down / key_up edge events, not held-key spam.
// ---------------------------------------------------------------------------
typedef struct { int vk; doom_key_t key; } KeyMap;
// NOTE: Win32 VK codes for letters are uppercase ASCII (e.g. 'W' = 0x57).
// DOOM_KEY_* for letters are lowercase ASCII (e.g. DOOM_KEY_W = 'w' = 0x77).
// These are different values — the VK column is what GetAsyncKeyState takes,
// the doom_key column is what PureDOOM wants. Don't collapse them.
static const KeyMap g_keymap[] = {
// Letters — VK is uppercase, DOOM key is lowercase
{ 'W', DOOM_KEY_W },
{ 'A', DOOM_KEY_A },
{ 'S', DOOM_KEY_S },
{ 'D', DOOM_KEY_D },
{ 'E', DOOM_KEY_E },
{ 'Q', DOOM_KEY_Q },
{ 'Y', DOOM_KEY_Y }, // yes/no menus
{ 'N', DOOM_KEY_N },
// Arrow keys
{ VK_UP, DOOM_KEY_UP_ARROW },
{ VK_DOWN, DOOM_KEY_DOWN_ARROW },
{ VK_LEFT, DOOM_KEY_LEFT_ARROW },
{ VK_RIGHT, DOOM_KEY_RIGHT_ARROW },
// Action keys — Ctrl=fire, Shift=run, Alt=strafe
{ VK_CONTROL, DOOM_KEY_CTRL },
{ VK_SPACE, DOOM_KEY_SPACE },
{ VK_SHIFT, DOOM_KEY_SHIFT },
{ VK_MENU, DOOM_KEY_ALT },
// Navigation
{ VK_ESCAPE, DOOM_KEY_ESCAPE },
{ VK_RETURN, DOOM_KEY_ENTER },
{ VK_TAB, DOOM_KEY_TAB },
{ VK_BACK, DOOM_KEY_BACKSPACE },
{ VK_PAUSE, DOOM_KEY_PAUSE },
// Weapon slots
{ '1', DOOM_KEY_1 },
{ '2', DOOM_KEY_2 },
{ '3', DOOM_KEY_3 },
{ '4', DOOM_KEY_4 },
{ '5', DOOM_KEY_5 },
{ '6', DOOM_KEY_6 },
{ '7', DOOM_KEY_7 },
// Misc
{ '-', DOOM_KEY_MINUS },
{ '=', DOOM_KEY_EQUALS },
{ ',', DOOM_KEY_COMMA }, // default strafe left
{ '.', DOOM_KEY_PERIOD }, // default strafe right
// Function keys — save/load/volume/etc
{ VK_F1, DOOM_KEY_F1 },
{ VK_F2, DOOM_KEY_F2 },
{ VK_F3, DOOM_KEY_F3 },
{ VK_F4, DOOM_KEY_F4 },
{ VK_F5, DOOM_KEY_F5 },
{ VK_F6, DOOM_KEY_F6 },
{ VK_F7, DOOM_KEY_F7 },
{ VK_F8, DOOM_KEY_F8 },
{ VK_F9, DOOM_KEY_F9 },
{ VK_F10, DOOM_KEY_F10 },
{ VK_F11, DOOM_KEY_F11 },
{ VK_F12, DOOM_KEY_F12 },
};
#define KEYMAP_COUNT (sizeof(g_keymap) / sizeof(g_keymap[0]))
static short g_prev_key[256];
static void poll_input(void)
{
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
int vk = g_keymap[i].vk;
short cur = GetAsyncKeyState(vk);
int cur_down = (cur & 0x8000) != 0;
int prev_down = (g_prev_key[vk] & 0x8000) != 0;
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
if (!cur_down && prev_down) doom_key_up (g_keymap[i].key);
g_prev_key[vk] = cur;
}
}
// ---------------------------------------------------------------------------
// Render — StretchDIBits to screen, same pipeline as video.c.
//
// NOTE: PureDOOM's framebuffer is RGBA; Win32 StretchDIBits with BI_RGB/32bpp
// expects BGRA. Channels will be swapped (red ↔ blue) until you either:
// a) swap in the blit loop below, or
// b) tell PureDOOM to give you BGRA if a future version supports it.
// For a first boot, blue-tinted demons are still demons.
// ---------------------------------------------------------------------------
#define DOOM_W 320
#define DOOM_H 200
#define SCALE 3
#define DISP_W (DOOM_W * SCALE)
#define DISP_H (DOOM_H * SCALE)
static BITMAPINFO g_bmi;
static uint32_t g_blit_buf[DOOM_W * DOOM_H]; // BGRA-swapped copy
static void render_frame(const uint8_t* rgba)
{
// Swap R <-> B so Win32 GDI sees correct colours
const uint32_t* src = (const uint32_t*)rgba;
for (int i = 0; i < DOOM_W * DOOM_H; i++) {
uint32_t p = src[i];
// RGBA -> BGRA: swap byte 0 (R) and byte 2 (B)
g_blit_buf[i] = (p & 0xFF00FF00u)
| ((p & 0x00FF0000u) >> 16)
| ((p & 0x000000FFu) << 16);
}
PAINTSTRUCT ps;
HDC hdc = BeginPaint((HWND)0, &ps);
StretchDIBits(
hdc,
0, 0, DISP_W, DISP_H, // destination — scaled up
0, 0, DOOM_W, DOOM_H, // source — native 320×200
g_blit_buf,
&g_bmi,
DIB_RGB_COLORS,
SRCCOPY
);
EndPaint((HWND)0, &ps);
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main(const char* cmdline)
{
(void)cmdline;
g_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(g_stdout, "doom: starting\n", 15, 0, 0);
// Prepare the BMI header once (top-down, like video.c)
memset(&g_bmi, 0, sizeof(g_bmi));
g_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
g_bmi.bmiHeader.biWidth = DOOM_W;
g_bmi.bmiHeader.biHeight = -DOOM_H; // negative = top-down
g_bmi.bmiHeader.biPlanes = 1;
g_bmi.bmiHeader.biBitCount = 32;
g_bmi.bmiHeader.biCompression = BI_RGB;
// Wire up PureDOOM
doom_set_print(doom_print_wrapper);
doom_set_malloc(doom_malloc_wrapper, doom_free_wrapper);
doom_set_file_io(
doom_open_wrapper,
doom_close_wrapper,
doom_read_wrapper,
doom_write_wrapper,
doom_seek_wrapper,
doom_tell_wrapper,
doom_eof_wrapper
);
doom_set_gettime(doom_gettime_wrapper);
doom_set_exit(doom_exit_wrapper);
doom_set_getenv(doom_getenv_wrapper);
// Resolution + modern WASD bindings
doom_set_resolution(DOOM_W, DOOM_H);
doom_set_default_int("key_up", DOOM_KEY_W);
doom_set_default_int("key_down", DOOM_KEY_S);
doom_set_default_int("key_strafeleft", DOOM_KEY_A);
doom_set_default_int("key_straferight", DOOM_KEY_D);
doom_set_default_int("key_use", DOOM_KEY_E);
doom_set_default_int("mouse_move", 0);
// No extra args — PureDOOM will scan for doom1.wad / doom.wad
const char* args[] = { "doom" };
doom_init(1, (char**)args, 0);
WriteConsoleA(g_stdout, "doom: entering game loop\n", 25, 0, 0);
// Game loop — identical shape to video.c's Bink loop
while (1) {
poll_input();
doom_update();
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
}
// Unreachable: doom_exit() -> ExitProcess() handles clean shutdown
ExitProcess(0);
}
Binary file not shown.