If you link to this page, your page will be listed in The TODO list under "ASM Examples".
(So we have a reference of pages containing ASM source code.)
All ASM examples in NASM syntax
Included from NasmAllInOne
This page collects the NASM translation of all the AsmExamples given for GAS AT&T syntax. Note that GCC inlined assembly statement will not be converted as they may assume too strong knowledge of structures, global variables, etc.
| Note | unless stated otherwise, these translation haven't been tested yet. You're welcome to stick a comment if you try them. |
|---|
From BareBones #loader.s
global _loader ; making entry point visible to linker
extern _main ; _main is defined elsewhere
; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ 1<<0 ; align loaded modules on page boundaries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MODULEALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum required
section .text
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
; reserve initial kernel stack space
STACKSIZE equ 0x4000 ; that's 16k.
_loader:
mov esp, stack+STACKSIZE ; set up the stack
push eax ; pass Multiboot magic number
push ebx ; pass Multiboot info structure
call _main ; call kernel proper
hlt ; halt machine should kernel return
section .bss
align 32
stack:
resb STACKSIZE ; reserve 16k stack on a quadword boundary
I fixed the above so that it will actually build, but I haven't run it. I had to change "extern _loader" to "global _loader", and I had to rename "ALIGN" to "MODULEALIGN" since "ALIGN" is a nasm keyword.
--BruceJohnstonI tried, this works nicely. Thanks, I'll be using this now.
Why not call this "loader.asm" ? (to distinguish from loader.s for GAS)
This can be assembled with "nasm -f elf loader.asm" (without "-f elf" nasm complains about unsupported external references)
--ZomgTest
Example from BareBonesC++ (loader.s)
Note that this is a replacement for bare bone's loader.s code, but that data should be kept
extern start_ctors, end_ctors, start_dtors, end_dtors
; declare ctors/dtors markers as extern (they come from linker.ld)
; (or add this^ at the top with other extern/global's)
_loader:
mov esp, stack+STACKSIZE ; set up the stack
push eax ; pass Multiboot magic number
push ebx ; pass Multiboot info structure
static_ctors_loop:
mov ebx, start_ctors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_ctors
jb .body
call _main ; call kernel proper
static_destructors_loop:
mov ebx, start_dtors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_dtors
jb .body
hlt ; halt machine should kernel return
Hmmm... the jump labels look funny to me. Can NASM really handle multiple jump labels of the same name?
-- MartinBauteActually, yes and no. NASM labels that starts with a dot are shortcuts for "last_non_dotted_symbol.dotted_symbols". In other words, .body is relative to static_..._loop
--PypeClickerI felt this was lacking {start,end}_{ctors,dtors} declared as extern.
--ZomgTest
Example from InterruptServiceRoutines
; filename : isr_wrapper.asm
extern _isr_wrapper
align 4
_isr_wrapper:
pushad
call _interrupt_handler
popad
iret
All ASM examples in GAS syntax
Included from GasAllInOne
| Note | The translations provided in this page are there for informative purpose only. You shouldn't expected them to be up-to-date compared to their original versions ... Any report of (un)successful use of this code is welcome. |
|---|
From BabyStep1
# Note that GAS cannot create flat binaries by itself, # but needs the help of the linker: # as -o boot.o boot.asm # ld boot.o -o boot.bin --oformat binary # partcopy boot.bin 0 200 -f0 .code16 # unlike NASM, GAS assumes 32 bits from the start .text hang: jmp hang .org 510 boot_flag: .word 0xAA55
From BabyStep2
Remember that GAS requires LD to create binaries, use LD to rebase the code if necessary (eg. ld --oformat binary --Ttext 0x7C00 -o boot.bin boot.o). Keep in mind that the --Ttext from LD refers to the offset from the start of the segment not the physical 0.
GAS supports local labels in the form of numbers for moving around in functions, there is also an odd way to use them, if the number is before the instruction then you use 1b, if it is after then you use 1f (Presumeably b=backward, f=forward).
# Link as ld --oformat binary --Ttext 0x0 -o Boot.bin Boot.o .code16 .text mov $0x07C0, %ax mov %ax, %ds mov $msg, %si 1: lodsb or %al,%al # zero=end of str jz 2f # get out mov $0x0E, %ah int $0x10 jmp 1b 2: jmp 2b msg: .asciz "Welcome to Macintosh\n" # .asciz automatically puts a NULL to terminate the string, use .ascii if you don't want that .org 510 .word 0xAA55
# Link as ld -oformat binary -Ttext 0x7C00 -o Boot.bin Boot.o .code16 .text ljmp $0, $RebasedStart RebasedStart: xor %ax, %ax mov %ax, %ds mov $msg, %si 1: lodsb or %al,%al # zero=end of str jz 2f # get out mov $0x0E, %ah int $0x10 jmp 1b 2: jmp 2b msg: .asciz "Welcome to Macintosh\n" .org 510 .word 0xAA55
This example also demonstrates how to create a label to describe functions so that when you come back to them later you can see what it does at a glance without having to read the code. (Clobbers is a term from GCC for registers that contain garbage when it returns)
# Link as ld -oformat binary -Ttext 0x7C00 -o Boot.bin Boot.o
.code16
.text
ljmp $0, $Start
Start:
xor %ax, %ax
mov %ax, %ds
mov $msg, %si
call Print
1:
jmp 1b
# Prints a NULL-terminated string
# INPUT: SI=String
# OUTPUT: None
# CLOBBERS: SI, AX, BX, FLAGS
Print:
mov $0x0E, %ah
xor %bx, %bx
1:
lodsb
or %al, %al
jz 2f
# INT 0x10 VIDEO TELETYPE
# AH=0xE AL=Character [BH=VideoPage BL=Attribute]
int $0x10
jmp 1b
2:
ret
msg: .asciz "Welcome to Macintosh\n"
.org 510
.word 0xAA55
GAS Macros are similar to NASM's macros
# Link as ld -oformat binary -Ttext 0x7C00 -o Boot.bin Boot.o .code16 .text # Syntax is .macro MacroName Param1, Param2, Param3, ... # Alternative: .macro MacroName Param1 Param2 Param3 ... # You can also set defaults like .macro DoSomething Int1=3 Int2=6 .macro BIOSPrint StringPointer mov $\StringPointer, %si 1: lodsb or %al,%al jz 2f mov $0x0E, %ah int $0x10 jmp 1b 2: .endm ljmp $0, $RebasedStart RebasedStart: xor %ax, %ax mov %ax, %ds BIOSPrint msg 2: jmp 2b msg: .asciz "Welcome to Macintosh\n" .org 510 .word 0xAA55
main: 1: jmp 1b .include "Other.S" # The code from these files is assembled as if pasted here .include "MoreOthers.S"
From BabyStep3
GAS Operates much the same way as NASM in this regard:
.code16 movl $0x12345678, %eax # the L suffix indicates a 32bit operation (l=long)
.code32 movw $0x1234, %ax # the W suffix indicates a 16bit operation (w=word), there is also the B suffix indicating an 8bit operation (b=byte)
The suffixes are not always required, for example moves between registers or registers and memory don't require you to add a suffix as the operation size is implied by the size of the register used. Usually you'll only need them in places where you have to use a byte/word/dword directive in the instruction in NASM.
From BabyStep4
From BabyStep5
From BabyStep7
.code16
.text
ljmp $0, $Start
Start:
xor %ax, %ax
mov %ax, %ds
mov %ax, %ss
mov $0x8200, %sp # Differs from the original since we hardly need 7KBs of stack
cli # Interrupts off, save current DS
push %ds
lgdt (gdtinfo) # The brackets are just decoration, it works without them but you may find it more legible this way
mov %cr0, %eax # Enable protected mode
or $1, %al
mov %eax, %cr0
mov $0x8, %ax # Load selector 1, 32bit flat data
mov %ax, %ds
mov $0x0f01, %ax # Use the segment to write to the screen
mov %ax, %ds:(0xB8000)
mov %cr0, %eax # Shut off protected mode
xor $1, %al
mov %eax, %cr0
pop %ds # Get the realmode segment back and enable interrupts
sti
1:
jmp 1b
# Description for the CPU to find the GDT
gdtinfo: .word gdtend - gdt - 1
.long gdt
gdt: .long 0 # Descriptor 0 is the NULL descriptor
.long 0
gdtkerneldata: # 32bit Flat Data, 0-4GB Writable
.word 0xFFFF
.word 0
.byte 0
.byte 0x92
.byte 0xCF
.byte 0
gdtend:
.org 510
.word 0xAA55
From BabyStep8
.code32
# ------------------
dochar:
call cprint # print one character
sprint:
lodsb # string char to AL
or %al, %al
jnz dochar # else, we're done
addb $1, (ypos) # down one row
movb $0, (xpos) # back to left
ret
cprint:
mov $0x0F, %ah # attrib = white on black
mov %eax, %ecx # save char/attribute
movzxb (ypos), %eax
mov $160, %edx # 2 bytes (char/attrib)
mul %edx # for 80 columns
movzxb (xpos), %ebx
shl $1, %ebx # times 2 to skip attrib
mov $0xb8000, %edi # start of video memory
add %eax, %edi # add y offset
add %ebx, %edi # add x offset
mov %ecx, %eax # restore char/attribute
movw %ax, %ds:(%edi)
addb $1, (xpos) # advance to right
ret
# ------------------------------------
printreg32:
mov $outstr32, %edi
mov (reg32), %eax
mov $hexstr, %esi
mov $8, %ecx # eight nibbles
hexloop:
rol $4, %eax # leftmost will
mov %eax, %ebx # become rightmost
and $0x0f, %ebx
mov (%esi, %ebx), %bl # index into hexstr
mov %bl, (%edi)
inc %edi
dec %ecx
jnz hexloop
mov $outstr32, %esi
call sprint
ret
# ------------------------------------
xpos: .byte 0
ypos: .byte 0
hexstr: .ascii "0123456789ABCDEF"
outstr32: .asciz "00000000" # register value
reg32: .long 0 # pass values to printreg32
# ------------------------------------
From Help!? I can't get interrupts working
int_handler:
mov $LINEAR_DATA_SELECTOR, %ax
mov %ax, %gs
movl $0x2029203A, %gs:(0xB8000)
hlt
test1:
lidt (idtr)
mov $int_handler, %eax
movw %ax, (idt+49*8)
movw $CODE_SELECTOR, (idt+49*8+2)
movw $0x8E00, (idt+49*8+4)
shr $16, %eax
movw %ax, (idt+49*8+6)
int $49
idtr:
.word (50*8)-1
.long LINEAR_ADDRESS(idt)
# Create in the COMMON section (usually part of .bss)
.comm idt, 50*2*4
From How can I tell CPU speed ?
RDTSC:
get_speed:
# First do a cpuid command, with eax=1 mov $1, %eax cpuid test $0x10, %edx # test bit #4. Do we have TSC ? jnz detect_end # no ?, go to detect_end # Wait until the timer interrupt has been called. mov (irq0_count), %ebx
wait_irq0:
cmp (irq0_count), %ebx jz wait_irq0 rdtsc # read time stamp counter mov %eax, (tscLoDword) mov %edx, (tscHiDword) add $2, %ebx # Set time delay value ticks. # Remember: so far ebx = [irq0]-1, so the next tick is # two steps ahead of the current ebx ;)
wait_for_elapsed_ticks:
cmp (irq0_count), %ebx # Have we hit the delay? jnz wait_for_elapsed_ticks rdtsc sub (tscLoDword), %eax # Calculate TSC sbb (tscHiDword), %edx # f(total_ticks_per_Second) = (1 / total_ticks_per_Second) * 1,000,000 # This adjusts for MHz. # so for this: f(100) = (1/100) * 1,000,000 = 10000 mov $10000, %ebx div %ebx # ax contains measured speed in MHz mov %ax, (mhz)
