【问题标题】:Reading Input in BIOS-Level Assembly? [closed]在 BIOS 级程序集中读取输入? [关闭]
【发布时间】:2017-09-25 20:53:29
【问题描述】:

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

【问题讨论】:

  • 输入来自什么?
  • 键盘输入;我将编辑我的帖子并澄清。
  • @BlueStrat:现代 DOS 和 BIOS 中断百科全书是 Ralph Brown's Interrupt List
  • @MichaelPetch,感谢您的更新,我猜这表明我正式成为成年灰胡子了;)
  • 我有点老(快 50 岁了) 过去,Waite Group 的书、Norton 的指南和 PC Intern 都是好书。仍然将它们与 Dobbs 博士杂志和字节杂志一起放在我的书架上。

标签: c assembly x86 printf bios


【解决方案1】:

看来您正在为实模式编写代码。您没有说您使用的是哪个 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;
}

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


getstr_echo 函数功能不完整,旨在用作您自己的代码的起点。它不能正确处理 backspace 之类的东西。

【讨论】:

  • 您的 GCC 示例的唯一问题是我无法使用任何库。这就是我从头开始创建 scanf 和 printf 的原因。除此之外,这些都是很好的例子,我会从中学习。谢谢。
  • @LoganJamison :如果您指的是stdint.h,它与标准库头文件是分开的(并且可以在像您的操作系统这样的独立代码中使用)。 stdint 定义了 uintptr_t 和 uint16_t 等内容。如果您不想使用 stdint 并且正在生成 32 位内核,则可以在代码中将 uint16_t 替换为 unsigned short int。两者都是 16 位无符号值。
  • 好的,非常感谢。我几乎不熟悉大多数 C 库,并且预计我不会在这个项目中使用任何库。
猜你喜欢
  • 2018-04-17
  • 2013-09-28
  • 2012-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
相关资源
最近更新 更多