Language reference
The layer everything else on the mainframe ultimately compiles down to.
IBM's Basic Assembly Language (BAL) arrived in 1964 as part of Basic Programming Support for the System/360, deliberately restricted to run on machines with as little as 8 KB of memory and only a card reader, card punch, and printer for input and output. It had no macro support at all. Assembler D followed in 1966 for DOS/360 and introduced macros, and the language kept splitting and recombining across OS/360's Assembler E and F, System/370's Assembler H in 1981, and Assembler XF, before IBM unified the whole line into High Level Assembler (HLASM) in June 1992, replacing Assembler H Version 2.
Assembler was never meant to be comfortable. It gives a program direct, unmediated control over registers, storage addresses, and the exact machine instructions the CPU executes, in exchange for none of the safety nets higher-level languages provide. That trade is precisely why it still exists: when a system needs to do something no higher-level language on the platform exposes, or needs the last possible bit of performance, HLASM is still the layer everything else on z/OS ultimately compiles down to.
1964 · BAL
Basic Assembly Language ships with System/360, no macro support, built for 8 KB machines.
1966 · Assembler D
First IBM assembler with macro support, for DOS/360 systems with 16 KB minimum memory.
1970s · Assembler E and F
E required 32 KB minimum storage; F ran on 64 KB machines and became a standard OS/360 component.
1981 · Assembler H
Version 2 announced supporting Extended Architecture (XA); IBM withdrew it from marketing in 1994.
1992 · HLASM
High Level Assembler ships in June, replacing Assembler H Version 2 as IBM's current assembler.
ongoing · HLASM today
Still IBM's assembler for z/OS, z/VSE, z/VM, and z/TPF; Release 6 and later also run on Linux on IBM Z.
$47 to $76 per hour
Reported pay range for mainframe assembler contract roles as of May 2026.
ZipRecruiter: Mainframe Assembler Jobs15% Gen Z, up from 1%
Share of surveyed mainframe technologists who are Generation Z, up from 1% seven years earlier, alongside millennials rising to just over half of respondents.
ACM CareerNews, September 2025Assembly / HLASM history and figures compiled from public sources, cited above. Corrections welcome.
The CPU tried to execute an invalid operation code, most classically because execution fell through from the last real instruction straight into a data area (a DC or literal pool) and started trying to interpret raw data bytes as machine instructions.
Check that every code path ends in an explicit branch back to the caller (typically BR 14) before any DC or literal declarations follow it. This is the single most common first-program mistake in assembler, catching even careful beginners who copy a working example and add code after it without an exit branch.
The program attempted to execute an instruction, or issue a supervisor call, that is restricted to an authorized state the program does not have.
Check whether the instruction or SVC actually requires APF authorization, and confirm the load module is running from an APF-authorized library if it genuinely needs that access. Most application-level assembler code should never need privileged instructions at all, so this often points to a mistaken opcode rather than a missing authorization.
Protection exception (reason code 4) is the classic case, but the official manual lists S0C4 as covering several other, quite different exceptions identified only by their SDWA reason code: segment-translation (10), page-translation (11), ASTE-validity (2B), ASCE-type (38), and Region-First/Second/Third (39, 3A, 3B) exceptions. The protection case is what most assembler programmers know it as: the key of the storage area referenced doesn't match the key the program is running under, most often from a base register that got overwritten by ordinary code so USING no longer matches what the register actually holds, from a module link-edited as reentrant without actually following reentrancy rules, or from trying to execute instructions out of storage that was obtained with EXECUTABLE=NO. The translation-exception reason codes instead point at storage that was never actually GETMAINed, or that was paged out while a disabled program tried to reference it.
Check the SDWA/dump reason code first, since it tells you which of these unrelated problems actually happened. For reason code 4, check every instruction that loads a value into a register also used as a base register for a stray overwrite, and check whether the module's actual link-edit attributes (reentrant or not, storage key, executable) match how it's coded and how it's running. For the translation reason codes (10, 11, 2B, 38, 39, 3A, 3B), check that the storage being referenced was actually obtained before use, and that a program running disabled page-fixed any storage it touches instead of leaving it pageable.
The program referenced a storage address that does not exist at all, distinct from S0C4 in that the address is invalid rather than merely off-limits.
Check for an uninitialized register used as a base or pointer, or address arithmetic that produced a value outside real storage entirely. This is less common than S0C4 in application code but shows up in the same class of base-register mistakes.
HLASM (High Level Assembler) is still used where a mainframe system needs direct control over registers, storage, or performance that COBOL, PL/I, or RPG can't provide, usually in systems-level or performance-critical routines underneath higher-level applications.
It pays well specifically because so few people maintain it: reported rates run roughly $47 to $76 an hour as of 2026. It's a specialist skill, not a general career path.
S0C7 is a data exception: an arithmetic instruction operated on data that isn't validly formatted for it, most often a packed-decimal field with invalid digits.
Sixteen general-purpose registers, each 64 bits wide, used as accumulators, base registers, and index registers. Every language that compiles for z/OS, not just assembler, targets this same register set. Addressing mode (AMODE 24, 31, or 64) then controls how much of a register a program can use as an address.
A program calculated or received an address it isn't authorized to touch. Common causes include an uncontrolled loop overwriting storage, a reference to a closed file's I/O area, mismatched Linkage Section definitions between a caller and callee, and moving data to an address under decimal 512.
No. HLASM, IBM's current assembler since June 1992, also runs on z/VSE, z/VM, and z/TPF, and Release 6 and later runs on Linux on IBM Z as well, generating ELF or GOFF object files there instead of the usual z/OS object format.
Not by the numbers, at least not yet. BMC's 2025 mainframe survey, its 20th and largest to date, found programmers aged 18-49 now make up roughly 80% of the mainframe workforce, up from about 53% in 2018. Whether that younger cohort is learning assembler specifically, versus COBOL or Java on Z, is a separate question the survey doesn't answer.
A lot. CICS alone, the transaction-processing environment most mainframe applications run inside, was reported to be handling roughly 30 billion transactions per day as of 2025, per Planet Mainframe's retrospective on CICS's 55th anniversary.
Real questions from public forum threads, with credit to who asked and who answered. Our answer restates the fix and links to the matching reference page where one exists.
My first assembler program abends S0C1 right after it prints its message. What went wrong?
Asked by kingo on zMainframes forum, ongoing thread
“You need a BR 14 (or whatever exit instruction the original program you copied used) to leave the program before the constant. Without it, execution runs straight off the end of your code into a DC C'TEST' data declaration and tries to execute the character data as instructions.”
Robert Sample (Global Moderator)
This is close to a rite of passage in learning assembler: without an explicit branch back to the caller, execution does not stop, it keeps reading whatever comes next in storage as instructions. If S0C1 appears right after code that looks correct, check that the very next thing in the source is data, not another instruction, and that there is a BR 14 or equivalent between them.
Full reference page →I added a few lines to a working assembler program and now it abends. I didn't touch the logic that was already there. What's the usual cause?
Asked by common pattern, compiled from field troubleshooting on planetmvs.com, reference article
“One of the most common ways to break working assembler is to reuse a register that is already serving as a base register from a USING statement. New code that loads a value into that register, without realizing it is already spoken for, corrupts every address computed from it afterward, and the abend can surface far from the actual mistake.”
planetmvs.com
Before assuming the new code itself has a logic bug, check whether it reused a register that an existing USING statement already claims as a base register. This is exactly the kind of mistake that produces an abend in code you didn't touch, because the corruption happens in the new code but the crash happens wherever the now-wrong base register is used next.
Full reference page →Compilers & platforms
Disambiguation across vendors.
Modernization guides
Cost, timeline, migration paths.
Vendors
Modernization consultancy comparison.
Migration checklist
What to check before or after a compiler move.
Salary & rate benchmark
What Assembly work actually pays.
Vendor match
Two questions to the right vendor.