Membuat bootstrap loader untuk UFD

Pada beberapa mata kuliah seperti sistem operasi dan sistem berkas, dosen mungkin harus mendemokan beberapa komponen low-level yang tidak tersedia pada sistem operasi modern.  Seperti yang diketahui, seluruh sistem operasi modern zaman sekarang beroperasi pada CPU protected mode.  Hal ini berarti hanya kode program saja yang berada di ring 0, sementara kode program yang dibuat user tidak akan mendapatkan akses ring 0 (kecuali bila diizinkan oleh sistem operasi).  Beberapa instruksi assembly seperti IN, OUT, dan beberapa INT umumnya dibatasi oleh sistem operasi.  Sebagai contoh, Windows akan menolak instruksi IN/OUT pada program user mode tetapi mengizinkannya pada driver.  Sementara di Linux, IN/OUT diperbolehkan bila sebelumnya terdapat pemanggilan ioperm() dan program di-akses dengan hak akses superuser.

Bagaimana bila seseorang ingin tetap menjalankan programnya di CPU real-mode?  Ia harus membuat sistem operasi sendiri!  Atau untuk sebuah eksperimen sederhana, ia dapat membuat sebuah bootstrap loader yang akan menjalankan sebuah program real mode begitu komputer dinyalakan!  Dan untuk eksperimen seperti ini, media UFD (USB Flash Drive) adalah pilihan yang tepat.  Kebanyakan BIOS di motherboard zaman sekarang sudah mendukung emulasi disk untuk UFD, sehingga mendukung proses boot dari UFD.

Ini adalah percobaan yang saya lakukan dengan menggunakan sebuah UFD berukuran 4 GB yang menggunakan file system FAT32.  UFD tersebut hanya mengandung sebuah partisi.  Saya menyalin boot sector dari partisi pertama.  Struktur boot sector untuk FAT32 dapat dilihat di http://en.wikipedia.org/wiki/FAT32.  Berikut ini kode program assembly (dalam NASM) yang membentuk boot sector di UFD saya beserta dengan bootstrap loader buatan sendiri:

[BITS 16]
[ORG 0x7C00]

	jmp	mulai
	nop

	; ------------------------------------------------------
	; Informasi Boot Sector
	; ------------------------------------------------------

	db 'SOS-JCH '					; OEM name
	db 00, 0x2					; Bytes per sector
	db 0x8						; Sector per cluster
	db 0x20, 00					; Reserved sector count
	db 0x2						; Number of FAT
	db 00, 00, 00,00
	db 0xF8						; Media descriptor (F8 = Fixed disk)
	db 00, 00
	db 0x3E, 00					; Sectors per track
	db 0x7C, 00					; Number of heads
	db 00, 00, 00, 00				; Count of hidden sectors
	db 0xA2, 0xA7, 0x77, 00				; Total sectors
	db 0xDB, 0x1D, 00, 00				; Sectors per FAT
	db 00, 00					; Mirroring flags
	db 00, 00					; Version
	db 0x02, 00, 00, 00				; Cluster number of root directory start
	db 0x01, 00					; Sector number of FS Information Sector
	db 0x06, 00					; First sector number of a copy of three FAT32 boot sectors
	db 00, 00, 00, 00
	db 00, 00, 00, 00
	db 00, 00, 00, 00
	db 00, 00
	db 0x29						; Extended boot signature
	db 0xA5, 0x82, 0x1D, 0x86			; Volume ID
	db 'Latihan    '				; Volume Label
	db 'FAT32   '					; File system type

	; ------------------------------------------------------
	; Starting point
	; ------------------------------------------------------
mulai:

	; ------------------------------------------------------
	; Inisialisasi
    	; ------------------------------------------------------
	cli
	xor ax, ax
	mov ss, ax
	mov sp, 0x7C00
	mov ds, ax
	mov es, ax
	sti

	; ------------------------------------------------------
	; Menyalin FAT dan menyimpannya ke lokasi offset 0x7E00
	; ------------------------------------------------------ 

	mov word [ds:dap_jumlah_sector], 1
	mov word [ds:dap_dest_segment], ds
 	mov word [ds:dap_dest_offset], 0x7E00
	mov dword [ds:dap_lba_lo], 94 

	call bacasector

	mov eax, [0x7E08]
	mov [fat_next_cluster], eax

	; ------------------------------------------------------
	; Menyalin RootDirectoryEntry ke lokasi offset 0x8000
	; ------------------------------------------------------
	mov word [ds:dap_jumlah_sector], 8
 	mov word [ds:dap_dest_offset], 0x8000
	mov dword [ds:dap_lba_lo], 0x3C14
	call bacasector

	; ------------------------------------------------------
	; Mencari File Dengan Nama KERNEL.BIN
	; ------------------------------------------------------
proses_cluster_berikutnya:

	mov si, 0x8000

pencarian_file_kernel:

	push si
	lea di,	[nama_file_kernel]
	cld
	mov cx, 11
	repe cmpsb
	jz file_kernel_ditemukan

	;
	; File kernel tidak ditemukan pada entry ini.
  	; Lanjut ke entry direktori berikutnya.
	;

	pop si
	add si, 32
	cmp si, 0x9000
	jbe pencarian_file_kernel

	;
	; File kernel tidak ditemukan di cluster ini
	; Periksa apakah masih ada cluster berikutnya.
	;

	mov eax, [fat_next_cluster]
	and eax, 0x0ffffff0
	cmp eax, 0x0ffffff0
	jz hang

	mov di, 0x8000
	call bacacluster
	call bacafat32

	jmp proses_cluster_berikutnya

file_kernel_ditemukan:

	;------------------------------------------------------------
	; Baca File Kernel dan Salin Di Lokasi Memori 0x8000
	;------------------------------------------------------------
	pop si
	mov ax, word [si+0x14]
	shl eax, 16
	mov ax, [si+0x1A]
	mov [fat_next_cluster], eax
	mov di, 0x8000

baca_cluster_kernel_berikutnya:
	call bacacluster
	call bacafat32
	mov eax, [fat_next_cluster]
	and eax, 0x0ffffff0
	cmp eax, 0x0ffffff0
	jz start_kernel

	add di, 0x1000
	mov eax, [fat_next_cluster]
	jmp baca_cluster_kernel_berikutnya

	;------------------------------------------------------------
	; Mulai eksekusi kernel
	;------------------------------------------------------------
start_kernel:	
	jmp 0x8000

hang:
	jmp hang

bacafat32:
	;
	; Baca sebuah sektor FAT32 ke lokasi 0x7E00
	; Perhitungan berdasarkan nilai memori [fat_next_cluster]
	;

	; Mengisi fat_next_cluster dengan nilai
	; entry fat berikutnya
	; Rumus: sector = 62 + 32 + fat_next_cluster / 128
	;	 offset = (fat_next_cluster % 128 ) * 4

	xor eax, eax
	xor edx, edx
	mov ax, [fat_next_cluster]
	mov dx, [fat_next_cluster+2]
	mov cx, 128
	div cx 

	push dx  	; remainder, untuk offset nanti

	add eax, 94;
	mov word [ds:dap_jumlah_sector], 1
	mov word [ds:dap_dest_offset], 0x7E00
	mov dword [ds:dap_lba_hi], 0
	mov dword [ds:dap_lba_lo], eax
	call bacasector

	pop dx
	shl dx, 2
	add edx, 0x7E00
	mov ecx, [edx]
	mov [fat_next_cluster], ecx

	ret

bacacluster:
	;
	; Baca cluster data berikutnya.
	; eax = nomor cluster
	;  di = offset tujuan
	; Rumus menghitung sector dari cluster:
	;  s = 62 + 15318 + ((c-2)*8)
	;
	dec eax
	dec eax
	mov ecx, 8
	mul ecx
	xor ecx, ecx
	add eax, 15380
	adc edx, ecx
	mov dword [ds:dap_lba_hi], edx
	mov dword [ds:dap_lba_lo], eax
	mov word [ds:dap_jumlah_sector], 8
 	mov word [ds:dap_dest_offset], di
	call bacasector
	ret

bacasector:
	mov ah, 0x42
	mov dl, 0x80
	mov si, dap
	int 0x13
	ret

dap			db	0x10, 0x00
dap_jumlah_sector	dw	0x0001
dap_dest_offset		dw	0x0000
dap_dest_segment	dw	0x007C
dap_lba_lo		dd	0x00000000
dap_lba_hi		dd	0x00000000

pesan_salah_fat32	db	'Kernel tidak ditemukan.',0
nama_file_kernel	db	'KERNEL  BIN'
fat_next_cluster	dd	0x00000000

times 510-($-$$) db 0
dw 0xAA55

Directive [BITS 16] memberi tahu NASM bahwa output dari assembly ini adalah kode program real mode 16-bit.  Pada saat BIOS selesai menyalin boot sector, ia akan memindahkan IP pada 0x7C00 sehingga instruksi yang akan dikerjakan adalah 0x7C00.  Itu sebabnya terdapat directive [ORG 0x7C00].

Assembly di atas jauh dari sederhana, sehingga masih banyak perbaikan yang dapat dilakukan.  Salah satunya adalah saya menganggap 1 cluster terdiri atas 8 sector dan 1 sector terdiri atas 512 bytes.  Hal ini berlaku pada UFD saya, tetapi mungkin berbeda pada UFD yang di-format secara berbeda.  Selain itu, saya menganggap sector yang menampung informasi Root Directory dimulai dari lokasi sector ke 15.318.  Saya juga menganggap jarak dari MBR ke boot sector adalah 62 sector.  Kode assembly yang lebih baik akan menyertakan proses perhitungan (berdasarkan informasi field di boot sector) sehingga bootstrap loader menjadi fleksibel.

Kode assembly yang ada pada dasarnya akan memeriksa linked list di FAT32 dan membaca seluruh cluster yang berisi informasi Root Directory.  Bootstrap loader akan mencari file bernama KERNEL.BIN.  Bila file ini ketemu, isi dari file tersebut akan disalin ke lokasi memori 0x8000, dan eksekusi akan dilanjutkan pada lokasi memori tersebut.

Kode assembly di atas dapat diproses menjadi sebuah boot sector dengan menggunakan NASM:

nasm -o boot.bin boot.asm

Output dari perintah di atas adalah sebuah file berukuran 512 bytes dan diakhir dengan 0x55AA.  Semuanya memenuhi persyaratan sebuah boot sector.  Langkah berikutnya adalah men-copy file tersebut ke boot sector partisi pertama di UFD.  Hal ini dapat dilakukan dengan perintah dd di Linux:

$ sudo dd if=boot.bin of=/dev/sdb1 bs=512 count=1

Sebelum mulai restart komputer dan mengatur prioritas boot device di BIOS, pastikan terlebih dahulu bahwa boot flag pada partisi /dev/sdb1 telah di-set menjadi true.  Untuk mengatur flag tersebut, seseorang bisa menggunakan perintah fdisk di Linux.

Sekarang, pada saat ingin mencoba kode program real mode, seorang programmer assembly hanya perlu menyimpan program tersebut dengan nama KERNEL.BIN.  Setelah itu, copy file KERNEL.BIN ke root directory flash disk.  Kini, setiap kali komputer di-boot melalui flash disk, file KERNEL.BIN akan dikerjakan.

Perlu diingat bahwa program real-mode memiliki memori yang terbatas.  Akses memori hanya dibatasi pada range 1 MB (bila gate A20 aktif, menjadi 1.114.096 bytes).  Area memori ini sudah termasuk yang dipakai untuk Interrupt Descriptor Table (IDT), buffer I/O, dsb sehingga yang tersedia bagi program lebih sedikit lagi.   Untuk memakai memori lebih dari 1 MB, programmer harus beralih ke protected mode (itu sebabnya semua sistem operasi modern bekerja pada protected mode).

Memakai nasm Di Linux

Pada tulisan sebelumnya yang berjudul Assembly di Linux Dengan GAS, saya memperlihatkan penggunaan GNU Assembler di Linux.  GNU Assembler (GAS) hampir tersedia di kebanyakan instalasi Linux, sehingga programmer tinggal memakainya saja.  Tapi untuk memakai GAS, seseorang harus mempelajari assembly dengan syntax AT&T.  Bagi yang terbiasa dengan syntax Intel, pada awalnya mungkin akan sering mengalami sindrom “operand tertukar” (hal ini karena letak operand di syntax AT&T terbalik dengan yang ada di dokumentasi Intel).  Sebagai contoh, perhatikan syntax Intel berikut:

mov al, bl

Baris di atas akan memintahkan isi register BL ke register AL.  Untuk melakukan hal yang sama pada syntax AT&T, programmer harus memberikan perintah seperti:

movb %bl, %al

Urutan operand yang terbalik seperti ini bisa jadi membingungkan bagi programmer yang sudah terbiasa memakai syntax Intel.

Salah satu solusinya adalah dengan meng-install Netwide Assembler (NASM) yang sangat populer di Linux.  Source NASM yang terbaru pada tulisan ini dibuat dapat di-download di situs resmi NASM.  Setelah men-download dan men-extract source NASM, kerjakan script configure.  Berikan perintah make dan make install untuk meng-install NASM pada lokasi default.

Untuk melihat dukungan format NASM, berikan perintah seperti berikut:

$ nasm -hf
...
valid output formats for -f are (`*' denotes default):
* bin       flat-form binary files (e.g. DOS .COM, .SYS)
ith       Intel hex
srec      Motorola S-records
aout      Linux a.out object files
aoutb     NetBSD/FreeBSD a.out object files
coff      COFF (i386) object files (e.g. DJGPP for DOS)
elf32     ELF32 (i386) object files (e.g. Linux)
elf64     ELF64 (x86_64) object files (e.g. Linux)
as86      Linux as86 (bin86 version 0.3) object files
obj       MS-DOS 16-bit/32-bit OMF object files
win32     Microsoft Win32 (i386) object files
win64     Microsoft Win64 (x86-64) object files
rdf       Relocatable Dynamic Object File Format v2.0
ieee      IEEE-695 (LADsoft variant) object file format
macho32   NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X (i386) object files
macho64   NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X (x86_64) object files
dbg       Trace of all info passed to output stage
elf       ELF (short name for ELF32)
macho     MACHO (short name for MACHO32)
win       WIN (short name for WIN32)

Pada Linux, format yang dipakai adalah elf.  Perhatikan bahwa NASM mendukung format bin yang akan menghasilkan flat-form binary file (mirip seperti file COM di zaman DOS).  Format bin seperti ini dapat dipakai untuk menghasilkan kode untuk bootloader dan berbagai keperluan lain dalam membuat sebuah sistem operasi baru.

Berikut ini adalah program yang sama seperti pada tulisan Assembly di Linux Dengan GAS, hanya saja kali ini memakai Intel syntax dan ditujukan untuk NASM:

section .data
output_vendor:
db  `Vendor ID Prosesor adalah 'xxxxxxxxxxxx'\n`
output_vendor_length    equ     $-output_vendor

output_brand:
db  `Prosesor Brand String adalah 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'\n`
output_brand_length    equ    $-output_brand

output_no_brand:
db  `Tidak ada informasi Processor Brand String\n`
output_no_brand_length    equ    $-output_no_brand

section .text
global _start
_start:
mov    eax, 0
cpuid
mov    edi, output_vendor
mov    [edi+27], ebx
mov    [edi+31], edx
mov    [edi+35], ecx
mov    eax, 4
mov    ebx, 1
mov    ecx, output_vendor
mov    edx, output_vendor_length
int    0x80

mov    eax, 0x80000000
cpuid
cmp    eax, 0x80000004
jl    no_brand

mov    eax, 0x80000002
cpuid
mov    edi, output_brand
mov    [edi+30], eax
mov    [edi+34], ebx
mov    [edi+38], ecx
mov    [edi+42], edx

mov    eax, 0x80000003
cpuid
mov    [edi+46], eax
mov    [edi+50], ebx
mov    [edi+54], ecx
mov    [edi+58], edx

mov    eax, 0x80000004
cpuid
mov    [edi+62], eax
mov    [edi+66], ebx
mov    [edi+70], ecx
mov    [edi+74], edx
mov    [edi+77], byte 0x20

mov    eax, 4
mov    ebx, 1
mov    ecx, output_brand
mov    edx, output_brand_length
int    0x80
jmp    selesai

no_brand:
mov    eax, 4
mov    ebx, 1
mov    ecx, output_no_brand
mov    edx, output_no_brand_length
int     0x80

selesai:
mov    eax, 1
mov     ebx, 0
int    0x80

Untuk menjalankan program tersebut, berikan perintah seperti berikut ini:

$ nasm -f elf cpuinfo.asm
$ ld -o cpuinfo cpuinfo.o
$ ./cpuinfo
Vendor ID Prosesor adalah 'GenuineIntel'
Prosesor Brand String adalah 'Pentium(R) Dual-Core CPU       T4400  @ 2.20GHz '

Nilai option “-f elf” menunjukkan bahwa NASM akan menghasilkan output dalam format ELF yang dipakai oleh Linux.  Pembuat program assembler yang terbiasa menggunakan IA-32 pun dapat tetap memakai Intel syntax di platform Linux.

Memakai System Call Linux Di Assembly

Untuk memanggil system call Linux, programmer assembly harus mengetahui nomor definisi system call yang disebut system call numbers.  Informasi ini dapat diperoleh dengan membaca file /usr/include/asm/unistd_32.h.  Berikut ini adalah contoh isi file tersebut:

#ifndef _ASM_X86_UNISTD_32_H
\#define _ASM_X86_UNISTD_32_H

/*
* This file contains the system call numbers.
*/

\#define __NR_restart_syscall      0
\#define __NR_exit          1
\#define __NR_fork          2
\#define __NR_read          3
\#define __NR_write          4
\#define __NR_open          5
\#define __NR_close          6
\#define __NR_waitpid          7
\#define __NR_creat          8
\#define __NR_link          9
\#define __NR_unlink         10
\#define __NR_execve         11
...
\#define __NR_inotify_init1    332
\#define __NR_preadv        333
\#define __NR_pwritev        334
\#define __NR_rt_tgsigqueueinfo    335
\#define __NR_perf_event_open    336

Isi file di atas menunjukkan bahwa pada sistem operasi Linux tersebut, terdapat 337 system call yang dapat dipanggil oleh programmer assembly, mulai dari 0 hingga 336. Setelah menemukan system call number, langkah berikutnya adalah menentukan apa saja yang dibutuhkan untuk memanggil system call tersebut.  Salah satu cara yang dapat dilakukan adalah dengan memanggil perintah man.  Walaupun ditujukan bagi programmer C, informasi yang ditampilkan oleh man cukup berguna bagi programmer assembly yang ingin memanggil system call tersebut.  Sebagai contoh, bila programmer ingin melihat informasi untuk system call ber-nomor 4 (didefinisikan sebagai __NR_write), maka ia dapat memberikan perintah:

$ man 2 write
NAME
write - write to a file descriptor

SYNOPSIS
\#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

...

Untuk mengerjakan system call di Linux, programmer assembly menggunakan INT dengan nomor interrupt 0x80.  Sebelum memberikan INT 0x80, nomor system call (sesuai dengan yang ada di file unistd_32.h) harus diletakkan terlebih dahulu di register EAX, sehingga kernel Linux dapat mengetahui system call apa yang akan dikerjakan.  Selain itu, bila ada, nilai parameter harus diberikan pada register tertentu dengan urutan seperti berikut ini:

  • Nilai parameter pertama di-isi pada register EBX
  • Nilai parameter kedua di-isi pada register ECX
  • Nilai parameter ketiga di-isi pada register EDX
  • Nilai parameter keempat di-isi pada register ESI
  • Nilai parameter kelima di-isi pada register EDI
  • Bila jumlah parameter lebih dari lima, EBX mengandung pointer ke lokasi memori yang berisi input parameter secara berurutan.

Pada system call write, informasi yang diberikan oleh man pages menunjukkan bahwa dibutuhkan tiga parameter, yaitu fd, *buf, dan count.  Dengan demikian:

  • Register EBX mengandung file descriptor (fd)
  • Register ECX mengandung pointer ke string yang akan ditampilkan (*buf)
  • Register EDX mengandung informasi jumlah karakter di string tersebut (count)

Nilai kembalian dari sebuah system call akan ditampung di register EAX.

Contoh kode program assembly secara lengkapnya akan menjadi:

$ vi test.s
.section .data
tulisan:
.ascii "Ini program pertama saya.\n"

.section .text
.globl _start
_start:
movl $4, %eax           # System call nomor 4 (write)
movl $1, %ebx           # Parameter 1: file descriptor = 1 (stdout)
movl $tulisan, %ecx     # Parameter 2: string yang dicetak
movl $26, %edx          # Parameter 3: jumlah karakter (26 huruf)
int $0x80               # Memanggil kernel Linux

movl $1, %eax           # System call nomor 1 (exit)
movl $0, %ebx           # Parameter 1: status code
int $0x80               # Memanggil kernel Linux

$ as -o test.o test.s
$ ld -o test test.o
$ ./test
Ini program pertama saya.

Assembly di Linux Dengan GAS

Microsoft Macro Assembler (MASM) adalah salah satu assembler yang paling tua dan kini versinya sudah mencapai versi 10. Walaupun demikian, MASM bukanlah sebuah produk terpisah yang dapat di-download sendiri seperti zaman dulu.  Tidak ada link untuk download MASM.  Cara untuk mendapatkan MASM, misalnya, dengan meng-install Visual Studio 2010 yang sudah menyertakan assembler tersebut.

Bagaimana kalau seseorang hanya ingin sebuah assembler saja, sebuah produk yang berdiri sendiri, sebuah assembler modern, dan gratis?  Jawabannya terdapat dalam GNU Binary Utilities atau kerap disingkat binutils.  GNU Binary Utilities adalah kumpulan tools open source yang umumnya sudah ada dalam bawaan dalam distro populer Linux.  Informasi lengkap dapat dilihat di www.gnu.org/software/binutils.  Salah satu tools yang diserta oleh GNU Binary Utilities adalah sebuah assembler yang disebut GNU Assembler (atau disingkat GAS).  Keunikan GAS adalah sifat cross-platform-nya.  GAS mendukung banyak arsitektur komputer yang berbeda seperti Alpha, ARC, ARM, CRIS, D10V, D30V, H8/300, i386, i960, IA-64, IP2K, M32C, M32R, M680X0, M68HC11, PowerPC, SPARC, dan lainnya.

Walaupun gratis dan cross-platform, GAS memiliki sebuah permasalahan yang cukup mengganggu, terutama bagi mereka yang ingin menggunakan assembler tersebut di platform IA-32.  GAS menggunakan syntax AT&T sehingga programmer assembly yang terbiasa dengan MASM (sebagai informasi, MASM  memakai syntax Intel) harus menyesuaikan diri dan mempelajari syntax baru.  Selain itu, Intel menggunakan syntax Intel dalam dokumentasi prosesornya sehingga programmer GAS yang membaca dokumentasi Intel harus mereka-reka mnemonic yang ada bila harus ditulis dalam versi syntax AT&T.

Berikut ini adalah contoh skeleton sebuah program assembly untuk GAS:

.section .data
   ... (bagian data)

.section .bss
   ... (bagian block started by symbol)

.section .text
.globl _start
_start:
   ... (bagian kode program)

Sebagai contoh, di bawah ini adalah kode program GAS yang memanggil instruksi CPUID untuk mendapatkan informasi CPU.  Dengan memberikan nilai 0 pada register EAX sebelum memanggil CPUID, CPU manufacturer ID akan disimpan di register EBX, EDX, ECX (12 karakter).  Program kemudian akan mencetak CPU manufacturer ID tersebut.  Kemudian program akan mencetak processor brand string bila ada.  Program akan memanggil CPUID dengan nilai EAX = 0x80000000 dan memeriksa apakah nilai yang dikembalikan lebih besar dari 0x800000004.  Bila iya, terdapat processor brand string yang dapat dicetak dengan memanggil CPUID dimana nilai EAX=0x80000002, 0x80000003, dan 0x80000004 secara berurutan.

.section .data
output_vendor:
   .ascii    "Vendor ID Prosesor adalah 'xxxxxxxxxxxx'\n"
output_brand:
   .ascii    "Prosesor Brand String adalah 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'\n"
output_no_brand:
   .ascii  "Tidak ada informasi Processor Brand String\n"

.section .text
.globl _start
_start:
   movl $0, %eax
   cpuid
   movl $output_vendor, %edi
   movl %ebx, 27(%edi)
   movl %edx, 31(%edi)
   movl %ecx, 35(%edi)
   movl $4, %eax
   movl $1, %ebx
   movl $output_vendor, %ecx
   movl $41, %edx
   int $0x80

   movl $0x80000000, %eax
   cpuid
   cmpl $0x80000004, %eax
   jl no_brand

   movl $0x80000002, %eax
   cpuid
   movl $output_brand, %edi
   movl %eax, 30(%edi)
   movl %ebx, 34(%edi)
   movl %ecx, 38(%edi)
   movl %edx, 42(%edi)
   movl $0x80000003, %eax
   cpuid
   movl %eax, 46(%edi)
   movl %ebx, 50(%edi)
   movl %ecx, 54(%edi)
   movl %edx, 58(%edi)
   movl $0x80000004, %eax
   cpuid
   movl %eax, 62(%edi)
   movl %ebx, 66(%edi)
   movl %ecx, 70(%edi)
   movl %edx, 74(%edi)

   movb $0x20, 77(%edi)
   movl $4, %eax
   movl $1, %ebx
   movl $output_brand, %ecx
   movl $80, %edx
   int $0x80

   jmp selesai

no_brand:
   movl $4, %eax
   movl $1, %ebx
   movl $output_no_brand, %ecx
   movl $43, %edx
   int $0x80

selesai:
   movl $1, %eax
   movl $0, %ebx
   int $0x80

Program di atas menggunakan Linux system call (int $0x80) untuk mencetak ke layar konsole, yaitu write.  Di sistem operasi DOS & Windows.  Hal ini sama seperti memanggil interrupt 0x20 untuk mencetak di console.

Untuk menjalankan program di atas, berikan perintah seperti:

$ as -o cpuinfo.o cpuinfo.s
$ ld -o cpuinfo cpuinfo.o
$ ./cpuinfo
Vendor ID Prosesor adalah 'GenuineIntel'
Prosesor Brand String adalah 'Pentium(R) Dual-Core CPU       T4400  @ 2.20GHz '

Output dari program tersebut akan berbeda tergantung pada prosesor yang dipakai oleh pengguna.