Diff for /wikisrc/examples/netbsd_assembly.mdwn between versions 1.1 and 1.2

version 1.1, 2011/11/20 21:35:54 version 1.2, 2012/02/05 07:14:36
Line 1 Line 1
 **Contents**  **Contents**
   
 [[!toc levels=3]]  [[!toc levels=3]]
   
 #   Assembly?  #   Assembly? 
   
 _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.  _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.  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:  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 registers (for example, to set the stack pointer). 
   * You need direct access to processor instructions (like for vector arithmetic or for atomic operations).    * 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 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 optimize code, because your higher-level compiler was not good enough. 
   * You want to learn assembly language.    * You want to learn assembly language. 
   
 #   i386  #   i386 
   
 _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:  _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.    * _IA-32_, which means Intel Architecture, 32 bit. 
   * _x86_, which can mean the 32-bit mode or the ancient 16-bit mode.    * _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.  The i386 assembly language is either AT&T syntax or Intel syntax. Most programmers seem to prefer the Intel syntax. 
   
 ##   nasm  ##   nasm 
   
 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).  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.  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 
          
     ; Hello world, NetBSD/i386 4.0      ; Hello world, NetBSD/i386 4.0
          
     section .note.netbsd.ident progbits alloc noexec nowrite      section .note.netbsd.ident progbits alloc noexec nowrite
         dd 0x00000007 ; Name size          dd 0x00000007 ; Name size
         dd 0x00000004 ; Desc size          dd 0x00000004 ; Desc size
         dd 0x00000001 ; value 0x01          dd 0x00000001 ; value 0x01
         db "NetBSD", 0x00, 0x00 ; "NetBSD\0\0"          db "NetBSD", 0x00, 0x00 ; "NetBSD\0\0"
         db 400000003 ; __NetBSD_Version__ (please see <sys/param.h>)          db 400000003 ; __NetBSD_Version__ (please see <sys/param.h>)
          
     section .data      section .data
         msg db "Hello world!", 0x0a ; "Hello world\n"          msg db "Hello world!", 0x0a ; "Hello world\n"
         len equ $ - msg          len equ $ - msg
          
     section .text      section .text
         global _start          global _start
          
     _start:      _start:
         ; write()          ; write()
         mov eax, 0x04 ; SYS_write          mov eax, 0x04 ; SYS_write
         push len ; write(..., size_t nbytes)          push len ; write(..., size_t nbytes)
         push msg ; write(..., const void *buf, ...)          push msg ; write(..., const void *buf, ...)
         push 0x01 ; write(int fd, ...)          push 0x01 ; write(int fd, ...)
         push 0x00          push 0x00
         int 0x80          int 0x80
         pop ebx          pop ebx
          
         ; exit()          ; exit()
         mov eax, 0x01 ; SYS_exit          mov eax, 0x01 ; SYS_exit
         push 0x00 ; exit(int status)          push 0x00 ; exit(int status)
         push 0x00          push 0x00
         int 0x80          int 0x80
          
   
 ###   How to compile and link  ###   How to compile and link 
   
 To use the above codes you need to compile and then link them:  To use the above codes you need to compile and then link them: 
          
     $ nasm -f elf hello.asm      $ nasm -f elf hello.asm
     $ ld -o hello hello.o      $ ld -o hello hello.o
     $ ./hello      $ ./hello
     Hello world!      Hello world!
          
   
 ##   gas  ##   gas 
   
 _the portable GNU assembler_  _the portable GNU assembler_
   
 It uses AT&T syntax and designed after the 4.2BSD assembler. You can use it on many CPU architectures.  It uses AT&T syntax and designed after the 4.2BSD assembler. You can use it on many CPU architectures. 
   
 Example:  Example: 
          
          
     .section ".note.netbsd.ident", "a"      .section ".note.netbsd.ident", "a"
             .long   2f-1f              .long   2f-1f
             .long   4f-3f              .long   4f-3f
             .long   1              .long   1
     1:      .asciz  "NetBSD"      1:      .asciz  "NetBSD"
     2:      .p2align 2      2:      .p2align 2
     3:      .long   400000000      3:      .long   400000000
     4:      .p2align 2      4:      .p2align 2
          
     .section .data      .section .data
         data_items:                                # this is an array          data_items:                                # this is an array
             .long 3,39,41,21,42,34,42,23,38,37,15,37,16,17,18,25,23,12,31,2              .long 3,39,41,21,42,34,42,23,38,37,15,37,16,17,18,25,23,12,31,2
             .set DATASIZE, ( . - data_items) / 4 - 1              .set DATASIZE, ( . - data_items) / 4 - 1
          
     .section .text      .section .text
         .globl _start          .globl _start
          
         _start:          _start:
             movl    $0, %edi                        # zero the index register              movl    $0, %edi                        # zero the index register
             movl    $DATASIZE, %ecx                 # set ecx to number of items              movl    $DATASIZE, %ecx                 # set ecx to number of items
             movl    data_items(,%ecx,4), %eax       # load first item              movl    data_items(,%ecx,4), %eax       # load first item
             movl    %eax, %ebx                      # its the biggest atm              movl    %eax, %ebx                      # its the biggest atm
          
         main_loop:          main_loop:
             decl    %ecx                            # decrement counter              decl    %ecx                            # decrement counter
             movl    data_items(,%ecx,4), %eax       # step to next element              movl    data_items(,%ecx,4), %eax       # step to next element
             cmpl    %eax, %ebx                      # is it greater?              cmpl    %eax, %ebx                      # is it greater?
             cmovll  %eax, %ebx                      # set ebx to greater if its less              cmovll  %eax, %ebx                      # set ebx to greater if its less
      than cur. num.       than cur. num.
             jecxz   end_prog                        # if we are at item 0 end iterat              jecxz   end_prog                        # if we are at item 0 end iterat
     ion      ion
             jmp     main_loop                       # again!              jmp     main_loop                       # again!
          
         end_prog:          end_prog:
             pushl   %ebx                            # return largest number              pushl   %ebx                            # return largest number
             pushl   %ebx                            # BSD-ism (has to push twice?)              pushl   %ebx                            # BSD-ism (has to push twice?)
             movl    $1, %eax                        # call exit              movl    $1, %eax                        # call exit
             int     $0x80                           # kernel              int     $0x80                           # kernel
             ret              ret
          
   
 #   powerpc  #   powerpc 
   
 _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 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.  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 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/)).    * 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/)). 
   
   
 ##   gas  ##   gas 
   
 Here is an example of a program for gas:  Here is an example of a program for gas: 
          
          
     ##  factorial.s      ##  factorial.s
     ##  This program is in the public domain and has no copyright.      ##  This program is in the public domain and has no copyright.
     ###      ###
     ##  This is an example of an assembly program for NetBSD/powerpc.      ##  This is an example of an assembly program for NetBSD/powerpc.
     ##  It computes the factorial of NUMBER using unsigned 32-bit integers      ##  It computes the factorial of NUMBER using unsigned 32-bit integers
     ##  and prints the answer to standard output.      ##  and prints the answer to standard output.
          
         .set    NUMBER, 10          .set    NUMBER, 10
                  
     .section ".note.netbsd.ident", "a"      .section ".note.netbsd.ident", "a"
                  
         #  ELF note to identify me as a native NetBSD program          #  ELF note to identify me as a native NetBSD program
         #  type = 0x01, desc = __NetBSD_Version__ from <sys/param.h>          #  type = 0x01, desc = __NetBSD_Version__ from <sys/param.h>
         ##                ##      
         .int    7                       # length of name                  .int    7                       # length of name        
         .int    4                       # length of desc          .int    4                       # length of desc
         .int    0x01                    # type          .int    0x01                    # type
         .ascii  "NetBSD\0"              # name          .ascii  "NetBSD\0"              # name
         .ascii  "\0"                    # padding          .ascii  "\0"                    # padding
         .int    500000003               # desc          .int    500000003               # desc
          
     .section ".data"      .section ".data"
                  
     decbuffer:      decbuffer:
         .fill   16                      # buffer for decimal ASCII          .fill   16                      # buffer for decimal ASCII
     decbufend:      decbufend:
         .ascii  "\n"                    # newline at end of ASCII          .ascii  "\n"                    # newline at end of ASCII
                  
     .section ".text"      .section ".text"
                  
         #  PowerPC instructions need an alignment of 4 bytes          #  PowerPC instructions need an alignment of 4 bytes
         .balign 4          .balign 4
                  
         .globl  _start          .globl  _start
         .type   _start, @function          .type   _start, @function
     _start:      _start:
         #  compute factorial in %r31          #  compute factorial in %r31
         li      %r0, NUMBER          li      %r0, NUMBER
         mtctr   %r0                     # ctr = number          mtctr   %r0                     # ctr = number
         li      %r31, 1                 # %r31 = factorial          li      %r31, 1                 # %r31 = factorial
         li      %r30, 1                 # %r30 = next factor          li      %r30, 1                 # %r30 = next factor
     factorial_loop:      factorial_loop:
         mullw   %r31, %r31, %r30        # multiply %r31 by next factor          mullw   %r31, %r31, %r30        # multiply %r31 by next factor
         addi    %r30, %r30, 1           # increment next factor          addi    %r30, %r30, 1           # increment next factor
         bdnz+   factorial_loop          # loop ctr times          bdnz+   factorial_loop          # loop ctr times
          
         #  prepare to convert factorial %r31 to ASCII.          #  prepare to convert factorial %r31 to ASCII.
         lis     %r9, decbufend@ha          lis     %r9, decbufend@ha
         la      %r4, decbufend@l(%r9)   # %r4 = decbufend          la      %r4, decbufend@l(%r9)   # %r4 = decbufend
         lis     %r8, decbuffer@ha          lis     %r8, decbuffer@ha
         la      %r29, decbuffer@l(%r8)  # %r29 = decbuffer          la      %r29, decbuffer@l(%r8)  # %r29 = decbuffer
         li      %r5, 1                  # %r5 = length of ASCII          li      %r5, 1                  # %r5 = length of ASCII
          
         #  Each loop iteration divides %r31 by 10 and writes digit to          #  Each loop iteration divides %r31 by 10 and writes digit to
         #  position %r4. Formula (suggested by gcc) to divide by 10,          #  position %r4. Formula (suggested by gcc) to divide by 10,
         #                     0xcccccccd          #                     0xcccccccd
         #  is to multiply by ----------- = 0.100000000005821          #  is to multiply by ----------- = 0.100000000005821
         #                    0x800000000          #                    0x800000000
         #  which is to multiply by 0xcccccccd, then shift right 35.          #  which is to multiply by 0xcccccccd, then shift right 35.
         ##          ##
         .set    numerator, 0xcccccccd          .set    numerator, 0xcccccccd
         lis     %r9, numerator@ha          lis     %r9, numerator@ha
         la      %r28, numerator@l(%r9)  # %r28 = numerator          la      %r28, numerator@l(%r9)  # %r28 = numerator
     decloop:      decloop:
         cmpw    %r29, %r4               # start of buffer <=> position          cmpw    %r29, %r4               # start of buffer <=> position
         beq-    buffer_overflow          beq-    buffer_overflow
         #  begin %r9 = (%r31 / 10)          #  begin %r9 = (%r31 / 10)
         mulhwu  %r9, %r31, %r28         # %r9 = ((%r31 * %r28) >> 32)          mulhwu  %r9, %r31, %r28         # %r9 = ((%r31 * %r28) >> 32)
         addi    %r4, %r4, -1            # move %r4 to next position          addi    %r4, %r4, -1            # move %r4 to next position
         srwi    %r9, %r9, 3             # %r9 = (%r9 >> 3) = %r31 / 10          srwi    %r9, %r9, 3             # %r9 = (%r9 >> 3) = %r31 / 10
         mulli   %r8, %r9, 10            # %r8 = (%r31 / 10) * 10          mulli   %r8, %r9, 10            # %r8 = (%r31 / 10) * 10
         sub     %r27, %r31, %r8         # %r27 = %r31 % 10 = digit          sub     %r27, %r31, %r8         # %r27 = %r31 % 10 = digit
         addi    %r27, %r27, '0          # convert digit to ASCII          addi    %r27, %r27, '0          # convert digit to ASCII
         addi    %r5, %r5, 1             # count this ASCII digit          addi    %r5, %r5, 1             # count this ASCII digit
         stb     %r27, 0(%r4)            # write ASCII digit to buffer          stb     %r27, 0(%r4)            # write ASCII digit to buffer
         mr.     %r31, %r9               # %r31 /= 10, %r31 <=> 0          mr.     %r31, %r9               # %r31 /= 10, %r31 <=> 0
         bne+    decloop                 # loop until %r31 == 0          bne+    decloop                 # loop until %r31 == 0
         #  FALLTHROUGH          #  FALLTHROUGH
          
     buffer_overflow:      buffer_overflow:
         #  write(2) our factorial to standard output          #  write(2) our factorial to standard output
         li      %r0, 4                  # SYS_write from <sys/syscall.h>          li      %r0, 4                  # SYS_write from <sys/syscall.h>
         li      %r3, 1                  # standard output          li      %r3, 1                  # standard output
         ##      %r4                     # buffer          ##      %r4                     # buffer
         ##      %r5                     # size of buffer          ##      %r5                     # size of buffer
         sc          sc
          
         #  exit(2)          #  exit(2)
         li      %r0, 1                  # SYS_exit from <sys/syscall.h>          li      %r0, 1                  # SYS_exit from <sys/syscall.h>
         li      %r3, 0                  # exit status          li      %r3, 0                  # exit status
         sc          sc
          
         .size   _start, . - _start          .size   _start, . - _start
          
   
 With a NetBSD/powerpc system, you can run this program using  With a NetBSD/powerpc system, you can run this program using 
          
     $ as -o factorial.o factorial.s      $ as -o factorial.o factorial.s
     $ ld -o factorial factorial.o      $ ld -o factorial factorial.o
     $ ./factorial      $ ./factorial
     3628800      3628800
     $      $
          
   
 ##   Useful Documents  ##   Useful Documents 
   
 To learn about PowerPC assembly language, here are two documents to start with.  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).    * 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.    * 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.      * NetBSD, Linux and OpenBSD (and FreeBSD?) all use ELF with PowerPC and all follow this specification, with a few deviations and extensions. 
   
 ##   Wiki Pages  ##   Wiki Pages 
   
   * [[ELF Executables for PowerPC]]. This introduces assembly language with a commented example.    * [[ELF Executables for PowerPC]]. This introduces assembly language with a commented example. 
   

Removed from v.1.1  
changed lines
  Added in v.1.2


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