Why assembler still matters: registers and addressing
Assembler is the human-readable form of the machine's own instructions. Most new code is not written in it, but exits, system code and older performance-critical modules are, and every dump you read is ultimately in its terms. This lesson covers the registers and the base-plus-displacement addressing that everything else builds on.
Why learn assembler at all
IBM's High Level Assembler (HLASM) turns mnemonic instructions such as L and MVC into the machine code the processor runs. Very few teams start new business applications in it, yet it remains part of everyday mainframe work for three reasons.
- Exits and system code. z/OS, JES2, CICS, DB2, RACF and many vendor products let sites plug in their own routines at defined points. Many of these exits must be written in assembler because they run with system-level interfaces.
- Legacy and performance. Many shops still run assembler subroutines written decades ago for date handling, table lookups or I/O, called from COBOL. Someone has to maintain them.
- Debugging. When a program abends, the dump shows registers, storage and machine instructions. Even for COBOL failures, being able to read a few assembler instructions turns a dump from noise into evidence.
General purpose registers
The processor has 16 general purpose registers, numbered 0 to 15 and usually written R0 to R15. In z/Architecture each is 64 bits wide, but a great deal of existing code uses only the low 32 bits. Registers are the processor's scratch pad: arithmetic, comparisons and address calculations happen in registers, and storage is loaded into them or stored from them.
| Register | Conventional use |
|---|---|
| R0 | Work register; cannot be used as a base or index (0 there means no register) |
| R1 | Address of the parameter list passed to a called program |
| R2 to R11 | Free for the program: work values, base registers, pointers to data |
| R12 | Often the program's base register (a convention, not a rule) |
| R13 | Address of the current save area |
| R14 | Return address |
| R15 | Entry point address on a call; return code on the way back |
There are also floating-point, access and control registers, but application assembler mostly lives in the general purpose ones.
Base and displacement addressing
Instructions do not usually hold a full storage address. Instead, an operand names a base register and a displacement. The processor adds the displacement to the address in the base register (plus an optional index register) to get the effective address. Classic instructions have a 12-bit displacement, so one base register can reach 4,096 bytes (0 to 4095 past the base).
Because addresses are relative to a register, a load module can be placed anywhere in storage. The program loads its own address into a base register at entry, and the USING statement tells the assembler which register to assume holds which address, so it can work out displacements for every label. Code larger than 4K needs extra base registers or a different technique.
Newer instructions offer a 20-bit signed long displacement, and relative branch instructions (such as J and BRC) branch by an offset from the current instruction. Code written this way, often called baseless, needs no base register for its instructions, though it still uses registers to address data.
Instruction formats, at concept level
Every instruction is 2, 4 or 6 bytes long. Its format describes where the operands come from. You do not need to memorise encodings, but recognising the format tells you what an instruction touches.
| Format | Length | Operands | Example |
|---|---|---|---|
| RR | 2 bytes | Register and register | BALR 14,15 / LR 3,4 |
| RX | 4 bytes | Register and storage (base, index, displacement) | L 3,COUNT |
| RS | 4 bytes | Registers and storage, no index | STM 14,12,12(13) |
| SI | 4 bytes | Storage and an immediate byte | MVI FLAG,C'Y' |
| SS | 6 bytes | Storage to storage, with length | MVC OUT(8),IN |
LOC OBJECT CODE ADDR1 ADDR2 STMT SOURCE STATEMENT
000000 1 PAYCALC CSECT
000000 90EC D00C 0000C 2 STM 14,12,12(13)
000004 18CF 3 LR 12,15
00000 4 USING PAYCALC,12
000006 5830 C040 00040 5 L 3,COUNTRead the listing left to right: the location within the module, the generated machine code, the resolved addresses, then your source. The object code 5830 C040 is L (X'58') into R3, using base R12 with displacement X'040'. This is exactly the view you need when matching a dump offset to a line of source.
Addressing modes
Programs run in 24-bit, 31-bit or 64-bit addressing mode (AMODE), and are loaded below or above the 16 MB line according to their residency mode (RMODE). Old modules are often AMODE 24, which matters when they are called by newer code passing addresses above the line: a classic source of S0C4 abends.
Common mistakes
Most real work is maintenance and dump reading. Start by following listings and existing modules, then make small changes under review.
R12 is a common convention only. Check the USING statements in the listing to see which registers the assembler is actually using as bases.
Passing a 31-bit address to an AMODE 24 routine, or the reverse, causes wrong-address abends. Check the binder output for the modes of every module involved.
What you will see at work
- Systems programmers maintain JES2, SMF, RACF and other exits written in assembler, and review them at every product upgrade.
- Application teams often own a handful of old assembler subroutines called from COBOL, and someone must be able to change them safely.
- Production support reads registers and listings during abend analysis, even when the failing program was COBOL.
Key terms
Check your understanding.
Take this lesson's quiz and save your progress. Free.