自定义内存分配器用于实模式DOS .COM(独立式) – 如何调试?

首先是一个小背景:在这篇博文上磕磕绊绊,我了解到有可能用GNU链接器创建DOS .COM文件,它甚至不是火箭科学。 使用clang-m16开关(通过相应地为32位指令加前缀来创建实模式兼容的32位代码),这非常好。 所以我有想法尝试实现足够的运行时来获得我最近写的一个小诅咒游戏 ,编译成.COM并在实模式 DOS 运行。 游戏足够小,所以压缩64KB的所有内容(文本,数据,bss,堆,堆栈)似乎是可行的。 当然,它使用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  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 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 free) break; hdr->next = next; next = next->next; } if ((char *)next == hbreak) hbreak = (char *)hdr; } 

_heap符号由链接器定义。 这里没有显示realloc() ,因为它现在还没有被使用(因此完全未经测试)。

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

当我第一次经历一个奇怪的heisenbug (我现在就在它周围工作)时,我想它可能是优化器在编译实模式时出错的错误,这确实不常见。 但我不能确定,下一个敏感的候选人可能是我的memory management,见上文。

现在这是一个棘手的问题:我如何调试这样的事情? 只有我自己的测试代码,它运行良好。 我没有优化就无法编译我的游戏,因为这样做会超过64KB。 有什么建议? 或者任何人都能发现上述代码明显错误的东西?

如果这是实模式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程序。