使用 x86 程序集初始化串口

Initialize serial port with x86 assembly

我想在不使用 BIOS 中断 14h 的情况下使用串行端口 COM1,为此我正在按照 osdev 上的教程进行操作,但在初始化过程中遇到了一些问题。 (我对 asm 和 bios 相关的东西很陌生,所以我的代码可能非常错误,或者可能需要在我初始化串口之前初始化其他东西)

我现在的代码是这样的,应该是直接翻译他们的C代码。

%macro outb 2
    mov dx, %1
    mov al, %2
    out dx, al
%endmacro

%macro inb 1
    mov dx, %1
    in al, dx
%endmacro

    bits 16
    org 0x7c00 ;; set the origin at the start of the bootloader address space

    xor ax, ax
    mov dx, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

serial_init:
    outb [com1+1], 0x00 ;; disable all interrupts
    outb [com1+3], 0x80 ;; enable DLAB (set baud rate divisor)
    outb [com1+0], 0x03 ;; Set divisor to 3 (lo byte) 38440 baud
    outb [com1+1], 0x00 ;;                  (hi byte)
    outb [com1+3], 0x03 ;; 8 bits, no parity, one stop bit
    outb [com1+2], 0xc7 ;; enable fifo, clear them, with 14-byte threshold
    outb [com1+4], 0x0b ;; IRQs enabled, RTS/DSR set
    outb [com1+4], 0x1e ;; set in loopback mode, test the serial chip
    outb [com1+0], 0xae ;; test serial chip

hang:
    jmp hang

com1:
    dw 0x3f8

    times 510-($-$$) db 0
    dw 0xaa55

我有一些调试原语和一切,似乎很顺利,直到行 outb [com1+0], 0x03 之后,如果我读取行控制寄存器 [com1+5] 我得到一个错误,但我不确定如何解释它(它似乎是与停止位相关的错误,错误3)

鉴于 outb 宏定义,NASM 会将您的 outb [com1+1], 0x00 宏调用扩展为:

mov dx, [com1+1]
mov al, 0x00
out dx, al

因为方括号,第一条指令将从内存中加载 DX,遗憾的是它不包含预期的端口地址 (0x03F9)!你得到的是 0x0003,由存储在 com1 的字的高字节和内存中由于 times 零填充而恰好为 0 的下一个字节组成。

在你的辩护中,wiki 文章 https://wiki.osdev.org/Serial_Ports 在说你应该将数据发送到例如[PORT + 1]。然而,这些括号与汇编编程语言中使用的相同括号无关。

C代码片段更清晰。它有 define PORT 0x3f8,在汇编中变成 PORT equ 0x03F8outb(PORT + 1, 0x00) 指令在汇编中变为 outb PORT + 1, 0x00。这次 NASM 会将您的 outb 宏扩展为以下 3 条指令:

mov  dx, PORT + 1    ; Same as `mov dx, 0x03F9`
mov  al, 0x00
out  dx, al

给出的C代码供参考:

define PORT 0x3f8          // COM1
 
static int init_serial() {
   outb(PORT + 1, 0x00);    // Disable all interrupts
   outb(PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor)
   outb(PORT + 0, 0x03);    // Set divisor to 3 (lo byte) 38400 baud
   outb(PORT + 1, 0x00);    //                  (hi byte)
   outb(PORT + 3, 0x03);    // 8 bits, no parity, one stop bit
   outb(PORT + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
   outb(PORT + 4, 0x0B);    // IRQs enabled, RTS/DSR set
   outb(PORT + 4, 0x1E);    // Set in loopback mode, test the serial chip
   outb(PORT + 0, 0xAE);    // Test serial chip (send byte 0xAE and check if serial returns same byte)
 
   // Check if serial is faulty (i.e: not same byte as sent)
   if(inb(PORT + 0) != 0xAE) {
      return 1;
   }
 
   // If serial is not faulty set it in normal operation mode
   // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
   outb(PORT + 4, 0x0F);
   return 0;
}

if I read the line control register [com1+5] ...

准确性将是对该硬件进行编程的关键。对于一般的编程来说也是如此。
线路控制寄存器 (LCR) 位于 03FB (PORT + 3)。
线路状态寄存器 (LSR) 位于 03FD (PORT + 5)。