我试图将键盘交互添加到this example的代码中。请考虑以下文件:
Cargo.toml
[package]
name = "kernelhello"
version = "0.0.1"
[dependencies]
bootloader = "0.3.12"
[package.metadata.bootimage]
default-target = "build.json"
build.json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
SRC / main.rs
// src/main.rs
#![feature(asm)]
#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points
use core::panic::PanicInfo;
/// This function is called on panic.
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub extern "C" fn _start() -> ! {
let mut HELLO: &mut [u8] = &mut b"Hello World!".clone();
let vga_buffer = 0xb8000 as *mut u8;
let mut z = 0;
loop {
for (i, byte) in HELLO.iter_mut().enumerate() {
unsafe {
z += 14;
z %= 4000;
*vga_buffer.offset(z + i as isize * 2) = *byte;
*vga_buffer.offset(z + i as isize * 2 + 1) = 0xa;
asm!("mov $$0, %ah\nint $$0x16");
}
}
}
}
不幸的是,尝试做bootimage run
的结果是一个卡在重启循环中的图像 - 如果我注释掉asm!
调用就不会发生这种情况。这是一个反汇编:
➜ rust-kernel-hello objdump -D -b binary -Mintel,x86-64 -m i386 target/build/debug/bootimage-kernelhello.bin | grep -C5 'ef01'
eef0: 00
eef1: 48 8b 84 24 d0 00 00 mov rax,QWORD PTR [rsp+0xd0]
eef8: 00
eef9: 48 89 84 24 00 01 00 mov QWORD PTR [rsp+0x100],rax
ef00: 00
ef01: 48 8d bc 24 f0 00 00 lea rdi,[rsp+0xf0]
ef08: 00
ef09: e8 72 fe ff ff call 0xed80
ef0e: 48 89 94 24 20 01 00 mov QWORD PTR [rsp+0x120],rdx
ef15: 00
ef16: 48 89 84 24 18 01 00 mov QWORD PTR [rsp+0x118],rax
--
f119: 48 89 04 24 mov QWORD PTR [rsp],rax
f11d: 48 8b 04 24 mov rax,QWORD PTR [rsp]
f121: c6 00 0a mov BYTE PTR [rax],0xa
f124: b4 00 mov ah,0x0
f126: cd 16 int 0x16
f128: e9 d4 fd ff ff jmp 0xef01
f12d: 48 8d 3d cc 0a 00 00 lea rdi,[rip+0xacc] # 0xfc00
f134: e8 07 04 00 00 call 0xf540
f139: 0f 0b ud2
f13b: 48 8d 3d e6 0a 00 00 lea rdi,[rip+0xae6] # 0xfc28
f142: e8 f9 03 00 00 call 0xf540
我究竟做错了什么?
我想更详细地解释zx485的评论:
我经常看到汇编语言初学者对int
(x86),syscall
(MIPS)或SWI
(ARM)的指令实际上有所困惑。
实际上,这些指令是call
指令的一种特殊形式:它们调用一些通常位于操作系统中的子程序。
64位x86 CPU具有不同的操作模式。其中一个被命名为“真实模式”。在此模式下,CPU只能执行16位代码。
可以使用int 0x16
调用的BIOS子程序仅在PC以“实模式”运行时才有效。
您的程序是64位程序(使用rax
之类的寄存器)这一事实告诉我们您的CPU没有以实模式运行。
如果您正在编写自己的操作系统,则可以定义由某些int
指令调用的自己的子例程:
您可以定义由int 0x16
调用的子例程,该子例程读取键盘,另一个由int 0x10
调用,写入屏幕。
但是,您也可以自由定义int 0x16
用于写入屏幕,int 0x10
用于操作系统中的硬盘访问。
在每种情况下,您都必须自己编写子例程,因为BIOS中的现有子例程不能用于“实模式”之外的任何其他操作模式。 (这就是罗斯里奇在评论中指出的。)