The engineering notebook

How the machine at abend.dev was built, what it is measured against, and why a cycle defined for a 1964 mainframe is the reason this page exists at all.

Fifteen items, transcribed

The program cycle on the front page is not a paraphrase of RPG's behaviour. It is the fifteen numbered items printed beside figure 31 of IBM's Report Program Generator Language, form C24-3337-3, November 1968, transcribed one at a time. The test requires a distinctive clause from every one of the fifteen to appear in the archived copy, so an item reworded to read better turns the build red.

Three of the fifteen are yours: items 6, 7 and 11, the calculations and the output. The other twelve are the compiler's, and that is the whole subject. You did not write the loop. The loop was wrapped around what you wrote, and it read a record, set indicators, took the branches and printed the totals whether or not you had thought about any of it.

The manual has a real text layer, so no character recognition is involved anywhere in this page's quotations. That matters more than it sounds: an OCR'd 1968 manual would put invented commas into sentences the test then demands verbatim.

Why two control levels and not one

RPG supports control levels L1 through L9, nested, so a report can break on branch inside region inside division. The page drives two, and the reason is that one level never shows why they are numbered.

With a single level you see a group total print. With two you see something you cannot see any other way: a break at L2 turns L1 on with it, so the division total prints underneath the group totals that make it up, in that order, without anybody writing the order down. The deck on the page is arranged so the arithmetic can be checked three independent ways — the group totals sum to the division totals, the division totals sum to the final total, and the final total equals the sum of the deck.

Levels three to nine are simply never reached by this deck. The engine carries all nine indicators and sets them all on at end of file, which is what the manual says happens.

A card is eighty columns, and the columns are the language

You did not type a program. You wrote it on a pre-printed form and the compiler read each field out of its own columns: Factor 1 in 18 to 27, the operation in 28 to 32, the result field in 43 to 48. A name beginning one column too far left was a different field, or no field at all.

Every column range on the specification sheet is quoted from the manual's own prose, and the sheet is checked the way a compiler would check it: writing a card from the boxes and reading it back must return every field unchanged, and shifting the card one column must change what the compiler reads. That second test is the one that carries the subject. It is easy to say that columns mattered; it is better to have a check that fails when they stop mattering.

A name too long for its field loses its tail rather than overflowing, because that is what the card does. Six characters of Result Field is six characters.

The sheet that says where a field is

The calculation sheet works on names: ADD AMOUNT TO TOTAL. The names come from the Input specifications sheet, which says which columns of the card each field occupies, and the manual is explicit about what that buys you: “Once the card columns of the field have been specified and the appropriate field name has been defined for the field, other references to this field are made by using its field name rather than writing down the specific card columns each time.”

You say it once and everything afterwards trusts the name. Which is why a field described one column out is not a syntax error anywhere.

What a one-column error actually does

Take an amount punched in columns 23 to 28 with two decimal places, and describe it in the Input sheet as 22 to 27. Both sheets are valid. The deck compiles. The job runs to normal end of job. The report prints.

And every figure on it is out by exactly a factor of ten, because the field has picked up the blank in column 22 and dropped the last digit in column 28. A total of 1,042.50 comes out as 104.25, which still looks like money and still balances against itself, because every row moved by the same factor.

There is no diagnostic. Not a warning, not a return code of 4, nothing at any severity, because nothing anybody wrote is wrong. The panel on the front page runs exactly this and prints the diagnostic count, which is zero.

What the compiler CAN see, and why it is not enough

Three things are genuinely checkable from the sheet alone, and the engine implements all three: columns outside 1 to 80, more decimal positions than the field has columns, and two fields whose column ranges overlap so one is reading the other’s digits.

None of them is the fault above. They are all statements about the sheet’s internal consistency, and the sheet is perfectly consistent. What no sheet anywhere records is what the card actually contains, so the compiler has nothing to compare against. The only instrument that can find it is a person reading the report and disbelieving it.

Where the column numbers come from

Field Location is columns 44 to 51 of the Input sheet, From in 44 to 47 and To in 48 to 51; the manual refers to it as “Field Location (columns 44-51)”. The field name sits in 53 to 58. Those are quoted from the manual and pinned in the test suite rather than remembered.

The first version of the test put the amount at columns 21 to 26 and the engine disagreed with it. The engine was right: the columns are counted now rather than guessed at, which is the whole reason the test exists.

What an abend is, exactly

This site is named for the abnormal end and everything above it is a fault the job survives. An abend is the other kind: the machine stops in the middle, having already done some of the work. The one this page is about is a data exception. It said “the commonest one on this hardware” until an outside reader asked where the count was, and there is no count: nobody here has measured it. It is worth being precise about anyway, because it is not a bug in arithmetic. It is arithmetic being asked to read something that is not a number.

Two digits to a byte, and a sign in the last half of the last one

A packed field in RPG, which the Input sheet marks with a P in the packed column and which COBOL calls COMP-3, holds its digits two to a byte. IBM System/360 Principles of Operation: “In the packed format, two decimal digits normally are placed adjacent in a byte, except for the rightmost byte of the field.” That last half-byte is the sign.

The codes are the mechanism. “The digits 0-9 have the binary encoding 0000-1001. The codes 1010-1111 are invalid as digits.” Read as a sign instead, 1010, 1100, 1110 and 1111 are plus, 1011 and 1101 are minus, and “The codes 0000-1001 are invalid as sign codes.”

So every half-byte position has exactly one job and there is no code that can do both. A digit position holding an A is wrong; a sign position holding a 4 is wrong. Nothing in the bytes themselves says which position is which. Only the field’s declared length does, which is the same indirection as the Input sheet above, one layer further down.

Why a field of spaces is the interesting case

An EBCDIC space is X'40'. A four-byte field nobody filled in is X'40404040', and its half-bytes are 4, 0, 4, 0, 4, 0, 4, 0. Every one of the seven digit positions holds a 4 or a 0, and both are perfectly good digits. The sign position holds a 0, which is not a sign at all.

Nothing wrote those bytes in error. A record with a field nobody filled in is the most ordinary thing on a tape, and the program that left it that way was not doing arithmetic, so it never failed and never will. The program that reads it fails, part way through a run that has already written records, at whatever hour the batch window put it.

High values, which is where the folklore is wrong

A field initialised to X'FF' throughout is often described as failing the sign check. It does not. F is one of the four plus codes, so the sign position passes, and it is all seven digit positions that fail instead.

That distinction is the difference between knowing the table and remembering a story about it, and it is the reason the sign table on this page is checked against two manuals rather than typed from memory: the System/360 Principles of Operation, A22-6821-0, and the System/370 one of September 1974, GA22-7000-4.

What is terminated, and what is not

“A data exception is recognized when: 1. The sign or digit codes of operands in decimal arithmetic or editing operations or in CONVERT TO BINARY are incorrect.” And then the sentence that matters: “The operation is terminated.”

Terminated, not skipped, not defaulted to zero, not flagged. The instruction does not complete and the program does not carry on past it. Everything the job did before that point is done, and on a file update that means a partly updated file and a restart procedure.

The completion code everybody remembers, and why it is not on the page

Under OS/360 and MVS a data exception came back as a system completion code of 0C7, and anyone who worked on that hardware knows the number. It is deliberately not printed on the front page.

What the operator actually saw depended on the operating system, the release, and the language: 0C7 under MVS, a different message entirely on the System/38 and the AS/400 that came later, and RPG’s own halt indicators before either. The mechanism underneath is identical everywhere, and the mechanism is what the page is about, so the page shows the mechanism and the ledger says why the number is missing.

The machine, drawn

Where the fields sit on the calculation sheet

Seven fields, each one a fixed range of columns on an eighty-column card. Everything the compiler knows about your program is where these boundaries fall.

The calculation sheet's seven fields, by columnAn eighty-column strip. Control level occupies columns 7 to 8, Factor 1 columns 18 to 27, Operation 28 to 32, Factor 2 33 to 42, Result field 43 to 48, Field length 49 to 51, and Decimal positions is the single column 52.column 1807-818-2728-3233-4243-4849-5152Control level, Factor 1, Operation, Factor 2, Result field, Field length, Decimal positions.

Packed, against the same number written as characters

Four bytes, twice. Packed, they hold seven digits and a sign in the last half-byte; written as characters they hold four digits and no sign at all. Only one of them is a number, and the two do not hold the same count of them: four packed digits and their sign need three bytes, not four. The drawing used to be captioned as the same four digits both ways, which no arrangement of these cells is.

A packed field and a zoned field, half-byte by half-byteTwo rows of eight half-byte cells. The packed row holds seven digits and a sign code of C. The zoned row alternates a zone half-byte of F with one digit, so its last half-byte is a digit rather than a sign and every zone fails as a digit.packedas characters0 10 42 50 CF 1F 0F 4F 2seven digits and a sign. 1042.50four zones and four digits. Not a number.Read as packed, the zoned field fails on all four zone half-bytes and on the sign, which is a 2.

The return code worth caring about is 4

The compiler graded every diagnostic it found and returned the worst severity. The scale is Table 8 of the manual, and it grades the outlook for running the program rather than whether one came out: minor errors at 4 with successful execution probable, errors at 8 with unsuccessful execution possible, serious errors at 12 with it probable, and critical errors at 16 where normal execution is impossible. The table’s own note sends you to the job control language, because whether the next step ran on a given code was the COND parameter of the job and nothing to do with the compiler. This page said “at 8 and above you got a listing and no program” on both of its surfaces, and an outside reader was right that that is neither the manual’s definition nor how a job behaved. At 4 you got a program, because the compiler had assumed something on your behalf and carried on.

That is the failure this page would keep if it could only keep one. Not the day you lost to an error: the day you did not lose, because output came back and it was the wrong output. A misspelt field name is a warning, the deck compiles, the field is assumed to be zero, and the report is full of zeros that look like a business result.

So the sheet's diagnostics are graded on the compiler's own scale, and the day counter is fed by them rather than by a set of buttons. What your specifications get wrong is what decides how many days the deck took to come back. There is no second way to set the faults, because two ways to answer one question is two answers.

The messages themselves are this page's words, not IBM's. The manual's diagnostic text is in its Appendix G and that part of the scan is too damaged to quote, so the wording is ours and the ledger says so. What is not invented is the grading.

The cycle outlived the manual

The manual on this page is dated November 1968 and describes a System/360. The cycle it defines did not stop there. RPG went to the System/3, the System/34 and /38, and then to the AS/400 in 1988, which became the System i; the language became RPG/400 and later RPG IV, and the implicit cycle came with it. Code written against the 1968 description kept running on machines sold decades later.

The other end of the era needs saying too, because a bare 1964–1990s does not say what is being dated. It is not the language: RPG is older than both documents here, having started on IBM machines before the System/360, and this page does not date it. 1964 is the Principles of Operation, which is where the packed decimal format, the digit and sign codes and the data exception itself come from — the architecture the fault on this page is a fault of. The cycle is the 1968 manual’s.

The 1990s end is the reason the range is a range rather than a date. The interesting fact about the program cycle is not that it existed in 1968. It is that it was still doing the work long after everyone had stopped thinking about it, on hardware nobody in 1968 had designed, being maintained by people who inherited it.

Where this page was wrong first

What is not here

JCL, which was its own language and its own class of overnight failure. Matching and secondary files. Table lookup. Exception output. The other specification sheets: this page has the calculation sheet and nothing else, so a check it cannot make is a check nobody wrote. And the day count is a model rather than a log. One submission a day is a round number standing in for a range that ran from two hours to three days depending on the shop, the operator and whether the tape you needed was mounted on another job.

Sources