Disassembled Code Window


The disassembled code window contains a disassembled version of the machine language program last loaded into memory. The contents of the disassembler window are not updated when you directly edit memory. The disassembled code is generated only when a machine language program is loaded into memory (from a file or by the assembler). The table below shows the original source code, the machine language code, and the disassembled code for a simple program.

Source Code Machine Code Disassembled Code
.constant
  OBJREF 100
.end-constant

.main
  .var
    sum
  .end-var

  ldc_w OBJREF
  bipush 10
  bipush 20
  invokevirtual Add
  istore sum
  halt
.end-main

.method Add(a, b)
  iload a
  iload b
  iadd
  ireturn
.end-method
MAIN:

   0:  LDC_W value 100
   3:  BIPUSH 10
   5:  BIPUSH 20
   7:  INVOKEVIRTUAL method at 13
  10:  ISTORE 0
  12:  HALT

Method 1:

  13:  3 Parameters
  15:  0 Local Variables
  17:  ILOAD 1
  19:  ILOAD 2
  21:  IADD
  22:  IRETURN

Each disassembled instruction is preceded by its address in memory. The format of the disassembled code was chosen to make it as easy to read as possible. As a result, there are some differences between the way in which the machine language instructions are actually stored and the way in which they are represented in the disassembled code.

Constant Values are Displayed Rather Than Offsets

There are two instructions that access constants: LDC_W and INVOKEVIRTUAL. In both cases, the operand is a two-byte offset which is added to the value in the CPP register to yield the address of the constant value. The LDC_W instruction loads the constant value onto the top of the stack. The INVOKEVIRTUAL instruction uses the constant value as the address of the method being invoked.

A literal disassembly of these instructions would display the offset:

0:  LDC_W 0
7:  INVOKEVIRTUAL 1

The disassembler, however, uses the offset to find the corresponding constant value and displays that value in the disassembled code:

0:  LDC_W value 100
7:  INVOKEVIRTUAL method at 13

Method Headers

The first four bytes of a method are used to store the number of parameters (the first two bytes) and the number of local variables (the second two bytes). In the disassembled code window, these values are labeled and shown at their respective addresses:

13:  3 Parameters
15:  0 Local Variables

Branching Operands

The operand of an IJVM branching instruction (GOTO, IFEQ, IFLT, or IF_ICMPEQ) is an offset that is added to the address at which the instruction opcode is stored. For example,

20  IFEQ 10

means "If the top of the stack is zero then jump forward 10 bytes". The disassembler, however, performs the arithmetic and displays the target address as shown here:

20  IFEQ goto 30

Disassembler Limitations

Disassembly is complicated by the fact that there is nothing in the machine language code to indicate when one method (including the main method) ends and another begins. The disassembler will work correctly as long as an invocation of a method is found in the code before reaching the corresponding method definition.

For example, suppose the main method invokes method A and method A invokes method B. If method A precedes method B in the code area, the disassembler will function properly (see the example below). When it processes the invocation of method A (in the main method) it will remember the address of method A (from the constant pool). When it gets to that address, it will know that it has reached the beginning of method A. In a similar fashion, when it gets to the invocation of method B (within method A) it will remember the address of method B. When it reaches that address, it will know that it has reached the beginning of method B.

Source Code Disassembled Code
.constant
  OBJREF 0
.end-constant

.main
  ldc_w OBJREF
  invokevirtual A
  halt
.end-main

.method A()
  ldc_w OBJREF
  invokevirtual B
  ireturn
.end-method

.method B()
  ireturn
.end-method
MAIN:

   0:  LDC_W value 0
   3:  INVOKEVIRTUAL method at 7
   6:  HALT

Method 1:

   7:  1 Parameter
   9:  0 Local Variables
  11:  LDC_W value 0
  14:  INVOKEVIRTUAL method at 18
  17:  IRETURN

Method 2:

  18:  1 Parameter
  20:  0 Local Variables
  22:  IRETURN

On the other hand, if method B precedes method A in the code then the disassembler will not work correctly (see the example below). Since the main method does not invoke method B, the disassembler will not know that method B starts at address 7. In fact, it won't even know that method B exists. The value at address 7 is 0 (the high-order byte of the number of parameters). An opcode of 0 corresponds to the NOP (no operation) instruction. At address 8, the disassembler finds the low-order byte of the number of parameters (1). No instruction has an opcode of 1 so the disassembler displays ??? to indicate that this is an invalid opcode. And so it goes; the disassembler doing the best that it can.

Source Code Disassembled Code
.constant
  OBJREF 0
.end-constant

.main
  ldc_w OBJREF
  invokevirtual A
  halt
.end-main

.method B()
  ireturn
.end-method

.method A()
  ldc_w OBJREF
  invokevirtual B
  ireturn
.end-method
MAIN:

   0:  LDC_W value 0
   3:  INVOKEVIRTUAL method at 12
   6:  HALT
   7:  NOP
   8:  ???
   9:  NOP
  10:  NOP
  11:  IRETURN

Method 1:

  12:  1 Parameter
  14:  0 Local variables
  16:  LDC_W value 0
  19:  INVOKEVIRTUAL method at 7
  22:  IRETURN

Methods should always be placed in the source code so that they follow a method in which they are invoked.

Note: Programs that fail to do so will still run correctly but the disassembler will not be able to disassemble them properly.