Annotation of wikisrc/examples/netbsd_assembly.mdwn, revision 1.3

1.2       schmonz     1: **Contents**
                      2: 
                      3: [[!toc levels=3]]
                      4: 
                      5: #   Assembly? 
                      6: 
                      7: _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. 
                      8: 
                      9: 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. 
                     10: 
                     11: There remain a few reasons to use assembly language. For example: 
                     12: 
                     13:   * You need direct access to processor registers (for example, to set the stack pointer). 
                     14:   * You need direct access to processor instructions (like for vector arithmetic or for atomic operations). 
                     15:   * You want to improve or fix the higher-level compiler, assembler, or linker. 
                     16:   * You want to optimize code, because your higher-level compiler was not good enough. 
                     17:   * You want to learn assembly language. 
                     18: 
                     19: #   i386 
                     20: 
                     21: _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: 
                     22: 
                     23:   * _IA-32_, which means Intel Architecture, 32 bit. 
                     24:   * _x86_, which can mean the 32-bit mode or the ancient 16-bit mode. 
                     25: 
                     26: The i386 assembly language is either AT&T syntax or Intel syntax. Most programmers seem to prefer the Intel syntax. 
                     27: 
                     28: ##   nasm 
                     29: 
1.3     ! leot       30: NASM (the Netwide Assembler) is an x86 assembler that uses the Intel syntax. It is easily available via [devel/nasm](http://pkgsrc.se/devel/nasm#main). 
1.2       schmonz    31: 
                     32: You can also use [devel/yasm](http://pkgsrc.se/devel/yasm#main) with [devel/nasm](http://pkgsrc.se/devel/nasm#main) syntax. 
                     33: 
                     34: 
                     35: ###   Hello world, NetBSD/i386 
                     36:     
                     37:     ; Hello world, NetBSD/i386 4.0
                     38:     
                     39:     section .note.netbsd.ident progbits alloc noexec nowrite
                     40:        dd 0x00000007 ; Name size
                     41:        dd 0x00000004 ; Desc size
                     42:        dd 0x00000001 ; value 0x01
                     43:        db "NetBSD", 0x00, 0x00 ; "NetBSD\0\0"
                     44:        db 400000003 ; __NetBSD_Version__ (please see <sys/param.h>)
                     45:     
                     46:     section .data
                     47:        msg db "Hello world!", 0x0a ; "Hello world\n"
                     48:        len equ $ - msg
                     49:     
                     50:     section .text
                     51:        global _start
                     52:     
                     53:     _start:
                     54:        ; write()
                     55:        mov eax, 0x04 ; SYS_write
                     56:        push len ; write(..., size_t nbytes)
                     57:        push msg ; write(..., const void *buf, ...)
                     58:        push 0x01 ; write(int fd, ...)
                     59:        push 0x00
                     60:        int 0x80
                     61:        pop ebx
                     62:     
                     63:        ; exit()
                     64:        mov eax, 0x01 ; SYS_exit
                     65:        push 0x00 ; exit(int status)
                     66:        push 0x00
                     67:        int 0x80
                     68:     
                     69: 
                     70: ###   How to compile and link 
                     71: 
1.3     ! leot       72: To use the above code you need to compile and then link it: 
1.2       schmonz    73:     
                     74:     $ nasm -f elf hello.asm
                     75:     $ ld -o hello hello.o
                     76:     $ ./hello
                     77:     Hello world!
                     78:     
                     79: 
                     80: ##   gas 
                     81: 
                     82: _the portable GNU assembler_
                     83: 
1.3     ! leot       84: It uses AT&T syntax and is designed after the 4.2BSD assembler. You can use it on many CPU architectures. 
1.2       schmonz    85: 
                     86: Example: 
                     87:     
                     88:     
                     89:     .section ".note.netbsd.ident", "a"
                     90:             .long   2f-1f
                     91:             .long   4f-3f
                     92:             .long   1
                     93:     1:      .asciz  "NetBSD"
                     94:     2:      .p2align 2
                     95:     3:      .long   400000000
                     96:     4:      .p2align 2
                     97:     
                     98:     .section .data
                     99:         data_items:                                # this is an array
                    100:             .long 3,39,41,21,42,34,42,23,38,37,15,37,16,17,18,25,23,12,31,2
                    101:             .set DATASIZE, ( . - data_items) / 4 - 1
                    102:     
                    103:     .section .text
                    104:         .globl _start
                    105:     
                    106:         _start:
                    107:             movl    $0, %edi                        # zero the index register
                    108:             movl    $DATASIZE, %ecx                 # set ecx to number of items
                    109:             movl    data_items(,%ecx,4), %eax       # load first item
                    110:             movl    %eax, %ebx                      # its the biggest atm
                    111:     
                    112:         main_loop:
                    113:             decl    %ecx                            # decrement counter
                    114:             movl    data_items(,%ecx,4), %eax       # step to next element
                    115:             cmpl    %eax, %ebx                      # is it greater?
                    116:             cmovll  %eax, %ebx                      # set ebx to greater if its less
                    117:      than cur. num.
                    118:             jecxz   end_prog                        # if we are at item 0 end iterat
                    119:     ion
                    120:             jmp     main_loop                       # again!
                    121:     
                    122:         end_prog:
                    123:             pushl   %ebx                            # return largest number
                    124:             pushl   %ebx                            # BSD-ism (has to push twice?)
                    125:             movl    $1, %eax                        # call exit
                    126:             int     $0x80                           # kernel
                    127:             ret
                    128:     
                    129: 
                    130: #   powerpc 
                    131: 
                    132: _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). 
                    133: 
                    134: PowerPC processors have 32-bit registers and pointers and use big-endian byte order. 
                    135: 
                    136:   * A very few boards (not with NetBSD) run the PowerPC in little-endian mode to match the hardware. 
                    137:   * 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/)). 
                    138: 
                    139: 
                    140: ##   gas 
                    141: 
                    142: Here is an example of a program for gas: 
                    143:     
                    144:     
                    145:     ##  factorial.s
                    146:     ##  This program is in the public domain and has no copyright.
                    147:     ###
                    148:     ##  This is an example of an assembly program for NetBSD/powerpc.
                    149:     ##  It computes the factorial of NUMBER using unsigned 32-bit integers
                    150:     ##  and prints the answer to standard output.
                    151:     
                    152:        .set    NUMBER, 10
                    153:        
                    154:     .section ".note.netbsd.ident", "a"
                    155:        
                    156:        #  ELF note to identify me as a native NetBSD program
                    157:        #  type = 0x01, desc = __NetBSD_Version__ from <sys/param.h>
                    158:        ##      
                    159:        .int    7                       # length of name        
                    160:        .int    4                       # length of desc
                    161:        .int    0x01                    # type
                    162:        .ascii  "NetBSD\0"              # name
                    163:        .ascii  "\0"                    # padding
                    164:        .int    500000003               # desc
                    165:     
                    166:     .section ".data"
                    167:        
                    168:     decbuffer:
                    169:        .fill   16                      # buffer for decimal ASCII
                    170:     decbufend:
                    171:        .ascii  "\n"                    # newline at end of ASCII
                    172:        
                    173:     .section ".text"
                    174:        
                    175:        #  PowerPC instructions need an alignment of 4 bytes
                    176:        .balign 4
                    177:        
                    178:        .globl  _start
                    179:        .type   _start, @function
                    180:     _start:
                    181:        #  compute factorial in %r31
                    182:        li      %r0, NUMBER
                    183:        mtctr   %r0                     # ctr = number
                    184:        li      %r31, 1                 # %r31 = factorial
                    185:        li      %r30, 1                 # %r30 = next factor
                    186:     factorial_loop:
                    187:        mullw   %r31, %r31, %r30        # multiply %r31 by next factor
                    188:        addi    %r30, %r30, 1           # increment next factor
                    189:        bdnz+   factorial_loop          # loop ctr times
                    190:     
                    191:        #  prepare to convert factorial %r31 to ASCII.
                    192:        lis     %r9, decbufend@ha
                    193:        la      %r4, decbufend@l(%r9)   # %r4 = decbufend
                    194:        lis     %r8, decbuffer@ha
                    195:        la      %r29, decbuffer@l(%r8)  # %r29 = decbuffer
                    196:        li      %r5, 1                  # %r5 = length of ASCII
                    197:     
                    198:        #  Each loop iteration divides %r31 by 10 and writes digit to
                    199:        #  position %r4. Formula (suggested by gcc) to divide by 10,
                    200:        #                     0xcccccccd
                    201:        #  is to multiply by ----------- = 0.100000000005821
                    202:        #                    0x800000000
                    203:        #  which is to multiply by 0xcccccccd, then shift right 35.
                    204:        ##
                    205:        .set    numerator, 0xcccccccd
                    206:        lis     %r9, numerator@ha
                    207:        la      %r28, numerator@l(%r9)  # %r28 = numerator
                    208:     decloop:
                    209:        cmpw    %r29, %r4               # start of buffer <=> position
                    210:        beq-    buffer_overflow
                    211:        #  begin %r9 = (%r31 / 10)
                    212:        mulhwu  %r9, %r31, %r28         # %r9 = ((%r31 * %r28) >> 32)
                    213:        addi    %r4, %r4, -1            # move %r4 to next position
                    214:        srwi    %r9, %r9, 3             # %r9 = (%r9 >> 3) = %r31 / 10
                    215:        mulli   %r8, %r9, 10            # %r8 = (%r31 / 10) * 10
                    216:        sub     %r27, %r31, %r8         # %r27 = %r31 % 10 = digit
                    217:        addi    %r27, %r27, '0          # convert digit to ASCII
                    218:        addi    %r5, %r5, 1             # count this ASCII digit
                    219:        stb     %r27, 0(%r4)            # write ASCII digit to buffer
                    220:        mr.     %r31, %r9               # %r31 /= 10, %r31 <=> 0
                    221:        bne+    decloop                 # loop until %r31 == 0
                    222:        #  FALLTHROUGH
                    223:     
                    224:     buffer_overflow:
                    225:        #  write(2) our factorial to standard output
                    226:        li      %r0, 4                  # SYS_write from <sys/syscall.h>
                    227:        li      %r3, 1                  # standard output
                    228:        ##      %r4                     # buffer
                    229:        ##      %r5                     # size of buffer
                    230:        sc
                    231:     
                    232:        #  exit(2)
                    233:        li      %r0, 1                  # SYS_exit from <sys/syscall.h>
                    234:        li      %r3, 0                  # exit status
                    235:        sc
                    236:     
                    237:        .size   _start, . - _start
                    238:     
                    239: 
                    240: With a NetBSD/powerpc system, you can run this program using 
                    241:     
                    242:     $ as -o factorial.o factorial.s
                    243:     $ ld -o factorial factorial.o
                    244:     $ ./factorial
                    245:     3628800
                    246:     $
                    247:     
                    248: 
                    249: ##   Useful Documents 
                    250: 
                    251: To learn about PowerPC assembly language, here are two documents to start with. 
                    252: 
                    253:   * 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). 
                    254:   * 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. 
                    255: 
                    256:     * NetBSD, Linux and OpenBSD (and FreeBSD?) all use ELF with PowerPC and all follow this specification, with a few deviations and extensions. 
                    257: 
                    258: ##   Wiki Pages 
                    259: 
                    260:   * [[ELF Executables for PowerPC]]. This introduces assembly language with a commented example. 
                    261: 

CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb