自定义内存分配器,用于实模式DOS .COM(独立式)-如何调试? [英] Custom memory allocator for real-mode DOS .COM (freestanding) -- how to debug?

查看:60
本文介绍了自定义内存分配器,用于实模式DOS .COM(独立式)-如何调试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先了解一些背景知识:绊脚石此博客文章,我知道这是可以使用GNU链接器创建DOS .COM文件,这甚至不是火箭科学.使用clang-m16开关(通过相应地为32位指令加上前缀来创建 real-mode 兼容的32位代码),效果很好.因此,我想到了尝试实现足够的运行时来获得我最近编写并编译为的小诅咒游戏的想法. .COM并以实模式 DOS运行.游戏足够小,可以压缩64KB的所有内容(文本,数据,bss,堆,堆栈)似乎是可行的.当然,它使用malloc().因此,我不得不提出自己的实现方案.看起来像这样:

A little background first: stumbling upon this blog post, I learned it was possible to create DOS .COM files with the GNU linker and it's not even rocket science. Using clang and the -m16 switch (creating real-mode compatible 32bit code by prefixing 32bit instructions accordingly), this worked out quite well. So I had the idea to try implementing just enough runtime to get a little curses game I wrote recently to compile to a .COM and run in real-mode DOS. The game is small enough so that squeezing everything (text, data, bss, heap, stack) in 64KB seemed doable. Of course, it uses malloc(). So I had to come up with my own implementation. This is what it looks like:

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(),因为它现在仍未使用(因此已完全未经测试).

The _heap symbol is defined by the linker. Not showing realloc() here as it isn't used right now anyways (and therefore completely untested).

现在的问题是:我在这里创建了运行时(malloc位于src/libdos/stdlib.c),编写了很多测试资料,最后,一切似乎都运行良好.另一方面,我的游戏已经过全面测试,并使用valgrind检查了无效的内存访问.尽管如此,将这两个部分放在一起,只是崩溃. (尝试使用make -f libdos.mk从git构建游戏,您将需要安装llvm/clang.)

The problem now is: I created my runtime here (malloc is in src/libdos/stdlib.c), wrote a lot of testing stuff and in the end, everything seemed to work quite well. My game on the other hand is thoroughly tested and checked for invalid memory accesses using valgrind. Still, putting both parts together, it just crashes. (Try building the game from git with make -f libdos.mk, you will need to have llvm/clang installed).

由于我首先经历了一个奇怪的 heisenbug (我现在已经解决了),所以我猜想这可能是优化程序在为实模式编译时出错而导致的错误,这确实不常见.但是我不确定,下一个敏感的候选人可能是我的内存管理,请参见上文.

As I experienced a strange heisenbug first (I worked around it for now), I guess it COULD be the optimizers fault getting things wrong when compiling for real mode, which is indeed uncommon. But I can't be sure, and the next sensitive candidate would be probably my memory management, see above.

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

Now the tricky question: How would I debug such a thing? With just my own test code, it works very well. I can't compile my game without optimizations because it will exceed 64KB when doing so. Any suggestions? Or can anyone spot something obviously wrong with the above code?

推荐答案

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

If this is real mode DOS, I'm not sure about the upper bits of esp. As for malloc(), use the memory between ss:sp and 0xa000:0000, the memory between the top of the stack and the 640k boundary . I don't recall if MS-DOS allocates all of the 640k region for a .COM program or not. There are two DOS calls, INT 21H, ah = 04Ah releases memory, ah = 048H allocates memory, but I don't recall if these are for .COM or .EXE programs.

这篇关于自定义内存分配器,用于实模式DOS .COM(独立式)-如何调试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆