135 lines
5.8 KiB
C
135 lines
5.8 KiB
C
#ifndef THUNK_H
|
|
#define THUNK_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#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);
|
|
|
|
// Set a watchpoint on 3 bytes at sel:off. Logs any changes during callbacks.
|
|
void thunkSetWatch(uint16_t sel, uint32_t off);
|
|
|
|
// 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
|