第十四章 CALL 和 RET 指令
● 与总线相连的各设备芯片中均有一组可供 CPU 访问的寄存器,总线除了连接存储器芯片外还有:① 各种接口卡(网卡、显卡、声卡)的芯片;② 主板接口芯片,用于访问部分外设;③ 其他芯片,用于存储相关系统信息及处理输入输出
● CPU 定位设备地址范围为 0 ~ 65535,共 64 KB。端口读写指令只有两条,in 和 out,分别用于读取和写入,用 ax 或 al 来存放转移数据。
● 地址 0 ~ 255 设备读写时可以使用立即数作地址,地址 256 ~ 65536 设备读写时端口号放在 dx 中
1 in al, 20h ; 20h 端口读取 1 Byte 数据,放入 al 2 out 20h, al ; 20h 端口写入 1 Byte 数据,来自 al,注意操作数顺序 3 4 mov dx, 0100h ; 大地址读写 5 in al, dx 6 out dx, al
● CMOS RAM 芯片,特性:
① 包含一个实时钟
② 靠纽扣电池供电
③ 包含128 Byte 的 RAM 存储器,00h ~ 0dh 单元存储时间信息,其他部分存储系统配置信息供启动时 BIOS 读取,BIOS 中有相关例程可以编辑 CMOS 信息。
④ 端口地址 70h 和 71h,前者存放目标 RAM 地址,后者存放读写数据。CPU 读写分两步,先向 70h 写入地址,再对 71h 进行读写操作。
● shl 和 shr 命令,当位移数大于 1 时 要将位移数放入 cl 中
● BCD 码:用四位二进制数表示两位十进制数码的方法,每两位十进制数(如36)分别写成二进制后拼成 1 Byte(00110110)
● CMOS 中,年月日时分秒 6 个信息长度均为 1 Byte,存放地址:秒(0)分(2)时(4)日(7)月(8)年(9),
● 代码,从 CMOS 中读取时间,在屏幕上动态输出(之前一直不能用的输出方式,现在能用了)
1 assume cs:code 2 code segment 3 s1: db 9, 8, 7, 4, 2, 0 ; 各数据的位置 4 s2: dw 0, 0, 0, 0, 0, 0 5 s3: db '/', '/', ' ', ':', ':', ' ' ; 用于输出的字符串 6 7 start: 8 mov ax, cs 9 mov ds, ax 10 mov bx, offset s1 11 mov si, offset s2 12 13 mov cx, 6 ; 一个时间要读 6 次得到 14 reads: 15 push cx 16 mov al, [bx] ; 取所需数据的地址 17 out 70h, al ; 读取数据 18 in al, 71h 19 mov ah, al ; ah 存十位数,al 存个位数 20 mov cl, 4 21 shr ah, cl 22 and al, 00001111b 23 24 add ah, 30h ; 十六进制数转 ASCII 码 25 add al, 30h 26 mov [si], ah ; 保存读取的数据 27 mov [si+1], al 28 inc bx ; 指向下一个数据 29 add si, 2 30 pop cx 31 loop reads 32 33 mov ax, 0b800h ; 数据移动到特定内存用于显示,es : bx 表示终端第 12 行 10 列 34 mov es, ax 35 mov bx, 160 * 12 + 10 * 2 36 mov si, offset s2 37 mov di, offset s3 38 39 mov ah, 2 ; 指定颜色 40 mov cx, 6 41 show: 42 mov al, [si] ; 每读两位 s2(用 si ),读一位 S3(用 di) 43 mov es:[bx], al 44 mov es:[bx+1], ah 45 add bx, 2 46 mov al, [si+1] 47 mov es:[bx], al 48 mov es:[bx+1], ah 49 add bx, 2 50 mov al, [di] 51 mov es:[bx], al 52 mov es:[bx+1], ah 53 54 add bx, 2 ; 移动指针 55 add si, 2 56 inc di 57 loop show 58 59 in al, 60h ; 键盘输入 60 cmp al, 10h ; 检查输入是否为 'q' 61 jne start ; 循环 62 63 mov ax, 4c00h 64 int 21h 65 code ends 66 67 end start
第十五章 外中断
● CPU 在执行完一条指令后可以检测外中断信息,引发中断过程,处理外设输入
● 标志寄存器 IF =1 时 CPU 响应外中断,IF = 0 时 CPU 屏蔽外中断不响应
● 指令 sti 置标志寄存器 IF 为 1;cti 置标志寄存器 IF 为 0
● 8086 CPU 中,几乎所有外设引发的终端都是可屏蔽中断,不可屏蔽中断的终端类型码固定为 2
● 键盘中断特性:
① 按下或放开一个键,键盘产生相应的扫描码;
② 按下产生通码,松开产生断码,同一按键,断码 = 通码 + 20h;
③ 按下或放开一个键,键盘芯片向 60h 端口寄存器写入扫描码;
④ 相关芯片向 CPU 引发 9 号中断码;
⑤ BIOS 键盘缓冲区可以存储 15 个键盘输入等待 CPU 响应,每个键盘输入为 1 个字,高字节存放扫描码,低字节存放字符码;
⑥ 0400 : 0017 存放键盘状态字节,该字节的各位记录辅助按键的状态:Insert(7),CapsLock(6),NumLock(5),ScrollLock(4),Alt(3),Ctrl(2),左 Shift(1),右 Shift(0)
● 代码,在屏幕上逐个显示 a ~ z,期间按下 Esc 改变字体颜色
1 assume cs:code 2 3 stack segment 4 db 128 dup (0) 5 stack ends 6 7 data segment 8 dw 0, 0 9 data ends 10 11 code segment 12 start: 13 mov ax, stack 14 mov ss, ax 15 mov sp, 128 16 mov ax, data 17 mov ds, ax 18 mov ax, 0 19 mov es, ax 20 21 push es:[9*4] ; 原 int 9 中断例程的入口地址保存到 0000 : 0200 22 pop ds:[0] ; 使用栈中转,把 es : [] 指向的内存放入 ds : [] 23 push es:[9*4+2] 24 pop ds:[2] 25 26 cli ; 不需要等待键盘输入时,及时置屏蔽中断标志位 IF = 0 27 28 mov word ptr es:[9*4], offset int9 ; 设置新的 int 9 程序的入口地址 29 mov es:[9*4+2], cs 30 31 sti ; 需要键盘输入时,置屏蔽中断标志位 IF = 1 32 33 mov ax, 0b800h ; 最初显示 34 mov es, ax 35 mov ah, 'a' 36 s: 37 mov es:[160*12+40*2], ah ; 循环显示 38 call delay 39 inc ah 40 cmp ah, 'z' 41 jna s 42 mov ax, 0 43 mov es, ax 44 45 cli 46 47 push ds:[0] ; 将中断向量表中 int 9 中断例程的入口恢复为原来的地址 48 pop es:[9*4] 49 push ds ; [2] 50 pop es ; [9*4+2] 51 52 sti 53 54 mov ax, 4c00h 55 int 21h 56 57 delay: 58 push ax ; 延时 59 push dx 60 mov dx, 1h ; 使用 dx : ax = 1000000000 向下递减,减到零时退出 61 mov ax, 0 62 s1: 63 sub ax, 1 64 sbb dx, 0 65 cmp ax, 0 66 jne s1 67 cmp dx, 0 68 jne s1 69 pop dx 70 pop ax 71 ret 72 73 int9: 74 push ax 75 push bx 76 push es 77 78 in al, 60h ; 读取键盘 79 80 pushf 81 82 call dword ptr ds:[0] ; 调用原 int 9 中断例程 83 84 cmp al, 1 85 jne int9ret 86 87 mov ax, 0b800h 88 mov es, ax 89 inc byte ptr es:[160*12+40*2+1] ; 属性值增加 1,从前景色开始改变颜色 90 91 int9ret: 92 pop es 93 pop bx 94 pop ax 95 iret 96 97 code ends 98 99 end start
第十六章 直接址表(即表驱动查找)
● 地址标号与数据标号
1 code segment 2 a: db 1, 2, 3, 4, 5, 6, 7, 8 ; 地址标号,仅表地址,只能在代码段中使用 3 4 mov si, OFFSET a ; 要用 OFFSET 来计算 5 mov al, cs:[si] 6 7 ... 8 ;---------------------------------------------------------------- 9 code segment 10 a db 1, 2, 3, 4, 5, 6, 7, 8 ; 数据标号,表地址和内存单元长 11 12 mov si, 0 ; 直接使用数组名和偏移地址来取 13 mov al, a[si] 14 15 ... 16 ;---------------------------------------------------------------- 17 code segment 18 a db 1, 2, 3, 4, 5, 6, 7, 8 19 b dw 0, 0, 0, 0 20 c dw a, b ; c 中存储 a 和 b 的偏移地址 21 ; 等价于 c dw OFFSET a, OFFSET b 22 d dd a, b ; d 中存储 a 和 b 的短地址和偏移地址 23 ; 等价于 d dd OFFSET a, SEG a, OFFSET b, SEG b
第十七章 使用 BIOS 进行键盘输入和磁盘读写
● BIOS int 16h 中断例程 0 号子程序,从键盘缓冲区读取一个输入,并将其从缓冲区中删除,若缓冲区为空,则循环等待直到有数据。调用时 mov ah,0 int 16h ,返回 ah 为扫描码,al 为 ASCII 码
● 代码,简单的字符串输入程序,能够输入、删除
1 assume cs:code 2 3 code segment 4 start: 5 call getstr 6 7 return: 8 mov ax, 4c00h 9 int 21h 10 11 getstr: ; 接收字符串 12 push ax 13 14 getstrs: 15 mov ah, 0 16 int 16h 17 18 cmp al, 20h 19 jb nochar ; ASCII < 0,不是字符 20 mov ah, 0 21 call charstack ; 字符入栈 22 mov ah, 2 23 call charstack ; 显示栈中的字符 24 jmp getstrs 25 26 nochar: ; 输入非字符键 27 cmp ah, 0eh ; 退格键 28 je backspace 29 cmp ah, 1ch ; 回车键 30 je enter 31 jmp getstrs 32 33 backspace: ; 输入退格键 34 mov ah, 1 35 call charstack ; 字符出栈 36 mov ah, 2 37 call charstack ; 打印栈中字符串 38 jmp getstrs 39 40 enter: ; 输入回车键 41 mov al, 0 42 mov ah, 0 43 call charstack ; 0 入栈 44 mov ah, 2 45 call charstack ;显示栈中的字符 46 47 pop ax 48 ret 49 50 charstack: ; 实现与栈相关功能的程序 51 jmp short charstart 52 table dw charpush, charpop, charshow ; 驱动表,0 入栈,1 出栈,2 打印栈 53 top dw 0 ; 栈顶 54 charstart: 55 push bx 56 push dx 57 push di 58 push es 59 60 cmp ah, 2 61 ja sret ; >2 的非法输入,直接结束 62 mov bl, ah ; [0, 1, 2] -> [0, 2, 4] 63 mov bh, 0 64 add bx, bx 65 jmp word ptr table[bx] ; 每个表项占 4 字节 66 67 charpush: ; al 入栈 68 mov bx, top 69 mov [si][bx], al 70 inc top 71 jmp sret 72 73 charpop: ; 出栈到 al 74 cmp top, 0 75 je sret 76 dec top 77 mov bx, top 78 mov al, [si][bx] 79 jmp sret 80 81 charshow: ; 显示栈中的内容 82 mov bx, 0b800h 83 mov es, bx 84 mov al, 160 85 mov ah, 0 86 mul dh 87 mov di, ax 88 add dl, dl 89 mov dh, 0 90 add di, dx 91 92 mov bx, 0 93 94 charshows: 95 cmp bx, top 96 jne noempty 97 mov byte ptr es:[di], ' ' 98 jmp sret 99 100 noempty: 101 mov al, [si][bx] 102 mov es:[di], al 103 mov byte ptr es:[di+2], ' ' 104 inc bx 105 add di, 2 106 jmp charshows 107 108 sret: 109 pop es 110 pop di 111 pop dx 112 pop bx 113 ret 114 115 code ends 116 end start
● 代码,从硬盘读取数据
1 assume cs:code 2 3 code segment 4 start: 5 mov ax, 0 6 mov es, ax 7 mov bx, 200h ; es : bx 指向接收数据的内存区 8 9 mov al, 1 ; 要读取的扇区数,3.5 英寸盘一个扇区 512 Byte 10 mov ch, 0 ; 磁道号,从0开始 11 mov cl, 1 ; 扇区号,从1开始 12 mov dl, 1 ; 驱动器号,0:软驱A,1:软驱B,硬盘从 80h 开始,80h:硬盘C,81h:硬盘D 13 mov dh, 0 ; 磁头号,对于软盘即面号 14 mov ah, 2 ; int 13h 的 2 号子程序,读取数据 15 int 13h ; 读取硬盘,操作成功:ah 为 0,al 为 读取的扇区数;操作失败:ah 为 出错代码 16 17 return: 18 mov ax, 4c00h 19 int 21h 20 21 code ends 22 end start
● 代码,内存数据写入硬盘
1 assume cs:code 2 3 code segment 4 start: 5 mov ax, 0 6 mov es, ax 7 mov bx, 200h ; es : bx 指向写入磁盘的数据的内存区 8 9 mov al, 1 10 mov ch, 0 11 mov cl, 1 12 mov dl, 1 13 mov dh, 0 14 mov ah, 3 ; int 13h 的 3 号子程序,写入数据 15 int 13h ; 写入硬盘 16 17 return: 18 mov ax, 4c00h 19 int 21h 20 21 code ends 22 end start