| 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)
