File:  [NetBSD Developer Wiki] / wikisrc / examples / netbsd_assembly.mdwn
Revision 1.3: download - view: text, annotated - select for diffs
Sat Oct 13 08:35:49 2018 UTC (3 years, 1 month ago) by leot
Branches: MAIN
CVS tags: HEAD
Fix some spello.

Reported by graff on #NetBSD@Freenode.

    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: 
   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). 
   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: 
   72: To use the above code you need to compile and then link it: 
   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: 
   84: It uses AT&T syntax and is designed after the 4.2BSD assembler. You can use it on many CPU architectures. 
   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