第十二章 APO编程语言

第十二章      APO编程语言

APO编程语言是基于汇编语言和面向对象编程。基本指令只有7种:赋值指令、COPY指令、BTX(位X测试为1、或0转移)指令、查表跳转指令switch(RN){….}、移位与循环指令S、三操作数运算指令、调用与返回指令。所有的指令大小、除了32位立即数赋值是2字外;其它都是32位,一个字。 指令执行时间,除了32位立即数赋值是2ns、COPY指令取决于拷贝的长度外;其它指令都是1ns。 应用程序只能使用R0-R4,R8-R15的21个寄存器作为高速的寄存器局部变量;其中R0-R4作为方法参数寄存器,R0通常为返回结果寄存器。

通常,子程序的形式用C语言表达就是函数;用所谓面向对象语言表达的是函数或方法。APO使用的是汇编语言,通常用伪指令(一组指令的缩写形式宏)来描述调用指令CALL;如:方法名称(参数1,参数2,参数3,参数4,参数5); 由编译器翻译成相关的最多6条汇编指令。其实,参数i是需要对应写入寄存器Ri(Ri = R0----R4);这就需要若干指令;以上的缩写编译器会自动展开成对应的一组指令的。参数可以有一个或多个,或者没有。没有参数的形式:方法名称();编译后就是一条指令CALL。返回的结果:数值可以写到寄存器R0;也可以写到寄存器R8-R31或静态变量中去。
CALL是压入H2的R0—R4及PSR、PRR、PPC;而RET是弹出R1—R4及PSR、PRR、PPC。 R0是不被覆盖的! 所以,R1-R4可看作是自动变量。指针或数据入口参数也可以先写入R8-R31;之后,再调用方法。注意:寄存器R24-R31是系统方法使用的局部变量;用户应用程序使用时;应注意到调用系统方法时,它们可能会被改写。

方法(子程序)的编写形式如下:

方法名称:

指令1;

……

指令n;

RET;

或:方法名称{…}

一些方法的集合就构成了方法库,方法库是放在只读的保护区域;由内核管理。一些公共的方法库是常驻在保护区域的。用户进程的方法库是动态存在于保护区域,当用户进程退出时,如果用户方法库的引用计数为0;就会使 用户方法库被踢出保护区域。方法库就是一个文件形式。所谓面向对象语言表达的接口概念其实就是指对象的一个方法库。

CALL指令用来调用一个方法; 此时,下一条指令地址PPC会被压入堆栈,是压入行寄存器H0:R0—R4及PSR、PRR、PPC;以备返回时能恢复执行下条令, RET指令用来从一个方法返回;之前CALL保存的下条指令地址会从栈内弹出到H0行寄存器中,RET是弹出H0的R1—R4及PSR、PRR、PPC。程序转到CALL之前的下条指令处执行。

调用方法格式:

方法名称(参数1,参数2,参数3,参数4,参数5);

与C函数的区别:

1)、无须指明函数返回值类型;返回的值取决于方法或编程员的说明。

2)、没有复杂的形参、实参、指针、Void、数组引用等说法;全看作是32位寄存器参数。你认为是什么样的参数,那是由你决定的。

3)、无须头文件、无须函数原型声明。

4)、除了寄存器变量,就只有你声明的静态变量。

立即数可以8位或16位一组用“.”号隔开。已经声明的变量,可直接代入寄存器相关位置;实际上,是变量的地址代入寄存器相关位置。如:方法1(3, 5.5, ,6.0.1.5,AAA); 入口参数4个,写入寄存器R0、R1、R3、R4; R2不管;其中R4为变量AAA的地址。

R0-R4是方法内的自动变量,方法返回时被调用前的覆盖。通常是,在方法中,R0-R4只是作为只读的入口参数;而输出结果可用寄存器:R8—R31。在APO中,栈的用途除了CALL,RET就没有什么用了。APO只有寄存器型自动变量、静态变量(代码中申请内存分配的动态变量,本质上还是静态变量)。可以直接:如,R17 = 89; 用户自定义;不一定非要编译器分配变量空间。毕竟,寄存器空间有限;自己分配更为清晰些。使用寄存器变量的好处是无须写变量声明等;如果需要更大的空间来使用,你只能申请内存分配的动态变量了;BUxH 
new(YYY); 就可得到x行的内存空间了,对该空间的初始化方法得自己编写了。YYY的释放你无须考虑,YYY一旦不用,系统会立即回收。最好,还是一开始就分配成静态变量并初始化了,省事。

一、变量与空间

声明一个变量,实际上就是分配一个一定大小的存储空间;编译器会把变量名字翻译成该存储空间的相对逻辑地址。寄存器变量的地址是固定的实际地址,大小是32位的位容器;所以,无须声明;直接使用就行了。存储空间是有单位的,如BU1[32]  A; 声明变量A的空间是32个的1位数组,与BU32  A; 声明变量A的空间是32位的位容器,与BU1W  A; 声明变量A的空间是32位字的位容器,与BU2Z  A; 声明变量A的空间是2个16位的字符位容器;它们的意义是一样的。变量空间的寻址,是应该用修饰符.Z、.W、.E说明;比如BU256
 A;  那么A.0,是变量A的第0个位的位地址?还是变量A的第0个字符地址?还是变量A的第0个字地址?还是变量A的第0个行地址?编译器通常认为是第0位。如果说A.0.Z,那就清楚了,是变量A的第0个字符;A.3.W, 就是变量A的空间以字为单位,是第3个字地址。寄存器变量是32位的位容器;R3.0.Z简写为R3H高半字;R3.1.Z简写为R3L低半字。类似,变量空间中地址的内容赋值,是应该用修饰符 .Z、.W、.E说明;比如BU64K  A; 那么A.3.W = 1; 是赋值给变量A中第3个字地址中的字地址内容?还是字符地址内容?还是位地址内容?如果说A.3.W.1,那就清楚了,是变量A的第3个字地址的第1位;
A.3.W.W = 1; 就是赋值给变量A中第3个字地址中的字地址内容。A.3.W.H= 1; 就是赋值给变量A中第3个字地址中的高半字地址内容。A.3.E.E = 0; 就是赋值0给变量A中第3个行地址中的行地址内容。A.256.E.E= 1; 编译器报错,赋值超出变量声明范围;因变量A只是0-255行。

用户程序只是能操作自己声明的变量空间、和寄存器变量R0-R4、R8-R31;所以,没有指针变量。指针操作,如(RX)、(变量)、在用户应用程序中是不允许的;编译器会报错;指针操作会带来风险。寄存器都是32位的,如R2.230、R3.3.W、(R16)等等、编译器都是报错。但也可以分为高低2个半字操作,如R1H、R1L,和行寄存器方式H1: R8-R15, H2:R16-R23, H3: R24-R31。要注意到H3行寄存器,在调用系统方法时,会被改写;所以,尽可能不使用R24-R31。变量是有空间大小的,赋值操作不能超出其范围;否则编译器报错。

二、指令简介:

1、赋值指令:

1)、位赋值:如R3.11 = 0; A.B.C.9.W.3 = 1; A.RH.W.6 = 0;  B.R1.W.0 = 1; 等等。

2)、16位赋值:如R4L = 11; A.B.C.5.W.H = 3; 等等。

3)、32位赋值: 如R31 = 11;  A.B.C.9.E.W = 7; 等等。

4)、 行赋值:H2 = 0;  A.B.C.E= 0;  A.B.E = 1; 等等; 行只有全0,或全1赋值。

除了位数组,表中的成员变量最小是字,所以16位赋值要指明高H、低L半字。

5)、变量、寄存器相互赋值:

R9 = A.B.C.9.W;  R11 = A.B.3.W;  C.9.W = R13;  B.A.10.Z = H3; 等等。

应注意到位容器的大小;否则会有截断。通常以寄存器变量大小为准。

6)、运算赋值:只能是寄存器变量,如:R3 = ALU, #5; 实际是R3 = R3 ALU, #5;

只能是16位的立即数。

ALU有:   ADD 加法 + 、ADC 带进位加法、SUB 减法-、SBC带进位减法、CMP 比较、

DADD 十进制调整、 BIC(!N and RD) 立即数求反后再“与” & !N、

BIS(N or RD) “或|”、 AND “与&”、

BIT(N and RD  “与”但不保存)、 XOR “异或^”、

ORN(!N or RD) 立即数求反后再“或”、 RSB(N – RD)反减、

MLU 乘*、DIV 除/、 SSAT饱和运算

另一些写法:RD = ALU N; RD+、RD-; 等等,也可以。

2、COPY 指令:

将一个变量的部分内容拷贝到另一个变量,长度为立即数length、或寄存器。长度不能超过64K。有Z、W、E的3种拷贝方式。拷贝内容大小不能超过目标声明的空间大小,否则被截断。有3个参数:目标变量、源变量、长度;指令执行时,这3个参数被拷贝到专用寄存器B5、B6、B7L;B7H为编译器自动给出的范围监控参数。用户代码是不能对专用寄存器B0-B7进行写操作;只能是隐含操作。还可以使用“+、-” 2种模式;不注明通常是+模式,(B5)
= (B6); B5+、B6+、直到长度为0,-模式就是B5-、B6-、直到长度为0。在代码中,你可能第一次使用COPY时,已经设置了B5、B6;而第二次用时,想利用原设置的B5或B6;那么可以少传一个参数。但编译器会验证是否越界。耗时:3ns + n(长度)/2  ns。

如:COPY.W( A, B.3.W, #N ); 变量B的第3成员开始,拷贝N个字到变量A;如果是COPY.E,则是拷贝N行到变量A。一到多拷贝,源变量前要加#号固定;COPY.E( A, #H2, #N ); 立即数也可以是寄存器变量,如COPY.W( A, B.3.W, R1L);等等。

3、BTX(位测试为X转移)指令:

如:BT0  R3.7, #A; 如果R3的第7位为0跳到A执行。

BT1  A.B.23, #A; 如果A.B的第23位为1跳到A执行。

测试状态寄存器PSR的位后的通用指令,左右2种写法都可以。

JEQ/JZ  标号; 零位被置时转移到标号语句  等于时程序跳转  
     BT1 PSR.Z,标号;

JNE/JNZ 标号; 零位复位时转移到标号语句 不等时程序跳转   
     BT0PSR.Z,标号;

JC/JHS  标号; 进位位被置时转移到标号语句
大于或等于时程序跳转  BT1 PSR.C,标号;

JNC/JLO 标号; 进位位复位时转移到标号语句   
小于时程序跳转     BT0PSR.C,标号;

JN    标号; 负位被置时转移到标号语句       为负时程序跳转  
   BT1PSR.N,标号;

JNN   标号; 负位被清零时转移到标号语句 
大于或等于时程序跳转  BT0PSR.N,标号;

JV   标号;  溢出位被置时转移到标号语句                          BT1PSR.V,标号;

JNV  标号; 没有溢出时转移到标号语句   
                         BT0 PSR.V,标号;

JCZ  标号;  C置位,Z清零时转移到标号语句  大于时程序跳转 
      BT1
PSR.CZ,标号;

JZC  标号;  C清零, Z置位时转移到标号语句 
小于或等于时程序跳转  BT0PSR.CZ,标号;

JL   标号; N .xor. V = 1 时转移到标号      带符号小于时程序跳转 
BT1 PSR.NV,标号;

JGE  标号; N .xor. V = 0 时转移到标号
带符号大于或等于时程序跳转BT0
PSR.NV,标号;

JGT  标号; Z清零且N或V置位, 或N清零、V清零 
带符号大于时跳转 BT1
PSR.GT,标号;

JLE  标号; Z,或N置位且V零, 或N零且V 1
带符号小于或等时跳转    BT0PSR.GT,标号;

JMP  标号; 无条件转移到标号语句                                   BT1
PSR.B1,标号;

程序状态寄存器PSR = R5:

31 29 28 27 26  25 24  23 22  21 20  19  18  17  16 15 14 13 12    8   7    0

N  Z C  V   CZ ZC  LT  GE  GT LE  B1  YJ  IN  OUT      保留    执行标志    x号

N:   负号置1标志。      为负数

NN:  N = 0。             非负数,大于或等于

Z:   结果为0置1标志。  相等(EQ)

NZ:  结果为非0,Z = 0。  结果非0或不等NE

C:   进位置1标志。      大于或等于(或HS)

NC:  无进位,C = 0。     小于LO

V:   溢出位置1标志。

NV:  没有溢出, V = 0。

CZ:  C置1、结果非0(Z = 0)。  大于HI

ZC:  C = 0, 或Z = 1。           小于或等于LS

LT:  N XOR V = 1;N != V。        带符号小于LT

GE:  Z XOR V = 0;N = V。         带符号大于或等于GE

GT:  Z = 0,N或V置1;或N = 0、V = 0。       带负号大于GT

LE:  Z置位,或N置位且V= 0;或N = 0, V = 1。带符号小于或等于LE

B1:  为1。                       总是1, AL

4、查表跳转指令: switch(RN){….};

有时候,一个方法有n个分支,n值在寄存器RN;这样使用查表跳转指令,就会据RN寄存器中的值而进入到正确的分支了。RN为: R0-R4,R8-R31中的一个。

RN是一个16位或32位寄存器整形变量,可以从表达式得到。这类似于C语言switch()、Case的情形。

5、移位与循环指令S:只能是寄存器变量

S RD, RN, #N; S为下列之一,N为移动的最多32的位数。

逻辑左移<<(变进位)LSL(C); 逻辑右移>>(变进位)LSR(C);

算术右移S>>(变进位)ASR(C); 循环右移(变进位)ROR(C);

连进位一起循环右移RRX;

如:LSLC  R1, R3,#6;  R1 = R3 C<< 6,即R1 = R3左移6位,高位进C。

移位与循环指令也可以写成:RD = RN S N;

6、三操作数运算指令:只能是寄存器变量

RD = RN1 ALU RN2 S #N; // RN2移位或循环N位后与RN1运算的结果存放到RD。

7、调用与返回指令:

调用CALL则是直接写方法名,方法返回是RET。

如果要做浮点运算、或更多的其它功能就要用到系统库里的方法了;APO语言就7种基本指令。硬件模块的专用指令在用到再说,内核编程则还包含一些指针操作的指令。

三、面向对象编程

面向对象编程在上一章已经介绍很多,这里只是举一些例子。

例子1:

BU1W [100]  A;  // 数组变量A的成员都赋值为其下标。

数组成员赋值方法(){ // 指令数6W,占内存空间一行1E,运行时间:402ns。

R1 = 0;

L1:

A.R1 = R1;      // 如果已经声明A是字数组,A.R1.W.W可简写为A.R1

R1+;

CMP.W  R1, #100;

JNZ L1;

RET

}

汇编语言清晰、简单。可能有人会说,这么简单就要6行代码;其实不然,一些高级语言编译成汇编码,会远超过这里的。汇编语言,指令的空间、时间都可明确掌握、底层清晰;根本不是别的语言可以取代的!其实,简单的东西需要复杂的实现;复杂的东西只是简单的代码。这就是我们的真实世界规律!

例子2:排序

1、不带序号的数的位图式排序

先介绍多功能硬件模块的比较指令CMP与位查询指令BIX。硬件模块的256E,可以分成2部分:输入128E,输出128E;如果输入128E中符合条件的结果全部给出在输出,那么标志PSR.IN置位,请求再输入128E;如果输出128E中的结果满,那么标志PSR.OUT置位,请求读出结果。这样,就无须多次启动SS1。对于范围在64K以内的无序数列,我们可以构建一个64K位的位图变量;让数列值映射到位图变量中的相应位;再利用多功能硬件模块的位查询指令BIX,很容易就能得到按小到大的有序数列;而且速度极快。对于超过64K、大小为x的无序数列,我们都可以构建有n个64K位的位图变量;让数列映射到位图变量中的相应位;之后,使用同样的方法进行。

BU16 [X]  M, N; // 声明一个16位的无序数组变量M、和排序后结果变量N。

BU64K  WTKJ = 0; // 声明一个位图变量,内存空间256H。编译器初始化为0

范围64K内无序数列排序( M, N, X, WTKJ ){ // 占用:25W, 4E;耗时最大约:325us

R16L = 0;

PTXU1:

R16H = M.R16L; // 读入无序数列M[X]的一项, 简写M.R16L.Z.Z为M.R16L

WTKJ.R16H = 1; // 映射到位图变量WTKJ空间中的一位。

R16L+;  X-;    // X-,非零继续循环。耗时:5Xns

JNZ  PTXU1;

COPY.H( YJMK, WTKJ, 128 ); // 拷贝位图变量WTKJ的128E到硬件模块,67ns

BIX;   SS1;            // 位查询、排序。

BT1 PSR.OUT, PTXU2;  // 输出满,跳。

PT1:

COPY.H(YJMK, WTKJ.128.E, 128 ); // 拷贝位图变量WTKJ的剩下128E到硬件模块

SS1;

BT1 PSR.OUT, PTXU3;  // 输出满,跳。

PT2:

COPY.H( R1, YJMK.128.E, 128 ); // 输出结果。

PT3:

RET

PTXU2:

PTX();

JMP  PT1;

PTXU3:

PTX();

JMP PT2;         // 跳,输出最后一次结果

PTX:

COPY.H( R1, YJMK.128.E, 128 ); // 输出结果。

PT4:

R1 = B5;                       // 保存结果变量地址序号。

SS1; BT0  PSR.OUT, PT3;       //输出不满,跳返回。

COPY.H( , YJMK.128.E, 128 );  // 否,继续输出结果。

JMP PT4;                      // 循环、继续SS1、直到搜完整个位图。

}

考虑M[64K]的无序不重复数组所花时间:位映射,320000ns;位排序,4680ns。所以,耗时还是在位映射320us,位排序只是4.68us。位图排序的方法,对于有重复数的情形下;只是保留其中一个数;所以,经位图排序后是数组的成员都是唯一的。位图排序需要使用到公共资源-硬件模块,要不调用系统方法key_YJMK();清YJMK、并锁住进程调度,要不位图排序升级为系统方法。在APO系统中有位图排序方法;但最大数只能到24位,这时要使用256个64K位的位图变量;占一个数据块。32位数的位图排序,应用程序只能先做256种数组分类,再调用系统位图排序方法WTPX(
无序数组变量, 最大数值, 数组成员数 )。排序后,结果还是放在输入无序数组变量;至于位图的大小、建立那是方法据最大数值的事情。

2、带序号的数排序

这不能再用位图式排序方法了;类似数据库表中,据某字段的数值大、或小顺序排序出相应的记录组来。这只能是基数排序方法;基数是半字,那么32位的无序数组排序就要来2次;基数是字节,就要来4次,但易于硬件实现;我还在研究中。

前面章节中,都说行的符号是H;写到本章时,发现行H与高半字的H冲突;所以,行的符号改为E。没法的,写新的一章;可能全面的章节都要改动。晕倒!

时间: 2024-07-30 10:18:24

第十二章 APO编程语言的相关文章

第二十二章 TCP/IP层的实现

                      第二十二章    TCP/IP层的实现        我比较喜欢先难后易,如果把GPU显示管理.和网络管理拿下后:我会从头整理.改写一遍APO操作系统.这样,就会形成APO操作系统的锥形.也获得了全局观.内核CPU线路.和用户CPU线路,你可以将它们看成是独立的2个32位CPU核:内核CPU主要任务是实时处理.硬件中断,256个实时线程包含了一些中断程序的后半部.用户CPU主要是动态优先级进程.线程调度,各种应用程序的运行:2个核之间是通过消息交互.句

进击的Python【第十二章】:mysql介绍与简单操作,sqlachemy介绍与简单应用

进击的Python[第十二章]:mysql介绍与简单操作,sqlachemy介绍与简单应用 一.数据库介绍 什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据.我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢.所以,现在我们使用关系型数据库管理系统(RDBMS)来存储和管理的大数据量.所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来

C和指针 (pointers on C)——第十二章:使用结构和指针

第十二章 使用结构和指针 这章就是链表.先单链表,后双向链表. 总结: 单链表是一种使用指针来存储值的数据结构.链表中的每个节点包含一个字段,用于指向链表的下一个节点. 有一个独立的根指针指向链表的第1个节点.单链表只能从一个方向遍历. 如何insert单链表:1.新节点的link字段必须设置为指向它的后面节点.2.前一个节点的link字段必须指向这个新节点. 为了防止可能会插入链表的起始位置这种情况,在C中,可以保存一个指向必须进行修改的link字段的指针,而不是保存一个指向前一个节点的指针.

《构建之法》第十一、十二章学习总结

第十一章的内容是软件设计与实现. 在第一节中,讲的是关于分析和设计方法,向我们介绍在"需求分析"."设计与实现"阶段."测试""发布"阶段该搞清楚的问题. 在第二节中,讲的是关于图形建模和分析方法.在表达实体和实体之间的关系时,可以用到思维导图(Mind Map).实体关系图(ERD).UCD ;在表达数据的流动时,可以用到DFD工具:在表达控制流的时候可以用到FSM工具:前面提到的这些图形建模方法各有特点,UML却可以有一个

C primer plus 第五版十二章习题

看完C prime plus(第五版)第十二章,随带完成了后面的习题. 1.不使用全局变量,重写程序清单12.4的程序. 先贴出12.4的程序,方便对照: 1 /* global.c --- 使用外部变量 */ 2 #include <stdio.h> 3 int units = 0; //一个外部变量 4 void critic(void); 5 int main(void) 6 { 7 extern int units; 8 9 printf ("How many pounds

构造之法第十一、十二章

第十一章 软件设计与实现 图形建模和分析方法 1表达实体和实体之间的关系(思维导图) 2实体关系图 3Use Case Diagram 表达数据的流动 (1)和管理机构相关的数据流 (2)和读者相关的数据流 (3)和新书入库相关的数据流 (4)和时间相关的数据流 表达数据的流动 (1)和管理机构相关的数据流 (2)和读者相关的数据流 (3)和新书入库相关的数据流 (4)和时间相关的数据流 其他设计方法 1.形式化的方法 2.文学化编程 第十二章 用户体验 用户体验的要素 1.用户的第一印象 2.

Java 第十二章 继承 笔记

Java 第十二章  继承 笔记 一.使用继承:     1)方便修改代码     2)减少代码量 二.super 继承object 类:super 访问父类的无参构造:super 指的是object 的无参构造.     例:子类调用父类:super.属性 / super.方法    注意:子类不能继承父类私有属性:得用set.get方法来调用:    super只能写在代码块的第一句:super只能调用非私有的方法:    super只能出现在子类的方法和构造方法中. 三.不能被继承的父类成

JavaScript DOM编程艺术-学习笔记(第十二章)

第十二章 1.本章是综合前面章节的所有东西的,一个综合实例 2.流程:①项目简介:a.获取原始资料(包括文本.图片.音视频等) b.站点结构(文件目录结构) c.页面(文件)结构 ②设计(切图) ③css -  base.css用于引入使用的css文件 color.css  - 用于设置样式 layout.css - 用于设置布局 Typography.css - 用于设置版式 3.题外话:①在实际开发中,即使是一个空白项目也往往不会从一无所有做起,而借助的平台一般会提供目录结构,所以需要把自己

R in action读书笔记(16)第十二章 重抽样与自助法之 置换检验

第十二章:重抽样与自助法 本章,我们将探究两种应用广泛的依据随机化思想的统计方法:置换检验和自助法 12.1 置换检验 置换检验,也称随机化检验或重随机化检验. 有两种处理条件的实验,十个受试者已经被随机分配到其中一种条件(A或B)中,相应的结果变量(score)也已经被记录.实验结果如下: 如果两种处理方式真的等价,那么分配给观测得分的标签(A处理或B处理)便是任意的.为检验两种处理方式的差异,我们可遵循如下步骤: (1) 与参数方法类似,计算观测数据的t统计量,称为t0: (2) 将10个得