A friend of mine, Melvyn Maltz, was amused by some code I wrote recently. The code simply printed out the contents of a register, and I took a quick and dirty approach: Store the register in a buffer, use PUT to print the buffer, SET HEX ON. It’s a technique I often use when debugging on a mainframe. I should tell you that my friend is an expert in most things IBM – a master programmer. He’s also generous enough to read or watch some of the material on this site and offer suggestions. In any case, the code amused him, and he reminded me that it’s not that hard to print a register – and it’s instructive. I realized I had never written about how to print a hexadecimal representation of a register (or small field) on this site, so I’ll do that now. It involves TR and UNPK.
Let’s start with UNPK which treats each byte of the sending field (except the last) the same. UNPK separates the zone and numeric parts of each byte (dare I call them nibbles?) into two bytes after appending a hexadecimal F on the front of each nibble, like this:
| B 3 | / \ | F B| F 3 |
A moment’s thought here is instructive. The 3 is converted to x’F3’, which is exactly what we need to print it in a character format. The same would be true for all digits 0-9. The treatment given to B doesn’t work so well. The same bad treatment would occur for digits A-F. So, bytes x’FA’, x’FB’, … x’FF’ would have to be repaired after applying UNPK. Can we fix those? Yes. TR can do the trick.
So, the basic idea is to use UNPK followed by TR. What about that last byte that UNPK converts by simply flipping the nibbles? We can avoid that problem by adding a byte (logically or physically) to the field we are unpacking. Problem solved.
What about the TR table? What does that look like? My friend might suggest this,
HEXTABLE DC C’0123456789ABCDEF’
How do we get away without defining a 256-byte table? The solution leverages the fact that we are only translating a narrow range of sequential values: x’F0’ – x’FF’ – every byte we need to translate begins with x’F’. We can pretend to have created a 256-byte table by coding TR this way (assuming the register we want to print is R5):
ST R5,FWRD UNPK HEXAREAX,FWRDX TR HEXAREA,HEXTABLE-240 … DS 0F ALIGNMENT FWRDX DS 0CL5 FWRD DS F DC X'00' AN EXTRA BYTE FOR UNPK HEXAREAX DS 0CL9 8 HEX DIGITS+1 IN CHARACTER HEXAREA DS CL8 8 HEX DIGITS IN CHARACTER DC X'00' AN EXTRA BYTE FOR UNPK
We can do this, confident that the first 240 bytes of our fictitious table will never be referenced by TR. (240 is the offset to a character 0). After translation, HEXAREA contains the result. Variations of this technique use explicit lengths and leaving off an extra byte. The only drawback to the technique is that the relative address HEXTABLE-240 may generate an addressability error if it references an address that is smaller than the base register address. This rarely happens.
My friend may get another chuckle out of this, but I’m going to suggest a second solution that uses a 256-byte table:
HEXTABLE DC 256AL1(*-HEXTABLE) NEUTRAL TABLE ORG HEXTABLE+X'FA' FIX X'FA' THROUGH X'FF' DC C'ABCDEF' ORG ,
The rest of the solution is similar and looks like this:
ST R5,FWRD UNPK HEXAREAX,FWRDX UNPK SLIGHTLY LARGER FIELDS TR HEXAREA,HEXTABLE TRANSLATE THE SMALLER FIELD … DS 0F ALIGNMENT FWRDX DS 0CL5 FWRD DS F DC X'00' AN EXTRA BYTE FOR UNPK HEXAREAX DS 0CL9 8 HEX DIGITS+1 IN CHARACTER FORMAT HEXAREA DS CL8 8 HEX DIGITS IN CHARACTER FORMAT DC X'00' AN EXTRA BYTE FOR UNPK
After executing the first three lines above, HEXAREA will contain a printable version of register 5.
This leads into how we can print larger areas of memory in a hexadecimal format. There’s always the old standby: SNAP. Melvyn sent a nice example of TROT, but I’ll save that gem for another post.