Annotation of wikisrc/examples/netbsd_assembly.mdwn, revision 1.2
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:
! 30: 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).
! 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 codes you need to compile and then link them:
! 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 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