关于西门子间接寻址的一些理解

今天不研究高大上的,研究一个接近底层的东西,叫间接寻址。它很基础,基础到我们无时无刻都在用,但是同时它也很难理解,因为其实寄存器的概念已经被渐渐隐藏了。

1. 地址的概念

我们通常提到的物理输入输出印象区P,印象输入区I,印象输出区Q,位存储区M,定时器T,计数器C,数据区DB,背景数据区DI,本地数据区L。
每个区域(除T/C)都可以用BIT,BYTE,WORD,DWORD来指定它们的大小。
区域+区域位置和长度,其实构成了我们描述一个地址的必要条件。

  • 存储的区域
  • 这个区域中具体的位置
1
2
3
4
5
6
7
8
9
10
//例子: A Q2.0
A = 指令符;
Q = 输出印象区;
2.0 = 输出印象区的第二个字节的第0位;

//例子: DBX200.0
DB = 数据区;
X = 位;
200 = 具体位置
0 = 具体的位位置

所以一个地址的确切构成应该是<存储区符号><存储区尺寸符号><尺寸数值>.<位数值>。把<存储区符号><存储区尺寸符号>统一看作<地址标志符>,那么地址组成又可以写成**<地址标志符><数值单元>**。

2. 间接寻址

  • 间接寻址对应的是直接寻址,那什么是直接寻址?
    直接给出指令的确切操作数就叫直接寻址。
    地址的最小单元是BIT。
1
2
//直接操作Q20.0的地址,直接寻址
A Q20.0
  • 对应的,间接给出的寻址方式。就是间接寻址。
    用[ ]标明的内容,间接指代地址
    其中MD100和DBW100被称为指针Pointer,它指向它们包含的数值所代表的地址
    1
    2
    3
    //间接访问地址,间接寻址
    A Q[MD100]
    A T[DBW100]
  • 指针格式(存储器间接寻址
    1. 存储器间接寻址具有两个指针格式:单字和双字。单字指针是一个16bit的结构,从0-15bit,指示一个从0-65535的数值,这个数值就是被寻址的存储区域的编号。双字指针是一个32bit的结构,从0-2bit,共三位,按照8进制指示被寻址的位编号,也就是0-7;而从3-18bit,共16位,指示一个从0-65535的数值,这个数值就是被寻址的字节编号。*这句话说明相比于单字指针,双字指针可以多表达0-7bit的位编号*
    2. 指针可以存放在M、DI、DB和L区域中,也就是说,可以用这些区域的内容来做指针。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      //
      L DW#16#35 //将3216进制数35存入ACC1
      T MD2 //这个值再存入MD2,这是个32位的位存储区域
      L +10 //将16位整数10存入ACC1,3216进制数35自动移动到ACC2
      T MW100 //这个值再存入MW100,这是个16位的位存储区域
      OPN DBW[MW100] //打开DBW10。这里的[MW100]就是个单字指针,存放指针的区域是M区,MW100中的值10,就是指针间接指定的地址,它是个16位的值!
      //
      L L#+10 //以32位形式,把10放入ACC1,此时,ACC2中的内容为:16位整数10
      T MD104 //这个值再存入MD104,这是个32位的位存储区域
      A I[MD104] //对I1.2进行与逻辑操作!
      =DIX[MD2] //赋值背景数据位DIX6.5
      //
      A DB[MW100].DBX[MD2] //读入DB10.DBX6.5数据位状态
      =Q[MD2] //赋值给Q6.5
      //
      A DB[MW100].DBX[MD2] //读入DB10.DBX6.5数据位状态
      =Q10 //错误!!没有Q10这个元件
      通过上面发现:双字指针描述byte.bit的这种结构,单字指针只能描述存储区域。
      换句话说,单字指针不能描述P,I,Q,L这些区域,因为描述它们要精确到bit。
      对双字指针而言,似乎一切都好,但是也有一个限制,对于非位区域寻址的时候,它的描述必须要保全其0-2bit都是0,也就是说对于非位区域,它的的精度到不了位
      也正是由于双字指针是一个具有位的指针,因此,当对字节、字或者双字存储区地址进行寻址时,必须确保双字指针的内容是8或者8的倍数

上诉例子中有段代码是

1
A I[MD104] //对I1.2进行与逻辑操作!

那么,为什么是I1.2呢?

1
2
3
4
5
6
原因:
通过L L#+10,MD104应该是等于十进制的10,转换为32位二进制数如下:
MD104 = 2#0000 0000 0000 0000 0000 0000 0000 1010
按照双字指针的概念,3-18bit指定byte,0-2bit指定最终要操作的位,那么计算出来的结果就应该是1.2
按照双字指针的概念,3-18bit指定byte,0-2bit指定最终要操作的位,那么计算出来的结果就应该是1.2
按照双字指针的概念,3-18bit指定byte,0-2bit指定最终要操作的位,那么计算出来的结果就应该是1.2
  • 指针格式(寄存器间接寻址
    1. 存储器间接寻址寄存器间接寻址有什么不一样:
      在先前所说的存储器间接寻址中,间接指针用M、DB、DI和L直接指定,就是说,指针指向的存储区内容就是指令要执 行的确切地址数值单元。但在寄存器间接寻址中,指令要执行的确切地址数值单元,并非寄存器指向的存储区内容,也就是说,寄存器本身也是间接的指向真正的地址数值单元。

    2. 寄存器间接寻址的途径

      • 区域内寄存器间接寻址
      • 区域间寄存器间接寻址
    3. 寄存器间接寻址的格式

      • <地址标志符><寄存器,P#byte.bit>
1
2
3
//举个例子
DIX[AR1,P#1.5] //要寻址的地址区域事先已经确定(DI),AR1可以改变的只是这个区域内的确切地址数值单元,所以我们称之为区域内寄存器间接寻址方式,相应的,这里的[AR1,P#1.5] 就叫做区域内寻址指针。
X[AR1,P#1.5] //要寻址的地址区域和确切的地址数值单元,都未事先确定,只是确定了存储大小,这就是意味着我们可以在不同的区域间的不同地址数值单元以给定的区域大小进行寻址,所以称之为:区域间寄存器间接寻址方式,相应的,这里的[AR1,P#1.5] 就叫做区域间寻址指针。
  • AR的格式
    地址寄存器AR是专门用于寻址的一个特殊指针区域,西门子的地址寄存器一共2个,AR1和AR2,每个32位
    • 区域内寻址寄存器里面,AR的内容只是指代数值单元,因为其他的已经确认,所以这种情况的的AR等同于存储寄存器中的双字指针,也就是0-2指代bit,3-18指代byte字节,其中第31位固定为0。
    • 区域间寻址寄存器31位固定为1,24-26bit可以被定义用于指定存储区域,它其实是一种我们常见的区域划分标志(如B#16#84表示DB区的概念就来自于此)。
1
2
3
4
5
6
7
8
9
# 比较区域内寻址寄存器和区域间寻址寄存器的格式差别
区域内寻址寄存器:
AR:0000 0000 0000 0BBB BBBB BBBB BBBB BXXX

区域间寻址寄存器:
AR:1000 0YYY 0000 0BBB BBBB BBBB BBBB BXXX

# 差别1:区域间的第31位固定为1了。
# 差别2:区域间的第24-26位可以被定义和修改了。
  • 区域标志符(区域间寻址寄存器24-26位)
    • 我们一定不会陌生的知识如下:

      1. 数据区P,B#16#80(2#000)
      2. 数据区I,B#16#81(2#001)
      3. 数据区Q,B#16#82(2#010)
      4. 数据区M,B#16#83(2#011)
      5. 数据区DB,B#16#84(2#100)
      6. 数据区DI,B#16#85(2#101),表示背景DB
      7. 数据区L,B#16#86(2#111)
    • 当我们把这些AR的内容全部表达出来:

      1. 数据区P,B#16#800xxxxx
      2. 数据区I,B#16#810xxxxx
      3. 数据区Q,B#16#820xxxxx
      4. 数据区M,B#16#830xxxxx
      5. 数据区DB,B#16#840xxxxx
      6. 数据区DI,B#16#850xxxxx
      7. 数据区L,B#16#860xxxxxx

我们从上面能得到一个初步结论:果AR中的内容是8开头,那么就一定是区域间寻址。

1
2
3
4
5
6
7
8
9
10
//举个例子
AR = 16#840000D4
//我们可以转换位二进制显示
AR = 2# 1000 0100 0000 0000 0000 0000 1101 0100
// bit31=1,表示区域间寄存器寻址
// bit24-26=100,表示DB
// bit3-18=26,表示区域位置是26
// bit0-2=100,表示4
//合计如下
AR表达的地址是DBX26.4
  • P#指针是什么
    P#中的P是Pointer,是个32位的直接指针。所谓的直接,是指P#中的#后面所跟的数值或者存储单元,是P直接给定的。
    1
    2
    3
    4
    5
    6
    7
    8

    L P#Q1.0 //把Q1.0这个指针存入ACC1,此时ACC1的内容=82000008(hex)=Q1.0
    L P#1.0 //把1.0这个指针存入ACC1,此时ACC1的内容=00000008(hex)=1.0(这是个区域内寄存器寻址指针,如X[AR1,P#1.5])
    L P#MB100 //错误!必须按照byte.bit结构给定指针。
    L P#M100.0 //把M100.0这个指针存入ACC1,此时ACC1的内容=83000320(hex)=M100.0
    L P#DB100.DBX26.4 //错误!DBX已经提供了存储区域,不能重复指定
    L P#DBX26.4 //把DBX26.4这个指针存入ACC1,此时ACC1的内容=840000D4(hex)=DBX26.4
    //对P#指定带有存储区域时,累加器中的内容和区域间寻址指针内容完全相同。事实上,把什么样的值传给AR,就决定了是以什么样的方式来进行寄存器间接寻址。

在寄存器寻址中,P#XXX作为寄存器AR指针的偏移量,用来和AR指针进行相加运算,运算的结果,才是指令真正要操作的确切地址数值单元!

  • P#的运算规则

    1
    2
    3
    4
    //运算的法则是:AR1和P#中的数值,按照BYTE位和BIT位分类相加。BIT位相加按八进制规则运算,而BYTE位相加,则按照十进制规则运算。

    对于[AR1,P#2.6],若AR1=26.4 >>>AR1(26.4)+P#(2.6)=29.7,这是区域内寄存器间接寻址的最终确切地址数值单元
    对于[AR1,P#2.6],若AR1=DBX26.4 >>>AR1(DBX26.4)+P#(2.6)=DBX29.7,这是区域间寄存器间接寻址的最终确切地址数值单元
  • AR的地址运算

    1
    2
    3
    4
    5
    6
    7
    8
    //1.直接赋值,保证直接数是32位,包含相关区域,位置,地址信息
    L DW#16#83000320
    LAR1
    //2.间接赋值,可以用存储器间接寻址指针给定AR1内容。具体内容存储在MD100中
    L [MD100]
    LAR1
    //3.指针赋值,使用P#这个32位“常数”指针赋值AR。
    LAR1 P#26.2

关于西门子间接寻址的一些理解
http://example.com/2024/07/21/关于西门子间接寻址的一些理解/
作者
xiao cuncun
发布于
2024年7月21日
许可协议