本章的第一个代码功能是在屏幕中间实时显示时间,其实现的思想是:cpu停机---》时间每秒更新一次,每一次更新都会引起0x70中断(0x70中断的内容已经被我们更改为显示当前时间)---》中断唤醒cpu开始执行我们的中断程序知道结束---》CPU停机等待中断---》唤醒CPU。。。
本程序的难点在于对8259芯片的理解和设置,这件事重要但不紧急,一时理解不通可以暂时跳过去。
;代码清单9-1;文件名:c09_1.asm;文件说明:用户程序 ;创建日期:2011-4-16 22:03;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段 program_length dd program_end ;程序总长度[0x00];用户程序入口点code_entry dw start ;偏移地址[0x04]dd section.code.start ;段地址[0x06] realloc_tbl_len dw (header_end-realloc_begin)/4;段重定位表项个数[0x0a]realloc_begin:;段重定位表 code_segment dd section.code.start ;[0x0c]data_segment dd section.data.start ;[0x14]stack_segment dd section.stack.start ;[0x1c]header_end: ;===============================================================================
SECTION code align=16 vstart=0 ;定义代码段(16字节对齐)
new_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 axmov al,0x0c ;寄存器C的索引。且开放NMI out 0x70,alin al,0x71 ;读一下RTC的寄存器C,否则只发生一次中断;此处不考虑闹钟和周期性中断的情况 mov ax,0xb800mov es,axpop axcall bcd_to_asciimov bx,12*160 + 36*2 ;从屏幕上的12行36列开始显示mov [es:bx],ahmov [es:bx+2],al ;显示两位小时数字mov al,':'mov [es:bx+4],al ;显示分隔符':'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 ;显示两位小时数字mov al,0x20 ;中断结束命令EOI out 0xa0,al ;向从片发送 out 0x20,al ;向主片发送 pop espop dxpop cxpop bxpop axiret;-------------------------------------------------------------------------------
bcd_to_ascii: ;BCD码转ASCII;输入:AL=bcd码;输出:AX=asciimov ah,al ;分拆成两个数字 and al,0x0f ;仅保留低4位 add al,0x30 ;转换成ASCII shr ah,4 ;逻辑右移4位 and ah,0x0f add ah,0x30ret;-------------------------------------------------------------------------------
start:mov ax,[stack_segment]mov ss,axmov sp,ss_pointermov ax,[data_segment]mov ds,axmov bx,init_msg ;显示初始信息 call put_stringmov bx,inst_msg ;显示安装信息 call put_stringmov al,0x70mov bl,4mul bl ;计算0x70号中断在IVT中的偏移mov bx,ax cli ;防止改动期间发生新的0x70号中断push esmov ax,0x0000mov es,axmov word [es:bx],new_int_0x70 ;偏移地址。mov word [es:bx+2],cs ;段地址pop esmov 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 ;重新开放中断 mov bx,done_msg ;显示安装完成信息 call put_stringmov bx,tips_msg ;显示提示信息call put_stringmov cx,0xb800mov ds,cxmov byte [12*160 + 33*2],'@' ;屏幕第12行,35列.idle:hlt ;使CPU进入低功耗状态,直到用中断唤醒not byte [12*160 + 33*2+1] ;反转显示属性 jmp .idle;-------------------------------------------------------------------------------
put_string: ;显示串(0结尾)。;输入:DS:BX=串地址mov cl,[bx]or cl,cl ;cl=0 ?jz .exit ;是的,返回主程序 call put_charinc bx ;下一个字符 jmp put_string.exit:ret;-------------------------------------------------------------------------------
put_char: ;显示一个字符;输入:cl=字符asciipush axpush bxpush cxpush dxpush dspush es;以下取当前光标位置mov dx,0x3d4mov al,0x0eout dx,almov dx,0x3d5in al,dx ;高8位 mov ah,almov dx,0x3d4mov al,0x0fout dx,almov dx,0x3d5in al,dx ;低8位 mov bx,ax ;BX=代表光标位置的16位数cmp cl,0x0d ;回车符?jnz .put_0a ;不是。看看是不是换行等字符 mov ax,bx ; mov bl,80 div blmul blmov bx,axjmp .set_cursor.put_0a:cmp cl,0x0a ;换行符?jnz .put_other ;不是,那就正常显示字符 add bx,80jmp .roll_screen.put_other: ;正常显示字符mov ax,0xb800mov es,axshl bx,1mov [es:bx],cl;以下将光标位置推进一个字符shr bx,1add bx,1.roll_screen:cmp bx,2000 ;光标超出屏幕?滚屏jl .set_cursormov ax,0xb800mov ds,axmov es,axcldmov si,0xa0mov di,0x00mov cx,1920rep movswmov bx,3840 ;清除屏幕最底一行mov cx,80.cls:mov word[es:bx],0x0720add bx,2loop .clsmov bx,1920.set_cursor:mov dx,0x3d4mov al,0x0eout dx,almov dx,0x3d5mov al,bhout dx,almov dx,0x3d4mov al,0x0fout dx,almov dx,0x3d5mov al,blout dx,alpop espop dspop dxpop cxpop bxpop axret;===============================================================================
SECTION data align=16 vstart=0init_msg db 'Starting...',0x0d,0x0a,0inst_msg db 'Installing a new interrupt 70H...',0done_msg db 'Done.',0x0d,0x0a,0tips_msg db 'Clock is now working.',0;===============================================================================
SECTION stack align=16 vstart=0resb 256
ss_pointer:;===============================================================================
SECTION program_trail
program_end:
实验现象:
体会:作为工程性的学科,学习底层的东西就是学习历史,中间包含的不只是科学原因,还有当时的历史原因,里面有很多现在看来怪异的现象。
实验二
代码45~46行,在这里,当寄存器 AH 的内容是 0x00 时,执行 int 0x16 后,中断服务例程会监视键盘动作。当它返回时,会在寄存器 AL 中存放按键的 ASCII 码。假如 键盘没有什么动作,则cs:ip会一直陷在int 0x16中断中,直到有动作。
;代码清单9-2;文件名:c09_2.asm;文件说明:用于演示BIOS中断的用户程序 ;创建日期:2012-3-28 20:35;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段 program_length dd program_end ;程序总长度[0x00];用户程序入口点code_entry dw start ;偏移地址[0x04]dd section.code.start ;段地址[0x06] realloc_tbl_len dw (header_end-realloc_begin)/4;段重定位表项个数[0x0a]realloc_begin:;段重定位表 code_segment dd section.code.start ;[0x0c]data_segment dd section.data.start ;[0x14]stack_segment dd section.stack.start ;[0x1c]header_end: ;===============================================================================
SECTION code align=16 vstart=0 ;定义代码段(16字节对齐)
start:mov ax,[stack_segment]mov ss,axmov sp,ss_pointermov ax,[data_segment]mov ds,axmov cx,msg_end-messagemov bx,message.putc: ;该功能用于在屏幕上的光标位置处写一个字符,并推进光标位置mov ah,0x0emov al,[bx]int 0x10inc bxloop .putc.reps:mov ah,0x00int 0x16mov ah,0x0emov bl,0x07 ;bl前景色,但是设置了也没用???[yb]int 0x10jmp .reps;===============================================================================
SECTION data align=16 vstart=0message 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=0resb 256
ss_pointer:;===============================================================================
SECTION program_trail
program_end:
实验现象:
实验体会:
计算机的学习类似于学习英语,在语言中学习西方的思想和文化,同样,在计算机程序语言中学习计算机的设计理念和概念,这就是《像计算机学家一样思考Python》一书所倡导的学习理念,在程序中去学习,可以是数学可以是操作系统可以是网络,一个一个的问题解决,最后什么看似深刻的原理都明白了;
通过学习汇编语言,明白了cpu是怎样工作的,明白了程序的底层,这就是通过语言学到的理念。
不难,多看,多思考,多总结。