PART 1 >> 使用BIOS中断实现键盘输入的读取和显示

; File: c09_2.asm
; Date: 20191222; ===============================================================================
SECTION head vstart=0                       ; 定义用户程序头部段 ; 用户程序可能很大,16位可能不够program_length  dd program_end          ; 程序总长度[0x00]; 程序入口点(Entry Point)program_entry   dw beginning            ; 偏移地址[0x04]; 只是编译阶段确定的汇编地址。程序加载到内存后,需要根据加载的实际位置重新计算; 尽管在16位的环境中,一个段最长为64KB,但它却可以起始于任何20位的物理地址处。; 不可能用16位来保存20位的地址,所以需要32位dd section.code.start ; 汇编地址[0x06] realloc_tbl_size dw (head_end-segment_code_1)/4     ; 段重定位表项个数[0x0a]segment_code_1  dd section.code.start   ; [0x0c]    segment_data_1  dd section.data.start   ; [0x10]segment_stack   dd section.stack.start  ; [0x14]    ; 这里section 和 start 不能用大写 ???head_end:; ===============================================================================
SECTION code align=16 vstart=0beginning:; 设置用户程序自己的堆栈段; ds和es依然指向着用户程序头部head段mov ax, [segment_stack]mov ss, axmov sp, stack_end; 设置用户程序自己的数据段; 如果先初始化数据段ds和附加段es,那么头部head段中的数据将无法访问mov ax, [segment_data_1]mov ds, axmov cx, msg_end-messagemov bx, message.show_char:; 在频幕上写字符; 中断0x10的0x0e号功能。在屏幕光标位置处写一个字符,并推进光标位置。; Input: al, 字符mov ah, 0x0e        ; ah中指定0x0e号功能mov al, [bx]int 0x10inc bxloop .show_char.rw_keyboard:; 从键盘读字符; 中断0x10的0x00号功能。; Output: al, 字符mov ah, 0x00        ; ah中指定0x00号功能int 0x16mov ah, 0x0emov bl, 0x07int 0x10jmp .rw_keyboard; ===============================================================================
SECTION data align=16 vstart=0    message       db 'Hello, friend!',0x0d,0x0adb 'This simple procedure used to demonstrate 'db 'the BIOS interrupt.',0x0d,0x0adb 'Please press the keys on the keyboard ->'msg_end:; ===============================================================================
SECTION stack align=16 vstart=0   resb 256
stack_end:; ===============================================================================
SECTION program_tail
program_end:

===================================================================================================

PART 2 >> 使用RTC芯片实现实时时间的显示

; FILE: c09_1.asm
; DATE: 20191211; ===============================================================================
SECTION head vstart=0                       ; 定义用户程序头部段 ; 用户程序可能很大,16位可能不够program_length  dd program_end          ; 程序总长度[0x00]; 程序入口点(Entry Point)program_entry   dw beginning            ; 偏移地址[0x04]; 只是编译阶段确定的汇编地址。程序加载到内存后,需要根据加载的实际位置重新计算; 尽管在16位的环境中,一个段最长为64KB,但它却可以起始于任何20位的物理地址处。; 不可能用16位来保存20位的地址,所以需要32位dd section.code.start ; 汇编地址[0x06] realloc_tbl_size dw (head_end-segment_code_1)/4     ; 段重定位表项个数[0x0a]segment_code_1  dd section.code.start   ; [0x0c]    segment_data_1  dd section.data.start   ; [0x10]segment_stack   dd section.stack.start  ; [0x14]    ; 这里section 和 start 不能用大写 ???head_end:; ===============================================================================
SECTION code align=16 vstart=0beginning:; 设置用户程序自己的堆栈段; ds和es依然指向着用户程序头部head段mov ax, [segment_stack]mov ss, axmov sp, stack_end; 设置用户程序自己的数据段; 如果先初始化数据段ds和附加段es,那么头部head段中的数据将无法访问mov ax, [segment_data_1]mov ds, axmov bx, msg_init            ; 显示初识信息call show_stringmov bx, msg_install         ; 显示安装信息call show_string; 计算RTC芯片中断处理过程的段地址和偏移地址; RTC芯片的中断信号,通向中断控制器8259从片的第1个中断引脚IR0。; 计算机启动期间,BIOS会初始化中断控制器8259,将主片的中断号设为从0x08开始,从片的中断号从0x70开始。; 所以,计算机启动后,RTC芯片的中断号默认是0x70(可通过对8259编程来修改默认中断号)xor ax, axmov al, 0x70                ; RTC芯片的默认中断号0x70mov bl, 4                   ; 每个中断向量表项占4字节(段地址:偏移地址), 乘4,得中断像量表内的偏移mul blmov bx, axcli                         ; cli 清楚IF标志位,禁止中断,防止改动期间发生新的0x70号中断; 设置中断向量表0x70号表项内容; 实模式下,256个中断程序的入口点集中存放在内存0x00000~0x003FF共1KB的空间内,即中断向量表push esxor ax, axmov es, ax                  ; 将es指向中断向量表所在的段    mov word [es:bx], my_int_0x70    ; 中断处理过程的偏移地址    mov word [es:bx+2], cs           ; 段地址pop es; 不懂 ……; 设置RTC的工作状态,使它能够产生中断信号给8259中断控制器mov al,0x0b                        ;RTC寄存器Bor al,0x80                         ;阻断NMI out 0x70,almov al,0x12                        ;设置寄存器B,禁止周期性中断,开放更 out 0x71,al                        ;新结束后中断,BCD码,24小时制 mov al,0x0cout 0x70,alin al,0x71                         ;读RTC寄存器C,复位未决的中断状态in al,0xa1                         ;读8259从片的IMR寄存器 and al,0xfe                        ;清除bit 0(此位连接RTC)out 0xa1,al                        ;写回此寄存器 sti                         ; sti 放开中断,与cli相对应mov bx, msg_donecall show_string            ; 显示中断安装完成信息mov bx, msg_tipscall show_string            ; 显示提示信息; 屏幕中心显示字符@mov ax, 0xb800mov ds, axmov byte [12*160 + 33*2], '@'       ; 25row*80col    ; hlt 使处理器处于停机状态,停止执行指令,; 可以被外部中断唤醒并恢复执行.idle:hltnot byte [12*160 + 33*2 + 1] ; 反转上面@字符的显示属性jmp .idle; Function: 频幕上显示文本
; Input: ds:bx 字符串起始地址,以0结尾
show_string:mov cl, [bx]or cl, cljz .exitcall show_charinc bxjmp show_string.exit:ret; Function:
; Input: cl 字符
show_char:push axpush bxpush cxpush dxpush dspush es; 读取当前光标位置; 索引寄存器端口0x3d4,其索引值14(0x0e)和15(0x0f)分别用于提供光标位置的高和低8位; 数据端口0x3d5mov dx, 0x3d4   mov al, 0x0e   out dx, almov dx, 0x3d5in al, dxmov ah, almov dx, 0x3d4mov al, 0x0fout dx, almov dx, 0x3d5in al, dxmov bx, ax      ; 此处用bx存放光标位置的16位数; 判断是否为回车符0x0dcmp cl, 0x0d    ; 0x0d 为回车符jnz .show_0a    ; 不是回车符0x0d,再判断是否换行符0x0amov ax, bx      ; 是回车符,则将光标置位到行首mov bl, 80div blmul blmov bx, axjmp .set_cursor; ; 将光标位置移到行首,可以直接减去当前行吗??; mov ax, bx; mov dl, 80; div dl; sub bx, ah; jmp .set_cursor; 判断是否为换行符0x0a.show_0a:cmp cl, 0x0a    ; 0x0a 为换行符    jnz .show_normal; 不是换行符,则正常显示字符add bx, 80      ; 是换行符,再判断是否需要滚屏jmp .roll_screen; 正常显示字符; 在写入其它内容之前,显存里全是黑底白字的空白字符0x0720,所以可以不重写黑底白字的属性.show_normal:mov ax, 0xb800  ; 显存映射在 0xb8000~0xbffffmov es, axshl bx, 1       ; 光标指示字符位置,显存中一个字符占2字节,光标位置乘2得到该字符在显存中得偏移地址    mov [es:bx], clshr bx, 1       ; 恢复bxinc bx          ; 将光标推进到下一个位置; 判断是否需要向上滚动一行屏幕.roll_screen:cmp bx, 2000    ; 25行x80列jl .set_cursormov ax, 0xb800    mov ds, ax      ; movsw的源地址ds:simov es, ax      ; movsw的目的地址es:dimov si, 0xa0mov di, 0cld             ; 传送方向cls stdmov cx, 1920    ; rep次数 24行*每行80个字符*每个字符加显示属性占2字节 / 一个字为2字节rep movsw; 清除屏幕最底一行,即写入黑底白字的空白字符0x0720mov bx, 3840    ; 24行*每行80个字符*每个字符加显示属性占2字节mov cx, 80.cls:mov word [es:bx], 0x0720add bx, 2loop .clsmov bx, 1920    ; 重置光标位置为最底一行行首; 根据bx重置光标位置; 索引寄存器端口0x3d4,其索引值14(0x0e)和15(0x0f)分别用于提供光标位置的高和低8位; 数据端口0x3d5.set_cursor:mov dx, 0x3d4   mov al, 0x0e   out dx, almov dx, 0x3d5mov al, bh      ; in和out 只能用al或者axout dx, almov dx, 0x3d4mov al, 0x0fout dx, almov dx, 0x3d5mov al, blout dx, alpop espop dspop dxpop cxpop bxpop axretmy_int_0x70:push axpush bxpush cxpush dxpush es.w0:                                    mov al,0x0a                        ;阻断NMI。当然,通常是不必要的or al,0x80                          out 0x70,alin al,0x71                         ;读寄存器Atest al,0x80                       ;测试第7位UIP jnz .w0                            ;以上代码对于更新周期结束中断来说 ;是不必要的 xor al,alor al,0x80out 0x70,alin al,0x71                         ;读RTC当前时间(秒)push axmov al,2or al,0x80out 0x70,alin al,0x71                         ;读RTC当前时间(分)push axmov al,4or al,0x80out 0x70,alin al,0x71                         ;读RTC当前时间(时)push ax; 读一下RTC的寄存器C,使得所有中断标志复位。相当于,告诉RTC,中断已得到处理,可以继续下一次中断。; 否则,RTC看到中断未被处理,将不再产生中断信号。; RTC产生中断的原因有多种,可以在程序中通过读寄存器C来判断。不过,这里不需要,因为除了更新周期结束中断外,其他中断都被关闭了。mov al,0x0c                        ;寄存器C的索引。且开放NMI out 0x70,alin al,0x71                         ;读一下RTC的寄存器C,否则只发生一次中断;此处不考虑闹钟和周期性中断的情况 ; 屏幕是黑的,默认的显示属性是0x07,即黑底白字mov ax,0xb800mov es,ax                          ; es指向显示缓冲区mov bx,12*160 + 36*2               ;从屏幕上的12行36列开始显示    ; 小时pop axcall bcd_to_asciimov [es:bx],ahmov [es:bx+2],al                   ;显示两位小时数字mov byte [es:bx+4],':'             ;显示分隔符':'not byte [es:bx+5]                 ;反转显示属性 ; 分钟pop axcall bcd_to_asciimov [es:bx+6],ahmov [es:bx+8],al                   ;显示两位分钟数字mov al,':'mov [es:bx+10],al                  ;显示分隔符':'not byte [es:bx+11]                ;反转显示属性; 秒pop axcall bcd_to_asciimov [es:bx+12],ahmov [es:bx+14],al                  ;显示两位小时数字; 向8259芯片发送中断结束命令(End Of Interrupt, EOI)mov al,0x20                        ;中断结束命令EOI out 0xa0,al                        ;向从片发送 out 0x20,al                        ;向主片发送pop espop dxpop cxpop bxpop axiret        ; iret 中断返回指令,Interrupt Return,回到中断之前的地方继续执行; 这里如果用ret,显示的时间将不会更新 ? ? ?; Function: BCD码转ASCII
; Input: AL, BCD码
; Output: AX, ascii码
bcd_to_ascii:mov ah, al      ; 先复制到ah,用于后面处理十位数,al用于处理个位数and al, 0x0f    ; 仅保留低4位add al, 0x30    ; 转换成ASCIIshr ah, 4and ah, 0x0fadd ah, 0x30ret;===============================================================================
SECTION data align=16 vstart=0msg_init       db 'Starting...',0x0d,0x0a,0msg_install    db 'Installing a new interrupt 70H...',0msg_done       db 'Done.',0x0d,0x0a,0msg_tips       db 'Clock is now working.',0; ===============================================================================
SECTION stack align=16 vstart=0resb 256
stack_end:; ===============================================================================
SECTION program_tail
program_end:

[书]x86汇编语言:从实模式到保护模式 -- 第九章 硬中断,使用RTC芯片实现实时时间的显示;软中断,使用BIOS中断实现键盘输入的读取和显示相关推荐

  1. [书]x86汇编语言:从实模式到保护模式 -- 第13章 mbr加载内核、内核加载应用程序

    # mbr加载内核 1.0x7c00,16位实模式 2.进入保护模式前的准备工作:创建段描述符(代码段.数据段.堆栈段.显示缓冲区),构建gdt 3.进入保护模式 ; 开启保护模式 ; CR0的第1位 ...

  2. [书]x86汇编语言:从实模式到保护模式 -- 第17章 中断、任务切换、分页机制、平坦模型

    # 任务切换 内核任务.用户任务1.用户任务2,之前的轮询切换 利用RTC芯片的硬件中断来实现任务切换 计算机主板上有实时时钟芯片RTC,可以设置RTC芯片,使得它每次更新CMOS中的时间信息后,发出 ...

  3. [书]x86汇编语言:从实模式到保护模式 -- 第11章 进入保护模式,初识全局描述符表GDT; 第12章 别名,冒泡排序

    第11章 进入保护模式:初始化全局描述符表,通过GDT进入代码段.数据段.堆栈段 ; FILE: c11_mbr.asm ; DATE: 20191229 ; TITLE: 硬盘主引导扇区代码; 设置 ...

  4. [书]x86汇编语言:从实模式到保护模式 -- 第16章 分页机制、平坦模型

    # 分页机制 二级页表:页目录.页表 ==> 4KB物理页 32位线性地址中:高10位为页目录中的索引号(乘4得偏移量),该目录项指向页表的基地址:中间10位为页表中的索引号,该页表项指向4KB ...

  5. [书]x86汇编语言:从实模式到保护模式 -- 第15章 任务切换

    # 执行结果 # TODO:字符串显示函数的滚屏部分应该是有bug. # file_02: c15_core.asm ; FILE: c13_core.asm ; DATE: 20200104 ; T ...

  6. [书]x86汇编语言:从实模式到保护模式 -- 第14章 任务和特权级保护,调用门、LDT、TSS、TCB

    # 加载用户程序 Part 1.TCB, Task Control Block, 任务控制块 分配内存作为该任务的TCB,并插入至TCB链表. Part 2.LDT, Locak Descriptor ...

  7. [书]x86汇编语言:从实模式到保护模式 -- 第八章 硬盘和显卡的访问与控制,mbr加载并重定位应用程序

    第八章 硬盘和显卡的访问与控制 mbr加载.重定位用户程序 PART 1 >> VirtualBox显示最终效果 ===================================== ...

  8. [书]x86汇编语言:从实模式到保护模式 -- 第六、七章 编写主引导扇区代码

    第六章 编写主引导扇区代码(启动时显示文字:Label offset:) PART 1 >> 用VirtualBox显示最终效果 1.1 汇编 启用nasm的工具"nasm-sh ...

  9. 硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01

    本文是<x86汇编语言:从实模式到保护模式>(电子工业出版社)的读书实验笔记. 这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果. 需要的源码文件 第一个文件是加载程 ...

最新文章

  1. [GYM 100492A] Average Convex Hull 凸包好题
  2. torch_geometric 笔记:nn.ChebNet
  3. java解压文件、复制文件、删除文件代码示例
  4. R语言blotter包及相关工具
  5. html云雾效果,PS中怎么做像这样的云雾效果?(有图)?
  6. 任重道远——记2016暑假训练
  7. wordpress 主题开发
  8. org.xml.sax.SAXParseException; lineNumber: 11; columnNumber: 110; schema_reference.4: 无法读取方案文档
  9. diag开关什么意思_双控开关接线图_一灯双控开关接线图_单联双控开关接线图_双控开关接线图实物图...
  10. Pandas数据框操作完整教程
  11. 热门开源CI/CD解决方案 GoCD 中曝极严重漏洞,可被用于接管服务器并执行任意代码...
  12. IP转发引擎的三种核心处理器区别介绍
  13. vivo android 6.0 root,vivo手机怎么获取root权限?vivo手机一键ROOT教程
  14. Windows 7 激活时提示“系统保留分区未分配驱动器号!”解决方法
  15. Coolpad/酷派炫影5890 root教程_方法
  16. pandas daraframe 写入读取excel文件,并简单计算
  17. 如果我找到了我的伴侣,一定要和他做以下几件事
  18. 一文看懂为什么边缘计算是大势所趋 | GSMA 报告
  19. 读《阿里铁军》有感【4000字】
  20. Mars3D(含Cesium)数据及服务篇:发布三维数据服务

热门文章

  1. 成都计算机大专院校排名,四川省计算机信息职业技术学院2020年排名
  2. 纵横网络靶场社区 大工UDP协议
  3. Web前端:UI设计对提高用户参与度的重要性
  4. godaddy停止支持php哪个版本,Godaddy主机修改PHP版本教程 | Godaddy美国主机中文指南...
  5. convert: no images defined
  6. 运维前线:一线运维专家的运维方法、技巧与实践3.4 小结
  7. 用友872服务器系统,用友ERP-U8V872 安装图解.doc
  8. 第10周 Mongodb的GridFS与分片
  9. 百度网盘如何下载下载ed2k链接
  10. 2022年度总结 EXI-小洲