linux 0.11 内核学习 -- rs_io.s,串口汇编代码
/*
* 该文件实现rs232 串行通信中断处理
*/
/*
* linux/kernel/rs_io.s
*
* (C) 1991 Linus Torvalds
*/
/*
* rs_io.s
*
* This module implements the rs232 io interrupts.
*/
.text
.globl _rs1_interrupt,_rs2_interrupt
/* 读写队列缓冲区的长度 */
size = 1024 /* must be power of two !
and must match the value
in tty_io.c!!! */
/* these are the offsets into the read/write buffer structures */
/* 读写缓冲结构中的偏移量tty_queue */
rs_addr = 0
head = 4
tail = 8
proc_list = 12
buf = 16
/* 当写队列里还剩256 个字符空间(WAKEUP_CHARS)时,我们就可以写 */
startup = 256 /* chars left in write queue when we restart it */
/*
* These are the actual interrupt routines. They look where
* the interrupt is coming from, and take appropriate action.
*/
.align 2
/* 串行端口1 中断处理程序入口点 */
_rs1_interrupt:
// tty 表中对应串口1 的读写缓冲指针的地址入栈
pushl $_table_list+8 // table_list定义在文件tty_io.c文件中
jmp rs_int
/* 串行端口2 中断处理程序入口点 */
.align 2
_rs2_interrupt:
pushl $_table_list+16 // 同上
rs_int:
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
push %es
push %ds /* as this is an interrupt, we cannot */
/* 让ds、es 指向内核数据段 */
pushl $0x10 /* know that bs is ok. Load it */
pop %ds
pushl $0x10
pop %es
movl 24(%esp),%edx // 将缓冲队列指针地址存入edx 寄存器
movl (%edx),%edx // 取读队列指针(地址)-> edx
movl rs_addr(%edx),%edx // 取串口1 的端口号-> edx
/* edx 指向中断标识寄存器 */
addl $2,%edx /* interrupt ident. reg */
rep_int:
xorl %eax,%eax // eax = 0
inb %dx,%al // 取中断标识字节,用以判断中断来源
testb $1,%al // 有无待处理的中断?
jne end // 没有,则跳转至退出处理处end
cmpb $6,%al /* this shouldn't happen, but ... */
ja end // al 值>6? 是则跳转至end
movl 24(%esp),%ecx // 再取缓冲队列指针地址-> ecx
pushl %edx // 将端口号0x3fa(0x2fa)入栈
subl $2,%edx // 0x3f8(0x2f8)
/* jmp_table在下面定义 */
call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */
popl %edx // 弹出中断标识寄存器端口号0x3fa(或0x2fa)
jmp rep_int // 继续处理
/* 向中断控制器发送结束中断指令EOI */
end: movb $0x20,%al
outb %al,$0x20 /* EOI */
pop %ds
pop %es
popl %eax
popl %ebx
popl %ecx
popl %edx
/* 丢弃缓冲队列指针地址 */
addl $4,%esp # jump over _table_list entry
iret
/* 各中断类型处理程序地址跳转表 */
/* modem 状态变化中断,写字符中断,读字符中断,线路状态有问题中断 */
jmp_table:
.long modem_status,write_char,read_char,line_status
.align 2
/* 通过读modem 状态寄存器进行复位(0x3fe) */
modem_status:
addl $6,%edx /* clear intr by reading modem status reg */
inb %dx,%al
ret
.align 2
/* 通过读线路状态寄存器进行复位(0x3fd) */
line_status:
addl $5,%edx /* clear intr by reading line status reg. */
inb %dx,%al
ret
.align 2
/*
* read_char实现的是从CPU读取,将结果存储到缓冲队列中
*/
read_char:
inb %dx,%al // 读取字符-> al
movl %ecx,%edx // 当前串口缓冲队列指针地址-> edx
subl $_table_list,%edx // 当前串口队列指针地址-> edx
shrl $3,%edx
movl (%ecx),%ecx # read-queue
// 取读队列中缓冲头指针-> ebx
movl head(%ecx),%ebx
// 将字符放在缓冲区中头指针所指的位置
movb %al,buf(%ecx,%ebx)
incl %ebx // 将头指针前移一字节
andl $size-1,%ebx // 缓冲区头指针进行模操作
cmpl tail(%ecx),%ebx // 缓冲区头指针与尾指针比较
je 1f // 若相等,表示缓冲区满,跳转到标号1 处
movl %ebx,head(%ecx) // 保存修改过的头指针
1: pushl %edx // 将串口号压入堆栈
call _do_tty_interrupt // 调用tty 中断处理C 函数
addl $4,%esp // 丢弃入栈参数,并返回
ret
.align 2
/*
* write_char函数是将收到的数据传送给CPU
*/
write_char:
// 取写缓冲队列结构地址-> ecx
movl 4(%ecx),%ecx # write-queue
movl head(%ecx),%ebx // 取写队列头指针-> ebx
subl tail(%ecx),%ebx // 头指针 - 尾指针 = 队列中字符数
andl $size-1,%ebx # nr chars in queue
je write_buffer_empty // 如果头指针 = 尾指针,说明写队列无字符,跳转处理
cmpl $startup,%ebx // 队列中字符数超过256 个?
ja 1f // 超过,则跳转处理
// 取等待该队列的进程的指针,并判断是否为空
movl proc_list(%ecx),%ebx # wake up sleeping process
testl %ebx,%ebx # is there any?
je 1f // 是空的,则向前跳转到标号1 处
movl $0,(%ebx) // 否则将进程置为可运行状态(唤醒进程)
/*
* 缓冲区的长度是1024,但是在缓冲区中含有256个字符时,就开始
* 进行写操作。下面的饿代码首先将超过256的字节的后一个字节
* 写入端口中,然后该段代码从尾部读取一个字符,然后继续rep_int
* ,程序继续判断是那种的处理情况
*
* tail(low address) ---------------------> head(high address)
*/
1: movl tail(%ecx),%ebx // 取尾指针
movb buf(%ecx,%ebx),%al // 从缓冲中尾指针处取一字符-> al
outb %al,%dx // 将端口0x3f8(0x2f8)送出保存到寄存器中
incl %ebx // 尾指针前移
andl $size-1,%ebx // 尾指针若到缓冲区末端,则折回
movl %ebx,tail(%ecx) // // 保存已修改过的尾指针
cmpl head(%ecx),%ebx // 尾指针与头指针比较
// 注意队列数据结构
je write_buffer_empty // // 若相等,表示队列已空,则跳转
ret
.align 2
/*
* 唤醒进程,改写端口寄存器值
*/
write_buffer_empty:
// 唤醒等待的进程
movl proc_list(%ecx),%ebx # wake up sleeping process
// 有等待的进程吗?
testl %ebx,%ebx # is there any?
je 1f // 无,则向前跳转到标号1 处
movl $0,(%ebx) // 唤醒进程
1: incl %edx // 指向端口0x3f9(0x2f9)
inb %dx,%al // 读取中断允许寄存器
jmp 1f // 延迟
1: jmp 1f
/* disable transmit interrupt */
1: andb $0xd,%al
outb %al,%dx
ret
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?