各种存储器都和CPU的地址线,数据线,控制线相连.CPU把它们都当做内存来对待, 把它们总地看做一个由若干存储单元组成的逻辑存储器
, 这个逻辑存储器我们称其为内存地址空间
在PC机系统中, 和CPU通过总线相连的芯片除各种存储器外, 还有以下3种芯片.
- 各种接口卡(如网卡,显卡)上的接口芯片, 它们控制接口卡进行工作
- 主板上的接口芯片卡, CPU通过它们对部分外设进行访问
- 其他芯片, 用来存储相关的系统信息, 或进行相关的输入输出处理
这些芯片中, 都有一组可以有CPU读写的寄存器.
这些寄存器的共同点:
- 都和CPU的总线相连, 这种连接是通过它们所在的芯片进行的
- CPU对它们进行读或写的时候都通过控制线向它们所在的芯片发出端口读写命令
从CPU的角度, 将这些寄存器都当做端口
, 对它们进行统一编址
, 从而建立了一个统一的端口地址空间.每一个端口在地址空间都有一个地址
CPU可以直接读写以下3个地方的数据:
- CPU内部的寄存器
- 内存单元
- 端口
- 在PC系统中, CPU最多可以定位64KB个不同的端口. 端口地址的范围为
0~65535
- 对端口的读写不能使用mov,push,pop等内存读写指令
- 从端口读取数据in, 往端口写入数据out
CPU执行内存访问指令和端口访问指令时候, 总线上的信息:
- 访问内存
mov ax,ds:[8] ;假设执行前(ds)=0
;执行时总线相关的操作如下
;1. CPU通过地址线将地址信息8发出
;2. CPU通过控制线发出内存读命令, 选中存储器芯片, 并通知它, 将要从中读取数据
;3. 存储器将8号单元中的数据通过数据线送入CPU
- 访问端口
in al,60h ;从60h号端口读入一个字节
;执行时总线相关的操作如下
;1. CPU通过地址线将地址信息60h发出
;2. CPU通过控制线发出端口读命令, 选中端口所在芯片, 并通知它, 将要从中读取数据
;3. 端口所在的芯片将60h端口中的数据通过数据线送入CPU
注意:
- 在in和out指令中, 只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据
- 访问8位端口是用al, 访问16位端口时用ax
; 对8位0~255 以内的端口进行读写:
in al,20h ;从20h端口读入一个字节
out 20h,al ;往20h端口写入一个字节
; 对 256~65535 的端口进行读写, 端口号放在dx中:
mov dx, 3f8h ;将端口号3f8h送入dx
in al,dx ;从3f8h端口读入一个字节
out dx,al ;向3f8h端口写入一个字节
PC机中, 有一个CMOS RAM芯片, 一般简称为CMOS. 此芯片特征如下
- 包含一个实时钟和一个有128个存储单元的RAM存储器
- 该芯片靠电池供电. 所以, 关机后其内部的实时钟仍可正常工作, RAM中的信息不丢失
- 128个字节的RAM中, 内部实时钟占用
0~0dh
单元来保存时间信息, 其余大部分单元用于保存系统配置信息, 供系统启动时BIOS程序读取. BIOS也提供了先关的程序, 使我们可以在开机的时候配置CMOS RAM中的系统信息. - 该芯片内部有两个端口(70h和71h),CPU通过这两个端口来读写CMOS RAM
- 70h为地址端口, 存放要访问的CMOS RAM单元的地址;71h端口为数据端口, 存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据
- CPU对CMOS RAM的读写分为两步进行(如: 读取CMOS RAM的2号单元)
- 将2送入端口70h
- 从端口71h独处2号单元的内容
- CPU对CMOS RAM的读写分为两步进行(如: 读取CMOS RAM的2号单元)
shl是逻辑左移指令:
- 将一个寄存器或内存单元中的数据向左移位
- 将最后移出的一位写入CF中
- 最低位用0补充
mov al,01001000b
shl al,1 ;将al中的数据左移一位, 执行后(al)=10010000b, CF=0
shl al,1 ;将al中的数据左移一位, 执行后(al)=00100000b, CF=1
如果移动位数大于1, 必须将移动位数放在cl中
mov al,01010001b
mov cl,3
shl al,cl ;将al中的数据左移3位, 执行后(al)=100001000b, 因为最后移出的一位是0, 所以CF=0
shr是逻辑又移指令, 和shl所进行的操作相反:
- 将一个寄存器或内存单元中的数据向又移位
- 将最后移出的一位写入CF中
- 最高位用0补充
在CMOS RAM中, 存放着当前的时间: 年月日时分秒. 这6个信息的长度都为一个字节
时间 | 存放单元 |
---|---|
秒 | 0 |
分 | 2 |
时 | 4 |
日 | 7 |
月 | 8 |
年 | 9 |
数据以BCD码的方式存放. BCD码是以4位二进制数表示十进制数码的编码方法(一个字节可以表示两个BCD码)
编程, 在屏幕中间显示当前的月份
分析:
- 从CMOS RAM的8号单元中读出当前月份的BCD码
mov al,8 out 70h,al ;向地址端口70h写入要访问的单元地址 in al,71h ;从数据端口71中取得指定单元中的数据
- 将BCD码表示的月份以十进制的形式显示到屏幕上
- BCD码值=十进制数码值, BCD码值+30h=十进制数对应的ASCII码
- 解析获取到的BCD码值, 高4位代表十位, 低4位代表各位
assume cs:code
code segment
start:
mov al,8
out 70h,al
in al,71h
mov ah,al ;al中是从CMOS RAM的8号单元读出的数据(即月份)
mov cl,4
shr ah,cl ;分离出al中的高4位, 保存到ah中
and al,00001111b ;分离出al中的低4位, 保存到al中
add ah,30h
add al,30h
mov bx,0b800h
mov es,bx
mov cl,2 ;设置颜色属性为绿色
mov byte ptr es:[160*12+40*2],ah
mov byte ptr es:[160*12+40*2+1],cl
mov byte ptr es:[160*12+40*2+2],al
mov byte ptr es:[160*12+40*2+2+1],cl
mov ax,4c00h
int 21h
code ends
end start
编程, 以"年/月/日 时:分:秒"的格式, 显示当前的日期, 时间.
分析:
- 将从CMOS RAM获取时间的逻辑抽离到子程序中
- 依次将
年/月/日 时:分:秒
写到显示缓冲区中
assume cs:code
code segment
unit: db 9,8,7,4,2,0 ;存储需要获取的时间单元
format: db '// ::',0 ;存储每个时间单元带的格式
start:
mov ax,cs
mov ds,ax
mov bx,offset unit
mov si,offset format
mov ax,0b800h
mov es,ax
mov bp,160*2+5*2 ;偏移变量 第2行5列开始
mov di,0 ;偏移变量
mov cx,6 ;设置循环次数为6
run:
mov al,[bx]
call time ;获取CMOS RAM中的时间单元
mov es:[bp][di],ah
inc di
mov byte ptr es:[bp][di], 2 ;绿色
inc di
mov es:[bp][di],al
inc di
mov byte ptr es:[bp][di], 2 ;显示时间
inc di
inc bx
call sformat ;追加显示格式
loop run
mov ax,4c00h
int 21h
sformat: cmp byte ptr ds:[si], 0;
je sfok
mov al, ds:[si]
mov es:[bp][di],al
inc di
mov byte ptr es:[bp][di], 2 ;绿色
inc di
inc si
sfok: nop
ret
;---------
; 需要取的时间单元放入到al寄存器中
; 结果: ah保存十位的ASCII码, al保存个位的ASCII码
;---------
time:
push cx
out 70h,al
in al,71h
mov ah,al ;al中是从CMOS RAM的8号单元读出的数据(即月份)
mov cl,4
shr ah,cl ;分离出al中的高4位, 保存到ah中
and al,00001111b ;分离出al中的低4位, 保存到al中
add ah,30h
add al,30h
pop cx
ret
code ends
end start
修改时钟的小时数为20
.
以下代码思路虽然正确, 但是并没有修改成功
可能不是纯DOS环境或其他原因造成的, ...后续有结果之后在进行试验
assume cs:code
code segment
mov ax,0b800h
mov es,ax
mov cl,2 ;绿色
mov al,4 ;目标为小时数
call time
mov byte ptr es:[2*160+10*2], ah
mov byte ptr es:[2*160+10*2+1], cl
mov byte ptr es:[2*160+10*2+2], al
mov byte ptr es:[2*160+10*2+3], cl
;修改前,读取小时数
mov al,4 ;目标为小时数
out 70h,al ;单元7送入70h, 表示即将修改时
mov al,00100000b ;BCD码 20
out 71h,al ;送入到71h
mov al,4 ;目标为小时数
mov cl,2 ;绿色
call time
mov byte ptr es:[3*160+10*2], ah
mov byte ptr es:[3*160+10*2+1], cl
mov byte ptr es:[3*160+10*2+2], al
mov byte ptr es:[3*160+10*2+3], cl
;修改后,读取小时数
mov ax,4c00h
int 21h
;---------
; 需要取的时间单元放入到al寄存器中
; 结果: ah保存十位的ASCII码, al保存个位的ASCII码
;---------
time:
push cx
out 70h,al
in al,71h
mov ah,al ;al中是从CMOS RAM的n号单元读出的数据
mov cl,4
shr ah,cl ;分离出al中的高4位, 保存到ah中
and al,00001111b ;分离出al中的低4位, 保存到al中
add ah,30h
add al,30h
pop cx
ret
code ends
end