_Assembly_ is the programming language that gives direct access to the instructions and registers of the processor. A program called the _assembler_ compiles assembly language into machine code. NetBSD installs the GNU assembler "gas" into /usr/bin/as and this program assembles for the host processor architecture.
A higher-level compiler like "gcc" acts as a preprocessor to the assembler, by translating code from C (or other language) to assembler. Just run cc -S yourfile.c and look at the output yourfile.s to see assembly code. A higher-level compiler can probably write better assembly code than a human programmer who knows assembly language.
There remain a few reasons to use assembly language. For example:
* You need direct access to processor registers (for example, to set the stack pointer).
* You need direct access to processor instructions (like for vector arithmetic or for atomic operations).
* You want to improve or fix the higher-level compiler, assembler, or linker.
* You want to optimize code, because your higher-level compiler was not good enough.
* You want to learn assembly language.
_i386_ architecture takes its name from the Intel 386, the first x86 processor to have a 32-bit mode. Other names for this architecture are:
* _IA-32_, which means Intel Architecture, 32 bit.
* _x86_, which can mean the 32-bit mode or the ancient 16-bit mode.
The i386 assembly language is either AT&T syntax or Intel syntax. Most programmers seem to prefer the Intel syntax.
NASM (the Netwide Assembler) is a x86 assembler that uses the Intel syntax. It is easily available via [devel/nasm](http://pkgsrc.se/devel/nasm#main).
You can also use [devel/yasm](http://pkgsrc.se/devel/yasm#main) with [devel/nasm](http://pkgsrc.se/devel/nasm#main) syntax.
### Hello world, NetBSD/i386
; Hello world, NetBSD/i386 4.0
section .note.netbsd.ident progbits alloc noexec nowrite
dd 0x00000007 ; Name size
dd 0x00000004 ; Desc size
dd 0x00000001 ; value 0x01
db "NetBSD", 0x00, 0x00 ; "NetBSD\0\0"
db 400000003 ; __NetBSD_Version__ (please see <sys/param.h>)
msg db "Hello world!", 0x0a ; "Hello world\n"
len equ $ - msg
mov eax, 0x04 ; SYS_write
push len ; write(..., size_t nbytes)
push msg ; write(..., const void *buf, ...)
push 0x01 ; write(int fd, ...)
mov eax, 0x01 ; SYS_exit
push 0x00 ; exit(int status)
### How to compile and link
To use the above codes you need to compile and then link them:
$ nasm -f elf hello.asm
$ ld -o hello hello.o
_the portable GNU assembler_
It uses AT&T syntax and designed after the 4.2BSD assembler. You can use it on many CPU architectures.
.section ".note.netbsd.ident", "a"
1: .asciz "NetBSD"
2: .p2align 2
3: .long 400000000
4: .p2align 2
data_items: # this is an array
.set DATASIZE, ( . - data_items) / 4 - 1
movl $0, %edi # zero the index register
movl $DATASIZE, %ecx # set ecx to number of items
movl data_items(,%ecx,4), %eax # load first item
movl %eax, %ebx # its the biggest atm
decl %ecx # decrement counter
movl data_items(,%ecx,4), %eax # step to next element
cmpl %eax, %ebx # is it greater?
cmovll %eax, %ebx # set ebx to greater if its less
than cur. num.
jecxz end_prog # if we are at item 0 end iterat
jmp main_loop # again!
pushl %ebx # return largest number
pushl %ebx # BSD-ism (has to push twice?)
movl $1, %eax # call exit
int $0x80 # kernel
_PowerPC_ processors appear inside multiple different hardware platforms; NetBSD has at least 11 ports, see [[Platforms#PowerPC]]. The easiest way to obtain a PowerPC machine is probably to acquire a used Macintosh, choosing from among the [supported models for NetBSD/macppc](http://www.netbsd.org/ports/macppc/models.html).
PowerPC processors have 32-bit registers and pointers and use big-endian byte order.
* A very few boards (not with NetBSD) run the PowerPC in little-endian mode to match the hardware.
* A few PowerPC processors also have a 64-bit mode. NetBSD 5.0 will support some Apple G5 machines with these processors, but only in 32-bit mode (see [ppcg5 project](http://netbsd-soc.sourceforge.net/projects/ppcg5/)).
Here is an example of a program for gas:
## This program is in the public domain and has no copyright.
## This is an example of an assembly program for NetBSD/powerpc.
## It computes the factorial of NUMBER using unsigned 32-bit integers
## and prints the answer to standard output.
.set NUMBER, 10
.section ".note.netbsd.ident", "a"
# ELF note to identify me as a native NetBSD program
# type = 0x01, desc = __NetBSD_Version__ from <sys/param.h>
.int 7 # length of name
.int 4 # length of desc
.int 0x01 # type
.ascii "NetBSD\0" # name
.ascii "\0" # padding
.int 500000003 # desc
.fill 16 # buffer for decimal ASCII
.ascii "\n" # newline at end of ASCII
# PowerPC instructions need an alignment of 4 bytes
.type _start, @function
# compute factorial in %r31
li %r0, NUMBER
mtctr %r0 # ctr = number
li %r31, 1 # %r31 = factorial
li %r30, 1 # %r30 = next factor
mullw %r31, %r31, %r30 # multiply %r31 by next factor
addi %r30, %r30, 1 # increment next factor
bdnz+ factorial_loop # loop ctr times
# prepare to convert factorial %r31 to ASCII.
lis %r9, decbufend@ha
la %r4, decbufend@l(%r9) # %r4 = decbufend
lis %r8, decbuffer@ha
la %r29, decbuffer@l(%r8) # %r29 = decbuffer
li %r5, 1 # %r5 = length of ASCII
# Each loop iteration divides %r31 by 10 and writes digit to
# position %r4. Formula (suggested by gcc) to divide by 10,
# is to multiply by ----------- = 0.100000000005821
# which is to multiply by 0xcccccccd, then shift right 35.
.set numerator, 0xcccccccd
lis %r9, numerator@ha
la %r28, numerator@l(%r9) # %r28 = numerator
cmpw %r29, %r4 # start of buffer <=> position
# begin %r9 = (%r31 / 10)
mulhwu %r9, %r31, %r28 # %r9 = ((%r31 * %r28) >> 32)
addi %r4, %r4, -1 # move %r4 to next position
srwi %r9, %r9, 3 # %r9 = (%r9 >> 3) = %r31 / 10
mulli %r8, %r9, 10 # %r8 = (%r31 / 10) * 10
sub %r27, %r31, %r8 # %r27 = %r31 % 10 = digit
addi %r27, %r27, '0 # convert digit to ASCII
addi %r5, %r5, 1 # count this ASCII digit
stb %r27, 0(%r4) # write ASCII digit to buffer
mr. %r31, %r9 # %r31 /= 10, %r31 <=> 0
bne+ decloop # loop until %r31 == 0
# write(2) our factorial to standard output
li %r0, 4 # SYS_write from <sys/syscall.h>
li %r3, 1 # standard output
## %r4 # buffer
## %r5 # size of buffer
li %r0, 1 # SYS_exit from <sys/syscall.h>
li %r3, 0 # exit status
.size _start, . - _start
With a NetBSD/powerpc system, you can run this program using
$ as -o factorial.o factorial.s
$ ld -o factorial factorial.o
## Useful Documents
To learn about PowerPC assembly language, here are two documents to start with.
* IBM developerWorks. [PowerPC Assembly](http://www.ibm.com/developerworks/library/l-ppc/). This is a very good introduction to PowerPC assembly. It provides and explains the Hello World example (but using a Linux system call).
* SunSoft and IBM. [System V Application Binary Interface, PowerPC Processor Supplement](http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf) (PDF file, hosted by Linux Foundation). This is the specification for 32-bit PowerPC code in ELF systems. It establishes %r1 as the stack pointer and describes the stack layout. It explains the C calling conventions, how to pass arguments to and return values from C functions, how to align data structures, and which registers to save to the stack.
* NetBSD, Linux and OpenBSD (and FreeBSD?) all use ELF with PowerPC and all follow this specification, with a few deviations and extensions.
## Wiki Pages
* [[ELF Executables for PowerPC]]. This introduces assembly language with a commented example.
CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb