Archive
Running a control block chain…
Disclaimer. I didn’t invent this. I saw the general technique many years ago in some code I was working on and I have always thought it was pretty neat, so it’s time to pass it on to the probably few people left in the world that have not seen it.
Lets assume you have a control block structure like this:
WSA_FIRST@ is a full word pointer somewhere in storage (working storage for example), addressed by some register so we have a using on it can can address it by label. It points to the first control block in the structure, or contains zero if there are none on the chain.
The control blocks are mapped by this DSECT:
CB DSECT CB_LENGTH DS F Control block length CB_NEXT@ DS A Address of next cb or zero if last : Other fields as required
Assuming I am using register R08 to address the control blocks, this is how I would typically run this chain:
USING CB,R08 LA R08,WSA_FIRST@-(CB_NEXT@-CB) LOOP DS 0H ICM R08,15,CB_NEXT@ First, next or zero if end JZ LOOPEND : Code here to process the control block J LOOP LOOPEND DS 0H DROP R08 Drop CB using
The LA (Load Address) sets the control block base register, R08, to point to some point BEFORE the address of the WSA_FIRST@ field. The resultant address does not even have to be addressable by the code because the instruction is just calculating an address of some storage location, not actually referencing it. So, NO S0C4!
In this example R08 would end up pointing to an address 4 bytes BEFORE the location of WSA_FIRST@ because the CB_NEXT@ field is at offset 4 in the control block (CB_NEXT@-CB) and we subtract that from the address of WSA_FIRST@.
So now comes the clever part.
The assembler thinks the control block (CB DSECT) is addressed by R08, and it thinks the CB_NEXT@ field is at offset +4 in the CB DSECT so the label (CB_NEXT@) in the ICM (Insert Characters under Mask) instruction resolves to R08 plus 4. So the ICM gets the 4 bytes from where R08 is pointing (remember, it is pointing to 4 bytes BEFORE the actual first control block address field in storage somewhere) plus 4, which means on the first time through the loop it loads R08 with the data from WSA_FIRST@.
So now, R08 is either zero, if there are no control blocks on the chain, or it addresses the first control block (CB1).
If it is zero, the JZ (Jump if Zero) instruction causes us to exit the loop, otherwise we drop through to process the control block before jumping back to the start of the loop.
This time, R08 is pointing at CB1 so the ICM instruction loads R08 from the CB_NEXT@ address field in CB1 so now R08 is pointing at CB2.
Again we process the control block before jumping to the top of the loop.
This time, R08 is pointing at CB2 so the ICM instruction loads R08 from the CB_NEXT@ address field in CB2 so now R08 is pointing at CB3.
Again we process the control block before jumping to the top of the loop.
This time, R08 is pointing at CB3 so the ICM instruction loads R08 from the CB_NEXT@ address field in CB3. Since CB3 is the last control block on the chain its CB_NEXT@ field is zero so the JZ instruction is true and we exit the loop.
Using the IBM Assembler Toolkit Macros
The following is functionally equivalent, and in my opinion, a lot cleaner and easier to read (you also don’t have to worry about label names just to implement logic):
USING CB,R08 LA R08,WSA_FIRST@-(CB_NEXT@-CB) DO WHILE=(ICM,R08,15,CB_NEXT@,NZ) : Process the control block here ENDDO DROP R08
There is however one slight difference in how the code assembles between the DO/ENDDO version and the hard coded version I showed initially.
Even though the ICM instruction and it’s operands are coded on the WHILE operand of the DO macro, the actual instruction is generated at the END of the loop by the ENDDO macro. In addition it generates a JNZ (Jump if Not Zero) instruction to return to top of the loop, versus the JZ (Jump if Zero) instruction in my hard coded loop.
However the biggest impact of this is that you must have addressability to the control block when the ENDDO macro is invoked. So something like this will not assemble:
USING CB,R08 LA R08,WSA_FIRST@-(CB_NEXT@-CB) DO WHILE=(ICM,R08,15,CB_NEXT@,NZ) : Process the control block here DROP R08 ENDDO
When the ENDDO generates the ICM instruction, the assembler no longer has addressability to the CB DSECT addressed by R08 so the assembly fails.
Running a chain backwards
The same general technique works when running a chain backwards, that is from the last entry to the first.
Obviously the anchor point field in storage has to point to the LAST control block on the chain and each control block has to have a back pointer to the control block BEFORE it (CB_PREV@ maybe) on the chain.
A Matter of Style – Labels…
As much as possible I try to make it obvious from a variable label, what type of variable it is and wherever possible, what other variable or control block it is related to.
Constants
I start variable names for constants with a # sign, thus:
#NAME DC C'NAME'
If a variable starting with a # sign is on the receiving end of an instruction, it is wrong because I should not be modifying constants!
Equates
I start equated value names with an @ sign, thus:
@ONE EQU 1 @FLAG1 EQU X'80'
For equated symbols that define a length, I add the suffix ‘_LEN’ to the symbol name, thus:
FIELD1 DS CL8 @FIELD1_LEN EQU *-FIELD1
That way, it is obvious in the code that I am referring to an equated length value, for example:
LA R1,@FIELD1_LEN Get field length in R1
It it obvious that I intend to load the field length and not the field address into the register but having the _LEN suffix ensures that I get the length and do not accidentally code the field address instead.
Equate/Field relationship
I prefer to ‘tie’ equated values for a field to the field they relate to by using a naming convention that uses the field name as part of the equate symbol, thus:
FIELD1 DS C @FIELD1_YES EQU C'Y' @FIELD1_NO EQU C'N' @FIELD1_NOTSET EQU X'00'
Then in code I would write something like this:
CLI FIELD1,@FIELD1_YES BE DO_YES CLI FIELD1,@FIELD1_NO BE DO_NO B NOT_SET
Similar code would apply to setting the field value, only use equated symbols that are related to the target field. This convention ensures that you are only setting and testing for values actually defined for the field.
About the only exception to this rule that I use is the use of the following common equated symbols:
@YES EQU C'Y' @NO EQU C'N' @POSTED EQU X'40'
@YES and @NO should be obvious, @POSTED is to test an ECB posted bit flag,E.G:
TM ECB1,@POSTED Is ECB posted? BO DONE
Field/DSECT relationship
When I am creating my own DSECTS I prefer to prefix the name of each field within the DSECT with the DSECT name and an underscore, thus:
SOMEBLOK DSECT SOMEBLOK_FIELD1 DS CL8 SOMEBLOK_FIELD2 DS F @SOMEBLOK_LEN EQU *-SOMEBLOK
Using this standard makes it easy when reading source code to determine who owns a field. There is no confusion. The length equate for the control block also follows my convention of adding _LEN to the end and prefixing the equate symbol with an @ sign to avoid ambiguity in the executable code.
The exception to the rule…
I do allow for one exception to this rule though and that is for a program’s working storage. Typically I prefix working storage fields with the prefix STG_ (short for ‘storage’) although you can use anything you want, to identify working storage fields, for example W_ etc. It is preferable to use the underscore as part of the name to avoid situations where a field name ‘might’ happen to start with whatever prefix you are using. For example if you use just W as your working storage field prefix, is WORKF1 a field in your working storage or somewhere else? Using this convention, STG_WORKF1 or W_WORKF1 are obviously part of the code’s working storage. And again, the convention ensures that you are using the field you intend and not a WORK1 field defined somewhere else that might allow the program to assemble but my then fail at execution time because the storage it refers to is not addressable or it is addressable but it’s the wrong field and messes up some other code (good luck finding that one!).
Summary
Whatever conventions you use, the aim is to make it less likely to introduce errors when developing the code and to make it much easier to read, and locate field owners several months later when someone else has to try to follow, understand and debug or modify your code.
A Matter of Style…
There are probably as many ways to write a program as there are programmers in the world, more if you accept that a program is solution or part thereof, to a problem and could be written in more than one language, each of which causes the writer to adopt differing styles.
So basically, there’s more than one way to skin a cat!
Now I’m not saying that the style of assembler programming that I have developed over the years is the best but I think it satisfies many of the requirements that I will cover in what I hope will be a short series of articles. Let’s put it this way, unless someone tells me different, I’ll tend to try to influence any code that I work in towards my preferred style although there are reasons for NOT doing that, for example to maintain the existing style or coding standards since a mix can make things worse.
SO! Why is style important?
Sometimes it isn’t. You may need to write a quick, one off, one use program to solve a problem. As long as it does the job, does style really matter? Well, it depends. If the code is simple then probably not but what if the code is complex? Then making the code at least readable and logical makes sense since if you get it wrong the first time through, it will be a lot easier to diagnose the problem and correct the code.
But let’s consider the more common situation where you are developing a program or even multiple programs that form a piece of software and those programs are for use by paying customers. In other words you are creating commercial software. I am also including software written for in house use (in the business sense) because such development, use and support usually involves internal billing and thus still qualifies it as commercial software.
Basically, if someone else other than the developer or developing department uses it, you can consider it to be commercial software in my mind.
So let’s say that you are assigned the task of developing a z/OS super widget application which, because it uses lots of system services will be written in assembler, or at least a lot of it will be. Let’s looks at how programming style will affect it’s life cycle.
Development
The faster you can develop and test the code to the point where it does what the requirements require without failing or causing other problems, the faster the product will reach the market and thus generate revenue for the business.
Everyone probably has a standard program entry and exit macro and possibly similar ones for subroutine entry and exit but what about other stuff. Parsing of input for example? Do you do it with hard coded assembler or use a macro or service to do parsing in a standard and consistent manner? If a service, do you call that service using a macro to make coding the call simpler and standard? Or, do you let your programmers set everything up by hand (guess which is the more reliable method)?
Do you use a standard form of label name for program labels and subroutine names? Are you still stuck in the ‘all labels must be 8 characters long’ mental state or do you use longer and more meaningful names, either CamelCase or with parts separated by underscores, E.G. WORK_FIELD_1.
Do you use naming standards to tie equated symbols to the field they relate to or is it a free for all. Is it easy to tell, without looking at the variable definition, if a variable name is a label or an equated value and if a label, is it easy to tell which control block it belongs to, or if a constant, is it easy to tell that it is a constant?
And most importantly in my mind, do you create comment blocks for the program as a whole and for each subroutine that describes the program, it’s inputs, outputs and return codes and the same for all subroutines? Also, if the program is reliant upon a particular control block structure, do you document that WITHIN the code as best you can (using good old EBCDIC character graphics if need be) so that the architecture is right there in the code, not lost on some hard drive somewhere that got wiped because someone didn’t realize that what was on it was needed (the ‘not my job’ syndrome!).
Maintenance
If the code is well structured, follows consistent naming standards, uses macros to standardize the coding of interface, has proper comments describing what each routine does, what it’s input and outputs are then understanding the code and diagnosing a problem becomes a lot easier and more importantly faster. You are also more likely to be able to correctly fix any problem without introducing new problems. All the above result in a faster turn around time for the resolution of any code related problems. They also mean it is much easier for someone other than the original developer to pick up the code and work on it with confidence.
This all results in improved customer confidence in the product and better tolerance for any problems that do occur because they know that they can be quickly resolved.
New Features
Code that is well structured is much easier to modify to add new features to. And just like the maintenance situation, there is less likelihood of such changes introducing new problems into either the new or the existing code. This all helps to get the product out to market that much quicker and earning revenue.
The Bottom Line
If you are a commercial programmer, that is, it’s your job, your work earns the business that you work for, the money that pays your wages. The quicker you can develop new code or fix existing code directly affects the companies business results and therefore your earnings.