You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在.lib库中添加TSR?MS-DOS平台C语言项目技术咨询

TSRs in .lib Libraries & MS-DOS Multitasking with C

Great questions—let’s break this down step by step since TSRs and MS-DOS multitasking are such fun (and tricky!) retro computing topics.

Can You Add a TSR to a .lib Static Library?

Short answer: Yes, technically you can—but with important caveats. A .lib file is just a collection of compiled object (.obj) files, so you absolutely can package your TSR code into one. Here’s how and what to watch out for:

  • Step 1: Isolate your TSR logic
    Split your TSR code into two parts: the initialization/installation code (which sets up the TSR, hooks interrupts, and triggers memory residency) and the resident service code (the part that stays in memory and handles interrupts). Only the resident part needs to live in the .lib—though you can include the init code too if you want to reuse it across projects.

  • Step 2: Compile to .obj, then add to .lib
    Use your C compiler (like Turbo C or Watcom C) to compile the TSR code to an object file. Then use the library manager tool (e.g., lib in Turbo C) to add the .obj to your .lib:

    lib my_custom_lib.lib + tsr_resident_code.obj
    
  • Critical Caveats

    • TSRs rely on being loaded into memory and staying there—when you link the .lib into an executable, the TSR code won’t automatically驻留 unless you explicitly call the initialization function that triggers _dos_keep() (or equivalent) at runtime.
    • Don’t accidentally link the TSR init code into programs that don’t need to install the TSR—this could cause unexpected memory bloat or unwanted residency.

MS-DOS Multitasking with TSRs in C

MS-DOS is a single-tasking OS, but you can implement pseudomultitasking using TSRs that hook system interrupts (most commonly the 18.2Hz clock interrupt, INT 08h). Here’s a practical, actionable breakdown:

Core Concepts

Your TSR will:

  1. Save the original interrupt vector for INT 08h (or another interrupt you want to use).
  2. Replace the vector with your custom interrupt handler.
  3. In the handler, switch between different task contexts (registers, stack pointers, etc.) on each clock tick.
  4. Use _dos_keep() (Turbo C) or keep() (other compilers) to keep the TSR resident in memory after the initial program exits.

Example Framework (Turbo C)

1. TSR Installation & Residency Code

#include <dos.h>
#include <stdlib.h>

/* Function prototypes */
void interrupt (*old_int08)(void);
void interrupt new_int08(void);
void task1(void);
void task2(void);

/* Task context storage */
typedef struct {
    unsigned int ax, bx, cx, dx, si, di, bp, sp;
    unsigned int cs, ip, flags;
} TaskContext;

TaskContext task1_ctx, task2_ctx;
int current_task = 0;
char msg1[] = "Task 1 running\r\n$";
char msg2[] = "Task 2 running\r\n$";

void main() {
    /* Save original INT 08h vector */
    old_int08 = getvect(0x08);
    setvect(0x08, new_int08);

    /* Initialize task contexts (simplified example) */
    task1_ctx.cs = FP_SEG(task1);
    task1_ctx.ip = FP_OFF(task1);
    task2_ctx.cs = FP_SEG(task2);
    task2_ctx.ip = FP_OFF(task2);

    /* Keep TSR resident: 0 = exit code, size = memory to keep (in paragraphs) */
    _dos_keep(0, (_DS + (_SS + (_SP/16) - _DS)) / 16 + 1);
}

2. Interrupt Handler & Task Switching

void interrupt new_int08(void) {
    /* Call original INT 08h first to maintain system clock functionality */
    (*old_int08)();

    /* Save current task's context */
    if (current_task == 0) {
        asm {
            mov task1_ctx.ax, ax
            mov task1_ctx.bx, bx
            mov task1_ctx.cx, cx
            mov task1_ctx.dx, dx
            mov task1_ctx.si, si
            mov task1_ctx.di, di
            mov task1_ctx.bp, bp
            mov task1_ctx.sp, sp
            pushf
            pop task1_ctx.flags
        }
    } else {
        asm {
            mov task2_ctx.ax, ax
            mov task2_ctx.bx, bx
            mov task2_ctx.cx, cx
            mov task2_ctx.dx, dx
            mov task2_ctx.si, si
            mov task2_ctx.di, di
            mov task2_ctx.bp, bp
            mov task2_ctx.sp, sp
            pushf
            pop task2_ctx.flags
        }
    }

    /* Switch to the other task */
    current_task = !current_task;
    if (current_task == 0) {
        asm {
            mov ax, task1_ctx.ax
            mov bx, task1_ctx.bx
            mov cx, task1_ctx.cx
            mov dx, task1_ctx.dx
            mov si, task1_ctx.si
            mov di, task1_ctx.di
            mov bp, task1_ctx.bp
            mov sp, task1_ctx.sp
            push task1_ctx.flags
            popf
            jmp far [task1_ctx.cs:task1_ctx.ip]
        }
    } else {
        asm {
            mov ax, task2_ctx.ax
            mov bx, task2_ctx.bx
            mov cx, task2_ctx.cx
            mov dx, task2_ctx.dx
            mov si, task2_ctx.si
            mov di, task2_ctx.di
            mov bp, task2_ctx.bp
            mov sp, task2_ctx.sp
            push task2_ctx.flags
            popf
            jmp far [task2_ctx.cs:task2_ctx.ip]
        }
    }
}

3. Sample Tasks

void task1(void) {
    while(1) {
        // Write to screen (simple task example)
        asm { mov ah, 0x09; mov dx, offset msg1; int 0x21 }
        // Yield control—interrupt handler will switch tasks on next tick
    }
}

void task2(void) {
    while(1) {
        asm { mov ah, 0x09; mov dx, offset msg2; int 0x21 }
    }
}

Key Tips for Success

  • Keep interrupt handlers short: DOS is not reentrant, so avoid calling DOS interrupts (INT 21h) inside your handler unless you’re using a reentrant DOS extender.
  • Calculate residency size correctly: If you get this wrong, your TSR might overwrite itself or other memory. Use the MEM command in DOS to verify residency after installation.
  • Test with simple tasks first: Get basic task switching working before adding complex logic—debugging TSRs in MS-DOS is tricky, so start small!
  • Use a compatible compiler: Turbo C 2.0/3.0 is a great choice for MS-DOS TSR development, as it has built-in functions like _dos_keep() and native interrupt support.

内容的提问来源于stack exchange,提问作者Jaymin Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:27:01