先说明下左移右移得指令
SHL
(左移<<)
SHR
(右移>>)
左移就是将寄存器中的二进制整体向左移动一位,在低位补0
,被移动出去的那一位数放到CF
标志位中,右移则相反,移动出去的一位也是放到CF
中,例如
左移
mov al,1000 0001
shl al,1 此时al=0000 0010 CF=1
右移
mov al,1000 0001
shr al,1 此时al =0100 0000 CF=1
移动4次
shl al,4
那么它有什么作用呢?
操作系统中有一块芯片叫CMOS
,存储了bios
程序的配置以及一些主板信息,还有时间,那么这里的时间使用的是一种特殊的编码叫BCD
码:使用4位表示的数字,那么cpu
处理都是以一个字节8
位为单位,那么它如何读取4
位的数据呢?即
0011 1001
十六进制 39
十进制57
但是它却表示十进制的39
此时使用端口命令从CMOS
读取出来的数据存放在al
中,那么如何让它表示十进制的39
呢?
所以把 0011 1001
保存到 ah
中然后对ah
进行右移4位
- al = 0011 1001
- ah=0000 0011
然后将al
与0000 1111
做与运算即and al,00001111
即将高位抹除这样就得到两个独立的数据 一个是3
一个是9
然后将两个数加上30H
拼接展示到屏幕上。
这里也许有人会说直接将十六进制当十进制用不就可以了吗?干嘛搞那么麻烦?
哈哈,这个还真不行,在屏幕上展示出来的字符是ascii码,所以要想将这个数据展示到屏幕上还得将它转换成ascii码即加上 30H。那么你试试看显示多少?
而这里使用BCD
码原因也很简单,节省内存啊,以前的内存是很宝贵的,都抠到bit
上了,真的是抠。在想想自己写java代码动不动就是int、long
,是不是很浪费。但是在那个年代,真的是每一个bit
都被用到淋漓进制,好接下来我们写个程序读取当前时间显示到屏幕上
在写这个程序前还得介绍两个命令
in
cpu
从端口读入数据
out
cpu
向端口写数据
这里都是相对cpu而言的读入与写出。
还有CMOS
相关的知识,cmos
有128
个字节,那么cpu
要使用in,out
指令拿数据的时候要拿哪个位置的呢?,所以cmos
提供了两个端口
70H
告诉cmos
我要哪个字节
71H
从这个端口将字节拿出来
这个跟总线的概念是一样的。
- 控制总线告诉内存我要读还是要写
- 地址总线告诉内存我操作的地方在哪
- 数据总线传输数据
在cmos中时间所在的字节入下表
秒 | 分 | 时 | 日 | 月 | 年 |
---|---|---|---|---|---|
0 | 2 | 4 | 7 | 8 | 9 |
我们先看效果,不断循环输出时间
代码如下,比较简单
DATAS SEGMENT
;此处输入数据段代码
DATAS ENDS
STACKS SEGMENT
;此处输入堆栈段代码
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
show_loop:
call init
mov cx,3
mov ax,4
show:
call show_time
sub ax,2
add di,6
loop show
jmp show_loop
MOV AH,4CH
INT 21H
;=======================================
init:
mov bx,0B800H
mov es,bx
mov di,160*10+30*2
ret
show_time:
push ax
out 70H,al
in al,71H
mov ah,al
shr ah,1
shr ah,1
shr ah,1
shr ah,1
and al,00001111B
add al,30H
add ah,30H
mov bx,00100001B
mov es:[di+0],ah
mov es:[di+1],bx
mov es:[di+2],al
mov es:[di+3],bx
cmp cx,1
je show_ret
mov ax,3AH
mov es:[di+4],ax
mov es:[di+5],bx
show_ret:
pop ax
ret
;=======================================
CODES ENDS
END START