问题描述
我使用的是 QTCreator 3.5.1 和虚拟操作系统,它是 Linux Mint 18.3 Cinnamon 32 位,GCC 编译器。
我必须编写一个使用中断的代码,使用汇编命令获取在键盘上按下的键。
本来应该在windows上运行的代码是这样的:
#include <iostream>
unsigned char x;
int main()
{
do {
__asm {
mov ah,00h
int 16h
mov x,ah
};
std::cout <<"The pressed key code is "<< x << std::endl;
} while (true);
}
尽管它不适用于在 Windows 7x32 上使用 Visual Studio 2003 编译的 Windows 98 和任何其他 Windows 系统,但会出现运行时错误或关闭系统(在 Windows 98 上)。
所以我切换到 Linux,下面来自 http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html(第 7 部分)的汇编示例代码运行良好:
#include <iostream>
using std::cout;
int main(void)
{
int foo = 10,bar = 15;
__asm__ __volatile__("addl %%ebx,%%eax"
:"=a"(foo)
:"a"(foo),"b"(bar)
);
printf("foo+bar=%d\n",foo);
return 0;
}
(为了使其正常工作,我还必须在 QT Creator Tool->Options 中的“Desktop Kit”上指定编译器版本(在 usr/bin 中为 GCC x86 32 位)和一个可用的 QT 版本 4.8.7,最新版本 5.5.1 给我一个“未正确安装,请运行 make install”错误)
尽管我很难理解中断的概念以及“int 16h”中断如何在带有 GCC 内联汇编的 linux 上工作,但语法是什么以及如何获取任何按下的键盘键代码?
解决方法
对于现代系统;在启动期间,操作系统从固件中控制所有硬件,之后固件将无法使用(因为无法假设任何硬件处于固件预期的状态)。然后操作系统启动它自己为操作系统设计的驱动程序,并使用 IRQ 并与操作系统调度程序一起工作(并且不要浪费 CPU 时间轮询),并为多 CPU 工作,并正确支持内存(并且不是t 限制为 640 KiB)。
这意味着 BIOS 中断 "int 0x16" 只能在以下情况下工作:
a) 您使用的操作系统在 20 多年前变得无关紧要(MS-DOS)
b) 您使用的操作系统做了大量额外工作来模拟 20 多年前变得无关紧要的操作系统(例如,Windows95 内置的“传统 DOS 兼容性”,而不是任何适用于 Windows 的 Windows 可执行文件)。
c) 您没有使用操作系统,而且计算机太旧以至于固件是 BIOS(较新的计算机改为使用 UEFI)。
我必须编写一个使用中断的代码,使用汇编命令获取在键盘上按下的键。
我会仔细检查你应该做什么。例如。 “使用中断”可能意味着“使用旧的 32 位 Linux 内核 API (int 0x80) 而不是过时的 BIOS 功能”(它可能意味着“使用过时的 PS/2 控制器芯片 IRQ(就像设备驱动程序一样)”),或者别的什么)。
更具体地说;我猜您遵循了包括“在虚拟机中安装旧的 32 位 Linux 版本”的说明,因为您应该使用旧的 Linux 内核 API(“int 0x80”)来执行“汇编语言中的 read(STDIN_FILENO,buffer,1);
"(从 stdin
中获取一个字节,它可能来自也可能不来自键盘,具体取决于您的可执行文件的启动/使用方式)。
服务 int 16h
是 BIOS 服务。它只在实模式下工作,通常用于引导加载程序或 DOS 程序。不能从 Windows(任何版本,除非执行 DOS 程序,在这种情况下模拟服务)或 Linux(任何版本)或任何其他保护模式操作系统。
根据 Freenode 上 #winapi 上的人的说法,可能可以从 Windows 98 上的 Win32 程序调用 int 16h
,但该调用可能会导致 BIOS 挂起。无论如何,这对实现您的目标没有帮助。
要解决您的问题,请使用诸如 ia16-gcc、Open Watcom 或旧版 MSVC 之类的 DOS 工具链编写 DOS 程序,或者使用您正在编程的操作系统支持的保护模式服务。