#ifndef THUNK_H #define THUNK_H #include #include #include "wintypes.h" // ============================================================================ // 32-to-16 bit thunking layer // // Provides a mechanism for 32-bit DJGPP code to call into 16-bit Windows // driver code running in 16-bit protected mode segments. // // Architecture: // - A small 16-bit relay thunk is placed in a 16-bit code segment // - Parameters are pre-built on a dedicated 16-bit stack // - The relay handles 32/16-bit return address translation // - Driver functions use Pascal calling convention (callee cleans stack) // // The relay thunk code: // 1. Pops the 32-bit return address (8 bytes: 32-bit CS + 32-bit EIP) // 2. Pushes parameters from the shared data area onto the 16-bit stack // 3. Does a 16-bit far call to the target driver function // 4. Saves the return value (DX:AX) // 5. Pushes the 32-bit return address back // 6. Does a 32-bit far return (o32 retf) // ============================================================================ // ============================================================================ // Thunk shared data area (in 16-bit addressable memory) // // The 32-bit side writes target address and parameters here before // calling the relay. The relay reads from here. // ============================================================================ #define THUNK_MAX_PARAMS 32 // Max 16-bit words of parameters per call typedef struct __attribute__((packed)) { uint16_t targetOff; // 0x00: target function offset uint16_t targetSeg; // 0x02: target function segment (selector) uint16_t paramCount; // 0x04: number of 16-bit parameter words uint16_t params[THUNK_MAX_PARAMS]; // 0x06: parameter words } ThunkDataT; // ============================================================================ // Thunk context (initialized once, used for all calls) // ============================================================================ typedef struct { // 16-bit relay code segment uint16_t relayCodeSel; // Selector for relay code segment uint32_t relayCodeBase; // Linear base address of relay code uint16_t relayCodeSize; // Size of relay code // 16-bit data segment (for ThunkDataT) uint16_t dataSegSel; // Selector for shared data segment uint32_t dataSegBase; // Linear base address of data segment uint16_t dataSegSize; // Size of data segment // 16-bit stack segment uint16_t stackSel; // Selector for 16-bit stack uint32_t stackBase; // Linear base address of stack uint16_t stackSize; // Size of stack // DOS memory for all 16-bit segments (single allocation) int dosMemSeg; // Real-mode segment int dosMemSel; // PM selector from DOS alloc uint32_t dosMemSize; // Total bytes allocated // Driver's auto data segment (DGROUP) selector. // Set this before calling thunkCall16 so the relay loads DS correctly. uint16_t dgroupSel; bool initialized; } ThunkContextT; // ============================================================================ // Thunk layer functions // ============================================================================ // Initialize the thunking infrastructure. // Allocates DOS memory, creates 16-bit segments, installs relay code. bool thunkInit(ThunkContextT *ctx); // Enable or disable verbose callback tracing. void thunkSetDebug(bool debug); // Shut down the thunking infrastructure and free resources. void thunkShutdown(ThunkContextT *ctx); // Call a 16-bit function via the thunk. // targetSel:targetOff - far address of the 16-bit function // params - array of 16-bit parameter words in Pascal order: // params[0] = leftmost parameter (pushed first, deepest) // params[N-1] = rightmost parameter (pushed last, top) // paramCount - number of 16-bit words in params // // Returns DX:AX combined as a uint32_t (AX in low 16, DX in high 16). // For functions returning WORD, just use the low 16 bits. uint32_t thunkCall16(ThunkContextT *ctx, uint16_t targetSel, uint16_t targetOff, const uint16_t *params, uint16_t paramCount); // Convenience: call with individual parameters (up to 12 words). uint32_t thunkCall16v(ThunkContextT *ctx, uint16_t targetSel, uint16_t targetOff, uint16_t paramCount, ...); // ============================================================================ // 16-bit stub generation // // For Windows API stubs that the driver calls back into, we need 16-bit // entry points that thunk UP to 32-bit code. // ============================================================================ // Callback function type for 16-to-32 callbacks. // Receives the parameters as an array of 16-bit words. // Returns DX:AX as uint32_t. typedef uint32_t (*ThunkCallbackT)(uint16_t *params, uint16_t paramCount); // Maximum number of registered callbacks #define THUNK_MAX_CALLBACKS 128 // Register a callback and get a 16-bit far pointer (sel:off) that, // when called from 16-bit code, will invoke the callback in 32-bit. // paramWords = number of 16-bit parameter words the function expects // (used by the stub to clean the stack with retf N). // Returns true on success. bool thunkRegisterCallback(ThunkContextT *ctx, ThunkCallbackT callback, uint16_t paramWords, FarPtr16T *result); // Sanitize the callback frame after freeing a selector. // If the saved ES or DS in the callback interrupt frame matches freedSel, // zero it out so the IRET doesn't try to load a freed descriptor. void thunkSanitizeCbFrame(uint16_t freedSel); #endif // THUNK_H