本篇重在介绍,怎么把内存的实际数据显示出来,首先看下汇编怎么实现的,接下来是用COBOL实现的
000000 00000 00100 1 MAIN CSECT
R:C 00000 2 USING *,12
000000 90EC D00C 0000C 3 STM 14,12,12(13)
000004 0DC0 4 BASR 12,0
000006 50D0 C02C 0002C 5 ST 13,SAVE+4
00000A 41D0 C028 00028 6 LA 13,SAVE
0000E 7 START EQU *
00000E F384 C075 C070 00075 00070 8 UNPK OUTPUT(9),INPUT(5)
000014 DC07 C075 C000 00075 00000 9 TR OUTPUT,HEXTAB
0001A 10 EXIT EQU *
00001A 58D0 C02C 0002C 11 L 13,SAVE+4
00001E 98EC D00C 0000C 12 LM 14,12,12(13)
000022 41F0 0000 00000 13 LA 15,0
000026 07FE 14 BR 14
000028 15 SAVE DS 18F
000070 A0B1C2D312 16 INPUT DC X'A0B1C2D3',X'12'
000075 4040404040404040 17 OUTPUT DC CL8' ',C' '
00007E 0007E 000F0 18 ORG MAIN+C'0'
0000F0 000F0 000F0 19 ORG ,
00000 20 HEXTAB EQU *-C'0'
0000F0 F0F1F2F3F4F5F6F7 21 DC C'0123456789ABCDEF'
000000 22 END MAIN
上面的代码是把内存十六进制的数据变成可显示的数据,比如输入X'A0B1C2D3',结果输出的数据就是可显示的A0B1C2D3
这里需要明确几点:
1.OUTPUT DC CL8' ',C' '和OUTPUT DC CL9' '的唯一区别是,前者OUTPUT的长度是8后者是9.
同理INPUT DC X'A0B1C2D3',X'12'和INPUT DC X'A0B1C2D312'的区别前者长度是4后者长度是5,这也正是为什么在UNPK指令里要注明(9)的原因
其实在这个程序里,真正的输入数据就是存放在INPUT的前四个字节里,最后一个是什么无所谓,而真正的输出也是存放在OUTPUT的前八个字节里
2.计算机里专门有个location counter来标识每个指令或字段的地址,而base register,比如本程序的R12只能标识location connter中的一小部分,它们并不是一回事
本程序中USING *,12写在了第一行,如果往下写几行,就会发现它们的数据并不相同,location counter肯定是从本程序的第一行开始算起,而base register理论上放在程序的任何地方都可以
000014 DC07 C075 C000 00075 00000 9 TR OUTPUT,HEXTAB
这一句中我们发现HEXTAB的地址被计算机解析为C000,这不就是程序的开始地址吗?而HEXTAB是在程序的开始地址处吗?不是,因其本身没有地址,它用的是EQU,这个可以从listing里看出来,但它却代表程序的开始地址。
00000 20 HEXTAB EQU *-C'0'
0000F0 F0F1F2F3F4F5F6F7 21 DC C'0123456789ABCDEF'
它们是孤零零的两行,它们之间在计算机看来没任何关系,如果第一行是HEXTAB EQU *这只是说明HEXTAB所指向的地址正好是下一行的地址,现在这种情况HEXTAB代表的就是当前地址往前推240个字节,即指向程序开始处的地址。
3.TR指令是怎么转换数据的呢?
通过UNPK指令,OUTPUT变成X'FAF0FBF1FCF2FDF3',X'21',我们拿第一个字节举例,TR就应该找到HEXTAB+X'FA'处的数据,使之替换它,所以就是
*-C'0'+X'FA' =当前地址-X'F0'+X'FA' = 当前地址+X'0A',对应表中就是字符A
那可能有人会问X'21'怎么转换呢?其实你根本没必要担心,因为OUTPUT的长度是8,TR转换8次之后就停止了,压根就没处理X'21'这个数据
其实说到底INPUT和OUTPUT最后多定义那一个字节,纯粹是指令的需要,至于写什么根本不重要,但就是因为有了它,程序确实优美了许多
4.
00007E 0007E 000F0 18 ORG MAIN+C'0'
0000F0 000F0 000F0 19 ORG ,
这两行代码完全是为了HEXTAB EQU *-C'0'的需要,本程序很短,万一*所代表的当前地址没有C'0'大, 那岂不是要报错!?
上面第一行是说把location counter强制改成000F0(而它之前所设置的最大为0007E),后一行是说再把location counter再设置为之前所碰到的最大值,这样一来这两行就可以保证location counter最小也要是000F0
还有一点要说的是HEXTAB EQU *-C'0' 这里的*代表当前地址,可当前地址程序是怎么表示的呢?你可以理解为是location counter的数值,当然也可以用base register R12+偏移量来表示,因为它们是一致的,程序中用的就是CXXX),本程序中由于base register设置是在第四行,即location counter为6处,所以location counter的数值应该始终比以base register的偏移量少6
5. R:C 00000 2 USING *,12
本程序第一行如上所示,它是说在编译过程中,R12里存放的数值就是location counter为0000的数值,R12要从此处开始计算偏移量
如果把这行放在第四行,其会变成
R:C 00006 2 USING *,12
它的意思是说R12的数值是0006,即偏移量是从R12为0006处开始算起的,即往后每个指令或者字段的表示中,相对于R12算出来的偏移量都比去自身的location counter小6,等到了装载的时候R12的里的数据就会比入口地址大0006
解释到这里,你会不会觉得本程序把USING放在第一行有点问题?,不明白没关系,下一篇大型机汇编(mainframe assembler/HLASM之基址寄存器的设定帮你解惑
***************************************************************************************************************
用COBOL同样可以实现同样的功能:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 AAA PIC 9(7) COMP-3.
01 BBB REDEFINES AAA.
05 BBB1 PIC X(3).
05 BBB2 PIC X(1).
01 CCC PIC 9(7).
01 DDD REDEFINES CCC.
05 DDD1 PIC X(6).
05 DDD2 PIC X.
PROCEDURE DIVISION.
MOVE X'1A3E5B' TO BBB1.
MOVE AAA TO CCC.
INSPECT DDD1 REPLACING ALL X'FA' BY 'A',
ALL X'FB' BY 'B',ALL X'FC' BY 'C',ALL X'FD' BY 'D'
ALL X'FE' BY 'E',ALL X'FF' BY 'F'
STOP RUN.
有异议请联系QQ349106216