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