Mainframe Path Start learning free
Applied10 min readLesson 1 of 3

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.

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.

RegisterConventional use
R0Work register; cannot be used as a base or index (0 there means no register)
R1Address of the parameter list passed to a called program
R2 to R11Free for the program: work values, base registers, pointers to data
R12Often the program's base register (a convention, not a rule)
R13Address of the current save area
R14Return address
R15Entry 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).

How an operand address is formed
Base registere.g. R12 = 00027000
+ index (optional)e.g. R4 = 00000010
+ displacement0 to 4095 in classic formats
Effective addresswhere the data really is

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.

FormatLengthOperandsExample
RR2 bytesRegister and registerBALR 14,15 / LR 3,4
RX4 bytesRegister and storage (base, index, displacement)L 3,COUNT
RS4 bytesRegisters and storage, no indexSTM 14,12,12(13)
SI4 bytesStorage and an immediate byteMVI FLAG,C'Y'
SS6 bytesStorage to storage, with lengthMVC OUT(8),IN
Assembler listing extract (illustrative)
  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,COUNT

Read 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

Trying to write assembler before you can read it

Most real work is maintenance and dump reading. Start by following listings and existing modules, then make small changes under review.

Assuming R12 is always the base register

R12 is a common convention only. Check the USING statements in the listing to see which registers the assembler is actually using as bases.

Ignoring AMODE and RMODE

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

Key terms

Check your understanding.
Take this lesson's quiz and save your progress. Free.

Take the lesson quiz
Common instructions and linkage conventions →