看来您正在为实模式编写代码。您没有说您使用的是哪个 C 编译器,但您自己的回答表明您实际上处于实模式。我推荐WATCOM's C compiler,因为它可以生成真正的 16 位代码。如果您将 GCC 与 -m16 选项一起使用,我不建议将其用于 16 位代码。我有另一个 Stackoverflow 答案,其中讨论了一些 GCC issues。
DOS 和 BIOS 中断信息的最佳来源是Ralph Brown's Interrupt List。通过Int 16h/AH=00获取单次击键信息为:
键盘 - 获取键盘
AH = 00h
返回:
AH = BIOS scan code
AL = ASCII character
此 BIOS 函数不会回显字符,因此另一个有用的 BIOS 函数是 Int 10h/AH=0eh 正在向终端显示单个字符:
视频 - 电传输出
AH = 0Eh
AL = character to write
BH = page number
BL = foreground color (graphics modes only)
返回:
Nothing
描述:在屏幕上显示一个字符,根据需要推进光标并滚动屏幕
要在文本模式下打印字符,您可以将 BX 置为 0,即在 AL 中打印的字符并调用中断。
使用上面的信息,您可以使用内联汇编为两个 BIOS 中断编写一些简单的包装器。在 GCC 中,您可以使用 Extended Inline Assembly templates。它们可能看起来像这样:
#include <stdint.h>
static inline void
printch (char outchar, uint16_t attr)
{
__asm__ ("int $0x10\n\t"
:
: "a"((0x0e << 8) | outchar),
"b"(attr));
}
static inline char
getch (void)
{
uint16_t inchar;
__asm__ __volatile__ ("int $0x16\n\t"
: "=a"(inchar)
: "0"(0x0));
return ((char)inchar);
}
在Watcom C 中,您可以使用#pragma aux 创建函数:
#include <stdint.h>
void printch(char c, uint16_t pageattr);
char getch(void);
#pragma aux printch = \
"mov ah, 0x0e" \
"int 0x10" \
parm [al] [bx] nomemory \
modify [ah] nomemory
#pragma aux getch = \
"xor ah, ah" \
"int 0x16" \
parm [] nomemory \
modify [ah] nomemory \
value [al]
使用这些基本函数,您只需要编写一个从用户那里获取字符串、在输入字符时回显字符并将它们存储在缓冲区中的函数。换行符返回的 ASCII 字符 getch 是 回车 \r (0x0d)。当我们达到请求的最大字符数或遇到换行符时,我们停止并 NUL 终止字符串。这样的函数可能如下所示:
/* Get string up to maxchars. NUL terminate string.
Ensure inbuf has enough space for NUL.
Carriage return is stripped from string.
Return a pointer to the buffer passed in.
*/
char *getstr_echo(char *inbuf, int maxchars)
{
char *bufptr = inbuf;
while(bufptr < (inbuf + maxchars) && (*bufptr = getch()) != '\r')
printch(*bufptr++, 0);
*bufptr = '\0';
return inbuf;
}
如果您不想使用内联汇编,您可以创建一个汇编模块,其中getch 和printch 在纯汇编中完成。这比使用内联汇编生成的代码效率低,但更不容易出错。
getstr_echo 函数功能不完整,旨在用作您自己的代码的起点。它不能正确处理 backspace 之类的东西。