1

先介绍一点背景知识:偶然发现这篇博文,我了解到可以.COM使用 GNU 链接器创建 DOS 文件,这甚至不是火箭科学。使用clangand-m16开关(通过相应地为 32 位指令添加前缀来创建与实模式兼容的 32 位代码),效果很好。所以我有想法尝试实现足够的运行时来获得我最近编写的一个小诅咒游戏.COM,以编译为 a并在实模式DOS 中运行。游戏足够小,因此将所有内容(文本、数据、bss、堆、堆栈)压缩为 64KB 似乎是可行的。当然,它使用malloc(). 所以我必须想出我自己的实现。这是它的样子:

typedef unsigned short size_t; /* from stddef.h */

typedef struct hhdr hhdr;
struct hhdr
{
    void *next;
    int free;
};

extern char _heap;
static char *hbreak = &_heap;
static hhdr hhead = { &_heap, 0 };

static void *newchunk(size_t size)
{
    char *stack;
    __asm__("mov %%esp, %0": "=rm" (stack));
    if (hbreak + size > stack - 0x40) return 0;
    if (size < 1024) size = 1024;
    hhdr *chunk = (hhdr *)hbreak;
    hbreak += size;
    if (hbreak > stack - 0x40) hbreak = stack - 0x40;
    chunk->next = hbreak;
    chunk->free = 1;
    return chunk;
}

void *malloc(size_t size)
{
    if (!size) return 0;
    if (size % sizeof(hhdr)) size += sizeof(hhdr) - (size % sizeof(hhdr));

    hhdr *hdr = &hhead;
    while ((char *)hdr->next < hbreak)
    {
        hdr = hdr->next;
        if (hdr->free && 
                (char *)hdr->next - (char *)hdr - sizeof(hhdr) >= size)
        {
            if ((char *)hdr->next - (char *)hdr - 2*sizeof(hhdr) > size)
            {
                hhdr *hdr2 = (hhdr *)((char *)hdr + sizeof(hhdr) + size);
                hdr2->free = 1;
                hdr2->next = hdr->next;
                hdr->next = hdr2;
            }
            hdr->free = 0;
            return (char *)hdr + sizeof(hhdr);
        }
    }

    if (!(hdr->next = newchunk(size + sizeof(hhdr)))) return 0;
    return malloc(size);
}

void free(void *ptr)
{
    if (!ptr) return;
    hhdr *hdr = (hhdr *)((char *)ptr - sizeof(hhdr));
    hdr->free = 1;
    if ((void *)hdr != hhead.next)
    {
        hhdr *hdr2 = hhead.next;
        while (hdr2->next != hdr) hdr2 = hdr2->next;
        if (hdr2->free) hdr = hdr2;
    }
    hhdr *next = hdr->next;
    while ((char *)next < hbreak)
    {
        if (!next->free) break;
        hdr->next = next;
        next = next->next;
    }
    if ((char *)next == hbreak) hbreak = (char *)hdr;
}

_heap符号由链接器定义。未realloc()在此处显示,因为它现在无论如何都没有使用(因此完全未经测试)。

现在的问题是:我在这里创建了我的运行时(malloc 在 src/libdos/stdlib.c 中),写了很多测试内容,最后,一切似乎都运行良好。另一方面,我的游戏使用valgrind. 尽管如此,将这两个部分放在一起,它还是会崩溃。(尝试使用 git 从 git 构建游戏make -f libdos.mk,您需要安装 llvm/clang)。

由于我首先遇到了一个奇怪的heisenbug (我现在正在解决它),我想这可能是优化器在为实模式编译时出错了,这确实不常见。但我不能确定,下一个敏感的候选人可能是我的内存管理,见上文。

现在棘手的问题是:我将如何调试这样的东西?仅使用我自己的测试代码,它就可以很好地工作。我无法在没有优化的情况下编译我的游戏,因为这样做会超过 64KB。有什么建议么?或者任何人都可以发现上面的代码明显有问题吗?

4

1 回答 1

0

如果这是实模式 DOS,我不确定 esp 的高位。至于malloc(),使用之间ss:sp and 0xa000:0000的内存,栈顶和640k边界之间的内存。我不记得 MS-DOS 是否为 .COM 程序分配了所有 640k 区域。有两个 DOS 调用,INT 21H, ah = 04Ah releases memory, ah = 048H allocates memory但我不记得它们是针对 .COM 还是 .EXE 程序的。

于 2015-08-06T01:22:30.953 回答