# Section 1 / Register Sizes

## Overview

In each of the various sets of registers, each register can be referred
to by different synonyms which determine how wide the register operation
will be.

## General Purpose Registers

| Intended Width | Register Prefix | Instruction Postfix |
| -------------- | --------------- | ------------------- |
| 8 bytes | `x` | NA |
| 4 bytes | `w` | NA |
| 2 bytes | `w` | `h` |
| 1 byte | `w` | `b` |

### `ldr` (and `ldp`)

```asm
    ldr    x0, [sp]   // load 8 bytes from address specified by sp
    ldr    w0, [sp]   // load 4 bytes from address specified by sp
    ldrh   w0, [sp]   // load 2 bytes from address specified by sp
    ldrb   w0, [sp]   // load 1 byte  from address specified by sp
```

The address from which a load is taking *should* match the alignment of
what is being loaded. That is, a long *should* be found only at
addresses which are a multiple of 8 (the size of a long).

**When misaligned accesses to RAM are made, the processor must slow down
and access each byte individually. This is a big performance hit.
Properly aligned access is critical to performance.**

### `str` (and `stp`)

```asm
    str    x0, [sp]   // store 8 bytes to address specified by sp
    str    w0, [sp]   // store 4 bytes to address specified by sp
    strh   w0, [sp]   // store 2 bytes to address specified by sp
    strb   w0, [sp]   // store 1 byte  to address specified by sp
```

See above for comments about misaligned memory access.

### Example

Let's look at this program:

```asm
        .global    main                                           // 1 
        .text                                                     // 2 
        .align    2                                               // 3 
                                                                  // 4 
main:   mov     x0, xzr                                           // 5 
        ldr     x1, =ram                                          // 6 
        strb    w0, [x1]                                          // 7 
        strh    w0, [x1]                                          // 8 
        str     w0, [x1]                                          // 9 
        str     x0, [x1]                                          // 10 
        ret                                                       // 11 
                                                                  // 12 
        .data                                                     // 13 
ram:    .quad   0xFFFFFFFFFFFFFFFF                                // 14 
                                                                  // 15 
        .end                                                      // 16 
                                                                  // 17 
```

`Line 14` puts an identifiable pattern into 8 bytes of RAM and gives them the symbol `ram`.

`Line 6` gets the address of these bytes into `x1`.

The next four lines put zeros into that memory using progressively wider store instructions.

The following is a `gdb` session running the above program. Line numbers have been added to assist with the description of the session. Rather than describe all after a wall of text, descriptions will be provided inline.

```text
(gdb) b main                                                            // 1 
Breakpoint 1 at 0x740: file align.s, line 5.                            // 2 
```

Immediately after entering `gdb` we set a breakpoint at `main`.

```text
(gdb) run                                                               // 3 
Starting program: /media/psf/Home/buffet/3510/pk_do/regs/a.out          // 4 
                                                                        // 5 
Breakpoint 1, main () at align.s:5                                      // 6 
5    main:    mov     x0, xzr                                           // 7 
```

We launched the program and `gdb` stops its execution upon reaching the
breakpoint.

```text
(gdb) p/x $x0                                                           // 8 
$1 = 0x1                                                                // 9 
```

Before putting zero into `x0`, let's see what it currently holds... the
value 1. Recall this is `argc`. The `p` command means `print` and is
used to print the values in registers. The modifier `/x` says to print
in hexadecimal.

```text
(gdb) n                                                                 // 10 
6            ldr     x1, =ram                                           // 11 
(gdb) p/x $x0                                                           // 12 
$2 = 0x0                                                                // 13 
```

After putting zero into `x0`, we confirm its contents.

```text
(gdb) p/x $x1                                                           // 14 
$3 = 0xfffffffff028                                                     // 15 
```

Prior to loading the address of 8 bytes found with the label `ram`, we
print out the value already sitting in `x1`. The address it contains
will be the address of the `C`-string containing the name of the program
being run. Notice this value is 6-bytes long and not 8 as we might have
expected. Why? The answer relates to the size of the virtual address
each program is allowed. A full 64-bit virtual address space would make
certain OS data structures too large for efficiency.

```text
(gdb) n                                                            // 16 
7            strb    w0, [x1]                                      // 17 
(gdb) p/x $x1                                                      // 18 
$4 = 0xaaaaaaab1010                                                // 19 
```

After loading the address of `ram` into `x1`, we confirm its contents.

```text
(gdb) p/x &ram                                                     // 20 
$5 = 0xaaaaaaab1010                                                // 21 
```

Just for kicks, we confirm that the previous instruction really did get
the address correctly.

```text
(gdb) x/x &ram                                                     // 22 
0xaaaaaaab1010:    0xffffffff                                      // 23 
```

We shift from `p`rint to e`x`amine to reach into memory and see what is
found at `ram`.

```text
(gdb) x/gx &ram                                                    // 24 
0xaaaaaaab1010:    0xffffffffffffffff                              // 25 
```

Adding the `g` (for `g`iant) we can see all 8 bytes.

```text
(gdb) n                                                            // 26 
8            strh    w0, [x1]                                      // 27 
(gdb) x/gx &ram                                                    // 28 
0xaaaaaaab1010:    0xffffffffffffff00                              // 29 
```

We just did a `strb` and looking at memory, we see one byte's worth of zeros.

*Note: this brings up an interesting question... which byte is actually
sitting at the address of `ram`? We will have to look into this more
later.*

```text
(gdb) n                                                            // 30 
9            str     w0, [x1]                                      // 31 
(gdb) x/gx &ram                                                    // 32 
0xaaaaaaab1010:    0xffffffffffff0000                              // 33 
```

After storing a `short`.

```text
(gdb) n                                                            // 34 
10            str     x0, [x1]                                     // 35 
(gdb) x/gx &ram                                                    // 36 
0xaaaaaaab1010:    0xffffffff00000000                              // 37 
```

After storing an `int`.

```text
(gdb) n                                                            // 38 
11            ret                                                  // 39 
(gdb) x/gx &ram                                                    // 40 
0xaaaaaaab1010:    0x0000000000000000                              // 41 
(gdb) quit                                                         // 42 
```

And finally, after storing a `long`.

Let's circle back to the question asked above: Which byte is actually at
the address `ram`? When we examined the `long` just after putting in one
byte of zero, we saw this:

```text
(gdb) x/gx &ram                                                         // 28 
0xaaaaaaab1010:    0xffffffffffffff00                                   // 29 
```

Notice the zeros come at the end. Keep in mind, these bytes are printed
as a `long`.

But what if we look at these 8 bytes individually?

```text
(gdb) x/gx &ram
0xaaaaaaabb010: 0xffffffffffffff00
(gdb) x/8bx &ram
0xaaaaaaabb010: 0x00    0xff    0xff    0xff    0xff    0xff    0xff    0xff
```

Look at that... the *least significant* byte of a `long` comes **first**.

This is the definition of `little endian`.

The following image is from [here](https://medium.com/worldsensing-techblog/big-endian-or-little-endian-37c3ed008c94):

![eggs](./eggs.jpeg)

### Little Endian in More Detail

Given this program (not intended for meaningful execution... just
e`x`amining memory):

```asm
        .global    main                                                 // 1 
        .text                                                           // 2 
        .align    2                                                     // 3 
                                                                        // 4 
main:    mov     x0, xzr                                                // 5 
        ret                                                             // 6 
                                                                        // 7 
        .data                                                           // 8 
ram:    .quad   0xAABBCCDDEEFF0011                                      // 9 
        .end                                                            // 10 
```

let's take a look at the memory at location `ram` in two ways. Once
interpreted as a `long`:

```text
(gdb) x/gx &ram
0x11010:    0xaabbccddeeff0011
```

and then interpreted as 8 bytes appearing in the order of lowest address
to highest:

```text
(gdb) x/8bx &ram
0x11010:    0x11    0x00    0xff    0xee    0xdd    0xcc    0xbb    0xaa
```

Compare the order of the bytes. They are least significant to most
significant. Specifically:

* within a `long` the least significant `int` comes first
* within an `int`, the least significant `short` comes first
* within a `short` the least significant byte comes first

Endiannes isn't an issue unless you're exchanging data with a computer
that has a different endedness and then only if the data being
transferred is longer in native width than 1 byte. Text, expressed in
single bytes, is immune from endedness issues - text is an array of
bytes and is the same on all platforms.

### What Happens to the Rest of a Register When Only a Portion is Affected?

Whenever a narrower portion of a register is written to, the remainder
of the register is zero'd out. That is: `ldrb` overwrites the least
significant byte of an `x` register and zeros out the upper 7 bytes.

*There are dedicated instructions for manipulating bits in the middle of
registers*.

### Casting Between `int` Type

Casting between integer types is in some cases accomplished by `anding`
with `255` and `65535` (for `char` and `short`). Otherwise, see the
previous section (What Happens to the Rest of a Register...).
