Skip to content

Latest commit

 

History

History
299 lines (240 loc) · 9.18 KB

14-端口.md

File metadata and controls

299 lines (240 loc) · 9.18 KB

端口

各种存储器都和CPU的地址线,数据线,控制线相连.CPU把它们都当做内存来对待, 把它们总地看做一个由若干存储单元组成的逻辑存储器, 这个逻辑存储器我们称其为内存地址空间

在PC机系统中, 和CPU通过总线相连的芯片除各种存储器外, 还有以下3种芯片.

  1. 各种接口卡(如网卡,显卡)上的接口芯片, 它们控制接口卡进行工作
  2. 主板上的接口芯片卡, CPU通过它们对部分外设进行访问
  3. 其他芯片, 用来存储相关的系统信息, 或进行相关的输入输出处理

这些芯片中, 都有一组可以有CPU读写的寄存器.

这些寄存器的共同点:

  1. 都和CPU的总线相连, 这种连接是通过它们所在的芯片进行的
  2. CPU对它们进行读或写的时候都通过控制线向它们所在的芯片发出端口读写命令

从CPU的角度, 将这些寄存器都当做端口, 对它们进行统一编址, 从而建立了一个统一的端口地址空间.每一个端口在地址空间都有一个地址

CPU可以直接读写以下3个地方的数据:

  1. CPU内部的寄存器
  2. 内存单元
  3. 端口

端口的读写

  • 在PC系统中, CPU最多可以定位64KB个不同的端口. 端口地址的范围为0~65535
  • 对端口的读写不能使用mov,push,pop等内存读写指令
  • 从端口读取数据in, 往端口写入数据out

CPU执行内存访问指令和端口访问指令时候, 总线上的信息:

  1. 访问内存
mov ax,ds:[8]     ;假设执行前(ds)=0


;执行时总线相关的操作如下
;1. CPU通过地址线将地址信息8发出
;2. CPU通过控制线发出内存读命令, 选中存储器芯片, 并通知它, 将要从中读取数据
;3. 存储器将8号单元中的数据通过数据线送入CPU
  1. 访问端口
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端口写入一个字节

CMOS RAM芯片

PC机中, 有一个CMOS RAM芯片, 一般简称为CMOS. 此芯片特征如下

  1. 包含一个实时钟和一个有128个存储单元的RAM存储器
  2. 该芯片靠电池供电. 所以, 关机后其内部的实时钟仍可正常工作, RAM中的信息不丢失
  3. 128个字节的RAM中, 内部实时钟占用0~0dh单元来保存时间信息, 其余大部分单元用于保存系统配置信息, 供系统启动时BIOS程序读取. BIOS也提供了先关的程序, 使我们可以在开机的时候配置CMOS RAM中的系统信息.
  4. 该芯片内部有两个端口(70h和71h),CPU通过这两个端口来读写CMOS RAM
  5. 70h为地址端口, 存放要访问的CMOS RAM单元的地址;71h端口为数据端口, 存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据
    • CPU对CMOS RAM的读写分为两步进行(如: 读取CMOS RAM的2号单元)
      1. 将2送入端口70h
      2. 从端口71h独处2号单元的内容

shl和shr指令

shl是逻辑左移指令:

  1. 将一个寄存器或内存单元中的数据向左移位
  2. 将最后移出的一位写入CF中
  3. 最低位用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所进行的操作相反:

  1. 将一个寄存器或内存单元中的数据向又移位
  2. 将最后移出的一位写入CF中
  3. 最高位用0补充

CMOS RAM中存储的时间信息

在CMOS RAM中, 存放着当前的时间: 年月日时分秒. 这6个信息的长度都为一个字节

时间 存放单元
0
2
4
7
8
9

数据以BCD码的方式存放. BCD码是以4位二进制数表示十进制数码的编码方法(一个字节可以表示两个BCD码)

编程, 在屏幕中间显示当前的月份

分析:

  1. 从CMOS RAM的8号单元中读出当前月份的BCD码
      mov al,8
      out 70h,al      ;向地址端口70h写入要访问的单元地址
      in al,71h       ;从数据端口71中取得指定单元中的数据
  2. 将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

实验14 访问CMOS RAM

编程, 以"年/月/日 时:分:秒"的格式, 显示当前的日期, 时间.

分析:

  1. 将从CMOS RAM获取时间的逻辑抽离到子程序中
  2. 依次将年/月/日 时:分:秒写到显示缓冲区中
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