中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

中国DOS联盟论坛
现在时间是 2026-09-14 22:59
中国DOS联盟论坛 » DOS开发编程 & 发展交流 (开发室) » [求助]RTL8100BL的网卡驱动,某些寄存器无法操作 查看 2,478 回复 0
楼 主 [求助]RTL8100BL的网卡驱动,某些寄存器无法操作 发表于 2016-01-07 09:32 ·  中国 北京 电信
新手上路
积分 4
发帖 1
注册 2016-01-06 17:19
10年会员
UID 181417
性别 男
状态 离线
dos 版本:6.22 bc版本:3.1 硬件架构:x86
目前遇到问题:发现一些寄存器不能正常操作:例如:Transmit Configuration Register这个寄存器,按8位 16位 32位写,然后按8位读发现每次根本就写不进去数值,我的8位操作outport()函数,16位操作outportb()函数,32位用的是static void outportd(unsigned short portid, unsigned long value) //output dword to I/O port
{

asm MOV DX, portid
asm DB 0x66 //
asm MOV AX, WORD PTR value // MOV EAX,value
asm DB 0x66, 0xEF // OUT DX,EAX

}
这个函数,有一点我不是特别理解,一般的寄存器如果支持32位的写,按道理来说它可以8位写,也可以16位写,但目前我不知道我的哪块操作有问题,谁做过这方面的工作,给点建议或者意见,多谢。。。。。。
[已使用 Gemini 解答]
Gemini 解答

1. 核心原因:RTL8139/RTL8100 系列的写保护机制

在 Realtek RTL8139 / RTL8100 系列网络控制器中,包括 TCR(Transmit Configuration Register,偏移量 0x40)RCR(Receive Configuration Register,偏移量 0x44) 以及 CONFIG0~CONFIG4 在内的多个关键配置寄存器,受硬件写保护机制限制。

在芯片复位或默认上电状态下,这些寄存器处于只读保护状态。若要向 TCR 写入有效数据,必须先解锁配置寄存器:

  1. 解锁写保护:向 93C46 (EECR) 命令寄存器(偏移量 0x50)写入 0xC0(即设置 EEM1=1, EEM0=1,进入 Config Register Write Enable 模式)。
  2. 写入目标配置:此时再对 TCR、RCR 等寄存器执行写操作,写入值才会生效。
  3. 恢复写保护:配置完成后,向 EECR 写入 0x00(设置 EEM1=0, EEM0=0,进入 Normal Operation 模式)以锁定配置,防止运行过程中意外篡改。

若跳过解锁步骤,向 TCR 写入的数据会被硬件直接丢弃,导致“读出值始终不变或写不进去”。


2. PCI 寄存器访问宽度与 8/16/32 位操作的误区

“支持 32 位访问的寄存器必然支持 8 位或 16 位分段读写”这一假设在 PCI 硬件体系中并不完全成立。

  • PCI 字节使能(Byte Enable)限制:PCI 规范在物理层通过 C/BE[3:0]# 信号控制当前周期的有效字节。但具体到 ASIC 控制器内部的逻辑设计,部分 32 位宽度的内部配置寄存器并未实现单字节写入(Byte-Lane)的逻辑分支,只响应完整的 32 位双字(DWORD)写入周期。
  • 寄存器宽度与对齐:TCR 是一个严格的 32 位寄存器(基地址 + 0x40)。对其进行 8 位或 16 位读写属于非标准访问,在不同的主板芯片组和 PCI-to-PCI 桥接芯片上可能产生不可预期的总线行为。开发 PCI 驱动时,必须严格按照数据手册规定的宽度(TCR 必须用 32 位 DWORD)进行对齐读写。

3. Borland C++ 3.1 库函数与内嵌汇编的潜在问题

代码中存在两处实现缺陷:

(1) Borland C++ I/O 函数混淆

在 Borland C++ 3.1 标准库(<dos.h>)中:

  • outportb(port, val)8 位(Byte) 输出。
  • outport(port, val)16 位(Word) 输出。

帖子中将两者功能弄反,可能导致 8 位和 16 位操作在测试时产生误导。

(2) 16 位实模式下的 32 位 I/O 汇编封装

作者手写的 32 位 I/O 汇编函数:

static void outportd(unsigned short portid, unsigned long value)
{
  asm MOV DX, portid
  asm DB 0x66
  asm MOV AX, WORD PTR value   // 意图:MOV EAX, [BP+value]
  asm DB 0x66, 0xEF            // 意图:OUT DX, EAX
}

在 BC 3.1(16 位实模式编译器)中,unsigned long value 在栈上占用 4 字节(2 个 16 位字)。使用 asm DB 0x66 配合 asm MOV AX, WORD PTR value 时,生成的机器码为 66 8B 46 xx,CPU 在执行时会从 BP + offset 读取连续的 4 字节加载到 EAX。该写法虽然能碰巧读出完整的 32 位数值,但语法语义与编译器内部符号表存在歧义。

更为标准且对称的 32 位 I/O 读写实现应如下:

/* 32位 I/O 写入 */
static void outportd(unsigned short port, unsigned long val)
{
    __asm {
        mov dx, port
        db 0x66; mov ax, word ptr val  /* 0x66 操作数前缀,将栈上的 32 位 val 载入 EAX */
        db 0x66; out dx, ax            /* OUT DX, EAX */
    }
}

/* 32位 I/O 读取 */
static unsigned long inportd(unsigned short port)
{
    unsigned long result;
    __asm {
        mov dx, port
        db 0x66; in ax, dx             /* IN EAX, DX */
        db 0x66; mov word ptr result, ax /* 将 EAX 存入 32 位局部变量 result */
    }
    return result;
}

4. TCR 寄存器的读写对称性与默认位

TCR 寄存器内部包含保留位(Reserved)以及与硬件版本相关的固定位(例如部分位在读取时强制返回 10)。因此,即使成功写入数值,回读出的原始 32 位数值也可能与写入值并不逐位一致,应通过位掩码(Bitmask)校验可配置的特定字段(如 TxRRMXDMA 等),而不应直接比对整个双字。

论坛跳转: