AMD64: ISA and ASM
Intel released the 8086 processor in 1978. It was based on the earlier 8008
processor from 1972, but...
The 8086 was a 16-bit microproessor. That means:
- It had a 16-bit data bus connecting it to memory and maybe other stuff.
- That means a processor and RAM connected by 16 wires.
- How much RAM can we address with 16 bits?
- In addtion to RAM, this system gives us another place to put stuff called
registers. For a 16-bit processor, each register is 16 bits.
- The 8086 had 9-ish registers:
- "general purpose": ax, cx, dx, bx, si, di, bp, sp,
- "special purpose": ip, (segment registers, status register)
- What processors do is execute instructions. Kinds of instructions:
- Arithmetic: Example: add $5, %cx
- Test: cmp $5, %cx
- Conditional branch: jge bigger_label
- Movement instruction: mov (%sp), %dx
- A bunch of other stuff. You'll want to have a reference sheet.
- Instructions tend to operate on at least one register.
- Instructions can operate on memory addresses. If they do, the CPU needs
to stop and read or write from RAM.
The 80386 or i386 was a 32-bit microprocessor, backwards compatible with
the 8086. This was the first "Intel x86" processor:
- It had a 32-bit data bus.
- How much RAM can we address with 32-bits?
- It had 32-bit registers.
- If you used the old names (eg. %ax), you got the least significant
16-bits of the register.
- Each register got a new name with an "e" at the front to refer to
the full 32 bit "extended" register:
The AMD Athlon 64 was a 64-bit microprocessor, backwards compatible with the Intel
8086 and i386. This was the first "AMD64" processor:
- It had a 48-bit data bus, designed to be extended up to 64-bit later.
- How much RAM can we address with 64 bits?
- How about 48 bits?
- It had 64-bit registers.
- If you used the old names (e.g. %ax, %rax), you got the least significant
16 or 32 bits of the register.
- Each register got a new name with an "r" at the front to refer to
the full 64 bit register.
- 8 new general purpose registers were added: %r9, %r10, ..., %r15
And that's where we are today. Let's write an add2 program by hand in amd64
assembly:
# add2.s
.global main
.text
# long add2(long x)
# - the argument comes in in %rdi
# - we return the result by putting it in %rax
add2:
enter $0, $0
# long y = x;
mov %rdi, %rax
# y = y + 2;
add $2, %rax
# return y;
leave
ret
main:
enter $0, $0
# long x = 5;
mov $5, %rdi
# y = add1(x)
call add2
# result in %rax
# printf("%ld\n", y)
# - first arg goes in %rdi
# - second arg goes in %rsi
# - for a variable arg function, we need to zero %al
# - %al is the bottom 8 bits of %ax/%eax/%rax
mov $long_fmt, %rdi
mov %rax, %rsi
mov $0, %al
call printf
leave
ret
.data
long_fmt: .string "%ld\n"
To compile this simple hand-written assembly, we use:
$ gcc -no-pie -o add2 add2.s