在 BIOS 级程序集中读取输入?
Reading Input in BIOS-Level Assembly?
我正在创建一个非常低级的应用程序,并且我已经准备好引导加载程序。我基本上必须重建 scanf() 函数,因为我不能在项目中使用任何库。可以读取输入的 Assembly 基本应用程序是什么?我可以处理将它读入 C char[] 或我想要的任何其他数据类型,我只需要弄清楚如何在没有任何系统调用的情况下读取输入。
看来您正在为实模式编写代码。你没有说你正在使用哪个 C 编译器,但你自己的回答表明你实际上处于实模式。我推荐 WATCOM's C compiler as it can generate true 16-bit code. If you are using GCC with the -m16
option I don't recommend it for 16-bit code. I have another Whosebug answer that discusses some .
DOS和BIOS中断信息的最佳来源Ralph Brown's Interrupt List. The information about getting a single keystroke via Int 16h/AH=00是:
KEYBOARD - GET KEYSTROKE
AH = 00h
Return:
AH = BIOS scan code
AL = ASCII character
此 BIOS 功能不会回显字符,因此另一个有用的 BIOS 功能是 Int 10h/AH=0eh 正在向终端显示单个字符:
VIDEO - TELETYPE OUTPUT
AH = 0Eh
AL = character to write
BH = page number
BL = foreground color (graphics modes only)
Return:
Nothing
Desc: Display a character on the screen, advancing the cursor and scrolling the screen as necessary
要在文本模式下打印一个字符,您可以将 BX 放入 0,即要在 AL 中打印的字符并调用中断。
使用上面的信息,您可以使用内联汇编围绕两个 BIOS 中断编写一些简单的包装器。在 GCC 中,您可以使用 Extended Inline Assembly templates。它们可能看起来像这样:
#include <stdint.h>
static inline void
printch (char outchar, uint16_t attr)
{
__asm__ ("int [=14=]x10\n\t"
:
: "a"((0x0e << 8) | outchar),
"b"(attr));
}
static inline char
getch (void)
{
uint16_t inchar;
__asm__ __volatile__ ("int [=14=]x16\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
returns 是 回车符 return \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 = '[=16=]';
return inbuf;
}
如果您不想使用内联汇编,您可以创建一个汇编模块,其中 getch
和 printch
在纯汇编中完成。这比使用内联汇编生成的代码效率低,但更不容易出错。
getstr_echo
函数功能不完整,可用作您自己代码的起点。它不能正确处理 backspace.
之类的事情
我正在创建一个非常低级的应用程序,并且我已经准备好引导加载程序。我基本上必须重建 scanf() 函数,因为我不能在项目中使用任何库。可以读取输入的 Assembly 基本应用程序是什么?我可以处理将它读入 C char[] 或我想要的任何其他数据类型,我只需要弄清楚如何在没有任何系统调用的情况下读取输入。
看来您正在为实模式编写代码。你没有说你正在使用哪个 C 编译器,但你自己的回答表明你实际上处于实模式。我推荐 WATCOM's C compiler as it can generate true 16-bit code. If you are using GCC with the -m16
option I don't recommend it for 16-bit code. I have another Whosebug answer that discusses some
DOS和BIOS中断信息的最佳来源Ralph Brown's Interrupt List. The information about getting a single keystroke via Int 16h/AH=00是:
KEYBOARD - GET KEYSTROKE
AH = 00h
Return:
AH = BIOS scan code AL = ASCII character
此 BIOS 功能不会回显字符,因此另一个有用的 BIOS 功能是 Int 10h/AH=0eh 正在向终端显示单个字符:
VIDEO - TELETYPE OUTPUT
AH = 0Eh AL = character to write BH = page number BL = foreground color (graphics modes only)
Return:
Nothing
Desc: Display a character on the screen, advancing the cursor and scrolling the screen as necessary
要在文本模式下打印一个字符,您可以将 BX 放入 0,即要在 AL 中打印的字符并调用中断。
使用上面的信息,您可以使用内联汇编围绕两个 BIOS 中断编写一些简单的包装器。在 GCC 中,您可以使用 Extended Inline Assembly templates。它们可能看起来像这样:
#include <stdint.h>
static inline void
printch (char outchar, uint16_t attr)
{
__asm__ ("int [=14=]x10\n\t"
:
: "a"((0x0e << 8) | outchar),
"b"(attr));
}
static inline char
getch (void)
{
uint16_t inchar;
__asm__ __volatile__ ("int [=14=]x16\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
returns 是 回车符 return \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 = '[=16=]';
return inbuf;
}
如果您不想使用内联汇编,您可以创建一个汇编模块,其中 getch
和 printch
在纯汇编中完成。这比使用内联汇编生成的代码效率低,但更不容易出错。
getstr_echo
函数功能不完整,可用作您自己代码的起点。它不能正确处理 backspace.