在BIOS级组装中读取输入?

时间:2017-04-27 21:35:40

标签: c assembly x86 printf bios

我正在创建一个非常低级的应用程序,我已准备好引导加载程序。我基本上必须重建scanf()函数,因为我不能在项目中使用任何库。在Assembly中可以读取输入的一个好的基本应用程序是什么?我可以处理它读入C char []或我想要的任何其他数据类型,我只需要弄清楚如何在没有任何系统调用的情况下读取输入。

1 个答案:

答案 0 :(得分:1)

看来你正在为真实模式编写代码。您没有说明您正在使用哪个 C 编译器,但您自己的答案表明您实际上处于实模式。我推荐WATCOM's C compiler,因为它可以生成真正的16位代码。如果您使用带有-m16选项的 GCC ,我不建议将其用于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向终端显示单个字符:

  

视频 - TELETYPE OUTPUT

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;
}

如果您不希望使用内联汇编,则可以创建一个装配模块,其中getchprintch在纯装配中完成。这比使用内联汇编生成的代码效率低,但它更不容易出错。

getstr_echo函数功能不完整,可用作您自己代码的起点。它无法正确处理 backspace 等内容。