要在8086平台上用汇编语言控制CH375读取U盘的指定扇区(物理扇区),核心流程分为:初始化CH375、发送读扇区命令、在中断服务程序中读取数据块,并处理必要的错误状态。
核心代码实现
此程序实现读取U盘的逻辑块地址(LBA)0号扇区,并将512字节数据存放于DATA_BUFFER。
; 假设硬件连接(可根据实际译码修改) CH375_CMD_PORT EQU 0A000H ; A0引脚为高电平时的端口地址(写命令) CH375_DAT_PORT EQU 0A001H ; A0引脚为低电平时的端口地址(写数据) ; 定义CH375关键命令字 CMD_DISK_READ EQU 54H ; 从USB存储器读数据块 CMD_RD_USB_DATA EQU 28H ; 从CH375缓冲区读取数据块 CMD_DISK_RD_GO EQU 55H ; 继续执行USB存储器的读操作 ; 定义中断状态 USB_INT_DISK_READ EQU 1DH ; 请求数据读出 USB_INT_SUCCESS EQU 14H ; 操作成功 ; 定义缓冲区(至少512字节) DATA_BUFFER SEGMENT DB 512 DUP(?) DATA_BUFFER ENDS ;------------------------------------------------------- ; 主调用示例:读取0号扇区 ;------------------------------------------------------- READ_SECTOR_EXAMPLE: MOV BX, 0 ; BX:SI 指向32位LBA地址(低16位在SI,高16位在BX) MOV SI, 0 ; LBA = 0x00000000 MOV CX, 1 ; 读取扇区数(通常为1) LEA DX, DATA_BUFFER ; DX指向数据缓冲区 CALL READ_PHYSICAL_SECTOR RET ;------------------------------------------------------- ; 读取物理扇区子程序 ; 入口: BX:SI = 32位起始LBA扇区号 (BX高16位,SI低16位) ; CX = 读取扇区数 (1-255) ; DX = 数据缓冲区首地址 ; 出口: 成功则缓冲区有数据,失败则返回错误码在AX ;------------------------------------------------------- READ_PHYSICAL_SECTOR PROC PUSH CX PUSH DX PUSH SI PUSH BX ; 1. 发送 DISK_READ 命令 MOV DX, CH375_CMD_PORT MOV AL, CMD_DISK_READ OUT DX, AL ; 2. 发送32位LBA地址(小端模式,低字节在前) MOV DX, CH375_DAT_PORT MOV AX, SI ; LBA低16位 OUT DX, AL ; LBA 字节0 (bit7-0) MOV AL, AH OUT DX, AL ; LBA 字节1 (bit15-8) MOV AX, BX ; LBA高16位 OUT DX, AL ; LBA 字节2 (bit23-16) MOV AL, AH OUT DX, AL ; LBA 字节3 (bit31-24) ; 3. 发送扇区数 (低字节有效) MOV AL, CL ; CL存的是扇区数 OUT DX, AL ; 4. 准备读取数据块 (每个扇区对应8个64字节的数据块) POP BX POP SI POP DX POP CX MOV DI, DX ; DI指向数据缓冲区 MOV BP, CX ; BP = 扇区数 SHL BP, 3 ; BP = 扇区数 * 8 (数据块数,因为每个扇区512字节=8个64字节块) READ_BLOCK_LOOP: PUSH CX PUSH DX ; 等待中断并获取状态 CALL WAIT_CH375_INT ; 等待CH375中断引脚变低(需根据实际硬件实现) MOV DX, CH375_CMD_PORT MOV AL, CMD_GET_STATUS ; 获取中断状态命令(假设为0x22,根据库定义调整) OUT DX, AL MOV DX, CH375_DAT_PORT IN AL, DX ; AL = 中断状态 CMP AL, USB_INT_DISK_READ JZ READ_DATA_BLOCK ; 非请求数据状态,退出并返回错误 POP DX POP CX JMP READ_FAIL READ_DATA_BLOCK: ; 发送 RD_USB_DATA 命令 MOV DX, CH375_CMD_PORT MOV AL, CMD_RD_USB_DATA OUT DX, AL ; 读取数据块长度(通常为64) MOV DX, CH375_DAT_PORT IN AL, DX ; AL = 后续数据长度(0-64) MOV CL, AL XOR CH, CH ; 根据长度读取数据到缓冲区 MOV SI, DI ; SI指向当前缓冲区位置 READ_DATA_BYTE: TEST CX, CX JZ READ_BYTE_DONE IN AL, DX MOV [SI], AL INC SI LOOP READ_DATA_BYTE READ_BYTE_DONE: MOV DI, SI ; 更新缓冲区指针 ; 发送 DISK_RD_GO 继续读操作 MOV DX, CH375_CMD_PORT MOV AL, CMD_DISK_RD_GO OUT DX, AL POP DX POP CX ; 循环处理下一个数据块 DEC BP JNZ READ_BLOCK_LOOP ; 所有数据块读取完成,等待最终成功中断 CALL WAIT_CH375_INT MOV DX, CH375_CMD_PORT MOV AL, CMD_GET_STATUS OUT DX, AL MOV DX, CH375_DAT_PORT IN AL, DX CMP AL, USB_INT_SUCCESS JZ READ_SUCCESS READ_FAIL: ; 处理错误(可根据实际需求添加错误码返回) MOV AX, 0FFFFH ; 返回错误标志 RET READ_SUCCESS: XOR AX, AX ; 返回0表示成功 RET READ_PHYSICAL_SECTOR ENDP ;------------------------------------------------------- ; 等待CH375中断(简易轮询方式) ; 实际使用时可根据中断引脚接法,使用INT引脚触发或轮询 ;------------------------------------------------------- WAIT_CH375_INT PROC ; 假设CH375的INT引脚连接到某个GPIO或总线 ; 此处为占位,需根据实际硬件实现等待中断引脚变低 ; 示例:轮询状态端口 WAIT_LOOP: ; IN AL, CH375_INT_PORT ; 读中断状态 ; TEST AL, 01H ; 测试中断引脚 ; JNZ WAIT_LOOP RET WAIT_CH375_INT ENDP关键步骤解析
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1. 发送读命令 | OUT CMD_PORT, DISK_READ | 命令码0x54表示准备读取U盘物理扇区 |
| 2. 发送LBA地址 | 连续输出4字节(小端模式) | LBA是逻辑块地址,0表示U盘的第一个物理扇区(MBR) |
| 3. 发送扇区数 | 输出1字节 | 表示要连续读取的扇区数量(通常为1) |
| 4. 等待中断 | WAIT_CH375_INT | 等待CH375的INT引脚变低,表示操作有进展 |
| 5. 获取中断状态 | GET_STATUS→ 读数据口 | 判断中断类型,0x1D表示请求数据读出 |
| 6. 读取数据块 | RD_USB_DATA→ 读长度 → 循环读数据 | 先读1字节长度(通常64),再读取该长度的数据 |
| 7. 继续读操作 | DISK_RD_GO | 通知CH375继续下一块数据的传输 |
| 8. 重复4-7 | 循环扇区数 × 8次 | 每个扇区512字节,分8次64字节块读取 |
| 9. 等待完成 | 最终等待0x14成功状态 | 所有块读取完毕后,CH375会返回成功状态 |
常见调试要点
检查基础通信:在调用读扇区前,务必先执行CH375的初始化序列(
SET_USB_MODE、DISK_INIT等),确保U盘已就绪。中断等待的实现:上述代码中的
WAIT_CH375_INT使用了轮询占位,实际开发中建议连接CH375的INT引脚到8086的外部中断(如INTR),通过中断服务程序提高效率。若采用轮询方式,需注意在等待期间关闭中断以避免干扰。缓冲区对齐:CH375每块数据固定为64字节,但总长度可能不是64的倍数(如最后一块可能不足),代码中已按实际返回长度读取,具有通用性。
错误处理扩展:若
WAIT_CH375_INT返回的状态不是0x1D也不是最终0x14,应根据CH375数据手册查询错误码含义(如0x23等)。LBA范围:对于普通U盘(容量小于2TB),LBA 0扇区通常是主引导记录(MBR),包含分区表信息。用户数据一般从
LBA 63或更大扇区开始,具体需解析MBR得到。