首先有一点背景:在this blog post上磕磕绊绊,我了解到有可能用GNU链接器创建DOS .COM
文件,它甚至不是火箭科学。使用clang
和-m16
开关(通过相应地为32位指令添加前缀来创建实模式兼容的32位代码),这非常好用。所以我有想法尝试实现足够的运行时来获得我最近编写的little curses game编译到.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 < 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()
,因为它现在无法使用(因此完全未经测试)。
现在的问题是:我在这里创建了我的runtime(malloc在src / libdos / stdlib.c中),写了很多测试内容,最后,一切似乎都运行得很好。另一方面,我的游戏经过全面测试,并使用valgrind
检查无效的内存访问。尽管如此,将两个部分放在一起,只是崩溃。 (尝试使用make -f libdos.mk
从git构建游戏,你需要安装llvm / clang。
当我第一次经历一个奇怪的 heisenbug 时(我现在就在它身边工作),我想它可能是优化者在编译真实模式时出错的错误,这确实不常见。但我无法确定,下一个敏感的候选人可能是我的记忆管理,见上文。
现在这是一个棘手的问题:我如何调试这样的事情?只有我自己的测试代码,它的效果非常好。我无法在没有优化的情况下编译我的游戏,因为这样做会超过64KB。有什么建议?或者任何人都可以发现上述代码明显错误的东西?
答案 0 :(得分: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程序。